/modules/cms/widgets/TemplateList.php
PHP | 388 lines | 249 code | 78 blank | 61 comment | 28 complexity | 6b16ca9e8e33eafd075a181dbe75c884 MD5 | raw file
- <?php namespace Cms\Widgets;
- use Str;
- use File;
- use Input;
- use Request;
- use Response;
- use Cms\Classes\Theme;
- use Backend\Classes\WidgetBase;
- /**
- * Template list widget.
- * This widget displays templates of different types.
- *
- * @package october\cms
- * @author Alexey Bobkov, Samuel Georges
- */
- class TemplateList extends WidgetBase
- {
- use \Backend\Traits\SelectableWidget;
- protected $searchTerm = false;
- protected $dataSource;
- protected $theme;
- protected $groupStatusCache = false;
- /**
- * @var string object property to use as a title.
- */
- public $titleProperty;
- /**
- * @var array a list of object properties to use in the description area.
- * The array should include the property names and corresponding titles:
- * ['url'=>'URL']
- */
- public $descriptionProperties = [];
- /**
- * @var string object property to use as a description.
- */
- public $descriptionProperty;
- /**
- * @var string Message to display when there are no records in the list.
- */
- public $noRecordsMessage = 'No records found';
- /**
- * @var string Message to display when the Delete button is clicked.
- */
- public $deleteConfirmation = 'Delete selected templates?';
- /**
- * @var string Specifies the item type.
- */
- public $itemType;
- /**
- * @var string Extra CSS class name to apply to the control.
- */
- public $controlClass = null;
- /**
- * @var string A list of file name patterns to suppress / hide.
- */
- public $ignoreDirectories = [];
- /*
- * Public methods
- */
- public function __construct($controller, $alias, callable $dataSource)
- {
- $this->alias = $alias;
- $this->dataSource = $dataSource;
- $this->theme = Theme::getEditTheme();
- $this->selectionInputName = 'template';
- parent::__construct($controller, []);
- if (!Request::isXmlHttpRequest()) {
- $this->resetSelection();
- }
- $configFile = 'config_' . snake_case($alias) .'.yaml';
- $config = $this->makeConfig($configFile);
- foreach ($config as $field => $value) {
- if (property_exists($this, $field)) {
- $this->$field = $value;
- }
- }
- $this->bindToController();
- }
- /**
- * Renders the widget.
- * @return string
- */
- public function render()
- {
- $toolbarClass = Str::contains($this->controlClass, 'hero') ? 'separator' : null;
- $this->vars['toolbarClass'] = $toolbarClass;
- return $this->makePartial('body', [
- 'data' => $this->getData()
- ]);
- }
- /*
- * Event handlers
- */
- public function onSearch()
- {
- $this->setSearchTerm(Input::get('search'));
- $this->extendSelection();
- return $this->updateList();
- }
- public function onGroupStatusUpdate()
- {
- $this->setGroupStatus(Input::get('group'), Input::get('status'));
- }
- public function onUpdate()
- {
- $this->extendSelection();
- return $this->updateList();
- }
- //
- // Methods for the internal use
- //
- protected function getData()
- {
- /*
- * Load the data
- */
- $items = call_user_func($this->dataSource);
- if ($items instanceof \October\Rain\Support\Collection) {
- $items = $items->all();
- }
- $items = $this->removeIgnoredDirectories($items);
- $items = array_map([$this, 'normalizeItem'], $items);
- usort($items, function ($a, $b) {
- return strcmp($a->fileName, $b->fileName);
- });
- /*
- * Apply the search
- */
- $filteredItems = [];
- $searchTerm = Str::lower($this->getSearchTerm());
- if (strlen($searchTerm)) {
- /*
- * Exact
- */
- foreach ($items as $index => $item) {
- if ($this->itemContainsWord($searchTerm, $item, true)) {
- $filteredItems[] = $item;
- unset($items[$index]);
- }
- }
- /*
- * Fuzzy
- */
- $words = explode(' ', $searchTerm);
- foreach ($items as $item) {
- if ($this->itemMatchesSearch($words, $item)) {
- $filteredItems[] = $item;
- }
- }
- }
- else {
- $filteredItems = $items;
- }
- /*
- * Group the items
- */
- $result = [];
- $foundGroups = [];
- foreach ($filteredItems as $itemData) {
- $pos = strpos($itemData->fileName, '/');
- if ($pos !== false) {
- $group = substr($itemData->fileName, 0, $pos);
- if (!array_key_exists($group, $foundGroups)) {
- $newGroup = (object)[
- 'title' => $group,
- 'items' => []
- ];
- $foundGroups[$group] = $newGroup;
- }
- $foundGroups[$group]->items[] = $itemData;
- }
- else {
- $result[] = $itemData;
- }
- }
- foreach ($foundGroups as $group) {
- $result[] = $group;
- }
- return $result;
- }
- protected function removeIgnoredDirectories($items)
- {
- if (!$this->ignoreDirectories) {
- return $items;
- }
- $ignoreCache = [];
- $items = array_filter($items, function($item) use (&$ignoreCache) {
- $fileName = $item->getBaseFileName();
- $dirName = dirname($fileName);
- if (isset($ignoreCache[$dirName])) {
- return false;
- }
- foreach ($this->ignoreDirectories as $ignoreDir) {
- if (File::fileNameMatch($dirName, $ignoreDir)) {
- $ignoreCache[$dirName] = true;
- return false;
- }
- }
- return true;
- });
- return $items;
- }
- protected function normalizeItem($item)
- {
- $description = null;
- if ($descriptionProperty = $this->descriptionProperty) {
- $description = $item->$descriptionProperty;
- }
- $descriptions = [];
- foreach ($this->descriptionProperties as $property => $title) {
- if ($item->$property) {
- $descriptions[$title] = $item->$property;
- }
- }
- $result = [
- 'title' => $this->getItemTitle($item),
- 'fileName' => $item->getFileName(),
- 'description' => $description,
- 'descriptions' => $descriptions
- ];
- return (object) $result;
- }
- protected function getItemTitle($item)
- {
- $titleProperty = $this->titleProperty;
- if ($titleProperty) {
- return $item->$titleProperty ?: basename($item->getFileName());
- }
- return basename($item->getFileName());
- }
- protected function setSearchTerm($term)
- {
- $this->searchTerm = trim($term);
- $this->putSession('search', $this->searchTerm);
- }
- protected function getSearchTerm()
- {
- return $this->searchTerm !== false ? $this->searchTerm : $this->getSession('search');
- }
- protected function updateList()
- {
- return [
- '#'.$this->getId('template-list') => $this->makePartial('items', ['items' => $this->getData()])
- ];
- }
- protected function itemMatchesSearch($words, $item)
- {
- foreach ($words as $word) {
- $word = trim($word);
- if (!strlen($word)) {
- continue;
- }
- if (!$this->itemContainsWord($word, $item)) {
- return false;
- }
- }
- return true;
- }
- protected function itemContainsWord($word, $item, $exact = false)
- {
- $operator = $exact ? 'is' : 'contains';
- if (strlen($item->title)) {
- if (Str::$operator(Str::lower($item->title), $word)) {
- return true;
- }
- }
- if (Str::$operator(Str::lower($item->fileName), $word)) {
- return true;
- }
- if (Str::$operator(Str::lower($item->description), $word) && strlen($item->description)) {
- return true;
- }
- foreach ($item->descriptions as $value) {
- if (Str::$operator(Str::lower($value), $word) && strlen($value)) {
- return true;
- }
- }
- return false;
- }
- protected function getGroupStatus($group)
- {
- $statuses = $this->getGroupStatuses();
- if (array_key_exists($group, $statuses)) {
- return $statuses[$group];
- }
- return false;
- }
- protected function getThemeSessionKey($prefix)
- {
- return $prefix.$this->theme->getDirName();
- }
- protected function getGroupStatuses()
- {
- if ($this->groupStatusCache !== false) {
- return $this->groupStatusCache;
- }
- $groups = $this->getSession($this->getThemeSessionKey('groups'), []);
- if (!is_array($groups)) {
- return $this->groupStatusCache = [];
- }
- return $this->groupStatusCache = $groups;
- }
- protected function setGroupStatus($group, $status)
- {
- $statuses = $this->getGroupStatuses();
- $statuses[$group] = $status;
- $this->groupStatusCache = $statuses;
- $this->putSession($this->getThemeSessionKey('groups'), $statuses);
- }
- }