added the shortcut menu, part of the cleaner backend project
authorIngo Renner <ingo.renner@typo3.org>
Tue, 15 Jan 2008 16:10:17 +0000 (16:10 +0000)
committerIngo Renner <ingo.renner@typo3.org>
Tue, 15 Jan 2008 16:10:17 +0000 (16:10 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@2900 709f56b5-9817-0410-a4d7-c38de5d9e867

14 files changed:
ChangeLog
t3lib/config_default.php
typo3/backend.php
typo3/classes/class.backendsearchmenu.php
typo3/classes/class.clearcachemenu.php
typo3/classes/class.modulemenu.php
typo3/classes/class.shortcutmenu.php [new file with mode: 0644]
typo3/css/backend-style.css
typo3/gfx/shortcutgroups-bg.png [new file with mode: 0644]
typo3/gfx/toolbar_shortcut.png [new file with mode: 0644]
typo3/js/backendsearch.js
typo3/js/clearcachemenu.js
typo3/js/shortcutmenu.js [new file with mode: 0644]
typo3/js/toolbarmanager.js [new file with mode: 0644]

index 60b549f..5799a56 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,7 @@
 
        * added feature #5536: Some Hooks for tslib_content
        * added feature #7161: create a shortcut manager to delegate calls to create a new shortcut to the right target
+       * added the shortcut menu, part of the cleaner backend project
 
 2008-01-15  Benjamin Mack  <mack@xnos.org>
 
index 1158e79..be889e0 100755 (executable)
@@ -176,6 +176,11 @@ $TYPO3_CONF_VARS = Array(
                        'SC_alt_file_navframe::expandCollapse' => 'typo3/alt_file_navframe.php:SC_alt_file_navframe->ajaxExpandCollapse',
                        't3lib_TCEforms_inline::createNewRecord'                => 't3lib/class.t3lib_tceforms_inline.php:t3lib_TCEforms_inline->processAjaxRequest',
                        't3lib_TCEforms_inline::setExpandedCollapsedState'      => 't3lib/class.t3lib_tceforms_inline.php:t3lib_TCEforms_inline->processAjaxRequest',
+                       'ShortcutMenu::getGroups'              => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->getAjaxShortcutGroups',
+                       'ShortcutMenu::saveShortcut'           => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->setAjaxShortcut',
+                       'ShortcutMenu::render'                 => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->renderAjax',
+                       'ShortcutMenu::delete'                 => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->deleteAjaxShortcut',
+                       'ShortcutMenu::create'                 => 'typo3/classes/class.shortcutmenu.php:ShortcutMenu->createAjaxShortcut'
                ),
        ),
        'FE' => Array(                  // Configuration for the TypoScript frontend (FE). Nothing here relates to the administration backend!
index 39ac538..2dc4950 100644 (file)
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/
 
-require_once ('init.php');
-require_once ('template.php');
-require_once ('interfaces/interface.backend_toolbaritem.php');
+require_once('init.php');
+require_once('template.php');
+require_once('interfaces/interface.backend_toolbaritem.php');
 
-require ('classes/class.typo3logo.php');
-require ('classes/class.modulemenu.php');
+require('classes/class.typo3logo.php');
+require('classes/class.modulemenu.php');
 
        // core toolbar items
-require ('classes/class.workspaceselector.php');
-require ('classes/class.clearcachemenu.php');
-#require ('classes/class.shortcutmenu.php');
-require ('classes/class.backendsearchmenu.php');
-
-require_once (PATH_t3lib.'class.t3lib_loadmodules.php');
-require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
-require_once ('class.alt_menu_functions.inc');
+require('classes/class.workspaceselector.php');
+require('classes/class.clearcachemenu.php');
+require('classes/class.shortcutmenu.php');
+require('classes/class.backendsearchmenu.php');
+
+require_once(PATH_t3lib.'class.t3lib_loadmodules.php');
+require_once(PATH_t3lib.'class.t3lib_basicfilefunc.php');
+require_once('class.alt_menu_functions.inc');
 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_misc.xml');
 
 
@@ -106,7 +106,11 @@ class TYPO3backend {
                        'contrib/prototype/prototype.js',
                        'contrib/scriptaculous/scriptaculous.js?load=builder,effects,controls,dragdrop',
                        'md5.js',
+                       'js/backend.js',
+                       'js/ajax.js',
+                       'js/common.js',
                        'js/sizemanager.js',
+                       'js/toolbarmanager.js',
                        '../t3lib/jsfunc.evalfield.js'
                );
 
@@ -131,7 +135,7 @@ class TYPO3backend {
                $coreToolbarItems = array(
                        'workspaceSelector' => 'WorkspaceSelector',
                        'clearCacheActions' => 'ClearCacheMenu',
-#                      'shortcuts'         => 'ShortcutMenu',
+                       'shortcuts'         => 'ShortcutMenu',
                        'backendSearch'     => 'BackendSearchMenu'
                );
 
@@ -727,7 +731,7 @@ if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/backe
 }
 
 
-// document generation
+       // document generation
 $TYPO3backend = t3lib_div::makeInstance('TYPO3backend');
 
        // include extensions which may add css, javascript or toolbar items
index ca16d56..5318989 100644 (file)
@@ -64,7 +64,7 @@ class BackendSearchMenu implements backend_toolbarItem {
 
                $searchMenu[] = '<a href="#" class="toolbar-item"><img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/magnifier.png', 'width="16" height="16"').' title="Search" alt="" /></a>';
 
-               $searchMenu[] = '<div style="display: none;">';
+               $searchMenu[] = '<div class="toolbar-item-menu" style="display: none;">';
                $searchMenu[] = '<input type="text" id="search-query" name="search-query" value="" />';
                $searchMenu[] = '</div>';
 
index 38431ea..3bb83c1 100644 (file)
@@ -106,7 +106,7 @@ class ClearCacheMenu implements backend_toolbarItem {
 
                $cacheMenu[] = '<a href="#" class="toolbar-item"><img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/lightning.png', 'width="16" height="16"').' title="Clear Cache" alt="" /></a>';
 
-               $cacheMenu[] = '<ul style="display: none;">';
+               $cacheMenu[] = '<ul class="toolbar-item-menu" style="display: none;">';
 
                foreach($this->cacheActions as $actionKey => $cacheAction) {
                        $cacheMenu[] = '<li><a href="'.htmlspecialchars($cacheAction['href']).'">'.$cacheAction['icon'].' '.$cacheAction['title'].'</a></li>';
index 7a938de..8aeb537 100644 (file)
@@ -122,6 +122,7 @@ class ModuleMenu {
                                $moduleLabel = '<a href="#" onclick="top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;">'.$moduleLabel.'</a>';
                        }
 
+                               //TODO make icon a background image using css
                        $menu .= '<li><div>'.$moduleData['icon']['html'].' '.$moduleLabel.'</div>';
 
                                // traverse submodules
@@ -157,6 +158,7 @@ class ModuleMenu {
 
                                $onClickString = htmlspecialchars('top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;');
                                $submoduleLink = '<a href="#" onclick="'.$onClickString.'" title="'.$moduleData['description'].'">'
+                                               //TODO make icon a background image using css
                                        .$moduleData['icon']['html'].' '
                                        .'<span>'.htmlspecialchars($moduleData['title']).'</span>'
                                        .'</a>';
diff --git a/typo3/classes/class.shortcutmenu.php b/typo3/classes/class.shortcutmenu.php
new file mode 100644 (file)
index 0000000..38b7848
--- /dev/null
@@ -0,0 +1,678 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007 Ingo Renner <ingo@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+if(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
+       require_once('interfaces/interface.backend_toolbaritem.php');
+       require_once(PATH_t3lib.'class.t3lib_loadmodules.php');
+       require_once(PATH_typo3.'sysext/lang/lang.php');
+
+       $GLOBALS['LANG'] = t3lib_div::makeInstance('language');
+       $GLOBALS['LANG']->init($GLOBALS['BE_USER']->uc['lang']);
+       $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_misc.xml');
+
+               // needed to get the correct icons when reloading the menu after saving it
+       $loadModules = t3lib_div::makeInstance('t3lib_loadModules');
+       $loadModules->load($GLOBALS['TBE_MODULES']);
+}
+
+
+/**
+ * class to render the shortcut menu
+ *
+ * $Id$
+ *
+ * @author     Ingo Renner <ingo@typo3.org>
+ * @package TYPO3
+ * @subpackage core
+ */
+class ShortcutMenu implements backend_toolbarItem {
+
+       private $shortcutGroups;
+
+       /**
+        * all available shortcuts
+        *
+        * @var array
+        */
+       private $shortcuts;
+
+       /**
+        * labels of all groups.
+        * If value is 1, the system will try to find a label in the locallang array.
+        *
+        * @var array
+        */
+       private $groupLabels;
+
+       /**
+        * reference back to the backend object
+        *
+        * @var TYPO3backend
+        */
+       private $backendReference;
+
+       /**
+        * constructor
+        *
+        * @return      void
+        */
+       public function __construct() {
+               $this->shortcuts = array();
+
+                       // by default, 5 groups are set
+               $this->shortcutGroups = array(
+                       1 => '1',
+                       2 => '1',
+                       3 => '1',
+                       4 => '1',
+                       5 => '1',
+               );
+
+               $this->shortcutGroups  = $this->initShortcutGroups();
+               $this->shortcuts       = $this->initShortcuts();
+       }
+
+       /**
+        * sets the backend reference
+        *
+        * @param TYPO3backend backend object reference
+        */
+       public function setBackend(&$backendReference) {
+               $this->backendReference = $backendReference;
+       }
+
+       /**
+        * Creates the shortcut menu (default renderer)
+        *
+        * @return      string          workspace selector as HTML select
+        */
+       public function render() {
+               $this->addJavascriptToBackend();
+
+               $shortcutMenu = array();
+
+               $shortcutMenu[] = '<a href="#" class="toolbar-item"><img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/toolbar_shortcut.png', 'width="16" height="16"').' title="Shortcuts" alt="" /></a>';
+               $shortcutMenu[] = '<div class="toolbar-item-menu" style="display: none;">';
+               $shortcutMenu[] = $this->renderMenu();
+               $shortcutMenu[] = '</div>';
+
+               return implode("\n", $shortcutMenu);
+       }
+
+       /**
+        * renders the pure contents of the menu
+        *
+        * @return      string          the menu's content
+        */
+       public function renderMenu() {
+
+               $groupIcon  = '<img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/i/sysf.gif', 'width="18" height="16"').' title="Shortcut Group" alt="" />';
+               $editIcon   = '<img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/edit2.gif', 'width="11" height="12"').' title="Edit Shortcut" alt=""';
+               $deleteIcon = '<img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/garbage.gif', 'width="11" height="12"').' title="Delete Shortcut" alt="" />';
+
+               $shortcutMenu[] = '<table border="0" cellspacing="0" cellpadding="0" class="shortcut-list">';
+
+               // render shortcuts with no group (group id = 0) first
+               $noGroupShortcuts = $this->getShortcutsByGroup(0);
+               foreach($noGroupShortcuts as $shortcut) {
+                       $shortcutMenu[] = '
+                       <tr id="shortcut-'.$shortcut['raw']['uid'].'" class="shortcut">
+                               <td class="shortcut-icon">'.$shortcut['icon'].'</td>
+                               <td class="shortcut-label">
+                                       <a id="shortcut-label-'.$shortcut['raw']['uid'].'" href="" onclick="'.$shortcut['action'].'">'.$shortcut['label'].'</a>
+                               </td>
+                               <td class="shortcut-edit">'.$editIcon.' id="shortcut-edit-'.$shortcut['raw']['uid'].'" /></td>
+                               <td class="shortcut-delete">'.$deleteIcon.'</td>
+                       </tr>';
+               }
+
+                       // now render groups and the contained shortcuts
+               $groups = $this->getGroupsFromShortcuts();
+               krsort($groups, SORT_NUMERIC);
+               foreach($groups as $groupId => $groupLabel) {
+                       if($groupId != 0 ) {
+                               $shortcutGroup = '
+                               <tr class="shortcut-group" id="shortcut-group-'.$groupId.'">
+                                       <td class="shortcut-group-icon">'.$groupIcon.'</td>
+                                       <td class="shortcut-group-label">'.$groupLabel.'</td>
+                                       <td colspan="2">&nbsp;</td>
+                               </tr>';
+
+                               $shortcuts = $this->getShortcutsByGroup($groupId);
+                               $i = 0;
+                               foreach($shortcuts as $shortcut) {
+                                       $i++;
+
+                                       $firstRow = '';
+                                       if($i == 1) {
+                                               $firstRow = ' first-row';
+                                       }
+
+                                       $shortcutGroup .= '
+                                       <tr id="shortcut-'.$shortcut['raw']['uid'].'" class="shortcut'.$firstRow.'">
+                                               <td class="shortcut-icon">'.$shortcut['icon'].'</td>
+                                               <td class="shortcut-label">
+                                                       <a id="shortcut-label-'.$shortcut['raw']['uid'].'" href="" onclick="'.$shortcut['action'].'">'.$shortcut['label'].'</a>
+                                               </td>
+                                               <td class="shortcut-edit">'.$editIcon.' id="shortcut-edit-'.$shortcut['raw']['uid'].'" /></td>
+                                               <td class="shortcut-delete">'.$deleteIcon.'</td>
+                                       </tr>';
+                               }
+
+                               $shortcutMenu[] = $shortcutGroup;
+                       }
+               }
+
+               $shortcutMenu[] = '</table>';
+               $compiledShortcutMenu = implode("\n", $shortcutMenu);
+
+               return $compiledShortcutMenu;
+       }
+
+       /**
+        * renders the menu so that it can be returned as response to an AJAX call
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      void
+        */
+       public function renderAjax($params = array(), TYPO3AJAX &$ajaxObj = null) {
+               $menuContent = $this->renderMenu();
+
+               $ajaxObj->addContent('shortcutMenu', $menuContent);
+       }
+
+       /**
+        * adds the neccessary javascript ot the backend
+        *
+        * @return      void
+        */
+       private function addJavascriptToBackend() {
+               $this->backendReference->addJavascriptFile('typo3/js/shortcutmenu.js');
+       }
+
+       /**
+        * returns additional attributes for the list item in the toolbar
+        *
+        * @return      string          list item HTML attibutes
+        */
+       public function getAdditionalAttributes() {
+               return ' id="shortcut-menu"';
+       }
+
+       /**
+        * retrieves the shortcuts for the current user
+        *
+        * @return      array           array of shortcuts
+        */
+       private function initShortcuts() {
+               $shortcuts    = array();
+               $globalGroups = $this->getGlobalShortcutGroups();
+
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                       '*',
+                       'sys_be_shortcuts',
+                       '((userid = '.$GLOBALS['BE_USER']->user['uid'].' AND sc_group>=0) OR sc_group IN ('.implode(',', array_keys($globalGroups)).'))',
+                       '',
+                       'sc_group,sorting'
+               );
+
+                       // Traverse shortcuts
+               while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                       $shortcut             = array('raw' => $row);
+                       $moduleParts          = explode('|', $row['module_name']);
+                       $row['module_name']   = $moduleParts[0];
+                       $row['M_module_name'] = $moduleParts[1];
+                       $moduleParts          = explode('_', $row['M_module_name'] ?
+                               $row['M_module_name'] :
+                               $row['module_name']
+                       );
+                       $queryParts           = parse_url($row['url']);
+
+                               // check for module access
+                       if(!$GLOBALS['BE_USER']->isAdmin()) {
+                               if(!isset($GLOBALS['LANG']->moduleLabels['tabs_images'][implode('_', $moduleParts).'_tab'])) {
+                                               // nice hack to check if the user has access to this module
+                                               // - otherwise the translation label would not have been loaded :-)
+                                       continue;
+                               }
+
+                               $pageId = $this->getLinkedPageId($row['url']);
+                               if(t3lib_div::testInt($pageId)) {
+                                               // check for webmount access
+                                       if(!$GLOBALS['BE_USER']->isInWebMount($page_id)) {
+                                               continue;
+                                       }
+
+                                               // check for record access
+                                       $pageRow = t3lib_BEfunc::getRecord('pages', $pageId);
+                                       if(!$GLOBALS['BE_USER']->doesUserHaveAccess($pageRow, $perms = 1)) {
+                                               continue;
+                                       }
+                               }
+                       }
+
+                       $shortcutGroup = $row['sc_group'];
+                       if($shortcutGroup && strcmp($lastGroup, $shortcutGroup) && ($shortcutGroup != -100)) {
+                               $shortcut['groupLabel'] = $this->getShortcutGroupLabel($shortcutGroup);
+                       }
+
+                       if($row['description']) {
+                               $shortcut['label'] = $row['description'];
+                       } else {
+                               $shortcut['label'] = t3lib_div::fixed_lgd(rawurldecode($queryParts['query']), 150);
+                       }
+
+                       $shortcut['group']     = $shortcutGroup;
+                       $shortcut['icon']      = $this->getShortcutIcon($row['module_name']);
+                       $shortcut['iconTitle'] = $this->getShortcutIconTitle($shortcutLabel, $row['module_name'], $row['M_module_name']);
+                       $shortcut['action']    = 'jump(unescape(\''.rawurlencode($row['url']).'\'),\''.implode('_',$moduleParts).'\',\''.$moduleParts[0].'\');';
+
+                       $lastGroup   = $row['sc_group'];
+                       $shortcuts[] = $shortcut;
+               }
+
+               return $shortcuts;
+       }
+
+       /**
+        * gets shortcuts for a specific group
+        *
+        * @param       integer         group Id
+        * @return      array           array of shortcuts that matched the group
+        */
+       private function getShortcutsByGroup($groupId) {
+               $shortcuts = array();
+
+               foreach($this->shortcuts as $shortcut) {
+                       if($shortcut['group'] == $groupId) {
+                               $shortcuts[] = $shortcut;
+                       }
+               }
+
+               return $shortcuts;
+       }
+
+       /**
+        * gets a shortcut by its uid
+        *
+        * @param       integer         shortcut id to get the complete shortcut for
+        * @return      mixed           an array containing the shortcut's data on success or false on failure
+        */
+       private function getShortcutById($shortcutId) {
+               $returnShortcut = false;
+
+               foreach($this->shortcuts as $shortcut) {
+                       if($shortcut['raw']['uid'] == (int) $shortcutId) {
+                               $returnShortcut = $shortcut;
+                               continue;
+                       }
+               }
+
+               return $returnShortcut;
+       }
+
+       /**
+        * gets the available shortcut groups from default gropups, user TSConfig,
+        * and global groups
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      array
+        */
+       private function initShortcutGroups($params = array(), TYPO3AJAX &$ajaxObj = null) {
+                       // groups from TSConfig
+               $userShortcutGroups = $GLOBALS['BE_USER']->getTSConfig('options.shortcutGroups');
+
+               if(is_array($userShortcutGroups['properties']) && count($userShortcutGroups['properties'])) {
+                       foreach($userShortcutGroups['properties'] as $groupId => $label) {
+                               if(strcmp('', $label) && strcmp('0', $label)) {
+                                       $this->shortcutGroups[$groupId] = (string) $label;
+                               } elseif($GLOBALS['BE_USER']->isAdmin()) {
+                                       unset($this->shortcutGroups[$groupId]);
+                               }
+                       }
+               }
+
+                       // generate global groups, all global groups have negative IDs.
+               if(count($this->shortcutGroups)) {
+                       $groups = $this->shortcutGroups;
+                       foreach($groups as $groupId => $groupLabel) {
+                               $this->shortcutGroups[($groupId * -1)] = $groupLabel;
+                       }
+               }
+
+                       // group -100 is kind of superglobal and can't be changed.
+               $this->shortcutGroups[-100] = 1;
+
+                       // add labels
+               foreach($this->shortcutGroups as $groupId => $groupLabel) {
+                       $label = $groupLabel;
+
+                       if($groupLabel == '1') {
+                               $label = $GLOBALS['LANG']->getLL('shortcut_group_'.abs($groupId), 1);
+
+                               if(empty($label)) {
+                                               // fallback label
+                                       $label = $GLOBALS['LANG']->getLL('shortcut_group', 1).' '.abs($groupId);
+                               }
+                       }
+
+                       if($groupId < 0) {
+                                       // global group
+                               $label = $GLOBALS['LANG']->getLL('shortcut_global', 1).': '.
+                                       (!empty($label) ?
+                                               $label :
+                                               abs($groupId)
+                                       );
+
+                               if($groupId == -100) {
+                                       $label = $GLOBALS['LANG']->getLL('shortcut_global', 1).': '.$GLOBALS['LANG']->getLL('shortcut_all', 1);
+                               }
+                       }
+
+                       $this->shortcutGroups[$groupId] = $label;
+               }
+
+               return $this->shortcutGroups;
+       }
+
+       /**
+        * gets the available shortcut groups
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      void
+        */
+       public function getAjaxShortcutGroups($params = array(), TYPO3AJAX &$ajaxObj = null) {
+
+               //TODO remove global (negative id) groups if the user is no admin !!!
+
+               $ajaxObj->addContent('shortcutGroups', $this->shortcutGroups);
+               $ajaxObj->setContentFormat('json');
+       }
+
+       /**
+        * deletes a shortcut through an AJAX call
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      void
+        */
+       public function deleteAjaxShortcut($params = array(), TYPO3AJAX &$ajaxObj = null) {
+               $shortcutId   = (int) t3lib_div::_POST('shortcutId');
+               $fullShortcut = $this->getShortcutById($shortcutId);
+               $ajaxReturn   = 'failed';
+
+               if($fullShortcut['raw']['userid'] == $GLOBALS['BE_USER']->user['uid']) {
+                       $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                               'sys_be_shortcuts',
+                               'uid = '.$shortcutId
+                       );
+
+                       if($GLOBALS['TYPO3_DB']->sql_affected_rows() == 1) {
+                               $ajaxReturn = 'deleted';
+                       }
+               }
+
+               $ajaxObj->addContent('delete', $ajaxReturn);
+       }
+
+       /**
+        * creates a shortcut through an AJAX call
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      void
+        */
+       public function createAjaxShortcut($params = array(), TYPO3AJAX &$ajaxObj = null) {
+               $shortcutCreated = 'failed';
+               $shortcutName    = 'Shortcut'; // default name
+
+               $url             = urldecode(t3lib_div::_POST('url'));
+               $module          = t3lib_div::_POST('module');
+               $motherModule    = t3lib_div::_POST('motherModName');
+
+                       // Lookup the title of this page and use it as default description
+               $pageId = $this->getLinkedPageId($url);
+
+               if(t3lib_div::testInt($pageId)) {
+                       $page = t3lib_BEfunc::getRecord('pages', $pageId);
+                       if(count($page)) {
+                                       // set the name to the title of the page
+                               $shortcutName = $page['title'];
+                       }
+               } else {
+                       if (preg_match('/\/$/', $pageId))       {
+                                       // if $pageId is a string and ends with a slash,
+                                       // assume it is a fileadmin reference and set
+                                       // the description to the basename of that path
+                               $shortcutName = basename($pageId);
+                       }
+               }
+
+                       // adding the shortcut
+               if($module && $url) {
+                       $fieldValues = array(
+                               'userid'      => $GLOBALS['BE_USER']->user['uid'],
+                               'module_name' => $module.'|'.$motherModule,
+                               'url'         => $url,
+                               'description' => $shortcutName,
+                               'sorting'     => time(),
+                       );
+                       $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_be_shortcuts', $fieldValues);
+
+                       if($GLOBALS['TYPO3_DB']->sql_affected_rows() == 1) {
+                               $shortcutCreated = 'success';
+                       }
+               }
+
+               $ajaxObj->addContent('create', $shortcutCreated);
+       }
+
+       /**
+        * gets called when a shortcut is changed, checks whether the user has
+        * permissions to do so and saves the changes if everything is ok
+        *
+        * @param       array           array of parameters from the AJAX interface, currently unused
+        * @param       TYPO3AJAX       object of type TYPO3AJAX
+        * @return      void
+        */
+       public function setAjaxShortcut($params = array(), TYPO3AJAX &$ajaxObj = null) {
+
+               $shortcutId      = (int) t3lib_div::_POST('shortcutId');
+               $shortcutName    = strip_tags(t3lib_div::_POST('value'));
+               $shortcutGroupId = (int) t3lib_div::_POST('shortcut-group');
+
+               if($shortcutGroupId > 0 || $GLOBALS['BE_USER']->isAdmin()) {
+                               // users can delete only their own shortcuts (except admins)
+                       $addUserWhere = (!$GLOBALS['BE_USER']->isAdmin() ?
+                               ' AND userid='.intval($GLOBALS['BE_USER']->user['uid'])
+                               : ''
+                       );
+
+                       $fieldValues = array(
+                               'description' => $shortcutName,
+                               'sc_group'    => $shortcutGroupId
+                       );
+
+                       if($fieldValues['sc_group'] < 0 && !$GLOBALS['BE_USER']->isAdmin()) {
+                               $fieldValues['sc_group'] = 0;
+                       }
+
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                               'sys_be_shortcuts',
+                               'uid='.$shortcutId.$addUserWhere,
+                               $fieldValues
+                       );
+
+                       $affectedRows = $GLOBALS['TYPO3_DB']->sql_affected_rows();
+                       if($affectedRows == 1) {
+                               $ajaxObj->addContent('shortcut', $shortcutName);
+                       } else {
+                               $ajaxObj->addContent('shortcut', 'failed');
+                       }
+               }
+
+               $ajaxObj->setContentFormat('plain');
+       }
+
+       /**
+        * gets the label for a shortcut group
+        *
+        * @param       integer         a shortcut group id
+        * @return      string          the shortcut group label, can be an empty string if no group was found for the id
+        */
+       private function getShortcutGroupLabel($groupId) {
+               $label = '';
+
+               if($this->shortcutGroups[$groupId]) {
+                       $label = $this->shortcutGroups[$groupId];
+               }
+
+               return $label;
+       }
+
+       /**
+        * gets a list of global groups, shortcuts in these groups are available to all users
+        *
+        * @return      array           array of global groups
+        */
+       private function getGlobalShortcutGroups() {
+               $globalGroups = array();
+
+               foreach($this->shortcutGroups as $groupId => $groupLabel) {
+                       if($groupId < 0) {
+                               $globalGroups[$groupId] = $groupLabel;
+                       }
+               }
+
+               return $globalGroups;
+       }
+
+       /**
+        * runs through the available shortcuts an collects their groups
+        *
+        * @return      array   array of groups which have shortcuts
+        */
+       private function getGroupsFromShortcuts() {
+               $groups = array();
+
+               foreach($this->shortcuts as $shortcut) {
+                       $groups[$shortcut['group']] = $this->shortcutGroups[$shortcut['group']];
+               }
+
+               return array_unique($groups);
+       }
+
+       /**
+        * gets the icon for the shortcut
+        *
+        * @param       string          backend module name
+        * @return      string          shortcut icon as img tag
+        */
+       private function getShortcutIcon($moduleName) {
+
+               switch($moduleName) {
+                       case 'xMOD_alt_doc.php':
+                               $icon = 'gfx/edit2.gif';
+                               break;
+                       case 'xMOD_file_edit.php':
+                               $icon = 'gfx/edit_file.gif';
+                               break;
+                       case 'xMOD_wizard_rte.php':
+                               $icon = 'gfx/edit_rtewiz.gif';
+                               break;
+                       default:
+                               if($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleName.'_tab']) {
+                                       $icon = $GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleName.'_tab'];
+
+                                               // change icon of fileadmin references - otherwise it doesn't differ with Web->List
+                                       $icon = str_replace('mod/file/list/list.gif', 'mod/file/file.gif', $icon);
+
+                                       if(t3lib_div::isAbsPath($icon)) {
+                                               $icon = '../'.substr($icon, strlen(PATH_site));
+                                       }
+                               } else {
+                                       $icon = 'gfx/dummy_module.gif';
+                               }
+               }
+
+               $icon = '<img src="'.$icon.'" alt="shortcut icon" />';
+
+               return $icon;
+       }
+
+       /**
+        * Returns title for the shortcut icon
+        *
+        * @param       string          shortcut label
+        * @param       string          backend module name (key)
+        * @param       string          parent module label
+        * @return      string          title for the shortcut icon
+        */
+       private function getShortcutIconTitle($shortcutLabel, $moduleName, $parentModuleName = '') {
+               $title = '';
+
+               if(substr($moduleName, 0, 5) == 'xMOD_') {
+                       $title = substr($moduleName, 5);
+               } else {
+                       $splitModuleName = explode('_', $moduleName);
+                       $title = $GLOBALS['LANG']->moduleLabels['tabs'][$splitModuleName[0].'_tab'];
+
+                       if(count($splitModuleName) > 1) {
+                               $title .= '>'.$GLOBALS['LANG']->moduleLabels['tabs'][$moduleName.'_tab'];
+                       }
+               }
+
+               if($parentModuleName) {
+                       $title .= ' ('.$parentModuleName.')';
+               }
+
+               $title .= ': '.$shortcutLabel;
+
+               return $title;
+       }
+
+       /**
+        * Return the ID of the page in the URL if found.
+        *
+        * @param       string          The URL of the current shortcut link
+        * @return      string          If a page ID was found, it is returned. Otherwise: 0
+        */
+       private function getLinkedPageId($url)  {
+               return preg_replace('/.*[\?&]id=([^&]+).*/', '$1', $url);
+       }
+
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.shortcutmenu.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.shortcutmenu.php']);
+}
+
+?>
\ No newline at end of file
index 1b43af5..f18bb3c 100644 (file)
@@ -149,6 +149,69 @@ Description : styles the TYPO3 backend
        width: 30px;
 }
 
