/**
  *  Display a tree of all page types and their children, with perms
  *
  *  @param Group group show permissions related to this group
  *  @return string A fully formatted tree as a HTML list	
  */
 public static function PageTree($group = null)
 {
     self::$group = $group;
     //Get all the page types
     $classes = SiteTree::page_type_classes();
     $pageTreeList = array();
     //We don't want to remove the fields from this list, so disable the remove
     SimplifyPermissionProvider::setRemoveEnabled(false);
     //Get an instance of each page type, add to an array
     //TODO: This works, but only returns the initial field state of the objects
     //      Might be a better way to scrape ALL the possible fields?
     foreach ($classes as $class) {
         $instance = singleton($class);
         if ($instance instanceof HiddenClass) {
             continue;
         }
         if (!$instance->canCreate()) {
             continue;
         }
         $pageTreeList[] = $instance;
     }
     //Get the children of each page type and return it as an UL
     $pageTree = self::getChildrenAsUL($pageTreeList, 0, " id='perm-tree' class='tree' ", "SiteTree");
     //Re-enable the remove
     SimplifyPermissionProvider::setRemoveEnabled(true);
     return $pageTree;
 }
Пример #2
0
 /**
  * Returns a sorted array suitable for a dropdown with pagetypes and their translated name.
  *
  * @return array
  */
 protected function pageTypeOptions()
 {
     $pageTypes = array();
     $classes = ArrayLib::valueKey(SiteTree::page_type_classes());
     unset($classes['VirtualPage']);
     unset($classes['ErrorPage']);
     unset($classes['RedirectorPage']);
     foreach ($classes as $pageTypeClass) {
         $pageTypes[$pageTypeClass] = singleton($pageTypeClass)->i18n_singular_name();
     }
     asort($pageTypes);
     return $pageTypes;
 }
