/vendor/Adapto/src/Adapto/Entity.php
PHP | 4713 lines | 2431 code | 494 blank | 1788 comment | 475 complexity | 7994ef1883eb7656025eced0247d6d35 MD5 | raw file
- <?php
- /**
- * This file is part of the Adapto Toolkit.
- * Detailed copyright and licensing information can be found
- * in the doc/COPYRIGHT and doc/LICENSE files which should be
- * included in the distribution.
- *
- * @package adapto
- *
- * @copyright (c)2000-2007 Ivo Jansch
- * @copyright (c)2000-2008 Ibuildings.nl BV
- * @license http://www.achievo.org/atk/licensing ATK Open Source License
- *
- */
- /**
- * some includes
- */
- /**
- * Define some flags for entitys. Use the constructor of the Adapto_Entity
- * class to set the flags. (concatenate multiple flags with '|')
- */
- /**
- * No new records may be added
- */
- define("EF_NO_ADD", 1);
- /**
- * Records may not be edited
- */
- define("EF_NO_EDIT", 2);
- /**
- * Records may not be deleted
- */
- define("EF_NO_DELETE", 4);
- /**
- * Immediately after you add a new record,
- * you get the editpage for that record
- */
- define("EF_EDITAFTERADD", 8);
- /**
- * Records may not be searched
- */
- define("EF_NO_SEARCH", 16);
- /**
- * Ignore addFilter filters
- */
- define("EF_NO_FILTER", 32);
- /**
- * Doesn't show an add form on the admin page
- * but a link to the form
- */
- define("EF_ADD_LINK", 64);
- /**
- * Records may not be viewed
- */
- define("EF_NO_VIEW", 128);
- /**
- * Records / trees may be copied
- */
- define("EF_COPY", 256);
- /**
- * If this flag is set and only one record is
- * present on a selectpage, atk automagically
- * selects it and moves on to the target
- */
- define("EF_AUTOSELECT", 512);
- /**
- * If set, atk stores the old values of
- * a record as ["atkorgrec"] in the $rec that
- * gets passed to the postUpdate
- */
- define("EF_TRACK_CHANGES", 1024);
- /**
- * Quick way to disable accessright checking
- * for an entire entity. (Everybody may access this entity)
- */
- define("EF_NO_SECURITY", 2048);
- /**
- * Extended search feature is turned off
- */
- define("EF_NO_EXTENDED_SEARCH", 4096);
- /**
- * Multi-selection of records is turned on
- */
- define("EF_MULTI_RECORD_ACTIONS", 8192);
- /**
- * Multi-priority-selection of records is turned on
- */
- define("EF_MRPA", 16384);
- /**
- * Add locking support to entity, if one user is editing a record,
- * no one else may edit it.
- */
- define("EF_LOCK", 32768);
- /**
- * Multi-language support
- */
- define("EF_ML", 65536);
- /**
- * Quick way to ensable the csv import feature
- */
- define("EF_IMPORT", 131072);
- /**
- * Add CSV export ability to the entity.
- */
- define("EF_EXPORT", 262144);
- /**
- * Enable extended sorting (multicolumn sort)
- */
- define("EF_EXT_SORT", 524288);
- /**
- * Makes an entity cache it's recordlist
- */
- define("EF_CACHE_RECORDLIST", 1048576);
- /**
- * After adding a new record add another one instantaniously.
- */
- define("EF_ADDAFTERADD", 2097152);
- /**
- * No sorting possible.
- */
- define("EF_NO_SORT", 4194304);
- /**
- * Use the dialog popup box when adding a new record for this entity.
- */
- define("EF_ADD_DIALOG", 8388608);
- /**
- * Use the dialog add-or-copy popup box when adding a new record for this entity.
- */
- define("EF_ADDORCOPY_DIALOG", 16777216);
- /**
- * Specific entity flag 1
- */
- define("EF_SPECIFIC_1", 33554432);
- /**
- * Specific entity flag 2
- */
- define("EF_SPECIFIC_2", 67108864);
- /**
- * Specific entity flag 3
- */
- define("EF_SPECIFIC_3", 134217728);
- /**
- * Specific entity flag 4
- */
- define("EF_SPECIFIC_4", 268435456);
- /**
- * Specific entity flag 5
- */
- define("EF_SPECIFIC_5", 536870912);
- /**
- * Records may be copied and open for editing
- */
- define("EF_EDITAFTERCOPY", 1073741824);
- /**
- * Alias for EF_MULTI_RECORD_ACTIONS flag (shortcut)
- */
- define("EF_MRA", EF_MULTI_RECORD_ACTIONS);
- /**
- * Alias for EF_ML flag (typed out)
- */
- define("EF_MULTILANGUAGE", EF_ML);
- /**
- * Aggregate flag to quickly create readonly entitys
- */
- define("EF_READONLY", EF_NO_ADD | EF_NO_DELETE | EF_NO_EDIT);
- /**
- * action status flags
- * Note that these have binary numbers, even though an action could never have
- * two statusses at the same time.
- * This is done however, so the flags can be used as a mask in the setFeedback
- * function.
- */
- /**
- * The action is cancelled
- *
- * action status flag
- */
- define("ACTION_CANCELLED", 1);
- /**
- * The action failed to accomplish it's goal
- *
- * action status flag
- */
- define("ACTION_FAILED", 2);
- /**
- * The action is a success
- *
- * action status flag
- */
- define("ACTION_SUCCESS", 4);
- /**
- * Trigger flags
- */
- define("TRIGGER_NONE", 0);
- define("TRIGGER_AUTO", 1);
- define("TRIGGER_PRE", 2);
- define("TRIGGER_POST", 4);
- define("TRIGGER_ALL", TRIGGER_PRE | TRIGGER_POST);
- /**
- * Multi-record-actions selection modes. These
- * modes are mutually exclusive.
- */
- /**
- * Multiple selections possible.
- */
- define("MRA_MULTI_SELECT", 1);
- /**
- * Only one selection possible.
- */
- define("MRA_SINGLE_SELECT", 2);
- /**
- * No selection possible (e.g. action is always for all (visible) records!).
- */
- define("MRA_NO_SELECT", 3);
- /**
- * The Adapto_Entity class represents a piece of information that is part of an
- * application. This class provides standard functionality for adding,
- * editing and deleting entitys.
- * This class must be seen as an abstract base class: For every piece of
- * information in an application, a class must be derived from this class
- * with specific implementations for that type of entity.
- *
- * @author ijansch
- * @package adapto
- */
- class Adapto_Entity
- {
- /**
- * reference to the class which is used to validate atkentitys
- * the validator is overridable by changing this variabele
- *
- * @var String
- */
- private $m_validate_class = "Adapto_EntityValidator";
- /**
- * Unique field sets of a certain entity.
- *
- * Indicates which field combinations should be unique.
- * It doesn't contain the unique fields which have been set by flag
- * AF_UNIQUE.
- *
- * @var array
- */
- private $m_uniqueFieldSets = array();
- /**
- * Entitys must be initialised using the init() function before they can be
- * used. This member indicated whether the entity has been initialised.
- * @var boolean
- */
- private $m_initialised = false;
- /**
- * Check to prevent double execution of setAttribSizes on pages with more
- * than one form.
- * @var boolean
- */
- private $m_attribsizesset = false;
- /**
- * The list of attributes of an entity. These should be of the class
- * atkAttribute or one of its derivatives.
- * @var array
- */
- private $m_attribList = array();
- /**
- * Index list containing the attributes in the order in which they will
- * appear on screen.
- * @var array
- */
- private $m_attribIndexList = array();
- /**
- * Reference to the page on which the entity is rendering its output.
- * @var atkPage
- */
- private $m_page = NULL;
- /**
- * List of available tabs. Associative array structured like this:
- * array($action=>$arrayOfTabnames)
- * @var array
- */
- private $m_tabList = array();
- /**
- * List of available sections. Associative array structured like this:
- * array($action=>$arrayOfSectionnames)
- * @var array
- */
- private $m_sectionList = array();
- /**
- * Keep track of tabs per attribute.
- * @var array
- */
- private $m_attributeTabs = array();
- /**
- * Keep track if a tab contains attribs (checkEmptyTabs function)
- * @var array
- */
- private $m_filledTabs = array();
- /**
- * The entitytype.
- * @var String
- */
- protected $m_type;
- /**
- * The module of the entity.
- * @var String
- */
- protected $m_module;
- /**
- * The database that the entity is using for storing and loading its data.
- * @var mixed
- */
- protected $m_db = NULL;
- /**
- * The table to use for data storage.
- * @var String
- */
- protected $m_table;
- /**
- * The name of the sequence used for autoincrement fields.
- * @var String
- */
- protected $m_seq;
- /**
- * Name of the attribute that contains the language of a record.
- * Used with ATK's data internationalization feature.
- * @var String
- */
- private $m_lngfield;
- /**
- * List of names of the attributes that form this entity's primary key.
- * @var array
- */
- protected $m_primaryKey = array();
- /**
- * The postvars (or getvars) that are passed to a page will be passed
- * to the class using the dispatch function. We store them in a member
- * variable for easy access.
- * @var array
- */
- protected $m_postvars = array();
- /**
- * The action that the entity is currently performing.
- * @var String
- */
- protected $m_action;
- /**
- * Contains the definition of what needs to rendered partially.
- * If set to NULL not in partial rendering mode.
- */
- public $m_partial = NULL; // defaulted to public
- /**
- * The active action handler.
- * @var atkActionHandler
- */
- protected $m_handler = NULL;
- /**
- * Default order by statement.
- * @var String
- */
- protected $m_default_order = "";
- /**
- * var used for tracking relation within this entity.
- * @todo Remove this member, it's using memory while it's used only in
- * the case of multilanguage entity, and even then only on one
- * occasion.
- * @var array
- */
- private $m_relations = array();
- /**
- * Bitwise mask of entity flags (EF_* flags).
- * @var int
- */
- public $m_flags; // defaulted to public
- /*
- * Name of the field that is used for creating an alphabetical index in
- * admin/select pages.
- * @var String
- */
- private $m_index = "";
- /**
- * Default tab being displayed in add/edit mode.
- * @var String
- */
- private $m_default_tab = "default";
- /**
- * Default sections that are expanded.
- * @var String
- */
- private $m_default_expanded_sections = array();
- /**
- * Record filters, in attributename/required value pairs.
- * @var array
- */
- private $m_filters = array();
- /**
- * Record filters, as a list of sql statements.
- * @var array
- */
- private $m_fuzzyFilters = array();
- /**
- * For speed, we keep track of a list of attributes that we don't have to
- * load in recordlists.
- * @var array
- */
- protected $m_listExcludes = array();
- /**
- * For speed, we keep track of a list of attributes that we don't have to
- * load when in view pages.
- * @todo This can probably be moved to the view handler.
- * @var array
- */
- protected $m_viewExcludes = array();
- /**
- * For speed, we keep track of a list of attributes that have the cascade
- * delete flag set.
- * @todo This should be moved to the delete handler, or should not be
- * cached at all. (caching this on each load is slower than just
- * retrieving the list when it's needed)
- * @var array
- */
- private $m_cascadingAttribs = array();
- /**
- * Actions are mapped to security units.
- *
- * For example, both actions "save" and "add" require access "add". If an
- * item is not in this list, it's treated 'as-is'. Derived entitys may add
- * more mappings to tell the systems that some custom actions require the
- * same privilege as others.
- * Structure: array($action=>$requiredPrivilege)
- * @var array
- */
- protected $m_securityMap = array("save" => "add", "update" => "edit", "multiupdate" => "edit", "copy" => "add", "import" => "add", "editcopy" => "add",
- "search" => "admin", "smartsearch" => "admin");
- /**
- * The right to execute certain actions can be implied by the fact that you
- * have some other right. For example, if you have the right to access a
- * feature (admin right), you may also view that record, and don't need
- * explicit rights to view it. So the 'view' right is said to be 'implied'
- * by the 'admin' right.
- * This is a subtle difference with m_securityMap.
- * @var array
- */
- protected $m_securityImplied = array("view" => "admin");
- /**
- * Name of the entity that is used for privilege checking.
- *
- * If a class is named 'project', then by default, if the system needs to
- * know whether a user may edit a record, the securitymanager searches
- * for 'edit' access on 'project'. However, if an alias is set here, the
- * securitymanger searches for 'edit' on that alias.
- * @var String
- */
- private $m_securityAlias = "";
- /*
- * Entitys can specify actions that require no access level
- * Note: for the moment, the "select" action is always allowed.
- * @todo This may not be correct. We have to find a way to bind the
- * select action to the action that follows after the select.
- * @var array
- */
- private $m_unsecuredActions = array("select", "multiselect", "feedback");
- /**
- * Auto search-actions; action that will be performed if only one record
- * is found.
- * @var array
- */
- private $m_search_action;
- /**
- * Priority actions
- * @todo This, and the priority_min/max members, should be moved
- * to the recordlist
- * @var array
- */
- private $m_priority_actions = array();
- /**
- * Minimum for the mra priority select
- * @var int
- */
- private $m_priority_min = 1;
- /**
- * Maximum for the mra priority select
- * @var int
- */
- private $m_priority_max = 0;
- /**
- * The lock instance
- * @var atkLock
- */
- protected $m_lock = NULL;
- /**
- * List of actions that should give success/failure feedback
- * @var array
- */
- private $m_feedback = array();
- /**
- * Default language used by Multilanguage Entitys.
- * @var String
- */
- protected $m_defaultlanguage = "";
- /**
- * Number to use with numbering
- * @var mixed
- */
- protected $m_numbering = null;
- /**
- * Descriptor template.
- * @var String
- */
- protected $m_descTemplate = NULL;
- /**
- * Descriptor handler.
- * @var Object
- */
- protected $m_descHandler = NULL;
- /**
- * List of action listeners
- * @var Array
- */
- protected $m_actionListeners = array();
- /**
- * List of trigger listeners
- * @var Array
- */
- protected $m_triggerListeners = array();
- /**
- * List of callback functions to manipulate the record actions
- *
- * @var array
- */
- protected $m_recordActionsCallbacks = array();
- /**
- * List of callback functions to add css class to row.
- * See details in atkDGList::getRecordlistData() method
- *
- * @var array
- */
- protected $m_rowClassCallback = array();
- /**
- * Tracker variable to see if we are currently in 'modifier mode' (running inside
- * the scope of a modname_entityname_modifier() method). The variable contains the
- * name of the modifying module.
- * @var String
- */
- private $m_modifier = "";
- /**
- * Extended search action. The action which is called if the user
- * wants to perform an extended search.
- *
- * @var String
- */
- private $m_extended_search_action = NULL;
- /**
- * List of editable list attributes.
- * @var Array
- */
- private $m_editableListAttributes = array();
- /**
- * Multi-record actions, selection mode.
- * @var int
- */
- private $m_mraSelectionMode = MRA_MULTI_SELECT;
- /**
- * The default edit fieldprefix to use for atk
- * @var String
- */
- private $m_edit_fieldprefix = '';
- /**
- * Lock mode.
- *
- * @var int
- */
- private $m_lockMode = 'exclusive'; // atkLock::EXCLUSIVE (would mean atkLock needs to be available!)
- /**
- * Default column name (null means across all columns)
- *
- * @var string
- */
- private $m_defaultColumn = null;
- /**
- * Current maximum attribute order value.
- *
- * @var int
- */
- private $m_attribOrder = 0;
- /**
- * Constructor.
- *
- * This initialises the entity. Derived classes should always call their
- * parent constructor ($this->Adapto_Entity($name, $flags), to initialize the
- * base class.
- * <br>
- * <b>Example:</b>
- * <code>$this->Adapto_Entity('test',EF_NO_EDIT);</code>
- * @param String $type The entitytype (by default equal to the classname)
- * @param int $flags Bitmask of entity flags (EF_*).
- */
- public function __construct($type = "", $flags = 0)
- {
- if ($type == "")
- $type = strtolower(get_class($this));
- Adapto_Util_Debugger::debug("Creating a new Adapto_Entity for $type");
- $this->m_type = $type;
- $this->m_flags = $flags;
- $this->m_module = Adapto_Module::getModuleScope();
- $this->setEditFieldPrefix(Adapto_Config::getGlobal('edit_fieldprefix', ''));
- }
- /**
- * Resolve section. If a section is only prefixed by
- * a dot this means we need to add the default tab
- * before the dot.
- *
- * @param string $section section name
- * @return resolved section name
- */
- function resolveSection($section)
- {
- list($part1, $part2) = (strpos($section, ".") !== false) ? explode('.', $section) : array($section, "");
- if ($part2 != NULL && strlen($part2) > 0 && strlen($part1) == 0)
- return $this->m_default_tab . "." . $part2;
- else if (strlen($part2) == 0 && strlen($part1) == 0)
- return $this->m_default_tab;
- else
- return $section;
- }
- /**
- * Resolve sections.
- *
- * @param array $sections section list
- * @return array resolved section list
- *
- * @see resolveSection
- */
- function resolveSections($sections)
- {
- $result = array();
- foreach ($sections as $section) {
- $result[] = $this->resolveSection($section);
- }
- return $result;
- }
- /**
- * Returns the default column name.
- *
- * @return string default column name
- */
- public function getDefaultColumn()
- {
- return $this->m_defaultColumn;
- }
- /**
- * Set default column name.
- *
- * @param string name default column name
- */
- public function setDefaultColumn($name)
- {
- $this->m_defaultColumn = $name;
- }
- /**
- * Resolve column for sections.
- *
- * If one of the sections contains something after a double
- * colon (:) than that's used as column name, else the default
- * column name will be used.
- *
- * @param array $sections sections
- *
- * @return string column name
- */
- protected function resolveColumn(&$sections)
- {
- $column = $this->getDefaultColumn();
- if (!is_array($sections)) {
- return $column;
- }
- foreach ($sections as &$section) {
- if (strpos($section, ":") !== false) {
- list($section, $column) = explode(':', $section);
- }
- }
- return $column;
- }
- /**
- * Resolve sections, tabs and the order based on the given
- * argument to the attribute add method.
- *
- * @param mixed $sections
- * @param mixed $tabs
- * @param mixed $order
- */
- function resolveSectionsTabsOrder(&$sections, &$tabs, &$column, &$order)
- {
- // Because sections/tabs will probably be used more than the order override option
- // the API for this method now favours the $sections argument. For backwards
- // compatibility we still support the old API ($attribute,$order=0).
- if ($sections !== NULL && is_int($sections)) {
- $order = $sections;
- $sections = array($this->m_default_tab);
- }
- // If no section/tab is specified or tabs are disabled, we use the current default tab
- // (specified with the setDefaultTab method, or "default" otherwise)
- elseif ($sections === NULL || (is_string($sections) && strlen($sections) == 0) || !Adapto_Config::getGlobal("tabs")) {
- $sections = array($this->m_default_tab);
- }
- // Sections should be an array.
- else if ($sections != "*" && !is_array($sections)) {
- $sections = array($sections);
- }
- $column = $this->resolveColumn($sections);
- if (is_array($sections)) {
- $sections = $this->resolveSections($sections);
- }
- // Filter tabs from section names.
- $tabs = $this->getTabsFromSections($sections);
- }
- /**
- * Add an atkAttribute (or one of its derivatives) to the entity.
- * @param atkAttribute $attribute The attribute you want to add
- * @param mixed $sections The sections/tab(s) on which the attribute should be
- * displayed. Can be a tabname (String) or a list of
- * tabs (array) or "*" if the attribute should be
- * displayed on all tabs.
- * @param int $order The order at which the attribute should be displayed.
- * If ommitted, this defaults to 100 for the first
- * attribute, and 100 more for each next attribute that
- * is added.
- * @return atkAttribute the attribute just added
- */
- public function add($attribute, $sections = NULL, $order = 0)
- {
- $tabs = null;
- $column = null;
- $attribute->m_owner = $this->m_type;
- // If we're running inside modifier scope, we have to tell the attribute
- // what module he originated from.
- if ($this->m_modifier != "")
- $attribute->m_module = $this->m_modifier;
- if (!atkReadOptimizer()) {
- $this->resolveSectionsTabsOrder($sections, $tabs, $column, $order);
- // check for parent fieldname (treeview)
- if ($attribute->hasFlag(AF_PARENT)) {
- $this->m_parent = $attribute->fieldName();
- }
- // check for cascading delete flag
- if ($attribute->hasFlag(AF_CASCADE_DELETE)) {
- $this->m_cascadingAttribs[] = $attribute->fieldName();
- }
- if ($attribute->hasFlag(AF_HIDE_LIST) && !$attribute->hasFlag(AF_PRIMARY)) {
- if (!in_array($attribute->fieldName(), $this->m_listExcludes)) {
- $this->m_listExcludes[] = $attribute->fieldName();
- }
- }
- if ($attribute->hasFlag(AF_HIDE_VIEW) && !$attribute->hasFlag(AF_PRIMARY)) {
- if (!in_array($attribute->fieldName(), $this->m_viewExcludes)) {
- $this->m_viewExcludes[] = $attribute->fieldName();
- }
- }
- } else {
- // when the read optimizer is enabled there is no active tab
- // we circument this by putting all attributes on all tabs
- if ($sections !== NULL && is_int($sections))
- $order = $sections;
- $tabs = "*";
- $sections = "*";
- $column = $this->getDefaultColumn();
- }
- // NOTE: THIS SHOULD WORK. BUT, since add() is called from inside the $this
- // constructor, m_ownerInstance ends up being a copy of $this, rather than
- // a reference. Don't ask me why, it has something to do with the way PHP
- // handles the constructor.
- // To work around this, we reassign the this pointer to the attributes as
- // soon as possible AFTER the constructor. (the dispatcher function)
- $attribute->setOwnerInstance($this);
- if ($attribute->hasFlag(AF_PRIMARY)) {
- if (!in_array($attribute->fieldName(), $this->m_primaryKey)) {
- $this->m_primaryKey[] = $attribute->fieldName();
- }
- }
- if ($attribute->hasFlag(AF_MULTILANGUAGE)) {
- $this->m_lngfield = $attribute->fieldName();
- }
- $attribute->init();
- $exist = false;
- if (isset($this->m_attribList[$attribute->fieldName()]) && is_object($this->m_attribList[$attribute->fieldName()])) {
- $exist = true;
- // if order is set, overwrite it with new order, last order will count
- if ($order != 0) {
- $this->m_attribIndexList[$this->m_attribList[$attribute->fieldName()]->m_index]["order"] = $order;
- }
- $attribute->m_index = $this->m_attribList[$attribute->fieldName()]->m_index;
- }
- if (!$exist) {
- if ($order == 0) {
- $this->m_attribOrder += 100;
- $order = $this->m_attribOrder;
- }
- if (!atkReadOptimizer()) {
- // add new tab(s) to the tab list ("*" isn't a tab!)
- if ($tabs != "*") {
- if (!$attribute->hasFlag(AF_HIDE_ADD))
- $this->m_tabList["add"] = isset($this->m_tabList["add"]) ? Adapto_array_merge($this->m_tabList["add"], $tabs) : $tabs;
- if (!$attribute->hasFlag(AF_HIDE_EDIT))
- $this->m_tabList["edit"] = isset($this->m_tabList["edit"]) ? Adapto_array_merge($this->m_tabList["edit"], $tabs) : $tabs;
- if (!$attribute->hasFlag(AF_HIDE_VIEW))
- $this->m_tabList["view"] = isset($this->m_tabList["view"]) ? Adapto_array_merge($this->m_tabList["view"], $tabs) : $tabs;
- }
- if ($sections != "*") {
- if (!$attribute->hasFlag(AF_HIDE_ADD))
- $this->m_sectionList["add"] = isset($this->m_sectionList["add"]) ? Adapto_array_merge($this->m_sectionList["add"], $sections)
- : $sections;
- if (!$attribute->hasFlag(AF_HIDE_EDIT))
- $this->m_sectionList["edit"] = isset($this->m_sectionList['edit']) ? Adapto_array_merge($this->m_sectionList["edit"], $sections)
- : $sections;
- if (!$attribute->hasFlag(AF_HIDE_VIEW))
- $this->m_sectionList["view"] = isset($this->m_sectionList['view']) ? Adapto_array_merge($this->m_sectionList["view"], $sections)
- : $sections;
- }
- }
- $attribute->m_order = $order;
- $this->m_attribIndexList[] = array("name" => $attribute->fieldName(), "tabs" => $tabs, "sections" => $sections, "order" => $attribute->m_order);
- $attribute->m_index = max(array_keys($this->m_attribIndexList)); // might contain gaps
- $attribute->setTabs($tabs);
- $attribute->setSections($sections);
- $this->m_attributeTabs[$attribute->fieldname()] = $tabs;
- }
- // Order the tablist
- $this->m_attribList[$attribute->fieldName()] = &$attribute;
- if (is_subclass_of($attribute, "atkrelation")) {
- $this->m_relations[strtolower(get_class($attribute))][$attribute->fieldName()] = &$attribute;
- }
- $attribute->setTabs($this->m_attributeTabs[$attribute->fieldName()]);
- $attribute->setSections($this->m_attribIndexList[$attribute->m_index]['sections']);
- $attribute->setColumn($column);
- return $attribute;
- }
- /**
- * Add fieldset.
- *
- * To include an attribute label use [attribute.label] inside your
- * template. To include an attribute edit/display field use
- * [attribute.field] inside your template.
- *
- * @param string $name name
- * @param string $template template string
- * @param int $flags attribute flags
- * @param mixed $sections The sections/tab(s) on which the attribute should be
- * displayed. Can be a tabname (String) or a list of
- * tabs (array) or "*" if the attribute should be
- * displayed on all tabs.
- * @param int $order The order at which the attribute should be displayed.
- * If ommitted, this defaults to 100 for the first
- * attribute, and 100 more for each next attribute that
- * is added.
- */
- public function addFieldSet($name, $template, $flags = 0, $sections = NULL, $order = 0)
- {
- useattrib('atkfieldset');
- $this->add(new Adapto_FieldSet($name, $template, $flags), $sections, $order);
- }
- /**
- * Retrieve the tabnames from the sections string (tab.section).
- *
- * @param mixed $sections An array with sections or a section string
- */
- function getTabsFromSections($sections)
- {
- if ($sections == "*" || $sections === NULL)
- return $sections;
- $tabs = array();
- if (!isset($sections))
- $section = array();
- elseif (!is_array($sections))
- $sections = array($sections);
- foreach ($sections as $section) {
- $tabs[] = $this->getTabFromSection($section);
- }
- //when using the tab.sections notation, we can have duplicate tabs
- //strip them out.
- return array_unique($tabs);
- }
- /**
- * Strip section part from a section and return the tab.
- *
- * If no tab name is provided, the default tab is returned.
- *
- * @param string $section The section to get the tab from
- */
- function getTabFromSection($section)
- {
- $tab = ($section == NULL) ? "" : $section;
- if (strstr($tab, ".") !== false)
- list($tab) = explode(".", $tab);
- return (($tab == "") ? $this->m_default_tab : $tab);
- }
- /**
- * Remove an attribute.
- *
- * Completely removes an attribute from an entity.
- * Note: Since other functionality may already depend on the attribute
- * that you are about to remove, it's often better to just hide an
- * attribute if you don't need it.
- * @param String $attribname The name of the attribute to remove.
- */
- function remove($attribname)
- {
- if (is_object($this->m_attribList[$attribname])) {
- Adapto_Util_Debugger::debug("removing attribute $attribname");
- $listindex = $this->m_attribList[$attribname]->m_index;
- unset($this->m_attribList[$attribname]);
- foreach ($this->m_listExcludes as $i => $name) {
- if ($name == $attribname)
- unset($this->m_listExcludes[$i]);
- }
- foreach ($this->m_viewExcludes as $i => $name) {
- if ($name == $attribname)
- unset($this->m_viewExcludes[$i]);
- }
- foreach ($this->m_cascadingAttribs as $i => $name) {
- if ($name == $attribname) {
- unset($this->m_cascadingAttribs[$i]);
- $this->m_cascadingAttribs = array_values($this->m_cascadingAttribs);
- }
- }
- unset($this->m_attribIndexList[$listindex]);
- unset($this->m_attributeTabs[$attribname]);
- }
- }
- /**
- * Returns the table name for this entity.
- *
- * @return string table name
- */
- function getTable()
- {
- return $this->m_table;
- }
- /**
- * Get an attribute by name.
- * @param String $name The name of the attribute to retrieve.
- * @return atkAttribute The attribute.
- */
- function &getAttribute($name)
- {
- $returnValue = isset($this->m_attribList[$name]) ? $this->m_attribList[$name] : NULL;
- return $returnValue;
- }
- /**
- * Checks if the user has filled in something:
- * return true if he has, otherwise return false
- *
- * @param -
- * @return boolean.
- */
- function &filledInForm()
- {
- if (is_null($this->getAttributes()))
- return false;
- $postvars = atkGetPostVar();
- foreach ($this->m_attribList AS $name => $value)
- if (!$value->hasFlag(AF_HIDE_LIST))
- if (!is_array($value->fetchValue($postvars)) && $value->fetchValue($postvars) !== "")
- return true;
- return false;
- }
- /**
- * Gets all the attributes.
- * @return array Array with the attributes.
- */
- function &getAttributes()
- {
- if (isset($this->m_attribList))
- return $this->m_attribList;
- else
- return NULL;
- }
- /**
- * Returns a list of attribute names.
- *
- * @return array attribute names
- */
- function getAttributeNames()
- {
- return array_keys($this->m_attribList);
- }
- /**
- * Gets the attribute order.
- *
- * @param string $name The name of the attribute
- */
- function getAttributeOrder($name)
- {
- return $this->m_attribIndexList[$this->m_attribList[$name]->m_index]["order"];
- }
- /**
- * Sets an attributes order
- *
- * @param string $name The name of the attribute
- * @param int $order The order of the attribute
- */
- function setAttributeOrder($name, $order)
- {
- $this->m_attribList[$name]->m_order = $order;
- $this->m_attribIndexList[$this->m_attribList[$name]->m_index]["order"] = $order;
- }
- /**
- * Checks if the entity has a certain flag set.
- * @param int $flag The flag to check.
- * @return boolean True if the entity has the flag.
- */
- function hasFlag($flag)
- {
- return (($this->m_flags & $flag) == $flag);
- }
- /**
- * Add a flag to the entity.
- * @param int $flag The flag to add.
- */
- function addFlag($flag)
- {
- $this->m_flags |= $flag;
- }
- /**
- * Removes a flag from the entity.
- *
- * @param int $flag The flag to remove from the attribute
- */
- function removeFlag($flag)
- {
- if ($this->hasFlag($flag))
- $this->m_flags ^= $flag;
- }
- /**
- * Returns the entity flags.
- * @return Integer entity flags
- */
- function getFlags()
- {
- return $this->m_flags;
- }
- /**
- * Set entity flags.
- *
- * @param int $flags entity flags
- */
- public function setFlags($flags)
- {
- $this->m_flags = $flags;
- }
- /**
- * Returns the current partial name.
- *
- * @return string partial name
- */
- public function getPartial()
- {
- return $this->m_partial;
- }
- /**
- * Is partial request?
- *
- * @return boolean is partial
- */
- function isPartial()
- {
- return $this->m_partial;
- }
- /**
- * Sets the editable list attributes. If you supply this method
- * with one or more string arguments, all arguments are collected in
- * an array. Else the first parameter will be used.
- *
- * @param array $attrs list of attribute names
- */
- function setEditableListAttributes($attrs)
- {
- if (is_array($attrs))
- $this->m_editableListAttributes = $attrs;
- else
- $this->m_editableListAttributes = func_get_args();
- }
- /**
- * Sets the multi-record-action selection mode. Can either be
- * MRA_MULTI_SELECT (default), MRA_SINGLE_SELECT or
- * MRA_NO_SELECT.
- *
- * @param string $mode selection mode
- */
- function setMRASelectionMode($mode)
- {
- $this->m_mraSelectionMode = $mode;
- }
- /**
- * Returns the multi-record-action selection mode.
- * @return Integer multi-record-action selection mode
- */
- function getMRASelectionMode()
- {
- return $this->m_mraSelectionMode;
- }
- /**
- * Returns the primary key sql expression of a record.
- * @param array $rec The record for which the primary key is calculated.
- * @return String the primary key of the record.
- */
- function primaryKey($rec)
- {
- $primKey = "";
- $nrOfElements = count($this->m_primaryKey);
- for ($i = 0; $i < $nrOfElements; $i++) {
- $p_attrib = &$this->m_attribList[$this->m_primaryKey[$i]];
- $primKey .= $this->m_table . "." . $this->m_primaryKey[$i] . "='" . $p_attrib->value2db($rec) . "'";
- if ($i < ($nrOfElements - 1))
- $primKey .= " AND ";
- }
- return $primKey;
- }
- /**
- * Retrieve the name of the primary key attribute.
- *
- * Note: If an entity has a primary key that consists of multiple attributes,
- * this method will retrieve only the first attribute!
- * @return String First primary key attribute
- */
- function primaryKeyField()
- {
- if (count($this->m_primaryKey) === 0) {
- atkwarning($this->atkEntityType() . "::primaryKeyField() called, but there are no primary key fields defined!");
- return null;
- }
- return $this->m_primaryKey[0];
- }
- /**
- * Returns a primary key template.
- *
- * Like primaryKey(), this method returns a sql expression, but in this
- * case, no actual data is used. Instead, template fields are inserted
- * into the expression. This is useful for rendering multiple primary
- * keys later with a record and a template parser.
- *
- * @return String Primary key template
- */
- function primaryKeyTpl()
- {
- $primKey = "";
- $nrOfElements = count($this->m_primaryKey);
- for ($i = 0; $i < $nrOfElements; $i++) {
- $primKey .= $this->m_primaryKey[$i] . "='[" . $this->m_primaryKey[$i] . "]'";
- if ($i < ($nrOfElements - 1))
- $primKey .= " AND ";
- }
- return $primKey;
- }
- /**
- * Set default sort order for the entity.
- * @param String $orderby Default order by. Can be an attribute name or a
- * SQL expression.
- */
- function setOrder($orderby)
- {
- $this->m_default_order = $orderby;
- }
- /**
- * Get default sort order for the entity.
- * @return String $orderby Default order by. Can be an attribute name or a
- * SQL expression.
- */
- function getOrder()
- {
- return $this->m_default_order;
- }
- /**
- * Set the table that the entity should use.
- *
- * Note: This should be called in the constructor of derived classes,
- * after the base class constructor is called.
- * @param String $tablename The name of the table to use.
- * @param String $seq The name of the sequence to use for autoincrement
- * attributes.
- * @param mixed $db The database connection to use. If ommitted, this
- * defaults to the default database connection.
- * So in apps using only one database, it's not necessary
- * to pass this parameter.
- * You can pass either a connection (atkDb instance), or
- * a string containing the name of the connection to use.
- */
- function setTable($tablename, $seq = "", $db = NULL)
- {
- $this->m_table = $tablename;
- if ($seq == "")
- $seq = $tablename;
- $this->m_seq = $seq;
- $this->m_db = $db;
- }
- /**
- * Sets the database connection.
- *
- * @param string|atkDb $db database name or object
- */
- public function setDb($db)
- {
- $this->m_db = $db;
- }
- /**
- * Get the database connection for this entity.
- * @return atkDb Database connection instance
- */
- function getDb()
- {
- if ($this->m_db == NULL) {
- return atkGetDb();
- } else if (is_object($this->m_db)) {
- return $this->m_db;
- } else {
- // must be a named connection
- return atkGetDb($this->m_db);
- }
- }
- /**
- * Create an alphabetical index.
- *
- * Any string- or textbased attribute can be used to create an
- * alphabetical index in admin- and selectpages.
- * @param String $attribname The name of the attribute for which to create
- * the alphabetical index.
- */
- function setIndex($attribname)
- {
- $this->m_index = $attribname;
- }
- /**
- * Set tab index
- *
- * @param string $tabname Tabname
- * @param int $index Index number
- * @param string $action Action name (add,edit,view)
- */
- function setTabIndex($tabname, $index, $action = "")
- {
- Adapto_Util_Debugger::debug("Adapto_Entity::setTabIndex($tabname,$index,$action)");
- $actionList = array("add", "edit", "view");
- if ($action != "")
- $actionList = array($action);
- foreach ($actionList as $action) {
- $new_index = $index;
- $list = &$this->m_tabList[$action];
- if ($new_index < 0)
- $new_index = 0;
- if ($new_index > count($list))
- $new_index = count($list);
- $current_index = array_search($tabname, $list);
- if ($current_index !== NULL) {
- $tmp = array_splice($list, $current_index, 1);
- array_splice($list, $new_index, 0, $tmp);
- }
- }
- }
- /**
- * Set default tab being displayed in view/add/edit mode.
- * After calling this method, all attributes which are added after the
- * method call without specification of tab will be placed on the default
- * tab. This means you should use this method before you add any
- * attributes to the entity.
- * If you accept the default name for the first tab ("default") you do not
- * need to call this method.
- * @param String $tab the name of the default tab
- */
- function setDefaultTab($tab = "default")
- {
- $this->m_default_tab = $tab;
- }
- /**
- * Get a list of tabs for a certain action.
- * @param String $action The action for which you want to retrieve the
- * list of tabs.
- * @return array The list of tabnames.
- *
- */
- function getTabs($action)
- {
- $list = &$this->m_tabList[$action];
- $disable = $this->checkTabRights($list);
- $tabCode = "";
- if (!is_array($list)) {
- // fallback to view tabs.
- $list = &$this->m_tabList["view"];
- }
- // Attributes can also add tabs to the tablist.
- $this->m_filledTabs = array();
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- if ($p_attrib->hasFlag(AF_HIDE))
- continue;
- // attributes to which we don't have access are explicitly hidden
- // Only display the attribute if the attribute
- // resides on at least on visible tab
- for ($i = 0, $_i = sizeof($p_attrib->m_tabs); $i < $_i; $i++) {
- if ((is_array($list) && in_array($p_attrib->m_tabs[$i], $list)) || (!is_array($disable) || !in_array($p_attrib->m_tabs[$i], $disable))) {
- break;
- }
- }
- if (is_object($p_attrib)) {
- $additional = $p_attrib->getAdditionalTabs($action);
- if (is_array($additional) && count($additional) > 0) {
- $list = Adapto_array_merge($list, $additional);
- $this->m_filledTabs = Adapto_array_merge($this->m_filledTabs, $additional);
- }
- // Keep track of the tabs that containg attribs
- // so we only display none-empty tabs
- $tabCode = $this->m_attributeTabs[$attribname][0];
- if (!in_array($tabCode, $this->m_filledTabs)) {
- $this->m_filledTabs[] = $tabCode;
- }
- } else {
- Adapto_Util_Debugger::debug("atkentity::getTabs() Warning: $attribname is not an object!?");
- }
- }
- // Check if the currently known tabs all containg attributes
- // so we don't end up with empty tabs
- return $this->checkEmptyTabs($list);
- }
- /**
- * Retrieve the sections for the active tab.
- *
- * @param String $action
- * @return array The active sections.
- */
- function getSections($action)
- {
- $sections = array();
- if (is_array($this->m_sectionList[$action])) {
- foreach ($this->m_sectionList[$action] as $element) {
- list($tab, $sec) = (strpos($element, ".") !== false) ? explode(".", $element) : array($element, null);
- //if this section is on an active tab, we return it.
- if ($tab == $this->getActiveTab() && $sec !== NULL)
- $sections[] = $sec;
- }
- }
- //we do not want duplicate sections on the same tab.
- return array_unique($sections);
- }
- /**
- * Add sections that must be expanded by default.
- *
- */
- function addDefaultExpandedSections()
- {
- $sections = func_get_args();
- $sections = $this->resolveSections($sections);
- $this->m_default_expanded_sections = array_unique(array_merge($sections, $this->m_default_expanded_sections));
- }
- /**
- * Remove sections that must be expanded by default.
- *
- */
- function removeDefaultExpandedSections()
- {
- $sections = func_get_args();
- $this->m_default_expanded_sections = array_diff($this->m_default_expanded_sections, $sections);
- }
- /**
- * Check if the user has the rights to access existing tabs and
- * removes tabs from the list that may not be accessed
- *
- * @param array $tablist Array containing the current tablist
- * @return array with disable tabs
- */
- function checkTabRights(&$tablist)
- {
- global $g_entitys;
- $disable = array();
- if (empty($this->m_module))
- return $disable;
- for ($i = 0, $_i = count($tablist); $i < $_i; $i++) {
- if ($tablist[$i] == "" || $tablist[$i] == "default")
- continue;
- $secMgr = &atkGetSecurityManager();
- // load the $g_entitys array to find out what tabs are required
- if (!isset($g_entitys[$this->m_module][$this->m_type])) {
- include_once(Adapto_Config::getGlobal("atkroot") . "atk/atkentitytools.inc");
- $module = &getModule($this->m_module);
- $module->getEntitys();
- }
- $priv = "tab_" . $tablist[$i];
- if (isset($g_entitys[$this->m_module][$this->m_type]) && Adapto_in_array($priv, $g_entitys[$this->m_module][$this->m_type])) {
- // authorisation is required
- if (!$secMgr->allowed($this->m_module . "." . $this->m_type, "tab_" . $tablist[$i])) {
- Adapto_Util_Debugger::debug("Removing TAB " . $tablist[$i] . " because access to this tab was denied");
- $disable[] = $tablist[$i];
- unset($tablist[$i]);
- }
- }
- }
- if (is_array($tablist)) {
- // we might have now something like:
- // [0]=>tabA,[3]=>tabD
- // we convert this to a 'normal' array:
- // [0]=>tabA,[1]=>tabD;
- $newarray = array();
- foreach ($tablist as $tab)
- $newarray[] = $tab;
- $tablist = $newarray;
- }
- return $disable;
- }
- /**
- * Remove tabs without attribs from the tablist
- * @param array $list The list of tabnames
- * @return array The list of tabnames without the empty tabs.
- *
- */
- function checkEmptyTabs($list)
- {
- $tabList = array();
- if (is_array($list)) {
- foreach ($list AS $tabEntry) {
- if (in_array($tabEntry, $this->m_filledTabs)) {
- $tabList[] = $tabEntry;
- } else {
- Adapto_Util_Debugger::debug("Removing TAB " . $tabEntry . " because it had no attributes assigned");
- }
- }
- }
- return $tabList;
- }
- /**
- * Returns the currently active tab.
- *
- * Note that in themes which use dhtml tabs (tabs without reloads), this
- * method will always return the name of the first tab.
- * @return String The name of the currently visible tab.
- */
- function getActiveTab()
- {
- global $Adapto_VARS;
- $tablist = $this->getTabs($Adapto_VARS["atkaction"]);
- // Note: we may not read atktab from $this->m_postvars, because $this->m_postvars is not filled if this is
- // a nested entity (in a relation for example).
- if (!empty($Adapto_VARS["atktab"]) && in_array($Adapto_VARS["atktab"], $tablist))
- $tab = $Adapto_VARS["atktab"];
- elseif (!empty($this->m_default_tab) && in_array($this->m_default_tab, $tablist))
- $tab = $this->m_default_tab;
- else
- $tab = $tablist[0];
- return $tab;
- }
- /**
- * Get the active sections.
- *
- * @param string $tab The currently active tab
- * @param string $mode The current mode ("edit", "add", etc.)
- */
- function getActiveSections($tab, $mode)
- {
- $activeSections = array();
- if (is_array($this->m_sectionList[$mode])) {
- foreach ($this->m_sectionList[$mode] as $section) {
- if (substr($section, 0, strlen($tab)) == $tab) {
- $sectionName = 'section_' . str_replace('.', '_', $section);
- $key = array("entitytype" => $this->atkentitytype(), "section" => $sectionName);
- $defaultOpen = in_array($section, $this->m_default_expanded_sections);
- if (atkState::get($key, $defaultOpen ? 'opened' : 'closed') != 'closed') {
- $activeSections[] = $section;
- }
- }
- }
- }
- return $activeSections;
- }
- /**
- * Add a recordset filter.
- * @param String $filter The fieldname you want to filter OR a SQL where
- * clause expression.
- * @param String $value Required value. (Ommit this parameter if you pass
- * an SQL expression for $filter.)
- */
- function addFilter($filter, $value = "")
- {
- if ($value == "") {
- // $key is a where clause kind of thing
- $this->m_fuzzyFilters[] = $filter;
- } else {
- // $key is a $key, $value is a value
- $this->m_filters[$filter] = $value;
- }
- }
- /**
- * Search and remove a recordset filter.
- * @param String $filter The filter to search for
- * @param String $value The value to search for in case it is not a fuzzy filter
- * @return TRUE if the given filter was found and removed, FALSE otherwise.
- */
- function removeFilter($filter, $value = "")
- {
- if ($value == "") {
- // fuzzy
- $key = array_search($filter, $this->m_fuzzyFilters);
- if (is_numeric($key)) {
- unset($this->m_fuzzyFilters[$key]);
- $this->m_fuzzyFilters = array_values($this->m_fuzzyFilters);
- return true;
- }
- } else {
- // not fuzzy
- foreach (array_keys($this->m_filters) as $key) {
- if ($filter == $key && $value == $this->m_filters[$key]) {
- unset($this->m_filters[$key]);
- return true;
- }
- }
- }
- return false;
- }
- /**
- * Returns the form buttons for a certain page.
- *
- * Can be overridden by derived classes to define custom buttons.
- * @param String $mode The action for which the buttons are retrieved.
- * @param array $record The record currently displayed/edited in the form.
- * This param can be used to define record specific
- * buttons.
- * @return array
- */
- function getFormButtons($mode, $record)
- {
- $controller = Adapto_ClassLoader::getInstance('atk.atkcontroller');
- $controller->setEntity($this);
- return $controller->getFormButtons($mode, $record);
- }
- /**
- * Generate a box displaying a message that the current record is locked.
- * @return String The HTML fragment containing a box with the message and
- * a back-button.
- */
- function lockPage()
- {
- $output = $this->statusbar();
- $output .= '<img src="' . Adapto_Config::getGlobal("atkroot") . 'atk/images/lock.gif"><br><br>' . atktext("lock_locked") . '<br>';
- $output .= '<br><form method="get">' . session_form(SESSION_BACK) . '<input type="submit" class="btn_cancel" value="<< ' . atktext('back')
- . '"></form>';
- $ui = &$this->getUi();
- if (is_object($ui)) {
- $total = $ui->renderBox(array("title" => $this->actionTitle($this->m_action), "content" => $output));
- }
- return $total;
- }
- /**
- * Get the ui instance for drawing and templating purposes.
- *
- * @return atkUi An atkUi instance for drawing and templating.
- */
- function &getUi()
- {
- $ui = Adapto_ClassLoader::getInstance("atk.ui.atkui");
- return $ui;
- }
- /**
- * Generate a title for a certain action on a certain action.
- *
- * The default implementation displayes the action name, and the
- * descriptor of the current record between brackets. This can be
- * overridden by derived classes.
- * @param String $action The action for which the title is generated.
- * @param array $record The record for which the title is generated.
- * @return String The full title of the action.
- */
- function actionTitle($action, $record = "")
- {
- global $g_sessionManager;
- $ui = &$this->getUi();
- $res = "";
- if ($record != "") {
- $descr = $this->descriptor($record);
- $g_sessionManager->pageVar("descriptor", $descr);
- }
- $descriptortrace = $g_sessionManager->descriptorTrace();
- $nomodule = false;
- if (!empty($descriptortrace)) {
- $nomodule = true;
- $descrtrace = "";
- // only show the last 3 elems
- $cnt = count($descriptortrace);
- if ($cnt > 3)
- $descrtrace = "... - ";
- for ($i = max(0, $cnt - 3), $_i = $cnt; $i < $_i; $i++) {
- $desc = $descriptortrace[$i];
- $descrtrace .= Adapto_htmlentities($desc, ENT_COMPAT) . " - ";
- }
- $res = $descrtrace . $res;
- }
- if (is_object($ui)) {
- $res .= $ui->entityTitle($this, $action, $nomodule);
- }
- return $res;
- }
- /**
- * Place a set of tabs around content.
- * @param String $action The action for which the tabs are loaded.
- * @param String $content The content that is to be displayed within the
- * tabset.
- * @return String The complete tabset with content.
- */
- function tabulate($action, $content)
- {
- $this->addStyle("sections.css");
- $this->addStyle("tabs.css");
- $list = $this->getTabs($action);
- $sections = $this->getSections($action);
- $tabs = count($list);
- if (count($sections) > 0 || $tabs > 1) {
- $page = &$this->getPage();
- $page->register_script(Adapto_Config::getGlobal("atkroot") . "atk/javascript/dhtml_tabs.js.php?stateful=" . (Adapto_Config::getGlobal('dhtml_tabs_stateful') ? 1 : 0));
- // Load default tab show script.
- $page->register_loadscript('if ( window.showTab ) {showTab(\'' . (isset($this->m_postvars['atktab']) ? $this->m_postvars['atktab'] : '') . '\');}');
- $fulltabs = $this->buildTabs($action);
- $tabscript = "var tabs = new Array();\n";
- foreach ($fulltabs as $tab) {
- $tabscript .= "tabs[tabs.length] = '" . $tab['tab'] . "';\n";
- }
- $page->register_scriptcode($tabscript);
- }
- if ($tabs > 1) {
- $ui = &$this->getUi();
- if (is_object($ui)) {
- return $ui->renderTabs(array("tabs" => $this->buildTabs($action), "content" => $content));
- }
- }
- return $content;
- }
- /**
- * Determine the default form parameters for an action template.
- * @param boolean $locked If the current record is locked, pass true, so
- * the lockicon can be placed in the params too.
- * @return array Default form parameters for action forms (assoc. array)
- */
- function getDefaultActionParams($locked = false)
- {
- $params = $this->getHelp();
- $params["lockstatus"] = $this->getLockStatusIcon($locked);
- $params["formend"] = '</form>';
- return $params;
- }
- /**
- * Check attribute security.
- *
- * Makes some attributes read-only, or hides the attribute based
- * on the current mode / record.
- *
- * @param string $mode current mode (add, edit, view etc.)
- * @param array $record current record (optional)
- */
- function checkAttributeSecurity($mode, $record = NULL)
- {
- // check if an attribute needs to be read-only or
- // even hidden based on the current record
- $secMgr = &atkGetSecurityManager();
- foreach (array_keys($this->m_attribList) as $attrName) {
- $attr = &$this->getAttribute($attrName);
- if (($mode == "add" || $mode == "edit") && !$secMgr->attribAllowed($attr, $mode, $record) && $secMgr->attribAllowed($attr, "view", $record)) {
- $attr->addFlag(AF_READONLY);
- } else if (!$secMgr->attribAllowed($attr, $mode, $record)) {
- $attr->addFlag(AF_HIDE);
- }
- }
- }
- /**
- * The preAddToEditArray method is called from within the editArray
- * method prior to letting the attributes add themselves to the edit
- * array, but after the edit record values have been collected (a
- * combination of the current record, initial/edit values and the forced
- * values). This makes it possible to do some last-minute modifications to
- * the record data and possibily add some last-minute attributes etc.
- *
- * @param array $record the edit record
- * @param string $mode edit mode (add or edit)
- */
- function preAddToEditArray(&$record, $mode)
- {
- // do nothing
- }
- /**
- * The preAddToViewArray method is called from within the viewArray
- * method prior to letting the attributes add themselves to the view
- * array, but after the view record values have been collected This makes
- * it possible to do some last-minute modifications to the record data
- * and possibily add some last-minute attributes etc.
- *
- * @param array $record the edit record
- * @param string $mode view mode
- */
- function preAddToViewArray(&$record, $mode)
- {
- // do nothing
- }
- /**
- * Function outputs an array with edit fields. For each field the array
- * contains the name, edit HTML code etc. (name, html, obligatory,
- * error, label)
- *
- * @todo The editArray method should use a set of classes to build the
- * form, instead of an array with an overly complex structure.
- * @param String $mode The edit mode ("add" or "edit")
- * @param array $record The record currently being edited.
- * @param array $forceList A key-value array used to preset certain
- * fields to a certain value, regardless of the
- * value in the record.
- * @param array $suppressList List of attributenames that you want to hide
- * @param String $fieldprefix Of set, each form element is prefixed with
- * the specified prefix (used in embedded form
- * fields)
- * @param bool $ignoreTab Ignore the tabs an attribute should be shown on.
- * @param bool $injectSections Inject sections?
- * @return array List of edit fields (per field ( name, html, obligatory,
- * error, label })
- */
- function editArray($mode = "add", $record = NULL, $forceList = "", $suppressList = "", $fieldprefix = "", $ignoreTab = false, $injectSections = true)
- {
- // update visibility of some attributes based on the current record
- $this->checkAttributeSecurity($mode, $record);
- /* read metadata */
- $this->setAttribSizes();
- /* default values */
- if (!empty($record))
- $defaults = $record;
- else
- $defaults = array();
- $result['hide'] = array();
- $result['fields'] = array();
- /* edit mode */
- if ($mode == "edit") {
- /* entitys can define edit_values */
- $overrides = $this->edit_values($defaults);
- foreach ($overrides as $varname => $value) {
- $defaults[$varname] = $value;
- }
- } /* add mode */
- else {
- /* entitys can define initial values, if they don't already have values. */
- if (!isset($defaults['atkerror'])) // only load initial values the first time (not after an error occured)
- {
- $overrides = $this->initial_values();
- if (is_array($overrides) && count($overrides) > 0) {
- foreach ($overrides as $varname => $value) {
- if (!isset($defaults[$varname]) || $defaults[$varname] == "")
- $defaults[$varname] = $value;
- }
- }
- }
- }
- /* check for forced values */
- if (is_array($forceList)) {
- foreach ($forceList as $forcedvarname => $forcedvalue) {
- $attribname = "";
- if ($forcedvarname != "") {
- if (strpos($forcedvarname, '.') > 0) {
- list($firstpart, $field) = explode('.', $forcedvarname);
- if ($firstpart == $this->m_table) {
- // this is a filter on the current table.
- $defaults[$field] = $forcedvalue;
- $attribname = $field;
- } else {
- // this is a filter on a field of another table (something we have a
- // relationship with.if(is_object($this->m_attribList[$table]))
- if (is_object($this->m_attribList[$firstpart])) {
- $defaults[$firstpart][$field] = $forcedvalue;
- $attribname = $firstpart;
- } else {
- // This is not a filter for this entity.
- }
- }
- } else {
- $defaults[$forcedvarname] = $forcedvalue;
- $attribname = $forcedvarname;
- }
- if ($attribname != "") {
- if (isset($this->m_attribList[$attribname])) {
- $p_attrib = &$this->m_attribList[$attribname];
- if (is_object($p_attrib) && (!$p_attrib->hasFlag(AF_NO_FILTER)))
- $p_attrib->m_flags |= AF_READONLY | AF_HIDE_ADD;
- } else {
- throw new Adapto_Exception("Attribute '$attribname' doesn't exist in the attributelist");
- }
- }
- }
- }
- }
- // call preAddToEditArray at the attribute level, allows attribute to do
- // some last minute manipulations on for example the record
- foreach ($this->getAttributes() as $attr) {
- $attr->preAddToEditArray($defaults, $fieldprefix, $mode);
- }
- // call preAddToEditArray for the entity itself.
- $this->preAddToEditArray($defaults, $mode);
- // initialize dependencies
- foreach ($this->getAttributes() as $attr) {
- $attr->initDependencies($defaults, $fieldprefix, $mode);
- }
- // extra submission data
- $result["hide"][] = '<input type="hidden" name="atkfieldprefix" value="' . $this->getEditFieldPrefix(false) . '">';
- $result["hide"][] = '<input type="hidden" name="' . $fieldprefix . 'atkentitytype" value="' . $this->atkentitytype() . '">';
- $result["hide"][] = '<input type="hidden" name="' . $fieldprefix . 'atkprimkey" value="' . atkArrayNvl($record, "atkprimkey", "") . '">';
- /* For all attributes we use the edit() method to get HTML code for editting the
- * attribute's data. If the attribute is hidden we use the hide() method method
- * to get HTML code for hideing the attribute's data. You can override the attribute's
- * edit() method by supplying an <attributename>_edit function in the derived classes.
- */
- $tab = $this->getActiveTab();
- foreach (array_keys($this->m_attribIndexList) as $r) {
- $attribname = $this->m_attribIndexList[$r]["name"];
- $field = array("name" => $attribname);
- $p_attrib = &$this->m_attribList[$attribname];
- if ($p_attrib != NULL) {
- if ($p_attrib->hasDisabledMode(DISABLED_EDIT))
- continue;
- $field = array("name" => $attribname);
- /* fields that have not yet been initialised may be overriden in the url */
- if (!array_key_exists($p_attrib->fieldName(), $defaults) && array_key_exists($p_attrib->fieldName(), $this->m_postvars)) {
- $defaults[$p_attrib->fieldName()] = $this->m_postvars[$p_attrib->fieldName()];
- }
- /* sometimes a field is hidden although not specified by the field itself */
- $theme = Adapto_ClassLoader::getInstance("Adapto_Ui_Theme");
- if ($theme->getAttribute("tabtype") == "dhtml" || $ignoreTab) {
- $notOnTab = FALSE;
- } else {
- $notOnTab = !$p_attrib->showOnTab($tab);
- }
- if ((is_array($suppressList) && count($suppressList) > 0 && in_array($attribname, $suppressList)) || $notOnTab) {
- $p_attrib->m_flags |= ($mode == "add" ? AF_HIDE_ADD : AF_HIDE_EDIT);
- }
- /* we let the attribute add itself to the edit array */
- $p_attrib->addToEditArray($mode, $result, $defaults, $record['atkerror'], $fieldprefix);
- } else {
- throw new Adapto_Exception("Attribute $attribname not found!");
- }
- }
- if ($injectSections) {
- $this->injectSections($result['fields'], $mode);
- }
- /* check for errors */
- $result["error"] = $record['atkerror'];
- /* return the result array */
- return $result;
- }
- /**
- * Function outputs an array with view fields. For each field the array
- * contains the name, view HTML code etc.
- *
- * @todo The viewArray method should use a set of classes to build the
- * form, instead of an array with an overly complex structure.
- * @param String $mode The edit mode ("view")
- * @param array $record The record currently being viewed.
- * @param bool $injectSections Inject sections?
- * @return array List of edit fields (per field ( name, html, obligatory,
- * error, label })
- */
- function viewArray($mode, $record, $injectSections = true)
- {
- // update visibility of some attributes based on the current record
- $this->checkAttributeSecurity($mode, $record);
- // call preAddToViewArray at the attribute level, allows attribute to do
- // some last minute manipulations on for example the record
- foreach ($this->getAttributes() as $attr) {
- $attr->preAddToViewArray($record, $mode);
- }
- // call preAddToViewArray for the entity itself.
- $this->preAddToViewArray($record, $mode);
- $tab = $this->getActiveTab();
- $result = array();
- foreach (array_keys($this->m_attribIndexList) as $r) {
- $attribname = $this->m_attribIndexList[$r]["name"];
- $p_attrib = &$this->m_attribList[$attribname];
- if ($p_attrib != NULL) {
- if ($p_attrib->hasDisabledMode(DISABLED_VIEW))
- continue;
- /* we let the attribute add itself to the view array */
- $p_attrib->addToViewArray($mode, $result, $record);
- } else {
- throw new Adapto_Exception("Attribute $attribname not found!");
- }
- }
- /* inject sections */
- if ($injectSections) {
- $this->injectSections($result['fields'], $mode);
- }
- /* return the result array */
- return $result;
- }
- /**
- * Add sections to the edit/view fields array.
- *
- * @param array $fields fields array (will be modified in-place)
- */
- function injectSections(&$fields)
- {
- $this->groupFieldsBySection($fields);
- $addedSections = array();
- $result = array();
- foreach ($fields as $field) {
- /// we add the section link before the first attribute that is in it
- $fieldSections = $field['sections'];
- if (!is_array($fieldSections))
- $fieldSections = array($fieldSections);
- $newSections = array_diff($fieldSections, $addedSections);
- if (count($newSections) > 0) {
- foreach ($newSections as $section) {
- if (strpos($section, '.') !== FALSE) {
- $result[] = array("html" => "section", "name" => $section, "tabs" => $field['tabs']);
- $addedSections[] = $section;
- }
- }
- }
- $result[] = $field;
- }
- $fields = $result;
- }
- /**
- * Group fields by section.
- *
- * @param array $fields fields array (will be modified in-place)
- */
- function groupFieldsBySection(&$fields)
- {
- $result = array();
- $sections = array();
- // first find sectionless fields and collect all sections
- foreach ($fields as $field) {
- if ($field["sections"] == "*" || (count($field["sections"]) == 1 && $field["sections"][0] == $this->m_default_tab)) {
- $result[] = $field;
- } else if (is_array($field['sections'])) {
- $sections = array_merge($sections, $field['sections']);
- }
- }
- $sections = array_unique($sections);
- // loop through each section (except the default tab/section) of the mode we are currently in.
- while (count($sections) > 0) {
- $section = array_shift($sections);
- // find fields for this section
- foreach ($fields as $field) {
- if (is_array($field["sections"]) && in_array($section, $field["sections"]))
- $result[] = $field;
- }
- }
- $fields = $result;
- }
- /**
- * Retrieve the initial values for a new record.
- *
- * The system calls this method to create a new record. By default
- * this method returns an empty record, but derived entitys may override
- * this method to perform record initialization.
- *
- * @return array Array containing an initial value per attribute.
- * Only attributes that are initialized appear in the
- * array.
- */
- function initial_values()
- {
- $record = array();
- foreach (array_keys($this->m_attribList) as $attrName) {
- $attr = &$this->getAttribute($attrName);
- if (is_array($this->m_postvars) && isset($this->m_postvars[$attrName])) {
- $value = $attr->fetchValue($this->m_postvars);
- } else {
- $value = $attr->initialValue();
- }
- if ($value !== NULL)
- $record[$attr->fieldName()] = $value;
- }
- return $record;
- }
- /**
- * Retrieve new values for an existing record.
- *
- * The system calls this method to override the values of a record
- * before editing the record.
- * The default implementation does not do anything to the record, but
- * derived classes may override this method to make modifications to.
- * the record.
- *
- * @param array $record The record that is about to be edited.
- * @return array The manipulated record.
- */
- function edit_values($record)
- {
- return $record;
- }
- /**
- * Get the template to use for a certain action.
- *
- * The system calls this method to determine which template to use when
- * rendering a certain screen. The default implementation always returns
- * the same template for the same action (it ignores parameter 2 and 3).
- * You can override this method in derived classes however, to determine
- * on the fly which template to use.
- * The action, the current record (if any) and the tab are passed as
- * parameter. By using these params, you can have custom templates per
- * action, and/or per tab, and even per record.
- *
- * @param String $action The action for which you wnat to retrieve the
- * template.
- * @param array $record The record for which you want to return the
- * template (or NULL if there is no record).
- * @param String $tab The name of the tab for which you want to
- * retrieve the template.
- * @return String The filename of the template (without path)
- */
- function getTemplate($action, $record = NULL, $tab = "")
- {
- switch ($action) {
- case "add": // add and edit both use the same form.
- case "edit":
- return "editform.tpl";
- case "view":
- return "viewform.tpl";
- case "search":
- return "searchform.tpl";
- case "smartsearch":
- return "smartsearchform.tpl";
- case "admin":
- return "recordlist.tpl";
- }
- }
- /**
- * Function outputs a form with all values hidden.
- *
- * This is probably only useful for the atkOneToOneRelation's hide method.
- *
- * @param String $mode The edit mode ("add" or "edit")
- * @param array $record The record that should be hidden.
- * @param array $forceList A key-value array used to preset certain
- * fields to a certain value, regardless of the
- * value in the record.
- * @param String $fieldprefix Of set, each form element is prefixed with
- * the specified prefix (used in embedded form
- * fields)
- * @return String HTML fragment containing all hidden elements.
- *
- */
- function hideForm($mode = "add", $record = NULL, $forceList = "", $fieldprefix = "")
- {
- /* suppress all */
- $suppressList = array();
- foreach (array_keys($this->m_attribIndexList) as $r)
- $suppressList[] = $this->m_attribIndexList[$r]["name"];
- /* get data, transform into "form", return */
- $data = $this->editArray($mode, $record, $forceList, $suppressList, $fieldprefix);
- foreach ($data["hide"] as $hide)
- $form .= $hide;
- return $form;
- }
- /**
- * Builds a list of tabs.
- *
- * This doesn't generate the actual HTML code, but returns the data for
- * the tabs (title, selected, urls that should be loaded upon click of the
- * tab etc).
- * @param String $action The action for which the tabs should be generated.
- * @return array List of tabs
- * @todo Make translation of tabs module aware
- */
- function buildTabs($action = "")
- {
- if ($action == "") {
- // assume active action
- $action = $this->m_action;
- }
- $result = array();
- // which tab is currently selected
- $tab = $this->getActiveTab();
- // build navigator
- $list = &$this->getTabs($action);
- if (is_array($list)) {
- $newtab["total"] = count($list);
- foreach ($list as $t) {
- $newtab["title"] = $this->text(array("tab_$t", $t));
- $newtab["tab"] = $t;
- $url = atkSelf() . "?atkentitytype=" . $this->atkEntityType() . "&atkaction=" . $this->m_action . "&atktab=" . $t;
- if ($this->m_action == "view") {
- $newtab["link"] = atkSessionManager::sessionUrl($url, SESSION_DEFAULT);
- } else {
- $newtab["link"] = "javascript:atkSubmit('" . atkurlencode(atkSessionManager::sessionUrl($url, SESSION_DEFAULT)) . "')";
- }
- $newtab["selected"] = ($t == $tab);
- $result[] = $newtab;
- }
- }
- return $result;
- }
- /**
- * Retrieve an array with the default actions for a certain mode.
- *
- * This will return a list of actions that can be performed on records
- * of this entity in an admin screen.
- * The actions may contain a [pk] template variable to reference a record,
- * so for each record you should run the stringparser on the action.
- *
- * @param String $mode The mode for which you want a list of actions.
- * Currently available modes for this method:
- * - "admin" (for actions in adminscreens)
- * - "relation" (for the list of actions when
- * displaying a recordlist in a onetomany-relation)
- * - "view" (for actions when viewing only)
- * Note: the default implementation of defaultActions
- * makes no difference between "relation" and "admin"
- * and will return the same actions for both, but you
- * might want to override this behaviour in derived
- * classes.
- * @param array $params An array of extra parameters to add to all the
- * action urls. You can use this to pass things like
- * an atkfilter for example. The array should be
- * key/value based.
- * @return array List of actions in the form array($action=>$actionurl)
- */
- function defaultActions($mode, $params = array())
- {
- $actions = array();
- $postfix = "";
- if (count($params) > 0) {
- foreach ($params as $key => $value) {
- $postfix .= "&$key=" . rawurlencode($value);
- }
- }
- // Changed: it used to be that you could only view if you didn't have
- // edit right. This was changed because of Achievo bug #41
- // (http://www.achievo.org/bug/41)
- $actionbase = atkSelf() . '?atkentitytype=' . $this->atkentitytype() . '&atkselector=[pk]' . $postfix;
- if (!$this->hasFlag(EF_NO_VIEW) && $this->allowed("view")) {
- $actions["view"] = $actionbase . '&atkaction=view';
- }
- if ($mode != "view") {
- if (!$this->hasFlag(EF_NO_EDIT) && $this->allowed("edit")) {
- $actions["edit"] = $actionbase . '&atkaction=edit';
- }
- if (!$this->hasFlag(EF_NO_DELETE) && $this->allowed("delete")) {
- $actions["delete"] = $actionbase . '&atkaction=delete';
- }
- if ($this->hasFlag(EF_COPY) && $this->allowed("copy")) {
- $actions["copy"] = $actionbase . '&atkaction=copy';
- }
- if ($this->hasFlag(EF_EDITAFTERCOPY) && $this->allowed("editcopy")) {
- $actions["editcopy"] = $actionbase . '&atkaction=editcopy';
- }
- }
- return $actions;
- }
- /**
- * Sets the priority range, for multi-record-priority actions.
- * @param int $min the minimum priority
- * @param int $max the maximum priority (0 for auto => min + record count)
- */
- function setPriorityRange($min = 1, $max = 0)
- {
- $this->m_priority_min = (int) $min;
- if ($max < $this->m_priority_min)
- $max = 0;
- else
- $this->m_priority_max = $max;
- }
- /**
- * Sets the possible multi-record-priority actions.
- * @param array $actions list of actions
- */
- function setPriorityActions($actions)
- {
- if (!is_array($actions))
- $this->m_priority_actions = array();
- else
- $this->m_priority_actions = $actions;
- }
- /**
- * Get extended search action.
- *
- * @return extended search action
- */
- function getExtendedSearchAction()
- {
- if (empty($this->m_extended_search_action))
- return Adapto_Config::getGlobal('extended_search_action');
- else
- return $this->m_extended_search_action;
- }
- /**
- * Set extended search action.
- *
- * @param string $action extended search action
- */
- function setExtendedSearchAction($action)
- {
- $this->m_extended_search_action = $action;
- }
- /**
- * Function returns a page in which the user is asked if he really wants
- * to perform a certain action.
- * @param mixed $atkselector Selector of current record on which the
- * action will be performed (String), or an
- * array of selectors when multiple records are
- * processed at once. The method uses the
- * selector(s) to display the current record(s)
- * in the confirmation page.
- * @param String $action The action for which confirmation is needed.
- * @param boolean $locked Pass true if the current record is locked.
- * @param boolean $checkoverride If set to true, this method will try to
- * find a custom method named
- * "confirm".$action."()" (e.g.
- * confirmDelete() and call that method
- * instead.
- * @param boolean $mergeSelectors Merge all selectors to one selector string (if more then one)?
- *
- * @return String Complete html fragment containing a box with the
- * confirmation page, or the output of the custom
- * override if $checkoverride was true.
- */
- function confirmAction($atkselector, $action, $locked = false, $checkoverride = true, $mergeSelectors = true, $csrfToken = null)
- {
- $method = 'confirm' . $action;
- if ($checkoverride && method_exists($this, $method))
- return $this->$method($atkselector, $locked);
- $ui = &$this->getUi();
- $this->addStyle("style.css");
- if (is_array($atkselector))
- $atkselector_str = '((' . implode($atkselector, ') OR (') . '))';
- else
- $atkselector_str = $atkselector;
- $formstart = '<form action="' . atkSelf() . '?"' . SID . ' method="post">';
- $formstart .= session_form();
- $formstart .= '<input type="hidden" name="atkaction" value="' . $action . '">';
- $formstart .= '<input type="hidden" name="atkentitytype" value="' . $this->atkentitytype() . '">';
- if (isset($csrfToken)) {
- $this->getHandler($action);
- $formstart .= '<input type="hidden" name="atkcsrftoken" value="' . $csrfToken . '">';
- }
- if ($mergeSelectors) {
- $formstart .= '<input type="hidden" name="atkselector" value="' . $atkselector_str . '">';
- } else if (!is_array($atkselector)) {
- $formstart .= '<input type="hidden" name="atkselector" value="' . $atkselector . '">';
- } else {
- foreach ($atkselector as $selector)
- $formstart .= '<input type="hidden" name="atkselector[]" value="' . $selector . '">';
- }
- $buttons = $this->getFormButtons($action, array());
- if (count($buttons) == 0) {
- $buttons[] = '<input name="confirm" type="submit" class="btn_ok atkdefaultbutton" value="' . $this->text('yes') . '">';
- $buttons[] = '<input name="cancel" type="submit" class="btn_cancel" value="' . $this->text('no') . '">';
- }
- $content = "";
- $recs = $this->selectDb($atkselector_str, "", "", "", $this->descriptorFields());
- if (count($recs) == 1) {
- // 1 record, put it in the page title (with the actionTitle call, a few lines below)
- $record = $recs[0];
- $this->getPage()->setTitle(atktext('app_shorttitle') . " - " . $this->actionTitle($action, $record));
- } else {
- // we are gonna perform an action on more than one record
- // show a list of affected records, at least if we can find a
- // descriptor_def method
- if ($this->m_descTemplate != NULL || method_exists($this, "descriptor_def")) {
- $record = "";
- $content .= "<ul>";
- for ($i = 0, $_i = count($recs); $i < $_i; $i++) {
- $content .= "<li>" . str_replace(' ', ' ', Adapto_htmlentities($this->descriptor($recs[$i])));
- }
- $content .= "</ul>";
- }
- }
- $content .= '<br>' . $this->confirmActionText($atkselector, $action, true);
- $output = $ui
- ->renderAction($action, array("content" => $content, "formstart" => $formstart, "formend" => '</form>', "buttons" => $buttons));
- return $ui->renderBox(array("title" => $this->actionTitle($action, $record), "content" => $output));
- }
- /**
- * Determine the confirmation message.
- * @param String $atkselector The record(s) on which the action is
- * performed.
- * @param String $action The action being performed.
- * @param boolean $checkoverride If true, returns the output of a custom
- * method named "confirm".$action."text()"
- * @return String The confirmation text.
- */
- function confirmActionText($atkselector = "", $action = "delete", $checkoverride = TRUE)
- {
- $method = 'confirm' . $action . 'text';
- if ($checkoverride && method_exists($this, $method))
- return $this->$method($atkselector);
- else
- return $this->text("confirm_$action" . (is_array($atkselector) && count($atkselector) > 1 ? '_multi' : ''));
- }
- /**
- * Small compare function for sorting attribs on order field
- * @access private
- * @param array $a The first attribute
- * @param array $b The second attribute
- * @return int
- */
- function attrib_cmp($a, $b)
- {
- if ($a["order"] == $b["order"])
- return 0;
- return ($a["order"] < $b["order"]) ? -1 : 1;
- }
- /**
- * This function initialises certain elements of the entity.
- *
- * This must be called right after the constructor. The function has a
- * check to prevent it from being executed twice. If you construct an entity
- * using 'new', you have to call this method. If you construct it with the
- * getEntity or newEntity method, you don't have to call this method.
- */
- function init()
- {
- Adapto_Util_Debugger::debug("init for " . $this->m_type);
- global $g_modifiers;
- // Check if initialisation is not already done.
- if ($this->m_initialised == true)
- return;
- // We assign the $this reference to the attributes at this stage, since
- // it fails when we do it in the add() function.
- // See also the comments in the add() function.
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- $p_attrib->setOwnerInstance($this);
- }
- // See if there are modules active that modify this entity, and apply the
- // modifiers if found.
- if (isset($g_modifiers[$this->atkentitytype()])) {
- foreach ($g_modifiers[$this->atkentitytype()] as $modulename) {
- $module = &getModule($modulename);
- $module->modifier($this);
- }
- }
- $this->_addListeners();
- // We set the tabs for the attributes
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- $p_attrib->setTabs($this->m_attributeTabs[$attribname]);
- }
- $this->attribSort();
- $lockType = Adapto_Config::getGlobal("lock_type");
- if (!empty($lockType) && $this->hasFlag(EF_LOCK)) {
- $this->m_lock = Adapto_ClassLoader::getInstance("atk.lock.atklock");
- } else
- $this->removeFlag(EF_LOCK);
- $this->m_defaultlanguage = strtoupper(Adapto_Config::getGlobal("defaultlanguage"));
- $this->m_initialised = true;
- // Call the attributes postInit method to doe some last time
- // initialization if necessary.
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- $p_attrib->postInit();
- }
- }
- /**
- * Add the listeners for the current entity
- * A listener can be defined either by placing an instantiated object
- * or the full location in
- * called $g_entityListeners (useful for example for adding listeners
- * to entitys from another module's module.inc file. in module.inc files,
- * $listeners can be used to add listeners to an entity.
- * @access private
- */
- function _addListeners()
- {
- global $g_entityListeners;
- if (isset($g_entityListeners[$this->atkentitytype()])) {
- foreach ($g_entityListeners[$this->atkentitytype()] as $listener) {
- if (is_object($listener)) {
- $this->addListener($listener);
- } else {
- if (is_string($listener)) {
- $listenerobj = &Adapto_ClassLoader::create($listener);
- if (is_object($listenerobj)) {
- $this->addListener($listenerobj);
- } else
- Adapto_Util_Debugger::debug("We couldn't find a classname for listener with supposed entitytype: '$listener'");
- } else {
- Adapto_Util_Debugger::debug("Failed to add listener with supposed entitytype: '$listener'");
- }
- }
- }
- }
- }
- /**
- * This function reads meta information from the database and initialises
- * its attributes with the metadata.
- *
- * This method should be called before rendering a form, if you want the
- * sizes of all the inputs to match the fieldlengths from the database.
- */
- function setAttribSizes()
- {
- if ($this->m_attribsizesset)
- return true;
- $db = &$this->getDb();
- $metainfo = $db->tableMeta($this->m_table);
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- if (is_object($p_attrib)) {
- $p_attrib->fetchMeta($metainfo);
- }
- }
- $this->m_attribsizesset = true;
- }
- /**
- * This is the wrapper method for all http requests on an entity.
- *
- * The method looks at the atkaction from the postvars and determines what
- * should be done. If possible, it instantiates actionHandlers for
- * handling the actual action.
- *
- * @param array $postvars The request variables for the entity.
- * @param int $flags Render flags (see class atkPage).
- */
- function dispatch($postvars, $flags = NULL)
- {
- Adapto_Util_Debugger::debug("Adapto_Entity::dispatch()");
- $controller = &atkcontroller::getInstance();
- $controller->setEntity($this);
- return $controller->handleRequest($postvars, $flags);
- }
- /**
- * Render a generic page, with a box, title, stacktrace etc.
- * @param String $title The pagetitle and if $content is a string, also
- * the boxtitle.
- * @param mixed $content The content to display on the page. This can be:
- * - A string which will be the content of a single
- * box on the page.
- * - An associative array of $boxtitle=>$boxcontent
- * pairs. Each pair will be rendered as a seperate
- * box.
- * @return String A complete html page with the desired content.
- */
- function genericPage($title, $content)
- {
- $controller = &atkcontroller::getInstance();
- $controller->setEntity($this);
- return $controller->genericPage($title, $content);
- }
- /**
- * Render a generic action.
- *
- * Renders actionpage.tpl for the desired action. This includes the
- * given block(s) and a pagetrial, but not a box.
- * @param String $action The action for which the page is rendered.
- * @param mixed $blocks Pieces of html content to be rendered. Can be a
- * single string with content, or an array with
- * multiple content blocks.
- * @return String Piece of HTML containing the given blocks and a pagetrail.
- */
- function renderActionPage($action, $blocks = array())
- {
- $controller = &atkcontroller::getInstance();
- $controller->setEntity($this);
- return $controller->renderActionPage($action, $blocks);
- }
- /**
- * Use this function to enable feedback for one or more actions.
- *
- * When feedback is enabled, the action does not immediately return to the
- * previous screen, but first displays a message to the user. (e.g. 'The
- * record has been saved').
- *
- * @param mixed $action The action for which feedback is enabled. You can
- * either pass one action or an array of actions.
- * @param int $statusmask The status(ses) for which feedback is enabled.
- * If for example this is set to ACTION_FAILED,
- * feedback is enabled only when the specified
- * action failed. It is possible to specify more
- * than one status by concatenating with '|'.
- */
- function setFeedback($action, $statusmask)
- {
- if (is_array($action)) {
- for ($i = 0, $_i = count($action); $i < $_i; $i++) {
- $this->m_feedback[$action[$i]] = $statusmask;
- }
- } else {
- $this->m_feedback[$action] = $statusmask;
- }
- }
- /**
- * Get the page instance of the page on which the entity can render output.
- * @return atkPage The page instance.
- */
- function &getPage()
- {
- $page = Adapto_ClassLoader::getInstance("atk.ui.atkpage");
- return $page;
- }
- /**
- * Returns a new page builder instance.
- *
- * @return atkPageBuilder
- */
- public function createPageBuilder()
- {
- return new Adapto_PageBuilder($this);
- }
- /**
- * Redirect the browser to a different location.
- *
- * This is usually used at the end of actions that have no output. An
- * example: when the user clicks 'save and close' in an edit screen, the
- * action 'save' is executed. If the save is succesful, this method is
- * called to redirect the user back to the adminpage.
- * When $config_debug is set to 2, redirects are paused and you can click
- * a link to execute the redirect (useful for debugging the action that
- * called the redirect).
- * Note: this method should be called before any output has been sent to
- * the browser, i.e. before any echo or before the call to
- * atkOutput::outputFlush().
- *
- * @static
- * @param String $location The url to which you want to redirect the user.
- * If ommitted, the call automatically redirects
- * to the previous screen of the user. (one level
- * back on the session stack).
- * @param array $recordOrExit If you pass a record here, the record is passed
- * as 'atkpkret' to the redirected url. Usually it's
- * not necessary to pass this parameter. If you pass a
- * boolean here we assume it's value must be used for
- * the exit parameter.
- * @param boolean $exit Exit script after redirect.
- * @param int $levelskip Number of levels to skip
- */
- function redirect($location = "", $recordOrExit = array(), $exit = false, $levelskip = 1)
- {
- global $g_returnurl;
- Adapto_Util_Debugger::debug("atkentity::redirect()");
- $record = $recordOrExit;
- if (is_bool($recordOrExit)) {
- $record = array();
- $exit = $recordOrExit;
- }
- if ($g_returnurl != "")
- $location = $g_returnurl;
- if ($location == "") {
- $location = atkSessionManager::sessionUrl(atkSelf(), SESSION_BACK, $levelskip);
- }
- if (count($record)) {
- if (isset($this->m_postvars["atkpkret"])) {
- $location .= "&" . $this->m_postvars["atkpkret"] . "=" . rawurlencode($this->primaryKey($record));
- }
- }
- // The actual redirect.
- if (Adapto_Config::getGlobal("debug") >= 2) {
- $debugger = Adapto_ClassLoader::getInstance('atk.utils.Adapto_Util_Debugger::debugger');
- $debugger->setRedirectUrl($location);
- Adapto_Util_Debugger::debug('Non-debug version would have redirected to <a href="' . $location . '">' . $location . '</a>');
- if ($exit) {
- $output = &atkOutput::getInstance();
- $output->outputFlush();
- exit();
- }
- } else {
- Adapto_Util_Debugger::debug('redirecting to: ' . $location);
- if (substr($location, -1) == "&") {
- $location = substr($location, 0, -1);
- }
- if (substr($location, -1) == "?") {
- $location = substr($location, 0, -1);
- }
- global $g_error_msg;
- if (count($g_error_msg) > 0) {
- mailreport();
- }
- header('Location: ' . $location);
- if ($exit) {
- exit();
- }
- }
- }
- /**
- * Parse a set of url vars into a valid record structure.
- *
- * When attributes are posted in a formposting, the values may not be
- * valid yet. After posting, a call to updateRecord should be made to
- * translate the html values into the internal values that the attributes
- * work with.
- * @param array $vars The request variables that were posted from a form.
- * @param array $includes Only fetch the value for these attributes.
- * @param array $excludes Don't fetch the value for these attributes.
- * @param array $postedOnly Only fetch the value for attributes that have really been posted.
- * @return array A valid record.
- */
- function updateRecord($vars = "", $includes = NULL, $excludes = NULL, $postedOnly = false)
- {
- if ($vars == "")
- $vars = $this->m_postvars;
- $record = array();
- foreach (array_keys($this->m_attribList) as $attribname) {
- if ((!is_array($includes) || in_array($attribname, $includes)) && (!is_array($excludes) || !in_array($attribname, $excludes))) {
- $p_attrib = &$this->m_attribList[$attribname];
- if (!$postedOnly || $p_attrib->isPosted($vars)) {
- $record[$p_attrib->fieldName()] = $p_attrib->fetchValue($vars);
- }
- }
- }
- if (isset($vars['atkprimkey']))
- $record["atkprimkey"] = $vars["atkprimkey"];
- return $record;
- }
- /**
- * Update a record with variables from a form posting.
- *
- * Similar to updateRecord(), but here you can pass an existing record
- * (for example loaded from the db), and update it with the the variables
- * from the request. Instead of returning a record, the record you pass
- * is modified directly.
- *
- * @param array $record The record to update.
- * @param array $vars The request variables that were posted from a form.
- */
- function modifyRecord(&$record, $vars)
- {
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- $record[$p_attrib->fieldName()] = $p_attrib->fetchValue($vars);
- }
- }
- /**
- * Get descriptor handler.
- * @return Object descriptor handler
- */
- function &getDescriptorHandler()
- {
- return $this->m_descHandler;
- }
- /**
- * Set descriptor handler.
- * @param Object $handler The descriptor handler.
- */
- function setDescriptorHandler(&$handler)
- {
- $this->m_descHandler = &$handler;
- }
- /**
- * Returns the descriptor template for this entity.
- * @return String The descriptor Template
- */
- function getDescriptorTemplate()
- {
- return $this->m_descTemplate;
- }
- /**
- * Sets the descriptor template for this entity.
- * @param String $template The descriptor template.
- */
- function setDescriptorTemplate($template)
- {
- $this->m_descTemplate = $template;
- }
- /**
- * Retrieve the list of attributes that are used in the descriptor
- * definition.
- * @return array The names of the attributes forming the descriptor.
- */
- function descriptorFields()
- {
- $fields = array();
- // See if entity has a custom descriptor definition.
- if ($this->m_descTemplate != NULL || method_exists($this, "descriptor_def")) {
- if ($this->m_descTemplate != NULL)
- $descriptordef = $this->m_descTemplate;
- else
- $descriptordef = $this->descriptor_def();
- // parse fields from descriptordef
- $parser = new Adapto_StringParser($descriptordef);
- $fields = $parser->getFields();
- // There might be fields that have a '.' in them. These fields are
- // a concatenation of an attributename (probably a relation), and a subfield
- // (a field of the destination entity).
- // The actual field is the one in front of the '.'.
- for ($i = 0, $_i = count($fields); $i < $_i; $i++) {
- $elems = explode(".", $fields[$i]);
- if (count($elems) > 1) {
- // dot found. attribute is the first item.
- $fields[$i] = $elems[0];
- }
- }
- } else {
- // default descriptor.. (default is first attribute of an entity)
- $keys = array_keys($this->m_attribList);
- $fields[0] = $keys[0];
- }
- return $fields;
- }
- /**
- * Determine a descriptor of a record.
- *
- * The descriptor is a string that describes a record for the user. For
- * person records, this may be the firstname and the lastname, for
- * companies it may be the company name plus the city etc.
- * The descriptor is used when displaying records in a dropdown for
- * example, or in the title of editpages, delete confirmations etc.
- *
- * The descriptor method calls a method named descriptor_def() on the entity
- * to retrieve a template for the descriptor (string with attributenames
- * between blockquotes, for example "[lastname], [firstname]".
- *
- * If the entity has no descriptor_def() method, the first attribute of the
- * entity is used as descriptor.
- *
- * Derived classes may override this method to implement custom descriptor
- * logic.
- *
- * @param array $rec The record for which the descriptor is returned.
- * @return String The descriptor for the record.
- */
- function descriptor($rec = "")
- {
- // Descriptor handler is set?
- if ($this->m_descHandler != NULL)
- return $this->m_descHandler->descriptor($rec, $this);
- // Descriptor template is set?
- if ($this->m_descTemplate != NULL) {
- $parser = new Adapto_StringParser($this->m_descTemplate);
- return $parser->parse($rec);
- }
- // See if entity has a custom descriptor definition.
- else if (method_exists($this, "descriptor_def")) {
- $parser = new Adapto_StringParser($this->descriptor_def());
- return $parser->parse($rec);
- } else {
- // default descriptor.. (default is first attribute of an entity)
- $keys = array_keys($this->m_attribList);
- return $rec[$keys[0]];
- }
- }
- /**
- * Sets the lock mode.
- *
- * @param int $lockMode lock mode (atkLock::EXCLUSIVE, atkLock::SHARED)
- */
- public function setLockMode($lockMode)
- {
- $this->m_lockMode = $lockMode;
- }
- /**
- * Returns the lock mode.
- *
- * @return int lock mode (atkLock::EXCLUSIVE, atkLock::SHARED)
- */
- public function getLockMode()
- {
- return $this->m_lockMode;
- }
- /**
- * Validates a record.
- *
- * Validates unique fields, required fields, dataformat etc.
- *
- * @internal This method instantiates the entity's validator object, and
- * delegates validation to that object.
- *
- * @param array $record The record to validate
- * @param String $mode The mode for which validation is performed ('add' or 'update')
- * @param array $ignoreList The list of attributes that should not be
- * validated
- */
- function validate(&$record, $mode, $ignoreList = array())
- {
- $validateObj = &Adapto_ClassLoader::create($this->m_validate_class);
- $validateObj->setEntity($this);
- $validateObj->setRecord($record);
- $validateObj->setIgnoreList($ignoreList);
- $validateObj->setMode($mode);
- return $validateObj->validate();
- }
- /**
- * Add a unique field set.
- *
- * When you add a set of attributes using this method, any combination of
- * values for the attributes should be unique. For example, if you pass
- * array("name", "parent_id"), name does not have to be unique, parent_id
- * does not have to be unique, but the combination should be unique.
- *
- * @param array $fieldArr The list of names of attributes that should be
- * unique in combination.
- */
- function addUniqueFieldset($fieldArr)
- {
- sort($fieldArr);
- if (!in_array($fieldArr, $this->m_uniqueFieldSets))
- $this->m_uniqueFieldSets[] = $fieldArr;
- }
- /**
- * Called by updateDb to load the original record inside the record if the
- * EF_TRACK_CHANGES flag is set.
- *
- * NOTE: this method is made public because it's called from the update handler
- *
- * @param array $record
- * @param array $excludes
- * @param array $includes
- */
- public function trackChangesIfNeeded(&$record, $excludes = '', $includes = '')
- {
- if (!$this->hasFlag(EF_TRACK_CHANGES) || isset($record['atkorgrec']))
- return;
- // We need to add the NO_FILTER flag in case the new values would filter the record.
- $flags = $this->m_flags;
- $this->addFlag(EF_NO_FILTER);
- $record["atkorgrec"] = $this->select()->where($record['atkprimkey'])->excludes($excludes)->includes($includes)->mode('edit')->firstRow();
- // Need to restore the NO_FILTER bit back to its original value.
- $this->m_flags = $flags;
- }
- /**
- * Update a record in the database.
- *
- * The record should already exist in the database, or this method will
- * fail.
- *
- * NOTE: Does not commit your transaction! If you are using a database that uses
- * transactions you will need to call 'atkGetDb()->commit()' manually.
- *
- * @param array $record The record to update in the database.
- * @param bool $exectrigger wether to execute the pre/post update triggers
- * @param array $excludes exclude list (these attribute will *not* be updated)
- * @param array $includes include list (only these attributes will be updated)
- * @return boolean True if succesful, false if not.
- */
- function updateDb(&$record, $exectrigger = true, $excludes = "", $includes = "")
- {
- $db = &$this->getDb();
- $query = &$db->createQuery();
- $query->addTable($this->m_table);
- // The record that must be updated is indicated by 'atkprimkey'
- // (not by atkselector, since the primary key might have
- // changed, so we use the atkprimkey, which is the value before
- // any update happened.)
- if ($record['atkprimkey'] != "") {
- $this->trackChangesIfNeeded($record, $excludes, $includes);
- if ($exectrigger)
- $this->executeTrigger("preUpdate", $record);
- $pk = $record['atkprimkey'];
- $query->addCondition($pk);
- $storelist = array("pre" => array(), "post" => array(), "query" => array());
- foreach (array_keys($this->m_attribList) as $attribname) {
- if ((!is_array($excludes) || !in_array($attribname, $excludes)) && (!is_array($includes) || in_array($attribname, $includes))) {
- $p_attrib = &$this->m_attribList[$attribname];
- if ($p_attrib->needsUpdate($record) || Adapto_in_array($attribname, $includes)) {
- $storemode = $p_attrib->storageType("update");
- if (hasFlag($storemode, PRESTORE))
- $storelist["pre"][] = $attribname;
- if (hasFlag($storemode, POSTSTORE))
- $storelist["post"][] = $attribname;
- if (hasFlag($storemode, ADDTOQUERY))
- $storelist["query"][] = $attribname;
- }
- }
- }
- if (!$this->_storeAttributes($storelist["pre"], $record, "update"))
- 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, "update"); // start at level 1
- }
- if (count($query->m_fields) && !$query->executeUpdate())
- return false;
- if ($this->hasFlag(EF_ML) && $record["atkmlsplit"] == "") {
- $record["atkmlsplit"] = 1;
- $mltool = Adapto_ClassLoader::getInstance("atk.utils.atkmlsplitter");
- $mltool->updateMlRecords($this, $record, "update", $excludes, $includes);
- }
- if (!$this->_storeAttributes($storelist["post"], $record, "update"))
- return false;
- // Now we call a postUpdate function, that can be used to do some processing after the record
- // has been saved.
- if ($exectrigger)
- return $this->executeTrigger("postUpdate", $record);
- else
- return true;
- } else {
- Adapto_Util_Debugger::debug("NOT UPDATING! NO SELECTOR SET!");
- return false;
- }
- return true;
- }
- /**
- * Call the store() method on a list of attributes.
- * @access private
- * @param array $storelist The list of attributes for which the
- * store() method should be called.
- * @param array $record The master record being stored.
- * @param String $mode The storage mode ("add", "copy" or "update")
- * @return boolean True if succesful, false if not.
- */
- function _storeAttributes($storelist, &$record, $mode)
- {
- // store special storage attributes.
- for ($i = 0, $_i = count($storelist); $i < $_i; $i++) {
- $p_attrib = &$this->m_attribList[$storelist[$i]];
- if (!$p_attrib->store($this->getDb(), $record, $mode)) {
- // something went wrong.
- Adapto_Util_Debugger::debug("Store aborted. Attribute '" . $storelist[$i] . "' reported an error.");
- return false;
- }
- }
- return true;
- }
- /**
- * Copy a record in the database.
- *
- * Primarykeys are automatically regenerated for the copied record. Any
- * detail records (onetomanyrelation) are copied too. Refered records
- * manytoonerelation) are not copied.
- *
- * @param array $record The record to copy.
- * @param string $mode The mode we're in (mostly "copy")
- * @return boolean True if succesful, false if not.
- */
- function copyDb(&$record, $mode = "copy")
- {
- // add original record
- $original = $record; // force copy
- $record['atkorgrec'] = $original;
- //notify precopy listeners
- $this->preNotify("precopy", $record);
- // remove primarykey (copied record will get a new primary key)
- unset($record["atkprimkey"]);
- // remove trigger has been executed references
- foreach (array_keys($record) as $key) {
- if (preg_match('/^__executed.*$/', $key))
- unset($record[$key]);
- }
- $this->preCopy($record);
- return $this->addDb($record, true, $mode);
- }
- /**
- * Get the current searchmode.
- *
- * @return mixed If there is one searchmode set for all attributes, this
- * method returns a string. If there are searchmodes per
- * attribute, an array of strings is returned.
- */
- function getSearchMode()
- {
- //The searchmode of an index should be used only once, therefore it uses
- // atksinglesearchmode instead of atksearchmode.
- if (isset($this->m_postvars["atksinglesearchmode"])) {
- return $this->m_postvars["atksinglesearchmode"];
- } else if (isset($this->m_postvars["atksearchmode"])) {
- return $this->m_postvars["atksearchmode"];
- }
- return Adapto_Config::getGlobal("search_defaultmode");
- }
- /**
- * Set some default for the selector.
- *
- * @param atkSelector $selector selector
- * @param string $condition condition
- * @param array $params condition bind parameters
- */
- protected function _initSelector(atkSelector $selector, $condition = null, $params = array())
- {
- $selector->orderBy($this->getOrder());
- $selector->ignoreDefaultFilters($this->hasFlag(EF_NO_FILTER));
- $selector->ignorePostvars(atkReadOptimizer());
- if ($condition != null) {
- $selector->where($condition, $params);
- }
- }
- /**
- * Retrieve records from the database using a handy helper class.
- *
- * @param string $condition condition
- * @param array $params condition bind parameters
- *
- * @return atkSelector
- */
- public function select($condition = null, array $params = array())
- {
- $class = 'atkselector';
- if (method_exists($this, 'selectDb') || method_exists($this, 'countDb')) {
- $class = 'atkcompatselector';
- } else if ($this->hasFlag(EF_ML)) {
- $class = 'atkmlselector';
- }
- $selector = Adapto_ClassLoader::create('atk.utils.' . $class, $this);
- $this->_initSelector($selector, $condition, $params);
- return $selector;
- }
- /**
- * Returns a record (array) as identified by a primary key (usually an "id" column),
- * including applicable relations.
- *
- * @param int $pk primary key identifying the record
- * @return array the associated record, or null if no such record exists
- */
- public function fetchByPk($pk)
- {
- return $this->select($this->getTable() . "." . $this->primaryKeyField() . '= ?', array($pk))->firstRow();
- }
- /**
- * Add this entity to an existing query.
- *
- * Framework method, it should not be necessary to call this method
- * directly.
- * This method is used when adding the entire entity to an existing
- * query, as part of a join.
- * @todo The allfields parameter is too inflexible.
- * @param atkQuery $query The query statement
- * @param String $alias The aliasprefix to use for fields from this entity
- * @param int $level The recursion level.
- * @param boolean $allfields If set to true, all fields from the entity are
- * added to the query. If set to false, only
- * the primary key and fields from the desriptor
- * are added.
- * @param string $mode The mode we're in
- * @param array $includes List of fields that should be included
- */
- function addToQuery(&$query, $alias = "", $level = 0, $allfields = false, $mode = "select", $includes = array())
- {
- if ($level >= 4)
- return;
- $usefieldalias = false;
- if ($alias == "") {
- $alias = $this->m_table;
- } else {
- $usefieldalias = true;
- }
- // If allfields is set, we load the entire record.. otherwise, we only
- // load the important fields (descriptor and primary key fields)
- // this is mainly used by onetoonerelation.
- if ($allfields) {
- $usedFields = array_keys($this->m_attribList);
- } else {
- $usedFields = Adapto_array_merge($this->descriptorFields(), $this->m_primaryKey, $includes);
- foreach (array_keys($this->m_attribList) as $name)
- if (is_object($this->m_attribList[$name]) && $this->m_attribList[$name]->hasFlag(AF_FORCE_LOAD))
- $usedFields[] = $name;
- $usedFields = array_unique($usedFields);
- }
- foreach ($usedFields as $usedfield) {
- list($attribname) = explode(".", $usedfield);
- $p_attrib = &$this->m_attribList[$attribname];
- if (is_object($p_attrib)) {
- $loadmode = $p_attrib->loadType("");
- if ($loadmode && hasFlag($loadmode, ADDTOQUERY)) {
- if ($usefieldalias)
- $fieldaliasprefix = $alias . "_AE_";
- $dummy = array();
- $p_attrib->addToQuery($query, $alias, $fieldaliasprefix, $dummy, $level, $mode);
- }
- } else
- Adapto_Util_Debugger::debug("$attribname is not an object?! Check your descriptor_def for non-existant fields");
- }
- if ($this->hasFlag(EF_ML)) {
- $mltool = Adapto_ClassLoader::getInstance("atk.utils.atkmlsplitter");
- $mltool->addMlCondition($query, $this, $mode, $alias);
- }
- }
- /**
- * Get search condition for this entity.
- *
- * @param atkQuery $query
- * @param string $table
- * @param string $alias
- * @param string $value
- * @param string $searchmode
- * @return string The search condition
- */
- function getSearchCondition(&$query, $table, $alias, $value, $searchmode)
- {
- $usefieldalias = false;
- if ($alias == "") {
- $alias = $this->m_table;
- } else {
- $usefieldalias = true;
- }
- $searchConditions = array();
- $attribs = $this->descriptorFields();
- array_unique($attribs);
- foreach ($attribs as $field) {
- $p_attrib = &$this->getAttribute($field);
- if (!is_object($p_attrib))
- continue;
- if ($usefieldalias)
- $fieldaliasprefix = $alias . "_AE_";
- // check if the entity has a searchcondition method defined for this attr
- $methodName = $field . '_searchcondition';
- if (method_exists($this, $methodName)) {
- $searchCondition = $this->$methodName($query, $table, $value, $searchmode);
- if ($searchCondition != "") {
- $searchConditions[] = $searchCondition;
- }
- } else {
- // checking for the getSearchCondition for backwards compatibility
- if (method_exists($p_attrib, "getSearchCondition")) {
- $attribsearchmode = $searchmode;
- if (is_array($searchmode)) {
- $attribsearchmode = $attribsearchmode[$p_attrib->m_name];
- }
- Adapto_Util_Debugger::debug("getSearchCondition: $table - $fieldaliasprefix");
- $searchCondition = $p_attrib->getSearchCondition($query, $table, $value, $searchmode, $fieldaliasprefix);
- if ($searchCondition != "")
- $searchConditions[] = $searchCondition;
- } else {
- // if the attrib can't return it's searchcondition, we'll just add it to the query
- // and hope for the best
- $p_attrib->searchCondition($query, $table, $value, $searchmode, $fieldaliasprefix);
- }
- }
- }
- if (count($searchConditions)) {
- return "(" . implode(" OR ", $searchConditions) . ")";
- } else {
- return "";
- }
- }
- /**
- * 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 'atkGetDb()->commit()' manually.
- *
- * @param array $record The record to save.
- * @param boolean $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 boolean True if succesful, false if not.
- */
- function addDb(&$record, $exectrigger = true, $mode = "add", $excludelist = array())
- {
- if ($exectrigger)
- if (!$this->executeTrigger("preAdd", $record, $mode))
- return throw new Adapto_Exception("preAdd() failed!");
- $db = &$this->getDb();
- $query = &$db->createQuery();
- $storelist = array("pre" => array(), "post" => array(), "query" => array());
- $query->addTable($this->m_table);
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- if (!Adapto_in_array($attribname, $excludelist) && ($mode != "add" || $p_attrib->needsInsert($record))) {
- $storemode = $p_attrib->storageType($mode);
- if (hasFlag($storemode, PRESTORE))
- $storelist["pre"][] = $attribname;
- if (hasFlag($storemode, POSTSTORE))
- $storelist["post"][] = $attribname;
- if (hasFlag($storemode, 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()) {
- Adapto_Util_Debugger::debug("executeInsert failed..");
- return false;
- }
- // new primary key
- $record["atkprimkey"] = $this->primaryKey($record);
- if ($this->hasFlag(EF_ML) && $record["atkmlsplit"] == "") {
- $record["atkmlsplit"] = 1;
- $mltool = Adapto_ClassLoader::getInstance("atk.utils.atkmlsplitter");
- $mltool->updateMlRecords($this, $record, $mode);
- }
- if (!$this->_storeAttributes($storelist["post"], $record, $mode)) {
- Adapto_Util_Debugger::debug("_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;
- }
- /**
- * Executes a trigger on a add,update or delete action
- *
- * To prevent triggers from executing twice, the method stores an
- * indication in the record when a trigger is executed.
- * ('__executed<triggername>')
- *
- * @param string $trigger function, such as 'postUpdate'
- * @param array $record record on which action is performed
- * @param string $mode mode like add or update
- * @return bool true on case of success or when the trigger isn't returning anything (assumes success)
- */
- function executeTrigger($trigger, &$record, $mode = null)
- {
- if (!isset($record["__executed" . $trigger])) {
- $record["__executed" . $trigger] = true;
- $return = $this->$trigger($record, $mode);
- if ($return === NULL) {
- Adapto_Util_Debugger::debug("Undefined return: " . $this->atkEntityType() . ".$trigger doesn't return anything, it should return a boolean!", DEBUG_WARNING);
- $return = true;
- }
- if (!$return) {
- Adapto_Util_Debugger::debug($this->atkEntityType() . ".$trigger failed!");
- return false;
- }
- for ($i = 0, $_i = count($this->m_triggerListeners); $i < $_i; $i++) {
- $listener = &$this->m_triggerListeners[$i];
- $return = $listener->notify($trigger, $record, $mode);
- if ($return === NULL) {
- Adapto_Util_Debugger::debug(
- "Undefined return: " . $this->atkEntityType() . ", " . get_class($listener)
- . ".notify('$trigger', ...) doesn't return anything, it should return a boolean!", DEBUG_WARNING);
- $return = true;
- }
- if (!$return) {
- Adapto_Util_Debugger::debug($this->atkEntityType() . ", " . get_class($listener) . ".notify('$trigger', ...) failed!");
- return false;
- }
- }
- }
- return true;
- }
- /**
- * Delete record(s) from the database.
- *
- * After deletion, the postDel() trigger in the entity method is called, and
- * on any attribute that has the AF_CASCADE_DELETE flag set, the delete()
- * method is invoked.
- *
- * NOTE: Does not commit your transaction! If you are using a database that uses
- * transactions you will need to call 'atkGetDb()->commit()' manually.
- *
- * @todo There's a discrepancy between updateDb, addDb and deleteDb:
- * There should be a deleteDb which accepts a record, instead
- * of a selector.
- * @param String $selector SQL expression used as where-clause that
- * indicates which records to delete.
- * @param bool $exectrigger wether to execute the pre/post triggers
- * @param bool $failwhenempty determine whether to throw an error if there is nothing to delete
- * @returns boolean True if successful, false if not.
- */
- function deleteDb($selector, $exectrigger = true, $failwhenempty = false)
- {
- $recordset = $this->selectDb($selector, "", "", "", "", "delete");
- // nothing to delete, throw an error (determined by $failwhenempty)!
- if (count($recordset) == 0) {
- atkwarning($this->atkentitytype() . "->deleteDb($selector): 0 records found, not deleting anything.");
- return !$failwhenempty;
- }
- if ($exectrigger) {
- for ($i = 0, $_i = count($recordset); $i < $_i; $i++) {
- $return = $this->executeTrigger("preDelete", $recordset[$i]);
- if (!$return)
- return false;
- }
- }
- if (count($this->m_cascadingAttribs) > 0) {
- for ($i = 0, $_i = count($recordset); $i < $_i; $i++) {
- for ($j = 0, $_j = count($this->m_cascadingAttribs); $j < $_j; $j++) {
- $p_attrib = $this->m_attribList[$this->m_cascadingAttribs[$j]];
- if (isset($recordset[$i][$this->m_cascadingAttribs[$j]]) && !$p_attrib->isEmpty($recordset[$i])) {
- if (!$p_attrib->delete($recordset[$i])) {
- // error
- return false;
- }
- }
- }
- }
- }
- $query = $this->getDb()->createQuery();
- $query->addTable($this->m_table);
- $query->addCondition($selector);
- if ($query->executeDelete()) {
- if ($exectrigger) {
- for ($i = 0, $_i = count($recordset); $i < $_i; $i++) {
- $return = ($this->executeTrigger("postDel", $recordset[$i]) && $this->executeTrigger("postDelete", $recordset[$i]));
- if (!$return)
- return false;
- }
- }
- return true;
- } else {
- return false;
- }
- }
- /**
- * Function that is called by the framework, right after a new record has
- * been saved to the database.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to do something special after you saved a
- * record.
- *
- * @param array $record The record that has just been saved.
- * @param String $mode The 'mode' indicates whether the added record was a
- * completely new record ("add") or a copy ("copy").
- * @return boolean True if succesful, false if not.
- */
- function postAdd($record, $mode = "add")
- {
- // Do nothing
- return true;
- }
- /**
- * Function that is called by the framework, just before a new record will
- * be saved to the database.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to modify the record just before it will
- * be saved.
- *
- * @param array $record The record that will be saved to the database.
- */
- function preAdd(&$record)
- {
- // Do nothing
- return true;
- }
- /**
- * Function that is called by the framework, right after an existing
- * record has been updated in the database.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to do something special after the record is
- * updated.
- *
- * If the EF_TRACK_CHANGES flag is present for the entity, both the new
- * and the original record are passed to this method. The original
- * record is stored in the new record, in $record["atkorgrec"].
- *
- * @param array $record The record that has just been updated in the
- * database.
- * @return boolean True if succesful, false if not.
- */
- function postUpdate($record)
- {
- // Do nothing
- return true;
- }
- /**
- * Function that is called by the framework, just before an existing
- * record will be saved to the database.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to modify the record just before it will
- * be saved.
- *
- * @param array $record The record that will be updated in the database.
- * @return bool Wether or not we succeeded in what we wanted to do.
- */
- function preUpdate(&$record)
- {
- // Do nothing
- return true;
- }
- /**
- * Function that is called by the framework, right before a record will be
- * deleted. Should this method return false the deleting will halt.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to do something special after a record is
- * deleted.
- *
- * If this function returns false the delete action will not continue.
- *
- * @param array $record The record that will be deleted.
- */
- function preDelete($record)
- {
- return true;
- }
- /**
- * Function that is called by the framework, right after a record has been
- * deleted.
- *
- * This function does essentially nothing, but it can be overriden in
- * derived classes if you want to do something special after a record is
- * deleted.
- *
- * @param array $record The record that has just been deleted.
- * @return bool Wether or not we succeeded in what we wanted to do.
- */
- function postDelete($record)
- {
- // Do nothing
- return true;
- }
- /**
- * Function that is called by the framework, right before a copied record
- * is stored to the database.
- *
- * This function does nothing, but it can be overriden in derived classes
- * if you want to do some processing on a record before it is
- * being copied.
- * Typical usage would be: Suppose you have a field named 'title' in a
- * record. In the preCopy method, you could change the title field of the
- * record to 'Copy of ..', so the user can distinguish between the
- * original and the copy.
- *
- * @param array $record A reference to the copied record. You can change the
- * contents of the record, since it is passed by
- * reference.
- */
- function preCopy(&$record)
- {
- }
- /**
- * Function that is called for each record in a recordlist, to determine
- * what actions may be performed on the record.
- *
- * This function does nothing, but it can be overriden in derived classes,
- * to make custom actions for certain records.
- * The array with actions (edit, delete, etc.) is passed to the function
- * and can be modified.
- * To create a new action, just do $actions["new_action"]=$url;
- * in the derived function.
- * To disable existing actions, for example the edit action, for a record,
- * use: unset($actions["edit"]);
- *
- * @param array $record The record for which the actions need to be
- * determined.
- * @param array &$actions Reference to an array with the already defined
- * actions. This is an associative array with the action
- * identifier as key, and an url as value. Actions can be
- * removed from it, or added to the array.
- * @param array &$mraactions List of multirecordactions that are supported for
- * the passed record.
- */
- function recordActions($record, &$actions, &$mraactions)
- {
- // Do nothing.
- }
- /**
- * Registers a function/method that is called for each record in a recordlist,
- * to determine what actions may be performed on the record.
- *
- * The callback receives the record, a reference to the record actions and
- * a reference to the MRA actions as arguments.
- *
- */
- public function registerRecordActionsCallback($callback)
- {
- if (is_callable($callback, false, $callableName)) {
- if (is_array($callback)) {
- if (!method_exists($callback[0], $callback[1])) {
- throw new Adapto_Exception("The registered record actions callback method '$callableName' doesn't exist");
- return;
- }
- }
- $this->m_recordActionsCallbacks[] = $callback;
- } else {
- throw new Adapto_Exception("The registered record actions callback '$callableName' is not callable");
- return;
- }
- }
- /**
- * Function that is called for each record in a recordlist, to determine
- * what actions may be performed on the record.
- *
- * This function is a framework method and should not be called directly.
- * It should not be overridden either.
- *
- * To change the record actions, either override Adapto_Entity::recordActions() in you entity,
- * or call Adapto_Entity::registerRecordActionsCallback to register a callback.
- *
- * @param array $record The record for which the actions need to be
- * determined.
- * @param array &$actions Reference to an array with the already defined
- * actions. This is an associative array with the action
- * identifier as key, and an url as value. Actions can be
- * removed from it, or added to the array.
- * @param array &$mraactions List of multirecordactions that are supported for
- * the passed record.
- * @return void;
- */
- public function collectRecordActions($record, &$actions, &$mraactions)
- {
- $this->recordActions($record, $actions, $mraactions);
- foreach ($this->m_recordActionsCallbacks as $callback) {
- call_user_func_array($callback, array($record, &$actions, &$mraactions));
- }
- }
- /**
- * Retrieve the security key of an action.
- *
- * Returns the privilege required to perform a certain action.
- * Usually, the privilege and the action are equal, but in m_securityMap,
- * aliasses may be defined.
- * @param String $action The action for which you want to determine the
- * privilege.
- * @return String The security privilege required to perform the action.
- */
- function securityKey($action)
- {
- if (!isset($this->m_securityMap[$action]))
- return $action;
- return $this->m_securityMap[$action];
- }
- /**
- * Returns the type of this entity. (This is *not* the full ATK entity type;
- * see atkEntityType() for the full entity type.)
- *
- * @return string type
- */
- public function getType()
- {
- return $this->m_type;
- }
- /**
- * Returns the module for this entity.
- *
- * @return string entity
- */
- public function getModule()
- {
- return $this->m_module;
- }
- /**
- * Returns the current action for this entity.
- *
- * @return string action
- */
- public function getAction()
- {
- return $this->m_action;
- }
- /**
- * Get the full atkentitytype of this entity (module.entitytype notation). This is sometimes
- * referred to as the entity name (or entityname) or entity string.
- *
- * @return String The atkentitytype of the entity.
- */
- function atkEntityType()
- {
- return (empty($this->m_module) ? "" : $this->m_module . ".") . $this->m_type;
- }
- /**
- * This function determines if the user has the privilege to perform a certain
- * action on the entity.
- *
- * @param String $action The action to be checked.
- * @param array $record The record on which the action is to be performed.
- * The standard implementation ignores this
- * parameter, but derived classes may override this
- * method to implement their own record based
- * security policy. Keep in mind that a record is not
- * passed in every occasion. The method is called
- * several times without a record, to just see if
- * the user has the privilege for the action
- * regardless of the record being processed.
- *
- * @return boolean True if the action may be performed, false if not.
- */
- function allowed($action, $record = "")
- {
- $secMgr = &atkGetSecurityManager();
- $alias = $this->atkEntityType();
- $this->resolveEntityTypeAndAction($alias, $action);
- return ($this->hasFlag(EF_NO_SECURITY) || in_array($action, $this->m_unsecuredActions) || $secMgr->allowed($alias, $action)
- || (isset($this->m_securityImplied[$action]) && $secMgr->allowed($alias, $this->m_securityImplied[$action])));
- }
- /**
- * Resolves a possible entity / action alias for the given entity / action.
- * The given entity alias and action are updated depending on
- * the found mapping.
- *
- * @param string $alias entity type
- * @param string $action action name
- */
- function resolveEntityTypeAndAction(&$alias, &$action)
- {
- if (!empty($this->m_securityAlias)) {
- $alias = $this->m_securityAlias;
- }
- // Resolve action
- $action = $this->securityKey($action);
- // If action contains a dot, it's a complete entityname.action or modulename.entityname.action alias.
- // Else, it's only an action alias, and we use the default entity.
- if (strpos($action, ".") !== false) {
- $complete = explode(".", $action);
- if (count($complete) == 3) {
- $alias = $complete[0] . "." . $complete[1];
- $action = $complete[2];
- } else {
- $alias = $this->m_module . "." . $complete[0];
- $action = $complete[1];
- }
- }
- }
- /**
- * Set the security alias of an entity.
- *
- * By default an entity has it's own set of privileges. With this method,
- * the privileges of another entity can be used. This is useful when you
- * have a master/detail relationship, and people may manipulate details
- * when they have privileges on the master entity.
- * Note: When setting an alias for the entity, the entity no longer has to
- * have a registerEntity call in the getEntitys method in module.inc.
- *
- * @param String $alias The entity (module.entityname) to set as a security
- * alias for this entity.
- */
- function setSecurityAlias($alias)
- {
- $this->m_securityAlias = $alias;
- }
- /**
- * Returns the entity's security alias (if set).
- *
- * @return string security alias
- */
- function getSecurityAlias()
- {
- return $this->m_securityAlias;
- }
- /**
- * Disable privilege checking for an action.
- *
- * This method disables privilege checks for the specified action, for the
- * duration of the current http request.
- * @param String $action The name of the action for which security is
- * disabled.
- */
- function addAllowedAction($action)
- {
- if (is_array($action)) {
- $this->m_unsecuredActions = Adapto_array_merge($this->m_unsecuredActions, $action);
- } else {
- $this->m_unsecuredActions[] = $action;
- }
- }
- /**
- * Retrieve help link for the current entity.
- * @return String Complete html link, linking to the help popup.
- */
- function getHelp()
- {
- $res = array();
- $res["helpurl"] = $this->helpUrl();
- if ($res["helpurl"] != "") {
- $page = &$this->getPage();
- $page->register_script(Adapto_Config::getGlobal("atkroot") . "atk/javascript/newwindow.js");
- $res["helplabel"] = atktext("help");
- $res["helplink"] = '<a href="' . $res["helpurl"] . '">' . $res["helplabel"] . '</a>';
- }
- return $res;
- }
- /**
- * Get img tag for lock icon.
- * @param boolean $lockstatus True if the record is locked, false if not.
- * @return String HTML image tag with the correct lock icon.
- */
- function getLockStatusIcon($lockstatus)
- {
- if ($lockstatus) {
- $icon = atkTheme::getInstance()->iconPath('lock_' . $this->getLockMode(), 'lock', $this->m_module);
- return '<img src="' . $icon . '" border="0" name="_lock_">';
- }
- return "";
- }
- /**
- * Get the help url for this entity.
- *
- * Retrieves the url of the help popup, if there is help available for
- * this entity.
- * @return String The help url, or an empty string if help is not
- * available.
- */
- function helpUrl()
- {
- $language = Adapto_Config::getGlobal("language");
- $entity = $this->m_type;
- $file = moduleDir($this->m_module) . "help/" . $language . "/help." . $entity . ".inc";
- $helpmodule = "";
- if (file_exists($file)) {
- $helpmodule = $this->m_module;
- } else {
- // bwc
- $file = "help/" . $language . "/help." . $entity . ".inc";
- if (!file_exists($file)) {
- // no help available..
- return "";
- }
- }
- $name = atktext("help");
- return atkPopup('atk/popups/help.inc', 'entity=' . $entity . ($helpmodule != "" ? "&module=" . $helpmodule : ""), $name, 650, 650, 'yes', 'no');
- }
- /**
- * Invoke the handler for an action.
- *
- * If there is a known registered external handler method for the
- * specified action, this method will call it. If there is no custom
- * external handler, the atkActionHandler object is determined and the
- * actionis invoked on the actionhandler.
- * @param String $action the entity action
- */
- function callHandler($action)
- {
- Adapto_Util_Debugger::debug("Adapto_Entity::callHandler(); action: " . $action);
- $handler = &atkGetEntityHandler($this->m_type, $action);
- // handler function
- if ($handler != NULL && is_string($handler) && function_exists($handler)) {
- Adapto_Util_Debugger::debug("Adapto_Entity::callHandler: Calling external handler function for '" . $action . "'");
- $handler($this, $action);
- }
- // handler object
- elseif ($handler != NULL && $handler instanceof atkActionHandler) {
- Adapto_Util_Debugger::debug("Adapto_Entity::callHandler:Using override/existing atkActionHandler " . get_class($handler) . " class for '" . $action . "'");
- $handler->handle($this, $action, $this->m_postvars);
- }
- // no (valid) handler
- else {
- Adapto_Util_Debugger::debug("Calling default handler function for '" . $action . "'");
- $this->m_handler = &$this->getHandler($action);
- $this->m_handler->handle($this, $action, $this->m_postvars);
- }
- }
- /**
- * Get the atkActionHandler object for a certain action.
- *
- * The default implementation returns a default handler for the action,
- * but derived classes may override this to return a custom handler.
- * @param String $action The action for which the handler is retrieved.
- * @return atkActionHandler The action handler.
- */
- function &getHandler($action)
- {
- Adapto_Util_Debugger::debug("Adapto_Entity::getHandler(); action: " . $action);
- // for backwards compatibility we first check if a handler exists without using the module name
- $handler = &atkGetEntityHandler($this->m_type, $action);
- // then check if a handler exists registered including the module name
- if ($handler == NULL) {
- $handler = &atkGetEntityHandler($this->atkEntityType(), $action);
- }
- // The entity handler might return a class, then we need to instantiate the handler
- if (is_string($handler) && !function_exists($handler) && atkimport($handler)) {
- $handler = &Adapto_ClassLoader::create($handler);
- }
- // The entity handler might return a function as entityhandler. We cannot
- // return a function so we ignore this option.
- // @todo why not implement a base atkfunctionactionhandler which just calls the given function?
- // this would probably only work fine when using PHP5, but's better then nothing?
- // or why support functions at all?!
- // handler object
- if ($handler != NULL && is_subclass_of($handler, "atkActionHandler")) {
- Adapto_Util_Debugger::debug("Adapto_Entity::getHandler: Using existing atkActionHandler " . get_class($handler) . " class for '" . $action . "'");
- $handler->setEntity($this);
- $handler->setAction($action);
- } else {
- $handler = &atkActionHandler::getDefaultHandler($action);
- $handler->setEntity($this);
- $handler->setPostvars($this->m_postvars);
- $handler->setAction($action);
- //If we use a default handler we need to register it to this entity
- //because we might call it a second time.
- Adapto_Util_Debugger::debug("Adapto_Entity::getHandler: Register default atkActionHandler for " . $this->m_type . " action: '" . $action . "'");
- atkRegisterEntityHandler($this->m_type, $action, $handler);
- }
- return $handler;
- }
- /**
- * Sets the search action.
- *
- * The search action is the action that will be performed
- * if only a single record is found after doing a certain search query.
- *
- * You can specify more then 1 action. If the user isn't allowed to
- * execute the 1st action, the 2nd action will be used, etc. If you
- * want to pass multiple actions, just pass multiple params (function
- * has a variable number of arguments).
- * @todo Using func_get_args is non-standard. It's cleaner to accept an
- * array.
- * @param String $action The name of the action.
- */
- function setSearchAction()
- {
- $this->m_search_action = func_get_args();
- }
- /**
- * This function resorts the attribIndexList and attribList.
- *
- * This is necessary if you add attributes *after* init() is already
- * called, and you set an order for those attributes.
- */
- function attribSort()
- {
- usort($this->m_attribIndexList, array("atkentity", "attrib_cmp"));
- // after sorting we need to update the attribute indices
- $attrs = array();
- foreach ($this->m_attribIndexList as $index => $info) {
- $attr = $this->getAttribute($info['name']);
- $attr->m_index = $index;
- $attrs[$info['name']] = $attr;
- }
- $this->m_attribList = $attrs;
- }
- /**
- * Search all records for the occurance of a certain expression.
- *
- * This function searches in all fields that are not AF_HIDE_SEARCH, for
- * a certain expression (substring match). The search performed is an
- * 'or' search. If any of the fields contains the expression, the record
- * is added to the resultset.\
- *
- * Currently, searchDb only searches those attributes that are of type
- * string or text.
- *
- * @param String $expression The keyword to search for.
- * @param string $searchmethod
- * @return array Set of records matching the keyword.
- */
- function searchDb($expression, $searchmethod = "OR")
- {
- // Set default searchmethod to OR (put it in m_postvars, because selectDb
- // will use m_postvars to built it's search conditions).
- $this->m_postvars['atksearchmethod'] = $searchmethod;
- // To perform the search, we fill atksearch, so selectDb automatically
- // searches. Because an atksearch variable may have already been set,
- // we save it to restore it after the query.
- $orgsearch = atkArrayNvl($this->m_postvars, "atksearch");
- // Built whereclause.
- foreach (array_keys($this->m_attribList) as $attribname) {
- $p_attrib = &$this->m_attribList[$attribname];
- // Only search in fields that aren't explicitly hidden from search
- if (!$p_attrib->hasFlag(AF_HIDE_SEARCH) && (in_array($p_attrib->dbFieldType(), array("string", "text")) || $p_attrib->hasFlag(AF_SEARCHABLE))) {
- $this->m_postvars['atksearch'][$attribname] = $expression;
- }
- }
- // We load records in admin mode, se we are certain that all fields are added.
- $recs = $this->selectDb("", "", "", $this->m_listExcludes, "", "admin");
- // Restore original atksearch
- $this->m_postvars['atksearch'] = $orgsearch;
- return $recs;
- }
- /**
- * Determine the url for the feedbackpage.
- *
- * Output is dependent on the feedback configuration. If feedback is not
- * enabled for the action, this method returns an empty string, so the
- * result of this method can be passed directly to the redirect() method
- * after completing the action.
- *
- * The $record parameter is ignored by the default implementation, but
- * derived classes may override this method to perform record-specific
- * feedback.
- * @param String $action The action that was performed
- * @param int $status The status of the action.
- * @param array $record The record on which the action was performed.
- * @param String $message An optional message to pass to the feedbackpage,
- * for example to explain the reason why an action
- * failed.
- * @param int $levelskip Number of levels to skip
- * @return String The feedback url.
- */
- function feedbackUrl($action, $status, $record = "", $message = "", $levelskip = null)
- {
- $controller = &atkcontroller::getInstance();
- $controller->setEntity($this);
- return $controller->feedbackUrl($action, $status, $record, $message, $levelskip);
- }
- /**
- * Validates if a filter is valid for this entity.
- *
- * A filter is considered valid if it doesn't contain any fields that are
- * not part of the entity.
- *
- * Why isn't this used more often???
- *
- * @param String $filter The filter expression to validate
- * @returns String Returns $filter if the filter is valid or a empty
- * string if not.
- */
- public function validateFilter($filter)
- {
- // If the filter is blank
- // Or we can't find the target field
- if ($filter === '') {
- return $filter;
- }
- $targetField = $this->getFirstTargetFieldFromFilterSql($filter);
- if (!$targetField) {
- atkwarning($this->atkEntityType() . '->' . __FUNCTION__ . "($filter): Disallowed because it has no target field");
- // Don't allow the filter
- return '';
- }
- // Separate the table name from the column name
- $targetDetails = explode('.', $targetField);
- $targetTable = $this->m_table;
- $targetColumn = array_pop($targetDetails);
- // If no table is specified then it is implied that it is the current table
- if (count($targetDetails) == 1) {
- $targetTable = array_pop($targetDetails);
- }
- // If the table isn't $this one
- if (strtolower(trim($targetTable)) !== strtolower($this->m_table) && !($this->getAttribute($targetTable) instanceof atkManyToOneRelation)) {
- atkwarning(
- $this->atkEntityType() . '->' . __FUNCTION__ . "($filter): Disallowed because " . strtolower(trim($targetTable)) . " !== "
- . strtolower($this->m_table) . ' and not a valid many-to-one relation.');
- return '';
- }
- // Or the column doesn't belong to $this
- if (!($this->getAttribute($targetTable) instanceof atkManyToOneRelation) && !in_array($targetColumn, array_keys($this->m_attribList))) {
- atkwarning($this->atkEntityType() . '->' . __FUNCTION__ . "($filter): Disallowed because target column $targetColumn isn't in entity");
- return "";
- }
- return $filter;
- }
- /**
- * Get the targeted field and table from a snippet of filter string sql
- *
- * @param string $sql is the filter string sql
- * @return string the target table and field or an empty string
- */
- function getFirstTargetFieldFromFilterSql($sql)
- {
- // All standard SQL operators
- $sqloperators = array('=', '<>', '>', '<', '>=', '<=', 'BETWEEN', 'LIKE', 'IN', 'IS', 'NOT IN', '&');
- $sqlOperatorsString = implode('|', array_map('preg_quote', $sqloperators));
- $matches = array();
- preg_match("/^(\w.+?)\s*({$sqlOperatorsString})/", str_replace('`', '', trim($sql)), $matches);
- if (count($matches) != 3)
- return '';
- return $matches[1];
- }
- /**
- * Add a stylesheet to the page.
- *
- * The theme engine is used to determine the path, and load the correct
- * stylesheet.
- * @param String $style The filename of the stylesheet (without path).
- */
- function addStyle($style)
- {
- $theme = Adapto_ClassLoader::getInstance("Adapto_Ui_Theme");
- $page = &$this->getPage();
- $page->register_style($theme->stylePath($style));
- }
- /**
- * Sets numbering of the attributes to begin with the number that was passed to it,
- * or defaults to 1.
- * @param mixed $number the number that the first attribute begins with
- */
- function setNumbering($number = 1)
- {
- $this->m_numbering = $number;
- }
- /**
- * Gets the numbering of the attributes
- * @return mixed the number whith which the numbering starts
- */
- function getNumbering()
- {
- return $this->m_numbering;
- }
- /**
- * Set the security of one or more actions action the same as other actions.
- * If $mapped is empty $action has to be an array. The key would be used as action and would be mapped to the value.
- * If $mapped is not empty $action kan be a string containing one action of an array with one or more action. In both
- * cases al actions would be mapped to $mappped
- * @param Mixed $action The action that has to be mapped
- * @param String $mapped The action on witch $action has to be mapped
- */
- function addSecurityMap($action, $mapped = "")
- {
- if ($mapped != "")
- if (!is_array($action)) {
- $this->m_securityMap[$action] = $mapped;
- $this->changeMapping($action, $mapped);
- } else {
- foreach ($action as $value) {
- $this->m_securityMap[$value] = $mapped;
- $this->changeMapping($value, $mapped);
- }
- }
- else if (is_array($action))
- foreach ($action as $key => $value) {
- $this->m_securityMap[$key] = $value;
- $this->changeMapping($key, $value);
- }
- }
- /**
- * change the securitymap that already exist. Where actions are mapped on $oldmapped change it by $newmapped
- * @param string $oldmapped the old value
- * @param string $newmapped the new value with replace the old one
- */
- function changeMapping($oldmapped, $newmapped)
- {
- foreach ($this->m_securityMap as $key => $value) {
- if ($value == $oldmapped)
- $this->m_securityMap[$key] = $newmapped;
- }
- }
- /**
- * Add an atkActionListener to the entity.
- *
- * @param atkActionListener $listener
- */
- function addListener(&$listener)
- {
- $listener->setEntity($this);
- if (is_a($listener, 'atkActionListener')) {
- $this->m_actionListeners[] = &$listener;
- } else if (is_a($listener, 'atkTriggerListener')) {
- $this->m_triggerListeners[] = &$listener;
- } else {
- Adapto_Util_Debugger::debug('Adapto_Entity::addListener: Unknown listener base class ' . get_class($listener));
- }
- }
- /**
- * Notify all listeners of the occurance of a certain action.
- *
- * @param String $action The action that occurred
- * @param array $record The record on which the action was performed
- */
- function notify($action, $record)
- {
- for ($i = 0, $_i = count($this->m_actionListeners); $i < $_i; $i++) {
- $this->m_actionListeners[$i]->notify($action, $record);
- }
- }
- /**
- * Notify all listeners in advance of the occurance of a certain action.
- *
- * @param String $action The action that will occur
- * @param array $record The record on which the action will be performed
- */
- function preNotify($action, &$record)
- {
- for ($i = 0, $_i = count($this->m_actionListeners); $i < $_i; $i++) {
- $this->m_actionListeners[$i]->preNotify($action, $record);
- }
- }
- /**
- * Get the column configuration object
- *
- * @param string $id optional column config id
- * @param boolean $forceNew force new instance?
- *
- * @return atkColumnConfig
- */
- function &getColumnConfig($id = NULL, $forceNew = false)
- {
- $columnConfig = Adapto_Recordlist_ColumnConfig::getConfig($this, $id, $forceNew);
- return $columnConfig;
- }
- /**
- * Translate using this entity's module and type.
- *
- * @param mixed $string string or array of strings containing the name(s) of the string to return
- * when an array of strings is passed, the second will be the fallback if
- * the first one isn't found, and so forth
- * @param String $module module in which the language file should be looked for,
- * defaults to core module with fallback to ATK
- * @param String $lng ISO 639-1 language code, defaults to config variable
- * @param String $firstfallback the first module to check as part of the fallback
- * @param boolean $entityfaulttext if true, then it doesn't return a default text
- * when it can't find a translation
- * @return String the string from the languagefile
- */
- function text($string, $module = NULL, $lng = "", $firstfallback = "", $entityfaulttext = false)
- {
- if ($module == NULL)
- $module = $this->m_module;
- return atktext($string, $module, $this->m_type, $lng, $firstfallback, $entityfaulttext);
- }
- /**
- * String representation for this entity (PHP5 only).
- *
- * @return string ATK entity type
- */
- function __toString()
- {
- return $this->atkEntityType();
- }
- /**
- * Set the edit fieldprefix to use in atk
- *
- * @param string $prefix
- */
- function setEditFieldPrefix($prefix)
- {
- $this->m_edit_fieldprefix = $prefix;
- }
- /**
- * Get the edit fieldprefix to use
- *
- * @param boolean $Adapto_layout do we want the prefix in atkstyle (with _AE_) or not
- * @return string with edit fieldprefix
- */
- function getEditFieldPrefix($Adapto_layout = true)
- {
- if ($this->m_edit_fieldprefix == '')
- return '';
- else
- return $this->m_edit_fieldprefix . ($Adapto_layout ? '_AE_' : '');
- }
- /**
- * Escape SQL string, uses the entity's database to do the escaping.
- *
- * @param string $string string to escape
- *
- * @return string escaped string
- */
- public function escapeSQL($string)
- {
- return $this->getDb()->escapeSQL($string);
- }
- /**
- * Row CSS class.
- *
- * Used to determine the CSS class(s) for rows in the datagrid list.
- *
- * @param array $record record
- * @param int $nr row number
- *
- * @return string CSS class(es)
- */
- public function rowClass($record, $nr)
- {
- return $nr % 2 == 0 ? 'row1' : 'row2';
- }
- /**
- * Add callback function for add css class to row.
- *
- * @param mixed $callback name of a function or array with an object
- * and the name of a method or closure
- *
- * @return boolean
- */
- public function setRowClassCallback($callback)
- {
- $res = false;
- if (is_callable($callback, false, $callableName)) {
- if (is_array($callback) && !method_exists($callback[0], $callback[1])) {
- throw new Adapto_Exception("The registered row class callback method '$callableName' doesn't exist");
- } else {
- $this->m_rowClassCallback[] = $callback;
- $res = true;
- }
- } else {
- if (is_array($callback)) {
- if (!method_exists($callback[0], $callback[1])) {
- throw new Adapto_Exception("The registered row class callback method '$callableName' doesn't exist");
- }
- }
- throw new Adapto_Exception("The registered row class callback '$callableName' is not callable");
- }
- return $res;
- }
- /**
- * Return array with callback function list, which use for add css class to row
- *
- * @return array
- */
- public function getRowClassCallback()
- {
- return $this->m_rowClassCallback;
- }
- }
- ?>