forked from GNUsocial/gnu-social
		
	[CONTROLLER][Feeds] Implement query for home feed: note-from:subscribed
This commit is contained in:
		| @@ -25,6 +25,7 @@ use App\Core\Cache; | ||||
| use App\Core\Event; | ||||
| use App\Core\Modules\Component; | ||||
| use App\Core\Router\RouteLoader; | ||||
| use App\Entity\Actor; | ||||
| use App\Entity\Note; | ||||
| use App\Util\Formatting; | ||||
| use Component\Attachment\Controller as C; | ||||
| @@ -74,7 +75,7 @@ class Attachment extends Component | ||||
|     /** | ||||
|      * Populate $note_expr with the criteria for looking for notes with attachments | ||||
|      */ | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr) | ||||
|     { | ||||
|         $include_term = str_contains($term, ':') ? explode(':', $term)[1] : $term; | ||||
|         if (Formatting::startsWith($term, ['note-types:', 'notes-incude:', 'note-filter:'])) { | ||||
|   | ||||
| @@ -27,18 +27,21 @@ use App\Core\DB\DB; | ||||
| use App\Core\Event; | ||||
| use App\Core\Modules\Component; | ||||
| use App\Entity\Actor; | ||||
| use App\Entity\Subscription; | ||||
| use App\Util\Formatting; | ||||
| use Component\Search\Util\Parser; | ||||
| use Doctrine\Common\Collections\ExpressionBuilder; | ||||
| use Doctrine\ORM\Query\Expr; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
|  | ||||
| class Feed extends Component | ||||
| { | ||||
|     public static function query(string $query, int $page, ?string $language = null): array | ||||
|     public static function query(string $query, int $page, ?string $language = null, ?Actor $actor = null): array | ||||
|     { | ||||
|         $note_criteria  = null; | ||||
|         $actor_criteria = null; | ||||
|         if (!empty($query = trim($query))) { | ||||
|             [$note_criteria, $actor_criteria] = Parser::parse($query, $language); | ||||
|             [$note_criteria, $actor_criteria] = Parser::parse($query, $language, $actor); | ||||
|         } | ||||
|         $note_qb  = DB::createQueryBuilder(); | ||||
|         $actor_qb = DB::createQueryBuilder(); | ||||
| @@ -62,34 +65,45 @@ class Feed extends Component | ||||
|         return ['notes' => $notes ?? null, 'actors' => $actors ?? null]; | ||||
|     } | ||||
|  | ||||
|     public function onSearchQueryAddJoins(QueryBuilder &$note_qb, QueryBuilder &$actor_qb) | ||||
|     { | ||||
|         $note_qb->leftJoin(Subscription::class, 'subscription', Expr\Join::WITH, 'note.actor_id = subscription.subscribed'); | ||||
|         return Event::next; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert $term to $note_expr and $actor_expr, search criteria. Handles searching for text | ||||
|      * notes, for different types of actors and for the content of text notes | ||||
|      */ | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr) | ||||
|     { | ||||
|         if (str_contains($term, ':')) { | ||||
|             $term = explode(':', $term); | ||||
|             if (Formatting::startsWith($term[0], 'note-')) { | ||||
|                 switch ($term[0]) { | ||||
|                     case 'note-local': | ||||
|                         $note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN)); | ||||
|                         break; | ||||
|                     case 'note-types': | ||||
|                     case 'notes-include': | ||||
|                     case 'note-filter': | ||||
|                         if (\is_null($note_expr)) { | ||||
|                             $note_expr = []; | ||||
|                         } | ||||
|                         if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) { | ||||
|                             $note_expr[] = $eb->neq('note.content', null); | ||||
|                         } else { | ||||
|                             $note_expr[] = $eb->eq('note.content', null); | ||||
|                         } | ||||
|                         break; | ||||
|                     case 'note-conversation': | ||||
|                         $note_expr = $eb->eq('note.conversation_id', (int)trim($term[1])); | ||||
|                         break; | ||||
|                 case 'note-local': | ||||
|                     $note_expr = $eb->eq('note.is_local', filter_var($term[1], \FILTER_VALIDATE_BOOLEAN)); | ||||
|                     break; | ||||
|                 case 'note-types': | ||||
|                 case 'notes-include': | ||||
|                 case 'note-filter': | ||||
|                     if (\is_null($note_expr)) { | ||||
|                         $note_expr = []; | ||||
|                     } | ||||
|                     if (array_intersect(explode(',', $term[1]), ['text', 'words']) !== []) { | ||||
|                         $note_expr[] = $eb->neq('note.content', null); | ||||
|                     } else { | ||||
|                         $note_expr[] = $eb->eq('note.content', null); | ||||
|                     } | ||||
|                     break; | ||||
|                 case 'note-conversation': | ||||
|                     $note_expr = $eb->eq('note.conversation_id', (int) trim($term[1])); | ||||
|                     break; | ||||
|                 case 'note-from': | ||||
|                     if ($term[1] === 'subscribed') { | ||||
|                         $note_expr = $eb->eq('subscription.subscriber', $actor->getId()); | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|             } elseif (Formatting::startsWith($term, 'actor-')) { | ||||
|                 switch ($term[0]) { | ||||
|   | ||||
| @@ -31,10 +31,10 @@ use App\Util\Formatting; | ||||
| use App\Util\Functional as GSF; | ||||
| use Component\Language\Controller as C; | ||||
| use Doctrine\Common\Collections\ExpressionBuilder; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Doctrine\ORM\Query\Expr; | ||||
| use Doctrine\ORM\QueryBuilder; | ||||
| use Functional as F; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| class Language extends Component | ||||
| { | ||||
| @@ -46,7 +46,9 @@ class Language extends Component | ||||
|  | ||||
|     public function onFilterNoteList(?Actor $actor, array &$notes, Request $request) | ||||
|     { | ||||
|         if (\is_null($actor)) return Event::next; | ||||
|         if (\is_null($actor)) { | ||||
|             return Event::next; | ||||
|         } | ||||
|         $notes = F\select( | ||||
|             $notes, | ||||
|             fn (Note $n) => \in_array($n->getLanguageId(), ActorLanguage::getActorRelatedLanguagesIds($actor)), | ||||
| @@ -58,7 +60,7 @@ class Language extends Component | ||||
|     /** | ||||
|      * Populate $note_expr or $actor_expr with an expression to match a language | ||||
|      */ | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr) | ||||
|     { | ||||
|         $search_term = str_contains($term, ':') ? explode(':', $term)[1] : $term; | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ declare(strict_types = 1); | ||||
| namespace Component\Search\Util; | ||||
|  | ||||
| use App\Core\Event; | ||||
| use App\Entity\Actor; | ||||
| use App\Util\Exception\ServerException; | ||||
| use Doctrine\Common\Collections\Criteria; | ||||
|  | ||||
| @@ -52,7 +53,7 @@ abstract class Parser | ||||
|      * | ||||
|      * @return Criteria[] | ||||
|      */ | ||||
|     public static function parse(string $input, ?string $language = null, int $level = 0): array | ||||
|     public static function parse(string $input, ?string $language = null, ?Actor $actor = null, int $level = 0): array | ||||
|     { | ||||
|         if ($level === 0) { | ||||
|             $input = trim(preg_replace(['/\s+/', '/\s+AND\s+/', '/\s+OR\s+/'], [' ', '&', '|'], $input), ' |&'); | ||||
| @@ -77,7 +78,7 @@ abstract class Parser | ||||
|                     $term      = mb_substr($input, $left, $end ? null : $right - $left); | ||||
|                     $note_res  = null; | ||||
|                     $actor_res = null; | ||||
|                     Event::handle('SearchCreateExpression', [$eb, $term, $language, &$note_res, &$actor_res]); | ||||
|                     Event::handle('SearchCreateExpression', [$eb, $term, $language, $actor, &$note_res, &$actor_res]); | ||||
|                     if (\is_null($note_res) && \is_null($actor_res)) { | ||||
|                         throw new ServerException("No one claimed responsibility for a match term: {$term}"); | ||||
|                     } | ||||
|   | ||||
| @@ -155,7 +155,7 @@ class Tag extends Component | ||||
|      * | ||||
|      * $term /^(note|tag|people|actor)/ means we want to match only either a note or an actor | ||||
|      */ | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, &$note_expr, &$actor_expr): bool | ||||
|     public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, ?string $language, ?Actor $actor, &$note_expr, &$actor_expr) | ||||
|     { | ||||
|         $search_term       = str_contains($term, ':#') ? explode(':', $term)[1] : $term; | ||||
|         $canon_search_term = self::canonicalTag($search_term, $language); | ||||
|   | ||||
| @@ -74,9 +74,10 @@ class Feeds extends FeedController | ||||
|     public function home(Request $request): array | ||||
|     { | ||||
|         $data = Feed::query( | ||||
|             query: 'from:subscribed-actors OR from:subscribed-groups', | ||||
|             query: 'note-from:subscribed', | ||||
|             page: $this->int('p'), | ||||
|             language: Common::actor()?->getTopLanguage()?->getLocale(), | ||||
|             actor: Common::actor(), | ||||
|         ); | ||||
|         return [ | ||||
|             '_template'     => 'feed/feed.html.twig', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user