PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/t3lib/class.t3lib_tceforms_inline.php

https://github.com/andreaswolf/typo3-tceforms
PHP | 2550 lines | 1431 code | 285 blank | 834 comment | 355 complexity | fa1d9ef6d86be102ccb11bf272ba9809 MD5 | raw file
Possible License(s): Apache-2.0, BSD-2-Clause, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2006-2011 Oliver Hader <oliver@typo3.org>
  6. * All rights reserved
  7. *
  8. * This script is part of the TYPO3 project. The TYPO3 project is
  9. * free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * The GNU General Public License can be found at
  15. * http://www.gnu.org/copyleft/gpl.html.
  16. * A copy is found in the textfile GPL.txt and important notices to the license
  17. * from the author is found in LICENSE.txt distributed with these scripts.
  18. *
  19. *
  20. * This script is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * This copyright notice MUST APPEAR in all copies of the script!
  26. ***************************************************************/
  27. /**
  28. * The Inline-Relational-Record-Editing (IRRE) functions as part of the TCEforms.
  29. *
  30. * $Id$
  31. *
  32. * @author Oliver Hader <oliver@typo3.org>
  33. */
  34. /**
  35. * [CLASS/FUNCTION INDEX of SCRIPT]
  36. *
  37. *
  38. *
  39. * 88: class t3lib_TCEforms_inline
  40. * 109: function init(&$tceForms)
  41. * 127: function getSingleField_typeInline($table,$field,$row,&$PA)
  42. *
  43. * SECTION: Regular rendering of forms, fields, etc.
  44. * 263: function renderForeignRecord($parentUid, $rec, $config = array())
  45. * 319: function renderForeignRecordHeader($parentUid, $foreign_table,$rec,$config = array())
  46. * 375: function renderForeignRecordHeaderControl($table,$row,$config = array())
  47. * 506: function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array())
  48. * 560: function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds=array())
  49. * 627: function addJavaScript()
  50. * 643: function addJavaScriptSortable($objectId)
  51. *
  52. * SECTION: Handling of AJAX calls
  53. * 665: function createNewRecord($domObjectId, $foreignUid = 0)
  54. * 755: function getJSON($jsonArray)
  55. * 770: function getNewRecordLink($objectPrefix, $conf = array())
  56. *
  57. * SECTION: Get data from database and handle relations
  58. * 807: function getRelatedRecords($table,$field,$row,&$PA,$config)
  59. * 839: function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector')
  60. * 885: function getUniqueIds($records, $conf=array())
  61. * 905: function getRecord($pid, $table, $uid, $cmd='')
  62. * 929: function getNewRecord($pid, $table)
  63. *
  64. * SECTION: Structure stack for handling inline objects/levels
  65. * 951: function pushStructure($table, $uid, $field = '', $config = array())
  66. * 967: function popStructure()
  67. * 984: function updateStructureNames()
  68. * 1000: function getStructureItemName($levelData)
  69. * 1015: function getStructureLevel($level)
  70. * 1032: function getStructurePath($structureDepth = -1)
  71. * 1057: function parseStructureString($string, $loadConfig = false)
  72. *
  73. * SECTION: Helper functions
  74. * 1098: function checkConfiguration(&$config)
  75. * 1123: function checkAccess($cmd, $table, $theUid)
  76. * 1185: function compareStructureConfiguration($compare)
  77. * 1199: function normalizeUid($string)
  78. * 1213: function wrapFormsSection($section, $styleAttrs = array(), $tableAttrs = array())
  79. * 1242: function isInlineChildAndLabelField($table, $field)
  80. * 1258: function getStructureDepth()
  81. * 1295: function arrayCompareComplex($subjectArray, $searchArray, $type = '')
  82. * 1349: function isAssociativeArray($object)
  83. * 1364: function getPossibleRecordsFlat($possibleRecords)
  84. * 1383: function skipField($table, $field, $row, $config)
  85. *
  86. * TOTAL FUNCTIONS: 35
  87. * (This index is automatically created/updated by the extension "extdeveval")
  88. *
  89. */
  90. class t3lib_TCEforms_inline {
  91. const Structure_Separator = '-';
  92. const Disposal_AttributeName = 'Disposal_AttributeName';
  93. const Disposal_AttributeId = 'Disposal_AttributeId';
  94. /**
  95. * Reference to the calling TCEforms instance
  96. *
  97. * @var t3lib_TCEforms
  98. */
  99. var $fObj;
  100. var $backPath; // Reference to $fObj->backPath
  101. var $isAjaxCall = FALSE; // Indicates if a field is rendered upon an AJAX call
  102. var $inlineStructure = array(); // the structure/hierarchy where working in, e.g. cascading inline tables
  103. var $inlineFirstPid; // the first call of an inline type appeared on this page (pid of record)
  104. var $inlineNames = array(); // keys: form, object -> hold the name/id for each of them
  105. var $inlineData = array(); // inline data array used for JSON output
  106. var $inlineView = array(); // expanded/collapsed states for the current BE user
  107. var $inlineCount = 0; // count the number of inline types used
  108. var $inlineStyles = array();
  109. var $prependNaming = 'data'; // how the $this->fObj->prependFormFieldNames should be set ('data' is default)
  110. var $prependFormFieldNames; // reference to $this->fObj->prependFormFieldNames
  111. var $prependCmdFieldNames; // reference to $this->fObj->prependCmdFieldNames
  112. protected $hookObjects = array(); // array containing instances of hook classes called once for IRRE objects
  113. /**
  114. * Intialize an instance of t3lib_TCEforms_inline
  115. *
  116. * @param t3lib_TCEforms $tceForms: Reference to an TCEforms instance
  117. * @return void
  118. */
  119. function init(&$tceForms) {
  120. $this->fObj = $tceForms;
  121. $this->backPath =& $tceForms->backPath;
  122. $this->prependFormFieldNames =& $this->fObj->prependFormFieldNames;
  123. $this->prependCmdFieldNames =& $this->fObj->prependCmdFieldNames;
  124. $this->inlineStyles['margin-right'] = '5';
  125. $this->initHookObjects();
  126. }
  127. /**
  128. * Initialized the hook objects for this class.
  129. * Each hook object has to implement the interface t3lib_tceformsInlineHook.
  130. *
  131. * @return void
  132. */
  133. protected function initHookObjects() {
  134. $this->hookObjects = array();
  135. if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms_inline.php']['tceformsInlineHook'])) {
  136. $tceformsInlineHook =& $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms_inline.php']['tceformsInlineHook'];
  137. if (is_array($tceformsInlineHook)) {
  138. foreach ($tceformsInlineHook as $classData) {
  139. $processObject = t3lib_div::getUserObj($classData);
  140. if (!($processObject instanceof t3lib_tceformsInlineHook)) {
  141. throw new UnexpectedValueException('$processObject must implement interface t3lib_tceformsInlineHook', 1202072000);
  142. }
  143. $processObject->init($this);
  144. $this->hookObjects[] = $processObject;
  145. }
  146. }
  147. }
  148. }
  149. /**
  150. * Generation of TCEform elements of the type "inline"
  151. * This will render inline-relational-record sets. Relations.
  152. *
  153. * @param string $table: The table name of the record
  154. * @param string $field: The field name which this element is supposed to edit
  155. * @param array $row: The record data array where the value(s) for the field can be found
  156. * @param array $PA: An array with additional configuration options.
  157. * @return string The HTML code for the TCEform field
  158. */
  159. function getSingleField_typeInline($table, $field, $row, &$PA) {
  160. // check the TCA configuration - if false is returned, something was wrong
  161. if ($this->checkConfiguration($PA['fieldConf']['config']) === FALSE) {
  162. return FALSE;
  163. }
  164. // count the number of processed inline elements
  165. $this->inlineCount++;
  166. // Init:
  167. $config = $PA['fieldConf']['config'];
  168. $foreign_table = $config['foreign_table'];
  169. t3lib_div::loadTCA($foreign_table);
  170. if (t3lib_BEfunc::isTableLocalizable($table)) {
  171. $language = intval($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
  172. }
  173. $minitems = t3lib_div::intInRange($config['minitems'], 0);
  174. $maxitems = t3lib_div::intInRange($config['maxitems'], 0);
  175. if (!$maxitems) {
  176. $maxitems = 100000;
  177. }
  178. // Register the required number of elements:
  179. $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems, $maxitems, 'imgName' => $table . '_' . $row['uid'] . '_' . $field);
  180. // remember the page id (pid of record) where inline editing started first
  181. // we need that pid for ajax calls, so that they would know where the action takes place on the page structure
  182. if (!isset($this->inlineFirstPid)) {
  183. // if this record is not new, try to fetch the inlineView states
  184. // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field
  185. if (t3lib_div::testInt($row['uid'])) {
  186. $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
  187. $this->inlineView = $inlineView[$table][$row['uid']];
  188. }
  189. // If the parent is a page, use the uid(!) of the (new?) page as pid for the child records:
  190. if ($table == 'pages') {
  191. $liveVersionId = t3lib_BEfunc::getLiveVersionIdOfRecord('pages', $row['uid']);
  192. $this->inlineFirstPid = (is_null($liveVersionId) ? $row['uid'] : $liveVersionId);
  193. // If pid is negative, fetch the previous record and take its pid:
  194. } elseif ($row['pid'] < 0) {
  195. $prevRec = t3lib_BEfunc::getRecord($table, abs($row['pid']));
  196. $this->inlineFirstPid = $prevRec['pid'];
  197. // Take the pid as it is:
  198. } else {
  199. $this->inlineFirstPid = $row['pid'];
  200. }
  201. }
  202. // add the current inline job to the structure stack
  203. $this->pushStructure($table, $row['uid'], $field, $config);
  204. // e.g. data[<table>][<uid>][<field>]
  205. $nameForm = $this->inlineNames['form'];
  206. // e.g. data-<pid>-<table1>-<uid1>-<field1>-<table2>-<uid2>-<field2>
  207. $nameObject = $this->inlineNames['object'];
  208. // get the records related to this inline record
  209. $relatedRecords = $this->getRelatedRecords($table, $field, $row, $PA, $config);
  210. // set the first and last record to the config array
  211. $relatedRecordsUids = array_keys($relatedRecords['records']);
  212. $config['inline']['first'] = reset($relatedRecordsUids);
  213. $config['inline']['last'] = end($relatedRecordsUids);
  214. // Tell the browser what we have (using JSON later):
  215. $top = $this->getStructureLevel(0);
  216. $this->inlineData['config'][$nameObject] = array(
  217. 'table' => $foreign_table,
  218. 'md5' => md5($nameObject),
  219. );
  220. $this->inlineData['config'][$nameObject . self::Structure_Separator . $foreign_table] = array(
  221. 'min' => $minitems,
  222. 'max' => $maxitems,
  223. 'sortable' => $config['appearance']['useSortable'],
  224. 'top' => array(
  225. 'table' => $top['table'],
  226. 'uid' => $top['uid'],
  227. ),
  228. );
  229. // Set a hint for nested IRRE and tab elements:
  230. $this->inlineData['nested'][$nameObject] = $this->fObj->getDynNestedStack(FALSE, $this->isAjaxCall);
  231. // if relations are required to be unique, get the uids that have already been used on the foreign side of the relation
  232. if ($config['foreign_unique']) {
  233. // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one:
  234. $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']);
  235. // Get the used unique ids:
  236. $uniqueIds = $this->getUniqueIds($relatedRecords['records'], $config, $selConfig['type'] == 'groupdb');
  237. $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config, 'foreign_unique');
  238. $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === FALSE ? -1 : count($possibleRecords);
  239. $this->inlineData['unique'][$nameObject . self::Structure_Separator . $foreign_table] = array(
  240. 'max' => $uniqueMax,
  241. 'used' => $uniqueIds,
  242. 'type' => $selConfig['type'],
  243. 'table' => $config['foreign_table'],
  244. 'elTable' => $selConfig['table'], // element/record table (one step down in hierarchy)
  245. 'field' => $config['foreign_unique'],
  246. 'selector' => $selConfig['selector'],
  247. 'possible' => $this->getPossibleRecordsFlat($possibleRecords),
  248. );
  249. }
  250. // if it's required to select from possible child records (reusable children), add a selector box
  251. if ($config['foreign_selector']) {
  252. // if not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array
  253. if (!$config['foreign_unique']) {
  254. $possibleRecords = $this->getPossibleRecords($table, $field, $row, $config);
  255. $uniqueIds = array();
  256. }
  257. $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords, $config, $uniqueIds);
  258. $item .= $selectorBox;
  259. }
  260. // wrap all inline fields of a record with a <div> (like a container)
  261. $item .= '<div id="' . $nameObject . '">';
  262. // define how to show the "Create new record" link - if there are more than maxitems, hide it
  263. if ($relatedRecords['count'] >= $maxitems || ($uniqueMax > 0 && $relatedRecords['count'] >= $uniqueMax)) {
  264. $config['inline']['inlineNewButtonStyle'] = 'display: none;';
  265. }
  266. // Render the level links (create new record, localize all, synchronize):
  267. if ($config['appearance']['levelLinksPosition'] != 'none') {
  268. $levelLinks = $this->getLevelInteractionLink('newRecord', $nameObject . self::Structure_Separator . $foreign_table, $config);
  269. if ($language > 0) {
  270. // Add the "Localize all records" link before all child records:
  271. if (isset($config['appearance']['showAllLocalizationLink']) && $config['appearance']['showAllLocalizationLink']) {
  272. $levelLinks .= $this->getLevelInteractionLink('localize', $nameObject . self::Structure_Separator . $foreign_table, $config);
  273. }
  274. // Add the "Synchronize with default language" link before all child records:
  275. if (isset($config['appearance']['showSynchronizationLink']) && $config['appearance']['showSynchronizationLink']) {
  276. $levelLinks .= $this->getLevelInteractionLink('synchronize', $nameObject . self::Structure_Separator . $foreign_table, $config);
  277. }
  278. }
  279. }
  280. // Add the level links before all child records:
  281. if (in_array($config['appearance']['levelLinksPosition'], array('both', 'top'))) {
  282. $item .= $levelLinks;
  283. }
  284. $item .= '<div id="' . $nameObject . '_records">';
  285. $relationList = array();
  286. if (count($relatedRecords['records'])) {
  287. foreach ($relatedRecords['records'] as $rec) {
  288. $item .= $this->renderForeignRecord($row['uid'], $rec, $config);
  289. if (!isset($rec['__virtual']) || !$rec['__virtual']) {
  290. $relationList[] = $rec['uid'];
  291. }
  292. }
  293. }
  294. $item .= '</div>';
  295. // Add the level links after all child records:
  296. if (in_array($config['appearance']['levelLinksPosition'], array('both', 'bottom'))) {
  297. $item .= $levelLinks;
  298. }
  299. // add Drag&Drop functions for sorting to TCEforms::$additionalJS_post
  300. if (count($relationList) > 1 && $config['appearance']['useSortable']) {
  301. $this->addJavaScriptSortable($nameObject . '_records');
  302. }
  303. // publish the uids of the child records in the given order to the browser
  304. $item .= '<input type="hidden" name="' . $nameForm . '" value="' . implode(',', $relationList) . '" class="inlineRecord" />';
  305. // close the wrap for all inline fields (container)
  306. $item .= '</div>';
  307. // on finishing this section, remove the last item from the structure stack
  308. $this->popStructure();
  309. // if this was the first call to the inline type, restore the values
  310. if (!$this->getStructureDepth()) {
  311. unset($this->inlineFirstPid);
  312. }
  313. return $item;
  314. }
  315. /*******************************************************
  316. *
  317. * Regular rendering of forms, fields, etc.
  318. *
  319. *******************************************************/
  320. /**
  321. * Render the form-fields of a related (foreign) record.
  322. *
  323. * @param string $parentUid: The uid of the parent (embedding) record (uid or NEW...)
  324. * @param array $rec: The table record of the child/embedded table (normaly post-processed by t3lib_transferData)
  325. * @param array $config: content of $PA['fieldConf']['config']
  326. * @return string The HTML code for this "foreign record"
  327. */
  328. function renderForeignRecord($parentUid, $rec, $config = array()) {
  329. $foreign_table = $config['foreign_table'];
  330. $foreign_field = $config['foreign_field'];
  331. $foreign_selector = $config['foreign_selector'];
  332. // Register default localization content:
  333. $parent = $this->getStructureLevel(-1);
  334. if (isset($parent['localizationMode']) && $parent['localizationMode'] != FALSE) {
  335. $this->fObj->registerDefaultLanguageData($foreign_table, $rec);
  336. }
  337. // Send a mapping information to the browser via JSON:
  338. // e.g. data[<curTable>][<curId>][<curField>] => data-<pid>-<parentTable>-<parentId>-<parentField>-<curTable>-<curId>-<curField>
  339. $this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
  340. // Set this variable if we handle a brand new unsaved record:
  341. $isNewRecord = t3lib_div::testInt($rec['uid']) ? FALSE : TRUE;
  342. // Set this variable if the record is virtual and only show with header and not editable fields:
  343. $isVirtualRecord = (isset($rec['__virtual']) && $rec['__virtual']);
  344. // If there is a selector field, normalize it:
  345. if ($foreign_selector) {
  346. $rec[$foreign_selector] = $this->normalizeUid($rec[$foreign_selector]);
  347. }
  348. if (!$this->checkAccess(($isNewRecord ? 'new' : 'edit'), $foreign_table, $rec['uid'])) {
  349. return FALSE;
  350. }
  351. // Get the current naming scheme for DOM name/id attributes:
  352. $nameObject = $this->inlineNames['object'];
  353. $appendFormFieldNames = '[' . $foreign_table . '][' . $rec['uid'] . ']';
  354. $objectId = $nameObject . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid'];
  355. // Put the current level also to the dynNestedStack of TCEforms:
  356. $this->fObj->pushToDynNestedStack('inline', $objectId);
  357. if (!$isVirtualRecord) {
  358. // Get configuration:
  359. $collapseAll = (isset($config['appearance']['collapseAll']) && $config['appearance']['collapseAll']);
  360. $expandAll = (isset($config['appearance']['collapseAll']) && !$config['appearance']['collapseAll']);
  361. $ajaxLoad = (isset($config['appearance']['ajaxLoad']) && !$config['appearance']['ajaxLoad']) ? FALSE : TRUE;
  362. if ($isNewRecord) {
  363. // show this record expanded or collapsed
  364. $isExpanded = ($expandAll || (!$collapseAll ? 1 : 0));
  365. } else {
  366. $isExpanded = ($config['renderFieldsOnly'] || (!$collapseAll && $this->getExpandedCollapsedState($foreign_table, $rec['uid'])) || $expandAll);
  367. }
  368. // Render full content ONLY IF this is a AJAX-request, a new record, the record is not collapsed or AJAX-loading is explicitly turned off
  369. if ($isNewRecord || $isExpanded || !$ajaxLoad) {
  370. $combination = $this->renderCombinationTable($rec, $appendFormFieldNames, $config);
  371. $fields = $this->renderMainFields($foreign_table, $rec);
  372. $fields = $this->wrapFormsSection($fields);
  373. // Replace returnUrl in Wizard-Code, if this is an AJAX call
  374. $ajaxArguments = t3lib_div::_GP('ajax');
  375. if (isset($ajaxArguments[2]) && trim($ajaxArguments[2]) != '') {
  376. $fields = str_replace('P[returnUrl]=%2F' . rawurlencode(TYPO3_mainDir) . '%2Fajax.php', 'P[returnUrl]=' . rawurlencode($ajaxArguments[2]), $fields);
  377. }
  378. } else {
  379. $combination = '';
  380. // This string is the marker for the JS-function to check if the full content has already been loaded
  381. $fields = '<!--notloaded-->';
  382. }
  383. if ($isNewRecord) {
  384. // get the top parent table
  385. $top = $this->getStructureLevel(0);
  386. $ucFieldName = 'uc[inlineView][' . $top['table'] . '][' . $top['uid'] . ']' . $appendFormFieldNames;
  387. // set additional fields for processing for saving
  388. $fields .= '<input type="hidden" name="' . $this->prependFormFieldNames . $appendFormFieldNames . '[pid]" value="' . $rec['pid'] . '"/>';
  389. $fields .= '<input type="hidden" name="' . $ucFieldName . '" value="' . $isExpanded . '" />';
  390. } else {
  391. // set additional field for processing for saving
  392. $fields .= '<input type="hidden" name="' . $this->prependCmdFieldNames . $appendFormFieldNames . '[delete]" value="1" disabled="disabled" />';
  393. }
  394. // if this record should be shown collapsed
  395. if (!$isExpanded) {
  396. $appearanceStyleFields = ' style="display: none;"';
  397. }
  398. }
  399. if ($config['renderFieldsOnly']) {
  400. $out = $fields . $combination;
  401. } else {
  402. // set the record container with data for output
  403. $out = '<div class="t3-form-field-record-inline" id="' . $objectId . '_fields"' . $appearanceStyleFields . '>' . $fields . $combination . '</div>';
  404. $header = $this->renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord);
  405. $out = '<div class="t3-form-field-header-inline" id="' . $objectId . '_header">' . $header . '</div>' . $out;
  406. // wrap the header, fields and combination part of a child record with a div container
  407. $classMSIE = ($this->fObj->clientInfo['BROWSER'] == 'msie' && $this->fObj->clientInfo['VERSION'] < 8 ? 'MSIE' : '');
  408. $class = 'inlineDiv' . $classMSIE . ($isNewRecord ? ' inlineIsNewRecord' : '');
  409. $out = '<div id="' . $objectId . '_div" class="t3-form-field-container-inline ' . $class . '">' . $out . '</div>';
  410. }
  411. // Remove the current level also from the dynNestedStack of TCEforms:
  412. $this->fObj->popFromDynNestedStack();
  413. return $out;
  414. }
  415. /**
  416. * Wrapper for TCEforms::getMainFields().
  417. *
  418. * @param string $table: The table name
  419. * @param array $row: The record to be rendered
  420. * @return string The rendered form
  421. */
  422. protected function renderMainFields($table, $row) {
  423. // The current render depth of t3lib_TCEforms:
  424. $depth = $this->fObj->renderDepth;
  425. // If there is some information about already rendered palettes of our parent, store this info:
  426. if (isset($this->fObj->palettesRendered[$depth][$table])) {
  427. $palettesRendered = $this->fObj->palettesRendered[$depth][$table];
  428. }
  429. // Render the form:
  430. $content = $this->fObj->getMainFields($table, $row, $depth);
  431. // If there was some info about rendered palettes stored, write it back for our parent:
  432. if (isset($palettesRendered)) {
  433. $this->fObj->palettesRendered[$depth][$table] = $palettesRendered;
  434. }
  435. return $content;
  436. }
  437. /**
  438. * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc.
  439. * Later on the command-icons are inserted here.
  440. *
  441. * @param string $parentUid: The uid of the parent (embedding) record (uid or NEW...)
  442. * @param string $foreign_table: The foreign_table we create a header for
  443. * @param array $rec: The current record of that foreign_table
  444. * @param array $config: content of $PA['fieldConf']['config']
  445. * @param boolean $isVirtualRecord:
  446. * @return string The HTML code of the header
  447. */
  448. function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config, $isVirtualRecord = FALSE) {
  449. // Init:
  450. $objectId = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table . self::Structure_Separator . $rec['uid'];
  451. $expandSingle = $config['appearance']['expandSingle'] ? 1 : 0;
  452. // we need the returnUrl of the main script when loading the fields via AJAX-call (to correct wizard code, so include it as 3rd parameter)
  453. $onClick = "return inline.expandCollapseRecord('" . htmlspecialchars($objectId) . "', $expandSingle, '" . rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI')) . "')";
  454. // Pre-Processing:
  455. $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
  456. $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? TRUE : FALSE;
  457. $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? TRUE : FALSE;
  458. // Get the record title/label for a record:
  459. // render using a self-defined user function
  460. if ($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc']) {
  461. $params = array(
  462. 'table' => $foreign_table,
  463. 'row' => $rec,
  464. 'title' => '',
  465. 'isOnSymmetricSide' => $isOnSymmetricSide,
  466. 'parent' => array(
  467. 'uid' => $parentUid,
  468. 'config' => $config,
  469. ),
  470. );
  471. $null = NULL; // callUserFunction requires a third parameter, but we don't want to give $this as reference!
  472. t3lib_div::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null);
  473. $recTitle = $params['title'];
  474. // render the special alternative title
  475. } elseif ($hasForeignLabel || $hasSymmetricLabel) {
  476. $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
  477. $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol);
  478. // Render title for everything else than group/db:
  479. if ($foreignConfig['type'] != 'groupdb') {
  480. $recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, FALSE);
  481. // Render title for group/db:
  482. } else {
  483. // $recTitle could be something like: "tx_table_123|...",
  484. $valueParts = t3lib_div::trimExplode('|', $rec[$titleCol]);
  485. $itemParts = t3lib_div::revExplode('_', $valueParts[0], 2);
  486. $recTemp = t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]);
  487. $recTitle = t3lib_BEfunc::getRecordTitle($itemParts[0], $recTemp, FALSE);
  488. }
  489. $recTitle = t3lib_BEfunc::getRecordTitlePrep($recTitle);
  490. if (!strcmp(trim($recTitle), '')) {
  491. $recTitle = t3lib_BEfunc::getNoRecordTitle(TRUE);
  492. }
  493. // render the standard
  494. } else {
  495. $recTitle = t3lib_BEfunc::getRecordTitle($foreign_table, $rec, TRUE);
  496. }
  497. $altText = t3lib_BEfunc::getRecordIconAltText($rec, $foreign_table);
  498. $iconImg = t3lib_iconWorks::getSpriteIconForRecord($foreign_table, $rec, array('title' => htmlspecialchars($altText), 'id' => $objectId . '_icon'));
  499. $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>';
  500. if (!$isVirtualRecord) {
  501. $iconImg = $this->wrapWithAnchor($iconImg, '#', array('onclick' => $onClick));
  502. $label = $this->wrapWithAnchor($label, '#', array('onclick' => $onClick, 'style' => 'display: block;'));
  503. }
  504. $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config, $isVirtualRecord);
  505. // @TODO: Check the table wrapping and the CSS definitions
  506. $header =
  507. '<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-right: ' . $this->inlineStyles['margin-right'] . 'px;"' .
  508. ($this->fObj->borderStyle[2] ? ' background="' . htmlspecialchars($this->backPath . $this->fObj->borderStyle[2]) . '"' : '') .
  509. ($this->fObj->borderStyle[3] ? ' class="' . htmlspecialchars($this->fObj->borderStyle[3]) . '"' : '') . '>' .
  510. '<tr class="class-main12"><td width="18" id="' . $objectId . '_iconcontainer">' . $iconImg . '</td><td align="left"><strong>' . $label . '</strong></td><td align="right">' . $ctrl . '</td></tr></table>';
  511. return $header;
  512. }
  513. /**
  514. * Render the control-icons for a record header (create new, sorting, delete, disable/enable).
  515. * Most of the parts are copy&paste from class.db_list_extra.inc and modified for the JavaScript calls here
  516. *
  517. * @param string $parentUid: The uid of the parent (embedding) record (uid or NEW...)
  518. * @param string $foreign_table: The table (foreign_table) we create control-icons for
  519. * @param array $rec: The current record of that foreign_table
  520. * @param array $config: (modified) TCA configuration of the field
  521. * @return string The HTML code with the control-icons
  522. */
  523. function renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config = array(), $isVirtualRecord = FALSE) {
  524. // Initialize:
  525. $cells = array();
  526. $isNewItem = substr($rec['uid'], 0, 3) == 'NEW';
  527. $tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl'];
  528. $tcaTableCols =& $GLOBALS['TCA'][$foreign_table]['columns'];
  529. $isPagesTable = $foreign_table == 'pages' ? TRUE : FALSE;
  530. $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
  531. $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || (!$isOnSymmetricSide && $config['foreign_sortby']) || ($isOnSymmetricSide && $config['symmetric_sortby']) ? TRUE : FALSE;
  532. $nameObject = $this->inlineNames['object'];
  533. $nameObjectFt = $nameObject . self::Structure_Separator . $foreign_table;
  534. $nameObjectFtId = $nameObjectFt . self::Structure_Separator . $rec['uid'];
  535. $calcPerms = $GLOBALS['BE_USER']->calcPerms(
  536. t3lib_BEfunc::readPageAccess($rec['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1))
  537. );
  538. // If the listed table is 'pages' we have to request the permission settings for each page:
  539. if ($isPagesTable) {
  540. $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages', $rec['uid']));
  541. }
  542. // This expresses the edit permissions for this particular element:
  543. $permsEdit = ($isPagesTable && ($localCalcPerms & 2)) || (!$isPagesTable && ($calcPerms & 16));
  544. // Controls: Defines which controls should be shown
  545. $enabledControls = $config['appearance']['enabledControls'];
  546. // Hook: Can disable/enable single controls for specific child records:
  547. foreach ($this->hookObjects as $hookObj) {
  548. $hookObj->renderForeignRecordHeaderControl_preProcess($parentUid, $foreign_table, $rec, $config, $isVirtualRecord, $enabledControls);
  549. }
  550. // Icon to visualize that a required field is nested in this inline level:
  551. $cells['required'] = '<img name="' . $nameObjectFtId . '_req" src="clear.gif" width="10" height="10" hspace="4" vspace="3" alt="" />';
  552. if (isset($rec['__create'])) {
  553. $cells['localize.isLocalizable'] = t3lib_iconWorks::getSpriteIcon('actions-edit-localize-status-low', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:localize.isLocalizable', TRUE)));
  554. } elseif (isset($rec['__remove'])) {
  555. $cells['localize.wasRemovedInOriginal'] = t3lib_iconWorks::getSpriteIcon('actions-edit-localize-status-high', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:localize.wasRemovedInOriginal', 1)));
  556. }
  557. // "Info": (All records)
  558. if ($enabledControls['info'] && !$isNewItem) {
  559. $cells['info'] = '<a href="#" onclick="' . htmlspecialchars('top.launchView(\'' . $foreign_table . '\', \'' . $rec['uid'] . '\'); return false;') . '">' .
  560. t3lib_iconWorks::getSpriteIcon('status-dialog-information', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:showInfo', TRUE))) .
  561. '</a>';
  562. }
  563. // If the table is NOT a read-only table, then show these links:
  564. if (!$tcaTableCtrl['readOnly'] && !$isVirtualRecord) {
  565. // "New record after" link (ONLY if the records in the table are sorted by a "sortby"-row or if default values can depend on previous record):
  566. if ($enabledControls['new'] && ($enableManualSorting || $tcaTableCtrl['useColumnsForDefaultValues'])) {
  567. if (
  568. (!$isPagesTable && ($calcPerms & 16)) || // For NON-pages, must have permission to edit content on this parent page
  569. ($isPagesTable && ($calcPerms & 8)) // For pages, must have permission to create new pages here.
  570. ) {
  571. $onClick = "return inline.createNewRecord('" . $nameObjectFt . "','" . $rec['uid'] . "')";
  572. $class = ' class="inlineNewButton ' . $this->inlineData['config'][$nameObject]['md5'] . '"';
  573. if ($config['inline']['inlineNewButtonStyle']) {
  574. $style = ' style="' . $config['inline']['inlineNewButtonStyle'] . '"';
  575. }
  576. $cells['new'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '"' . $class . $style . '>' .
  577. t3lib_iconWorks::getSpriteIcon('actions-' . ($isPagesTable ? 'page' : 'document') . '-new',
  578. array(
  579. 'title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:new' . ($isPagesTable ? 'Page' : 'Record'), 1)
  580. )
  581. ) .
  582. '</a>';
  583. }
  584. }
  585. // Drag&Drop Sorting: Sortable handler for script.aculo.us
  586. if ($enabledControls['dragdrop'] && $permsEdit && $enableManualSorting && $config['appearance']['useSortable']) {
  587. $cells['dragdrop'] = t3lib_iconWorks::getSpriteIcon('actions-move-move', array('class' => 'sortableHandle', 'title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.move', TRUE)));
  588. }
  589. // "Up/Down" links
  590. if ($enabledControls['sort'] && $permsEdit && $enableManualSorting) {
  591. $onClick = "return inline.changeSorting('" . $nameObjectFtId . "', '1')"; // Up
  592. $style = $config['inline']['first'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
  593. $cells['sort.up'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '" class="sortingUp" ' . $style . '>' .
  594. t3lib_iconWorks::getSpriteIcon('actions-move-up', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:moveUp', TRUE))) .
  595. '</a>';
  596. $onClick = "return inline.changeSorting('" . $nameObjectFtId . "', '-1')"; // Down
  597. $style = $config['inline']['last'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
  598. $cells['sort.down'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '" class="sortingDown" ' . $style . '>' .
  599. t3lib_iconWorks::getSpriteIcon('actions-move-down', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:moveDown', TRUE))) .
  600. '</a>';
  601. }
  602. // "Hide/Unhide" links:
  603. $hiddenField = $tcaTableCtrl['enablecolumns']['disabled'];
  604. if ($enabledControls['hide'] && $permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $foreign_table . ':' . $hiddenField))) {
  605. $onClick = "return inline.enableDisableRecord('" . $nameObjectFtId . "')";
  606. if ($rec[$hiddenField]) {
  607. $cells['hide.unhide'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' .
  608. t3lib_iconWorks::getSpriteIcon(
  609. 'actions-edit-unhide',
  610. array(
  611. 'title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:unHide' . ($isPagesTable ? 'Page' : ''), 1),
  612. 'id' => $nameObjectFtId . '_disabled'
  613. )
  614. ) .
  615. '</a>';
  616. } else {
  617. $cells['hide.hide'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' .
  618. t3lib_iconWorks::getSpriteIcon(
  619. 'actions-edit-hide',
  620. array(
  621. 'title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:hide' . ($isPagesTable ? 'Page' : ''), 1),
  622. 'id' => $nameObjectFtId . '_disabled'
  623. )
  624. ) .
  625. '</a>';
  626. }
  627. }
  628. // "Delete" link:
  629. if ($enabledControls['delete'] && ($isPagesTable && $localCalcPerms & 4 || !$isPagesTable && $calcPerms & 16)) {
  630. $onClick = "inline.deleteRecord('" . $nameObjectFtId . "');";
  631. $cells['delete'] = '<a href="#" onclick="' . htmlspecialchars('if (confirm(' . $GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')) . ')) { ' . $onClick . ' } return false;') . '">' .
  632. t3lib_iconWorks::getSpriteIcon('actions-edit-delete', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:delete', TRUE))) .
  633. '</a>';
  634. }
  635. // If this is a virtual record offer a minimized set of icons for user interaction:
  636. } elseif ($isVirtualRecord) {
  637. if ($enabledControls['localize'] && isset($rec['__create'])) {
  638. $onClick = "inline.synchronizeLocalizeRecords('" . $nameObjectFt . "', " . $rec['uid'] . ");";
  639. $cells['localize'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' .
  640. t3lib_iconWorks::getSpriteIcon('actions-document-localize', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:localize', TRUE))) .
  641. '</a>';
  642. }
  643. }
  644. // If the record is edit-locked by another user, we will show a little warning sign:
  645. if ($lockInfo = t3lib_BEfunc::isRecordLocked($foreign_table, $rec['uid'])) {
  646. $cells['locked'] = '<a href="#" onclick="' . htmlspecialchars('alert(' . $GLOBALS['LANG']->JScharCode($lockInfo['msg']) . ');return false;') . '">' .
  647. t3lib_iconWorks::getSpriteIcon('status-warning-in-use', array('title' => htmlspecialchars($lockInfo['msg']))) .
  648. '</a>';
  649. }
  650. // Hook: Post-processing of single controls for specific child records:
  651. foreach ($this->hookObjects as $hookObj) {
  652. $hookObj->renderForeignRecordHeaderControl_postProcess($parentUid, $foreign_table, $rec, $config, $isVirtualRecord, $cells);
  653. }
  654. // Compile items into a DIV-element:
  655. return '
  656. <!-- CONTROL PANEL: ' . $foreign_table . ':' . $rec['uid'] . ' -->
  657. <div class="typo3-DBctrl">' . implode('', $cells) . '</div>';
  658. }
  659. /**
  660. * Render a table with TCEforms, that occurs on a intermediate table but should be editable directly,
  661. * so two tables are combined (the intermediate table with attributes and the sub-embedded table).
  662. * -> This is a direct embedding over two levels!
  663. *
  664. * @param array $rec: The table record of the child/embedded table (normaly post-processed by t3lib_transferData)
  665. * @param string $appendFormFieldNames: The [<table>][<uid>] of the parent record (the intermediate table)
  666. * @param array $config: content of $PA['fieldConf']['config']
  667. * @return string A HTML string with <table> tag around.
  668. */
  669. function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array()) {
  670. $foreign_table = $config['foreign_table'];
  671. $foreign_selector = $config['foreign_selector'];
  672. if ($foreign_selector && $config['appearance']['useCombination']) {
  673. $comboConfig = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector]['config'];
  674. $comboRecord = array();
  675. // If record does already exist, load it:
  676. if ($rec[$foreign_selector] && t3lib_div::testInt($rec[$foreign_selector])) {
  677. $comboRecord = $this->getRecord(
  678. $this->inlineFirstPid,
  679. $comboConfig['foreign_table'],
  680. $rec[$foreign_selector]
  681. );
  682. $isNewRecord = FALSE;
  683. // It is a new record, create a new record virtually:
  684. } else {
  685. $comboRecord = $this->getNewRecord(
  686. $this->inlineFirstPid,
  687. $comboConfig['foreign_table']
  688. );
  689. $isNewRecord = TRUE;
  690. }
  691. // get the TCEforms interpretation of the TCA of the child table
  692. $out = $this->renderMainFields($comboConfig['foreign_table'], $comboRecord);
  693. $out = $this->wrapFormsSection($out, array(), array('class' => 'wrapperAttention'));
  694. // if this is a new record, add a pid value to store this record and the pointer value for the intermediate table
  695. if ($isNewRecord) {
  696. $comboFormFieldName = $this->prependFormFieldNames . '[' . $comboConfig['foreign_table'] . '][' . $comboRecord['uid'] . '][pid]';
  697. $out .= '<input type="hidden" name="' . $comboFormFieldName . '" value="' . $comboRecord['pid'] . '" />';
  698. }
  699. // if the foreign_selector field is also responsible for uniqueness, tell the browser the uid of the "other" side of the relation
  700. if ($isNewRecord || $config['foreign_unique'] == $foreign_selector) {
  701. $parentFormFieldName = $this->prependFormFieldNames . $appendFormFieldNames . '[' . $foreign_selector . ']';
  702. $out .= '<input type="hidden" name="' . $parentFormFieldName . '" value="' . $comboRecord['uid'] . '" />';
  703. }
  704. }
  705. return $out;
  706. }
  707. /**
  708. * Get a selector as used for the select type, to select from all available
  709. * records and to create a relation to the embedding record (e.g. like MM).
  710. *
  711. * @param array $selItems: Array of all possible records
  712. * @param array $conf: TCA configuration of the parent(!) field
  713. * @param array $uniqueIds: The uids that have already been used and should be unique
  714. * @return string A HTML <select> box with all possible records
  715. */
  716. function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds = array()) {
  717. $foreign_table = $conf['foreign_table'];
  718. $foreign_selector = $conf['foreign_selector'];
  719. $selConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_selector);
  720. $config = $selConfig['PA']['fieldConf']['config'];
  721. if ($selConfig['type'] == 'select') {
  722. $item = $this->renderPossibleRecordsSelectorTypeSelect($selItems, $conf, $selConfig['PA'], $uniqueIds);
  723. } elseif ($selConfig['type'] == 'groupdb') {
  724. $item = $this->renderPossibleRecordsSelectorTypeGroupDB($conf, $selConfig['PA']);
  725. }
  726. return $item;
  727. }
  728. /**
  729. * Get a selector as used for the select type, to select from all available
  730. * records and to create a relation to the embedding record (e.g. like MM).
  731. *
  732. * @param array $selItems: Array of all possible records
  733. * @param array $conf: TCA configuration of the parent(!) field
  734. * @param array $PA: An array with additional configuration options
  735. * @param array $uniqueIds: The uids that have already been used and should be unique
  736. * @return string A HTML <select> box with all possible records
  737. */
  738. function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds = array()) {
  739. $foreign_table = $conf['foreign_table'];
  740. $foreign_selector = $conf['foreign_selector'];
  741. $PA = array();
  742. $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
  743. $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ? $PA['fieldConf']['config']['form_type'] : $PA['fieldConf']['config']['type']; // Using "form_type" locally in this script
  744. $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table, array(), $foreign_selector);
  745. $config = $PA['fieldConf']['config'];
  746. //TODO: $disabled is not present - should be read from config?
  747. $disabled = FALSE;
  748. if (!$disabled) {
  749. // Create option tags:
  750. $opt = array();
  751. $styleAttrValue = '';
  752. foreach ($selItems as $p) {
  753. if ($config['iconsInOptionTags']) {
  754. $styleAttrValue = $this->fObj->optionTagStyle($p[2]);
  755. }
  756. if (!in_array($p[1], $uniqueIds)) {
  757. $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' .
  758. ' style="' . (in_array($p[1], $uniqueIds) ? '' : '') .
  759. ($styleAttrValue ? ' style="' . htmlspecialchars($styleAttrValue) : '') . '">' .
  760. htmlspecialchars($p[0]) . '</option>';
  761. }
  762. }
  763. // Put together the selector box:
  764. $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"' : ' style="' . $this->fObj->defaultMultipleSelectorStyle . '"';
  765. $size = intval($conf['size']);
  766. $size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($selItems) + 1, t3lib_div::intInRange($size, 1), $conf['autoSizeMax']) : $size;
  767. $onChange = "return inline.importNewRecord('" . $this->inlineNames['object'] . self::Structure_Separator . $conf['foreign_table'] . "')";
  768. $item = '
  769. <select id="' . $this->inlineNames['object'] . self::Structure_Separator . $conf['foreign_table'] . '_selector"' .
  770. $this->fObj->insertDefStyle('select') .
  771. ($size ? ' size="' . $size . '"' : '') .
  772. ' onchange="' . htmlspecialchars($onChange) . '"' .
  773. $PA['onFocus'] .
  774. $selector_itemListStyle .
  775. ($conf['foreign_unique'] ? ' isunique="isunique"' : '') . '>
  776. ' . implode('
  777. ', $opt) . '
  778. </select>';
  779. // add a "Create new relation" link for adding new relations
  780. // this is neccessary, if the size of the selector is "1" or if
  781. // there is only one record item in the select-box, that is selected by default
  782. // the selector-box creates a new relation on using a onChange event (see some line above)
  783. $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation', 1);
  784. $item .=
  785. '<a href="#" onclick="' . htmlspecialchars($onChange) . '" align="abstop">' .
  786. t3lib_iconWorks::getSpriteIcon('actions-document-new', array('title' => $createNewRelationText)) . $createNewRelationText .
  787. '</a>';
  788. // wrap the selector and add a spacer to the bottom
  789. $item = '<div style="margin-bottom: 20px;">' . $item . '</div>';
  790. }
  791. return $item;
  792. }
  793. /**
  794. * Generate a link that opens an element browser in a new window.
  795. * For group/db there is no way o use a "selector" like a <select>|</select>-box.
  796. *
  797. * @param array $conf: TCA configuration of the parent(!) field
  798. * @param array $PA: An array with additional configuration options
  799. * @return string A HTML link that opens an element browser in a new window
  800. */
  801. function renderPossibleRecordsSelectorTypeGroupDB($conf, &$PA) {
  802. $foreign_table = $conf['foreign_table'];
  803. $config = $PA['fieldConf']['config'];
  804. $allowed = $config['allowed'];
  805. $objectPrefix = $this->inlineNames['object'] . self::Structure_Separator . $foreign_table;
  806. $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation', 1);
  807. $onClick = "setFormValueOpenBrowser('db','" . ('|||' . $allowed . '|' . $objectPrefix . '|inline.checkUniqueElement||inline.importElement') . "'); return false;";
  808. $item =
  809. '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' .
  810. t3lib_iconWorks::getSpriteIcon('actions-insert-record', array('title' => $createNewRelationText)) . $createNewRelationText .
  811. '</a>';
  812. return $item;
  813. }
  814. /**
  815. * Creates the HTML code of a general link to be used on a level of inline children.
  816. * The possible keys for the parameter $type are 'newRecord', 'localize' and 'synchronize'.
  817. *
  818. * @param string $type: The link type, values are 'newRecord', 'localize' and 'synchronize'.
  819. * @param string $objectPrefix: The "path" to the child record to create (e.g. 'data-parentPageId-partenTable-parentUid-parentField-childTable]')
  820. * @param array $conf: TCA configuration of the parent(!) field
  821. * @return string The HTML code of the new link, wrapped in a div
  822. */
  823. protected function getLevelInteractionLink($type, $objectPrefix, $conf = array()) {
  824. $nameObject = $this->inlineNames['object'];
  825. $attributes = array();
  826. switch ($type) {
  827. case 'newRecord':
  828. $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:cm.createnew', 1);
  829. $icon = 'actions-document-new';
  830. $className = 'typo3-newRecordLink';
  831. $attributes['class'] = 'inlineNewButton ' . $this->inlineData['config'][$nameObject]['md5'];
  832. $attributes['onclick'] = "return inline.createNewRecord('$objectPrefix')";
  833. if (isset($conf['inline']['inlineNewButtonStyle']) && $conf['inline']['inlineNewButtonStyle']) {
  834. $attributes['style'] = $conf['inline']['inlineNewButtonStyle'];
  835. }
  836. if (isset($conf['appearance']['newRecordLinkAddTitle']) && $conf['appearance']['newRecordLinkAddTitle']) {
  837. $titleAddon = ' ' . $GLOBALS['LANG']->sL($GLOBALS['TCA'][$conf['foreign_table']]['ctrl']['title'], 1);
  838. }
  839. break;
  840. case 'localize':
  841. $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:localizeAllRecords', 1);
  842. $icon = 'actions-document-localize';
  843. $className = 'typo3-localizationLink';
  844. $attributes['onclick'] = "return inline.synchronizeLocalizeRecords('$objectPrefix', 'localize')";
  845. break;
  846. case 'synchronize':
  847. $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xml:synchronizeWithOriginalLanguage', 1);
  848. $icon = 'actions-document-synchronize';
  849. $className = 'typo3-synchronizationLink';
  850. $attributes['class'] = 'inlineNewButton ' . $this->inlineData['config'][$nameObject]['md5'];
  851. $attributes['onclick'] = "return inline.synchronizeLocalizeRecords('$objectPrefix', 'synchronize')";
  852. break;
  853. }
  854. // Create the link:
  855. $icon = ($icon ? t3lib_iconWorks::getSpriteIcon($icon, array('title' => htmlspecialchars($title . $titleAddon))) : '');
  856. $link = $this->wrapWithAnchor($icon . $title . $titleAddon, '#', $attributes);
  857. return '<div' . ($className ? ' class="' . $className . '"' : '') . '>' . $link . '</div>';
  858. }
  859. /**
  860. * Add Sortable functionality using script.acolo.us "Sortable".
  861. *
  862. * @param string $objectId: The container id of the object - elements inside will be sortable
  863. * @return void
  864. */
  865. function addJavaScriptSortable($objectId) {
  866. $this->fObj->additionalJS_post[] = '
  867. inline.createDragAndDropSorting("' . $objectId . '");
  868. ';
  869. }
  870. /*******************************************************
  871. *
  872. * Handling of AJAX calls
  873. *
  874. *******************************************************/
  875. /**
  876. * General processor for AJAX requests concerning IRRE.
  877. * (called by typo3/ajax.php)
  878. *
  879. * @param array $params: additional parameters (not used here)
  880. * @param TYPO3AJAX $ajaxObj: the TYPO3AJAX object of this request
  881. * @return void
  882. */
  883. public function processAjaxRequest($params, $ajaxObj) {
  884. $ajaxArguments = t3lib_div::_GP('ajax');
  885. $ajaxIdParts = explode('::', $GLOBALS['ajaxID'], 2);
  886. if (isset($ajaxArguments) && is_array($ajaxArguments) && count($ajaxArguments)) {
  887. $ajaxMethod = $ajaxIdParts[1];
  888. switch ($ajaxMethod) {
  889. case 'createNewRecord':
  890. case 'synchronizeLocalizeRecords':
  891. case 'getRecordDetails':
  892. $this->isAjaxCall = TRUE;
  893. // Construct runtime environment for Inline Relational Record Editing:
  894. $this->processAjaxRequestConstruct($ajaxArguments);
  895. // Parse the DOM identifier (string), add the levels to the structure stack (array) and load the TCA config:
  896. $this->parseStructureString($ajaxArguments[0], TRUE);
  897. // Render content:
  898. $ajaxObj->setContentFormat('jsonbody');
  899. $ajaxObj->setContent(
  900. call_user_func_array(array(&$this, $ajaxMethod), $ajaxArguments)
  901. );
  902. break;
  903. case 'setExpandedCollapsedState':
  904. $ajaxObj->setContentFormat('jsonbody');
  905. call_user_func_array(array(&$this, $ajaxMethod), $ajaxArguments);
  906. break;
  907. }
  908. }
  909. }
  910. /**
  911. * Construct runtime environment for Inline Relational Record Editing.
  912. * - creates an anoymous SC_alt_doc in $GLOBALS['SOBE']
  913. * - creates a t3lib_TCEforms in $GLOBALS['SOBE']->tceforms
  914. * - sets ourself as reference to $GLOBALS['SOBE']->tceforms->inline
  915. * - sets $GLOBALS['SOBE']->tceforms->RTEcounter to the current situation on client-side
  916. *
  917. * @param array &$ajaxArguments: The arguments to be processed by the AJAX request
  918. * @return void
  919. */
  920. protected function processAjaxRequestConstruct(&$ajaxArguments) {
  921. global $SOBE, $BE_USER, $TYPO3_CONF_VARS;
  922. require_once(PATH_typo3 . 'template.php');
  923. $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_alt_doc.xml');
  924. // Create a new anonymous object:
  925. $SOBE = new stdClass();
  926. $SOBE->MOD_MENU = array(
  927. 'showPalettes' => '',
  928. 'showDescriptions' => '',
  929. 'disableRTE' => ''
  930. );
  931. // Setting virtual document name
  932. $SOBE->MCONF['name'] = 'xMOD_alt_doc.php';
  933. // CLEANSE SETTINGS
  934. $SOBE->MOD_SETTINGS = t3lib_BEfunc::getModuleData(
  935. $SOBE->MOD_MENU,
  936. t3lib_div::_GP('SET'),
  937. $SOBE->MCONF['…

Large files files are truncated, but you can click here to view the full file