public static function getFilterFieldValue($filter, $name) { $field = $filter[$name]; $value = $field->getValue(); $types = $filter->getFields(); $type = $types[$field->getName()]; switch ($type) { case 'Enum': return $value; case 'Boolean': return aBlogToolkit::getValueForId($field, $value); case 'ForeignKey': case 'ManyKey': if (is_array($value)) { $values = array(); foreach ($value as $v) { $values[] = aBlogToolkit::getValueForId($field, $v); } } else { $values = aBlogToolkit::getValueForId($field, $value); } return $values; case 'Text': case 'Number': return $value['text']; } }
public function preExecute() { parent::preExecute(); $request = $this->getRequest(); $this->info = aBlogToolkit::filterForEngine($this->getFilterForEngineParams()); if (sfConfig::get('app_aBlog_use_bundled_assets', true)) { $this->getResponse()->addJavascript('/apostropheBlogPlugin/js/aBlog.js'); } }
public function executeSearch(sfWebRequest $request) { return aBlogToolkit::searchBody($this, '@a_blog_redirect', 'aBlogPost', null, $request); }
public function preExecute() { parent::preExecute(); $request = $this->getRequest(); $this->info = aBlogToolkit::filterForEngine($this->getFilterForEngineParams()); }
public static function searchBody($action, $slugMatch, $modelClass, $categories, sfWebRequest $request) { $now = date('YmdHis'); // create the array of pages matching the query $ajax = false; if ($request->hasParameter('term')) { $ajax = true; $q = $request->getParameter('term'); // Wildcarding is better for autocomplete $q .= '*'; } else { $q = $request->getParameter('q'); if ($request->hasParameter('x')) { // We sometimes like to use input type="image" for presentation reasons, but it generates // ugly x and y parameters with click coordinates. Get rid of those and come back. Keep // all the other stuff return $action->redirect(sfContext::getInstance()->getController()->genUrl(aUrl::addParams($request->getParameter('module') . '/' . $request->getParameter('action'), array('q' => $q, 'cat' => $request->getParameter('cat'), 'tag' => $request->getParameter('tag'), 'year' => $request->getParameter('year'), 'month' => $request->getParameter('month'), 'day' => $request->getParameter('day'))))); } } $key = strtolower(trim($q)); $key = preg_replace('/\\s+/', ' ', $key); $replacements = sfConfig::get('app_a_search_refinements', array()); if (isset($replacements[$key])) { $q = $replacements[$key]; } try { $q = "({$q}) AND slug:{$slugMatch}"; $values = aZendSearch::searchLuceneWithValues(Doctrine::getTable('aPage'), $q, aTools::getUserCulture()); } catch (Exception $e) { // Lucene search error. TODO: display it nicely if they are always safe things to display. For now: just don't crash $values = array(); } $nvalues = array(); // The truth is that Zend cannot do all of our filtering for us, especially // permissions-based. So we can do some other filtering as well, although it // would be bad not to have Zend take care of the really big cuts (if 99% are // not being prefiltered by Zend, and we have a Zend max results of 1000, then // we are reduced to working with a maximum of 10 real results). if ($request->hasParameter('cat')) { $categories = Doctrine::getTable('aCategory')->createQuery()->where('slug = ?', array($request->getParameter('cat')))->execute(array(), Doctrine::HYDRATE_ARRAY); } if (is_null($categories)) { $categoryIds = array(); } else { $categoryIds = aArray::getIds($categories); } foreach ($values as $value) { if ($ajax && count($nvalues) >= sfConfig::get('app_aBlog_autocomplete_max', 10)) { break; } // 1.5: the names under which we store columns in Zend Lucene have changed to // avoid conflict with also indexing them $info = unserialize($value->info_stored); // Do a whole bunch of filtering that can't be easily done at the Lucene level. // This is not ideal because the 1000 results we consider might not meet the // criteria and some later set of results might. For 2.0 we need to find something // that fully merges text search and other criteria without complaint // The main performance killer isn't MySQL, it's Doctrine object hydration. Just keep it light if (count($categoryIds) && !count(Doctrine::getTable('aPage')->createQuery('p')->where('p.id = ?', $info['id'])->innerJoin('p.Categories c')->select('p.id, c.id')->andWhereIn('c.id', $categoryIds)->execute(array(), Doctrine::HYDRATE_NONE))) { continue; } if ($request->hasParameter('tag') && !count(Doctrine::getTable('Tagging')->createQuery('ta')->innerJoin('ta.Tag t WITH t.name = ?', $request->getParameter('tag'))->where('ta.taggable_model = ? AND ta.taggable_id = ?', array('aPage', $info['id']))->execute(array(), Doctrine::HYDRATE_NONE))) { continue; } // Filter search results chronologically. How to do this depends on whether // we're dealing with blog or events $year = sprintf("%04d", $request->getParameter('year')); $month = sprintf("%02d", $request->getParameter('month')); $day = sprintf("%02d", $request->getParameter('day')); // This if is gross and ought to be refactored by calling a method on // the actions class which is differently implemented by the two classes if (get_class($action) === 'aEventActions') { if ($day > 0) { if (!aBlogToolkit::between("{$year}-{$month}-{$day}", $value->start_date, $value->end_date)) { continue; } } elseif ($month > 0) { if (!aBlogToolkit::between("{$year}-{$month}", substr($value->start_date, 0, 7), substr($value->end_date, 0, 7))) { continue; } } elseif ($year > 0) { if (!aBlogToolkit::between("{$year}", substr($value->start_date, 0, 4), substr($value->end_date, 0, 4))) { continue; } } } else { // We store this one real picky in a keyword so it's searchable at the lucene level if (preg_match('/^(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)/', $value->published_at, $matches)) { list($dummy, $pyear, $pmonth, $pday) = $matches; if ($year > 0) { if ($pyear != $year) { continue; } } if ($month > 0) { if ($pmonth != $month) { continue; } } if ($day > 0) { if ($pday != $day) { continue; } } } } // Regardless of the above if it ain't published yet we can't see it if ($value->published_at > $now) { continue; } if (!aPageTable::checkPrivilege('view', $info)) { continue; } $nvalue = $value; $nvalue->page_id = $info['id']; $nvalue->slug = $nvalue->slug_stored; $nvalue->title = $nvalue->title_stored; $nvalue->summary = $nvalue->summary_stored; // Virtual page slug is a named Symfony route, it wants search results to go there $nvalue->url = $action->getController()->genUrl($nvalue->slug, true); $nvalue->class = $modelClass; $nvalues[] = $nvalue; } $values = $nvalues; if ($ajax) { // We need the IDs of the blog posts, not their virtual pages $pageIds = array(); foreach ($values as $value) { $pageIds[] = $value->page_id; } $action->results = array(); if (count($pageIds)) { $infos = Doctrine::getTable($modelClass)->createQuery('p')->select('p.id, p.page_id, p.status')->whereIn('p.page_id', $pageIds)->fetchArray(); // At this point, if we're an admin, we have some posts on our list that are not actually // useful to return as AJAX results because we only care about posts that are published when // we're building up a posts slot foreach ($infos as $info) { if ($info['status'] !== 'published') { continue; } $pageIdToPostId[$info['page_id']] = $info['id']; } foreach ($values as $value) { if (isset($pageIdToPostId[$value->page_id])) { // Titles are stored as entity-escaped HTML text, so decode to meet // the expectations of autocomplete $action->results[] = array('label' => html_entity_decode($value->title, ENT_COMPAT, 'UTF-8'), 'value' => $pageIdToPostId[$value->page_id]); } } } return 'Autocomplete'; } $action->pager = new aArrayPager(null, sfConfig::get('app_a_search_results_per_page', 10)); $action->pager->setResultArray($values); $action->pager->setPage($request->getParameter('page', 1)); $action->pager->init(); $action->pagerUrl = "aBlog/search?" . http_build_query(array("q" => $q)); // setTitle takes care of escaping things $action->getResponse()->setTitle(aTools::getOptionI18n('title_prefix') . 'Search for ' . $q . aTools::getOptionI18n('title_suffix')); $action->results = $action->pager->getResults(); }