DaPortal
<?php //$Id$
							//Copyright (c) 2011-2016 Pierre Pronchery <khorben@defora.org>
							//This file is part of DeforaOS Web DaPortal
							//
							//This program is free software: you can redistribute it and/or modify
							//it under the terms of the GNU General Public License as published by
							//the Free Software Foundation, version 3 of the License.
							//
							//This program is distributed in the hope that it will be useful,
							//but WITHOUT ANY WARRANTY; without even the implied warranty of
							//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							//GNU General Public License for more details.
							//
							//You should have received a copy of the GNU General Public License
							//along with this program.  If not, see <http://www.gnu.org/licenses/>.
							//SearchModule
							class SearchModule extends Module
							{
								//public
								//methods
								//useful
								//SearchModule::call
								public function call(Engine $engine, Request $request, $internal = 0)
								{
									if(($action = $request->getAction()) === FALSE)
										$action = 'default';
									if($internal)
										switch($action)
										{
											case 'actions':
												return $this->actions($engine,
														$request);
											default:
												return FALSE;
										}
									switch($action)
									{
										case 'advanced':
										case 'default':
										case 'widget':
											$action = 'call'.$action;
											return $this->$action($engine, $request);
										default:
											return new ErrorResponse(_('Invalid action'),
												Response::$CODE_ENOENT);
									}
								}
								//protected
								//properties
								protected $limit = FALSE;
								//queries
								static protected $query = 'SELECT content_id AS id, timestamp AS date,
									module_id, module, user_id, username, group_id, groupname,
									title, content, enabled, public
									FROM daportal_content_public
									WHERE 1=1';
								//IN:	module_id
								static protected $query_module = 'SELECT content_id AS id,
									timestamp AS date, module_id, module, user_id, username,
									group_id, groupname, title, content, enabled, public
									FROM daportal_content_public
									WHERE module_id=:module_id';
								//methods
								//accessors
								//SearchModule::getLimit
								protected function getLimit(Engine $engine, Request $request = NULL)
								{
									if($request !== NULL
											&& ($limit = $request->get('limit')) !== FALSE
											&& is_numeric($limit)
											&& $limit > 0 && $limit <= 500)
										return $limit;
									if($this->limit === FALSE
											&& ($limit = $this->configGet('limit'))
												!== FALSE && is_numeric($limit)
											&& $limit > 0 && $limit <= 500)
										$this->limit = $limit;
									else
										$this->limit = 20;
									return $this->limit;
								}
								//SearchModule::getPage
								protected function getPage(Engine $engine, Request $request = NULL)
								{
									if($request !== NULL && ($p = $request->get('page')) !== FALSE
											&& is_numeric($p) && $p >= 1)
										return $p;
									return 1;
								}
								//useful
								//SearchModule::actions
								protected function actions(Engine $engine, Request $request)
								{
									if($request->get('admin') !== FALSE)
										return FALSE;
									if($request->get('user') !== FALSE
											|| $request->get('group') !== FALSE)
										return FALSE;
									$ret = array();
									//advanced search
									$ret[] = $this->helperAction($engine, 'add',
											$r = $this->getRequest('advanced'),
											_('Advanced search'));
									return $ret;
								}
								//SearchModule::appendResult
								protected function appendResult(Engine $engine, &$view, &$res)
								{
									$row = $view->append('row');
									$row->set('title', $res['title']);
									$row->set('username', $res['username']);
									$row->set('date', $res['date']);
									if(($module = Module::load($engine, $res['module'])) === FALSE)
										return;
									if(($content = $module->getContent($engine, $res['id']))
											=== FALSE)
										return;
									$row->set('preview', $content->preview($engine));
								}
								//calls
								//SearchModule::callDefault
								protected function callDefault(Engine $engine, Request $request)
								{
									$case = FALSE;
									$limit = $this->getLimit($engine, $request);
									$page = $this->pageSearch($engine, $request, FALSE, $limit);
									if(($q = $request->get('q')) === FALSE || strlen($q) == 0)
										return new PageResponse($page);
									$time = microtime(TRUE);
									if(($res = $this->query($engine, $q, $case, TRUE, TRUE))
											=== FALSE)
									{
										$error = _('Unable to search');
										$page->append('dialog', array('type' => 'error',
												'text' => $error));
										return new PageResponse($page,
											Response::$CODE_EUNKNOWN);
									}
									else if($res === TRUE)
										return new PageResponse($page);
									$time = ceil((microtime(TRUE) - $time) * 1000);
									return $this->helperResults($engine, $request, $page, $res,
											$limit, $time);
								}
								//SearchModule::callAdvanced
								protected function callAdvanced(Engine $engine, Request $request)
								{
									$case = $request->get('case') ? '1' : '0';
									$limit = $this->getLimit($engine, $request);
									$page = $this->pageSearch($engine, $request, TRUE, $limit);
									if(($q = $request->get('q')) === FALSE || strlen($q) == 0)
										return new PageResponse($page);
									$intitle = $request->get('intitle');
									$incontent = $request->get('incontent');
									if($intitle === FALSE && $incontent === FALSE)
										$intitle = $incontent = TRUE;
									$module = $request->get('inmodule');
									$time = microtime(TRUE);
									if(($res = $this->query($engine, $q, $case, $intitle,
											$incontent, FALSE, $module)) === FALSE)
									{
										$error = _('Unable to search');
										$page->append('dialog', array('type' => 'error',
												'text' => $error));
										return new PageResponse($page,
											Response::$CODE_EUNKNOWN);
									}
									else if($res === TRUE)
										return new PageResponse($page);
									$time = ceil((microtime(TRUE) - $time) * 1000);
									return $this->helperResults($engine, $request, $page, $res,
											$limit, $time);
								}
								//SearchModule::callWidget
								protected function callWidget(Engine $engine, Request $request)
								{
									$form = new PageElement('form', array('idempotent' => TRUE));
									$form->set('request', $this->getRequest());
									$hbox = $form->append('hbox');
									$entry = $hbox->append('entry', array('name' => 'q',
											'text' => '', 'placeholder' => _('Search...')));
									$button = $hbox->append('button', array('stock' => 'search',
												'type' => 'submit',
												'text' => _('Search'),
												'autohide' => TRUE));
									return new PageResponse($form);
								}
								//helpers
								//SearchModule::helperAction
								protected function helperAction(Engine $engine, $stock,
										Request $request, $text)
								{
									$icon = new PageElement('image', array('stock' => $stock));
									$link = new PageElement('link', array('request' => $request,
											'text' => $text));
									return new PageElement('row', array('icon' => $icon,
											'label' => $link));
								}
								//SearchModule::helperPaging
								protected function helperPaging(Engine $engine, Request $request,
										PageElement $page, $limit, $pcnt, $pcur)
								{
									//XXX copied from ContentModule
									if($pcnt <= $limit)
										return;
									$pcnt = ceil($pcnt / $limit);
									$args = $request->getParameters();
									unset($args['page']);
									$r = $this->getRequest($request->getAction(), $args);
									$form = $page->append('form', array('idempotent' => TRUE,
											'request' => $r));
									$hbox = $form->append('hbox');
									//first page
									$hbox->append('link', array('stock' => 'gotofirst',
											'request' => $r, 'text' => ''));
									//previous page
									$args['page'] = max(1, $pcur - 1);
									$r = $this->getRequest($request->getAction(), $args);
									$hbox->append('link', array('stock' => 'previous',
											'request' => $r, 'text' => ''));
									//entry
									$hbox->append('entry', array('name' => 'page', 'width' => '4',
											'value' => $pcur));
									$hbox->append('label', array('text' => " / $pcnt"));
									//next page
									$args['page'] = min($pcur + 1, $pcnt);
									$r = $this->getRequest($request->getAction(), $args);
									$hbox->append('link', array('stock' => 'next',
											'request' => $r, 'text' => ''));
									//last page
									$args['page'] = $pcnt;
									$r = $this->getRequest($request->getAction(), $args);
									$hbox->append('link', array('stock' => 'gotolast',
											'request' => $r, 'text' => ''));
								}
								//SearchModule::helperResults
								protected function helperResults(Engine $engine, Request $request,
										PageElement $page, $res, $limit, $time = FALSE)
								{
									$p = $this->getPage($engine, $request);
									$count = count($res);
									$columns = array('title' => _('Title'),
										'username' => _('Username'), 'date' => _('Date'),
										'preview' => _('Preview'));
									if(($offset = ($p - 1) * $limit) >= $count)
									{
										$p = 1;
										$offset = 0;
									}
									$results = $page->append('vbox');
									$results->set('id', 'search_results');
									$label = $results->append('label');
									$text = $count.' result(s)';
									if($time !== FALSE)
										$text .= ' in '.$time.' ms';
									$label->set('text', $text);
									$view = $page->append('treeview', array('view' => 'preview',
												'columns' => $columns));
									$res->seek($offset);
									for($i = 0; $i++ < $limit && $res->valid(); $res->next())
										if(($r = $res->current()) === FALSE)
											break;
										else
											$this->appendResult($engine, $view, $r);
									//output paging information
									$this->helperPaging($engine, $request, $page, $limit, $count,
											$p);
									return new PageResponse($page);
								}
								//SearchModule::pageSearch
								protected function pageSearch(Engine $engine, Request $request,
										$advanced = FALSE, $limit = FALSE)
								{
									$q = $request->get('q');
									$title = $q ? _('Search results') : _('Search');
									$args = $q ? array('q' => $q) : FALSE;
									$case = $request->get('case') ? '1' : '0';
									$page = new Page(array('title' => $title));
									$page->append('title', array('stock' => 'search',
											'text' => $title));
									$form = $page->append('form');
									$r = $this->getRequest($advanced ? 'advanced' : FALSE);
									$form->set('request', $r);
									$entry = $form->append('entry');
									$entry->set('text', _('Search query: '));
									$entry->set('name', 'q');
									$entry->set('value', $request->get('q'));
									if($advanced)
										$this->_pageSearchAdvanced($engine, $request, $form,
												$limit, $case);
									$button = $form->append('button', array('stock' => 'search',
												'type' => 'submit',
												'text' => _('Search')));
									$link = $page->append('link');
									if($advanced)
									{
										$link->set('stock', 'remove');
										$link->set('text', _('Simpler search...'));
										$link->set('request', $this->getRequest(FALSE, $args));
									}
									else
									{
										$link->set('stock', 'add');
										$link->set('text', _('Advanced search...'));
										$link->set('request', $this->getRequest('advanced',
												$args));
									}
									return $page;
								}
								private function _pageSearchAdvanced(Engine $engine, Request $request,
										PageElement $form, $limit, $case)
								{
									$hbox = $form->append('hbox');
									$label = $hbox->append('label');
									$label->set('text', _('Search in: '));
									$checkbox = $hbox->append('checkbox');
									$checkbox->set('name', 'intitle');
									$checkbox->set('text', _('titles'));
									$checkbox->set('value', $request->get('intitle'));
									$checkbox = $hbox->append('checkbox');
									$checkbox->set('name', 'incontent');
									$checkbox->set('text', _('content'));
									$checkbox->set('value', $request->get('incontent'));
									//modules
									$list = $engine->getModules();
									$modules = array();
									foreach($list as $m)
									{
										$module = Module::load($engine, $m);
										if($module instanceof ContentModule)
											$modules[] = $m;
									}
									if(count($modules))
									{
										$hbox->append('label', array('text' => _('module: ')));
										$value = $request->get('inmodule');
										$combobox = $hbox->append('combobox', array(
												'name' => 'inmodule',
												'value' => $value));
										$combobox->append('label', array('text' => _('Any')));
										asort($modules);
										foreach($modules as $m)
											$combobox->append('label', array(
													'text' => ucfirst($m),
													'value' => $m));
									}
									$hbox = $form->append('hbox');
									$radio = $hbox->append('radiobutton', array('name' => 'case',
											'value' => $case));
									$radio->append('label', array('text' => _('Case-insensitive'),
											'value' => '0'));
									$radio->append('label', array('text' => _('Case-sensitive'),
											'value' => '1'));
									$hbox = $form->append('hbox');
									$combobox = $hbox->append('combobox', array(
											'name' => 'limit', 'value' => $limit,
											'text' => _('Results per page:')));
									foreach(array(10, 20, 50, 100) as $i)
										$combobox->append('label', array('text' => $i,
												'value' => $i));
									$button = $form->append('button', array('type' => 'reset',
											'text' => _('Reset')));
								}
								//SearchModule::query
								protected function query(Engine $engine, $string, $case, $intitle,
										$incontent, $user = FALSE, $module = FALSE)
								{
									global $config;
									$db = $engine->getDatabase();
									$module = ($module !== FALSE) ? Module::load($engine, $module)
										: FALSE;
									$query = ($module !== FALSE) ? static::$query_module.' AND (0=1'
										: static::$query.' AND (0=1';
									$regexp = $this->configGet('regexp');
									$func = $regexp ? 'regexp' : 'like';
									$wildcard = $regexp ? '' : '%';
									$string = str_replace('\\', '\\\\', trim($string));
									if($string == '')
										return TRUE;
									$q = explode(' ', $string);
									$args = ($module !== FALSE)
										? array('module_id' => $module->getID()) : array();
									$i = 0;
									if($intitle && count($q))
										foreach($q as $r)
										{
											$query .= $case
												? ' OR title '.$db->$func()." :arg$i"
												: ' OR LOWER(title) '
													.$db->$func()." LOWER(:arg$i)";
											if($func == 'like')
											{
												$query .= ' ESCAPE :escape';
												$args['escape'] = '\\';
											}
											$args['arg'.$i++] = $wildcard.$r.$wildcard;
										}
									if($incontent && count($q))
										foreach($q as $r)
										{
											$query .= $case
												? ' OR content '.$db->$func()." :arg$i"
												: ' OR LOWER(content) '
													.$db->$func()." LOWER(:arg$i)";
											if($func == 'like')
											{
												$query .= ' ESCAPE :escape';
												$args['escape'] = '\\';
											}
											$args['arg'.$i++] = $wildcard.$r.$wildcard;
										}
									$query .= ') ORDER BY timestamp DESC';
									//paging
									if(($res = $db->query($engine, $query, $args)) === FALSE)
										return $engine->log(LOG_ERR, 'Unable to search');
									return $res;
								}
							}
							?>
							