+#shortcut-menu .toolbar-item-menu {
+       position: absolute;
+       list-style: none;
+       padding: 2px 0px 0px;
+       margin: 0px;
+       background-color: #f9f9f9;
+       border: 1px solid #abb2bc;
+       border-top: none;
+       width: 220px;
+}
+
+#shortcut-menu .shortcut-list {
+       width: 100%;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut td {
+       padding: 2px 1px;
+       line-height: 16px;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut.first-row td {
+       background-image: url(../gfx/shortcutgroups-bg.png);
+       background-repeat: repeat-x;
+       padding-top: 4px;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut-group td.shortcut-group-icon,
+#shortcut-menu .shortcut-list tr.shortcut td.shortcut-icon,
+#shortcut-menu .shortcut-list td.shortcut-edit,
+#shortcut-menu .shortcut-list td.shortcut-delete {
+       width: 18px;
+       text-align: center;
+       vertical-align: top;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut td.shortcut-label a {
+       display: block;
+       cursor: pointer;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut td.shortcut-edit img,
+#shortcut-menu .shortcut-list tr.shortcut td.shortcut-delete img {
+       display: none;
+       cursor: pointer;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut:hover td.shortcut-edit img,
+#shortcut-menu .shortcut-list tr.shortcut:hover td.shortcut-delete img {
+       display: inline;
+}
+
+#shortcut-menu .shortcut-list tr.shortcut-group td.shortcut-group-icon,
+#shortcut-menu .shortcut-list tr.shortcut td.shortcut-icon {
+       padding-left: 7px;
+       vertical-align: top;
+}
+
+#shortcut-menu .shortcut-list .shortcut-group td {
+       border-top: 1px solid #a5a5a5;
+       border-bottom: 1px solid #a5a5a5;
+       background-color: #dbdbdb;
+}
+
 
 /* ----- Backend Search ----- */
 
