Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • AlphaController
  • CacheManager
  • Create
  • CreateArticle
  • Detail
  • Edit
  • EditArticle
  • EditDEnum
  • EditTags
  • GenSecureQueryStrings
  • Install
  • ListAll
  • ListBusinessObjects
  • ListDEnums
  • ListSequences
  • Login
  • Logout
  • PreviewArticle
  • Search
  • TagManager
  • ViewArticle
  • ViewArticleFile
  • ViewArticlePDF
  • ViewArticlePrint
  • ViewArticleTitle
  • ViewAttachment
  • ViewExcel
  • ViewFeed
  • ViewImage
  • ViewLog
  • ViewMetrics
  • ViewRecordSelector
  • ViewTestResults

Interfaces

  • AlphaControllerInterface
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: // include the config file
  4: if(!isset($config)) {
  5:     require_once '../util/AlphaConfig.inc';
  6:     $config = AlphaConfig::getInstance();
  7:     
  8:     require_once $config->get('app.root').'alpha/util/AlphaAutoLoader.inc';
  9: }
 10: 
 11: /**
 12:  * 
 13:  * Generic tag-based search engine controller
 14:  * 
 15:  * @package alpha::controller
 16:  * @since 1.0
 17:  * @author John Collins <dev@alphaframework.org>
 18:  * @version $Id: Search.php 1566 2012-08-05 19:56:14Z alphadevx $
 19:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 20:  * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).  
 21:  * All rights reserved.
 22:  * 
 23:  * <pre>
 24:  * Redistribution and use in source and binary forms, with or 
 25:  * without modification, are permitted provided that the 
 26:  * following conditions are met:
 27:  * 
 28:  * * Redistributions of source code must retain the above 
 29:  *   copyright notice, this list of conditions and the 
 30:  *   following disclaimer.
 31:  * * Redistributions in binary form must reproduce the above 
 32:  *   copyright notice, this list of conditions and the 
 33:  *   following disclaimer in the documentation and/or other 
 34:  *   materials provided with the distribution.
 35:  * * Neither the name of the Alpha Framework nor the names 
 36:  *   of its contributors may be used to endorse or promote 
 37:  *   products derived from this software without specific 
 38:  *   prior written permission.
 39:  *   
 40:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 41:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 42:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 43:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 44:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 45:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 46:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 47:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 48:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 49:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 50:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 51:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 52:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 53:  * </pre>
 54:  *  
 55:  */
 56: class Search extends AlphaController implements AlphaControllerInterface {
 57:     /**
 58:      * Trace logger
 59:      * 
 60:      * @var Logger
 61:      * @since 1.0
 62:      */
 63:     private static $logger = null;
 64:     
 65:     /**
 66:      * The start number for list pageination
 67:      * 
 68:      * @var integer
 69:      * @since 1.0
 70:      */
 71:     protected $startPoint;
 72:     
 73:     /**
 74:      * The result count from the search
 75:      * 
 76:      * @var integer
 77:      * @since 1.0
 78:      */
 79:     private $resultCount = 0;
 80:     
 81:     /**
 82:      * The search query supplied
 83:      * 
 84:      * @var string
 85:      * @since 1.0
 86:      */
 87:     private $query;
 88:     
 89:     /**
 90:      * constructor to set up the object
 91:      * 
 92:      * @param string $visibility The name of the rights group that can access this controller.
 93:      * @since 1.0
 94:      */
 95:     public function __construct($visibility='Public') {
 96:         self::$logger = new Logger('Search');
 97:         self::$logger->debug('>>__construct(visibility=['.$visibility.'])');
 98:         
 99:         global $config;
100:         
101:         // ensure that the super class constructor is called, indicating the rights group
102:         parent::__construct($visibility);
103:         
104:         self::$logger->debug('<<__construct');
105:     }
106:     
107:     /**
108:      * Handle GET requests
109:      * 
110:      * @param array $params
111:      * @since 1.0
112:      * @throws IllegalArguementException
113:      */
114:     public function doGET($params) {
115:         self::$logger->debug('>>doGET($params=['.var_export($params, true).'])');
116:         
117:         if (isset($params['start']) ? $this->startPoint = $params['start']: $this->startPoint = 0);
118:         
119:         global $config;
120:         
121:         $KPI = new AlphaKPI('search');
122:         
123:         if(isset($params['q'])) {
124:             
125:             $this->query = $params['q'];
126:             
127:             // replace any %20 on the URL with spaces
128:             $params['q'] = str_replace('%20', ' ', $params['q']);
129:             
130:             $this->setTitle('Search results - '.$params['q']);
131:             echo AlphaView::displayPageHead($this);
132:             
133:             // log the user's search query in a log file
134:             $log = new LogFile($config->get('app.file.store.dir').'logs/search.log');       
135:             $log->writeLine(array($params['q'], date('Y-m-d H:i:s'), $_SERVER['HTTP_USER_AGENT'], $_SERVER['REMOTE_ADDR']));
136:             
137:             $KPI->logStep('log search query');
138:             
139:             // if a BO name is provided, only search tags on that class, otherwise search all BOs
140:             if(isset($params['bo']))
141:                 $BOs = array($params['bo']);
142:             else            
143:                 $BOs = AlphaDAO::getBOClassNames();
144:             
145:             try {
146:                 foreach($BOs as $BO) {
147:                     AlphaDAO::loadClassDef($BO);
148:                     $temp = new $BO;
149:                     
150:                     if($temp->isTagged()) {
151:                         $BOIDs = $this->doTagSearch($params['q'], $BO);
152:                         
153:                         $this->resultCount += count($BOIDs);
154:                         
155:                         $KPI->logStep('built array of matching OIDs');
156:                         
157:                         // sort the BO IDs based on tag frequency weight                        
158:                         arsort($BOIDs);                     
159:                         
160:                         // render the list view for each BO
161:                         $this->renderResultList($BOIDs, $BO, $params['q']);                     
162:                         
163:                         AlphaDAO::disconnect();
164:                     }
165:                 }
166:             }catch(Exception $e) {
167:                 self::$logger->error($e->getMessage());
168:                 throw new IllegalArguementException('Error occured while searching for: ['.$params['q'].']');               
169:             }
170:         }else{
171:             $this->setTitle('Search results');          
172:             echo AlphaView::displayPageHead($this);
173:             self::$logger->debug('No search query provided!');
174:         }       
175:         
176:         echo AlphaView::displayPageFoot($this);
177:         
178:         $KPI->log();
179:         
180:         self::$logger->debug('<<doGET');
181:     }
182:     
183:     /**
184:      * Searches for tags on the given BO type matching the query provided
185:      * 
186:      * @param string $query
187:      * @param string $BOName
188:      * @param array $BOIDs
189:      * @return array
190:      * @since 1.1
191:      */
192:     protected function doTagSearch($query, $BOName, $BOIDs=array()) {
193:         // explode the user's query into a set of tokenized transient TagObjects
194:         $queryTags = TagObject::tokenize($query, '', '', false);            
195:         $matchingTags = array();
196:                         
197:         // load TagObjects from the DB where content equals the content of one of our transient TagObjects
198:         foreach($queryTags as $queryTag) {
199:             $tags = $queryTag->loadAllByAttribute('content', $queryTag->get('content'));
200:             $matchingTags = array_merge($matchingTags, $tags);
201:         }
202:                         
203:         self::$logger->debug('There are ['.count($matchingTags).'] TagObjects matching the query ['.$query.']');
204:                         
205:         /*
206:          * Build an array of BOs for the matching tags from the DB:
207:          * array key = BO ID
208:          * array value = weight (the amount of tags matching the BO)
209:          */
210:         foreach($matchingTags as $tag) {                            
211:             if($tag->get('taggedClass') == $BOName) {
212:                 
213:                 if(isset($BOIDs[$tag->get('taggedOID')])) {
214:                     // increment the weight if the same BO is tagged more than once
215:                     $weight = intval($BOIDs[$tag->get('taggedOID')]) + 1;
216:                     $BOIDs[$tag->get('taggedOID')] = $weight;                                   
217:                 }else{
218:                     $BOIDs[$tag->get('taggedOID')] = 1;                                 
219:                 }
220:                 
221:                 self::$logger->debug('Found BO ['.$tag->get('taggedOID').'] has weight ['.$BOIDs[$tag->get('taggedOID')].']');                              
222:             }
223:         }
224:         
225:         return $BOIDs;
226:     }
227:     
228:     /**
229:      * Renders the search result list
230:      * 
231:      * @param array $BOIDs
232:      * @param string $BO
233:      * @param string $query
234:      * @param bool $showTags
235:      * @param bool $showScore
236:      * @since 1.0
237:      */
238:     protected function renderResultList($BOIDs, $BO, $query='', $showTags=true, $showScore=false) {
239:         global $config;
240:         
241:         // used to track when our pagination range ends
242:         $end = ($this->startPoint+$config->get('app.list.page.amount'));
243:         // used to track how many results have been displayed or skipped from the pagination range
244:         $displayedCount = 0;
245:         
246:         if(!empty($query))
247:             echo '<h2>Displaying results for &quot;'.$query.'&quot;</h2>';
248:             
249:         foreach(array_keys($BOIDs) as $oid) {
250:             try {
251:                 // if we have reached the end of the pagination range then break out
252:                 if($displayedCount == $end)
253:                     break;
254:                             
255:                 // if our display count is >= the start but < the end...
256:                 if($displayedCount >= $this->startPoint) {
257:                     $temp = new $BO;
258:                     $temp->load($oid);
259:                     
260:                     if($temp instanceof ArticleObject && $temp->get('published') == false){
261:                         $this->resultCount--;
262:                     }else{          
263:                         $view = AlphaView::getInstance($temp);
264:                         echo $view->listView();
265: 
266:                         if($showTags) {
267:                             $tags = $temp->getPropObject('tags')->getRelatedObjects();
268:                         
269:                             if(count($tags) > 0) {
270:                                 echo '<p>Tags: ';
271:                                                 
272:                                 $queryTerms = explode(' ', strtolower($query));
273:                                                 
274:                                 foreach($tags as $tag) {
275:                                     echo (in_array($tag->get('content'), $queryTerms) ? '<strong>'.$tag->get('content').' </strong>' : $tag->get('content').' ');
276:                                 }
277:                                             
278:                                 echo '</p>';
279:                             }
280:                         }
281:                         
282:                         if($showScore) {
283:                             $score = $BOIDs[$oid];
284:                             
285:                             echo '<p>Score: <strong>'.$score.'</strong></p>';
286:                         }
287:                     }
288:                 }
289:                             
290:                 $displayedCount++;
291:             }catch(BONotFoundException $e) {
292:                 self::$logger->warn('Orpaned TagObject detected pointing to a non-existant BO of OID ['.$oid.'] and type ['.$BO.'].');
293:             }
294:         }
295:     }
296:     
297:     /**
298:      * Handle POST requests
299:      * 
300:      * @param array $params
301:      * @since 1.0
302:      */
303:     public function doPOST($params) {
304:         self::$logger->debug('>>doPOST($params=['.var_export($params, true).'])');
305:         
306:         self::$logger->debug('<<doPOST');
307:     }
308:     
309:     /**
310:      * Displays a search form on the top of the page
311:      * 
312:      * @return string
313:      * @since 1.0
314:      */
315:     public function after_displayPageHead_callback() {
316:         global $config;
317:         
318:         $html = '<div align="center"><form method="GET" id="search_form" onsubmit="document.location = \''.$config->get('app.url').'search/q/\'+document.getElementById(\'q\').value; return false;">';
319:         $html .= 'Search for: <input type="text" size="80" name="q" id="q"/>&nbsp;';        
320:         $button = new Button('document.location = \''.$config->get('app.url').'search/q/\'+document.getElementById(\'q\').value', 'Search', 'searchButton');
321:         $html .= $button->render();
322:         $html .= '</form></div>';
323:         
324:         return $html;
325:     }
326:     
327:     /**
328:      * Method to display the page footer with pageination links
329:      * 
330:      * @return string
331:      * @since 1.0
332:      */
333:     public function before_displayPageFoot_callback() {
334:         $html = $this->renderPageLinks();
335:         
336:         $html .= '<br>';
337:         
338:         return $html;
339:     }
340:     
341:     /**
342:      * Method for rendering the pagination links
343:      * 
344:      * @return string
345:      * @since 1.0
346:      */
347:     protected function renderPageLinks() {
348:         global $config;
349:         
350:         $html = '';
351:         
352:         $end = ($this->startPoint+$config->get('app.list.page.amount'));
353:         
354:         if($end > $this->resultCount)
355:             $end = $this->resultCount;
356:         
357:         if($this->resultCount > 0) {
358:             $html .= '<p align="center">Displaying '.($this->startPoint+1).' to '.$end.' of <strong>'.$this->resultCount.'</strong>.&nbsp;&nbsp;';
359:         }else{
360:             if(!empty($this->query))
361:                 $html .= AlphaView::displayUpdateMessage('There were no search results for your query.');
362:         }   
363:                 
364:         if ($this->startPoint > 0) {
365:             // handle secure URLs
366:             if(isset($_GET['tk']))
367:                 $html .= '<a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.($this->startPoint-$config->get('app.list.page.amount'))).'">&lt;&lt;-Previous</a>&nbsp;&nbsp;';
368:             else
369:                 $html .= '<a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.($this->startPoint-$config->get('app.list.page.amount')).'">&lt;&lt;-Previous</a>&nbsp;&nbsp;';
370:         }elseif($this->resultCount > $config->get('app.list.page.amount')){
371:             $html .= '&lt;&lt;-Previous&nbsp;&nbsp;';
372:         }
373:         $page = 1;
374:         for ($i = 0; $i < $this->resultCount; $i+=$config->get('app.list.page.amount')) {
375:             if($i != $this->startPoint) {
376:                 // handle secure URLs
377:                 if(isset($_GET['tk']))
378:                     $html .= '&nbsp;<a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.$i).'">'.$page.'</a>&nbsp;';
379:                 else
380:                     $html .= '&nbsp;<a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.$i.'">'.$page.'</a>&nbsp;';
381:             }elseif($this->resultCount > $config->get('app.list.page.amount')){
382:                 $html .= '&nbsp;'.$page.'&nbsp;';
383:             }
384:             $page++;
385:         }
386:         if ($this->resultCount > $end) {
387:             // handle secure URLs
388:             if(isset($_GET['tk']))
389:                 $html .= '&nbsp;&nbsp;<a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.($this->startPoint+$config->get('app.list.page.amount'))).'">Next-&gt;&gt;</a>';
390:             else
391:                 $html .= '&nbsp;&nbsp;<a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.($this->startPoint+$config->get('app.list.page.amount')).'">Next-&gt;&gt;</a>';
392:         }elseif($this->resultCount > $config->get('app.list.page.amount')){
393:             $html .= '&nbsp;&nbsp;Next-&gt;&gt;';
394:         }
395:         $html .= '</p>';
396:         
397:         return $html;
398:     }
399: }
400: 
401: // now build the new controller
402: if(basename($_SERVER['PHP_SELF']) == 'Search.php') {
403:     $controller = new Search();
404:     
405:     if(!empty($_POST)) {            
406:         $controller->doPOST($_REQUEST);
407:     }else{
408:         $controller->doGET($_GET);
409:     }
410: }
411: 
412: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0