1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-16 11:44:42 +02:00

Some code cleanup in ProcessPageSearch, plus add a hook for processwire/processwire-issues#584

This commit is contained in:
Ryan Cramer
2018-05-08 08:48:52 -04:00
parent 5127be3b35
commit 2f20fe402c

View File

@@ -8,8 +8,10 @@
* For more details about how Process modules work, please see:
* /wire/core/Process.php
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
* @method string findReady($selector)
*
*/
@@ -25,8 +27,18 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
);
}
/**
* Default operator for text searches
*
*/
const defaultOperator = '%=';
/**
* Native/system sortable properties
*
* @var array
*
*/
protected $nativeSorts = array(
'relevance',
'name',
@@ -46,11 +58,36 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
'sortfield',
);
/**
* Names of all Field objects in PW
*
* @var array
*
*/
protected $fieldOptions = array();
protected $customSorts = array();
/**
* All operators where key is operator and value is description
*
* @var array
*
*/
protected $operators = array();
/**
* Items per pagination
*
* @var int
*
*/
protected $resultLimit = 25;
protected $maxLimit = 250;
/**
* Lister instance, when applicable
*
* @var null|ProcessPageLister
*
*/
protected $lister = null;
/**
@@ -61,7 +98,11 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
*
*/
protected $adminSearchMode = false;
/**
* Initialize module
*
*/
public function init() {
foreach($this->fields as $field) {
@@ -74,6 +115,12 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
parent::init();
}
/**
* Get operators used for searches, where key is operator and value is description
*
* @return array
*
*/
static public function getOperators() {
$f = __FILE__;
return array(
@@ -105,6 +152,20 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$this->operators = self::getOperators();
}
/**
* Hookable function to optionally modify selector before it is sent to $pages->find()
*
* Not applicable when Lister is handling the search/render.
*
* #pw-hooker
*
* @param string $selector Selector that will be used to find pages
* @return string Must return the selector (optionally modified)
*
*/
protected function ___findReady($selector) {
return $selector;
}
/**
* Perform an interactive search and provide a search form (default)
@@ -140,6 +201,7 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$lister->columns = $this->getDisplayFields();
return $lister->execute();
} else {
$selector = $this->findReady($selector);
$matches = $this->pages->find($selector);
return $this->render($matches, $displaySelector);
}
@@ -157,7 +219,6 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$this->fullSetup();
$selector = '';
$displaySelector = '';
$limit = $this->resultLimit;
$start = 0;
$status = 0;
@@ -314,6 +375,7 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
}
}
$selector = $this->findReady($selector);
$items = $this->pages->find($selector);
if(!$superuser && $checkEditAccess) {
@@ -395,12 +457,25 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
/**
* Render the search results
*
* @param PageArray $matches
* @param string $displaySelector
* @return string
*
*/
protected function render(PageArray $matches, $displaySelector) {
$out = '';
if($displaySelector) $this->message(sprintf($this->_n('Found %1$d page using selector: %2$s', 'Found %1$d pages using selector: %2$s', $matches->getTotal()), $matches->getTotal(), $displaySelector));
if($displaySelector) {
$this->message(
sprintf(
$this->_n('Found %1$d page using selector: %2$s', 'Found %1$d pages using selector: %2$s', $matches->getTotal()),
$matches->getTotal(),
$displaySelector
)
);
}
// determine what fields will be displayed
$display = array();
@@ -423,7 +498,10 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$class = 'show_options';
}
$out .= "\n<div id='ProcessPageSearchResults' class='$class'>" . $this->renderMatchesTable($matches, $display) . "\n</div>";
$out .=
"\n<div id='ProcessPageSearchResults' class='$class'>" .
$this->renderMatchesTable($matches, $display) .
"\n</div>";
}
return $out;
@@ -431,12 +509,22 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
/**
* Build a selector based upon interactive choices from the search form
*
* Only used by execute(), not used by executeFor()
*
* ~~~~~
* Returns array(
* 0 => $selector, // string, main selector for search
* 1 => $displaySelector, // string, selector for display purposes
* 2 => $initSelector, // string, selector for initialization in Lister (the part user cannot change)
* 3 => $defaultSelector // string default selector used by Lister (the part user can change)
* );
* ~~~~~
* @return array
*
*/
protected function buildSelector() {
$selector = ''; // for regular ProcessPageSearch
$initSelector = ''; // for Lister, non-changable part of the selector
$defaultSelector = ''; // for Lister, changeable filters
// search query text
$q = $this->input->whitelist('q');
@@ -485,7 +573,7 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$s = ''; // anything added to this will be populated to both $selector and $initSelector below
// limit results for pagination
$s = ", limit={$this->resultLimit}";
$s .= ", limit={$this->resultLimit}";
$adminRootPage = $this->wire('pages')->get($this->wire('config')->adminRootPageID);
@@ -560,8 +648,12 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$this->input->whitelist('sort', 'relevance');
if($this->input->get->sort) {
$sort = $this->sanitizer->fieldName($this->input->get->sort);
if($sort && (in_array($sort, $this->nativeSorts) || in_array($sort, $this->fieldOptions))) $this->input->whitelist('sort', $sort);
if($this->input->get->reverse) $this->input->whitelist('reverse', 1);
if($sort && (in_array($sort, $this->nativeSorts) || in_array($sort, $this->fieldOptions))) {
$this->input->whitelist('sort', $sort);
}
if($this->input->get->reverse) {
$this->input->whitelist('reverse', 1);
}
}
// template
@@ -576,6 +668,10 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
/**
* Is the given field name selectable?
*
* @param string $name
* @param int $level
* @return bool
*
*/
protected function isSelectableFieldName($name, $level = 0) {
@@ -618,12 +714,14 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$out = "\n\t<p id='wrap_search_query'>";
$out .= "\n\t<p id='wrap_search_field'>" .
$out .=
"\n\t<p id='wrap_search_field'>" .
"\n\t<label for='search_field'>" . $this->_('Search in field(s):') . "</label>" .
"\n\t<input type='text' name='field' value='" . htmlentities($this->searchFields, ENT_QUOTES) . "' />" .
"\n\t</p>";
$out .= "\n\t<p id='wrap_search_operator'>" .
$out .=
"\n\t<p id='wrap_search_operator'>" .
"\n\t<label for='search_operator'>" . $this->_('Type of search:') . "</label>" .
"\n\t<select id='search_operator' name='operator'>";
@@ -633,10 +731,12 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$out .= "\n\t\t<option$attrs value='$n'>$desc (a" . htmlentities($operator) . "b)</option>";
$n++;
}
$out .= "\n\t</select>" .
$out .=
"\n\t</select>" .
"\n\t</p>";
$out .= "\n\t<label class='ui-priority-primary' for='search_query'>" . $this->_('Search for:') . "</label>" .
$out .=
"\n\t<label class='ui-priority-primary' for='search_query'>" . $this->_('Search for:') . "</label>" .
"\n\t<input id='search_query' type='text' name='q' value='" . htmlentities($this->input->whitelist('q'), ENT_QUOTES, "UTF-8") . "' />" .
"\n\t<input type='hidden' name='show_options' value='1' />" .
"\n\t</p>";
@@ -646,7 +746,8 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$advCollapsed = true;
$out2 = "\n\t<p id='wrap_search_template'>" .
$out2 =
"\n\t<p id='wrap_search_template'>" .
"\n\t<label for='search_template'>" . $this->_('Limit to template:') . "</label>" .
"\n\t<select id='search_template' name='template'>" .
"\n\t\t<option></option>";
@@ -658,11 +759,13 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$out2 .= "\n\t<option$attrs>{$template->name}</option>";
}
$out2 .= "\n\t</select>" .
$out2 .=
"\n\t</select>" .
"\n\t</p>";
$out2.= "\n\t<p id='wrap_search_sort'>" .
$out2.=
"\n\t<p id='wrap_search_sort'>" .
"\n\t<label for='search_sort'>" . $this->_('Sort by:') . "</label>" .
"\n\t<select id='search_sort' name='sort'>";
@@ -677,35 +780,41 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$out2 .= "\n\t\t<option$attrs>$s</option>";
}
$out2 .= "\n\t</select>" .
$out2 .=
"\n\t</select>" .
"\n\t</p>";
if($sort != 'relevance') {
$reverse = $this->input->whitelist('reverse');
$out2 .= "\n\t<p id='wrap_search_options'>" .
$out2 .=
"\n\t<p id='wrap_search_options'>" .
"\n\t<label><input type='checkbox' name='reverse' value='1' " . ($reverse ? "checked='checked' " : '') . "/> " . $this->_('Reverse sort?') . "</label>" .
"\n\t</p>";
if($reverse) $advCollapsed = false;
}
$display = $this->input->whitelist('display');
$out2.= "\n\t<p id='wrap_search_display'>" .
$out2 .=
"\n\t<p id='wrap_search_display'>" .
"\n\t<label for='search_display'>" . $this->_('Display field(s):') . "</label>" .
"\n\t<input type='text' name='display' value='" . htmlentities($display, ENT_QUOTES) . "' />" .
"\n\t</p>";
if($display && $display != 'title,path') $advCollapsed = false;
/** @var InputfieldSubmit $submit */
$submit = $this->modules->get("InputfieldSubmit");
$submit->attr('name', 'submit');
$submit->attr('value', $this->_x('Search', 'submit')); // Search submit button for advanced search
$out .= "<p>" . $submit->render() . "</p>";
/** @var InputfieldForm $form */
$form = $this->modules->get("InputfieldForm");
$form->attr('id', 'ProcessPageSearchOptionsForm');
$form->method = 'get';
$form->action = './';
/** @var InputfieldMarkup $field */
$field = $this->modules->get("InputfieldMarkup");
$field->label = $this->_("Search Options");
$field->value = $out;
@@ -719,7 +828,7 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$form->add($field);
/* Remove temporarily
/* no longer in use
$field = $this->modules->get("InputfieldMarkup");
$field->id = 'ProcessPageSearchShortcuts';
$field->collapsed = true;
@@ -733,46 +842,22 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
}
protected function renderShortcuts() {
$out = '';
$links = array(
'Quick Links',
"All by creation date" => '?q=&submit=Search&display=title+path+created&sort=created&reverse=1' ,
"All by latest edit date" => '?q=&submit=Search&display=title+path+created&sort=modified&reverse=1',
"Users by creation date" => '?q=&template=user&submit=Search&operator=~%3D&display=name+email+created&sort=created&reverse=1',
'New pages by template',
);
foreach($this->templates as $template) {
// Quick links only for content with more than one page
// if($template->getNumPages() < 2) continue;
// Users get own quick link earlier, others are rather irrelevant
if($template->flags & Template::flagSystem) continue;
$links[$template->name] = "?q=&template={$template->name}&submit=Search&operator=~%3D&display=title+path+created&sort=created&reverse=1";
}
foreach($links as $label => $value) {
if(is_int($label)) {
$out .= "<h4>$value</h4>";
} else {
$value .= "&show_options=1";
$value = htmlspecialchars($value);
$out .= "<a href='$value'>$label</a>";
}
}
return $out;
}
protected function renderMatchesTable(PageArray $matches, array $display, $id = 'ProcessPageSearchResultsList') {
/**
* Render a table of matches
*
* @param PageArray $matches
* @param array $display Fields to display (from getDisplayFields method)
* @return string
*
*/
protected function renderMatchesTable(PageArray $matches, array $display) {
if(!count($display)) $display = array('path');
$out = '';
if(!count($matches)) return $out;
/** @var MarkupAdminDataTable $table */
$table = $this->modules->get("MarkupAdminDataTable");
$table->setSortable(false);
$table->setEncodeEntities(false);
@@ -817,7 +902,7 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
*
* Applicable to adminSearchMode only.
*
* @param $q Text to find
* @param string $q Text to find
* @return array Array of matches
*
*/
@@ -872,8 +957,9 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
* Render the provided matches as a JSON string for AJAX use
*
* @param PageArray $matches
* @param array Array of fields to display, or display format associative array
* @param array $display Array of fields to display, or display format associative array
* @param string $selector
* @return string
*
*/
protected function renderMatchesAjax(PageArray $matches, $display, $selector) {
@@ -964,6 +1050,9 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
* Convert object to an array where possible, otherwise convert to a string
*
* For use by renderMatchesAjax
*
* @param Page|WireData|WireArray|Wire|object $o
* @return array
*
*/
protected function setupObjectMatch($o) {
@@ -985,6 +1074,9 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
* Filter an array converting any indexes containing objects to arrays or strings
*
* For use by renderMatchesAjax
*
* @param array $a
* @return array
*
*/
protected function setupArrayMatch(array $a) {
@@ -995,6 +1087,13 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
return $a;
}
/**
* Render search for that submits to this process
*
* @param string $placeholder Value for placeholder attribute in search input
* @return string
*
*/
public function renderSearchForm($placeholder = '') {
$q = substr($this->input->get->q, 0, 128);
@@ -1008,9 +1107,10 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
$placeholder = '';
}
$out = "\n<form id='ProcessPageSearchForm' data-action='{$adminURL}page/search/' action='{$adminURL}page/search/' method='get'>" .
$out =
"\n<form id='ProcessPageSearchForm' data-action='{$adminURL}page/search/' action='{$adminURL}page/search/' method='get'>" .
"\n\t<label for='ProcessPageSearchQuery'><i class='fa fa-search'></i></label>" .
"\n\t<input type='text' id='ProcessPageSearchQuery' name='q' value='$q'$placeholder />" .
"\n\t<input type='text' id='ProcessPageSearchQuery' name='q' value='$q' $placeholder />" .
"\n\t<input type='submit' id='ProcessPageSearchSubmit' name='search' value='Search' />" . //" . $this->_x('Search', 'input') . "' />" . // Text that appears as the placeholder text in the top search submit input
"\n\t<input type='hidden' name='show_options' value='1' />" .
"\n\t<span id='ProcessPageSearchStatus'></span>" .
@@ -1064,5 +1164,42 @@ class ProcessPageSearch extends Process implements ConfigurableModule {
return $inputfields;
}
/*
* No longer in use, but here for reference:
*
protected function renderShortcuts() {
$out = '';
$links = array(
'Quick Links',
"All by creation date" => '?q=&submit=Search&display=title+path+created&sort=created&reverse=1' ,
"All by latest edit date" => '?q=&submit=Search&display=title+path+created&sort=modified&reverse=1',
"Users by creation date" => '?q=&template=user&submit=Search&operator=~%3D&display=name+email+created&sort=created&reverse=1',
'New pages by template',
);
foreach($this->templates as $template) {
// Quick links only for content with more than one page
// if($template->getNumPages() < 2) continue;
// Users get own quick link earlier, others are rather irrelevant
if($template->flags & Template::flagSystem) continue;
$links[$template->name] = "?q=&template={$template->name}&submit=Search&operator=~%3D&display=title+path+created&sort=created&reverse=1";
}
foreach($links as $label => $value) {
if(is_int($label)) {
$out .= "<h4>$value</h4>";
} else {
$value .= "&show_options=1";
$value = htmlspecialchars($value);
$out .= "<a href='$value'>$label</a>";
}
}
return $out;
}
*/
}