diff --git a/typo3/gfx/shortcutgroups-bg.png b/typo3/gfx/shortcutgroups-bg.png
new file mode 100644 (file)
index 0000000..3cae0fa
Binary files /dev/null and b/typo3/gfx/shortcutgroups-bg.png differ
diff --git a/typo3/gfx/toolbar_shortcut.png b/typo3/gfx/toolbar_shortcut.png
new file mode 100644 (file)
index 0000000..63c9888
Binary files /dev/null and b/typo3/gfx/toolbar_shortcut.png differ
index 7ad1bc3..286d745 100644 (file)
@@ -94,16 +94,24 @@ var BackendSearch = Class.create({
         */
        toggleMenu: function() {
                var toolbarItem = $$('#backend-search-menu > a')[0];
-               var menu        = $$('#backend-search-menu div')[0];
+               var menu        = $$('#backend-search-menu .toolbar-item-menu')[0];
                toolbarItem.blur();
 
                if(!toolbarItem.hasClassName('toolbar-item-active')) {
                        toolbarItem.addClassName('toolbar-item-active');
                        Effect.Appear(menu, {duration: 0.2});
-                       $('search-query').activate();
+                       TYPO3BackendToolbarManager.hideOthers(toolbarItem);
+
+                       setTimeout(function() {
+                               $('search-query').activate();
+                       }, 200);
                } else {
                        toolbarItem.removeClassName('toolbar-item-active');
                        Effect.Fade(menu, {duration: 0.1});
+
+                       setTimeout(function() {
+                               $('search-query').clear();
+                       }, 100);
                }
        },
 
index 6680add..43a59e1 100644 (file)
@@ -81,12 +81,13 @@ var ClearCacheMenu = Class.create({
         */
        toggleMenu: function() {
                var toolbarItem = $$('#clear-cache-actions-menu > a')[0];
-               var menu        = $$('#clear-cache-actions-menu ul')[0];
+               var menu        = $$('#clear-cache-actions-menu .toolbar-item-menu')[0];
                toolbarItem.blur();
 
                if(!toolbarItem.hasClassName('toolbar-item-active')) {
                        toolbarItem.addClassName('toolbar-item-active');
                        Effect.Appear(menu, {duration: 0.2});
+                       TYPO3BackendToolbarManager.hideOthers(toolbarItem);
                } else {
                        toolbarItem.removeClassName('toolbar-item-active');
                        Effect.Fade(menu, {duration: 0.1});
diff --git a/typo3/js/shortcutmenu.js b/typo3/js/shortcutmenu.js
new file mode 100644 (file)
index 0000000..3668371
--- /dev/null
@@ -0,0 +1,261 @@
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007 Ingo Renner <ingo@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * class to handle the shortcut menu
+ *
+ * $Id$
+ */
+var ShortcutMenu = Class.create({
+
+       /**
+        * registers for resize event listener and executes on DOM ready
+        */
+       initialize: function() {
+               Event.observe(window, 'resize', this.positionMenu);
+
+               Event.observe(document, 'dom:loaded', function(){
+                       this.positionMenu();
+                       this.toolbarItemIcon = $$('#shortcut-menu .toolbar-item img')[0].src;
+
+                       Event.observe($$('#shortcut-menu .toolbar-item')[0], 'click', this.toggleMenu);
+                       this.initControls();
+               }.bindAsEventListener(this));
+       },
+
+       /**
+        * initializes the controls to follow, edit, and delete shortcuts
+        *
+        */
+       initControls: function() {
+
+               $$('.shortcut-label a').each(function(element) {
+                       var shortcutId = element.up('tr.shortcut').identify().slice(9);
+
+                               // map InPlaceEditor to edit icons
+                       new Ajax.InPlaceEditor('shortcut-label-' + shortcutId, 'ajax.php?ajaxID=ShortcutMenu::saveShortcut', {
+                               externalControl     : 'shortcut-edit-' + shortcutId,
+                               externalControlOnly : true,
+                               highlightcolor      : 'transparent',
+                               onFormCustomization : this.addGroupSelect,
+                               onComplete          : this.reRenderMenu.bind(this),
+                               callback            : function(form, nameInputFieldValue) {
+                                       var params = form.serialize();
+                                       params += '&shortcutId=' + shortcutId;
+
+                                       return params;
+                               },
+                               textBetweenControls : ' ',
+                               cancelControl       : 'button',
+                               clickToEditText     : '',
+                               htmlResponse        : true
+                       });
+
+                               // follow/execute shortcuts
+                       element.observe('click', function(event) {
+                               Event.stop(event);
+                               this.toggleMenu();
+                       }.bind(this));
+
+               }.bind(this));
+
+                       // activate delete icon
+               $$('.shortcut-delete img').each(function(element) {
+                       element.observe('click', function(event) {
+                               if(confirm('Do you really want to remove this shortcut?')) {
+                                       var deleteControl = event.element();
+                                       var shortcutId = deleteControl.up('tr.shortcut').identify().slice(9);
+
+                                       new Ajax.Request('ajax.php', {
+                                               parameters : 'ajaxID=ShortcutMenu::delete&shortcutId=' + shortcutId,
+                                               onComplete : this.reRenderMenu.bind(this)
+                                       });
+                               }
+                       }.bind(this));
+               }.bind(this));
+
+       },
+
+       /**
+        * positions the menu below the toolbar icon, let's do some math!
+        */
+       positionMenu: function() {
+               var calculatedOffset = 0;
+               var parentWidth      = $('shortcut-menu').getWidth();
+               var ownWidth         = $$('#shortcut-menu .toolbar-item-menu')[0].getWidth();
+               var parentSiblings   = $('shortcut-menu').previousSiblings();
+
+               parentSiblings.each(function(toolbarItem) {
+                       calculatedOffset += toolbarItem.getWidth() - 1;
+                       // -1 to compensate for the margin-right -1px of the list items,
+                       // which itself is necessary for overlaying the separator with the active state background
+
+                       if(toolbarItem.down().hasClassName('no-separator')) {
+                               calculatedOffset -= 1;
+                       }
+               });
+               calculatedOffset = calculatedOffset - ownWidth + parentWidth;
+
+
+               $$('#shortcut-menu .toolbar-item-menu')[0].setStyle({
+                       left: calculatedOffset + 'px'
+               });
+       },
+
+       /**
+        * toggles the visibility of the menu and places it under the toolbar icon
+        */
+       toggleMenu: function() {
+               var toolbarItem = $$('#shortcut-menu > a')[0];
+               var menu        = $$('#shortcut-menu .toolbar-item-menu')[0];
+               toolbarItem.blur();
+
+               if(!toolbarItem.hasClassName('toolbar-item-active')) {
+                       toolbarItem.addClassName('toolbar-item-active');
+                       Effect.Appear(menu, {duration: 0.2});
+                       TYPO3BackendToolbarManager.hideOthers(toolbarItem);
+               } else {
+                       toolbarItem.removeClassName('toolbar-item-active');
+                       Effect.Fade(menu, {duration: 0.1});
+               }
+       },
+
+       /**
+        * adds a select field for the groups
+        */
+       addGroupSelect: function(inPlaceEditor, inPlaceEditorForm) {
+               var selectField = document.createElement('select');
+
+                       // determine the shortcut id
+               var shortcutId  = inPlaceEditorForm.identify().slice(9, -14);
+
+                       // now determine the shortcut's group id
+               var shortcut        = $('shortcut-' + shortcutId).up('tr.shortcut');
+               var firstInGroup    = null;
+               var shortcutGroupId = 0;
+
+               if(shortcut.hasClassName('first-row')) {
+                       firstInGroup = shortcut;
+               } else {
+                       firstInGroup = shortcut.previous('.first-row');
+               }
+
+               if(undefined != firstInGroup) {
+                       shortcutGroupId = firstInGroup.previous().identify().slice(15);
+               }
+
+               selectField.name = 'shortcut-group';
+               selectField.id = 'shortcut-group-select-' + shortcutId;
+               selectField.size = 1;
+               selectField.setStyle({marginBottom: '5px'});
+
+                       // create options
+               var option;
+                       // first create an option for "no group"
+               option = document.createElement('option');
+               option.value = 0;
+               option.selected = (shortcutGroupId == 0 ? true : false);
+               option.appendChild(document.createTextNode('No Group'));
+               selectField.appendChild(option);
+
+                       // get the groups
+               new Ajax.Request('ajax.php', {
+                       method: 'get',
+                       parameters: 'ajaxID=ShortcutMenu::getGroups',
+                       asynchronous: false, // needs to be synchronous to build the options before adding the selectfield
+                       requestHeaders: {Accept: 'application/json'},
+                       onSuccess: function(transport, json) {
+                               var shortcutGroups = transport.responseText.evalJSON(true);
+
+                                       // explicitly make the object a Hash
+                               shortcutGroups = $H(json.shortcutGroups);
+                               shortcutGroups.each(function(group) {
+                                       option = document.createElement('option');
+                                       option.value = group.key
+                                       option.selected = (shortcutGroupId == group.key ? true : false);
+                                       option.appendChild(document.createTextNode(group.value));
+                                       selectField.appendChild(option);
+                               });
+
+                       }
+               });
+
+               inPlaceEditor._form.appendChild(selectField);
+               inPlaceEditor._form.appendChild(document.createElement('br'));
+       },
+
+       /**
+        * gets called when the update was succesfull, fetches the complete menu to
+        * honor changes in group assignments
+        */
+       reRenderMenu: function(transport, element, backPath) {
+               var container = $$('#shortcut-menu .toolbar-item-menu')[0];
+               if(!backPath) {
+                       var backPath = '';
+               }
+
+
+               container.setStyle({
+                       height: container.getHeight() + 'px'
+               });
+               container.update('LOADING');
+
+               new Ajax.Updater(
+                       container,
+                       backPath + 'ajax.php',
+                       {
+                               parameters : 'ajaxID=ShortcutMenu::render',
+                               asynchronous : false
+                       }
+               );
+
+               container.setStyle({
+                       height: 'auto'
+               });
+
+               this.initControls();
+       },
+
+       /**
+        * makes a call to the backend class to create a new shortcut,
+        * when finished it reloads the menu
+        */
+       createShortcut: function(backPath, moduleName, url) {
+
+                       // synchrous call to wait for it to complete and call the render
+                       // method with backpath _afterwards_
+               new Ajax.Request(backPath + 'ajax.php', {
+                       parameters : 'ajaxID=ShortcutMenu::create&module=' + moduleName + '&url=' + url,
+                       asynchronous : false
+               });
+
+               this.reRenderMenu(null, null, backPath);
+       }
+
+});
+
+var TYPO3BackendShortcutMenu = new ShortcutMenu();
diff --git a/typo3/js/toolbarmanager.js b/typo3/js/toolbarmanager.js
new file mode 100644 (file)
index 0000000..607e163
--- /dev/null
@@ -0,0 +1,53 @@
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007 Ingo Renner <ingo@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+var ToolbarManager = Class.create({
+
+       /**
+        * hides all toolbar item menus except for the which was clicked
+        */
+       hideOthers: function(toolbarItem) {
+               var sibling = toolbarItem.next();
+
+                       // check whether it is a toolbar item with menu
+               if(sibling.hasClassName('toolbar-item-menu')) {
+                               // hide all
+                       $$('#typo3-toolbar a.toolbar-item + .toolbar-item-menu').invoke('hide');
+                       $$('#typo3-toolbar a.toolbar-item').each(function(element) {
+                               element.removeClassName('toolbar-item-active');
+                       });
+
+                               // show toolbarItem
+                       toolbarItem.addClassName('toolbar-item-active');
+               }
+       }
+
+});
+
+var TYPO3BackendToolbarManager = new ToolbarManager();
+
+