Пример #3
0
 public function updateExtensibleSearchPageCMSFields(FieldList $fields)
 {
     if ($this->owner->SearchEngine === get_class($this)) {
         $types = SiteTree::page_type_classes();
         $source = array_combine($types, $types);
         // add in any explicitly configured
         asort($source);
         $source = $this->owner->updateSource($source);
         $parsers = $this->owner->getQueryBuilders();
         $options = array();
         foreach ($parsers as $key => $objCls) {
             $obj = new $objCls();
             $options[$key] = $obj->title;
         }
         $fields->addFieldToTab('Root.Main', new DropdownField('QueryType', _t('ExtensibleSearchPage.QUERY_TYPE', 'Query Type'), $options), 'Content');
         ksort($source);
         $source = array_merge($source, ExtensibleSearchPage::config()->additional_search_types);
         $types = MultiValueDropdownField::create('SearchType', _t('ExtensibleSearchPage.SEARCH_ITEM_TYPE', 'Search items of type'), $source);
         $fields->addFieldToTab('Root.Main', $types, 'Content');
         $objFields = $this->owner->getSelectableFields();
         $sortFields = $objFields;
         // Remove content and groups from being sortable (as they are not relevant).
         unset($sortFields['Content']);
         unset($sortFields['Groups']);
         $fields->replaceField('SortBy', new DropdownField('SortBy', _t('ExtensibleSearchPage.SORT_BY', 'Sort By'), $sortFields));
         $fields->addFieldToTab('Root.Main', MultiValueDropdownField::create('SearchOnFields', _t('ExtensibleSearchPage.INCLUDE_FIELDS', 'Search On Fields'), $objFields), 'Content');
         $fields->addFieldToTab('Root.Main', MultiValueTextField::create('ExtraSearchFields', _t('SolrSearch.EXTRA_FIELDS', 'Custom solr fields to search')), 'Content');
         $boostVals = array();
         for ($i = 1; $i <= static::BOOST_MAX; $i++) {
             $boostVals[$i] = $i;
         }
         $fields->addFieldToTab('Root.Main', new KeyValueField('BoostFields', _t('ExtensibleSearchPage.BOOST_FIELDS', 'Boost values'), $objFields, $boostVals), 'Content');
         $fields->addFieldToTab('Root.Main', $f = new KeyValueField('BoostMatchFields', _t('ExtensibleSearchPage.BOOST_MATCH_FIELDS', 'Boost fields with field/value matches'), array(), $boostVals), 'Content');
         $f->setRightTitle('Enter a field name, followed by the value to boost if found in the result set, eg "title:Home" ');
         $fields->addFieldToTab('Root.Main', $kv = new KeyValueField('FilterFields', _t('ExtensibleSearchPage.FILTER_FIELDS', 'Fields to filter by')), 'Content');
         $fields->addFieldToTab('Root.Main', new HeaderField('FacetHeader', _t('ExtensibleSearchPage.FACET_HEADER', 'Facet Settings')), 'Content');
         $fields->addFieldToTab('Root.Main', new MultiValueDropdownField('FacetFields', _t('ExtensibleSearchPage.FACET_FIELDS', 'Fields to create facets for'), $objFields), 'Content');
         $fields->addFieldToTab('Root.Main', new MultiValueTextField('CustomFacetFields', _t('ExtensibleSearchPage.CUSTOM_FACET_FIELDS', 'Additional fields to create facets for')), 'Content');
         $facetMappingFields = $objFields;
         if ($this->owner->CustomFacetFields && ($cff = $this->owner->CustomFacetFields->getValues())) {
             foreach ($cff as $facetField) {
                 $facetMappingFields[$facetField] = $facetField;
             }
         }
         $fields->addFieldToTab('Root.Main', new KeyValueField('FacetMapping', _t('ExtensibleSearchPage.FACET_MAPPING', 'Mapping of facet title to nice title'), $facetMappingFields), 'Content');
         $fields->addFieldToTab('Root.Main', new KeyValueField('FacetQueries', _t('ExtensibleSearchPage.FACET_QUERIES', 'Fields to create query facets for')), 'Content');
         $fields->addFieldToTab('Root.Main', new NumericField('MinFacetCount', _t('ExtensibleSearchPage.MIN_FACET_COUNT', 'Minimum facet count for inclusion in facet results'), 2), 'Content');
     }
     // Make sure previously existing hooks are carried across.
     $this->owner->extend('updateSolrCMSFields', $fields);
 }
 public function getCMSFields()
 {
     $fields = parent::getCMSFields();
     $fields->removeByName('ExcludeItems');
     $fields->removeByName('SourceID');
     $pageClasses = SiteTree::page_type_classes();
     $pageTypes = array();
     foreach ($pageClasses as $class) {
         $pageTypes[$class] = singleton($class)->i18n_singular_name();
     }
     $fields->addFieldsToTab('Root.Main', array(CheckboxField::create('EnforceShowInMenu', _t('ChildPageListBlock.EXCLUDEITEMSNOTINMENUS', "Exclude items which don't show in menus")), MultiValueListField::create('PageTypes', _t('ChildPageListBock.ONLYINCLUDETYPES', 'Only include these page types'), $pageTypes)->setRightTitle('Leave blank to include all types'), OptionsetField::create('AbsoluteSource', _t('ChildPageListBock.SOURCEOPTION', 'Source option'), array(false => _t('ChildPageListBock.SOURCECURRENTPAGE', 'Current page being viewed'), true => _t('ChildPageListBock.SOURCESPECIFICPAGE', 'A specific page'))), TreeDropdownField::create('SourceID', _t('ChildPageListBock.SOURCEPAGE', 'Source page'), 'SiteTree')->displayIf("AbsoluteSource")->isEqualTo(1)->end()));
     if ($this->AbsoluteSource) {
         $kids = $this->Source()->AllChildren();
         if ($kids && $kids->Count()) {
             $fields->addFieldToTab('Root.Main', MultiValueListField::create('ExcludeItems', _t('ChildPageListBock.EXCLUDECHILDREN', 'Exclude these children'), $kids->map('ID', 'Title'))->displayIf("AbsoluteSource")->isEqualTo(1)->end());
         }
     }
     return $fields;
 }
 /**
  * Determine the list of classnames and titles allowed for a given parent object
  *
  * @param SiteTree $parent
  * @return boolean
  */
 public function getAllowedChildren(SiteTree $parent = null)
 {
     if (!$parent || !$parent->canAddChildren()) {
         return array();
     }
     $nonHiddenPageTypes = SiteTree::page_type_classes();
     $allowedChildren = $parent->allowedChildren();
     $children = array();
     foreach ($allowedChildren as $class) {
         if (Config::inst()->get($class, "show_in_sitetree") === false) {
             $instance = Injector::inst()->get($class);
             // Note: Second argument to SiteTree::canCreate will support inherited permissions
             // post 3.1.12, and will default to the old permission model in 3.1.11 or below
             // See http://docs.silverstripe.org/en/changelogs/3.1.11
             if ($instance->canCreate(null, array('Parent' => $parent)) && in_array($class, $nonHiddenPageTypes)) {
                 $children[$class] = $instance->i18n_singular_name();
             }
         }
     }
     return $children;
 }
 public function testPageTypeClasses()
 {
     $classes = SiteTree::page_type_classes();
     $this->assertNotContains('SiteTree', $classes, 'Page types do not include base class');
     $this->assertContains('Page', $classes, 'Page types do contain subclasses');
 }
 /**
  * Populates an array of classes in the CMS
  * which allows the user to change the page type.
  *
  * @return DataObjectSet
  */
 public function PageTypes()
 {
     $classes = SiteTree::page_type_classes();
     $result = new DataObjectSet();
     foreach ($classes as $class) {
         $instance = singleton($class);
         if ($instance instanceof HiddenClass) {
             continue;
         }
         if (!$instance->canCreate()) {
             continue;
         }
         // skip this type if it is restricted
         if ($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) {
             continue;
         }
         $addAction = $instance->i18n_singular_name();
         // if we're in translation mode, the link between the translated pagetype
         // title and the actual classname might not be obvious, so we add it in parantheses
         // Example: class "RedirectorPage" has the title "Weiterleitung" in German,
         // so it shows up as "Weiterleitung (RedirectorPage)"
         if (i18n::get_locale() != 'en_US') {
             $addAction .= " ({$class})";
         }
         $result->push(new ArrayData(array('ClassName' => $class, 'AddAction' => $addAction)));
     }
     $result->sort('AddAction');
     return $result;
 }
