0
0
mirror of https://github.com/salesagility/SuiteCRM.git synced 2024-11-22 07:52:36 +00:00
salesagility_SuiteCRM/modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php
2023-07-18 15:53:09 +01:00

937 lines
36 KiB
PHP
Executable File

<?php
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataParser.php';
require_once 'modules/ModuleBuilder/parsers/views/MetaDataParserInterface.php';
require_once 'modules/ModuleBuilder/parsers/constants.php';
class GridLayoutMetaDataParser extends AbstractMetaDataParser implements MetaDataParserInterface
{
public $_view;
/**
* @var array $variableMap
*/
public static $variableMap = array(
MB_EDITVIEW => 'EditView',
MB_DETAILVIEW => 'DetailView',
MB_QUICKCREATE => 'QuickCreate',
);
/**
* @var array $FILLER
*/
protected $FILLER;
/**
* @var array $_originalViewDef
*/
protected $_originalViewDef;
/**
* Constructor
* @param string $view The view type, that is, editview, searchview etc
* @param string $moduleName The name of the module to which this view belongs
* @param string $packageName If not empty, the name of the package to which this view belongs
*/
public function __construct($view, $moduleName, $packageName = '')
{
$GLOBALS ['log']->debug(get_class($this) . "->__construct( {$view} , {$moduleName} , {$packageName} )");
$view = strtolower($view);
$this->FILLER = array(
'name' => MBConstants::$FILLER['name'],
'label' => translate(MBConstants::$FILLER['label'])
);
$this->_moduleName = $moduleName;
$this->_view = $view;
if (empty($packageName)) {
require_once 'modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php';
$this->implementation = new DeployedMetaDataImplementation($view, $moduleName, self::$variableMap);
} else {
require_once 'modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation.php';
$this->implementation = new UndeployedMetaDataImplementation($view, $moduleName, $packageName);
}
$viewdefs = $this->implementation->getViewdefs();
if (!isset(self::$variableMap [$view])) {
self::$variableMap [$view] = $view;
}
if (!isset($viewdefs [self::$variableMap [$view]])) {
sugar_die(get_class($this) . ": incorrect view variable for $view");
}
$viewdefs = $viewdefs [self::$variableMap [$view]];
if (!isset($viewdefs ['templateMeta'])) {
sugar_die(get_class($this) . ": missing templateMeta section in layout definition (case sensitive)");
}
if (!isset($viewdefs ['panels'])) {
sugar_die(get_class($this) . ": missing panels section in layout definition (case sensitive)");
}
$this->_viewdefs = $viewdefs;
if ($this->getMaxColumns() < 1) {
sugar_die(get_class($this) . ": maxColumns=" . $this->getMaxColumns() . " - must be greater than 0!");
}
$this->_fielddefs = $this->implementation->getFielddefs();
$this->_standardizeFieldLabels($this->_fielddefs);
$this->_viewdefs ['panels'] = $this->_convertFromCanonicalForm(
$this->_viewdefs ['panels'],
$this->_fielddefs
); // put into our internal format
$this->_originalViewDef = $this->getFieldsFromLayout($this->implementation->getOriginalViewdefs());
}
/**
* Save a draft layout
* @param bool $populate
*/
public function writeWorkingFile($populate = true)
{
if ($populate) {
$this->_populateFromRequest($this->_fielddefs);
}
$viewdefs = $this->_viewdefs;
$viewdefs ['panels'] = $this->_convertToCanonicalForm($this->_viewdefs ['panels'], $this->_fielddefs);
$this->implementation->save(array(self::$variableMap [$this->_view] => $viewdefs));
}
/**
* Deploy the layout
* @param boolean $populate If true (default), then update the layout first with new layout information from the $_REQUEST array
*/
public function handleSave($populate = true)
{
$GLOBALS ['log']->info(get_class($this) . "->handleSave()");
if ($populate) {
$this->_populateFromRequest($this->_fielddefs);
}
$viewdefs = $this->_viewdefs;
$viewdefs ['panels'] = $this->_convertToCanonicalForm($this->_viewdefs ['panels'], $this->_fielddefs);
$this->implementation->deploy(array(self::$variableMap [$this->_view] => $viewdefs));
}
/**
* Return the layout, padded out with (empty) and (filler) fields ready for display
* @return array
*/
public function getLayout()
{
$viewDefinitions = array();
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
foreach ($panel as $rowID => $row) {
foreach ($row as $colID => $fieldName) {
if (isset($this->_fielddefs [$fieldName])) {
$viewDefinitions [$panelID] [$rowID] [$colID] = self::_trimFieldDefs($this->_fielddefs [$fieldName]);
} else {
if (isset($this->_originalViewDef [$fieldName]) && is_array($this->_originalViewDef [$fieldName])) {
$viewDefinitions [$panelID] [$rowID] [$colID] = self::_trimFieldDefs($this->_originalViewDef [$fieldName]);
} else {
$viewDefinitions [$panelID] [$rowID] [$colID] = array(
"name" => $fieldName,
"label" => $fieldName
);
}
}
}
}
}
return $viewDefinitions;
}
/**
* Return the tab definitions for tab/panel combo
* @return array
*/
public function getTabDefs()
{
$tabDefs = array();
$this->setUseTabs(false);
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
$tabDefs [strtoupper($panelID)] = array();
// panel or tab setting
if (isset($this->_viewdefs ['templateMeta'] ['tabDefs'] [strtoupper($panelID)] ['newTab'])
&& is_bool($this->_viewdefs ['templateMeta'] ['tabDefs'] [strtoupper($panelID)] ['newTab'])
) {
$tabDefs [strtoupper($panelID)] ['newTab'] = $this->_viewdefs ['templateMeta'] ['tabDefs'] [strtoupper($panelID)] ['newTab'];
if ($tabDefs [strtoupper($panelID)] ['newTab'] == true) {
$this->setUseTabs(true);
}
} else {
$tabDefs [strtoupper($panelID)] ['newTab'] = false;
}
// collapsed panels
if (isset($this->_viewdefs ['templateMeta'] ['tabDefs'] [strtoupper($panelID)] ['panelDefault'])
&& $this->_viewdefs ['templateMeta'] ['tabDefs'] [strtoupper($panelID)] ['panelDefault'] == 'collapsed'
) {
$tabDefs [strtoupper($panelID)] ['panelDefault'] = 'collapsed';
} else {
$tabDefs [strtoupper($panelID)] ['panelDefault'] = 'expanded';
}
}
return $tabDefs;
}
/**
* Set tab definitions
* @param $tabDefs
*/
public function setTabDefs($tabDefs)
{
$this->_viewdefs ['templateMeta'] ['tabDefs'] = $tabDefs;
}
/**
* @return int
*/
public function getMaxColumns()
{
if (!empty($this->_viewdefs) && isset($this->_viewdefs ['templateMeta'] ['maxColumns'])) {
return $this->_viewdefs ['templateMeta'] ['maxColumns'];
} else {
return 2;
}
}
/**
* @return array
*/
public function getAvailableFields()
{
// Obtain the full list of valid fields in this module
$availableFields = array();
foreach ($this->_fielddefs as $key => $def) {
if (GridLayoutMetaDataParser::validField($def, $this->_view) || isset($this->_originalViewDef[$key])) {
//If the field original label existing, we should use the original label instead the label in its fielddefs.
if (isset($this->_originalViewDef[$key]) && is_array($this->_originalViewDef[$key]) && isset($this->_originalViewDef[$key]['label'])) {
$availableFields [$key] = array('name' => $key, 'label' => $this->_originalViewDef[$key]['label']);
} else {
$availableFields [$key] = array(
'name' => $key,
'label' => isset($def ['label']) ? $def ['label'] : $def['vname']
); // layouts use 'label' not 'vname' for the label entry
}
$availableFields[$key]['translatedLabel'] = translate(
isset($def ['label']) ? $def ['label'] : $def['vname'],
$this->_moduleName
);
}
}
// Available fields are those that are in the Model and the original layout definition, but not already shown in the View
// So, because the formats of the two are different we brute force loop through View and unset the fields we find in a copy of Model
if (!empty($this->_viewdefs)) {
foreach ($this->_viewdefs ['panels'] as $panel) {
foreach ($panel as $row) {
foreach ($row as $field) {
unset($availableFields [$field]);
}
}
}
}
//eggsurplus: Bug 10329 - sort on intuitive display labels
//sort by translatedLabel
function cmpLabel($a, $b)
{
return strcmp($a["translatedLabel"], $b["translatedLabel"]);
}
usort($availableFields, 'cmpLabel');
return $availableFields;
}
/**
* @param $panelID
* @return bool
*/
public function getPanelDependency($panelID)
{
if (!isset($this->_viewdefs ['templateMeta']['dependency']) && !isset($this->_viewdefs ['templateMeta']['dependency'] [$panelID])) {
return false;
}
return $this->_viewdefs ['templateMeta']['dependency'] [$panelID];
}
/**
* Add a new field to the layout
* If $panelID is passed in, attempt to add to that panel, otherwise add to the first panel
* The field is added in place of the first empty (not filler) slot after the last field in the panel; if that row is full, then a new row will be added to the end of the panel
* and the field added to the start of it.
* @param array $def Set of properties for the field, in same format as in the viewdefs
* @param string|bool $panelID Identifier of the panel to add the field to; empty or false if we should use the first panel
* @return bool
*/
public function addField($def, $panelID = false)
{
if ((is_countable($this->_viewdefs ['panels']) ? count($this->_viewdefs ['panels']) : 0) == 0) {
$GLOBALS ['log']->error(get_class($this) . "->addField(): _viewdefs empty for module {$this->_moduleName} and view {$this->_view}");
}
// if a panelID was not provided, use the first available panel in the list
if (!$panelID) {
$panelIdList = array_keys($this->_viewdefs ['panels']);
$panelID = array_shift($panelIdList);
}
if (isset($this->_viewdefs ['panels'] [$panelID])) {
$panel = $this->_viewdefs ['panels'] [$panelID];
$lastrow = (is_countable($panel) ? count($panel) : 0) - 1; // index starts at 0
$maxColumns = $this->getMaxColumns();
$lastRowDef = $this->_viewdefs ['panels'] [$panelID] [$lastrow];
for ($column = 0; $column < $maxColumns; $column++) {
if (!isset($lastRowDef [$column])
|| (is_array($lastRowDef [$column]) && $lastRowDef [$column]['name'] == '(empty)')
|| (is_string($lastRowDef [$column]) && $lastRowDef [$column] == '(empty)')
) {
break;
}
}
// if we're on the last column of the last row, start a new row
if ($column >= $maxColumns) {
$lastrow++;
$this->_viewdefs ['panels'] [$panelID] [$lastrow] = array();
$column = 0;
}
$this->_viewdefs ['panels'] [$panelID] [$lastrow] [$column] = $def ['name'];
// now update the fielddefs
if (isset($this->_fielddefs [$def ['name']])) {
$this->_fielddefs [$def ['name']] = array_merge($this->_fielddefs [$def ['name']], $def);
} else {
$this->_fielddefs [$def ['name']] = $def;
}
}
return true;
}
/**
* Remove all instances of a field from the layout, and replace by (filler)
* Filler because we attempt to preserve the customized layout as much as possible - replacing by (empty) would mean that the positions or sizes of adjacent fields may change
* If the last row of a panel only consists of (filler) after removing the fields, then remove the row also. This undoes the standard addField() scenario;
* If the fields had been moved around in the layout however then this will not completely undo any addField()
* @param string $fieldName Name of the field to remove
* @return boolean True if the field was removed; false otherwise
*/
public function removeField($fieldName)
{
$GLOBALS ['log']->info(get_class($this) . "->removeField($fieldName)");
$result = false;
reset($this->_viewdefs);
$firstPanelID = array_shift(array_keys($this->_viewdefs['panels']));
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
$lastRowTouched = false;
$lastRowID = (is_countable($this->_viewdefs ['panels'] [$panelID]) ? count($this->_viewdefs ['panels'] [$panelID]) : 0) - 1; // zero offset
foreach ($panel as $rowID => $row) {
foreach ($row as $colID => $field) {
if ($field == $fieldName) {
$lastRowTouched = $rowID;
$this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = $this->FILLER ['name'];
}
}
}
// if we removed a field from the last row of this panel, tidy up if the last row now consists only of (empty) or (filler)
if ($lastRowTouched == $lastRowID) {
$lastRow = $this->_viewdefs ['panels'] [$panelID] [$lastRowID]; // can't use 'end' for this as we need the key as well as the value...
$empty = true;
foreach ($lastRow as $colID => $field) {
$empty &= $field == MBConstants::$EMPTY ['name'] || $field == $this->FILLER ['name'];
}
if ($empty) {
unset($this->_viewdefs ['panels'] [$panelID] [$lastRowID]);
// if the row was the only one in the panel, and the panel is not the first (default) panel, then remove the panel also
if ((is_countable($this->_viewdefs ['panels'] [$panelID]) ? count($this->_viewdefs ['panels'] [$panelID]) : 0) == 0 && $panelID != $firstPanelID) {
unset($this->_viewdefs ['panels'] [$panelID]);
}
}
}
$result |= ($lastRowTouched !== false); // explicitly compare to false as row 0 will otherwise evaluate as false
}
return $result;
}
/**
* @param string $panelID
* @param $dependency
* @return bool
*/
public function setPanelDependency($panelID, $dependency)
{
// only accept dependencies for pre-existing panels
if (!isset($this->_viewdefs ['panels'] [$panelID])) {
return false;
}
$this->_viewdefs ['templateMeta'] ['dependency'] [$panelID] = $dependency;
return true;
}
/**
* Return an integer value for the next unused panel identifier, such that it and any larger numbers are guaranteed to be unused already in the layout
* Necessary when adding new panels to a layout
* @return integer First unique panel ID suffix
*/
public function getFirstNewPanelId()
{
$firstNewPanelId = 0;
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
// strip out all but the numerics from the panelID - can't just use a cast as numbers may not be first in the string
for ($i = 0, $result = ''; $i < strlen((string) $panelID); $i++) {
if (is_numeric($panelID [$i])) {
$result .= $panelID [$i];
}
}
$firstNewPanelId = max(( int )$result, $firstNewPanelId);
}
return $firstNewPanelId + 1;
}
/**
* Load the panel layout from the submitted form and update the _viewdefs
* @param array $fieldDefinitions
*/
protected function _populateFromRequest(&$fieldDefinitions)
{
$GLOBALS ['log']->debug(get_class($this) . "->populateFromRequest()");
$i = 1;
// set up the map of panel# (as provided in the _REQUEST) to panel ID (as used in $this->_viewdefs['panels'])
$i = 1;
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
$panelMap [$i++] = $panelID;
}
foreach ($_REQUEST as $key => $displayLabel) {
$components = explode('-', $key);
if ($components [0] == 'panel' && $components [2] == 'label') {
$panelMap [$components ['1']] = $displayLabel;
}
}
$this->_viewdefs ['panels'] = array(); // because the new field properties should replace the old fields, not be merged
// run through the $_REQUEST twice - first to obtain the fieldnames, the second to update the field properties
for ($pass = 1; $pass <= 2; $pass++) {
foreach ($_REQUEST as $slot => $value) {
$slotComponents = explode('-', $slot); // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
if ($slotComponents [0] == 'slot') {
$slotNumber = $slotComponents ['2'];
$panelID = $panelMap [$slotComponents ['1']];
$rowID = floor($slotNumber / $this->getMaxColumns());
$colID = $slotNumber - ($rowID * $this->getMaxColumns());
$property = $slotComponents ['3'];
//If this field has a custom definition, copy that over
if ($pass == 1) {
if ($property == 'name') {
$this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = $value;
}
} else {
// update fielddefs for this property in the provided position
if (isset($this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID])) {
$fieldname = $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID];
$fieldDefinitions [$fieldname] [$property] = $value;
}
}
}
}
}
//Set the tab definitions
$tabDefs = array();
$this->setUseTabs(false);
foreach ($this->_viewdefs ['panels'] as $panelID => $panel) {
// panel or tab setting
$tabDefs [strtoupper($panelID)] = array();
if (isset($_REQUEST['tabDefs_' . $panelID . '_newTab'])) {
$tabDefs [strtoupper($panelID)] ['newTab'] = ($_REQUEST['tabDefs_' . $panelID . '_newTab'] == '1') ? true : false;
if ($tabDefs [strtoupper($panelID)] ['newTab'] == true) {
$this->setUseTabs(true);
}
} else {
$tabDefs [strtoupper($panelID)] ['newTab'] = false;
}
// collapse panel
if (isset($_REQUEST['tabDefs_' . $panelID . '_panelDefault'])) {
$tabDefs [strtoupper($panelID)] ['panelDefault'] = ($_REQUEST['tabDefs_' . $panelID . '_panelDefault'] == 'collapsed') ? 'collapsed' : 'expanded';
} else {
$tabDefs [strtoupper($panelID)] ['panelDefault'] = 'expanded';
}
}
$this->setTabDefs($tabDefs);
//bug: 38232 - Set the sync detail and editview settings
if (isset($_REQUEST['sync_detail_and_edit'])) {
if ($_REQUEST['sync_detail_and_edit'] === false || $_REQUEST['sync_detail_and_edit'] === "false") {
$this->setSyncDetailEditViews(false);
} elseif (!empty($_REQUEST['sync_detail_and_edit'])) {
$this->setSyncDetailEditViews(true);
}
}
$GLOBALS ['log']->debug(print_r($this->_viewdefs ['panels'], true));
}
/**
* Convert our internal format back to the standard Canonical MetaData layout
* First non-(empty) field goes in at column 0; all other (empty)'s removed
* Studio required fields are also added to the layout.
* Do this AFTER reading in all the $_REQUEST parameters as can't guarantee the order of those, and we need to operate on complete rows
* @param array $panels
* @param array $fieldDefinitions
* @return mixed
*/
protected function _convertToCanonicalForm($panels, $fieldDefinitions)
{
$previousViewDef = $this->getFieldsFromLayout($this->implementation->getViewdefs());
$oldDefs = $this->implementation->getViewdefs();
$currentFields = $this->getFieldsFromLayout($this->_viewdefs);
foreach ($fieldDefinitions as $field => $def) {
if (self::fieldIsRequired($def) && !isset($currentFields[$field])) {
//Use the previous viewdef if this field was on it.
if (isset($previousViewDef[$field])) {
$def = $previousViewDef[$field];
} //next see if the field was on the original layout.
else {
if (isset($this->_originalViewDef [$field])) {
$def = $this->_originalViewDef [$field];
} //Otherwise make up a viewdef for it from field_defs
else {
$def = self::_trimFieldDefs($def);
}
}
$this->addField($def);
}
}
foreach ($panels as $panelID => $panel) {
// remove all (empty)s
foreach ($panel as $rowID => $row) {
$startOfRow = true;
$offset = 0;
foreach ($row as $colID => $fieldname) {
if ($fieldname == MBConstants::$EMPTY['name']) {
// if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
if ($startOfRow) {
$offset++;
}
unset($row [$colID]);
} else {
$startOfRow = false;
}
}
// reindex to remove leading (empty)s and replace fieldnames by full definition from fielddefs
$newRow = array();
foreach ($row as $colID => $fieldname) {
if ($fieldname == null) {
continue;
}
//Backwards compatibility and a safeguard against multiple calls to _convertToCanonicalForm
if (is_array($fieldname)) {
$newRow [$colID - $offset] = $fieldname;
continue;
}
if (!isset($fieldDefinitions[$fieldname]) && $fieldname != $this->FILLER['name']) {
continue;
}
//Replace (filler) with the empty string
if ($fieldname == $this->FILLER['name']) {
$newRow [$colID - $offset] = '';
} //Use the previous viewdef if this field was on it.
else {
if (isset($previousViewDef[$fieldname])) {
$newRow[$colID - $offset] = $this->getNewRowItem(
$previousViewDef[$fieldname],
$fieldDefinitions[$fieldname]
);
} //next see if the field was on the original layout.
else {
if (isset($this->_originalViewDef [$fieldname])) {
$newRow[$colID - $offset] = $this->getNewRowItem(
$this->_originalViewDef[$fieldname],
$fieldDefinitions[$fieldname]
);
} //Otherwise make up a viewdef for it from field_defs
else {
if (isset($fieldDefinitions [$fieldname])) {
$newRow [$colID - $offset] = self::_trimFieldDefs($fieldDefinitions [$fieldname]);
} //No additional info on this field can be found, jsut use the name;
else {
$newRow [$colID - $offset] = $fieldname;
}
}
}
}
}
$panels [$panelID] [$rowID] = $newRow;
}
}
return $panels;
}
/**
* fixing bug #44428: Studio | Tab Order causes layout errors
* @param string|array $source it can be a string which contain just a name of field
* or an array with field attributes including name
* @param array $fieldDefinitions stores field defs from request
* @return string|array definition of new row item
*/
public function getNewRowItem($source, $fieldDefinitions)
{
//We should copy over the tabindex if it is set.
$newRow = array();
if (isset($fieldDefinitions) && !empty($fieldDefinitions['tabindex'])) {
if (is_array($source)) {
$newRow = $source;
} else {
$newRow['name'] = $source;
}
$newRow['tabindex'] = $fieldDefinitions['tabindex'];
} else {
$newRow = $source;
}
return $newRow;
}
/**
* Convert from the standard MetaData format to our internal format
* Replace NULL with (filler) and missing entries with (empty)
* @param array $panels
* @param array $fieldDefinitions
* @return array
*/
protected function _convertFromCanonicalForm($panels, $fieldDefinitions)
{
$newPanels = array();
if (empty($panels)) {
return $newPanels;
}
// Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined,
// it goes straight into rows
// See EditView2 for similar treatment
if (!empty($panels) && count($panels) > 0) {
$keys = array_keys($panels);
if (is_numeric($keys [0])) {
$defaultPanel = $panels;
unset($panels); //blow away current value
$panels ['default'] = $defaultPanel;
}
}
// replace '' with (filler)
foreach ($panels as $panelID => $panel) {
foreach ($panel as $rowID => $row) {
$cols = 0;
foreach ($row as $colID => $col) {
if (!empty($col)) {
if (is_string($col)) {
$fieldname = $col;
} else {
if (!empty($col ['name'])) {
$fieldname = $col ['name'];
}
}
} else {
$fieldname = $this->FILLER['name'];
}
$newPanels [$panelID] [$rowID] [$cols] = $fieldname;
$cols++;
}
}
}
// replace missing fields with (empty)
foreach ($newPanels as $panelID => $panel) {
$column = 0;
foreach ($panel as $rowID => $row) {
// pad between fields on a row
foreach ($row as $colID => $col) {
for ($i = $column + 1; $i < $colID; $i++) {
$row [$i] = MBConstants::$EMPTY ['name'];
}
$column = $colID;
}
// now pad out to the end of the row
if (($column + 1) < $this->getMaxColumns()) { // last column is maxColumns-1
for ($i = $column + 1; $i < $this->getMaxColumns(); $i++) {
$row [$i] = MBConstants::$EMPTY ['name'];
}
}
ksort($row);
$newPanels [$panelID] [$rowID] = $row;
}
}
return $newPanels;
}
/**
* @param $viewDefinitions
* @return array
*/
protected function getFieldsFromLayout($viewDefinitions)
{
if (isset($viewDefinitions['panels'])) {
$panels = $viewDefinitions['panels'];
} else {
$panels = $viewDefinitions[self::$variableMap [$this->_view]]['panels'];
}
$ret = array();
if (is_array($panels)) {
foreach ($panels as $rows) {
foreach ($rows as $fields) {
if (!is_array($fields)) {
$ret[$fields] = $fields;
continue;
}
foreach ($fields as $field) {
if (is_array($field) && !empty($field['name'])) {
$ret[$field['name']] = $field;
} else {
if (!is_array($field)) {
$ret[$field] = $field;
}
}
}
}
}
}
return $ret;
}
/**
* @param array $fieldDefinitions
* @return bool
*/
protected function fieldIsRequired($fieldDefinitions)
{
if (isset($fieldDefinitions['studio'])) {
if (is_array($fieldDefinitions['studio'])) {
if (!empty($fieldDefinitions['studio'][$this->_view]) && $fieldDefinitions['studio'][$this->_view] == "required") {
return true;
} else {
if (!empty($fieldDefinitions['studio']['required']) && $fieldDefinitions['studio']['required'] == true) {
return true;
}
}
} else {
if ($fieldDefinitions['studio'] == "required") {
return true;
}
}
}
return false;
}
/**
* @param array $fieldDefinitions
* @return array
*/
public static function _trimFieldDefs($fieldDefinitions)
{
$ret = array_intersect_key(
$fieldDefinitions,
array(
'studio' => true,
'name' => true,
'label' => true,
'displayParams' => true,
'comment' => true,
'customCode' => true,
'customLabel' => true,
'tabindex' => true,
'hideLabel' => true
)
);
if (!empty($fieldDefinitions['vname']) && empty($fieldDefinitions['label'])) {
$ret['label'] = $fieldDefinitions['vname'];
}
return $ret;
}
/**
* @return bool
*/
public function getUseTabs()
{
if (isset($this->_viewdefs ['templateMeta']['useTabs'])) {
return $this->_viewdefs ['templateMeta']['useTabs'];
}
return false;
}
/**
* @param $useTabs
*/
public function setUseTabs($useTabs)
{
$this->_viewdefs ['templateMeta']['useTabs'] = $useTabs;
}
/**
* Return whether the Detail & EditView should be in sync.
* @return bool
*/
public function getSyncDetailEditViews()
{
if (isset($this->_viewdefs ['templateMeta']['syncDetailEditViews'])) {
return $this->_viewdefs ['templateMeta']['syncDetailEditViews'];
}
return false;
}
/**
* Sync DetailView & EditView. This should only be set on the EditView
* @param bool $syncDetailEditViews
*/
public function setSyncDetailEditViews($syncDetailEditViews)
{
$this->_viewdefs ['templateMeta']['syncDetailEditViews'] = $syncDetailEditViews;
}
/**
* Getter function to get the implementation method which is a private variable
* @return DeployedMetaDataImplementation
*/
public function getImplementation()
{
return $this->implementation;
}
/**
* Public access to _convertFromCanonicalForm
* @param array $panels
* @param array $fieldDefinitions
* @return array
*/
public function convertFromCanonicalForm($panels, $fieldDefinitions)
{
return $this->_convertFromCanonicalForm($panels, $fieldDefinitions);
}
/**
* Public access to _convertToCanonicalForm
* @param $panels
* @param $fieldDefinitions
* @return array
*/
public function convertToCanonicalForm($panels, $fieldDefinitions)
{
return $this->_convertToCanonicalForm($panels, $fieldDefinitions);
}
/**
* @return array list of fields in this module that have the calculated property
*/
public function getCalculatedFields()
{
$ret = array();
foreach ($this->_fielddefs as $field => $def) {
if (!empty($def['calculated']) && !empty($def['formula'])) {
$ret[] = $field;
}
}
return $ret;
}
/**
* @param string $targetPanel
* @return array|RecursiveIteratorIterator array fields in the given panel
*/
public function getFieldsInPanel($targetPanel)
{
return iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->_viewdefs['panels'][$targetPanel])));
}
}