monster_menus-9.0.x-dev/src/Controller/MMTreeBrowserController.php
src/Controller/MMTreeBrowserController.php
<?php
/**
* @file
* Contains \Drupal\monster_menus\Controller\MMTreeBrowserController.
*/
namespace Drupal\monster_menus\Controller;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\StatementInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Render\HtmlResponse;
use Drupal\Core\Render\Markup;
use Drupal\Core\Render\Renderer;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\Entity\MMTree;
use Drupal\monster_menus\MMTreeBrowserDisplay\MMTreeBrowserDisplayInterface;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Fallback;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Groups;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Default controller for the monster_menus module.
*/
class MMTreeBrowserController extends ControllerBase {
/**
* The database connection.
*
* @var Connection
*/
protected $database;
/**
* The current page request.
*
* @var Request
*/
protected $request;
/**
* The renderer service.
*
* @var RendererInterface
*/
protected $renderer;
/**
* The plugin service.
*
* @var PluginManagerInterface
*/
protected $plugin_manager;
/**
* The display plugin.
*
* @var MMTreeBrowserDisplayInterface
*/
protected $plugin;
/**
* The module extension list service.
*
* @var ModuleExtensionList
*/
protected $module_extension_list;
/**
* MM Tree ID of the initially selected page.
*
* @var int
*/
protected $selected;
/**
* MM Tree ID of the topmost page shown in the tree.
*
* @var int
*/
protected $top_mmtid = 1;
/**
* Display mode constant.
*
* @var string
*/
protected $mode = Fallback::BROWSER_MODE_PAGE;
/**
* List of permissions a page can have in order to be visible.
*
* @var string
*/
protected $enabled = Constants::MM_PERMS_READ;
/**
* List of permissions a page can have in order to be selectable.
*
* @var string
*/
protected $selectable = '';
/**
* Title to appear above the browser.
*
* @var string
*/
protected $title = '';
/**
* The field name, bundle and type to display in the right hand pane.
*
* @var string
*/
protected $field_id;
/**
* List of allowed MIME types to display in the right hand pane.
*
* @var string
*/
protected $file_types;
/**
* Minimum width/height, in pixels, of images that are selectable by the user.
*
* @var string
*/
protected $min_wh;
/**
* Constructs a MMTreeBrowserController object.
*
* @param Connection $database
* The database connection.
* @param Request $request
* The parameters of the current request.
*/
public function __construct(Connection $database, Request $request, Renderer $renderer, PluginManagerInterface $plugin_manager, ModuleExtensionList $extension_list_module) {
$this->database = $database;
$this->request = $request;
$this->renderer = $renderer;
$this->plugin_manager = $plugin_manager;
$this->module_extension_list = $extension_list_module;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database'),
$container->get('request_stack')->getCurrentRequest(),
$container->get('renderer'),
$container->get('plugin.manager.mm_tree_browser_display'),
$container->get('extension.list.module')
);
}
private function plugin($mode = '') {
if (empty($this->plugin)) {
$this->plugin = $this->plugin_manager->getInstance(['mode' => $mode ?: $this->request->query->getAlnum('browserMode')]);
}
return $this->plugin;
}
/**
* Get a portion of the MM Tree as JSON.
*
* @param MMTree $mm_tree
* Starting point
* @return JsonResponse
*/
public function getTreeJson(MMTree $mm_tree) {
if (($id = $this->request->query->getInt('_vusr', 1)) > 0) {
$id = $mm_tree->id();
}
return mm_json_response($this->getLeft($id), ['Pragma' => 'no-cache']);
}
/**
* Get the render array for the outer tree structure.
*
* @return HtmlResponse
*/
public function getWrapperRenderable() {
$path = explode('/', preg_replace('{//+}', '/', $this->request->query->get('_path')));
$start = array_shift($path);
$params = explode('-', $start, 9);
if (isset($path[0]) && $path[0] != $params[0]) {
array_unshift($path, $params[0]);
}
elseif (!$path) {
$path[] = 1;
}
$params = array_pad($params, 9, '');
$params = [
'selected' => end($path), // MM Tree ID of the initially selected page.
'top_mmtid' => $path[0], // MM Tree ID of the topmost page shown in the tree.
'mode' => $params[1], // Display mode constant.
'enabled' => $params[3], // List of permissions a page can have in order to be visible.
'selectable' => $params[4], // List of permissions a page can have in order to be selectable.
'title' => $params[5], // Title to appear above the browser.
'field_id' => $params[6], // The field name, bundle, and type to display in the right hand pane.
'file_types' => $params[7], // List of allowed MIME types to display in the right hand pane.
'min_wh' => $params[8], // Minimum width/height, in pixels, of images that are selectable by the user.
];
foreach ($params as $name => $value) {
$this->$name = $value;
}
return mm_page_wrapper(
$this->t('Tree Browser'),
$this->getWrapper(),
['id' => 'mm-media-assist-load', 'class' => ['mm-media-assist']]
);
}
/**
* Get the right hand pane's contents as JSON.
*
* @return JsonResponse
*/
public function getRightJSON() {
$get = $this->request->query->all();
if (empty($get['id']) || !($mmtid = intval(substr($get['id'], 5)))) {
return mm_json_response([]);
}
$mode = $get['browserMode'];
$actions = [];
$dialogs = [];
if (!$this->isBookmarked($mmtid, $mode)) {
$actions['bkmark'] = [
'#type' => 'button',
'#id' => mm_ui_modal_dialog([], $dialogs),
'#value' => $this->t('Bookmark'),
'#attributes' => [
'title' => $this->t('Bookmark this location'),
'rel' => Url::fromRoute('monster_menus.browser_bookmark_add',
['mm_tree' => $mmtid],
['query' => ['browserMode' => $mode]]
)->toString(),
]
];
}
$item = mm_content_get($mmtid, Constants::MM_GET_FLAGS);
$perms = $item->perms = mm_content_user_can($mmtid);
$item->is_group = $perms[Constants::MM_PERMS_IS_GROUP];
$item->is_user = $perms[Constants::MM_PERMS_IS_USER];
$this->plugin()->alterRightButtons($mode, $this->request->query, $item, $perms, $actions, $dialogs);
$actions['close'] = [
'#type' => 'button',
'#value' => $this->t('Close window'),
'#attributes' => ['rel' => '#close'],
];
mm_module_invoke_all_array('mm_browser_buttons_alter', [$mode, $item, &$actions, &$dialogs]);
$content = $this->plugin()->viewRight($mode, $this->request->query, $perms, $item, $this->database);
if ($content instanceof JsonResponse) {
// View function returned JSON, so don't do any further processing.
return $content;
}
$mm_dialogs = $dialogs['#attached']['drupalSettings']['MM']['MMDialog'] ?? [];
if (is_array($content)) {
if (isset($content['#attached']['drupalSettings']['MM']['MMDialog'])) {
$mm_dialogs = array_merge($mm_dialogs, $content['#attached']['drupalSettings']['MM']['MMDialog']);
}
$content = $this->renderer->renderRoot($content);
}
// Get the last viewed item
$lastviewed = '';
if ($last_mmtid = $this->getLastViewed($mode)) {
$lastviewed = $this->getRelativePath($last_mmtid, $get['browserTop']);
}
$this->setLastViewedMMTID(intval(substr($get['id'], 5)));
$actions = ['#type' => 'actions', 'actions' => $actions];
return mm_json_response([
'title' => mm_content_get_name($item),
'links' => $this->renderer->renderRoot($actions),
'body' => $content,
'lastviewed' => $lastviewed,
'dialogs' => $mm_dialogs,
]);
}
/**
* Search for a page/group for right hand pane, returning the matches as JSON.
*
* @return JsonResponse
*/
public function searchJSON() {
$limit = 10;
$mmtid = $this->request->query->getInt('mmtid');
$search = trim(preg_replace('{\W}', ' ', $this->request->query->get('search', '')));
if (empty($mmtid) || empty($search) || !mm_content_user_can($mmtid, Constants::MM_PERMS_READ)) {
return mm_json_response([]);
}
$wheres = [];
foreach (preg_split('{\s+}', $search) as $term) {
// It's safe to use $term in this way as it was sanitized above.
$wheres[] = "(t.name REGEXP '$term' OR t.alias REGEXP '$term') ";
}
$is_group = mm_content_is_group($mmtid);
$groups_mmtid = mm_content_groups_mmtid();
$users_mmtid = mm_content_users_mmtid();
$results = [];
$seen = [];
$n = $page = 0;
$prefix = 'mmbrs-';
$virt_re = FALSE;
if ($mmtid < 0 ) {
$ch = chr(-$mmtid);
$virt_re = $ch == '~' ? '{^[^A-Z]}i' : "{^$ch}i";
$mmtid = $users_mmtid;
}
while ($n <= $limit) {
$q = "SELECT t.mmtid, t.name FROM {mm_tree} t INNER JOIN {mm_tree_parents} p ON p.mmtid = t.mmtid WHERE p.parent = $mmtid AND (" . join(' AND ', $wheres) . ") ORDER BY LENGTH(sort_idx), sort_idx LIMIT $page, " . ($limit * 2);
$q = $this->database->query($q);
$had_result = FALSE;
foreach ($q as $tree) {
$had_result = TRUE;
$perms = mm_content_user_can($tree->mmtid);
if ($perms[Constants::MM_PERMS_READ] && !$perms[Constants::MM_PERMS_IS_RECYCLE_BIN] && !$perms[Constants::MM_PERMS_IS_RECYCLED] && $tree->name[0] != '.') {
$full_path = $path = mm_content_get_parents_with_self($tree->mmtid);
if ($virt_re && $path[1] == $users_mmtid && !preg_match($virt_re, mm_content_get($path[2])->name)) {
continue;
}
if ($is_group) {
array_shift($full_path);
}
if (in_array($groups_mmtid, $path) === $is_group) {
$path = array_slice($path, array_search($mmtid, $path) + 1);
$last = '#';
foreach ($path as $node) {
if (!isset($seen[$node])) {
if ($node == $tree->mmtid) {
if (++$n > $limit) {
break 2;
}
}
$seen[$node] = 1;
$results[] = [
'id' => "$prefix$node",
'parent' => $last,
'text' => $node == $tree->mmtid ? mm_content_expand_name($tree->name) : mm_content_get_name($node),
'state' => [
'opened' => TRUE,
'disabled' => $node != $tree->mmtid,
],
'fullPath' => $node == $tree->mmtid ? implode('/', $full_path) : '',
];
}
$last = "$prefix$node";
}
}
}
}
if (!$had_result) {
break;
}
$page++;
}
return mm_json_response([
'results' => $results,
'overflow' => $n > $limit ? t('(showing the first :num results)', [':num' => $limit]) : FALSE,
]);
}
/**
* Determine whether or not a bookmark already exists.
*
* @return JsonResponse
*/
public function bookmarkExistsJSON() {
return mm_json_response([
'exists' => $this->isBookmarked($this->request->query->getInt('id'), $this->request->query->get('browserMode', ''))
]);
}
/**
* Get all bookmarks as HTML.
*
* @return HtmlResponse
*/
public function getBookmarksHTML() {
// Display bookmarks.
$bookmarks = $this->getBookmarksRenderable($this->request->query->get('browserMode', ''), $this->top_mmtid);
$resp = new HTMLResponse($this->renderer->renderRoot($bookmarks));
$resp->addCacheableDependency(CacheableMetadata::createFromRenderArray($bookmarks));
return $resp;
}
/**
* Get the page last viewed by the user, if any, as JSON.
*
* @return null|JsonResponse
*/
public function getLastViewedJSON() {
$query = $this->request->query;
$return = NULL;
if ($last_mmtid = $this->getLastViewed($query->get('browserMode', ''))) {
$return = mm_json_response(['path' => $this->getRelativePath($last_mmtid, $query->get('browserTop'))]);
}
if ($get_id = $query->get('id', '')) {
$this->setLastViewedMMTID(intval(substr($get_id, 0, 5)));
}
return $return;
}
/**
* Set the last viewed page for the current user.
*/
public function setLastViewed(MMTree $mm_tree) {
$this->setLastViewedMMTID($mm_tree->id());
}
/**
* Set the last viewed page for the current user.
*
* @param int $mmtid
*/
private function setLastViewedMMTID($mmtid) {
$uid = $this->currentUser()->id();
$type = $this->plugin()->getBookmarksType($this->request->query->get('browserMode', '')) . '_last';
$this->database->merge('mm_tree_bookmarks')
->keys(['uid' => $uid, 'type' => $type, 'weight' => 0])
->fields(['data' => $mmtid])
->execute();
}
/**
* Get the UI form to add a bookmark, or submit the form to actually add the
* bookmark.
*
* @param MMTree $mm_tree
* MMTree entity of the page to be added.
* @return Response
* Response object.
* @throws \Exception
*/
public function getAddBookmarkForm(MMTree $mm_tree) {
$user_uid = $this->currentUser()->id();
$mmtid = $mm_tree->id();
$name = mm_content_get_name($mmtid);
$mode = $this->request->query->get('browserMode', '');
if ($this->isBookmarked($mmtid, $mode)) {
$output = [
'#theme' => 'mm_browser_bookmark_add',
'#name' => '',
'#mmtid' => '',
];
}
elseif ($this->request->get('linktitle')) {
$mm_bookmark_serialized = serialize([
'title' => strip_tags($this->request->get('linktitle', '')),
'mmtid' => $mmtid,
]);
$type = $this->plugin()->getBookmarksType($mode);
$transaction = $this->database->startTransaction();
$select = $this->database->select('mm_tree_bookmarks', 'b')
->condition('b.uid', $user_uid)
->condition('b.type', $type);
$select->addExpression('IFNULL(MAX(b.weight), -1) + 1', 'max_weight');
$max_weight = $select->execute()->fetchField();
$this->database->insert('mm_tree_bookmarks')
->fields([
'uid' => $user_uid,
'weight' => $max_weight,
'type' => $type,
'data' => $mm_bookmark_serialized,
])
->execute();
unset($transaction);
return mm_json_response([]);
}
else {
$output = [
'#theme' => 'mm_browser_bookmark_add',
'#name' => $name,
'#mmtid' => $mmtid,
];
}
return new HtmlResponse((string) \Drupal::service('renderer')->renderRoot($output));
}
/**
* Get the bookmarks overview form.
*
* @return HtmlResponse
*/
public function getManageBookmarksForm() {
$mode = $this->plugin()->getBookmarksType($this->request->query->get('browserMode', ''));
$num_rows = $this->database->select('mm_tree_bookmarks', 'b')
->condition('b.uid', $this->currentUser()->id())
->condition('b.type', $mode)
->countQuery()->execute()->fetchField();
$body = [];
if ($num_rows < 1) {
$body[] = ['#markup' => '<div id="message"><p>' . $this->t('No bookmarks found.') . '</p><p><input type="button" onclick="Drupal.mmDialogClose(); return false;" value="' . $this->t('Cancel') . '"></p></div>'];
}
else {
$rows = [];
$result = $this->getBookmarks($mode);
while ($row = $result->fetchAssoc()) {
$ds_data = unserialize($row['data']);
$edit_js = "Drupal.mmBrowserEditBookmarkEdit(event)";
$rows['row_' . $ds_data['mmtid']] = [
'item' => [
'#type' => 'item',
'#markup' => '<div>' . htmlentities($ds_data['title']) . '</div>',
'#wrapper_attributes' => ['name' => $ds_data['mmtid'], 'class' => ['tb-manage-name'], 'ondblclick' => $edit_js],
],
'delete' => mm_ui_js_link_no_href(['title' => $this->t('Remove this bookmark'), 'onclick' => "return Drupal.mmBrowserDeleteBookmarkConfirm(" . $ds_data['mmtid'] . ", this)"], $this->t('Delete')),
'edit' => mm_ui_js_link_no_href(['title' => $this->t('Edit this bookmark'), 'onclick' => 'return jQuery(this).closest("tr").find("td:first").dblclick()'], $this->t('Edit')),
'weight' => [
'#type' => 'weight',
'#title' => $this->t('Weight for @title', ['@title' => $ds_data['mmtid']]),
'#title_display' => 'invisible',
'#delta' => 50,
'#default_value' => count($rows),
'#wrapper_attributes' => ['class' => ['menu-weight'], 'style' => ['display: none']],
],
'#attributes' => ['class' => ['draggable', 'bookmark_tr_' . $ds_data['mmtid']]],
];
}
$body[] = [
'#prefix' => '<div id="tb-manage-body"><form id="manage-bookmarks-form"><div id="manage-bookmarks-div">',
'#type' => 'table',
'#id' => 'manage-bookmarks-table',
'#attributes' => ['class' => 'manage-bookmarks-table'],
'#suffix' => '</div></form>',
] + $rows;
}
return new HtmlResponse((string) \Drupal::service('renderer')->renderRoot($body));
}
private function getBookmarksCacheTag() {
return 'user_bookmarks:' . $this->currentUser()->id();
}
public function invalidateBookmarksCache() {
Cache::invalidateTags([$this->getBookmarksCacheTag()]);
}
/**
* Delete a bookmark.
*
* @param MMTree $mm_tree
* Entity of the bookmarked page to delete.
* @return JsonResponse
* The deleted bookmark's ID.
*/
public function deleteBookmarkJSON(MMTree $mm_tree) {
$mode = $this->request->query->get('browserMode', '');
$result = $this->getBookmarks($mode);
$type = $this->plugin()->getBookmarksType($mode);
while ($row = $result->fetchAssoc()) {
$ds_data = unserialize($row['data']);
if ($ds_data['mmtid'] == $mm_tree->id()) {
$this->database->delete('mm_tree_bookmarks')
->condition('uid', $this->currentUser()->id())
->condition('type', $type)
->condition('weight', $row['weight'])
->execute();
}
}
$this->invalidateBookmarksCache();
return mm_json_response(['mmtid' => $mm_tree->id()]);
}
/**
* Change the title of a bookmark.
*
* @param MMTree $mm_tree
* Entity of the bookmarked page to set the title for.
* @return JsonResponse
* The new title and the bookmark's ID.
*/
public function setBookmarkJSON(MMTree $mm_tree) {
$title = strip_tags($this->request->get('title', ''));
$mode = $this->request->query->get('browserMode', '');
$result = $this->getBookmarks($mode);
$type = $this->plugin()->getBookmarksType($mode);
while ($row = $result->fetchAssoc()) {
$ds_data = unserialize($row['data']);
if ($ds_data['mmtid'] == $mm_tree->id()) {
$ds_data['title'] = $title;
$ds_done = serialize($ds_data);
$this->database->update('mm_tree_bookmarks')
->fields(['data' => $ds_done])
->condition('uid', $this->currentUser()->id())
->condition('type', $type)
->condition('weight', $row['weight'])
->execute();
}
}
$this->invalidateBookmarksCache();
return mm_json_response(['title' => $title, 'mmtid' => $mm_tree->id()]);
}
/**
* Change the order of the list of bookmarks.
*
* @return JsonResponse
* The new order.
* @throws \Exception
*/
public function sortBookmarksJSON() {
$neworder = explode('|', $this->request->get('neworder', ''));
$mode = $this->request->query->get('browserMode', '');
$result = $this->getBookmarks($mode);
$type = $this->plugin()->getBookmarksType($mode);
$uid = $this->currentUser()->id();
$bookmarks = [];
while ($row = $result->fetchAssoc()) {
$ds_data = unserialize($row['data']);
$bookmarks[$ds_data['mmtid']] = $ds_data;
}
$this->database->delete('mm_tree_bookmarks')
->condition('uid', $uid)
->condition('type', $type)
->execute();
$weight = 0;
foreach ($neworder as $weight => $mmtid) {
if (isset($bookmarks[$mmtid])) {
$this->database->insert('mm_tree_bookmarks')
->fields([
'weight' => $weight,
'uid' => $uid,
'type' => $type,
'data' => serialize($bookmarks[$mmtid]),
])->execute();
unset($bookmarks[$mmtid]);
}
}
// Ensure any remaining bookmarks not in $neworder are preserved.
foreach ($bookmarks as $mmtid => $ds_data) {
$this->database->insert('mm_tree_bookmarks')
->fields([
'weight' => $weight++,
'uid' => $uid,
'type' => $type,
'data' => serialize($ds_data)])
->execute();
}
$this->invalidateBookmarksCache();
return mm_json_response(['neworder' => $neworder]);
}
/**
* Check to ensure a bookmark does not already exist prior to adding it.
*
* @param int $mmtid
* MM Tree ID of the bookmark.
* @param string $mode
* Display mode constant.
* @return bool
*/
private function isBookmarked($mmtid, $mode) {
$already_exists = FALSE;
$result = $this->getBookmarks($mode);
foreach ($result as $row) {
$ds_data = unserialize($row->data);
if ($ds_data['mmtid'] == $mmtid) {
$already_exists = TRUE;
break;
}
}
return $already_exists;
}
/**
* Get the outer structure of the tree browser.
*
* @return array
* Render array.
*/
private function getWrapper() {
$this->plugin($this->mode);
if (empty($this->title)) {
$this->title = $this->plugin()->label($this->mode);
}
$this->title = strip_tags($this->title);
if (!$this->top_mmtid) {
$show_root = $this->top_mmtid === '0';
$this->top_mmtid = 1;
}
$out = [];
$instance_id = $this->request->query->getInt('instanceId', 0);
$settings = [
'browserInstanceId' => $instance_id,
'libraryPath' => $this->module_extension_list->getPath('monster_menus') . '/libraries',
'browserDots' => $this->plugin()->showReservedEntries($this->mode),
'browserEnabled' => $this->enabled,
'browserMode' => $this->mode,
'browserSelectable' => $this->selectable,
'browserTop' => $this->top_mmtid,
'browserShowRoot' => (int) !empty($show_root),
'lastBrowserPath' => $this->getRelativePath($this->getLastViewed($this->mode), $this->top_mmtid),
'startBrowserPath' => $this->getRelativePath($this->selected, $this->top_mmtid),
];
if (!empty($this->field_id)) {
$settings['browserFieldID'] = $this->field_id;
}
if (!empty($this->file_types)) {
$settings['browserFileTypes'] = $this->file_types;
}
if (!empty($this->min_wh)) {
$min_wh = explode('x', $this->min_wh);
$settings += ['browserMinW' => $min_wh[0], 'browserMinH' => $min_wh[1]];
}
mm_add_js_setting($out, 'mmBrowser', $settings);
if (str_starts_with(\Drupal::VERSION, '9.')) {
mm_ui_modal_dialog('init', $out);
}
mm_ui_modal_dialog([], $out);
mm_add_library($out, 'mm_browser');
mm_add_library($out, 'jsTree');
// Suppress the admin module
\Drupal::moduleHandler()->invokeAll('suppress');
$out[] = [
'#prefix' => '<div id="mmtree-browse"><div id="mmtree-browse-nav">',
$this->getURLs($this->mode, $this->top_mmtid),
'#suffix' => <<<HTML
<h2 class="mmtree-assist-title">$this->title</h2>
</div>
<div id="mmtree-browse-browser">
<div id="mmtree-browse-tree-wrapper">
<div id="mmtree-browse-tree"></div>
</div>
<div id="mmtree-browse-items">
<div id="mmtree-browse-header">
<h4 id="mmtree-assist-title"></h4>
<div id="mmtree-assist-links"></div>
</div>
<div id="mmtree-assist-content"></div>
</div>
</div>
</div>
HTML
];
return $out;
}
/**
* Create the list of URLs at the top of the tree browser.
*
* @param string $mode
* Display mode constant.
* @param int $top_mmtid
* MM Tree ID of the topmost page shown in the tree.
* @return array
* Render array.
*/
private function getURLs($mode, $top_mmtid) {
// Simple buttons come first.
$urls = [];
$allowed_top = $this->plugin()->getTreeTop($mode);
if ($top_mmtid != $allowed_top) {
$urls[] = ['#markup' => Markup::create('<button onclick="Drupal.mm_browser_goto_top(\'' . $allowed_top . '\');" class="button">' . $this->t('View entire tree') . '</button>')];
}
$urls[] = ['#markup' => Markup::create('<button onclick="Drupal.mm_browser_last_viewed();" id="last-viewed-link" class="button">' . $this->t('Last location') . '</button>')];
$ph = $mode == Groups::BROWSER_MODE_GROUP || $mode == Groups::BROWSER_MODE_ADMIN_GROUP ? $this->t('Group name') : $this->t('Page name or alias');
$arr = [
'buttons' => [
'#prefix' => '<div class="ui-widget bookmarks-list">',
'urls' => [],
'bookmarks' => $this->getBookmarksRenderable($mode, $top_mmtid),
'#suffix' => '</div>',
],
'search' => [
'#markup' => Markup::create('<div id="search-dialog" style="display: none"><label for="search-text">' . $this->t('Find:') . '</label><input type="text" id="search-text" placeholder="' . $ph . '" size="40" autofocus><label for="search-mmtid">' . $this->t('Starting at:') . '</label><select id="search-mmtid"></select><div id="search-results"></div></div>'),
],
];
$urls[] = ['#markup' => Markup::create('<button onclick="Drupal.mm_browser_search();" id="search-link" class="button">' . $this->t('Search') . '</button>')];
foreach (mm_module_invoke_all('mm_browser_navigation', $mode, $top_mmtid) as $url) {
$urls[] = is_array($url) ? $url : ['#markup' => Markup::create($url)];
}
mm_module_invoke_all_array('mm_browser_menu_alter', [$mode, &$urls]);
$arr['buttons']['urls'] = $urls;
drupal_attach_tabledrag($arr, [
'table_id' => 'manage-bookmarks-table',
'action' => 'order',
'relationship' => 'sibling',
'group' => 'menu-weight',
]);
return $arr;
}
private function getBookmarksRenderable($mode, $top_mmtid) {
$manage_url = Url::fromRoute('monster_menus.browser_bookmark_manage', [], ['query' => ['browserMode' => $mode]]);
$bookmarks = [
'dummy' => [
'title' => Markup::create('<div>' . $this->t('Bookmarks') . '</div>'),
'url' => NULL,
],
'org' => [
'title' => $this->t('Organize Bookmarks...'),
'url' => $manage_url,
'attributes' => ['class' => ['organize']],
]
];
$result = $this->getBookmarks($mode);
while ($row = $result->fetchAssoc()) {
$ds_data = unserialize($row['data']);
$bookmarks[] = [
'title' => $ds_data['title'],
'url' => Url::fromUserInput('#', ['fragment' => $this->getRelativePath($ds_data['mmtid'], $top_mmtid)]),
];
}
return [
'#type' => 'dropbutton',
'#links' => $bookmarks,
'#cache' => ['contexts' => ['user'], 'tags' => [$this->getBookmarksCacheTag()]],
'#attributes' => ['class' => ['mm-bookmarks']],
];
}
/**
* @param array $mmtid
* 0 to get just the root, otherwise the MMTID to fetch the children of.
* @return array
* An array of entries, where any sub-entries are nested in 'children'.
*/
private function getLeft($mmtid) {
$mm_children = [];
$get = $this->request->query->all();
$depth = !$mmtid ? 0 : 1;
if ($mmtid == 0 || $mmtid == 1) {
$mmtid = $this->plugin()->getTreeTop($get['browserMode']);
}
$params = [
Constants::MM_GET_TREE_ADD_TO_CACHE => TRUE,
Constants::MM_GET_TREE_FILTER_BINS => FALSE,
Constants::MM_GET_TREE_FILTER_DOTS => $get['browserDots'] == 'true',
Constants::MM_GET_TREE_FILTER_HIDDEN => TRUE,
Constants::MM_GET_TREE_DEPTH => $depth,
Constants::MM_GET_TREE_RETURN_KID_COUNT => TRUE,
Constants::MM_GET_TREE_RETURN_PERMS => TRUE,
];
$this->plugin()->alterLeftQuery($get['browserMode'], $this->request->query, $params);
$list = mm_content_get_tree($mmtid, $params);
if ($depth) {
array_shift($list);
}
foreach ($list as $item) {
$class = [];
$children = TRUE;
$state = [ 'opened' => FALSE, 'disabled' => FALSE, 'selected' => FALSE ];
$attributes = [];
$name = mm_content_get_name($item);
$hidden = $item->state & Constants::MM_GET_TREE_STATE_HIDDEN;
if ($hidden) {
$name .= ' ' . t('(hidden)');
$class['mmtree-hidden'] = 1;
}
$text = $name;
if (!empty($item->fid_list)) {
// We can't get the count in SQL, so count the unique fids/mids here.
$fid_list = array_unique(explode(',', $item->fid_list));
$item->nodecount = count($fid_list);
}
if (isset($item->nodecount) && $item->nodecount > 0) {
$text = t('@name <span class="mmtree-browse-filecount">@count</span>', ['@name' => $name, '@count' => $this->formatPlural($item->nodecount, '(1 item)', '(@count items)')]);
}
if ($item->state & Constants::MM_GET_TREE_STATE_LEAF) {
$class['leaf'] = 1;
$children = FALSE;
}
$denied = $item->state & Constants::MM_GET_TREE_STATE_DENIED || !empty($get['browserEnabled']) && !$item->perms[$get['browserEnabled']];
if ($denied) {
$class['disabled'] = $class['leaf'] = 1;
$state['disabled'] = TRUE;
$children = FALSE;
}
$attributes['class'] = join(' ', array_keys($class));
$mm_children[] = [
'id' => 'mmbr-' . $item->mmtid,
'text' => $text,
'state' => $state,
'children' => $children,
'a_attr' => $attributes,
];
}
return $mm_children;
}
/**
* Get a path that is relative to the displayed tree's root.
*
* @param int $mmtid
* MM Tree ID of the page in question.
* @param int $top
* MM Tree ID of the tree's root.
* @return string
* The relative path.
*/
private function getRelativePath($mmtid, $top) {
$path = mm_content_get_full_path($mmtid);
if (preg_match('{\b' . $top . '(/|$)(.*)}', $path, $matches)) {
return $matches[0];
}
return $path;
}
/**
* Get the MMTID of the page last viewed by the current user.
*
* @param $mode
* Display mode constant.
* @return int|null
* MM Tree ID of the last viewed page.
*/
private function getLastViewed($mode) {
return $this->database->select('mm_tree_bookmarks', 'b')
->fields('b', ['data'])
->condition('b.uid', $this->currentUser()->id())
->condition('b.type', $this->plugin()->getBookmarksType($mode) . '_last')
->execute()->fetchField();
}
/**
* Get a database object for the query containing the list of bookmarks for
* the current user.
*
* @param $mode
* Display mode constant.
* @return StatementInterface
* The database object.
*/
private function getBookmarks($mode) {
return $this->database->select('mm_tree_bookmarks', 'b')
->fields('b')
->condition('b.uid', $this->currentUser()->id())
->condition('b.type', $this->plugin()->getBookmarksType($mode))
->orderBy('b.weight')
->execute();
}
}