Пример #8
0
 /**
  * Form used to filter the sitetree. It can only be used via javascript for now.
  * 
  * @return Form
  */
 function SearchTreeForm()
 {
     // get all page types in a dropdown-compatible format
     $pageTypes = SiteTree::page_type_classes();
     array_unshift($pageTypes, 'All');
     $pageTypes = array_combine($pageTypes, $pageTypes);
     asort($pageTypes);
     // get all filter instances
     $filters = ClassInfo::subclassesFor('CMSSiteTreeFilter');
     $filterMap = array();
     // remove base class
     array_shift($filters);
     // add filters to map
     foreach ($filters as $filter) {
         $filterMap[$filter] = call_user_func(array($filter, 'title'));
     }
     // ensure that 'all pages' filter is on top position
     uasort($filterMap, create_function('$a,$b', 'return ($a == "CMSSiteTreeFilter_Search") ? 1 : -1;'));
     $showDefaultFields = array();
     $form = new Form($this, 'SearchTreeForm', new FieldSet($showDefaultFields[] = new DropdownField('FilterClass', _t('CMSMain.SearchTreeFormPagesDropdown', 'Pages'), $filterMap), $showDefaultFields[] = new TextField('Title', _t('CMSMain.TITLEOPT', 'Title')), new TextField('Content', 'Text'), new DateField('EditedSince', _t('CMSMain_left.ss.EDITEDSINCE', 'Edited Since')), new DropdownField('ClassName', 'Page Type', $pageTypes, null, null, 'Any'), new TextField('MenuTitle', _t('CMSMain.MENUTITLEOPT', 'Navigation Label')), new TextField('Status', _t('CMSMain.STATUSOPT', 'Status')), new TextField('MetaDescription', _t('CMSMain.METADESCOPT', 'Description')), new TextField('MetaKeywords', _t('CMSMain.METAKEYWORDSOPT', 'Keywords'))), new FieldSet(new ResetFormAction('clear', _t('CMSMain_left.ss.CLEAR', 'Clear')), new FormAction('doSearchTree', _t('CMSMain_left.ss.SEARCH', 'Search'))));
     $form->setFormMethod('GET');
     $form->disableSecurityToken();
     $form->unsetValidator();
     foreach ($showDefaultFields as $f) {
         $f->addExtraClass('show-default');
     }
     return $form;
 }
Пример #9
0
 /**
  * Populates an array of classes in the CMS
  * which allows the user to change the page type.
  *
  * @return SS_List
  */
 public function PageTypes()
 {
     $classes = SiteTree::page_type_classes();
     $result = new ArrayList();
     foreach ($classes as $class) {
         $instance = singleton($class);
         if ($instance instanceof HiddenClass) {
             continue;
         }
         if (!$instance->canCreate()) {
             continue;
         }
         // skip this type if it is restricted
         if ($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) {
             continue;
         }
         $addAction = $instance->i18n_singular_name();
         // Get description (convert 'Page' to 'SiteTree' for correct localization lookups)
         $description = _t(($class == 'Page' ? 'SiteTree' : $class) . '.DESCRIPTION');
         if (!$description) {
             $description = $instance->uninherited('description');
         }
         if ($class == 'Page' && !$description) {
             $description = singleton('SiteTree')->uninherited('description');
         }
         $result->push(new ArrayData(array('ClassName' => $class, 'AddAction' => $addAction, 'Description' => $description, 'IconURL' => $instance->stat('icon'), 'Title' => singleton($class)->i18n_singular_name())));
     }
     $result = $result->sort('AddAction');
     return $result;
 }
