/** * Returns a piece of html code that can be used in a form to search. * * @param array $record Array with values * @param bool $extended if set to false, a simple search input is * returned for use in the searchbar of the * recordlist. If set to true, a more extended * search may be returned for the 'extended' * search page. The Attribute does not * make a difference for $extended is true, but * derived attributes may reimplement this. * @param string $fieldprefix The fieldprefix of this attribute's HTML element. * @param DataGrid $grid * * @return string Piece of html code */ public function search($record, $extended = false, $fieldprefix = '', DataGrid $grid = null) { $this->createDestination(); $id = $this->getHtmlId($fieldprefix); $name = $this->getSearchFieldName($fieldprefix); $selectOptions = []; $selectOptions['enable-select2'] = true; $selectOptions['dropdown-auto-width'] = true; $selectOptions['minimum-results-for-search'] = 10; if ($extended) { $selectOptions['placeholder'] = Tools::atktext('search_all'); } //width always auto $selectOptions['width'] = 'auto'; $selectOptions = array_merge($selectOptions, $this->m_select2Options['search']); $data = ''; foreach ($selectOptions as $k => $v) { $data .= ' data-' . $k . '="' . htmlspecialchars($v) . '"'; } // now select all records $recordset = $this->m_destInstance->select()->includes(Tools::atk_array_merge($this->m_destInstance->descriptorFields(), $this->m_destInstance->m_primaryKey))->getAllRows(); $result = '<select class="form-control"' . $data; if ($extended) { $result .= 'multiple="multiple" size="' . min(5, count($recordset) + 1) . '"'; } $result .= 'id="' . $id . '" name="' . $name . '[]">'; $pkfield = $this->m_destInstance->primaryKeyField(); if (!$extended) { $result .= '<option value="">' . Tools::atktext('search_all', 'atk') . '</option>'; } for ($i = 0; $i < count($recordset); ++$i) { $pk = $recordset[$i][$pkfield]; if (!empty($record[$this->fieldName()]) && Tools::atk_in_array($pk, $record[$this->fieldName()])) { $sel = ' selected="selected"'; } else { $sel = ''; } $result .= '<option value="' . $pk . '"' . $sel . '>' . $this->m_destInstance->descriptor($recordset[$i]) . '</option>'; } $result .= '</select>'; $result .= "<script>ATK.enableSelect2ForSelect('#{$id}');</script>"; return $result; }
/** * Validate a record. * * @param string $mode Override the mode * @param array $ignoreList Override the ignoreList */ public function validate($mode = '', $ignoreList = array()) { // check overrides if (count($ignoreList)) { $this->setIgnoreList($ignoreList); } if ($mode != '') { $this->setMode($mode); } Tools::atkdebug('validate() with mode ' . $this->m_mode . ' for node ' . $this->m_nodeObj->atkNodeUri()); // set the record $record =& $this->m_record; // Check flags and values $db = $this->m_nodeObj->getDb(); foreach ($this->m_nodeObj->m_attribIndexList as $attribdata) { $attribname = $attribdata['name']; if (!Tools::atk_in_array($attribname, $this->m_ignoreList)) { $p_attrib = $this->m_nodeObj->m_attribList[$attribname]; $this->validateAttributeValue($p_attrib, $record); if ($p_attrib->hasFlag(Attribute::AF_PRIMARY) && !$p_attrib->hasFlag(Attribute::AF_AUTO_INCREMENT)) { $atkorgkey = $record['atkprimkey']; if ($atkorgkey == '' || $atkorgkey != $this->m_nodeObj->primaryKey($record)) { $cnt = $this->m_nodeObj->select($this->m_nodeObj->primaryKey($record))->ignoreDefaultFilters(true)->ignorePostvars(true)->getRowCount(); if ($cnt > 0) { Tools::triggerError($record, $p_attrib, 'error_primarykey_exists'); } } } // if no root elements may be added to the tree, then every record needs to have a parent! if ($p_attrib->hasFlag(Attribute::AF_PARENT) && $this->m_nodeObj->hasFlag(TreeNode::NF_TREE_NO_ROOT_ADD) && $this->m_nodeObj->m_action == 'save') { $p_attrib->m_flags |= Attribute::AF_OBLIGATORY; } // validate obligatory fields (but not the auto_increment ones, because they don't have a value yet) if ($p_attrib->hasFlag(Attribute::AF_OBLIGATORY) && !$p_attrib->hasFlag(Attribute::AF_AUTO_INCREMENT) && $p_attrib->isEmpty($record)) { Tools::atkTriggerError($record, $p_attrib, 'error_obligatoryfield'); } else { if ($p_attrib->hasFlag(Attribute::AF_UNIQUE) && !$p_attrib->hasFlag(Attribute::AF_PRIMARY) && !$p_attrib->isEmpty($record)) { $condition = $this->m_nodeObj->getTable() . ".{$attribname}='" . $db->escapeSQL($p_attrib->value2db($record)) . "'"; if ($this->m_mode != 'add') { $condition .= ' AND NOT (' . $this->m_nodeObj->primaryKey($record) . ')'; } $cnt = $this->m_nodeObj->select($condition)->ignoreDefaultFilters(true)->ignorePostvars(true)->getRowCount(); if ($cnt > 0) { Tools::atkTriggerError($record, $p_attrib, 'error_uniquefield'); } } } } } if (isset($record['atkerror']) && count($record['atkerror']) > 0) { for ($i = 0, $_i = count($record['atkerror']); $i < $_i; ++$i) { $record['atkerror'][$i]['node'] = $this->m_nodeObj->m_type; } } $this->validateUniqueFieldSets($record); if (isset($record['atkerror'])) { for ($i = 0, $_i = count($record['atkerror']); $i < $_i; ++$i) { $record['atkerror'][$i]['node'] = $this->m_nodeObj->m_type; } return false; } return true; }
/** * Returns a piece of html code that can be used in a form to edit this * attribute's value. * * @param array $record Array with fields * @param string $fieldprefix The fieldprefix to put in front of the name * of any html form element for this attribute. * @param string $mode The mode we're in ('add' or 'edit') * * @return string piece of html code with radioboxes */ public function edit($record, $fieldprefix, $mode) { $id = $this->getHtmlId($fieldprefix); $name = $this->getHtmlName($fieldprefix); $page = Page::getInstance(); $page->register_script(Config::getGlobal('assets_url') . 'javascript/class.atkprofileattribute.js'); $result = ''; if (!$this->hasFlag(self::AF_LINKS_BOTTOM)) { $result .= $this->_addLinks($fieldprefix); } $css = $this->getCSSClassAttribute(''); $result .= '<div ' . $css . '>'; $values = $this->getValues(); if (!is_array($record[$this->fieldName()])) { $recordvalue = $this->db2value($record); } else { $recordvalue = $record[$this->fieldName()]; } for ($i = 0; $i < count($values); ++$i) { $checkId = $id . '_' . $i; $checkName = $name . '[]'; if (!$this->hasFlag(self::AF_CHECK_ALL)) { $sel = Tools::atk_in_array($values[$i], $recordvalue) ? 'checked' : ''; } else { $sel = 'checked'; } $result .= '<div>'; $result .= '<input type="checkbox" id="' . $checkId . '" name="' . $checkName . '" value="' . $values[$i] . '" ' . $sel . '> '; $result .= $this->_translateValue($values[$i], $record); $result .= '</div>'; } $result .= '</div>'; if ($this->hasFlag(self::AF_LINKS_BOTTOM)) { $result .= $this->_addLinks($fieldprefix); } return $result; }
/** * Returns a piece of html code that can be used to get search terms input * from the user. * * The framework calls this method to display the searchbox * in the search bar of the recordlist, and to display a more extensive * search in the 'extended' search screen. * * @todo Configurable rows * * @param array $record Array with values * @param bool $extended if set to false, a simple search input is * returned for use in the searchbar of the * recordlist. If set to true, a more extended * search may be returned for the 'extended' * search page. The Attribute does not * make a difference for $extended is true, but * derived attributes may reimplement this. * @param string $fieldprefix The fieldprefix of this attribute's HTML element. * @param DataGrid $grid * * @return string A piece of html-code with a checkbox */ public function search($record, $extended = false, $fieldprefix = '', DataGrid $grid = null) { $values = $this->getValues(); $id = $this->getHtmlId($fieldprefix); $name = $this->getSearchFieldName($fieldprefix); $isMultiple = $this->isMultipleSearch($extended); $class = $this->getCSSClassAttribute(['form-control']); $selectOptions = []; $selectOptions['enable-select2'] = true; $selectOptions['dropdown-auto-width'] = true; $selectOptions['minimum-results-for-search'] = 10; if ($isMultiple) { $selectOptions['placeholder'] = Tools::atktext('search_all'); } //width always auto $selectOptions['width'] = 'auto'; $selectOptions = array_merge($selectOptions, $this->m_select2Options['search']); $data = ''; foreach ($selectOptions as $k => $v) { $data .= ' data-' . $k . '="' . htmlspecialchars($v) . '"'; } $result = '<select ' . ($isMultiple ? 'multiple' : '') . ' ' . $class . ' id="' . $id . '" name="' . $name . '[]"' . $data . '>'; $selValues = isset($record[$this->fieldName()]) ? $record[$this->fieldName()] : null; if (!is_array($selValues)) { $selValues = [$selValues]; } if (in_array('', $selValues)) { $selValues = ['']; } $selected = !$isMultiple && $selValues[0] == '' ? ' selected' : ''; $option = Tools::atktext('search_all'); $result .= sprintf('<option value=""%s>%s</option>', $selected, $option); // "none" option if (!$this->hasFlag(self::AF_OBLIGATORY) && !$this->hasFlag(self::AF_LIST_NO_NULL_ITEM)) { $selected = Tools::atk_in_array('__NONE__', $selValues) ? ' selected' : ''; $option = Tools::atktext('search_none'); $result .= sprintf('<option value="__NONE__"%s>%s</option>', $selected, $option); } // normal options foreach ($values as $value) { $selected = Tools::atk_in_array((string) $value, $selValues, true) ? ' selected' : ''; $option = $this->_translateValue($value, $record); $result .= sprintf('<option value="%s"%s>%s</option>', $value, $selected, $option); } $result .= '</select>'; $result .= "<script>ATK.enableSelect2ForSelect('#{$id}');</script>"; // if we use autosearch, register an onchange event that submits the grid if (!is_null($grid) && !$extended && $this->m_autoSearch) { $onchange = $grid->getUpdateCall(array('atkstartat' => 0), [], 'ATK.DataGrid.extractSearchOverrides'); $this->getOwnerInstance()->getPage()->register_loadscript('jQuery("#' . $id . '").on("change", function(){' . $onchange . '})'); } return $result; }
/** * Save a new record to the database. * * The record is passed by reference, because any autoincrement field gets * its value when stored to the database. The record is updated, so after * the call to addDb you can use access the primary key fields. * * NOTE: Does not commit your transaction! If you are using a database that uses * transactions you will need to call 'Db::getInstance()>commit()' manually. * * @param array $record The record to save. * @param bool $exectrigger Indicates whether the postAdd trigger * should be fired. * @param string $mode The mode we're in * @param array $excludelist List of attributenames that should be ignored * and not stored in the database. * * @return bool True if succesful, false if not. */ public function addDb(&$record, $exectrigger = true, $mode = 'add', $excludelist = array()) { if ($exectrigger) { if (!$this->executeTrigger('preAdd', $record, $mode)) { return Tools::atkerror('preAdd() failed!'); } } $db = $this->getDb(); $query = $db->createQuery(); $storelist = array('pre' => [], 'post' => [], 'query' => array()); $query->addTable($this->m_table); foreach (array_keys($this->m_attribList) as $attribname) { $p_attrib = $this->m_attribList[$attribname]; if (!Tools::atk_in_array($attribname, $excludelist) && ($mode != 'add' || $p_attrib->needsInsert($record))) { $storemode = $p_attrib->storageType($mode); if (Tools::hasFlag($storemode, Attribute::PRESTORE)) { $storelist['pre'][] = $attribname; } if (Tools::hasFlag($storemode, Attribute::POSTSTORE)) { $storelist['post'][] = $attribname; } if (Tools::hasFlag($storemode, Attribute::ADDTOQUERY)) { $storelist['query'][] = $attribname; } } } if (!$this->_storeAttributes($storelist['pre'], $record, $mode)) { return false; } for ($i = 0, $_i = count($storelist['query']); $i < $_i; ++$i) { $p_attrib = $this->m_attribList[$storelist['query'][$i]]; $p_attrib->addToQuery($query, $this->m_table, '', $record, 1, 'add'); // start at level 1 } if (!$query->executeInsert()) { Tools::atkdebug('executeInsert failed..'); return false; } // new primary key $record['atkprimkey'] = $this->primaryKey($record); if (!$this->_storeAttributes($storelist['post'], $record, $mode)) { Tools::atkdebug('_storeAttributes failed..'); return false; } // Now we call a postAdd function, that can be used to do some processing after the record // has been saved. if ($exectrigger && !$this->executeTrigger('postAdd', $record, $mode)) { return false; } return true; }
/** * Notify the listener of any action about to be performed on a record. * * This method is called by the framework for each action called on a * node. Depending on the actionfilter passed in the constructor, the * call is forwarded to the preActionPerformed($action, $record) method. * * @param string $action The action about to be performed * @param array $record The record on which the action is about to be performed */ public function preNotify($action, &$record) { if (count($this->m_actionfilter) == 0 || Tools::atk_in_array($action, $this->m_actionfilter)) { Tools::atkdebug("Action {$action} to be performed on " . $this->m_node->atkNodeUri() . ' (' . $this->m_node->primaryKey($record) . ')'); $this->preActionPerformed($action, $record); } }
/** * Returns a piece of html code that can be used in a form to edit this * attribute's value. * * @param array $record The record that holds the value for this attribute. * @param string $fieldprefix The fieldprefix to put in front of the name * of any html form element for this attribute. * @param string $mode The mode we're in ('add' or 'edit') * * @return string A piece of htmlcode for editing this attribute */ public function edit($record, $fieldprefix, $mode) { $user = SecurityManager::atkGetUser(); $page = Page::getInstance(); $icons = "var ATK_PROFILE_ICON_OPEN = '" . Config::getGlobal('icon_plussquare') . "';"; $icons .= "var ATK_PROFILE_ICON_CLOSE = '" . Config::getGlobal('icon_minussquare') . "';"; $page->register_scriptcode($icons); $page->register_script(Config::getGlobal('assets_url') . 'javascript/class.atkprofileattribute.js'); $this->_restoreDivStates($page); $result = '<div align="right"> [<a href="javascript:void(0)" onclick="profile_checkAll(\'' . $this->fieldName() . '\'); return false;">' . Tools::atktext('check_all') . '</a> | <a href="javascript:void(0)" onclick="profile_checkNone(\'' . $this->fieldName() . '\'); return false;">' . Tools::atktext('check_none') . '</a> | <a href="javascript:void(0)" onclick="profile_checkInvert(\'' . $this->fieldName() . '\'); return false;">' . Tools::atktext('invert_selection') . '</a>]</div>'; $isAdmin = $user['name'] == 'administrator' || $this->canGrantAll(); $allActions = $this->getAllActions($record, true); $editableActions = $this->getEditableActions($record); $selectedActions = $this->getSelectedActions($record); foreach ($allActions as $section => $modules) { $result .= '<div class="profileSection">'; $result .= "<span onclick=\"profile_swapProfileDiv('div_{$section}');\" style=\"cursor: pointer; font-size: 110%; font-weight: bold\">"; $result .= ' <i class="' . Config::getGlobal('icon_plussquare') . "\" id=\"img_div_{$section}\"></i> " . Tools::atktext(array("title_{$section}", $section), $section); $result .= '</span><br/>'; $result .= "<div id='div_{$section}' name='div_{$section}' style='display: none; padding-left: 15px' class='checkbox'>"; $result .= " <input type='hidden' name=\"divstate['div_{$section}']\" id=\"divstate['div_{$section}']\" value='closed' />"; $result .= ' <div style="font-size: 80%; margin-top: 4px; margin-bottom: 4px" > [<a style="font-size: 100%" href="javascript:void(0)" onclick="profile_checkAllByValue(\'' . $this->fieldName() . '\',\'' . $section . '.\'); return false;">' . Tools::atktext('check_all', 'atk') . '</a> | <a style="font-size: 100%" href="javascript:void(0)" onclick="profile_checkNoneByValue(\'' . $this->fieldName() . '\',\'' . $section . '.\'); return false;">' . Tools::atktext('check_none', 'atk') . '</a> | <a style="font-size: 100%" href="javascript:void(0)" onclick="profile_checkInvertByValue(\'' . $this->fieldName() . '\',\'' . $section . '.\'); return false;">' . Tools::atktext('invert_selection', 'atk') . '</a>]'; $result .= ' </div>'; $result .= ' <br>'; foreach ($modules as $module => $nodes) { foreach ($nodes as $node => $actions) { $showBox = $isAdmin || count(array_intersect($actions, is_array($editableActions[$module][$node]) ? $editableActions[$module][$node] : array())) > 0; if ($showBox) { $result .= '<b>' . Tools::atktext($node, $module) . '</b><br>'; } $tabs_str = ''; $display_tabs_str = false; // Draw action checkboxes foreach ($actions as $action) { $temp_str = ''; $isEditable = $isAdmin || Tools::atk_in_array($action, $editableActions[$module][$node]); $isSelected = isset($selectedActions[$module][$node]) && in_array($action, $selectedActions[$module][$node]); if ($isEditable) { if (substr($action, 0, 4) == 'tab_') { $display_tabs_str = true; } $temp_str .= '<label>'; $temp_str .= '<input type="checkbox" name="' . $this->fieldName() . '[]" ' . $this->getCSSClassAttribute('atkcheckbox') . ' value="' . $section . '.' . $module . '.' . $node . '.' . $action . '" '; $temp_str .= ($isSelected ? ' checked="checked"' : '') . '>'; $temp_str .= ' ' . $this->permissionName($action, $node, $module); $temp_str .= '</label>'; } if (substr($action, 0, 4) == 'tab_') { $tabs_str .= $temp_str; } else { $result .= $temp_str; } } if ($display_tabs_str) { $result .= '<br>Tabs: '; } $result .= $tabs_str; if ($showBox) { $result .= "<br /><br />\n"; } } } $result .= ' </div>'; // end div_$section $result .= '</div>'; // end profileSection } return $result; }
/** * Check if the attribute wants to be shown on a certain tab. * * @param string $tab The name of the tab to check. * * @return bool */ public function showOnTab($tab) { return $this->getTabs() == '*' || Tools::atk_in_array($tab, $this->getTabs()); }
/** * Returns a piece of html code that can be used in a form to edit this * attribute's value. * * @param array $record Array with fields * @param string $fieldprefix The fieldprefix to put in front of the name * of any html form element for this attribute. * @param string $mode The mode we're in ('add' or 'edit') * * @return string piece of html code with radioboxes */ public function edit($record, $fieldprefix, $mode) { $id = $this->getHtmlId($fieldprefix); $name = $this->getHtmlName($fieldprefix); $selectOptions = []; $selectOptions['enable-select2'] = true; $selectOptions['dropdown-auto-width'] = true; $selectOptions['minimum-results-for-search'] = 10; $selectOptions['tags'] = true; if (!empty($this->getWidth())) { $selectOptions['width'] = $this->getWidth(); } else { $selectOptions['width'] = 'auto'; } $selectOptions['placeholder'] = $this->getNullLabel(); $selectOptions = array_merge($selectOptions, $this->m_select2Options['edit']); $data = ''; foreach ($selectOptions as $k => $v) { $data .= ' data-' . $k . '="' . htmlspecialchars($v) . '"'; } $onchange = ''; if (count($this->m_onchangecode)) { $onchange = ' onChange="' . $this->getHtmlId($fieldprefix) . '_onChange(this)"'; $this->_renderChangeHandler($fieldprefix); } $result = '<select multiple id="' . $id . '" name="' . $name . '[]" ' . $this->getCSSClassAttribute('form-control') . '" ' . $onchange . $data . '>'; $values = $this->getValues(); if (!is_array($record[$this->fieldName()])) { $recordvalue = $this->db2value($record); } else { $recordvalue = $record[$this->fieldName()]; } for ($i = 0; $i < count($values); ++$i) { // If the current value is selected or occurs in the record $sel = Tools::atk_in_array($values[$i], $recordvalue) ? 'selected' : ''; $result .= '<option value="' . $values[$i] . '" ' . $sel . '>' . $this->_translateValue($values[$i], $record); } $result .= '</select>'; $result .= "<script>ATK.enableSelect2ForSelect('#{$id}');</script>"; return $result; }