1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-24 23:31:01 +02:00

Some additional adjustments to the live search, including the addition of a "help" option, which you can access just by typing "help" into the search box. Bumped version to 3.0.108

This commit is contained in:
Ryan Cramer
2018-07-13 11:19:26 -04:00
parent 9741fbb818
commit 1f4ca1b8f4
14 changed files with 335 additions and 52 deletions

View File

@@ -534,6 +534,7 @@ interface SearchableModule {
* 'title' => 'Title of these items',
* 'total' => 999, // total number of items found, or omit if pagination not supported or active
* 'url' => '', // optional URL to view all items, or omit for a PW-generated one
* 'properties' => array(), // optional list of supported search properties, only looked for if $options['info'] === true;
* 'items' => array(
* [0] => array(
* 'id' => 123, // Unique ID of item (optional)
@@ -549,25 +550,35 @@ interface SearchableModule {
* [1] => array(
* ...
* ),
* )
* ),
* );
*
* Please note: When ProcessWire calls this method, if the module is not already loaded (autoload),
*
* PLEASE NOTE:
* When ProcessWire calls this method, if the module is not already loaded (autoload),
* it instantiates the module but DOES NOT call the init() or ready() methods. Thats because the
* search method is generally self contained. If you need either of those methods to be called,
* and your module is not autoload, you should call the method(s) from your search() method.
*
* and your module is not autoload, you should call the method(s) from your search() method.
*
* About the optional “properties” index:
* If ProcessWire calls your search() method with $options['info'] == true; then it is likely wanting to see
* what properties are available for search. For instance, properties for a Module search might be:
* [ 'name', 'title', 'summary' ]. Implementation of the properties index is optional, and for PWs informational
* purposes only.
*
* @param string $text Text to search for
* @param array $options Options array provided to search() calls:
* - `edit` (bool): True if any 'url' returned should be to edit items rather than view them
* - `edit` (bool): True if any 'url' returned should be to edit rather than view items, where access allows. (default=true)
* - `multilang` (bool): If true, search all languages rather than just current (default=true).
* - `start` (int): Start index (0-based), if pagination active (default=0).
* - `limit` (int): Limit to this many items, or 0 for no limit. (default=0).
* - `type` (string): If search should only be of a specific type, i.e. "pages", "modules", etc. then it is
* specified here. This corresponds with the getModuleInfo()['searchable'] name or item 'group' property.
* Note that ProcessWire wont call your search() method if the type cannot match this search.
* - `operator` (string): Selector operator type requested, if more than one is supported (default is %=).
* - `property` (string): If search should limit to a particular property/fieldj, it is named here.
* - `verbose` (bool): True if output can optionally be more verbose, false if not. (default=false)
* - `debug` (bool): True if DEBUG option was specified in query. (default=false)
* - `help` (bool): True if we are just querying for help/info and are not using the search results. (default=false)
* @return array
*
*/

View File

@@ -1538,7 +1538,7 @@ class Modules extends WireArray {
* ~~~~~
*
* @param string $prefix Specify prefix, i.e. "Process", "Fieldtype", "Inputfield", etc.
* @param bool|int $instantiate Specify one of the following:
* @param bool|int $load Specify one of the following:
* - Boolean true to return array of instantiated modules.
* - Boolean false to return array of module names (default).
* - Integer 1 to return array of module info for each matching module.
@@ -1546,16 +1546,16 @@ class Modules extends WireArray {
* @return array Returns array of module class names or Module objects. In either case, array indexes are class names.
*
*/
public function findByPrefix($prefix, $instantiate = false) {
public function findByPrefix($prefix, $load = false) {
$results = array();
foreach($this as $key => $value) {
$className = wireClassName($value->className(), false);
if(strpos($className, $prefix) !== 0) continue;
if($instantiate === 1) {
if($load === 1) {
$results[$className] = $this->getModuleInfo($className);
} else if($instantiate === 2) {
} else if($load === 2) {
$results[$className] = $this->getModuleInfoVerbose($className);
} else if($instantiate === true) {
} else if($load === true) {
$results[$className] = $this->getModule($className);
} else {
$results[$className] = $className;
@@ -1571,7 +1571,7 @@ class Modules extends WireArray {
* - Selector string to match module info.
* - Array of [ 'property' => 'value' ] to match in module info (this is not a selector array).
* - Name of property that will match module if not empty in module info.
* @param bool|int $instantiate Specify one of the following:
* @param bool|int $load Specify one of the following:
* - Boolean true to return array of instantiated modules.
* - Boolean false to return array of module names (default).
* - Integer 1 to return array of module info for each matching module.
@@ -1579,13 +1579,13 @@ class Modules extends WireArray {
* @return array Array of modules, module names or module info arrays, indexed by module name.
*
*/
public function findByInfo($selector, $instantiate = false) {
public function findByInfo($selector, $load = false) {
$selectors = null;
$infos = null;
$keys = null;
$results = array();
$verbose = $instantiate === 2;
$verbose = $load === 2;
$properties = array();
$has = '';
@@ -1612,7 +1612,12 @@ class Modules extends WireArray {
break;
}
foreach($this->getModuleInfo('*', array('verbose' => $verbose)) as $info) {
$moduleInfoOptions = array(
'verbose' => $verbose,
'minify' => false
);
foreach($this->getModuleInfo('*', $moduleInfoOptions) as $info) {
$isMatch = false;
if($has) {
@@ -1650,9 +1655,9 @@ class Modules extends WireArray {
if($isMatch) {
$moduleName = $info['name'];
if(is_int($instantiate)) {
if(is_int($load)) {
$results[$moduleName] = $info;
} else if($instantiate === true) {
} else if($load === true) {
$results[$moduleName] = $this->getModule($moduleName);
} else {
$results[$moduleName] = $moduleName;
@@ -3165,6 +3170,27 @@ class Modules extends WireArray {
public function getModuleConfigData($className) {
return $this->getConfig($className);
}
/**
* Return the URL where the module can be edited, configured or uninstalled
*
* If module is not installed, it just returns the URL to ProcessModule.
*
* #pw-group-configuration
*
* @param string|Module $className
* @param bool $collapseInfo
* @return string
*
*/
public function getModuleEditUrl($className, $collapseInfo = true) {
if(!is_string($className)) $className = $this->getModuleClass($className);
$url = $this->wire('config')->urls->admin . 'module/';
if(empty($className) || !$this->isInstalled($className)) return $url;
$url .= "edit/?name=$className";
if($collapseInfo) $url .= "&collapse_info=1";
return $url;
}
/**
* Given a module name, return an associative array of configuration data for it

View File

@@ -45,7 +45,7 @@ class ProcessWire extends Wire {
* Reversion revision number
*
*/
const versionRevision = 107;
const versionRevision = 108;
/**
* Version suffix string (when applicable)

View File

@@ -108,6 +108,7 @@ var ProcessWireAdminTheme = {
.attr('title', item.tip)
.append($label)
.append($("<small class='uk-text-muted'></small>").text(item.template));
if(item.edit_url == '#' || !item.edit_url.length) $a.removeAttr('href');
return $("<li></li>").append($a).appendTo(ul);
}
});
@@ -154,6 +155,7 @@ var ProcessWireAdminTheme = {
// follow the link if the Enter/Return key is tapped
if(typeof event.key != 'undefined') {
event.preventDefault();
if(ui.item.edit_url == '#' || !ui.item.edit_url.length) return false;
window.location = ui.item.edit_url;
}
}

View File

@@ -1 +1 @@
var ProcessWireAdminTheme={init:function(){var b=$("#head_button > button.pw-dropdown-toggle").hide();this.setupCloneButton();ProcessWireAdmin.init();this.setupSearch();this.setupMobile();var a=$("body");if(a.hasClass("hasWireTabs")&&$("ul.WireTabs").length==0){a.removeClass("hasWireTabs")}$("#content").removeClass("pw-fouc-fix");a.removeClass("pw-init").addClass("pw-ready");if(b.length>0){b.show()}},setupCloneButton:function(){if($("body").is(".modal")){return}var b=$("button.pw-head-button, button.head_button_clone");if(b.length==0){return}var a=$("#head_button");if(a.length==0){a=$("<div id='head_button'></div>").prependTo("#breadcrumbs .pw-container")}b.each(function(){var e=$(this);var d=e.parent("a");var c;if(d.length>0){c=e.parent("a").clone(true);a.prepend(c)}else{if(e.hasClass("pw-head-button")||e.hasClass("head_button_clone")){c=e.clone(true);c.attr("data-from_id",e.attr("id")).attr("id",e.attr("id")+"_copy");c.click(function(){$("#"+$(this).attr("data-from_id")).click();return false});a.prepend(c)}}});a.show()},setupSearch:function(){$.widget("custom.adminsearchautocomplete",$.ui.autocomplete,{_renderMenu:function(e,c){var f=this;var d="";e.attr("id","ProcessPageSearchAutocomplete");$.each(c,function(g,h){if(h.type!=d){$("<li>"+h.type+"</li>").addClass("ui-widget-header").appendTo(e);d=h.type}f._renderItemData(e,h)})},_renderItem:function(e,f){if(f.label==f.template){f.template=""}var c=$("<span></span>").text(f.label).css("margin-right","3px");if(f.unpublished){c.css("text-decoration","line-through")}if(f.hidden){c.addClass("ui-priority-secondary")}if(typeof f.icon!="undefined"&&f.icon.length){var d=$("<i></i>").addClass("fa fa-fw fa-"+f.icon).css("margin-right","2px");c.prepend(d)}var g=$("<a></a>").attr("href",f.edit_url).attr("title",f.tip).append(c).append($("<small class='uk-text-muted'></small>").text(f.template));return $("<li></li>").append(g).appendTo(e)}});var b=$("#ProcessPageSearchQuery");var a=$("#ProcessPageSearchStatus");b.adminsearchautocomplete({minLength:2,position:{my:"right top",at:"right bottom"},search:function(c,d){a.html("<img src='"+ProcessWire.config.urls.modules+"Process/ProcessPageList/images/loading.gif'>")},open:function(c,d){$("#topnav").hide()},close:function(c,d){$("#topnav").show()},source:function(e,c){var d=b.parents("form").attr("action")+"?q="+e.term;$.getJSON(d,function(g){var f=g.matches.length;if(f<g.total){a.text(g.matches.length+"/"+g.total)}else{a.text(f)}c($.map(g.matches,function(h){return{label:h.title,value:h.title,page_id:h.id,template:h.template_label?h.template_label:"",edit_url:h.editUrl,type:h.type,tip:h.tip,unpublished:(typeof h.unpublished!="undefined"?h.unpublished:false),hidden:(typeof h.hidden!="undefined"?h.hidden:false),locked:(typeof h.locked!="undefined"?h.locked:false),icon:(typeof h.icon!="undefined"?h.icon:"")}}))})},select:function(c,d){if(typeof c.key!="undefined"){c.preventDefault();window.location=d.item.edit_url}}}).focus(function(){$(this).siblings("label").find("i").hide()}).blur(function(){a.text("");$(this).siblings("label").find("i").show()})},setupMobile:function(){var a=0;var c=0;var b=function(){var h=$("#topnav");var g=$("body");var e=h.height();if(e>50){if(!g.hasClass("collapse-topnav")){g.addClass("collapse-topnav");a=g.width()}}else{if(a>0){var f=g.width();if(g.hasClass("collapse-topnav")&&f>a){g.removeClass("collapse-topnav");a=0}}}h.children(".collapse-topnav-menu").children("a").click(function(){if($(this).is(".hover")){$(this).mouseleave()}else{$(this).mouseenter()}return false});var d=$(".WireTabs");if(d.length<1){return}d.each(function(){var j=$(this);var i=j.height();if(i>65){if(!g.hasClass("collapse-wiretabs")){g.addClass("collapse-wiretabs");c=g.width()}}else{if(c>0){var k=g.width();if(g.hasClass("collapse-wiretabs")&&k>c){g.removeClass("collapse-wiretabs");c=0}}}})};b();$(window).resize(b)}};$(document).ready(function(){ProcessWireAdminTheme.init();$("a.notice-remove","#notices").click(function(){$("#notices").slideUp("fast",function(){$(this).remove()});return false})});
var ProcessWireAdminTheme={init:function(){var b=$("#head_button > button.pw-dropdown-toggle").hide();this.setupCloneButton();ProcessWireAdmin.init();this.setupSearch();this.setupMobile();var a=$("body");if(a.hasClass("hasWireTabs")&&$("ul.WireTabs").length==0){a.removeClass("hasWireTabs")}$("#content").removeClass("pw-fouc-fix");a.removeClass("pw-init").addClass("pw-ready");if(b.length>0){b.show()}},setupCloneButton:function(){if($("body").is(".modal")){return}var b=$("button.pw-head-button, button.head_button_clone");if(b.length==0){return}var a=$("#head_button");if(a.length==0){a=$("<div id='head_button'></div>").prependTo("#breadcrumbs .pw-container")}b.each(function(){var e=$(this);var d=e.parent("a");var c;if(d.length>0){c=e.parent("a").clone(true);a.prepend(c)}else{if(e.hasClass("pw-head-button")||e.hasClass("head_button_clone")){c=e.clone(true);c.attr("data-from_id",e.attr("id")).attr("id",e.attr("id")+"_copy");c.click(function(){$("#"+$(this).attr("data-from_id")).click();return false});a.prepend(c)}}});a.show()},setupSearch:function(){$.widget("custom.adminsearchautocomplete",$.ui.autocomplete,{_renderMenu:function(e,c){var f=this;var d="";e.attr("id","ProcessPageSearchAutocomplete");$.each(c,function(g,h){if(h.type!=d){$("<li>"+h.type+"</li>").addClass("ui-widget-header").appendTo(e);d=h.type}f._renderItemData(e,h)})},_renderItem:function(e,f){if(f.label==f.template){f.template=""}var c=$("<span></span>").text(f.label).css("margin-right","3px");if(f.unpublished){c.css("text-decoration","line-through")}if(f.hidden){c.addClass("ui-priority-secondary")}if(typeof f.icon!="undefined"&&f.icon.length){var d=$("<i></i>").addClass("fa fa-fw fa-"+f.icon).css("margin-right","2px");c.prepend(d)}var g=$("<a></a>").attr("href",f.edit_url).attr("title",f.tip).append(c).append($("<small class='uk-text-muted'></small>").text(f.template));if(f.edit_url=="#"||!f.edit_url.length){g.removeAttr("href")}return $("<li></li>").append(g).appendTo(e)}});var b=$("#ProcessPageSearchQuery");var a=$("#ProcessPageSearchStatus");b.adminsearchautocomplete({minLength:2,position:{my:"right top",at:"right bottom"},search:function(c,d){a.html("<img src='"+ProcessWire.config.urls.modules+"Process/ProcessPageList/images/loading.gif'>")},open:function(c,d){$("#topnav").hide()},close:function(c,d){$("#topnav").show()},source:function(e,c){var d=b.parents("form").attr("action")+"?q="+e.term;$.getJSON(d,function(g){var f=g.matches.length;if(f<g.total){a.text(g.matches.length+"/"+g.total)}else{a.text(f)}c($.map(g.matches,function(h){return{label:h.title,value:h.title,page_id:h.id,template:h.template_label?h.template_label:"",edit_url:h.editUrl,type:h.type,tip:h.tip,unpublished:(typeof h.unpublished!="undefined"?h.unpublished:false),hidden:(typeof h.hidden!="undefined"?h.hidden:false),locked:(typeof h.locked!="undefined"?h.locked:false),icon:(typeof h.icon!="undefined"?h.icon:"")}}))})},select:function(c,d){if(typeof c.key!="undefined"){c.preventDefault();if(d.item.edit_url=="#"||!d.item.edit_url.length){return false}window.location=d.item.edit_url}}}).focus(function(){$(this).siblings("label").find("i").hide()}).blur(function(){a.text("");$(this).siblings("label").find("i").show()})},setupMobile:function(){var a=0;var c=0;var b=function(){var h=$("#topnav");var g=$("body");var e=h.height();if(e>50){if(!g.hasClass("collapse-topnav")){g.addClass("collapse-topnav");a=g.width()}}else{if(a>0){var f=g.width();if(g.hasClass("collapse-topnav")&&f>a){g.removeClass("collapse-topnav");a=0}}}h.children(".collapse-topnav-menu").children("a").click(function(){if($(this).is(".hover")){$(this).mouseleave()}else{$(this).mouseenter()}return false});var d=$(".WireTabs");if(d.length<1){return}d.each(function(){var j=$(this);var i=j.height();if(i>65){if(!g.hasClass("collapse-wiretabs")){g.addClass("collapse-wiretabs");c=g.width()}}else{if(c>0){var k=g.width();if(g.hasClass("collapse-wiretabs")&&k>c){g.removeClass("collapse-wiretabs");c=0}}}})};b();$(window).resize(b)}};$(document).ready(function(){ProcessWireAdminTheme.init();$("a.notice-remove","#notices").click(function(){$("#notices").slideUp("fast",function(){$(this).remove()});return false})});

View File

@@ -338,6 +338,7 @@ var ProcessWireAdminTheme = {
.attr('title', item.tip)
.append($label)
.append($("<small class='uk-text-muted'></small>").text(item.template));
if(item.edit_url == '#' || !item.edit_url.length) $a.removeAttr('href');
return $("<li></li>").append($a).appendTo(ul);
}
});
@@ -378,6 +379,7 @@ var ProcessWireAdminTheme = {
// follow the link if the Enter/Return key is tapped
if(typeof event.key != 'undefined') {
event.preventDefault();
if(ui.item.edit_url == '#' || !ui.item.edit_url.length) return false;
window.location = ui.item.edit_url;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -313,6 +313,8 @@ var ProcessWireAdminTheme = {
.attr('title', item.tip)
.append($label)
.append($("<small class='uk-text-muted'></small>").text(item.template));
if(item.edit_url == '#' || !item.edit_url.length) $a.removeAttr('href');
return $("<li></li>").append($a).appendTo(ul);
}
@@ -378,6 +380,7 @@ var ProcessWireAdminTheme = {
$(this).val('');
if(typeof event.key !== 'undefined') {
event.preventDefault();
if(ui.item.edit_url === '#' || ui.item.edit_url.length < 1) return false;
if(typeof parent.isPresent == "undefined") {
window.location = ui.item.edit_url;
} else {

File diff suppressed because one or more lines are too long

View File

@@ -710,8 +710,23 @@ class ProcessCommentsManager extends Process {
'total' => 0,
'url' => '',
'items' => array(),
'properties' => array(
'text',
'cite',
'email',
'status',
'created',
'website',
'ip',
'user_agent',
'upvotes',
'downvotes',
'stars'
)
);
if($options['help']) return $result;
$operator = empty($options['operator']) ? '%=' : $options['operator'];
$value = $this->wire('sanitizer')->selectorValue($text);
$summaryLength = $options['verbose'] ? 1024 : 200;

View File

@@ -2625,7 +2625,20 @@ class ProcessField extends Process implements ConfigurableModule {
$result = array(
'title' => $page->id ? $page->title : $this->className(),
'items' => array(),
'total' => 0,
'properties' => array(
'name',
'type',
'label',
'description',
'notes',
'settings',
'tags',
'icon',
)
);
if(!empty($options['help'])) return $result;
$looseItems = array();
$exactItems = array();
@@ -2652,10 +2665,13 @@ class ProcessField extends Process implements ConfigurableModule {
if($property == 'notes' || $property == 'all') $search[] = $item->notes;
}
if($property == 'data') {
if($property == 'settings' || $property == 'data') {
foreach($item->getArray() as $k => $v) {
$search[] = (string) $v;
}
} else if(!in_array($property, $result['properties'])) {
$val = $item->get($property);
if($val !== null) $search[] = $val;
}
$search = implode(' ', $search);
@@ -2663,6 +2679,9 @@ class ProcessField extends Process implements ConfigurableModule {
if($pos === false) continue;
$exact = stripos($search, " $text");
$result['total']++;
if(!empty($options['limit']) && $cnt >= $options['limit']) break;
$item = array(
'id' => $item->id,
'name' => $item->name,
@@ -2680,11 +2699,10 @@ class ProcessField extends Process implements ConfigurableModule {
}
$cnt++;
if(!empty($options['limit']) && $cnt >= $options['limit']) break;
}
$result['items'] = array_merge($exactItems, $looseItems);
return $result;
}

View File

@@ -52,6 +52,7 @@ class ProcessPageSearchLive extends Wire {
'limit' => 15,
'verbose' => false,
'debug' => false,
'help' => false,
);
/**
@@ -370,7 +371,8 @@ class ProcessPageSearchLive extends Wire {
'multilang' => $this->wire('languages') ? true : false,
'language' => $language,
'start' => $start,
'limit' => $limit
'limit' => $limit,
'help' => strtolower($q) === 'help',
));
if($this->isViewAll) {
@@ -463,12 +465,14 @@ class ProcessPageSearchLive extends Wire {
}
if($this->process) {
$headline = $this->labels['search-results'];
if($type) $headline .= " - " . ucfirst($type);
$this->process->headline($this->pagination->getPaginationString(array(
'label' => $headline,
'count' => count($results)
)));
if($type) {
$this->process->headline($this->pagination->getPaginationString(array(
'label' => $this->labels['search-results'] . " - " . ucfirst($type),
'count' => count($results)
)));
} else {
$this->process->headline($this->labels['search-results']);
}
}
$out = $this->renderList($results);
@@ -517,6 +521,7 @@ class ProcessPageSearchLive extends Wire {
$type = $liveSearch['type'];
$foundTypes = array();
$modulesInfo = array();
$help = $liveSearch['help'];
/** @var Modules $modules */
$modules = $this->wire('modules');
@@ -536,7 +541,7 @@ class ProcessPageSearchLive extends Wire {
if($type != 'pages' && $type != 'trash') {
$modulesInfo = $modules->getModuleInfo('*', array('verbose' => true));
}
foreach($modulesInfo as $info) {
if(empty($info['searchable'])) continue;
@@ -564,7 +569,7 @@ class ProcessPageSearchLive extends Wire {
// ok
}
if(!$module || empty($result['items'])) continue;
if(!$module || (empty($result['items']) && empty($liveSearch['help']))) continue;
if(empty($result['total'])) $result['total'] = count($result['items']);
if(!in_array($thisType, $this->searchTypesOrder)) $this->searchTypesOrder[] = $thisType;
@@ -574,6 +579,14 @@ class ProcessPageSearchLive extends Wire {
$title = empty($result['title']) ? $info['title'] : $result['title'];
$n = $liveSearch['start'];
$item = null;
if($help) {
foreach($result['items'] as $key => $item) {
if($item['name'] != 'help') unset($result['items'][$key]);
}
$result['items'] = array_merge($this->makeHelpItems($result, $thisType), $result['items']);
}
foreach($result['items'] as $item) {
$n++;
$item = array_merge($this->itemTemplate, $item);
@@ -582,18 +595,20 @@ class ProcessPageSearchLive extends Wire {
$items[$order] = $item;
$order++;
}
if($n && $n < $result['total'] && !$this->isViewAll) {
if($n && $n < $result['total'] && !$this->isViewAll && !$help) {
$url = isset($result['url']) ? $result['url'] : '';
$items[$order] = $this->makeViewAllItem($liveSearch, $thisType, $item['group'], $result['total'], $url);
}
if($this->isViewAll && $this->pagination && $type) {
if($this->isViewAll && $this->pagination && $type && !$help) {
$this->pagination->setTotal($result['total']);
$this->pagination->setLimit($liveSearch['limit']);
$this->pagination->setStart($liveSearch['start']);
}
}
if($type && !count($foundTypes) && !in_array($type, array('pages', 'trash', 'modules'))) {
if($type && !$help && !count($foundTypes) && !in_array($type, array('pages', 'trash', 'modules'))) {
if(empty($liveSearch['template']) && !count($foundTypes)) {
// if no types matched, and its going to skip pages, assume type is a property, and do a pages search
$liveSearch = $this->init(array(
@@ -635,6 +650,8 @@ class ProcessPageSearchLive extends Wire {
}
ksort($items);
if($help) $items = array_merge($this->makeHelpItems(array(), 'help'), $items);
return $items;
}
@@ -653,6 +670,13 @@ class ProcessPageSearchLive extends Wire {
$superuser = $user->isSuperuser();
$pages = $this->wire('pages');
if(!empty($liveSearch['help'])) {
$result = array('title' => 'pages', 'items' => array(), 'properties' => array('name', 'title'));
if($this->wire('fields')->get('body')) $result['properties'][] = 'body';
$result['properties'][] = $this->_('or any field name');
return $this->makeHelpItems($result, 'pages');
}
// a $pages->find() search will be included in the live search
$selectors = &$liveSearch['selectors'];
$selectors[] = "start=$liveSearch[start], limit=$liveSearch[limit]";
@@ -766,6 +790,24 @@ class ProcessPageSearchLive extends Wire {
$items = array();
$forceMatch = false;
if(!empty($liveSearch['help'])) {
$info = $this->wire('modules')->getModuleInfoVerbose('ProcessPageSearch');
$properties = array();
foreach(array_keys($info) as $property) {
$value = $info[$property];
if(!is_array($value)) $properties[$property] = $property;
}
$exclude = array('id', 'file', 'versionStr', 'core');
foreach($exclude as $key) unset($properties[$key]);
$result = array(
'title' => 'Modules',
'items' => array(),
'properties' => $properties
);
$items = $this->makeHelpItems($result, 'modules');
return $items;
}
if($liveSearch['type'] === 'modules' && !empty($liveSearch['property'])) {
// searching for custom module property
$forceMatch = true;
@@ -779,18 +821,20 @@ class ProcessPageSearchLive extends Wire {
}
foreach($infos as $info) {
$id = isset($info['id']) ? $info['id'] : 0;
$name = $info['name'];
$title = $info['title'];
$summary = isset($info['summary']) ? $info['summary'] : '';
if(!$forceMatch) {
$searchText = "$name $title $info[summary]";
$searchText = "$name $title $summary";
if(stripos($searchText, $q) === false) continue;
}
$item = array(
'id' => $info['id'],
'id' => $id,
'name' => $name,
'title' => $title,
'subtitle' => $name,
'summary' => $info['summary'],
'summary' => $summary,
'url' => $this->wire('config')->urls->admin . "module/edit?name=$name",
'group' => $groupLabel,
);
@@ -866,6 +910,129 @@ class ProcessPageSearchLive extends Wire {
'group' => 'Debug',
));
}
/**
* Make a search result item that displays property info
*
* @param array $result Result array returned by a SearchableModule::search() method
* @param string $type
* @return array
*
*/
protected function makeHelpItems(array $result, $type) {
$items = array();
$helloLabel = $this->_('test');
$usage1desc = $this->wire('sanitizer')->unentities($this->_('Searches %1$s for: %2$s'));
$usage2desc = $this->wire('sanitizer')->unentities($this->_('Searches “%1$s” property of %2$s for: %3$s'));
if($type === 'help') {
$operators = ProcessPageSearch::getOperators();
$summary =
$this->_('Examples use the “=” equals operator.') . " \n" .
$this->_('In some cases you can also use these:') . "\n";
foreach($operators as $op => $label) {
$summary .= "$op " . rtrim($label, '*') . "\n";
}
$items[] = array(
'title' => $this->_('operators:'),
'subtitle' => implode(', ', array_keys($operators)),
'summary' => $summary,
'group' => 'help',
'url' => 'https://processwire.com/api/selectors/#operators'
);
if($this->wire('user')->isSuperuser() && $this->process) {
$items[] = array(
'title' => $this->_('configure'),
'subtitle' => $this->_('Click here to configure search settings'),
'url' => $url = $this->wire('modules')->getModuleEditUrl('ProcessPageSearch'),
'group' => 'help',
);
}
return $items;
}
// include any items from result that had the name "help"
foreach($result['items'] as $item) {
if($item['name'] == 'help') $items[] = $item;
}
$items[] = array(
'title' => "$type=$helloLabel",
'subtitle' => sprintf($usage1desc, $type, $helloLabel),
);
if(!empty($result['properties'])) {
if($type == 'pages' || $type == 'modules') {
$property = 'title';
} else if($type == 'fields' || $type == 'templates') {
$property = 'label';
} else {
$property = reset($result['properties']);
}
$items[] = array(
'title' => "$type.$property=$helloLabel",
'subtitle' => sprintf($usage2desc, $property, $type, $helloLabel)
);
if($type === 'pages') {
$items[] = array(
'title' => "$property=$helloLabel",
'subtitle' => $this->_('Same as above (shorter syntax if no names collide)')
);
$templateName = 'basic-page';
$items[] = array(
'title' => "$templateName=$helloLabel",
'subtitle' => sprintf($this->_('Limit results to template: %s'), $templateName),
);
$items[] = array(
'title' => "$templateName.$property=$helloLabel",
'subtitle' => sprintf($this->_('Limit results to %s field on template'), $property)
);
} else if($type === 'templates') {
$fieldName = 'images';
$items[] = array(
'title' => "templates.fields=$fieldName",
'subtitle' => sprintf($this->_('Find templates that have field: %s'), $fieldName)
);
} else if($type === 'fields') {
$items[] = array(
'title' => "fields.settings=ckeditor",
'subtitle' => $this->_('Find fields with “ckeditor” in settings'),
);
}
$properties = implode(', ', $result['properties']);
if(strlen($properties) > 50) {
$properties = $this->wire('sanitizer')->truncate($properties, 50) . ' ' . $this->_('(hover for more)');
}
$summary =
sprintf($this->_('The examples use the “%s” property.'), $property) . "\n" .
$this->_('You can also use any of these properties:') . "\n" .
implode("\n", $result['properties']);
$items[] = array(
'title' => $this->_('properties'),
'subtitle' => $properties,
'summary' => $summary
);
}
$group = sprintf($this->_('%s help'), $type);
foreach($items as $key => $item) {
$item['name'] = 'help';
$item['group'] = $group;
$items[$key] = array_merge($this->itemTemplate, $item);
}
return $items;
}
/**
* Make a search result item that displays a “view all” link
@@ -927,11 +1094,17 @@ class ProcessPageSearchLive extends Wire {
$pagination = $this->pagination->renderPager();
$a = array();
$group = '';
$out = "\n<div class='$class'>" . $pagination;
foreach($items as $item) {
$a[] = $this->renderItem($item, $prefix);
$headline = '';
if($item['group'] != $group) {
$group = $item['group'];
$headline = "<h2>" . $this->wire('sanitizer')->entities($group) . "</h2>";
}
$a[] = $headline . $this->renderItem($item, $prefix);
}
$out .= implode('<hr />', $a) . $pagination . "\n</div>";
@@ -961,7 +1134,7 @@ class ProcessPageSearchLive extends Wire {
}
}
$title = "<strong class='$prefix-title'>$item[title]</strong> ";
$title = "<strong class='$prefix-title'>$item[title]</strong>";
$subtitle = empty($item['subtitle']) ? '' : "<br /><em class='$prefix-subtitle'>$item[subtitle]</em> ";
$summary = empty($item['summary']) ? '' : "<br /><span class='$prefix-summary'>$item[summary]</span> ";

View File

@@ -620,7 +620,8 @@ class ProcessPageType extends Process implements ConfigurableModule, WirePageEdi
public function search($text, array $options = array()) {
$page = $this->getProcessPage();
$this->pages = $this->wire($page->name);
$this->pages = $this->wire($page->name);
$templates = $this->pages->getTemplates();
/** @var Languages $languages */
$page = $this->getProcessPage();
@@ -629,6 +630,18 @@ class ProcessPageType extends Process implements ConfigurableModule, WirePageEdi
'title' => $page->id ? $page->title : $this->className(),
'items' => array(),
);
if(!empty($options['help'])) {
$result['properties'] = array('name');
foreach($templates as $template) {
foreach($template->fieldgroup as $field) {
if($field->type instanceof FieldtypePassword) continue;
if($field->type instanceof FieldtypeFieldsetOpen) continue;
$result['properties'][] = $field->name;
}
}
return $result;
}
$text = $this->wire('sanitizer')->selectorValue($text);
$property = empty($options['property']) ? 'name' : $options['property'];

View File

@@ -3016,11 +3016,22 @@ class ProcessTemplate extends Process {
/** @var Languages $languages */
$languages = $this->wire('langauges');
$page = $this->getProcessPage();
$property = isset($options['property']) ? $options['property'] : '';
$result = array(
'title' => $page->id ? $page->title : $this->className(),
'items' => array(),
'total' => 0,
'properties' => array(
'name',
'label',
'tags',
'fields',
)
);
if(!empty($options['help'])) return $result;
if(!empty($property) && !in_array($property, $result['properties'])) return $result;
$looseItems = array();
$exactItems = array();
@@ -3028,19 +3039,26 @@ class ProcessTemplate extends Process {
foreach($this->wire('templates') as $item) {
/** @var Template $item */
$search = array(' ', $item->name);
$search = array(' ');
if(!$property || $property == 'name' ) $search[] = $item->name;
if(!empty($options['multilang']) && $languages) {
foreach($languages as $lang) {
$search[] = $item->getLabel($lang);
if(!$property || $property == 'label') {
if(!empty($options['multilang']) && $languages) {
foreach($languages as $lang) {
$search[] = $item->getLabel($lang);
}
} else {
$search[] = $item->getLabel();
}
} else {
$search[] = $item->getLabel();
}
// when search matches field name exactly (that template has), allow template to be matched
foreach($item->fieldgroup as $field) {
if(strtolower($text) == strtolower($field->name)) $search[] = $field->name;
if($property == 'fields') {
foreach($item->fieldgroup as $field) {
if(strtolower($text) == strtolower($field->name)) $search[] = $field->name;
}
} else if($property == 'tags') {
$search[] = $item->tags;
}
$search = implode(' ', $search);
@@ -3051,6 +3069,9 @@ class ProcessTemplate extends Process {
$labelFields = sprintf($this->_n('%d field', '%d fields', $numFields), $numFields);
$label = $item->getLabel();
$subtitle = $label == $item->name ? $labelFields : "$label ($labelFields)";
$result['total']++;
if(!empty($options['limit']) && $cnt >= $options['limit']) continue;
$item = array(
'id' => $item->id,
@@ -3067,9 +3088,8 @@ class ProcessTemplate extends Process {
} else {
$looseItems[] = $item;
}
$cnt++;
if(!empty($options['limit']) && $cnt >= $options['limit']) break;
}
$result['items'] = array_merge($exactItems, $looseItems);