Пример #10
0
 /**
  * Callback to request the list of page types allowed under a given page instance.
  * Provides a slower but more precise response over SiteTreeHints
  *
  * @param SS_HTTPRequest $request
  * @return SS_HTTPResponse
  */
 public function childfilter($request)
 {
     // Check valid parent specified
     $parentID = $request->requestVar('ParentID');
     $parent = SiteTree::get()->byID($parentID);
     if (!$parent || !$parent->exists()) {
         return $this->httpError(404);
     }
     // Build hints specific to this class
     // Identify disallows and set globals
     $classes = SiteTree::page_type_classes();
     $disallowedChildren = array();
     foreach ($classes as $class) {
         $obj = singleton($class);
         if ($obj instanceof HiddenClass) {
             continue;
         }
         if (!$obj->canCreate(null, array('Parent' => $parent))) {
             $disallowedChildren[] = $class;
         }
     }
     $this->extend('updateChildFilter', $disallowedChildren, $parentID);
     return $this->response->addHeader('Content-Type', 'application/json; charset=utf-8')->setBody(Convert::raw2json($disallowedChildren));
 }
Пример #11
0
 public function testPageTypeClasses()
 {
     $classes = SiteTree::page_type_classes();
     $this->assertNotContains('SiteTree', $classes, 'Page types do not include base class');
     $this->assertContains('Page', $classes, 'Page types do contain subclasses');
     // Testing what happens in an incorrect config value is set - hide_ancestor should be a string
     Config::inst()->update('SiteTreeTest_ClassA', 'hide_ancestor', true);
     $newClasses = SiteTree::page_type_classes();
     $this->assertEquals($classes, $newClasses, 'Setting hide_ancestor to a boolean (incorrect) value caused a page class to be hidden');
 }
