sunnysideup /
silverstripe-site-wide-search
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace Sunnysideup\SiteWideSearch\Admin; |
||||
| 4 | |||||
| 5 | use SilverStripe\Admin\LeftAndMain; |
||||
| 6 | use SilverStripe\Assets\File; |
||||
| 7 | use SilverStripe\Control\HTTPResponse; |
||||
| 8 | use SilverStripe\Core\ClassInfo; |
||||
| 9 | use SilverStripe\Core\Environment; |
||||
| 10 | use SilverStripe\Core\Injector\Injector; |
||||
| 11 | use SilverStripe\Forms\CheckboxField; |
||||
| 12 | use SilverStripe\Forms\Form; |
||||
| 13 | use SilverStripe\Forms\FormAction; |
||||
| 14 | use SilverStripe\Forms\HiddenField; |
||||
| 15 | use SilverStripe\Forms\HTMLReadonlyField; |
||||
| 16 | use SilverStripe\Forms\OptionsetField; |
||||
| 17 | use SilverStripe\Forms\TextField; |
||||
| 18 | use SilverStripe\Forms\ToggleCompositeField; |
||||
| 19 | use SilverStripe\ORM\ArrayList; |
||||
| 20 | use SilverStripe\ORM\FieldType\DBField; |
||||
| 21 | use SilverStripe\Security\PermissionProvider; |
||||
| 22 | use Sunnysideup\SiteWideSearch\Api\SearchApi; |
||||
| 23 | use Sunnysideup\SiteWideSearch\QuickSearches\QuickSearchBaseClass; |
||||
| 24 | |||||
| 25 | /** |
||||
| 26 | * Class \Sunnysideup\SiteWideSearch\Admin\SearchAdmin |
||||
| 27 | * |
||||
| 28 | */ |
||||
| 29 | class SearchAdmin extends LeftAndMain implements PermissionProvider |
||||
| 30 | { |
||||
| 31 | protected $listHTML = ''; |
||||
| 32 | |||||
| 33 | protected $keywords = ''; |
||||
| 34 | |||||
| 35 | protected $replace = ''; |
||||
| 36 | |||||
| 37 | protected $applyReplace = false; |
||||
| 38 | |||||
| 39 | protected $quickSearchType = ''; |
||||
| 40 | |||||
| 41 | protected $searchWholePhrase = true; |
||||
| 42 | |||||
| 43 | protected $rawData; |
||||
| 44 | |||||
| 45 | private static $default_quick_search_type = 'limited'; |
||||
| 46 | |||||
| 47 | private static $url_segment = 'find'; |
||||
| 48 | |||||
| 49 | private static $menu_title = 'Search'; |
||||
| 50 | |||||
| 51 | private static $menu_icon_class = 'font-icon-p-search'; |
||||
| 52 | |||||
| 53 | private static $menu_priority = 9999999999; |
||||
| 54 | |||||
| 55 | private static $required_permission_codes = [ |
||||
| 56 | 'CMS_ACCESS_SITE_WIDE_SEARCH', |
||||
| 57 | ]; |
||||
| 58 | |||||
| 59 | protected function init() |
||||
| 60 | { |
||||
| 61 | if ($this->request->param('Action') && empty($this->request->postVars())) { |
||||
| 62 | $this->redirect('/admin/find'); |
||||
| 63 | } |
||||
| 64 | parent::init(); |
||||
| 65 | } |
||||
| 66 | |||||
| 67 | public function getEditForm($id = null, $fields = null) |
||||
| 68 | { |
||||
| 69 | $form = parent::getEditForm($id, $fields); |
||||
| 70 | $fields = $form->Fields(); |
||||
| 71 | |||||
| 72 | // if ($form instanceof HTTPResponse) { |
||||
| 73 | // return $form; |
||||
| 74 | // } |
||||
| 75 | $fields->push( |
||||
| 76 | (new TextField('Keywords', 'Keyword(s)', $this->keywords ?? '')) |
||||
| 77 | ->setAttribute('placeholder', 'e.g. agreement') |
||||
| 78 | ); |
||||
| 79 | $fields->push( |
||||
| 80 | (new HiddenField('IsSubmitHiddenField', 'IsSubmitHiddenField', 1)) |
||||
| 81 | ); |
||||
| 82 | |||||
| 83 | $options = QuickSearchBaseClass::get_list_of_quick_searches(); |
||||
| 84 | $fields->push( |
||||
| 85 | OptionsetField::create( |
||||
| 86 | 'QuickSearchType', |
||||
| 87 | 'Quick Search', |
||||
| 88 | $options |
||||
| 89 | )->setValue($this->bestSearchType()) |
||||
| 90 | ); |
||||
| 91 | |||||
| 92 | $fields->push( |
||||
| 93 | (new CheckboxField('SearchWholePhrase', 'Search exact phrase', $this->searchWholePhrase)) |
||||
| 94 | ->setDescription('If ticked, only items will be included that includes the whole phrase (e.g. "New Zealand", rather than anything that includes "New" OR "Zealand")') |
||||
| 95 | ); |
||||
| 96 | $fields->push( |
||||
| 97 | ToggleCompositeField::create( |
||||
| 98 | 'ReplaceToggle', |
||||
| 99 | _t(__CLASS__ . '.ReplaceToggle', 'Replace with ... (optional - make a backup first!)'), |
||||
| 100 | [ |
||||
| 101 | (new CheckboxField('ApplyReplace', 'Run replace (please make sure to make a backup first!)', $this->applyReplace)) |
||||
| 102 | ->setDescription('Check this to replace the searched value set above with its replacement value. Note that searches ignore uppercase / lowercase, but replace actions will only search and replace values with the same upper / lowercase.'), |
||||
| 103 | (new TextField('ReplaceWith', 'Replace (optional - careful!)', $this->replace ?? '')) |
||||
| 104 | ->setAttribute('placeholder', 'e.g. contract - make sure to also tick checkbox below'), |
||||
| 105 | ] |
||||
| 106 | )->setHeadingLevel(4) |
||||
| 107 | ); |
||||
| 108 | |||||
| 109 | if (! $this->getRequest()->requestVar('Keywords')) { |
||||
| 110 | $lastResults = $this->lastSearchResults(); |
||||
| 111 | $resultsTitle = $lastResults instanceof \SilverStripe\ORM\ArrayList ? 'Last Results' : 'Last Edited'; |
||||
| 112 | $this->listHTML = $this->renderWith(self::class . '_Results'); |
||||
| 113 | } else { |
||||
| 114 | $resultsTitle = 'Search Results'; |
||||
| 115 | } |
||||
| 116 | |||||
| 117 | $form->setFormMethod('get', false); |
||||
| 118 | |||||
| 119 | $fields->push( |
||||
| 120 | (new HTMLReadonlyField('List', $resultsTitle, DBField::create_field('HTMLText', $this->listHTML))) |
||||
| 121 | ); |
||||
| 122 | $form->Actions()->push( |
||||
| 123 | FormAction::create('search', 'Find') |
||||
| 124 | ->addExtraClass('btn-primary') |
||||
| 125 | ->setUseButtonTag(true) |
||||
| 126 | ); |
||||
| 127 | $form->addExtraClass('root-form cms-edit-form center fill-height'); |
||||
| 128 | // $form->disableSecurityToken(); |
||||
| 129 | // $form->setFormMethod('get'); |
||||
| 130 | |||||
| 131 | return $form; |
||||
| 132 | } |
||||
| 133 | |||||
| 134 | public function search(array $data, Form $form): HTTPResponse |
||||
| 135 | { |
||||
| 136 | if (empty($data['Keywords'])) { |
||||
| 137 | $form->sessionMessage('Please enter one or more keywords', 'bad'); |
||||
| 138 | |||||
| 139 | return $this->redirectBack(); |
||||
| 140 | } |
||||
| 141 | |||||
| 142 | $request = $this->getRequest(); |
||||
| 143 | |||||
| 144 | $this->rawData = $data; |
||||
| 145 | $this->listHTML = $this->renderWith(self::class . '_Results'); |
||||
| 146 | // Existing or new record? |
||||
| 147 | |||||
| 148 | return $this->getResponseNegotiator()->respond($request); |
||||
| 149 | } |
||||
| 150 | |||||
| 151 | /** |
||||
| 152 | * Only show first element, as the profile form is limited to editing |
||||
| 153 | * the current member it doesn't make much sense to show the member name |
||||
| 154 | * in the breadcrumbs. |
||||
| 155 | * |
||||
| 156 | * @param bool $unlinked |
||||
| 157 | * |
||||
| 158 | * @return ArrayList |
||||
| 159 | */ |
||||
| 160 | public function Breadcrumbs($unlinked = false) |
||||
| 161 | { |
||||
| 162 | $items = parent::Breadcrumbs($unlinked); |
||||
| 163 | |||||
| 164 | return new ArrayList([$items[0]]); |
||||
| 165 | } |
||||
| 166 | |||||
| 167 | public function IsQuickSearch(): bool |
||||
| 168 | { |
||||
| 169 | return $this->quickSearchType === 'limited'; |
||||
| 170 | } |
||||
| 171 | |||||
| 172 | public function SearchResults(): ?ArrayList |
||||
| 173 | { |
||||
| 174 | Environment::increaseTimeLimitTo(300); |
||||
| 175 | Environment::setMemoryLimitMax(-1); |
||||
| 176 | Environment::increaseMemoryLimitTo(-1); |
||||
| 177 | if (empty($this->rawData)) { |
||||
| 178 | $lastResults = $this->lastSearchResults(); |
||||
| 179 | if ($lastResults instanceof \SilverStripe\ORM\ArrayList) { |
||||
| 180 | return $lastResults; |
||||
| 181 | } |
||||
| 182 | } |
||||
| 183 | $this->keywords = $this->workOutString('Keywords', $this->rawData); |
||||
| 184 | $this->quickSearchType = $this->workOutString('QuickSearchType', $this->rawData, $this->bestSearchType()); |
||||
| 185 | $this->searchWholePhrase = $this->workOutBoolean('SearchWholePhrase', $this->rawData, false); |
||||
| 186 | $this->applyReplace = isset($this->rawData['ReplaceWith']) && $this->workOutBoolean('ApplyReplace', $this->rawData, false); |
||||
| 187 | $this->replace = $this->workOutString('ReplaceWith', $this->rawData); |
||||
| 188 | if ($this->applyReplace) { |
||||
| 189 | Injector::inst()->get(SearchApi::class) |
||||
| 190 | ->setQuickSearchType($this->quickSearchType) |
||||
| 191 | ->setSearchWholePhrase(true) // always true |
||||
| 192 | ->setWordsAsString($this->keywords) |
||||
| 193 | ->doReplacement($this->keywords, $this->replace) |
||||
| 194 | ; |
||||
| 195 | $this->applyReplace = false; |
||||
| 196 | } |
||||
| 197 | |||||
| 198 | $results = Injector::inst()->get(SearchApi::class) |
||||
| 199 | ->setQuickSearchType($this->quickSearchType) |
||||
| 200 | ->setSearchWholePhrase($this->searchWholePhrase) |
||||
| 201 | ->setWordsAsString($this->keywords) |
||||
| 202 | ->getLinks(); |
||||
| 203 | if ($results->count() === 1) { |
||||
| 204 | $result = $results->first(); |
||||
| 205 | // files do not re-redirect nicely... |
||||
| 206 | if ($result->HasCMSEditLink && $result->CMSEditLink && ! in_array(File::class, ClassInfo::ancestry($result->ClassName), true)) { |
||||
| 207 | // this is a variable, not a method! |
||||
| 208 | $this->redirect($result->CMSEditLink); |
||||
| 209 | } |
||||
| 210 | } |
||||
| 211 | // Accessing the session |
||||
| 212 | $session = $this->getRequest()->getSession(); |
||||
| 213 | if ($session) { |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 214 | $session->set('QuickSearchLastResults', serialize($results->toArray())); |
||||
| 215 | } |
||||
| 216 | return $results; |
||||
| 217 | } |
||||
| 218 | |||||
| 219 | protected function workOutBoolean(string $fieldName, ?array $data = null, ?bool $default = false): bool |
||||
| 220 | { |
||||
| 221 | return (bool) (isset($data['IsSubmitHiddenField']) ? ! empty($data[$fieldName]) : $default); |
||||
| 222 | } |
||||
| 223 | |||||
| 224 | protected function workOutString(string $fieldName, ?array $data = null, ?string $default = ''): string |
||||
| 225 | { |
||||
| 226 | return trim($data[$fieldName] ?? $default); |
||||
|
0 ignored issues
–
show
It seems like
$data[$fieldName] ?? $default can also be of type null; however, parameter $string of trim() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 227 | } |
||||
| 228 | |||||
| 229 | public function providePermissions() |
||||
| 230 | { |
||||
| 231 | return [ |
||||
| 232 | 'CMS_ACCESS_SITE_WIDE_SEARCH' => [ |
||||
| 233 | 'name' => 'Access to Search Website in the CMS', |
||||
| 234 | 'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access'), |
||||
| 235 | 'help' => 'Allow users to search for documents (all documents will also be checked to see if they are allowed to be viewed)', |
||||
| 236 | ], |
||||
| 237 | ]; |
||||
| 238 | } |
||||
| 239 | |||||
| 240 | protected function bestSearchType(): string |
||||
| 241 | { |
||||
| 242 | // Accessing the session |
||||
| 243 | $session = $this->getRequest()->getSession(); |
||||
| 244 | if ($this->quickSearchType) { |
||||
| 245 | $session->set('QuickSearchType', $this->quickSearchType); |
||||
| 246 | } elseif ($session) { |
||||
|
0 ignored issues
–
show
|
|||||
| 247 | $this->quickSearchType = $session->get('QuickSearchType'); |
||||
| 248 | if (isset($_GET['flush'])) { |
||||
| 249 | $this->quickSearchType = ''; |
||||
| 250 | $session->set('QuickSearchType', ''); |
||||
| 251 | } |
||||
| 252 | } |
||||
| 253 | if (! $this->quickSearchType) { |
||||
| 254 | $this->quickSearchType = $this->Config()->get('default_quick_search_type'); |
||||
| 255 | } |
||||
| 256 | return (string) $this->quickSearchType; |
||||
| 257 | } |
||||
| 258 | |||||
| 259 | protected function lastSearchResults(): ?ArrayList |
||||
| 260 | { |
||||
| 261 | // Accessing the session |
||||
| 262 | $session = $this->getRequest()->getSession(); |
||||
| 263 | if ($session) { |
||||
|
0 ignored issues
–
show
|
|||||
| 264 | if (isset($_GET['flush'])) { |
||||
| 265 | $session->clear('QuickSearchLastResults'); |
||||
| 266 | } else { |
||||
| 267 | $data = $session->get('QuickSearchLastResults'); |
||||
| 268 | if ($data) { |
||||
| 269 | $array = unserialize($data); |
||||
| 270 | $al = ArrayList::create(); |
||||
| 271 | foreach ($array as $item) { |
||||
| 272 | $al->push($item); |
||||
| 273 | } |
||||
| 274 | return $al; |
||||
| 275 | } |
||||
| 276 | } |
||||
| 277 | } |
||||
| 278 | return null; |
||||
| 279 | } |
||||
| 280 | } |
||||
| 281 |