2010-11-16 Steffen Kamper <steffen@typo3.org>
+ * Added feature #16426: ExtJS based Pagetree (Thanks to Stefan Galinski and Peter Foerger)
* Fixed bug #16310: Migrate regular workspaces to custom-stage workspaces (Thanks to Tolleiv Nietsch)
* Fixed bug #16421: missing CSS style for pressed buttons in ExtJS / enable qtips / remove toolbar margin (Thanks to Fabien Udriot)
* Fixed bug #16405: pageRenderer call addJsFile with leaving out 2nd parameter
--- /dev/null
+<?php
+/***************************************************************
+* Copyright notice
+*
+* (c) 2009 Susanne Moog (s.moog@neusta.de)
+*
+* 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 with helperfunctions for backendtrees
+ *
+ * $Id: $
+ *
+ * @author Susanne Moog
+ * @package TYPO3
+ */
+class tx_contextmenu_Contextmenu {
+ /**
+ * Fetches the available context menu actions configured in TS config and merges
+ * those with the ones available after the access checks
+ *
+ * @param array $availableActions The actions that were already "access checked" and approved
+ * @param string $tskey the tskey holding the context menu configuration options. you only need the
+ * individual part, like options.contextMenu.[$tskey].items
+ *
+ * @return array An array of the allowed and configured actions
+ */
+ public function getTsConfigActions($availableActions, $tskey='records.pages') {
+ return $availableActions;
+
+// $allowedAndConfiguredActions = array();
+// $contextMenuItemTsConfig = $GLOBALS['BE_USER']->getTSConfig('options.contextMenu.' . $tskey . '.items');
+//
+// // we only need the action configuration
+// $actions = t3lib_div::multi_array_key_exists('action', $contextMenuItemTsConfig['properties']);
+//
+// // flatten the tsconfig actions array and intersect it with the avilableactions
+// return array_intersect($availableActions, array_values(t3lib_div::array_flatten('-', $actions)));;
+ }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/contextmenu/classes/tx_contextmenu_contextmenu.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/contextmenu/classes/tx_contextmenu_contextmenu.php']);
+}
+
+?>
--- /dev/null
+<?php
+/***************************************************************
+* Copyright notice
+*
+* (c) 2009 Susanne Moog <s.moog@neusta.de>
+* Bodo Eichstaedt <bodo.eichstaedt@wmdb.de>
+*
+* 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!
+***************************************************************/
+
+/**
+ * Abstract class for page trees
+ *
+ * $Id: $
+ *
+ * @author Susanne Moog
+ * @author Bodo Eichstaedt
+ * @package TYPO3
+ */
+abstract class tx_pagetree_AbstractTree {
+ protected $table = 'pages';
+ protected $fields = 'uid';
+
+ /**
+ * Fetches the subpages to a given id. Uses the class variables $table and $pages
+ * to determine what to fetch from where. Calls getFilterClause($id) to add a where
+ * clause to the query.
+ *
+ * @param int $id The parent ID of the subpages to fetch
+ * @return array The subpages as array
+ */
+ public function getSubPages($id) {
+ $where = 'pid= ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($id, $this->table) .
+ $this->getFilterClause($id);
+
+ $resultResourceSubpages = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+ $this->fields,
+ $this->table,
+ $where,
+ '',
+ 'sorting'
+ );
+
+ if ($GLOBALS['TYPO3_DB']->sql_num_rows($resultResourceSubpages)) {
+ while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resultResourceSubpages)) {
+ $row = t3lib_befunc::getRecordWSOL(
+ 'pages',
+ $row['uid'],
+ '*',
+ '',
+ TRUE,
+ $GLOBALS['BE_USER']->uc['currentPageTreeLanguage']
+ );
+ $this->addMetaInformationToPage($row['uid'], $row);
+ $subpages[$row['uid']] = $row;
+ }
+ }
+
+ return $subpages;
+ }
+
+
+ /**
+ * Gets information about the root page. Because the root page isn't fetched from DB
+ * we add infos here statically, like the sitename, the subpages and the id.
+ *
+ * @return array Information about the root page (structure like addMetaInformationToPage)
+ */
+ public function addRootPageInformation() {
+ $rootPageInfo = array(
+ 'uid' => 0,
+ 'title' => !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) ?
+ $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : 'TYPO3',
+ '_subpages' => 1,
+ '_actions' => false);
+ return $rootPageInfo;
+ }
+
+ /**
+ * Fetches a where clause to narrow the resulting list of tree elements
+ * Mostly this should contain access/permission checks
+ *
+ * @param int $id The page Id to select
+ */
+ abstract protected function getFilterClause($id);
+
+ /**
+ * Creates a new page
+ *
+ * @param int $parentId The ID of the future parent
+ * @param int $doktype The doctype of the new page
+ */
+ abstract public function create($parentId, $targetId, $doktype);
+ /**
+ * Moves a page
+ *
+ * @param int $sourceId The page to move
+ * @param int $targetId The page to move the page into
+ */
+ abstract public function move($sourceId, $targetId);
+
+ /**
+ * Copies a page
+ *
+ * @param int $sourceId The page to copy
+ * @param int $targetId The page to copy the page into
+ */
+ abstract public function copy($sourceId, $targetId);
+
+ /**
+ * Shows a page
+ *
+ * @param array $row The result row of the page to show
+ */
+ abstract public function show($row);
+
+ /**
+ * Hides a page
+ *
+ * @param array $row The result row of the page to hide
+ */
+ abstract public function disable($id);
+
+ /**
+ * Deletes a page
+ *
+ * @param array $row The result row of the page to delete
+ */
+ abstract public function remove($id);
+
+ /**
+ * Restores (undeletes) a page
+ *
+ * @param array $row The result row of the page to restore
+ */
+ abstract public function restore($id);
+
+ /**
+ * Check if a page can be edited
+ *
+ * @param array $row The result row of the page to be edited
+ */
+ abstract public function canBeEdited($row);
+
+
+ /**
+ * Check if new pages can be created
+ *
+ * @param array $row The result row of the page to be created
+ */
+ abstract public function canCreateNewPages($row);
+
+
+ /**
+ * Check if a page can be removed
+ *
+ * @param array $row The result row of the page to be removed
+ */
+ abstract public function canBeRemoved($row);
+
+
+ /**
+ * Check if a page can be viewed
+ *
+ * @param array $row The result row of the page to be viewed
+ */
+ abstract public function canBeViewed($row);
+
+
+ /**
+ * Check if a page can be moved
+ *
+ * @param array $row The result row of the page to be moved
+ */
+ abstract public function canBeMoved($row);
+
+ /**
+ * Check if a page can have subpages
+ *
+ * @param array $row The result row of the page to have subpages
+ */
+ abstract public function canHaveSubpages($row);
+
+
+ /**
+ * Check if a page can be copied
+ *
+ * @param array $row The result row of the page to be copied
+ */
+ abstract public function canBeCopied($row);
+
+
+ /**
+ * Check if a page can be disabled
+ *
+ * @param array $row The result row of the page to be disabled
+ */
+ abstract public function canBeDisabled($row);
+
+
+ /**
+ * Check if a page can show info
+ *
+ */
+ abstract public function canShowInfo();
+
+
+ /**
+ * Check if a page can be cut
+ *
+ */
+ abstract public function canBeCut();
+
+
+ /**
+ * Check if a page can be pasted
+ *
+ */
+ abstract public function canBePasted();
+
+ /**
+ * Check if a page can show history
+ *
+ */
+ abstract public function canShowHistory();
+
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/classes/class.tx_pagetree_abstracttree.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/classes/class.tx_pagetree_abstracttree.php']);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/***************************************************************
+* Copyright notice
+*
+* (c) 2009 Bodo Eichstaedt <bodo.eichstaedt@wmdb.de>
+* Susanne Moog <s.moog@neusta.de>
+* 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!
+***************************************************************/
+
+/**
+ * pagetree implementation for the backend
+ *
+ * $Id: $
+ *
+ * @author Susanne Moog
+ * @package TYPO3
+ */
+class tx_pagetree_Pagetree extends tx_pagetree_AbstractTree {
+ protected $contextMenuMapping = array(
+ 'view' => 'canBeViewed',
+ 'edit' => 'canBeEdited',
+ 'new' => 'canCreateNewPages',
+ 'info' => 'canShowInfo',
+ 'copy' => 'canBeCopied',
+ 'cut' => 'canBeCut',
+ 'paste' => 'canBePasted',
+ 'move_wizard' => 'canBeMoved',
+ 'new_wizard' => 'dsfdsfdsf',
+ 'mount_as_treeroot' => 'canBeTemporaryMountPoint',
+ 'hide' => 'canBeDisabled',
+ 'delete' => 'canBeRemoved',
+ 'history' => 'canShowHistory',
+ );
+
+ /**
+ * initializes the backendtreehelper and calls a method to set a class
+ * variable which holds the ts config for the context menu
+ *
+ */
+ public function __construct() {
+ $this->backendTreeHelper = t3lib_div::makeInstance('tx_contextmenu_Contextmenu');
+ $this->fetchTsConfigForContextMenuItems();
+ }
+
+ /**
+ * Gets the clause to filter the page tree elements by.
+ *
+ * @param int $id - The page ID
+ */
+ protected function getFilterClause($id) {
+ return ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1)
+ . t3lib_BEfunc::deleteClause($this->table)
+ . t3lib_BEfunc::versioningPlaceholderClause($this->table);
+ }
+
+ /**
+ * Checks if the user may create pages below the given page
+ *
+ * @param array $row The targets parent page
+ */
+ public function canCreate($row) {
+ return $GLOBALS['BE_USER']->doesUserHaveAccess($row,8);
+ }
+
+ /**
+ * Checks if the user has editing rights
+ *
+ * @param array $row The result row for the corresponding page
+ */
+ public function canEdit($row) {
+ return $GLOBALS['BE_USER']->doesUserHaveAccess($row,2);
+ }
+
+ /**
+ * Checks if the user has the right to delete the page
+ *
+ * @param array $row The result row for the corresponding page
+ */
+ public function canRemove($row) {
+ return $GLOBALS['BE_USER']->doesUserHaveAccess($row,4);
+ }
+
+ /**
+ *
+ *
+ * @param int $id
+ * @param string $workspacePreview
+ */
+ public static function getViewLink($id, $workspacePreview) {
+// $viewScriptPreviewEnabled = '/' . TYPO3_mainDir . 'mod/user/ws/wsol_preview.php?id=';
+// $viewScriptPreviewDisabled = '/index.php?id=';
+//
+// // check alternate Domains
+// $rootLine = t3lib_BEfunc::BEgetRootLine($id);
+// if ($rootLine) {
+// $parts = parse_url(t3lib_div::getIndpEnv('TYPO3_SITE_URL'));
+// if (t3lib_BEfunc::getDomainStartPage($parts['host'], $parts['path'])) {
+// $preUrl_temp = t3lib_BEfunc::firstDomainRecord($rootLine);
+// }
+// }
+// $preUrl = ($preUrl_temp ? (t3lib_div::getIndpEnv('TYPO3_SSL') ?
+// 'https://' : 'http://') . $preUrl_temp : '' . '..');
+//
+// // Look if a fixed preview language should be added:
+// $viewLanguageOrder = $GLOBALS['BE_USER']->getTSConfigVal('options.view.languageOrder');
+// if (strlen($viewLanguageOrder)) {
+// $suffix = '';
+//
+// // Find allowed languages (if none, all are allowed!)
+// if (!$GLOBALS['BE_USER']->user['admin'] &&
+// strlen($GLOBALS['BE_USER']->groupData['allowed_languages'])) {
+// $allowed_languages = array_flip(explode(',', $GLOBALS['BE_USER']->groupData['allowed_languages']));
+// }
+//
+// // Traverse the view order, match first occurrence
+// $lOrder = t3lib_div::intExplode(',',$viewLanguageOrder);
+// foreach($lOrder as $langUid) {
+// if (is_array($allowed_languages) && count($allowed_languages)) {
+// if (isset($allowed_languages[$langUid])) { // Choose if set.
+// $suffix = '&L='.$langUid;
+// break;
+// }
+// } else { // All allowed since no lang. are listed.
+// $suffix = '&L='.$langUid;
+// break;
+// }
+// }
+//
+// // Add it:
+// $addGetVars.= $suffix;
+// }
+//
+// $urlPreviewEnabled = $preUrl . $viewScriptPreviewEnabled . $id . $addGetVars;
+// $urlPreviewDisabled = $preUrl . $viewScriptPreviewDisabled . $id . $addGetVars;
+//
+// if ($workspacePreview) {
+// return $urlPreviewEnabled;
+// } else {
+// return $urlPreviewDisabled;
+// }
+
+// $javascriptLink = t3lib_BEfunc::viewOnClick($id);
+// debug($javascriptLink);
+
+ return 'http://linux-schmie.de/wp-content/uploads/2010/07/Baustelle.png';
+ }
+
+ /**
+ * Shows the page (unhide)
+ *
+ * @param int $id The page Id
+ */
+ public function show($id) {
+ $data['pages'][$id]['hidden'] = 0;
+ $this->processTceCmdAndDataMap(array(), $data);
+ }
+
+ /**
+ * Hides the page
+ *
+ * @param int $id The page Id
+ */
+ public function disable($id) {
+ $data['pages'][$id]['hidden'] = 1;
+ $this->processTceCmdAndDataMap(array(), $data);
+ }
+
+ /**
+ * Delete the page
+ *
+ * @param int $id The page Id
+ */
+ public function remove($id) {
+ $cmd['pages'][$id]['delete'] = 1;
+ $this->processTceCmdAndDataMap($cmd);
+ }
+
+ /**
+ * Restore the page ("undelete")
+ *
+ * @param int $id
+ */
+ public function restore($id) {
+ $cmd['pages'][$id]['undelete'] = 1;
+ $this->processTceCmdAndDataMap($cmd);
+ }
+
+ /**
+ * Copies a page. Use a negative target ID to specify a sibling target, else a parent is used
+ *
+ * @param int $sourceId The element to copy
+ * @param int $targetId The element to copy into (if you use a negative value: the element to copy after)
+ */
+ public function copy($sourceId, $targetId) {
+ $cmd['pages'][$sourceId]['copy'] = $targetId;
+ $returnValue = $this->processTceCmdAndDataMap($cmd);
+ return $returnValue['copy']['pages'][$sourceId];
+ }
+
+ /**
+ * Moves a page. Use a negative target ID to specify a sibling target, else a parent is used
+ *
+ * @param int $sourceId The element to move
+ * @param int $targetId The element to move into (if you use a negative value: the element to copy after)
+ */
+ public function move($sourceId, $targetId) {
+ $cmd['pages'][$sourceId]['move'] = $targetId;
+ $this->processTceCmdAndDataMap($cmd);
+ }
+
+ /**
+ * Creates a page of the given doktype
+ *
+ * @param int $parentId The ID of the parent page
+ * @param int $doktype The doktype for the new page
+ */
+ public function create($parentId, $targetId, $doktype) {
+ $placeholder = 'NEW12345';
+ $data['pages'][$placeholder] = array(
+ 'pid' => $parentId,
+ 'doktype' => $doktype,
+ 'title' => '[Default Title]'
+ );
+ $tceResultArr = $this->processTceCmdAndDataMap(array(), $data);
+ if($parentId != $targetId) {
+ $this->move($tceResultArr['new'][$placeholder], $targetId);
+ }
+ return $tceResultArr['new'][$placeholder];
+ }
+
+ /**
+ * fetches the tsconfig options for the context menu
+ *
+ */
+ public function fetchTsConfigForContextMenuItems() {
+ $this->contextMenuConfiguration = t3lib_div::trimExplode(',',$GLOBALS['BE_USER']->getTSConfigVal('options.contextMenu.pageTree.disableItems'),1);
+ }
+
+ /**
+ * Checks if the page is allowed to have subpages
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canHaveSubpages($row) {
+ return $this->getDoktypeDependentConfigOptions($row['doktype'], 'canHaveSubpages');
+ }
+
+ /**
+ * Checks if the page can be disabled
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeDisabled($row) {
+ if($this->canEdit($row) && !in_array('hide', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to show info
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canShowInfo() {
+ if(!in_array('info', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to be a temporary mountpoint
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeTemporaryMountPoint() {
+ if(!in_array('mount_as_treeroot', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to can be cut
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeCut() {
+ if(!in_array('cut', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to be pasted
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBePasted() {
+ if(!in_array('paste', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to show history
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canShowHistory() {
+ if(!in_array('history', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to be edited
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeEdited($row) {
+ if($this->canEdit($row)
+ && $this->getDoktypeDependentConfigOptions($row['doktype'], 'canBeEdited')
+ && !in_array('edit', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to be moved
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeMoved($row) {
+ if($this->canEdit($row)
+ && $this->getDoktypeDependentConfigOptions($row['doktype'], 'canBeEdited')
+ && !in_array('move_wizard', $this->contextMenuConfiguration)){
+ return true;
+ } else {
+ return false;
+ };
+ }
+
+ /**
+ * Checks if the page is allowed to be copied
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeCopied($row) {
+ if($this->canEdit($row)
+ && $this->getDoktypeDependentConfigOptions($row['doktype'], 'canBeCopied')
+ && !in_array('copy', $this->contextMenuConfiguration)){
+ return true;
+ } else {
+ return false;
+ };
+ }
+
+ /**
+ * Checks if there can be new pages created
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canCreateNewPages($row) {
+ return $this->canCreate($row) && !in_array('new', $this->contextMenuConfiguration) ? true : false;
+ }
+
+ /**
+ * Checks if the page is allowed to be removed
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeRemoved($row) {
+ if($this->canRemove($row)
+ && $this->getDoktypeDependentConfigOptions($row['doktype'], 'canBeDeleted')
+ && !in_array('delete', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the page is allowed to be viewed
+ *
+ * @param array $row The page result row to check
+ * @param boolean true if allowed
+ */
+ public function canBeViewed($row) {
+ if($this->getDoktypeDependentConfigOptions($row['doktype'], 'canBeViewed')
+ && !in_array('view', $this->contextMenuConfiguration)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Gets configuration options for the clickmenu depending on the current doctype
+ * For Example: No "view" link for sysfolders
+ *
+ * Possible actions are: isViewable, isDeletable, isEditable,isMovable,
+ * canHoldSubpages, canBeCopied
+ *
+ * @param int $doktype The doctype to check
+ * @param string $action The action to check
+ * @return boolean true if the action is allowed for the doctype, else false
+ */
+ public function getDoktypeDependentConfigOptions($doktype, $action) {
+ //@TODO method was dropped (replacement needed)
+// return t3lib_pageSelect::pageTypeHasCapability($doktype, $action);
+ }
+
+ /**
+ * Process TCEMAIN Commands and Datamaps
+ *
+ * @param array $cmd - The command array - processed by process_cmdmap()
+ * @param array $data - The data array - processed by process_datamap()
+ */
+ protected function processTceCmdAndDataMap(array $cmd, array $data=array()) {
+ /** @var $tce t3lib_TCEmain */
+ $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+ $tce->stripslashes_values = 0;
+ $tce->start($data,$cmd);
+ if($cmd) {
+ $tce->process_cmdmap();
+ $returnValues['copy'] = $tce->copyMappingArray_merged;
+ } else if ($data) {
+ $tce->process_datamap();
+ $returnValues['new'] = $tce->substNEWwithIDs;
+ }
+ return $returnValues;
+ }
+
+ /**
+ * returns the pagetree mounts for the current user
+ * @return array
+ */
+ public function getTreeMounts() {
+ $records = array();
+ $webmountIds = $GLOBALS['BE_USER']->returnWebmounts();
+ if(!empty($webmountIds)){
+ foreach ($webmountIds as $webmount) {
+ if ($webmount == 0) {
+ $records[] = $this->addRootPageInformation();
+ } else {
+ $record = t3lib_BEfunc::getRecordWSOL(
+ $this->table,
+ $webmount,
+ $fields = '*',
+ $where = '',
+ $useDeleteClause = TRUE,
+ $GLOBALS['BE_USER']->uc['currentPageTreeLanguage']
+ );
+ $this->addMetaInformationToPage($record['uid'], $record);
+ $records[] = $record;
+ }
+ }
+ }
+ return $records;
+ }
+
+ /**
+ * Updates the given field with a new text value, may be used to inline update
+ * the title field in the new page tree
+ *
+ * @param int $pageId
+ * @param string $newString
+ * @param string $field
+ */
+ public function updateTextInputField($pageId, $newString, $field) {
+ $data[$this->table][$pageId][$field] = $newString;
+ $this->processTceCmdAndDataMap(array(), $data);
+ }
+
+ /**
+ * Helper function for fetching ts config options with "options.pagetree.[key]"
+ *
+ * @param string $optionName the string to fetch
+ * @param string the ts config value
+ */
+ public function getTsConfigOptionForPagetree($optionName) {
+ return $GLOBALS['BE_USER']->getTSConfigVal('options.pageTree.' . $optionName);
+ }
+
+ /**
+ * Fetches domain records from site root
+ * Needed for "displayDomainNameWithTitle"
+ *
+ * @param int $pageId The page id of the page with the website_root flag
+ * @param string returns the domain
+ */
+ public function getDomainRecordFromSiteRoot($pageId) {
+ $domainRecord = t3lib_BEfunc::getRecordsByField('sys_domain', 'pid', $pageId, ' AND redirectTo=\'\' AND hidden=0', '', 'sorting');
+ if (is_array($domainRecord)) {
+ reset($domainRecord);
+ $firstDomainRecord = current($domainRecord);
+ return rtrim($firstDomainRecord['domainName'], '/');
+ }
+ }
+
+ /**
+ * Fetches a page result record for given fields
+ *
+ * @param int $pageId The page id to fetch infos for
+ * @param array $fields a field array with the fields to fetch
+ */
+ public function getPageInformationForGivenFields($pageId, $fields){
+ return t3lib_BEfunc::getRecordWSOL(
+ 'pages',
+ $pageId,
+ implode(',', $fields),
+ $where = '',
+ $useDeleteClause = TRUE,
+ $GLOBALS['BE_USER']->uc['currentPageTreeLanguage']
+ );
+ }
+
+ /**
+ * Adds meta data to a page
+ * Data:
+ * - if it has sub-pages
+ * - if the user may edit/create/delete the page
+ * - if the page may be viewed, deleted, edited, moved, copied, or can contain subpages
+ *
+ * @param int $id The ID of the page
+ * @param array $row the SQL result row with the db related information for the element
+ */
+ public function addMetaInformationToPage($id, &$row) {
+ $numRows = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
+ 'uid',
+ $this->table,
+ 'pid = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($id, $this->table)
+ );
+
+ if ($numRows) {
+ $row['_subpages'] = $numRows;
+ }
+
+ $row['_actions'] = array(
+ 'canBeEdited' => $this->canBeEdited($row),
+ 'canCreateNewPages' => $this->canCreateNewPages($row),
+ 'canBeRemoved' => $this->canBeRemoved($row),
+ 'canBeViewed' => $this->canBeViewed($row),
+ 'canBeMoved' => $this->canBeMoved($row),
+ 'canHaveSubpages' => $this->canHaveSubpages($row),
+ 'canBeCopied' => $this->canBeCopied($row),
+ 'canBeDisabled' => $this->canBeDisabled($row),
+ 'canShowInfo' => $this->canShowInfo(),
+ 'canBeCut' => $this->canBeCut(),
+ 'canBePasted' => $this->canBePasted(),
+ 'canShowHistory' => $this->canShowHistory(),
+ 'canBeTemporaryMountPoint' => $this->canBeTemporaryMountPoint()
+ );
+
+ foreach ($row['_actions'] as $action => $state) {
+ if (!$state) {
+ unset($row['_actions'][$action]);
+ }
+ }
+ $actions = $row['_actions'];
+
+ $localContextMenuMapping = array_flip($this->contextMenuMapping);
+
+ $availableActions = array();
+ foreach($localContextMenuMapping as $contextActionKey => $contextActionValue) {
+ if(array_key_exists($contextActionKey, $actions)) {
+ $availableActions[$contextActionValue] = $contextActionKey;
+ }
+ }
+
+ if(in_array('canBePasted', $availableActions)){
+ $availableActions[] = 'canBePastedAfter';
+ $availableActions[] = 'canBePastedInto';
+ }
+ if(in_array('canBeDisabled', $availableActions)){
+ $availableActions[] = 'canBeEnabled';
+ }
+
+ $availableAndConfiguredOptions = array_values($this->backendTreeHelper->getTsConfigActions($availableActions));
+
+ $row['_actions'] = $availableAndConfiguredOptions;
+ }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/classes/class.tx_pagetree_pagetree.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/classes/class.tx_pagetree_pagetree.php']);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+
+#typo3-pagetree-treeContainer {
+ margin-bottom: 60px;
+ padding-bottom: 10px;
+ height: 100%;
+}
+
+#typo3-pagetree .topPanel-button {
+ margin: 0 5px 0 0;
+ display: inline-block;
+ height: 16px;
+ overflow: hidden;
+ position: relative;
+ width: 16px;
+}
+
+#typo3-pagetree #topPanel .x-btn,
+#typo3-pagetree #topPanel .x-btn-over {
+ background: none;
+ border: none;
+}
+
+#typo3-pagetree .x-tree .x-panel-body {
+ background: none;
+}
+
+/*#typo3-navigationContainer .x-panel-body {
+ -moz-box-shadow: inset -2px 0 1px #C4C4C4;
+ -ms-box-shadow: inset -2px 0 1px #C4C4C4;
+ -webkit-box-shadow: inset -2px 0 1px #C4C4C4;
+ box-shadow: inset -2px 0 1px #C4C4C4;
+}
+
+#typo3-pagetree #topPanel {
+ -moz-box-shadow: inset -2px 0 0 #414141;
+ -ms-box-shadow: inset -2px 0 0 #414141;
+ -webkit-box-shadow: inset -2px 0 0 #414141;
+ box-shadow: inset -2px 0 0 #414141;
+}*/
+
+#typo3-pagetree .x-tree-node .x-tree-node-el {
+ border-bottom: 1px solid transparent;
+ border-left: 1px solid transparent;
+ border-top: 1px solid transparent;
+ margin-right: 0;
+}
+
+#typo3-pagetree .x-tree-node .x-tree-node-over {
+ background-color: #F8F8F8;
+ margin-right: 4px;
+}
+
+#typo3-pagetree .x-tree-node .x-tree-node-over,
+#typo3-pagetree .x-tree-node .x-tree-selected {
+ background-color: #F8F8F8;
+ border-bottom: 1px solid #D7D7D7;
+ border-left: 1px solid #D7D7D7;
+ border-top: 1px solid #D7D7D7;
+}
+
+#typo3-pagetree .x-tree-node .x-tree-drag-insert-above {
+ border-top-color: #686868;
+}
+
+#typo3-pagetree .x-tree-node .x-tree-drag-insert-below {
+ border-bottom-color: #686868;
+}
+
+#typo3-pagetree .x-panel-tbar {
+ padding: 0;
+ margin: 0;
+ background-color: #585858;
+ border: none;
+}
+
+#typo3-pagetree .x-toolbar {
+ background: none;
+ border: none;
+}
+
+#typo3-pagetree .typo3-pagetree-topbar-item,
+#typo3-pagetree .typo3-pagetree-topbar-item div {
+ background-color: #DADADA;
+}
+
+#typo3-pagetree .typo3-pagetree-topbar-item {
+ padding: 5px 3px 0 3px;
+ height: 22px;
+}
+
+#typo3-pagetree-topPanel-filter {
+ height: 14px;
+}
+
+#typo3-pagetree-deletionDropZone {
+ padding: 5px;
+ width: 100%;
+ position: fixed;
+ bottom: 0;
+}
+
+#typo3-pagetree-deletionDropZone,
+#typo3-pagetree-deletionDropZone .x-panel-body {
+ background-color: #DADADA;
+}
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+TYPO3.Components.PageTree.ContextMenu = Ext.extend(Ext.menu.Menu, {
+ dataRecord: null,
+ id: 'typo3-pagetree-contextmenu',
+ listeners: {
+ itemclick: {
+ scope: this,
+ fn: function (item) {
+ if (item.callbackAction != undefined) {
+ eval(item.callbackAction + '(item.parentMenu.dataRecord, item.attributes)');
+ }
+ }
+ }
+ },
+
+
+ /**
+ * Fill menu with menu items.
+ *
+ * @param dataRecord
+ * The data record to bind to the menu.
+ * MUST contain "attributes.actions" as an array defining the allowed actions for the current item.
+ *
+ * @param contextMenuConfiguration
+ * Context menu configuration. See Ext.MenuItem for properties.
+ * Additionally, the following two properties have to exist:
+ * - callback: Callback method to be called when the click happens. Gets two parameters: the dataRecord, and item.attributes.
+ * - action: The name of the action
+ *
+ * @return the number of menu items in the first level.
+ */
+ fillWithMenuItems: function(dataRecord, contextMenuConfiguration) {
+ this.dataRecord = dataRecord;
+ this.dataRecord.attributes.actions = Ext.toArray(this.dataRecord.attributes.actions);
+ //
+ var components = this.preProcessContextMenuConfiguration(contextMenuConfiguration);
+
+ if (components.length) {
+ for (var component in components) {
+ if (components[component] == '-') {
+ this.addSeparator();
+ } else {
+ if (typeof components[component] == 'object') {
+ this.addItem(components[component]);
+ }
+ }
+ }
+ }
+ return components.length;
+ },
+
+ // Private
+ // recursively parses the context menu actions array and generates the
+ // components for the context menu including separators/dividers and sub menus
+ preProcessContextMenuConfiguration: function(contextMenuConfiguration, level) {
+ level = level || 0;
+ if (level > 5) {
+ return [];
+ }
+
+ var components = [];
+ var index = 0;
+
+ var modulesInsideGroup = false;
+ var subMenus = 0;
+ for (var singleAction in contextMenuConfiguration) {
+ if (singleAction.indexOf('--submenu') != -1) {
+ var subMenuComponents = this.preProcessContextMenuConfiguration(
+ contextMenuConfiguration[singleAction],
+ level + 1
+ );
+
+ if (subMenuComponents.length) {
+ var subMenu = new TYPO3.Components.PageTree.ContextMenu({
+ id: this.id + '-sub' + ++subMenus,
+ items: subMenuComponents,
+ dataRecord: this.dataRecord
+ });
+
+ components[index++] = {
+ text: contextMenuConfiguration[singleAction]['text'],
+ cls: 'contextMenu-subMenu',
+ menu: subMenu
+ };
+ }
+ } else {
+ if (singleAction.indexOf('--divider') != -1) {
+ if (modulesInsideGroup) {
+ components[index++] = '-';
+ modulesInsideGroup = false;
+ }
+ } else {
+ modulesInsideGroup = true;
+
+ if (typeof contextMenuConfiguration[singleAction] == 'object') {
+ var component = contextMenuConfiguration[singleAction];
+ component.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
+ '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}">',
+ '<span class="{hrefTarget}">',
+ '<img src="{icon}" class="x-menu-item-icon {iconCls}" unselectable="on" />',
+ '</span>',
+ '<span class="x-menu-item-text">{text}</span>',
+ '</a>'
+ );
+
+ components[index++] = component;
+ }
+ }
+ }
+ }
+
+ return components;
+ }
+});
+
+// XTYPE Registration
+Ext.reg('TYPO3.Components.PageTree.ContextMenu', TYPO3.Components.PageTree.ContextMenu);
\ No newline at end of file
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+TYPO3.Components.PageTree.ContextMenuActions = {
+ removeNode: function(node) {
+ if (node.parentNode) {
+ node.remove();
+ }
+ },
+
+ stub: function() {
+ alert('Just a Stub! Don\'t Panic!');
+ },
+
+ viewPage: function(node) {
+ TYPO3.Components.PageTree.DataProvider.getViewLink(
+ node.id,
+ (TYPO3.configuration.workspaceFrontendPreviewEnabled && TYPO3.configuration.currentWorkspace != 0),
+ function(result) {
+ openUrlInWindow(result, 'typo3-contextMenu-view');
+ }
+ );
+ },
+
+ editPageProperties: function(node) {
+ TYPO3.Backend.ContentContainer.setUrl(
+ 'alt_doc.php?edit[pages][' + node.attributes.properties.realId + ']=edit'
+ );
+ },
+
+ newPageWizard: function(node) {
+ TYPO3.Backend.ContentContainer.setUrl(
+ 'db_new.php?id=' + node.attributes.properties.realId + '&pagesOnly=1'
+ );
+ },
+
+ openInfoPopUp: function(node) {
+ launchView('pages', node.attributes.properties.realId);
+ },
+
+ openHistoryPopUp: function(node) {
+ TYPO3.Backend.ContentContainer.setUrl(
+ 'show_rechis.php?element=pages:' + node.attributes.properties.realId
+ );
+ },
+
+ enablePage: function(node) {
+ this.tooglePageVisibility(node, false);
+ },
+
+ disablePage: function(node) {
+ this.tooglePageVisibility(node, true);
+ },
+
+ tooglePageVisibility: function(node, enabled) {
+ TYPO3.Components.PageTree.DataProvider.tooglePageVisibility(
+ node.id,
+ enabled,
+ function(updatedNodeFromServer) {
+ updatedNodeFromServer.uiProvider = TYPO3.Components.PageTree.PageTreeUI;
+ var newTreeNode = new Ext.tree.TreeNode(updatedNodeFromServer);
+ node.parentNode.replaceChild(newTreeNode, node);
+ }
+ );
+ }
+};
\ No newline at end of file
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+/**
+ * Main filtering tree widget
+ */
+TYPO3.Components.PageTree.DeletionDropZone = Ext.extend(Ext.Panel, {
+ id: 'typo3-pagetree-deletionDropZone',
+ border: true,
+ height: 50,
+ html: '<strong>Drag a page into this drop zone to delete it</strong>',
+
+ pageTree: null,
+
+ listeners: {
+ afterrender: {
+ fn: function() {
+ var filteringTree = this.pageTree;
+ (new Ext.dd.DropTarget(this.getEl(), {
+ notifyDrop: function(ddProxy, e, n) {
+ var node = n.node;
+ // TODO: Permission checking again
+
+ var dropZoneBody = Ext.get(this.el.query('.x-panel-body')[0]);
+ dropZoneBody.update('You just deleted "' + node.text + '"<span class="undelete" style="text-decoration: underline;">Undelete</span>');
+ Ext.get(dropZoneBody.query('.undelete')[0]).on('click', function() {
+ filteringTree.dataProvider.undeleteNode(node.id);
+ filteringTree.refreshTree();
+ this.update('<strong>Restored</strong>'); // TODO: LOCALIZE
+
+ }.createDelegate(dropZoneBody, [node.id, filteringTree]));
+
+ var fadeOutFn = function() {
+ this.animate({opacity: {to: 0}}, 1, function(dropZoneBody) {
+ dropZoneBody.update('<strong>DropZone</strong>'); // TODO
+ dropZoneBody.setStyle('opacity', 1);
+ });
+ };
+ fadeOutFn.defer(5000, dropZoneBody);
+
+ filteringTree.dataProvider.deleteNode(node.id);
+ node.remove();
+ },
+ ddGroup: 'TreeDD',
+ overClass: 'dd-delete-over'
+ }));
+
+ // TODO: Show drop zone only if currently dragging
+ }
+ }
+ }
+});
+
+// XTYPE Registration
+Ext.reg('TYPO3.Components.PageTree.DeletionDropZone', TYPO3.Components.PageTree.DeletionDropZone);
\ No newline at end of file
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+TYPO3.Components.PageTree.FeaturePanel = Ext.extend(Ext.Panel, {
+ id: 'topPanel',
+ border: false,
+ height: 60,
+
+ currentlyClickedButton: null,
+ currentlyShownPanel: null,
+
+ tbar: new Ext.Toolbar(),
+
+ pageTree: null,
+ filterTree: null,
+
+ initComponent: function() {
+ this.filterTree = this.addFilterFeature();
+ this.addDragDropNodeInsertionFeature();
+ this.addRefreshTreeFeature();
+ this.addLanguageSelection();
+
+ TYPO3.Components.PageTree.FeaturePanel.superclass.initComponent.apply(this, arguments);
+ },
+
+ // This is the callback method which toggles the sub-menu if you click
+ // on a top-bar item.
+ topbarButtonCallback: function() {
+ if (this.ownerCt.ownerCt.currentlyClickedButton === null) {
+ // first click, nothing selected yet
+ this.toggle(true);
+ this.connectedWidget.show();
+
+ this.ownerCt.ownerCt.currentlyClickedButton = this;
+ this.ownerCt.ownerCt.currentlyShownPanel = this.connectedWidget;
+ } else {
+ if (this.ownerCt.ownerCt.currentlyClickedButton === this) {
+ // second click onto currently clicked button
+ this.ownerCt.ownerCt.currentlyClickedButton.toggle(false);
+ this.ownerCt.ownerCt.currentlyShownPanel.hide();
+ this.ownerCt.ownerCt.currentlyClickedButton = null;
+ this.ownerCt.ownerCt.currentlyShownPanel = null;
+ } else {
+ // toggling a view
+ this.ownerCt.ownerCt.currentlyClickedButton.toggle(false);
+ this.ownerCt.ownerCt.currentlyShownPanel.hide();
+
+ this.toggle(true);
+ this.connectedWidget.show();
+ this.ownerCt.ownerCt.currentlyClickedButton = this;
+ this.ownerCt.ownerCt.currentlyShownPanel = this.connectedWidget;
+ }
+ }
+ },
+
+ addWidget: function(button, connectedWidget) {
+ button.connectedWidget = connectedWidget;
+ if (!button.hasListener('click')) {
+ button.addListener('click', this.topbarButtonCallback);
+ }
+
+ this.getTopToolbar().addItem(button);
+ this.add(connectedWidget);
+ this.doLayout();
+ },
+
+ /**
+ * Add the "Filter" feature to the top panel and the panel.
+ */
+ addFilterFeature: function() {
+ // Callback method displaying the results
+ var filterCallback = function(textField) {
+ var filterString = textField.getValue();
+ if (filterString != '') {
+ this.pageTree.dataProvider.getFilteredTree(filterString, function(results) {
+ this.filterTree.setRootNode({
+ id: 'root',
+ children: results
+ });
+ this.pageTree.tree.hide();
+ this.filterTree.expandAll();
+ this.filterTree.show();
+ this.doLayout();
+ }.createDelegate(this));
+ } else {
+ this.filterTree.hide();
+ this.pageTree.tree.show();
+ this.doLayout();
+ }
+ };
+
+ this.pageTree.dataProvider.getSpriteIconClasses('actions-system-tree-search-open', function(result) {
+ // Top Panel
+ var topPanelButton = new Ext.Button({
+ //text: 'filter',
+ cls: 'topPanel-button ' + result
+ });
+
+ var topPanelWidget = new Ext.Panel({
+ border: false,
+ hidden: true,
+ cls: 'typo3-pagetree-topbar-item',
+ items: [
+ new Ext.form.TextField({
+ id: 'typo3-pagetree-topPanel-filter',
+ enableKeyEvents: true,
+ listeners: {
+ keypress: {
+ fn: filterCallback,
+ scope: this,
+ buffer: 250
+ }
+ }
+ })
+ ]
+ });
+ this.addWidget(topPanelButton, topPanelWidget);
+ }, this);
+
+ // Tree initialization
+ return new Ext.tree.TreePanel({
+ anchor: '100% 100%',
+ border: false,
+ autoScroll: true,
+ animate: false,
+ id: 'typo3-pagetree-filterTree',
+ rootVisible: false,
+ hidden:true,
+ root: {
+ id: 'root',
+ text: 'Root',
+ expanded: true
+ }
+ });
+ },
+
+ /**
+ * Add drag and drop node insertion.
+ * @internal
+ */
+ addDragDropNodeInsertionFeature: function() {
+ // Initialization of the "new node" toolbar, via a dataProvider.
+ var newNodeToolbar = new Ext.Toolbar({
+ border: false,
+ id: 'typo3-pagetree-topbar-new',
+ cls: 'typo3-pagetree-topbar-item',
+ hidden: true,
+ anchor: '100% 100%',
+ autoWidth: true,
+ listeners: {
+ render: function() {
+ new Ext.dd.DragZone(newNodeToolbar.getEl(), {
+ ddGroup: 'TreeDD',
+ getDragData: function(e) {
+ var clickedButton = Ext.ComponentMgr.get(e.getTarget('.x-btn').id);
+ clickedButton.shouldCreateNewNode = true;
+
+ this.ddel = document.createElement('div');
+ return {ddel: this.ddel, item: clickedButton}
+ },
+ onInitDrag: function() {
+ var clickedButton = this.dragData.item;
+ this.ddel.innerHTML = '<span class="' + clickedButton.initialConfig.cls + '"></span>' + clickedButton.title;
+ this.ddel.style.width = '150px';
+ this.proxy.update(this.ddel);
+ }
+ });
+ }
+ }
+ });
+
+ // Load data from server
+ if (this.pageTree.dataProvider.getNodeTypes) {
+ // Only call the server if the server implements getNodeTypes();
+ this.pageTree.dataProvider.getNodeTypes(function(response) {
+ var length = response.length;
+ for (var i = 0; i < length; ++i) {
+ newNodeToolbar.addItem(response[i]);
+ }
+ newNodeToolbar.doLayout();
+ });
+ }
+
+ this.pageTree.dataProvider.getSpriteIconClasses('actions-page-new', function(result) {
+ var topPanelButton = new Ext.Button({
+ //text: 'filter',
+ cls: 'topPanel-button ' + result
+ });
+
+ this.addWidget(topPanelButton, newNodeToolbar);
+ }, this);
+ },
+
+ /**
+ * Adds a language selection menu to the topbar
+ * @internal
+ */
+ addLanguageSelection: function() {
+ // Initialization of the "new node" toolbar, via a dataProvider.
+ (new Ext.Toolbar({
+ border: false,
+ id: this.id + '-topbar-languageSelection',
+ cls: this.id + '-topbar-item',
+ hidden: true,
+ anchor: '100% 100%',
+ autoWidth: true
+ }));
+
+
+ },
+
+ /**
+ * Add the "Refresh Tree" feature to the top panel
+ */
+ addRefreshTreeFeature: function() {
+ this.pageTree.dataProvider.getSpriteIconClasses('actions-system-refresh', function(result) {
+ // Top Panel
+ var topPanelButton = new Ext.Button({
+ cls: 'topPanel-button ' + result,
+ listeners: {
+ scope: this.pageTree,
+ 'click': {
+ fn: this.pageTree.refreshTree
+ }
+ }
+ });
+
+ this.getTopToolbar().addItem(topPanelButton);
+ }, this);
+ }
+});
+
+// XTYPE Registration
+Ext.reg('TYPO3.Components.PageTree.FeaturePanel', TYPO3.Components.PageTree.FeaturePanel);
\ No newline at end of file
--- /dev/null
+/**
+ * This is a library of callback actions for the page tree.
+ */
+
+Ext.namespace('TYPO3.Components.PageTree');
+
+TYPO3.Components.PageTree.PageActions = {
+ singleClick: function(node) {
+ TYPO3.Backend.ContentContainer.setUrl(
+ TS.PATH_typo3 + currentSubScript + '?id=' + node.attributes.properties.realId
+ );
+ },
+
+ saveTitle: function(node, newText, oldText) {
+ if (newText == oldText) {
+ return;
+ }
+
+ node = node.editNode;
+ TYPO3.Components.PageTree.DataProvider.setPageTitle(
+ node.id,
+ newText,
+ node.attributes.properties.textSourceField,
+ Ext.emptyFn
+ );
+ }
+};
\ No newline at end of file
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+/**
+ * This is the TreeNodeUI for the FilteringTree. This is the class which renders
+ * the tree nodes.
+ * The below modifications add another span tag around the icon for better skinning,
+ * and a prefix text which is displayed in front of the real "text" contents.
+ * Because the "text" property can be edited inline, the "prefixText" is used to
+ * prepend the editable text.
+ */
+TYPO3.Components.PageTree.PageTreeUI = function() {
+ TYPO3.Components.PageTree.PageTreeUI.superclass.constructor.apply(this, arguments);
+};
+Ext.extend(TYPO3.Components.PageTree.PageTreeUI, Ext.tree.TreeNodeUI, {
+ // private
+ // This method is taken from ExtJS sources. Modifications are marked with // START TYPO3-MODIFICATION
+ renderElements : function(n, a, targetNode, bulkRender) {
+ // add some indent caching, this helps performance when rendering a large tree
+ this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
+ var cb = typeof a.checked == 'boolean';
+ var href = a.href ? a.href : Ext.isGecko ? "" : "#";
+ var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
+ '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
+ '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
+ // START TYPO3-MODIFICATION
+ a.spriteIconCode,
+ '<span class="prefixText">', a.prefixText, '</span>',
+ // END TYPO3-MODIFICATION
+ cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
+ '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
+ a.hrefTarget ? ' target="' + a.hrefTarget + '"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
+ '<ul class="x-tree-node-ct" style="display:none;"></ul>',
+ "</li>"].join('');
+
+ var nel;
+ if (bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())) {
+ this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
+ } else {
+ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
+ }
+
+ this.elNode = this.wrap.childNodes[0];
+ this.ctNode = this.wrap.childNodes[1];
+ var cs = this.elNode.childNodes;
+ this.indentNode = cs[0];
+ this.ecNode = cs[1];
+ this.iconNode = cs[2];
+ // START TYPO3-MODIFICATION
+ Ext.fly(this.iconNode).on('click', function(event) {
+ this.getOwnerTree().openContextMenu(this, event); // calling the context-menu event doesn't work!'
+ event.stopEvent();
+ }, n);
+ // Index from 3 to 4 incremented!
+ var index = 4;
+ // STOP TYPO3-MODIFICATION
+ if (cb) {
+ this.checkbox = cs[3];
+ // fix for IE6
+ this.checkbox.defaultChecked = this.checkbox.checked;
+ index++;
+ }
+ this.anchor = cs[index];
+ this.textNode = cs[index].firstChild;
+ },
+
+ // private
+ // Overwriting the double click event, because we don't want to collapse or expand nodes
+ // by this event
+ onDblClick : function(e) {
+ if (this.disabled) {
+ return;
+ }
+
+ if (this.fireEvent('beforedblclick', this.node, e) !== false) {
+ this.fireEvent('dblclick', this.node, e);
+ }
+ }
+});
+
+TYPO3.Components.PageTree.App = Ext.extend(Ext.Panel, {
+ id: 'typo3-pagetree',
+ border: false,
+
+ tree: null,
+ topPanel: null,
+
+ dataProvider: null,
+
+ contextMenuConfiguration: null,
+
+ isControlPressed: false,
+
+ initComponent: function() {
+ this.dataProvider = TYPO3.Components.PageTree.DataProvider;
+
+ this.tree = new TYPO3.Components.PageTree.Tree({
+ pageTree: this
+ });
+
+ this.topPanel = new TYPO3.Components.PageTree.FeaturePanel({
+ pageTree: this
+ });
+
+ this.deletionDropZone = new TYPO3.Components.PageTree.DeletionDropZone({
+ pageTree: this
+ });
+
+ this.addInlineEditorFeature(this.tree);
+ this.addNodeCopyPasteFeature(this.tree);
+
+ this.items = [
+ this.topPanel, {
+ border: false,
+ id: 'typo3-pagetree-treeContainer',
+ items: [
+ this.tree,
+ this.topPanel.filterTree
+ ]
+ },
+ this.deletionDropZone
+ ];
+
+ TYPO3.Components.PageTree.App.superclass.initComponent.apply(this, arguments);
+ },
+
+ refreshTree: function() {
+ this.tree.root.reload();
+ },
+
+ /**
+ * Initialize the inline editor for the given tree.
+ *
+ * @param tree The Ext.tree.TreePanel where the Inline Editor should be added.
+ * @internal
+ */
+ addInlineEditorFeature: function(tree) {
+ var treeEditor = new Ext.tree.TreeEditor(
+ tree, {
+ cancelOnEsc: true,
+ completeOnEnter: true,
+ ignoreNoChange: true,
+ editDelay: 250,
+ shadow: false
+ }
+ );
+
+ treeEditor.addListener('complete', TYPO3.Components.PageTree.PageActions.saveTitle, this);
+ },
+
+ addNodeCopyPasteFeature: function(tree) {
+ // When dragging starts, we need to add the explanation to the tool-tip
+ tree.addListener('startdrag', function(tree) {
+ var explanationNode = document.createElement('div');
+ Ext.fly(explanationNode).addClass('copyHelp');
+ explanationNode.appendChild(document.createTextNode('Press Ctrl to copy.'));
+
+ tree.explanationTooltip = explanationNode;
+ tree.dragZone.proxy.ghost.dom.appendChild(explanationNode);
+ this.deletionDropZone.setHeight(30);
+ this.doLayout();
+ }, this);
+
+ // SECTION: move
+ // When a node has been moved via drag and drop, this is called.
+ // This event is ONLY called on move, NOT on copy, insert or delete.
+ tree.addListener('movenode', function(tree, movedNode, oldParent, newParent, position) {
+ if (position == 0) {
+ this.dataProvider.moveNodeToFirstChildOfDestination(movedNode.id, newParent.id);
+ } else {
+ var previousSibling = newParent.childNodes[position - 1];
+ this.dataProvider.moveNodeAfterDestination(movedNode.id, previousSibling.id);
+ }
+ }, this);
+
+ // SECTION: copy / create
+ // The following two event handlers deal with the node copying.
+ // The first one is called because we need to copy the node, and replace it by a dummy,
+ // and the second one disables the node, does the ajax request and un-hides the node again.
+ tree.addListener('beforenodedrop', function(de) {
+ /*this.deletionDropZone.setHeight(0);
+ this.doLayout();*/
+
+ if (de.data && de.data.item && de.data.item.shouldCreateNewNode) {
+ // Insertion - part 1
+ var nodeType = de.data.item.nodeType;
+ de.dropNode = new Ext.tree.TreeNode({
+ text: 'New...',
+ leaf: true
+ });
+ de.cancel = false; // Somehow, "cancel" is currently set to "true" - but do not know why.
+ de.dropNode.uiProvider = TYPO3.Components.PageTree.PageTreeUI;
+ de.dropNode.isInsertedNode = true;
+ de.dropNode.serverNodeType = nodeType;
+ } else {
+ if (this.isControlPressed) {
+ // Copying - part 1
+ de.dropNode = new Ext.tree.TreeNode(de.dropNode.attributes);
+ de.dropNode.uiProvider = TYPO3.Components.PageTree.PageTreeUI;
+ de.dropNode.isCopiedNode = true;
+ }
+ }
+ return true;
+ }, this);
+
+ tree.addListener('nodedrop', function(de) {
+ // This callback method replaces the current node with the
+ // one transmitted from the server.
+ var callback = function(updatedNodeFromServer) {
+ // We need to make sure that the UI Provider is correctly set, so that the rendering works for the new node.
+ updatedNodeFromServer.uiProvider = TYPO3.Components.PageTree.PageTreeUI;
+ var newTreeNode = new Ext.tree.TreeNode(updatedNodeFromServer);
+ this.parentNode.replaceChild(newTreeNode, this);
+ };
+
+ if (de.dropNode.isInsertedNode) {
+ // Insertion: - part 2
+ de.dropNode.disable();
+ if (de.dropNode.previousSibling) {
+ // We have previous sibling, so we want to add the record AFTER the previous sibling
+ this.dataProvider.insertNodeAfterDestination(de.dropNode.parentNode.id, de.dropNode.previousSibling.id, de.dropNode.serverNodeType, callback.createDelegate(de.dropNode));
+ } else {
+ if (de.dropNode.parentNode) {
+ // We do not have a previous sibling, but a parent node. Thus, we add the node as the first child
+ // of the parent.
+ this.dataProvider.insertNodeToFirstChildOfDestination(de.dropNode.parentNode.id, de.dropNode.serverNodeType, callback.createDelegate(de.dropNode));
+ } else {
+ // Should not happen!
+ }
+ }
+ } else {
+ if (de.dropNode.isCopiedNode) {
+ // Copying - part 2
+ de.dropNode.disable();
+ if (de.dropNode.previousSibling) {
+ // We have previous sibling, so we want to add the record AFTER the previous sibling
+ this.dataProvider.copyNodeAfterDestination(de.dropNode.id, de.dropNode.previousSibling.id, callback.createDelegate(de.dropNode));
+ } else {
+ if (de.dropNode.parentNode) {
+ // We do not have a previous sibling, but a parent node. Thus, we add the node as the first child
+ // of the parent.
+ this.dataProvider.copyNodeToFirstChildOfDestination(de.dropNode.id, de.dropNode.parentNode, callback.createDelegate(de.dropNode));
+ } else {
+ // Should not happen!
+ }
+ }
+ }
+ }
+ }, this);
+
+ // SECTION: Key Handlers
+ new Ext.KeyMap(document, {
+ key: Ext.EventObject.CONTROL,
+ fn: function() {
+ this.isControlPressed = true;
+ var copyHelpDiv = Ext.fly(tree.explanationTooltip);
+ if (copyHelpDiv) {
+ copyHelpDiv.setVisibilityMode(Ext.Element.DISPLAY);
+ copyHelpDiv.hide();
+ }
+ },
+ scope: this
+ }, 'keydown');
+
+ new Ext.KeyMap(document, {
+ key: Ext.EventObject.CONTROL,
+ fn: function() {
+ this.isControlPressed = false;
+ var copyHelpDiv = Ext.fly(tree.explanationTooltip);
+ if (copyHelpDiv) {
+ copyHelpDiv.show();
+ }
+ },
+ scope: this
+ }, 'keyup');
+ }
+});
+
+TYPO3.ModuleMenu.App.registerNavigationComponent('typo3-pagetree', function() {
+ return new TYPO3.Components.PageTree.App();
+});
+
+// XTYPE Registration
+Ext.reg('TYPO3.Components.PageTree.App', TYPO3.Components.PageTree.App);
\ No newline at end of file
--- /dev/null
+Ext.namespace('TYPO3.Components.PageTree');
+
+TYPO3.Components.PageTree.Tree = Ext.extend(Ext.tree.TreePanel, {
+ id: 'typo3-pagetree-tree',
+ border: false,
+
+ enableDD: true,
+ dragConfig: {
+ ddGroup: 'TreeDD'
+ },
+
+ rootVisible: false,
+ pageTree: null,
+ contextMenuConfiguration: null,
+
+ initComponent: function() {
+ this.contextMenu = new TYPO3.Components.PageTree.ContextMenu({});
+
+ this.root = new Ext.tree.AsyncTreeNode({
+ expanded: true,
+ id: 'root'
+ });
+
+ this.loader = new Ext.tree.TreeLoader({
+ directFn: this.pageTree.dataProvider.getNextTreeLevel,
+ paramOrder: 'rootline',
+
+ baseAttrs: {
+ uiProvider: 't3'
+ },
+
+ uiProviders: {
+ t3: TYPO3.Components.PageTree.PageTreeUI,
+ rootNodeProvider: Ext.tree.TreeNodeUI
+ },
+
+ // The below method fixes a stupid bug of ExtJS / PHP JSON:
+ // ExtJS expects the "expanded" attribute to be "true", and
+ // it compares it with ===.
+ // PHP json_encode submits a "1" if the value is true - thus,
+ // the expanded property is not correctly evaluated by ExtJS.
+ // Below, we do a loose type checking, and if it matches, we
+ // set the JavaScript value "true". This circumvents the bug.
+ createNode: function(attr) {
+ if (attr.expanded) {
+ attr.expanded = true;
+ }
+ return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
+ },
+
+ listeners: {
+ // We always have to transmit the rootline to the server.
+ beforeload: function(treeLoader, node) {
+ treeLoader.baseParams.rootline = node.getPath();
+ },
+ load: function(treeLoader, node) {
+ // Helper function
+ var expandTransmittedNodesRecursively = function(node) {
+ var numberOfSubNodes = node.childNodes.length;
+ if (numberOfSubNodes > 0) {
+ node.expand(false, false);
+ }
+ for (var i = 0; i < numberOfSubNodes; i++) {
+ expandTransmittedNodesRecursively(node.childNodes[i]);
+ }
+ };
+ expandTransmittedNodesRecursively(node);
+ }
+ }
+ });
+
+ TYPO3.Components.PageTree.Tree.superclass.initComponent.apply(this, arguments);
+ },
+
+ // shows the context menu and creates it if it's not already done
+ openContextMenu: function(node, event) {
+ node.select();
+ var contextMenu = node.getOwnerTree().contextMenu;
+ contextMenu.removeAll();
+
+ var numberOfElementsInside = contextMenu.fillWithMenuItems(node, this.contextMenuConfiguration);
+ if (numberOfElementsInside > 0) {
+ contextMenu.showAt(event.getXY());
+ }
+ },
+
+ listeners: {
+ // SECTION Contextmenu
+ // After rendering of the tree, we start the preloading of the context
+ // menu configuration
+ afterrender: {
+ fn: function(tree) {
+ if (tree.contextMenuConfiguration == null) {
+ this.pageTree.dataProvider.getContextMenuConfiguration(
+ function(result) {
+ tree.contextMenuConfiguration = result;
+ }
+ );
+ }
+ }
+ },
+
+ // this event triggers the context menu
+ contextmenu: {
+ fn: function(node, event) {
+ node.getOwnerTree().openContextMenu(node, event);
+ }
+ },
+
+ // SECTION Tree State Remember
+ expandnode: {
+ fn: function (node) {
+ this.pageTree.dataProvider.registerExpandedNode(node.getPath());
+ }
+ },
+
+ collapsenode: {
+ fn: function(node) {
+ this.pageTree.dataProvider.registerCollapsedNode(node.getPath());
+ }
+ },
+
+ // calls a given single click callback for the tree
+ click: {
+ fn: function (node, event) {
+ if (this.doubleClickEventActive) {
+ this.doubleClickEventActive = false;
+ event.stopEvent();
+ } else {
+ eval(node.attributes.properties.clickCallback + '(node)');
+ }
+ },
+ delay: 400
+ },
+
+ // seems to prevent some internal issues with the double-click for the tree editor
+ dblclick: {
+ fn: function() {
+ this.doubleClickEventActive = true;
+ return false;
+ }
+ }
+ }
+});
+
+// XTYPE Registration
+Ext.reg('TYPO3.Components.PageTree.Tree', TYPO3.Components.PageTree.Tree);
\ No newline at end of file
--- /dev/null
+<?php
+
+$extensionPath = t3lib_extMgm::extPath('pagetree');
+return array(
+ 'tx_pagetree_abstracttree' => $extensionPath . 'classes/class.tx_pagetree_abstracttree.php',
+ 'tx_pagetree_pagetree' => $extensionPath . 'classes/class.tx_pagetree_pagetree.php',
+ 'tx_contextmenu_contextmenu' => $extensionPath . 'classes/class.tx_contextmenu_contextmenu.php',
+ 'tx_pagetree_dataprovider_pagetree' => $extensionPath . 'extdirect/dataproviderclass.tx_pagetree_dataprovider_pagetree.php',
+ 'tx_pagetree_dataprovider_abstracttree' => $extensionPath . 'extdirect/dataprovider/class.tx_pagetree_dataprovider_abstracttree.php',
+);
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+
+########################################################################
+# Extension Manager/Repository config file for ext "pagetree".
+#
+# Auto generated 16-11-2010 23:35
+#
+# Manual updates:
+# Only the data in the array - everything else is removed by next
+# writing. "version" and "dependencies" must not be touched!
+########################################################################
+
+$EM_CONF[$_EXTKEY] = array(
+ 'title' => 'Pagetree',
+ 'description' => 'Pagetree based on ExtJS',
+ 'category' => 'be',
+ 'dependencies' => 'cms',
+ 'conflicts' => '',
+ 'priority' => '',
+ 'loadOrder' => '',
+ 'module' => '',
+ 'doNotLoadInFE' => 1,
+ 'state' => 'stable',
+ 'internal' => 0,
+ 'uploadfolder' => 0,
+ 'createDirs' => '',
+ 'modify_tables' => '',
+ 'clearCacheOnLoad' => 0,
+ 'lockType' => '',
+ 'author' => 'TYPO3 Core Team',
+ 'author_email' => '',
+ 'author_company' => '',
+ 'CGLcompliance' => '',
+ 'CGLcompliance_note' => '',
+ 'version' => '1.0.0',
+ '_md5_values_when_last_written' => 'a:19:{s:16:"ext_autoload.php";s:4:"c92c";s:12:"ext_icon.gif";s:4:"39bd";s:17:"ext_localconf.php";s:4:"a736";s:14:"ext_tables.php";s:4:"1e9f";s:25:"locallang_contextmenu.xml";s:4:"72dd";s:22:"locallang_pagetree.xml";s:4:"cd10";s:44:"classes/class.tx_contextmenu_contextmenu.php";s:4:"d8ab";s:42:"classes/class.tx_pagetree_abstracttree.php";s:4:"fc36";s:38:"classes/class.tx_pagetree_pagetree.php";s:4:"a142";s:36:"components/pagetree/css/pagetree.css";s:4:"9764";s:45:"components/pagetree/javascript/contextmenu.js";s:4:"3661";s:52:"components/pagetree/javascript/contextmenuactions.js";s:4:"89bd";s:50:"components/pagetree/javascript/deletiondropzone.js";s:4:"a924";s:46:"components/pagetree/javascript/featurepanel.js";s:4:"7c58";s:45:"components/pagetree/javascript/pageactions.js";s:4:"83f9";s:42:"components/pagetree/javascript/pagetree.js";s:4:"0786";s:38:"components/pagetree/javascript/tree.js";s:4:"4a3e";s:70:"extdirect/dataprovider/class.tx_pagetree_dataprovider_abstracttree.php";s:4:"bfef";s:66:"extdirect/dataprovider/class.tx_pagetree_dataprovider_pagetree.php";s:4:"3eb3";}',
+ 'constraints' => array(
+ 'depends' => array(
+ 'cms' => '',
+ ),
+ 'conflicts' => array(
+ ),
+ 'suggests' => array(
+ ),
+ ),
+ 'suggests' => array(
+ ),
+);
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+if (!defined('TYPO3_MODE')) {
+ die ('Access denied.');
+}
+
+ // pagetree user default configuration
+$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'] .= '
+ options.pageTree.doktypesToShowInNewPageDragArea = 1,3,4,6,7,199,254
+';
+
+ // contextmenu user default configuration
+$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'] .= '
+ options.contextMenu {
+ defaults {
+ }
+
+ table {
+ pages.items {
+ 100 = ITEM
+ 100 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.viewPage
+ outerIcon = actions-document-view
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.viewPage
+ }
+
+ 200 = DIVIDER
+
+ 300 = ITEM
+ 300 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.disablePage
+ outerIcon = actions-edit-hide
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.disablePage
+ }
+
+ 400 = ITEM
+ 400 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.enablePage
+ outerIcon = actions-edit-unhide
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.enablePage
+ }
+
+ 500 = ITEM
+ 500 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.editPageProperties
+ outerIcon = actions-document-open
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.editPageProperties
+ }
+
+ 600 = ITEM
+ 600 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.info
+ outerIcon = actions-document-info
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.openInfoPopUp
+ }
+
+ 700 = ITEM
+ 700 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.history
+ outerIcon = apps-pagetree-page-default+status-overlay-timing
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.openHistoryPopUp
+ }
+
+ 800 = DIVIDER
+
+ 900 = SUBMENU
+ 900 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.copyPasteActions
+
+ 100 = ITEM
+ 100 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.newPage
+ outerIcon = actions-document-new
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.newPageWizard
+ }
+
+ 200 = DIVIDER
+
+ 300 = ITEM
+ 300 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.cutPage
+ outerIcon = actions-edit-cut
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+
+ 400 = ITEM
+ 400 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.copyPage
+ outerIcon = actions-edit-copy
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+
+ 500 = ITEM
+ 500 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.pasteIntoPage
+ outerIcon = actions-document-paste-after
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+
+ 600 = ITEM
+ 600 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.pasteAfterPage
+ outerIcon = actions-document-paste-into
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+
+ 700 = DIVIDER
+
+ 800 = ITEM
+ 800 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.deletePage
+ outerIcon = actions-edit-delete
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+ }
+
+ 1000 = SUBMENU
+ 1000 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.branchActions
+
+ 100 = ITEM
+ 100 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.tempMountPoint
+ outerIcon = actions-system-extension-documentation
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.stub
+ }
+ }
+ }
+ }
+
+ files {
+ items {
+ 100 = ITEM
+ 100 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.renameFolder
+ outerIcon = actions-edit-rename
+ icon =
+ callbackAction = TYPO3.Widget.ContextMenu.FolderActions.renameFolder
+ }
+
+ 200 = ITEM
+ 200 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.uploadFilesToFolder
+ outerIcon = actions-edit-upload
+ icon =
+ callbackAction = TYPO3.Widget.ContextMenu.FolderActions.uploadFilesToFolder
+ }
+
+ 300 = ITEM
+ 300 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.createFolder
+ outerIcon = actions-edit-add
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.createFolder
+ }
+
+ 400 = ITEM
+ 400 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.folderInfo
+ outerIcon = actions-document-info
+ icon =
+ callbackAction = TYPO3.Widget.ContextMenu.FolderActions.openInfoPopUp
+ }
+
+ 500 = DIVIDER
+
+ 600 = ITEM
+ 600 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.copyFolder
+ outerIcon = actions-edit-copy
+ icon =
+ callbackAction = TYPO3.Widget.ContextMenu.FolderActions.copyFolder
+ }
+
+ 700 = ITEM
+ 700 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.cutFolder
+ outerIcon = actions-edit-cut
+ icon =
+ callbackAction = TYPO3.Widget.ContextMenu.FolderActions.cutFolder
+ }
+
+ 800 = ITEM
+ 800 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.pasteIntoFolder
+ outerIcon = actions-document-paste-after
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.pasteIntoFolder
+ }
+
+ 900 = DIVIDER
+
+ 1000 = ITEM
+ 1000 {
+ label = LLL:EXT:pagetree/locallang_contextmenu.xml:cm.deleteFolder
+ outerIcon = actions-edit-delete
+ icon =
+ callbackAction = TYPO3.Components.PageTree.ContextMenuActions.deleteFolder
+ }
+ }
+ }
+ }
+';
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+if (!defined('TYPO3_MODE')) {
+ die ('Access denied.');
+}
+
+if (TYPO3_MODE === 'BE') {
+ $modules = array(
+ 'web_layout', 'web_view', 'web_list', 'web_info', 'web_perm', 'web_func', 'web_ts',
+ 'web_WorkspacesWorkspaces', 'web_txrecyclerM1', 'web_txversionM1'
+ );
+ foreach ($modules as $module) {
+ t3lib_extMgm::addNavigationComponent($module, 'typo3-pagetree', array(
+ 'TYPO3.Components.PageTree'
+ ));
+ }
+
+ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect']['TYPO3.Components.PageTree.DataProvider'] =
+ t3lib_extMgm::extPath($_EXTKEY) . 'extdirect/dataprovider/class.tx_pagetree_dataprovider_pagetree.php:tx_pagetree_dataprovider_Pagetree';
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2009 Sebastian Kurfuerst
+ * 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!
+ ***************************************************************/
+
+/**
+ * Abstract tree dataprovider
+ *
+ * @author Sebastian Kurfuerst <sebastian@typo3.org>
+ * @package TYPO3
+ */
+abstract class tx_pagetree_dataprovider_AbstractTree {
+
+ protected $treeName;
+
+ /**
+ * Fetches the Items for the next level of the tree
+ *
+ * @param mixed The ID of the tree node
+ * @param string The rootline of the element
+ * @return array The next level tree elements
+ */
+ public function getNextTreeLevel($id, $rootline) {
+ // Load UC
+ $GLOBALS['BE_USER']->backendSetUC();
+ $treeState =& $GLOBALS['BE_USER']->uc['treeState'][$this->treeName];
+
+ $records = $this->getRecordsForNextTreeLevel($this->decodeTreeNodeId($id));
+ $results = array();
+ foreach ($records as $record) {
+ $singleResult = $this->prepareRecordForResultList($record, $rootline);
+ $rootlineOfSubElement = $rootline;
+ // the rootline functionality is completely nonsense and should be replaced completely
+ if ($singleResult['id']) {
+ $rootlineOfSubElement = $rootline . '/' . $singleResult['id'];
+ }
+ if (isset($treeState[$rootlineOfSubElement])) {
+ $singleResult['expanded'] = true;
+ $singleResult['children'] = $this->getNextTreeLevel($singleResult['id'], $rootlineOfSubElement);
+ }
+
+ $results[] = $singleResult;
+ }
+ return $results;
+ }
+
+ /**
+ * Prepares the records for the result list & fetches the actions
+ *
+ * @param array $record The record to be prepared
+ * @param string $rootline The rootline of the record
+ * @return array The prepared result
+ */
+ protected function prepareRecordForResultList($record, $rootline = '') {
+ $currentResult = $this->getTreeNodePropertiesForRecord($record, $rootline);
+ $currentResult['actions'] = $this->getActionsForRecord($record);
+ return $currentResult;
+ }
+
+ /**
+ * Saves the current expanded state of the tree to the user settings
+ *
+ * @param string $rootline The current rootline
+ */
+ public function registerExpandedNode($rootline) {
+ $GLOBALS['BE_USER']->backendSetUC();
+ $treeState =& $GLOBALS['BE_USER']->uc['treeState'][$this->treeName];
+ $treeState[$rootline] = TRUE;
+ $GLOBALS['BE_USER']->writeUC();
+ }
+
+ /**
+ * Saves the current collapsed state of the tree to the user settings
+ *
+ * @param string $rootline The current rootline
+ */
+ public function registerCollapsedNode($rootline) {
+ $GLOBALS['BE_USER']->backendSetUC();
+ $treeState =& $GLOBALS['BE_USER']->uc['treeState'][$this->treeName];
+ if (isset($treeState[$rootline])) {
+ unset($treeState[$rootline]);
+ }
+ $GLOBALS['BE_USER']->writeUC();
+ }
+
+
+ /**
+ * Encode node id and its rootline into an global unique identifier even if the same
+ * node id is used a couple of times (webmounts, filemount, ...)
+ *
+ * @param string $id node id
+ * @param string $rootline rootline / path include base-node like webmount-id/filemount-name
+ */
+ protected function encodeTreeNodeId($id, $rootline) {
+ if ($rootline == '/root') {
+ return $this->idEncode($id);
+ } else {
+ list(, , $mountpoint) = explode('/', $rootline);
+ return 'mp' . $this->idEncode($mountpoint) . '-' . $this->idEncode($id);
+ }
+ }
+
+ /**
+ * Decodes the id of the tree node
+ *
+ * @param string $id The ID string
+ * @return int
+ */
+ protected function decodeTreeNodeId($id) {
+ if (strpos($id, 'mp') === 0) {
+ // everything _INSIDE_ a mountpage
+ // mp is in there, extract the ID!
+ list(, $id) = explode('-', $id);
+ return $this->idDecode($id);
+ } else {
+ // /root, and /root/XXX (mountpage)
+ return $id;
+ }
+ }
+
+ /**
+ * Encodes an id, this one just does an intval, if you need
+ * something more implement your own method
+ * @return the encoded ID
+ */
+ protected function idEncode($id) {
+ return intval($id);
+ }
+
+ /**
+ * Decodes an id, this one just does an intval, if you need
+ * something more implement your own method
+ * @return the decoded ID
+ */
+ protected function idDecode($id) {
+ return intval($id);
+ }
+
+ /**
+ * Get Label
+ *
+ * @param string $label label name
+ * @return string the localized label string
+ */
+ protected function getLabel($label) {
+ return $GLOBALS['LANG']->sL($label, TRUE);
+ }
+
+ /**
+ * Get Icon for Context Menu
+ *
+ * @param string $icon Icon name
+ * @return string Returns part of the img tag like ' src="[backPath][src]" [wHattribs]'
+ */
+ protected function getIcon($icon) {
+
+ return $icon;
+ }
+
+ /**
+ * Fetches a filtered tree
+ *
+ * @param string $searchString The string to search for
+ *
+ */
+ abstract public function getFilteredTree($searchString);
+
+ /**
+ * Fetches the records for the next tree level (subpages or subdirectories f.e.)
+ *
+ * @return array a multidimensional array of records to be displayed in the tree on the next level, including the metainformation for the record
+ */
+ abstract protected function getRecordsForNextTreeLevel($id);
+
+ /**
+ * gets a record, and MUST return an array with the following properties:
+ * - id (UNIQUE id)
+ * - leaf (boolean) - FALSE, if it has child records
+ * - text - the title of the node in the tree
+ * additionally, you can specify any of the Ext.tree.TreeNode properties, except "attributes".
+ */
+ abstract protected function getTreeNodePropertiesForRecord($record, $rootline);
+
+
+ /**
+ * Get an array of actions which can be performed with this single record. Used for the context menu.
+ */
+ abstract protected function getActionsForRecord($record);
+
+ /**
+ * Generates the context menu items which can be toogled on/off by the record actions.
+ * You need to define them inside a hash array with the fields "text" and "callback".
+ * @see getActionsForRecord
+ */
+ abstract public function getContextMenuConfiguration();
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/extdirect/dataprovider/class.tx_pagetree_dataprovider_abstracttree.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/extdirect/dataprovider/class.tx_pagetree_dataprovider_abstracttree.php']);
+}
+
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2009 Sebastian Kurfuerst
+ * 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 tx_pagetree_dataprovider_Pagetree extends tx_pagetree_dataprovider_AbstractTree {
+ public $enableIcons = TRUE;
+ public $backPath = '';
+ protected $treeName = 'pages';
+
+ /**
+ * PageTree
+ *
+ * @var tx_pagetree_Pagetree
+ */
+ protected $pageTree;
+
+ public function __construct() {
+ $this->pageTree = t3lib_div::makeInstance('tx_pagetree_Pagetree');
+
+ if ($GLOBALS['BE_USER']->uc['noMenuMode']
+ && strcmp($GLOBALS['BE_USER']->uc['noMenuMode'], 'icons')
+ ) {
+ $this->enableIcons = FALSE;
+ }
+ }
+
+ public function moveNodeToFirstChildOfDestination($movedNode, $destination) {
+ $movedNode = $this->decodeTreeNodeId($movedNode);
+ $destination = $this->decodeTreeNodeId($destination);
+
+ $uid = intval($movedNode);
+ $destination = intval($destination);
+ $this->pageTree->move($uid, $destination);
+ }
+
+ public function moveNodeAfterDestination($movedNode, $destination) {
+ $movedNode = $this->decodeTreeNodeId($movedNode);
+ $destination = $this->decodeTreeNodeId($destination);
+
+ $uid = intval($movedNode);
+ $destination = intval($destination);
+ $this->pageTree->move($uid, -$destination);
+ }
+
+ public function copyNodeToFirstChildOfDestination($copiedNode, $destination) {
+ $copiedNode = $this->decodeTreeNodeId($copiedNode);
+ $destination = $this->decodeTreeNodeId($destination);
+
+ $uid = intval($copiedNode);
+ $destination = intval($destination);
+ $newPageId = $this->pageTree->copy($uid, $destination);
+
+ return $this->getWholeRecordForIdAndPrepareItForResultList($newPageId);
+ }
+
+ public function copyNodeAfterDestination($copiedNode, $destination) {
+ $copiedNode = $this->decodeTreeNodeId($copiedNode);
+ $destination = $this->decodeTreeNodeId($destination);
+
+ $uid = intval($copiedNode);
+ $destination = intval($destination);
+
+ $newPageId = $this->pageTree->copy($uid, -$destination);
+
+ return $this->getWholeRecordForIdAndPrepareItForResultList($newPageId);
+ }
+
+ public function insertNodeToFirstChildOfDestination($parentNode, $pageType) {
+ $parentNode = intval($this->decodeTreeNodeId($parentNode));
+
+ $newPageId = $this->pageTree->create($parentNode, $parentNode, $pageType);
+ return $this->getWholeRecordForIdAndPrepareItForResultList($newPageId);
+ }
+
+ public function insertNodeAfterDestination($parentNode, $destination, $pageType) {
+ $parentNode = intval($this->decodeTreeNodeId($parentNode));
+ $destination = intval($this->decodeTreeNodeId($destination));
+
+ $newPageId = $this->pageTree->create($parentNode, -$destination, $pageType);
+ return $this->getWholeRecordForIdAndPrepareItForResultList($newPageId);
+ }
+
+
+ public function deleteNode($nodeId) {
+ $id = $this->decodeTreeNodeId($nodeId);
+ $this->pageTree->remove($id);
+ }
+
+ public function undeleteNode($nodeId) {
+ $id = $this->decodeTreeNodeId($nodeId);
+ $this->pageTree->restore($id);
+ }
+
+ protected function getWholeRecordForIdAndPrepareItForResultList($newNode) {
+ $resultRow = t3lib_BEfunc::getRecordWSOL(
+ 'pages',
+ $newNode,
+ $fields = '*',
+ $where = '',
+ $useDeleteClause = TRUE,
+ $GLOBALS['BE_USER']->uc['currentPageTreeLanguage']
+ );
+ return $this->prepareRecordForResultList($resultRow);
+ }
+
+ public function getFilteredTree($searchString) {
+ $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
+ 'uid',
+ 'pages',
+ 'title LIKE ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('%' . $searchString . '%', 'pages') .
+ t3lib_BEfunc::deleteClause('pages') .
+ t3lib_BEfunc::versioningPlaceholderClause('pages') . ' AND ' .
+ $GLOBALS['BE_USER']->getPagePermsClause(1)
+ );
+
+ $result = array();
+ foreach ($records as $singleRecord) {
+ $rootline = t3lib_BEfunc::BEgetRootLine($singleRecord['uid']);
+ $rootline = array_reverse($rootline);
+ array_shift($rootline);
+ $currentNode = &$result; // what the fuck? Who codes such unmaintainable stuff?
+
+ foreach ($rootline as $rootlineElement) {
+ $rootlineElement['_subpages'] = 1;
+ if (isset($currentNode[$rootlineElement['uid']])) {
+ $currentNode = &$currentNode[$rootlineElement['uid']]['children'];
+ }
+ else {
+ $currentNode[$rootlineElement['uid']] = $this->prepareRecordForResultList($rootlineElement);
+ $currentNode[$rootlineElement['uid']]['text'] = preg_replace('/(' .
+ preg_quote($searchString, '/') . ')/i', '<strong class="highlight">$1</strong>',
+ $currentNode[$rootlineElement['uid']]['text']);
+
+
+ $currentNode[$rootlineElement['uid']]['children'] = array();
+ $currentNode = &$currentNode[$rootlineElement['uid']]['children'];
+ }
+ }
+ }
+ $this->convertChildrenToUnnamedArray($result);
+ return $result;
+ }
+
+ private function convertChildrenToUnnamedArray(&$array) {
+ $array = array_values($array);
+ foreach ($array as &$value) {
+ if (isset($value['children']) && is_array($value['children'])) {
+ $this->convertChildrenToUnnamedArray($value['children']);
+ }
+ }
+ }
+
+ protected function getRecordsForNextTreeLevel($id) {
+ if ($id === 'root') {
+ return $this->pageTree->getTreeMounts();
+ } else {
+ return $this->pageTree->getSubPages($id);
+ }
+ }
+
+ protected function getTreeNodePropertiesForRecord($record, $rootline) {
+ if ($this->pageTree->getTsConfigOptionForPagetree('showNavTitle')) {
+ $fields = array('nav_title', 'title');
+ } else {
+ $fields = array('title', 'nav_title');
+ }
+
+ $textSourceField = '';
+ foreach ($fields as $field) {
+ if (!empty($record[$field])) {
+ $text = $record[$field];
+ $textSourceField = $field;
+ break;
+ }
+ }
+
+ $languageOverlayIcon = '';
+ if ($record['_PAGES_OVERLAY']) {
+ $currentTreeLanguage = intval($GLOBALS['BE_USER']->uc['currentPageTreeLanguage']);
+ $languageRecord = $this->getLanguage($currentTreeLanguage);
+ $languageShortcut = $this->getLanguageShortcutFromFile($languageRecord['flag']);
+ $languageOverlayIcon = t3lib_iconWorks::getSpriteIcon(
+ 'flags-' . $languageShortcut . '-overlay'
+ );
+ unset($languageRecord, $languageShortcut);
+ }
+
+ if ($record['uid'] !== 0) {
+ $spriteIconCode = t3lib_iconWorks::getSpriteIconForRecord(
+ 'pages',
+ $record,
+ array(
+ 'html' => $languageOverlayIcon,
+ )
+ );
+ } else {
+ $spriteIconCode = t3lib_iconWorks::getSpriteIcon('apps-pagetree-root');
+ }
+ return array(
+ 'record' => $record,
+ 'id' => $this->encodeTreeNodeId($record['uid'], $rootline),
+ 'qtip' => 'ID: ' . $record['uid'],
+ 'leaf' => $record['_subpages'] == 0,
+ 'text' => $text,
+ 'prefixText' => $this->getPrefixForDisplayedTitle($record), // This is the prefix before the title string
+ 'spriteIconCode' => $spriteIconCode,
+ '_meta' => array('numSubPages' => $record['_subpages']),
+ 'properties' => array(
+ 'clickCallback' => 'TYPO3.Components.PageTree.PageActions.singleClick',
+ 'textSourceField' => $textSourceField,
+ 'realId' => $record['uid']
+ )
+ );
+ }
+
+ public function getSpriteIconClasses($icon) {
+ return t3lib_iconWorks::getSpriteIconClasses($icon);
+ }
+
+ // @todo respect tsconfig options
+ protected function getActionsForRecord($record) {
+ return $record['_actions'];
+ }
+
+ protected function getPrefixForDisplayedTitle($row) {
+ $prefix = '';
+
+ if ($this->pageTree->getTsConfigOptionForPagetree('showPageIdWithTitle')) {
+ $prefix .= $row['uid'];
+ }
+ return $prefix;
+ }
+
+ public function getPageInformation($pageId, $fields) {
+ return $this->pageTree->getPageInformationForGivenFields($pageId, $fields);
+ }
+
+ protected function getNextContextMenuLevel($typoScriptConfiguration, $level) {
+ if ($level > 5) {
+ return array();
+ }
+
+ $type = '';
+ $contextMenuItems = array();
+ foreach ($typoScriptConfiguration as $index => $typoScriptItem) {
+ $hash = md5(microtime());
+
+ if (substr($index, -1) === '.') {
+ switch ($type) {
+ case 'SUBMENU':
+ $contextMenuItems['--submenu-' . $hash . '--'] =
+ $this->getNextContextMenuLevel($typoScriptItem, ++$level);
+
+ $contextMenuItems['--submenu-' . $hash . '--']['text'] =
+ $this->getLabel($typoScriptItem['label']);
+ break;
+
+ case 'ITEM':
+ // transform icon and text
+ $contextMenuItems[$index] = array(
+ 'text' => $this->getLabel($typoScriptItem['label'])
+ );
+
+ // push additional attributes
+ $contextMenuItems[$index] = array_merge(
+ $typoScriptItem,
+ $contextMenuItems[$index]
+ );
+
+ if ($this->enableIcons) {
+ if (!empty($typoScriptItem['icon'])) {
+ $contextMenuItems[$index]['icon'] =
+ $this->getIcon($typoScriptItem['icon']);
+ } elseif (isset($typoScriptItem['outerIcon'])) {
+ $contextMenuItems[$index] = array_merge(
+ $contextMenuItems[$index],
+ $this->getIconClassFromIcon($typoScriptItem['outerIcon'])
+ );
+ }
+ } else {
+ $contextMenuItems[$index]['icon'] = '';
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ $type = $typoScriptItem;
+
+ // add divider
+ if ($type === 'DIVIDER') {
+ $contextMenuItems['--divider-' . $hash . '--'] = array(
+ 'action' => 'divider'
+ );
+ }
+ }
+ }
+
+ return $contextMenuItems;
+ }
+
+ public function getContextMenuConfiguration() {
+ // @TODO defaults must be considered
+ $contextMenuItemTypoScriptConfiguration = $GLOBALS['BE_USER']->getTSConfig(
+ 'options.contextMenu.table.pages.items'
+ );
+ $contextMenuItemTypoScriptConfiguration = $contextMenuItemTypoScriptConfiguration['properties'];
+
+ $contextMenuItems = $this->getNextContextMenuLevel(
+ $contextMenuItemTypoScriptConfiguration,
+ 0
+ );
+ return $contextMenuItems;
+ }
+
+ /**
+ * Fetches the attributes for an action inside the context menu.
+ * @todo optimize to prevent much useless ajax calls...
+ *
+ * @param int $id page uid
+ * @param string $menuItemId clickmenu item identifier
+ */
+ public function getAttributesForContextMenu($id, $menuItemId) {
+ $this->decodeTreeNodeId($id);
+
+ switch ($menuItemId) {
+ default:
+ $attributes = array();
+ break;
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * Helper function to generate the neede css classes for img display with overlay
+ *
+ * @param string $icon icon identifier
+ */
+ protected function getIconClassFromIcon($icon) {
+ return array(
+ // 'hrefTarget' => $iconClass[0], // href target is used as a small hack for the template function of the menu.Item
+ 'iconCls' => $this->getSpriteIconClasses($icon)
+ );
+ }
+
+ /**
+ * Returns the page view link
+ *
+ * @param int $id page id
+ * @param unknown $workspacePreview ???
+ * @return string
+ */
+ public function getViewLink($id, $workspacePreview) {
+ $id = $this->decodeTreeNodeId($id);
+ return $this->pageTree->getViewLink($id, $workspacePreview);
+ }
+
+ /**
+ *
+ * @param string $id page id (can be numerical or like "mp-12" in case of mount-points ,...)
+ * @param string $title
+ * @param string $textSourceField
+ */
+ public function setPageTitle($id, $title, $textSourceField) {
+ $id = $this->decodeTreeNodeId($id);
+ $this->pageTree->updateTextInputField($id, $title, $textSourceField);
+ }
+
+ /**
+ * Enables or disables a page and returns the new node
+ *
+ * @param string $id page id (can be numerical or like "mp-12" in case of mount-points ,...)
+ * @param boolean $enabled true for enabling and false for disabling
+ * @return array new node
+ */
+ public function tooglePageVisibility($id, $enabled) {
+ $id = $this->decodeTreeNodeId($id);
+ $this->pageTree->updateTextInputField($id, $enabled, 'hidden');
+
+ $node = $this->getWholeRecordForIdAndPrepareItForResultList($id);
+ return $node;
+ }
+
+ /**
+ * Returns the localized list of doktypes to display
+ *
+ * see User TSconfig: options.pageTree.doktypesToShowInNewPageDragArea
+ */
+ public function getNodeTypes() {
+ $doktypes = t3lib_div::trimExplode(
+ ',',
+ $this->pageTree->getTsConfigOptionForPagetree('doktypesToShowInNewPageDragArea')
+ );
+
+ $output = array();
+ foreach ($doktypes as $doktype) {
+ $label = $this->getLabel('LLL:EXT:pagetree/locallang_pagetree.xml:page.doktype.' . $doktype);
+ $output[] = array(
+ 'nodeType' => $doktype,
+ 'title' => $label,
+ 'cls' => 'topPanel-button ' . $this->getSpriteIconClasses(
+ $GLOBALS['TCA']['pages']['ctrl']['typeicon_classes'][$doktype]
+ ),
+ );
+ }
+ return $output;
+ }
+
+ /**
+ * Returns the language shortcut from a language file
+ *
+ * @param string $file language flag with or without the related directory
+ * @return mixed language shortcut or boolean false
+ */
+ protected function getLanguageShortcutFromFile($file) {
+ if (strpos($file, '/') !== FALSE) {
+ $file = basename($file);
+ }
+
+ return substr($file, 0, strrpos($file, '.'));
+ }
+
+ /**
+ * Returns a language record defined by the id parameter
+ *
+ * @param int $uid language id
+ * @return array
+ */
+ protected function getLanguage($uid) {
+ $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+ 'flag',
+ 'sys_language',
+ 'pid=0 AND uid=' . $uid .
+ t3lib_BEfunc::deleteClause('sys_language')
+ );
+
+ $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+ $GLOBALS['TYPO3_DB']->sql_free_result($res);
+
+ return $record;
+ }
+
+ /**
+ * Returns an array of system languages with parameters based on the properties
+ * of Ext.Button.
+ *
+ * @return array
+ */
+ public function getLanguages() {
+ $languageRecords = t3lib_befunc::getSystemLanguages();
+
+ $output = array();
+ foreach ($languageRecords as $languageRecord) {
+ $languageShortcut = $this->getLanguageShortcutFromFile($languageRecord[2]);
+ if ($languageShortcut === FALSE) {
+ $languageShortcut = 'europeanunion';
+ }
+
+ $output[] = array(
+ 'language' => $languageRecord[1],
+ 'languageShortcut' => $languageShortcut,
+ 'cls' => 'topPanel-button ' .
+ $this->getSpriteIconClasses('flags-' . $languageShortcut),
+ );
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns the european flag sprite icon css classes
+ *
+ * @TODO What if a flag is added to the core, but isn't inside the sprite?
+ * @return string
+ */
+ public function getIconForCurrentLanguage() {
+ $currentTreeLanguage = intval($GLOBALS['BE_USER']->uc['currentPageTreeLanguage']);
+
+ $icon = 'flags-europeanunion';
+ if ($currentTreeLanguage !== 0) {
+ $languageRecord = $this->getLanguage($currentTreeLanguage);
+ $icon = 'flags-' . $this->getLanguageShortcutFromFile($languageRecord['flag']);
+ }
+
+ return $this->getSpriteIconClasses($icon);
+ }
+
+ /**
+ * Saves the given language id into the backend user configuration array
+ *
+ * @param int $languageId
+ * @return void
+ */
+ public function saveCurrentTreeLanguage($languageId) {
+ $GLOBALS['BE_USER']->backendSetUC();
+ $GLOBALS['BE_USER']->uc['currentPageTreeLanguage'] = intval($languageId);
+ $GLOBALS['BE_USER']->writeUC();
+ }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/extdirect/dataprovider/class.t3lib_tree_dataprovider_pagetree.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/sysext/pagetree/extdirect/dataprovider/class.t3lib_tree_dataprovider_pagetree.php']);
+}
+
+?>
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+ <meta type="array">
+ <description>TYPO3 core labels for the clickmenus</description>
+ <type>module</type>
+ </meta>
+ <data type="array">
+ <languageKey index="default" type="array">
+ <label index="cm.copyPage">Copy page</label>
+ <label index="cm.cutPage">Cut page</label>
+ <label index="cm.viewPage">View live page</label>
+ <label index="cm.editPageProperties">Edit page properties</label>
+ <label index="cm.newPage">Add page</label>
+ <label index="cm.pasteIntoPage">Paste into</label>
+ <label index="cm.pasteAfterPage">Paste after</label>
+ <label index="cm.deletePage">Delete page</label>
+ <label index="cm.disablePage">Disable page</label>
+ <label index="cm.enablePage">Enable page</label>
+ <label index="cm.info">Page infos</label>
+ <label index="cm.history">Page history</label>
+ <label index="cm.tempMountPoint">Mount as treeroot</label>
+ <label index="cm.branchActions">Branch Actions</label>
+ <label index="cm.copyPasteActions">Page Actions</label>
+
+ <label index="cm.renameFolder">Rename</label>
+ <label index="cm.uploadFilesToFolder">Upload Files</label>
+ <label index="cm.createFolder">Create New Folder</label>
+ <label index="cm.folderInfo">Folder Info</label>
+ <label index="cm.copyFolder">Copy</label>
+ <label index="cm.cutFolder">Cut</label>
+ <label index="cm.pasteIntoFolder">Paste</label>
+ <label index="cm.deleteFolder">Delete</label>
+ </languageKey>
+ </data>
+</T3locallang>
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+ <meta type="array">
+ <description>TYPO3 core labels the page tree</description>
+ <type>module</type>
+ </meta>
+ <data type="array">
+ <languageKey index="default" type="array">
+ <label index="page.doktype.1">Default</label>
+ <label index="page.doktype.3">External Link</label>
+ <label index="page.doktype.4">Shortcut</label>
+ <label index="page.doktype.5">Not in Menu</label>
+ <label index="page.doktype.6">BE user Section</label>
+ <label index="page.doktype.7">Mountpoint</label>
+ <label index="page.doktype.199">Spacer</label>
+ <label index="page.doktype.254">Sysfolder</label>
+ </languageKey>
+ </data>
+</T3locallang>