Пример #12
0
 /**
  * Show the configuration fields for each subsite
  * 
  * @return FieldList
  */
 public function getCMSFields()
 {
     if ($this->ID != 0) {
         $domainTable = new GridField("Domains", _t('Subsite.DomainsListTitle', "Domains"), $this->Domains(), GridFieldConfig_RecordEditor::create(10));
     } else {
         $domainTable = new LiteralField('Domains', '<p>' . _t('Subsite.DOMAINSAVEFIRST', 'You can only add domains after saving for the first time') . '</p>');
     }
     $languageSelector = new DropdownField('Language', $this->fieldLabel('Language'), i18n::get_common_locales());
     $pageTypeMap = array();
     $pageTypes = SiteTree::page_type_classes();
     foreach ($pageTypes as $pageType) {
         $pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
     }
     asort($pageTypeMap);
     $fields = new FieldList($subsiteTabs = new TabSet('Root', new Tab('Configuration', _t('Subsite.TabTitleConfig', 'Configuration'), new HeaderField($this->getClassName() . ' configuration', 2), new TextField('Title', $this->fieldLabel('Title'), $this->Title), new HeaderField(_t('Subsite.DomainsHeadline', "Domains for this subsite")), $domainTable, $languageSelector, new CheckboxField('DefaultSite', $this->fieldLabel('DefaultSite'), $this->DefaultSite), new CheckboxField('IsPublic', $this->fieldLabel('IsPublic'), $this->IsPublic), new DropdownField('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme), new LiteralField('PageTypeBlacklistToggle', sprintf('<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>', _t('Subsite.PageTypeBlacklistField', 'Disallow page types?'))), new CheckboxSetField('PageTypeBlacklist', false, $pageTypeMap))), new HiddenField('ID', '', $this->ID), new HiddenField('IsSubsite', '', 1));
     $subsiteTabs->addExtraClass('subsite-model');
     $this->extend('updateCMSFields', $fields);
     return $fields;
 }
Пример #13
0
 /**
  * Show the configuration fields for each subsite
  *
  * @return FieldList
  */
 public function getCMSFields()
 {
     if ($this->ID != 0) {
         $domainTable = new GridField("Domains", _t('Subsite.DomainsListTitle', "Domains"), $this->Domains(), GridFieldConfig_RecordEditor::create(10));
     } else {
         $domainTable = new LiteralField('Domains', '<p>' . _t('Subsite.DOMAINSAVEFIRST', 'You can only add domains after saving for the first time') . '</p>');
     }
     $languageSelector = new TextField('Language', $this->fieldLabel('Language'));
     $pageTypeMap = array();
     $pageTypes = SiteTree::page_type_classes();
     foreach ($pageTypes as $pageType) {
         $pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
     }
     asort($pageTypeMap);
     $fields = new FieldList($subsiteTabs = new TabSet('Root', new Tab('Configuration', _t('Subsite.TabTitleConfig', 'Configuration'), new HeaderField($this->getClassName() . ' configuration', 2), new TextField('Title', $this->fieldLabel('Title'), $this->Title), new HeaderField(_t('Subsite.DomainsHeadline', "Domains for this subsite")), $domainTable, $languageSelector, new DropdownField('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme))), new HiddenField('ID', '', $this->ID), new HiddenField('IsSubsite', '', 1));
     $subsiteTabs->addExtraClass('subsite-model');
     $this->extend('updateCMSFields', $fields);
     return $fields;
 }
Пример #14
0
	/**
	 * Populates an array of classes in the CMS
	 * which allows the user to change the page type.
	 *
	 * @return SS_List
	 */
	public function PageTypes() {
		$classes = SiteTree::page_type_classes();

		$result = new ArrayList();

		foreach($classes as $class) {
			$instance = singleton($class);

			if($instance instanceof HiddenClass) continue;

			if(!$instance->canCreate()) continue;

			// skip this type if it is restricted
			if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;

			$addAction = $instance->i18n_singular_name();
			
			// Get description
			$description = _t($class . 'DESCRIPTION');
			if(!$description) $description = $instance->uninherited('description');
			if($class == 'Page' && !$description) $description = singleton('SiteTree')->uninherited('description');
			
			$result->push(new ArrayData(array(
				'ClassName' => $class,
				'AddAction' => $addAction,
				'Description' => $description,
				// TODO Sprite support
				'IconURL' => $instance->stat('icon')
			)));
		}
		
		$result->sort('AddAction');
		return $result;
	}
 public function ListOfAllClasses($checkCurrentClass = true)
 {
     if (!self::$list_of_all_classes) {
         $ArrayOfAllClasses = array();
         //$classes = ClassInfo::subclassesFor("SiteTree");
         $classes = SiteTree::page_type_classes();
         $classesToRemove = array();
         foreach ($classes as $className) {
             if (!in_array($className, $this->config()->get("classes_to_exclude"))) {
                 if ($this->showAll) {
                     $objects = $className::get()->filter(array("ClassName" => $className))->sort("RAND() ASC")->limit(25);
                     $count = 0;
                     if ($objects->count()) {
                         foreach ($objects as $obj) {
                             if (!$count) {
                                 if ($ancestorToHide = $obj->stat('hide_ancestor')) {
                                     $classesToRemove[] = $ancestorToHide;
                                 }
                             }
                             $object = $this->createPageObject($obj, $count++);
                             $ArrayOfAllClasses[$object->indexNumber] = clone $object;
                         }
                     }
                 } else {
                     $obj = null;
                     $obj = $className::get()->filter(array("ClassName" => $className))->sort("RAND() ASC")->limit(1)->first();
                     if ($obj) {
                         $count = SiteTree::get()->filter(array("ClassName" => $obj->ClassName))->count();
                     } else {
                         $obj = $className::create();
                         $count = 0;
                     }
                     if ($ancestorToHide = $obj->stat('hide_ancestor')) {
                         $classesToRemove[] = $ancestorToHide;
                     }
                     $object = $this->createPageObject($obj, $count);
                     $object->TemplateOverviewDescription = $this->TemplateDetails($className);
                     $ArrayOfAllClasses[$object->indexNumber] = clone $object;
                 }
             }
         }
         //remove the hidden ancestors...
         if ($classesToRemove && count($classesToRemove)) {
             $classesToRemove = array_unique($classesToRemove);
             // unset from $classes
             foreach ($ArrayOfAllClasses as $tempKey => $tempClass) {
                 if (in_array($tempClass->ClassName, $classesToRemove)) {
                     unset($ArrayOfAllClasses[$tempKey]);
                 }
             }
         }
         ksort($ArrayOfAllClasses);
         self::$list_of_all_classes = new ArrayList();
         $currentClassname = '';
         if ($checkCurrentClass) {
             if ($c = Controller::curr()) {
                 if ($d = $c->dataRecord) {
                     $currentClassname = $d->ClassName;
                 }
             }
         }
         if (count($ArrayOfAllClasses)) {
             foreach ($ArrayOfAllClasses as $item) {
                 if ($item->ClassName == $currentClassname) {
                     $item->LinkingMode = "current";
                 } else {
                     $item->LinkingMode = "link";
                 }
                 self::$list_of_all_classes->push($item);
             }
         }
     }
     return self::$list_of_all_classes;
 }
 public function getCMSFields()
 {
     $fields = parent::getCMSFields();
     Requirements::css(EXTENSIBLE_SEARCH_PATH . '/css/extensible-search.css');
     // Restrict the search suggestion approval appropriately.
     $user = Member::currentUserID();
     if (Permission::checkMember($user, 'EXTENSIBLE_SEARCH_SUGGESTIONS')) {
         Requirements::javascript(EXTENSIBLE_SEARCH_PATH . '/javascript/extensible-search-approval.js');
     }
     // Determine if full text search is enabled.
     $engines = array('' => '');
     $searchable = Config::inst()->get('FulltextSearchable', 'searchable_classes');
     if (is_array($searchable) && count($searchable) > 0) {
         $engines['Full-Text'] = 'Full-Text';
     }
     // Retrieve a list of search engine extensions currently applied that end with 'Search'.
     $extensions = $this->get_extensions(get_class());
     foreach ($extensions as $extension) {
         $reversed = strrev($extension);
         if (strpos($reversed, strrev('Search')) === 0) {
             $engine = strrev(substr($reversed, 6));
             $engines[$engine] = $engine;
         }
     }
     // Allow selection of the search engine extension to use.
     $fields->addFieldToTab('Root.Main', new DropdownField('SearchEngine', 'Search Engine', $engines), 'Content');
     // Make sure a search engine is being used before allowing customisation.
     if ($this->SearchEngine) {
         // Construct the support array to determine the CMS customisation available to the current search engine/wrapper.
         $support = self::$support;
         if ($this->SearchEngine !== 'Full-Text' && $this->extension_instances) {
             $engine = "{$this->SearchEngine}Search";
             foreach ($this->extension_instances as $instance) {
                 if (get_class($instance) === $engine) {
                     $instance->setOwner($this);
                     if (isset($instance::$support)) {
                         $support = array_merge($support, $instance::$support);
                     }
                     $instance->clearOwner();
                     break;
                 }
             }
         }
         // Use the support array to determine the CMS customisation available to the current search engine/wrapper.
         if ($support['StartWithListing']) {
             $fields->addFieldToTab('Root.Main', new CheckboxField('StartWithListing', _t('ExtensibleSearchPage.START_LISTING', 'Display initial listing - useful for filterable "data type" lists')), 'Content');
         }
         if (class_exists('ListingTemplate') && $support['ListingTemplateID']) {
             $templates = DataObject::get('ListingTemplate');
             if ($templates) {
                 $templates = $templates->map();
             } else {
                 $templates = array();
             }
             $label = _t('ExtensibleSearchPage.CONTENT_TEMPLATE', 'Listing Template - if not set, theme template will be used');
             $fields->addFieldToTab('Root.Main', $template = DropdownField::create('ListingTemplateID', $label, $templates, '', null)->setEmptyString('(results template)'), 'Content');
             $template->setEmptyString('(results template)');
         }
         if ($support['ResultsPerPage']) {
             $perPage = array('5' => '5', '10' => '10', '15' => '15', '20' => '20');
             $fields->addFieldToTab('Root.Main', new DropdownField('ResultsPerPage', _t('ExtensibleSearchPage.RESULTS_PER_PAGE', 'Results per page'), $perPage), 'Content');
         }
         if ($support['SearchTrees']) {
             $fields->addFieldToTab('Root.Main', new TreeMultiselectField('SearchTrees', 'Restrict results to these subtrees', 'Page'), 'Content');
         }
         if (!$this->SortBy) {
             $this->SortBy = 'Created';
         }
         $objFields = $this->getSelectableFields();
         if ($support['SortBy']) {
             $sortFields = $objFields;
             // Remove content and groups from being sortable (as they are not relevant).
             unset($sortFields['Content']);
             unset($sortFields['Groups']);
             $fields->addFieldToTab('Root.Main', new DropdownField('SortBy', _t('ExtensibleSearchPage.SORT_BY', 'Sort By'), $sortFields), 'Content');
         }
         if ($support['SortDir']) {
             $fields->addFieldToTab('Root.Main', new DropdownField('SortDir', _t('ExtensibleSearchPage.SORT_DIR', 'Sort Direction'), $this->dbObject('SortDir')->enumValues()), 'Content');
         }
         $types = SiteTree::page_type_classes();
         $source = array_combine($types, $types);
         if ($this->SearchEngine !== 'Full-Text' && $this->extension_instances) {
             $engine = "{$this->SearchEngine}Search";
             foreach ($this->extension_instances as $instance) {
                 if (get_class($instance) === $engine) {
                     $instance->setOwner($this);
                     // Trigger the following methods on the current search engine extension.
                     if (method_exists($instance, 'updateSource')) {
                         // add in any explicitly configured
                         asort($source);
                         $instance->updateSource($source);
                     }
                     if (method_exists($instance, 'getQueryBuilders') && $support['QueryType']) {
                         $parsers = $instance->getQueryBuilders();
                         $options = array();
                         foreach ($parsers as $key => $objCls) {
                             $obj = new $objCls();
                             $options[$key] = $obj->title;
                         }
                         $fields->addFieldToTab('Root.Main', new DropdownField('QueryType', _t('ExtensibleSearchPage.QUERY_TYPE', 'Query Type'), $options), 'Content');
                     }
                     $instance->clearOwner();
                     break;
                 }
             }
         }
         if ($support['SearchType']) {
             ksort($source);
             $source = array_merge($source, self::$additional_search_types);
             $types = new MultiValueDropdownField('SearchType', _t('ExtensibleSearchPage.SEARCH_ITEM_TYPE', 'Search items of type'), $source);
             $fields->addFieldToTab('Root.Main', $types, 'Content');
         }
         if ($support['SearchOnFields']) {
             $fields->addFieldToTab('Root.Main', new MultiValueDropdownField('SearchOnFields', _t('ExtensibleSearchPage.INCLUDE_FIELDS', 'Search On Fields'), $objFields), 'Content');
         }
         $boostVals = array();
         for ($i = 1; $i <= 5; $i++) {
             $boostVals[$i] = $i;
         }
         if ($support['BoostFields']) {
             $fields->addFieldToTab('Root.Main', new KeyValueField('BoostFields', _t('ExtensibleSearchPage.BOOST_FIELDS', 'Boost values'), $objFields, $boostVals), 'Content');
         }
         if ($support['BoostMatchFields']) {
             $fields->addFieldToTab('Root.Main', $f = new KeyValueField('BoostMatchFields', _t('ExtensibleSearchPage.BOOST_MATCH_FIELDS', 'Boost fields with field/value matches'), array(), $boostVals), 'Content');
             $f->setRightTitle('Enter a field name, followed by the value to boost if found in the result set, eg "title:Home" ');
         }
         if ($support['FilterFields']) {
             $fields->addFieldToTab('Root.Main', $kv = new KeyValueField('FilterFields', _t('ExtensibleSearchPage.FILTER_FIELDS', 'Fields to filter by')), 'Content');
         }
         if ($this->SearchEngine !== 'Full-Text') {
             $fields->addFieldToTab('Root.Main', new HeaderField('FacetHeader', _t('ExtensibleSearchPage.FACET_HEADER', 'Facet Settings')), 'Content');
         }
         if ($support['FacetFields']) {
             $fields->addFieldToTab('Root.Main', new MultiValueDropdownField('FacetFields', _t('ExtensibleSearchPage.FACET_FIELDS', 'Fields to create facets for'), $objFields), 'Content');
         }
         if ($support['CustomFacetFields']) {
             $fields->addFieldToTab('Root.Main', new MultiValueTextField('CustomFacetFields', _t('ExtensibleSearchPage.CUSTOM_FACET_FIELDS', 'Additional fields to create facets for')), 'Content');
         }
         if ($support['FacetMapping']) {
             $facetMappingFields = $objFields;
             if ($this->CustomFacetFields && ($cff = $this->CustomFacetFields->getValues())) {
                 foreach ($cff as $facetField) {
                     $facetMappingFields[$facetField] = $facetField;
                 }
             }
             $fields->addFieldToTab('Root.Main', new KeyValueField('FacetMapping', _t('ExtensibleSearchPage.FACET_MAPPING', 'Mapping of facet title to nice title'), $facetMappingFields), 'Content');
         }
         if ($support['FacetQueries']) {
             $fields->addFieldToTab('Root.Main', new KeyValueField('FacetQueries', _t('ExtensibleSearchPage.FACET_QUERIES', 'Fields to create query facets for')), 'Content');
         }
         if ($support['MinFacetCount']) {
             $fields->addFieldToTab('Root.Main', new NumericField('MinFacetCount', _t('ExtensibleSearchPage.MIN_FACET_COUNT', 'Minimum facet count for inclusion in facet results'), 2), 'Content');
         }
         $this->extend('updateExtensibleSearchPageCMSFields', $fields);
     } else {
         $fields->addFieldToTab('Root.Main', LiteralField::create('SearchEngineNotification', "<p class='extensible-search notification'><strong>Select a Search Engine</strong></p>"), 'Title');
     }
     // Retrieve the extensible search analytics, when enabled.
     if (Config::inst()->get('ExtensibleSearch', 'enable_analytics')) {
         // Retrieve the search analytics.
         $history = $this->History();
         $query = new SQLQuery("Term, COUNT(*) AS Frequency, ((COUNT(*) * 100.00) / {$history->count()}) AS FrequencyPercentage, AVG(Time) AS AverageTimeTaken, (Results > 0) AS Results", 'ExtensibleSearch', "ExtensibleSearchPageID = {$this->ID}", array('Frequency' => 'DESC', 'Term' => 'ASC'), 'Term');
         $analytics = ArrayList::create();
         foreach ($query->execute() as $result) {
             $result = ArrayData::create($result);
             $result->FrequencyPercentage = sprintf('%.2f %%', $result->FrequencyPercentage);
             $result->AverageTimeTaken = sprintf('%.5f', $result->AverageTimeTaken);
             $result->Results = $result->Results ? 'true' : 'false';
             $analytics->push($result);
         }
         // Instantiate the search analytic summary display.
         $fields->addFieldToTab('Root.SearchAnalytics', $summary = GridField::create('Summary', 'Summary', $analytics)->setModelClass('ExtensibleSearch'));
         $summaryConfiguration = $summary->getConfig();
         $summaryConfiguration->removeComponentsByType('GridFieldFilterHeader');
         $summaryConfiguration->addComponent($summaryExport = new GridFieldExportButton());
         $summaryConfiguration->getComponentByType('GridFieldSortableHeader')->setFieldSorting(array('FrequencyPercentage' => 'Frequency'));
         // Update the export fields, since we're not using a data list.
         $summaryDisplay = array('Term' => 'Search Term', 'Frequency' => 'Frequency', 'FrequencyPercentage' => 'Frequency %', 'AverageTimeTaken' => 'Average Time Taken (s)', 'Results' => 'Has Results?');
         $summaryExport->setExportColumns($summaryDisplay);
         // Update the summary fields.
         $summaryConfiguration->getComponentByType('GridFieldDataColumns')->setDisplayFields($summaryDisplay);
         // Instantiate the search analytic history display.
         $fields->addFieldToTab('Root.SearchAnalytics', $history = GridField::create('History', 'History', $history)->setModelClass('ExtensibleSearch'));
         $historyConfiguration = $history->getConfig();
         $historyConfiguration->removeComponentsByType('GridFieldFilterHeader');
         $historyConfiguration->addComponent($historyExport = new GridFieldExportButton());
         // Update the custom summary fields to be sortable.
         $historyConfiguration->getComponentByType('GridFieldSortableHeader')->setFieldSorting(array('TimeSummary' => 'Created', 'TimeTakenSummary' => 'Time'));
     }
     // Retrieve the extensible search suggestions, when enabled.
     if (Config::inst()->get('ExtensibleSearchSuggestion', 'enable_suggestions')) {
         // Instantiate the search suggestion display.
         $fields->addFieldToTab('Root.SearchSuggestions', GridField::create('Suggestions', 'Suggestions', $this->Suggestions(), $suggestionsConfiguration = GridFieldConfig_RecordEditor::create())->setModelClass('ExtensibleSearchSuggestion'));
         $suggestionsConfiguration->removeComponentsByType('GridFieldFilterHeader');
         // Update the custom summary fields to be sortable.
         $suggestionsConfiguration->getComponentByType('GridFieldSortableHeader')->setFieldSorting(array('FrequencySummary' => 'Frequency', 'FrequencyPercentage' => 'Frequency', 'ApprovedField' => 'Approved'));
     }
     return $fields;
 }