      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 1805 2014-07-27 13:57:01Z alphadevx $
     19:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
     20:  * @copyright Copyright (c) 2014, 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:             $provider = SearchProviderFactory::getInstance('SearchProviderTags');
    140:
    141:             // if a BO name is provided, only search tags on that class, otherwise search all BOs
    142:             if(isset($params['bo']))
    143:                 $results = $provider->search($params['q'], $params['bo'], $this->startPoint);
    144:             else
    145:                 $results = $provider->search($params['q'], 'all', $this->startPoint);
    146:
    147:             $this->resultCount = $provider->getNumberFound();
    148:
    149:             $KPI->logStep('search completed using SearchProviderTags provider');
    150:
    151:             $this->renderResultList($results, $params['q']);
    152:
    153:         }else{
    154:             $this->setTitle('Search results');
    155:             echo AlphaView::displayPageHead($this);
    156:             self::$logger->debug('No search query provided!');
    157:         }
    158:
    159:         echo AlphaView::displayPageFoot($this);
    160:
    161:         $KPI->log();
    162:
    163:         self::$logger->debug('<<doGET');
    164:     }
    165:
    166:     /**
    167:      * Renders the search result list
    168:      *
    169:      * @param array $results
    170:      * @param string $query
    171:      * @param bool $showTags
    172:      * @since 1.0
    173:      */
    174:     protected function renderResultList($results, $query='', $showTags=true) {
    175:         global $config;
    176:
    177:         // used to track when our pagination range ends
    178:         $end = ($this->startPoint+$config->get('app.list.page.amount'));
    179:
    180:         if(!empty($query))
    181:             echo '<h2>Displaying results for &quot;'.$query.'&quot;</h2>';
    182:
    183:         foreach($results as $bo) {
    184:
    185:             if($bo instanceof ArticleObject && $bo->get('published') == false){
    186:                 $this->resultCount--;
    187:             }else{
    188:                 $view = AlphaView::getInstance($bo);
    189:                 echo $view->listView();
    190:
    191:                 if($showTags) {
    192:                     $tags = $bo->getPropObject('tags')->getRelatedObjects();
    193:
    194:                     if(count($tags) > 0) {
    195:                         echo '<p>Tags: ';
    196:
    197:                         $queryTerms = explode(' ', mb_strtolower($query));
    198:
    199:                         foreach($tags as $tag) {
    200:                             echo (in_array($tag->get('content'), $queryTerms) ? '<strong>'.$tag->get('content').' </strong>' : $tag->get('content').' ');
    201:                         }
    202:
    203:                         echo '</p>';
    204:                     }
    205:                 }
    206:             }
    207:
    208:         }
    209:     }
    210:
    211:     /**
    212:      * Handle POST requests
    213:      *
    214:      * @param array $params
    215:      * @since 1.0
    216:      */
    217:     public function doPOST($params) {
    218:         self::$logger->debug('>>doPOST($params=['.var_export($params, true).'])');
    219:
    220:         self::$logger->debug('<<doPOST');
    221:     }
    222:
    223:     /**
    224:      * Displays a search form on the top of the page
    225:      *
    226:      * @return string
    227:      * @since 1.0
    228:      */
    229:     public function after_displayPageHead_callback() {
    230:         global $config;
    231:
    232:         $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;">';
    233:         $html .= 'Search for: <input type="text" size="80" name="q" id="q"/>&nbsp;';
    234:         $button = new Button('document.location = \''.$config->get('app.url').'search/q/\'+document.getElementById(\'q\').value', 'Search', 'searchButton');
    235:         $html .= $button->render();
    236:         $html .= '</form></div>';
    237:
    238:         return $html;
    239:     }
    240:
    241:     /**
    242:      * Method to display the page footer with pageination links
    243:      *
    244:      * @return string
    245:      * @since 1.0
    246:      */
    247:     public function before_displayPageFoot_callback() {
    248:         $html = $this->renderPageLinks();
    249:
    250:         $html .= '<br>';
    251:
    252:         return $html;
    253:     }
    254:
    255:     /**
    256:      * Method for rendering the pagination links
    257:      *
    258:      * @return string
    259:      * @since 1.0
    260:      */
    261:     protected function renderPageLinks() {
    262:         global $config;
    263:
    264:         $html = '';
    265:
    266:         $end = ($this->startPoint+$config->get('app.list.page.amount'));
    267:
    268:         if($end > $this->resultCount)
    269:             $end = $this->resultCount;
    270:
    271:         if($this->resultCount > 0) {
    272:             $html .= '<p align="center">Displaying '.($this->startPoint+1).' to '.$end.' of <strong>'.$this->resultCount.'</strong>.&nbsp;&nbsp;';
    273:         }else{
    274:             if(!empty($this->query))
    275:                 $html .= AlphaView::displayUpdateMessage('There were no search results for your query.');
    276:         }
    277:
    278:         $html .= '<ul class="pagination">';
    279:
    280:         if ($this->startPoint > 0) {
    281:             // handle secure URLs
    282:             if(isset($_GET['tk']))
    283:                 $html .= '<li><a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.($this->startPoint-$config->get('app.list.page.amount'))).'">&laquo;</a></li>';
    284:             else
    285:                 $html .= '<li><a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.($this->startPoint-$config->get('app.list.page.amount')).'">&laquo;</a></li>';
    286:         }elseif($this->resultCount > $config->get('app.list.page.amount')){
    287:             $html .= '<li class="disabled"><a href="#">&laquo;</a></li>';
    288:         }
    289:         $page = 1;
    290:         for ($i = 0; $i < $this->resultCount; $i+=$config->get('app.list.page.amount')) {
    291:             if($i != $this->startPoint) {
    292:                 // handle secure URLs
    293:                 if(isset($_GET['tk']))
    294:                     $html .= '<li><a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.$i).'">'.$page.'</a></li>';
    295:                 else
    296:                     $html .= '<li><a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.$i.'">'.$page.'</a></li>';
    297:             }elseif($this->resultCount > $config->get('app.list.page.amount')){
    298:                 $html .= '<li class="active"><a href="#">'.$page.'</a></li>';
    299:             }
    300:             $page++;
    301:         }
    302:         if ($this->resultCount > $end) {
    303:             // handle secure URLs
    304:             if(isset($_GET['tk']))
    305:                 $html .= '<li><a href="'.FrontController::generateSecureURL('act=Search&q='.$this->query.'&start='.($this->startPoint+$config->get('app.list.page.amount'))).'">Next-&gt;&gt;</a></li>';
    306:             else
    307:                 $html .= '<li><a href="'.$config->get('app.url').'search/q/'.$this->query.'/start/'.($this->startPoint+$config->get('app.list.page.amount')).'">&raquo;</a></li>';
    308:         }elseif($this->resultCount > $config->get('app.list.page.amount')){
    309:             $html .= '<li class="disabled"><a href="#">&raquo;</a></li>';
    310:         }
    311:         $html .= '</ul>';
    312:         $html .= '</p>';
    313:
    314:         return $html;
    315:     }
    316:
    317:     /**
    318:      * Get the search result count
    319:      *
    320:      * @return int
    321:      * @since 1.2.4
    322:      */
    323:     public function getResultCount() {
    324:         return $this->resultCount;
    325:     }
    326:
    327:     /**
    328:      * Set the search result count
    329:      *
    330:      * @param int $resultCount
    331:      * @since 1.2.4
    332:      */
    333:     protected function setResultCount($resultCount) {
    334:         $this->resultCount = $resultCount;
    335:     }
    336:
    337:     /**
    338:      * Get the search query
    339:      *
    340:      * @return string
    341:      * @since 1.2.4
    342:      */
    343:     public function getSearchQuery() {
    344:         return $this->query;
    345:     }
    346:
    347:     /**
    348:      * Set the search query
    349:      *
    350:      * @param string $query
    351:      * @since 1.2.4
    352:      */
    353:     protected function setSearchQuery($query) {
    354:         $this->query = $query;
    355:     }
    356: }
    357:
    358: // now build the new controller
    359: if(basename($_SERVER['PHP_SELF']) == 'Search.php') {
    360:     $controller = new Search();
    361:
    362:     if(!empty($_POST)) {
    363:         $controller->doPOST($_REQUEST);
    364:     }else{
    365:         $controller->doGET($_GET);
    366:     }
    367: }
    368:
    369: ?>