_t('metadata element'),
'NAME_PLURAL' => _t('metadata elements'),
'FIELDS' => array(
'element_id' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_HIDDEN,
'IDENTITY' => true, 'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => _t('CollectiveAccess id'), 'DESCRIPTION' => _t('Unique numeric identifier used by CollectiveAccess internally to identify this metadata element')
),
'parent_id' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => true,
'DEFAULT' => '',
'LABEL' => 'Parent id', 'DESCRIPTION' => 'Parent id',
'BOUNDS_VALUE' => array(0,65535)
),
'hier_element_id' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => 'Element hierarchy', 'DESCRIPTION' => 'Identifier of element that is root of the element hierarchy.'
),
'element_code' => array(
'FIELD_TYPE' => FT_TEXT, 'DISPLAY_TYPE' => DT_FIELD,
'DISPLAY_WIDTH' => 30, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'FILTER' => '!^[\p{L}0-9_]+$!u',
'LABEL' => _t('Element code'), 'DESCRIPTION' => _t('Unique alphanumeric code for the metadata element.'),
'BOUNDS_LENGTH' => array(1,30),
'UNIQUE_WITHIN' => array()
),
'documentation_url' => array(
'FIELD_TYPE' => FT_TEXT, 'DISPLAY_TYPE' => DT_FIELD,
'DISPLAY_WIDTH' => 80, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => _t('Documentation url'), 'DESCRIPTION' => _t('URL pointing to documentation for this metadata element. Leave blank if no documentation URL exists.'),
'BOUNDS_LENGTH' => array(0,255)
),
'datatype' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_SELECT,
'DISPLAY_WIDTH' => 80, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => _t('Datatype'), 'DESCRIPTION' => _t('Data type of metadata element.'),
'BOUNDS_VALUE' => array(0,255)
),
'list_id' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_SELECT,
'DISPLAY_WIDTH' => 50, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => true,
'DISPLAY_FIELD' => array('ca_lists.list_code'),
'DISPLAY_ORDERBY' => array('ca_lists.list_code'),
'DEFAULT' => '',
'LABEL' => _t('Use list (for list elements only)'), 'DESCRIPTION' => _t('Specifies the list to use as value for this element. Element must be a list type for this to apply.'),
'BOUNDS_VALUE' => array(0,65535)
),
'settings' => array(
'FIELD_TYPE' => FT_VARS, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 88, 'DISPLAY_HEIGHT' => 15,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => _t('Settings'), 'DESCRIPTION' => _t('Type-specific ettings for metadata element')
),
'rank' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => _t('Sort order'), 'DESCRIPTION' => _t('The relative priority of the element when displayed in a list with other element. Lower numbers indicate higher priority.'),
'BOUNDS_VALUE' => array(0,65535)
),
'hier_left' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => 'Hierarchical index - left bound', 'DESCRIPTION' => 'Left-side boundary for nested set-style hierarchical indexing; used to accelerate search and retrieval of hierarchical record sets.'
),
'hier_right' => array(
'FIELD_TYPE' => FT_NUMBER, 'DISPLAY_TYPE' => DT_OMIT,
'DISPLAY_WIDTH' => 10, 'DISPLAY_HEIGHT' => 1,
'IS_NULL' => false,
'DEFAULT' => '',
'LABEL' => 'Hierarchical index - right bound', 'DESCRIPTION' => 'Right-side boundary for nested set-style hierarchical indexing; used to accelerate search and retrieval of hierarchical record sets.'
)
)
);
class ca_metadata_elements extends LabelableBaseModelWithAttributes implements ITakesSettings {
# ---------------------------------
# --- Object attribute properties
# ---------------------------------
# Describe structure of content object's properties - eg. database fields and their
# associated types, what modes are supported, et al.
#
# ------------------------------------------------------
# --- Basic object parameters
# ------------------------------------------------------
# what table does this class represent?
protected $TABLE = 'ca_metadata_elements';
# what is the primary key of the table?
protected $PRIMARY_KEY = 'element_id';
# ------------------------------------------------------
# --- Properties used by standard editing scripts
#
# These class properties allow generic scripts to properly display
# records from the table represented by this class
#
# ------------------------------------------------------
# Array of fields to display in a listing of records from this table
protected $LIST_FIELDS = array('element_code');
# When the list of "list fields" above contains more than one field,
# the LIST_DELIMITER text is displayed between fields as a delimiter.
# This is typically a comma or space, but can be any string you like
protected $LIST_DELIMITER = ' ';
# What you'd call a single record from this table (eg. a "person")
protected $NAME_SINGULAR;
# What you'd call more than one record from this table (eg. "people")
protected $NAME_PLURAL;
# List of fields to sort listing of records by; you can use
# SQL 'ASC' and 'DESC' here if you like.
protected $ORDER_BY = array('element_code');
# Maximum number of record to display per page in a listing
protected $MAX_RECORDS_PER_PAGE = 20;
# How do you want to page through records in a listing: by number pages ordered
# according to your setting above? Or alphabetically by the letters of the first
# LIST_FIELD?
protected $PAGE_SCHEME = 'alpha'; # alpha [alphabetical] or num [numbered pages; default]
# If you want to order records arbitrarily, add a numeric field to the table and place
# its name here. The generic list scripts can then use it to order table records.
protected $RANK = '';
# ------------------------------------------------------
# Hierarchical table properties
# ------------------------------------------------------
protected $HIERARCHY_TYPE = __CA_HIER_TYPE_ADHOC_MONO__;
protected $HIERARCHY_LEFT_INDEX_FLD = 'hier_left';
protected $HIERARCHY_RIGHT_INDEX_FLD = 'hier_right';
protected $HIERARCHY_PARENT_ID_FLD = 'parent_id';
protected $HIERARCHY_DEFINITION_TABLE = 'ca_metadata_elements';
protected $HIERARCHY_ID_FLD = 'hier_element_id';
protected $HIERARCHY_POLY_TABLE = null;
# ------------------------------------------------------
# Change logging
# ------------------------------------------------------
protected $UNIT_ID_FIELD = null;
protected $LOG_CHANGES_TO_SELF = false;
protected $LOG_CHANGES_USING_AS_SUBJECT = array(
"FOREIGN_KEYS" => array(
),
"RELATED_TABLES" => array(
)
);
# ------------------------------------------------------
# Labeling
# ------------------------------------------------------
protected $LABEL_TABLE_NAME = 'ca_metadata_element_labels';
# ------------------------------------------------------
# $FIELDS contains information about each field in the table. The order in which the fields
# are listed here is the order in which they will be returned using getFields()
protected $FIELDS;
static $s_element_list_cache;
static $s_element_set_cache;
static $s_settings_cache = array();
static $s_setting_value_cache = array();
# ------------------------------------------------------
# --- Constructor
#
# This is a function called when a new instance of this object is created. This
# standard constructor supports three calling modes:
#
# 1. If called without parameters, simply creates a new, empty objects object
# 2. If called with a single, valid primary key value, creates a new objects object and loads
# the record identified by the primary key value
#
# ------------------------------------------------------
public function __construct($pn_id=null) {
ca_metadata_elements::$s_settings_cache[null] = array();
ca_metadata_elements::$s_setting_value_cache[null] = array();
parent::__construct($pn_id); # call superclass constructor
$this->FIELDS['datatype']['BOUNDS_CHOICE_LIST'] = array_flip(ca_metadata_elements::getAttributeTypes());
}
# ------------------------------------------------------
public function load($pn_id) {
if ($vn_rc = parent::load($pn_id)) {
if (!isset(ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()])) {
ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()] = $this->get('settings');
}
}
return $vn_rc;
}
# ------------------------------------------------------
public function insert() {
$this->set('settings', ca_metadata_elements::$s_settings_cache[null]);
if ($vn_rc = parent::insert()) {
ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()] = ca_metadata_elements::$s_settings_cache[null];
}
return $vn_rc;
}
# ------------------------------------------------------
public function update() {
$this->set('settings', ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()]);
return parent::update();
}
# ------------------------------------------------------
public function delete($pb_delete_related=false) {
$vn_id = $this->getPrimaryKey();
if ($vn_rc = parent::delete($pb_delete_related)) {
unset(ca_metadata_elements::$s_settings_cache[$vn_id]);
}
return $vn_rc;
}
# ------------------------------------------------------
# Element set methods
# ------------------------------------------------------
/**
Returns array of elements in set of currently loaded row
*/
public function getElementsInSet($pn_element_id=null, $pb_use_cache=true) {
if (!$pn_element_id) {
$pn_element_id = $this->getPrimaryKey();
}
if (!$pn_element_id) { return null; }
if ($pb_use_cache && isset(ca_metadata_elements::$s_element_set_cache[$pn_element_id])) { return ca_metadata_elements::$s_element_set_cache[$pn_element_id]; }
$va_hier = $this->getHierarchyAsList($pn_element_id);
$va_element_set = array();
$va_root = null;
foreach($va_hier as $va_element) {
$va_element['NODE']['settings'] = unserialize(base64_decode($va_element['NODE']['settings'])); // decode settings vars into associative array
if (!trim($va_element['NODE']['parent_id'])) {
$va_root = $va_element['NODE'];
} else {
$va_element_set[$va_element['NODE']['parent_id']][$va_element['NODE']['rank']][$va_element['NODE']['element_id']] = $va_element['NODE'];
}
}
$va_tmp = $this->_getSortedElementsForParent($va_element_set, $va_root['element_id']);
array_unshift($va_tmp, $va_root);
return ca_metadata_elements::$s_element_set_cache[$pn_element_id] = $va_tmp;
}
# ------------------------------------------------------
private function _getSortedElementsForParent(&$pa_element_set, $pn_parent_id) {
if (!isset($pa_element_set[$pn_parent_id]) || !$pa_element_set[$pn_parent_id]) { return array(); }
ksort($pa_element_set[$pn_parent_id]);
$va_tmp = array();
foreach($pa_element_set[$pn_parent_id] as $vn_rank => $va_elements_by_id) {
foreach($va_elements_by_id as $vn_element_id => $va_element) {
$va_tmp[$vn_element_id] = $va_element;
$va_tmp = array_merge($va_tmp, $this->_getSortedElementsForParent($pa_element_set, $vn_element_id));
}
}
return $va_tmp;
}
# ------------------------------------------------------
# Settings
# ------------------------------------------------------
/**
* Returns associative array of setting descriptions (but *not* the setting values)
* The keys of this array are the setting codes, the values associative arrays containing
* info about the setting itself (label, description type of value, how to display an entry element for the setting in a form)
*/
public function getAvailableSettings() {
$t_attr_val = Attribute::getValueInstance((int)$this->get('datatype'));
return $t_attr_val ? $t_attr_val->getAvailableSettings() : null;
}
# ------------------------------------------------------
/**
* Returns an associative array with the setting values for this element
* The keys of the array are setting codes, the values are the setting values
*/
public function getSettings() {
if (is_null($this->get('datatype'))) { return null; }
return ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()];
}
# ------------------------------------------------------
/**
* Set setting value
* (you must call insert() or update() to write the settings to the database)
*/
public function setSetting($ps_setting, $pm_value, &$ps_error=null) {
if (is_null($this->get('datatype'))) { return null; }
if (!$this->isValidSetting($ps_setting)) { return null; }
$o_value_instance = Attribute::getValueInstance($this->get('datatype'), null, true);
if (!$o_value_instance->validateSetting($this->getFieldValuesArray(), $ps_setting, $pm_value, $vs_error)) {
$ps_error = $vs_error;
return false;
}
$va_settings = $this->getSettings();
$va_available_settings = $this->getAvailableSettings();
$va_properties = $va_available_settings[$ps_setting];
if (($va_properties['formatType'] == FT_NUMBER) && ($va_properties['displayType'] == DT_CHECKBOXES) && (!$pm_value)) {
$pm_value = '0';
}
$va_settings[$ps_setting] = $pm_value;
ca_metadata_elements::$s_settings_cache[$this->getPrimaryKey()] = $va_settings;
return true;
}
# ------------------------------------------------------
/**
* Return setting value
*/
public function getSetting($ps_setting, $pb_use_cache=false) {
if ($pb_use_cache && isset(ca_metadata_elements::$s_setting_value_cache[$vn_id = $this->getPrimaryKey()][$ps_setting])) { return ca_metadata_elements::$s_setting_value_cache[$vn_id][$ps_setting]; }
if (is_null($this->get('datatype'))) { return null; }
$va_settings = $this->getSettings();
$va_available_settings = $this->getAvailableSettings();
$vs_default = isset($va_available_settings[$ps_setting]['default']) ? $va_available_settings[$ps_setting]['default'] : null;
return ca_metadata_elements::$s_setting_value_cache[$vn_id][$ps_setting] = isset($va_settings[$ps_setting]) ? $va_settings[$ps_setting] : $vs_default;
}
# ------------------------------------------------------
/**
* Returns true if setting code exists for the current element's datatype
*/
public function isValidSetting($ps_setting) {
if (is_null($this->get('datatype'))) { return false; }
$va_settings = $this->getAvailableSettings();
return (isset($va_settings[$ps_setting])) ? true : false;
}
# ------------------------------------------------------
/**
* Returns HTML form element for editing of setting
*/
public function settingHTMLFormElement($ps_setting, $pa_options=null) {
if(!$this->isValidSetting($ps_setting)) {
return false;
}
$va_available_settings = $this->getAvailableSettings();
$va_properties = $va_available_settings[$ps_setting];
if (((int)$this->get('parent_id') > 0) && isset($va_properties['validForRootOnly']) && $va_properties['validForRootOnly']) {
return false;
}
$vs_input_name = "setting_$ps_setting";
if(isset($pa_options['label_id'])) {
$vs_label_id = $pa_options['label_id'];
} else {
$vs_label_id = "setting_{$ps_setting}_label";
}
$vs_return = "\n".'
".$va_properties["description"]);
return $vs_return;
}
# ------------------------------------------------------
# Static
# ------------------------------------------------------
public static function getAttributeTypes() {
$o_config = Configuration::load();
$o_types = Configuration::load($o_config->get('attribute_type_config'));
$va_types = $o_types->getList('types');
foreach($va_types as $vn_i => $vs_typename) {
if ($vs_typename == 'NOT_USED') { unset($va_types[$vn_i]); }
}
return $va_types;
}
# ------------------------------------------------------
/**
* Returns numeric code for data type name
*
* @param $ps_datatype_name string Name of data type (eg. "Text")
* @return int Numeric code for data type
*/
public static function getAttributeTypeCode($ps_datatype_name) {
$va_types = ca_metadata_elements::getAttributeTypes();
return array_search($ps_datatype_name, $va_types);
}
# ------------------------------------------------------
/**
* Returns data type name for numeric code
*
* @param $pn_type_code numeric type code
* @return string Name of data type (eg. 'Text') or null if code is not defined
*/
public static function getAttributeNameForTypeCode($pn_type_code) {
$va_types = ca_metadata_elements::getAttributeTypes();
return isset($va_types[$pn_type_code]) ? $va_types[$pn_type_code] : null;
}
# ------------------------------------------------------
/**
* Returns list of "root" metadata elements – elements that are either freestanding or at the top of an element hierarchy (Eg. have sub-elements but are not sub-elements themselves).
* Root elements are used to reference the element as a whole (including sub-elements), so it is useful to be able to obtain
* a list of these elements with sub-elements filtered out.
*
* @param $pm_table_name_or_num mixed Optional table name or number to filter list with. If specified then only elements that have a type restriction to the table are returned. If omitted (default) then all root elements, regardless of type restrictions, are returned.
* @param $pm_type_name_or_id mixed
* @return array A List of elements. Each list item is an array with keys set to field names; there is one additional value added with key "display_label" set to the display label of the element in the current locale
*/
public static function getRootElementsAsList($pm_table_name_or_num=null, $pm_type_name_or_id=null, $pb_use_cache=true, $pb_return_stats=false){
return ca_metadata_elements::getElementsAsList(true, $pm_table_name_or_num, $pm_type_name_or_id, $pb_use_cache, $pb_return_stats);
}
# ------------------------------------------------------
/**
* Returns all elements in system as list
*
* @param $pb_root_elements_only boolean If true, then only root elements are returned; default is false
* @param $pm_table_name_or_num mixed Optional table name or number to filter list with. If specified then only elements that have a type restriction to the table are returned. If omitted (default) then all elements, regardless of type restrictions, are returned.
* @param $pm_type_name_or_id mixed Optional type code or type_id to restrict elements to. If specified then only elements that have a type restriction to the specified table and type are returned.
* @param $pb_use_cache boolean Optional control for list cache; if true [default] then the will be cached for the request; if false the list will be generated from the database. The list is always generated at least once in the current request - there is no inter-request caching
* @return array A List of elements. Each list item is an array with keys set to field names; there is one additional value added with key "display_label" set to the display label of the element in the current locale
*/
public static function getElementsAsList($pb_root_elements_only=false, $pm_table_name_or_num=null, $pm_type_name_or_id=null, $pb_use_cache=true, $pb_return_stats=false, $pb_index_by_element_code=false){
$o_dm = Datamodel::load();
$vn_table_num = $o_dm->getTableNum($pm_table_name_or_num);
if ($pb_use_cache && ca_metadata_elements::$s_element_list_cache[$pm_table_name_or_num.'/'.$pm_type_name_or_id.'/'.($pb_root_elements_only ? '1' : '0')]) {
if (($pb_return_stats && isset(ca_metadata_elements::$s_element_list_cache[$pm_table_name_or_num.'/'.$pm_type_name_or_id.'/'.($pb_root_elements_only ? '1' : '0')]['ui_counts'])) || !$pb_return_stats) {
return ca_metadata_elements::$s_element_list_cache[$pm_table_name_or_num.'/'.$pm_type_name_or_id.'/'.($pb_root_elements_only ? '1' : '0')];
}
}
if ($pb_return_stats) {
$va_counts_by_attribute = ca_metadata_elements::getUIUsageCounts();
$va_restrictions_by_attribute = ca_metadata_elements::getTypeRestrictionsAsList();
}
$vo_db = new Db();
$va_wheres = array();
if ($pb_root_elements_only) {
$va_wheres[] = 'cme.parent_id is NULL';
}
if ($vn_table_num) {
$va_wheres[] = 'cmtr.table_num = ?';
if ($pm_type_name_or_id) {
if (!is_numeric($pm_type_name_or_id)) {
$t_list_item = new ca_list_items();
if ($t_list_item->load(array('idno' => $pm_type_name_or_id))) {
$vn_item_id = $t_list_item->getPrimaryKey();
$va_wheres[] = 'cmtr.type_id = '.intval($vn_item_id);
}
} else {
$va_wheres[] = 'cmtr.type_id = '.intval($pm_type_name_or_id);
}
}
$vs_wheres = ' WHERE '.join(' AND ', $va_wheres);
$qr_tmp = $vo_db->query("
SELECT cme.*
FROM ca_metadata_elements cme
INNER JOIN ca_metadata_type_restrictions AS cmtr ON cme.hier_element_id = cmtr.element_id
{$vs_wheres}
", (int)$vn_table_num);
} else {
if (sizeof($va_wheres)) {
$vs_wheres = ' WHERE '.join(' AND ', $va_wheres);
} else {
$vs_wheres = '';
}
$qr_tmp = $vo_db->query("
SELECT *
FROM ca_metadata_elements cme
{$vs_wheres}
");
}
$va_return = array();
$t_element = new ca_metadata_elements();
$va_element_ids = array();
while($qr_tmp->nextRow()){
$vn_element_id = $qr_tmp->get('element_id');
$vs_element_code = $qr_tmp->get('element_code');
foreach($t_element->getFields() as $vs_field){
$va_record[$vs_field] = $qr_tmp->get($vs_field);
}
$va_record['settings'] = caUnserializeForDatabase($qr_tmp->get('settings'));
if ($pb_return_stats) {
$va_record['ui_counts'] = $va_counts_by_attribute[$vs_code = $qr_tmp->get('element_code')];
$va_record['restrictions'] = $va_restrictions_by_attribute[$vs_code];
}
$va_return[($pb_index_by_element_code) ? $vs_element_code : $vn_element_id] = $va_record;
}
// Get labels
$va_labels = $t_element->getPreferredDisplayLabelsForIDs(array_keys($va_return));
foreach($va_labels as $vn_id => $vs_label) {
$va_return[$vn_id]['display_label'] = $vs_label;
}
return ca_metadata_elements::$s_element_list_cache[$pm_table_name_or_num.'/'.$pm_type_name_or_id.'/'.($pb_root_elements_only ? '1' : '0')] = sizeof($va_return) > 0 ? $va_return : false;
}
# ------------------------------------------------------
/**
* Returns number of elements in system
*
* @param $pb_root_elements_only boolean If true, then only root elements are counted; default is false
* @param $pm_table_name_or_num mixed Optional table name or number to filter list with. If specified then only elements that have a type restriction to the table are counted. If omitted (default) then all elements, regardless of type restrictions, are returned.
* @param $pm_type_name_or_id mixed Optional type code or type_id to restrict elements to. If specified then only elements that have a type restriction to the specified table and type are counted.
* @return int The number of elements
*/
public static function getElementCount($pb_root_elements_only=false, $pm_table_name_or_num=null, $pm_type_name_or_id=null){
$o_dm = Datamodel::load();
$vn_table_num = $o_dm->getTableNum($pm_table_name_or_num);
$vo_db = new Db();
$va_wheres = array();
if ($pb_root_elements_only) {
$va_wheres[] = 'cme.parent_id is NULL';
}
if ($vn_table_num) {
$va_wheres[] = 'cmtr.table_num = ?';
if ($pm_type_name_or_id) {
if (!is_numeric($pm_type_name_or_id)) {
$t_list_item = new ca_list_items();
if ($t_list_item->load(array('idno' => $pm_type_name_or_id))) {
$vn_item_id = $t_list_item->getPrimaryKey();
$va_wheres[] = 'cmtr.type_id = '.intval($vn_item_id);
}
} else {
$va_wheres[] = 'cmtr.type_id = '.intval($pm_type_name_or_id);
}
}
$vs_wheres = ' WHERE '.join(' AND ', $va_wheres);
$qr_tmp = $vo_db->query("
SELECT count(*) c
FROM ca_metadata_elements cme
INNER JOIN ca_metadata_type_restrictions AS cmtr ON cme.hier_element_id = cmtr.element_id
{$vs_wheres}
", (int)$vn_table_num);
} else {
if (sizeof($va_wheres)) {
$vs_wheres = ' WHERE '.join(' AND ', $va_wheres);
} else {
$vs_wheres = '';
}
$qr_tmp = $vo_db->query("
SELECT count(*) c
FROM ca_metadata_elements cme
{$vs_wheres}
");
}
if($qr_tmp->nextRow()){
return $qr_tmp->get('c');
}
return 0;
}
# ------------------------------------------------------
/*
*
*/
public static function getSortableElements($pm_table_name_or_num, $pm_type_name_or_id=null){
$va_elements = ca_metadata_elements::getElementsAsList(false, $pm_table_name_or_num, $pm_type_name_or_id);
if (!is_array($va_elements) || !sizeof($va_elements)) { return array(); }
$va_sortable_elements = array();
foreach($va_elements as $vn_id =>$va_element) {
if (isset($va_element['settings']['canBeUsedInSort']) && $va_element['settings']['canBeUsedInSort']) {
$va_sortable_elements[$va_element['element_id']] = $va_element;
}
}
return $va_sortable_elements;
}
# ------------------------------------------------------
/**
* Returns list of user interfaces that reference the currently loaded metadata element
*
* @return array List of user interfaces that include the currently loaded element. Array is keyed on ui_id. Values are arrays with keys set to ca_ui_editor_labels field names and associated values.
*/
public function getUIs() {
if (!($vn_element_id = $this->getPrimaryKey())) {
return null;
}
$vs_element_code = $this->get('element_code');
$qr_res = $this->getDb()->query("
SELECT ui.ui_id, uil.*, p.screen_id, ui.editor_type
FROM ca_editor_uis ui
INNER JOIN ca_editor_ui_labels AS uil ON uil.ui_id = ui.ui_id
INNER JOIN ca_editor_ui_screens AS s ON s.ui_id = ui.ui_id
INNER JOIN ca_editor_ui_bundle_placements AS p ON p.screen_id = s.screen_id
WHERE
p.bundle_name = 'ca_attribute_{$vs_element_code}'
");
$va_uis = array();
while($qr_res->nextRow()) {
$va_uis[$qr_res->get('ui_id')][$qr_res->get('locale_id')] = $qr_res->getRow();
}
return caExtractValuesByUserLocale($va_uis);
}
# ------------------------------------------------------
/**
* Returns array of with information about usage of metadata element(s) in user interfaces
*
* @param $pm_element_code_or_id mixed Optional element_id or code. If set then counts are only returned for the specified element. If omitted then counts are returned for all elements.
* @return array Array of counts. Keys are element_codes, values are arrays keyed on table DISPLAY name (eg. "set items", not "ca_set_items"). Values are the number of times the element is references in a user interface for the table.
*/
static public function getUIUsageCounts($pm_element_code_or_id=null) {
// Get UI usage counts
$vo_db = new Db();
$vn_element_id = null;
if ($pm_element_code_or_id) {
$t_element = new ca_metadata_elements($pm_element_code_or_id);
if (!($vn_element_id = $t_element->getPrimaryKey())) {
if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
$vn_element_id = $t_element->getPrimaryKey();
}
}
}
$vs_sql_where = '';
if ($vn_element_id) {
$vs_sql_where = " WHERE p.bundle_name = 'ca_attribute_".$t_element->get('element_code')."'";
}
$qr_use_counts = $vo_db->query("
SELECT count(*) c, p.bundle_name, u.editor_type
FROM ca_editor_ui_bundle_placements p
INNER JOIN ca_editor_ui_screens AS s ON s.screen_id = p.screen_id
INNER JOIN ca_editor_uis AS u ON u.ui_id = s.ui_id
{$vs_sql_where}
GROUP BY
p.bundle_name, u.editor_type
");
$va_counts_by_attribute = array();
$o_dm = Datamodel::load();
while($qr_use_counts->nextRow()) {
if (preg_match('!^ca_attribute_([A-Za-z0-9_\-]+)$!', $qr_use_counts->get('bundle_name'), $va_matches)) {
$t_table = $o_dm->getInstanceByTableNum($qr_use_counts->get('editor_type'), true);
$va_counts_by_attribute[$va_matches[1]][$t_table->getProperty('NAME_PLURAL')] = $qr_use_counts->get('c');
}
}
return $va_counts_by_attribute;
}
# ------------------------------------------------------
/**
* Returns array of with information about usage of metadata element(s) in user interfaces
*
* @param $pm_element_code_or_id mixed Optional element_id or code. If set then counts are only returned for the specified element. If omitted then counts are returned for all elements.
* @return array Array of counts. Keys are element_codes, values are arrays keyed on table DISPLAY name (eg. "set items", not "ca_set_items"). Values are the number of times the element is references in a user interface for the table.
*/
static public function getTypeRestrictionsAsList($pm_element_code_or_id=null) {
// Get UI usage counts
$vo_db = new Db();
$vn_element_id = null;
if ($pm_element_code_or_id) {
$t_element = new ca_metadata_elements($pm_element_code_or_id);
if (!($vn_element_id = $t_element->getPrimaryKey())) {
if ($t_element->load(array('element_code' => $pm_element_code_or_id))) {
$vn_element_id = $t_element->getPrimaryKey();
}
}
}
$vs_sql_where = '';
if ($vn_element_id) {
$vs_sql_where = " WHERE cmtr.element_id = {$vn_element_id}";
}
$qr_restrictions = $vo_db->query("
SELECT cmtr.*, cme.element_code
FROM ca_metadata_type_restrictions cmtr
INNER JOIN ca_metadata_elements AS cme ON cme.element_id = cmtr.element_id
{$vs_sql_where}
");
$va_restrictions = array();
$o_dm = Datamodel::load();
$t_list = new ca_lists();
while($qr_restrictions->nextRow()) {
$t_table = $o_dm->getInstanceByTableNum($qr_restrictions->get('table_num'), true);
if ($vn_type_id = $qr_restrictions->get('type_id')) {
$vs_type_name = $t_list->getItemForDisplayByItemID($vn_type_id);
} else {
$vs_type_name = '*';
}
$va_restrictions[$qr_restrictions->get('element_code')][$t_table->getProperty('NAME_PLURAL')][$vn_type_id] = $vs_type_name;
}
return $va_restrictions;
}
# ------------------------------------------------------
#
# ------------------------------------------------------
/**
* Adds restriction (a binding between a specific item and optional type and the attribute)
*
* $pn_table_num: the number of the table to bind to
*
*/
public function addTypeRestriction($pn_table_num, $pn_type_id, $va_settings) {
if (!($vn_element = $this->getPrimaryKey())) { return null; } // element must be loaded
if ($this->get('parent_id')) { return null; } // element must be root of hierarchy
if (!is_array($va_settings)) { $va_settings = array(); }
$t_restriction = new ca_metadata_type_restrictions();
$t_restriction->setMode(ACCESS_WRITE);
$t_restriction->set('table_num', $pn_table_num);
$t_restriction->set('type_id', $pn_type_id);
$t_restriction->set('element_id', $this->getPrimaryKey());
foreach($va_settings as $vs_setting => $vs_setting_value) {
$t_restriction->setSetting($vs_setting, $vs_setting_value);
}
$t_restriction->insert();
if ($t_restriction->numErrors()) {
$this->errors = $t_restriction->errors();
return false;
}
return true;
}
# ------------------------------------------------------
/**
* Remove type restrictions from this element for specified table and, optionally, type
*
* $pn_table_num: the number of the table to bind to
*
*/
public function removeTypeRestriction($pn_table_num, $pn_type_id=null) {
if (!($vn_element = $this->getPrimaryKey())) { return null; } // element must be loaded
if ($this->get('parent_id')) { return null; } // element must be root of hierarchy
$o_db = $this->getDb();
$vs_type_id_sql = ($pn_type_id) ? 'AND type_id = '.intval($pn_type_id) : 'AND type_id IS NULL ';
$qr_res = $o_db->query("
DELETE FROM ca_metadata_type_restrictions
WHERE
table_num = ? AND element_id = ? AND {$vs_type_id_sql}
", (int)$pn_table_num, (int)$this->getPrimaryKey());
if ($o_db->numErrors()) {
$this->errors = $o_db->errors();
return false;
}
return true;
}
# ------------------------------------------------------
/**
* Remove all type restrictions from loaded element
*
*/
public function removeAllTypeRestrictions() {
if (!($vn_element = $this->getPrimaryKey())) { return null; } // element must be loaded
if ($this->get('parent_id')) { return null; } // element must be root of hierarchy
$o_db = $this->getDb();
$qr_res = $o_db->query("
DELETE FROM ca_metadata_type_restrictions
WHERE
element_id = ?
", (int)$this->getPrimaryKey());
if ($o_db->numErrors()) {
$this->errors = $o_db->errors();
return false;
}
return true;
}
# ------------------------------------------------------
/**
* Return all type restrictions
*
*/
public function getTypeRestrictions($pn_table_num=null, $pn_type_id=null) {
if (!($vn_element_id = $this->getPrimaryKey())) { return null; } // element must be loaded
if ($this->get('parent_id')) {
// element must be root of hierarchy...
// if not, then use root of hierarchy since all type restrictions are bound to the root
$vn_element_id = $this->getHierarchyRootID(null);
}
$o_db = $this->getDb();
$vs_table_type_sql = '';
if ($pn_table_num > 0) {
$vs_table_type_sql .= ' AND table_num = '.intval($pn_table_num);
}
if ($pn_type_id > 0) {
$vs_table_type_sql .= ' AND type_id = '.intval($pn_type_id);
}
$qr_res = $o_db->query("
SELECT *
FROM ca_metadata_type_restrictions
WHERE
element_id = ? {$vs_table_type_sql}
", (int)$vn_element_id);
if ($o_db->numErrors()) {
$this->errors = $o_db->errors();
return false;
}
$va_restrictions = array();
while($qr_res->nextRow()) {
$va_restrictions[] = $qr_res->getRow();
}
return $va_restrictions;
}
# ------------------------------------------------------
/**
* Load type restriction for specified table and type and return loaded model instance.
* Will return specific restriction for type_id, or a general (type_id=null) restriction if no
* type-specific restriction is defined.
*
* @param $pn_table_num - table_num of type restriction
* @param $pn_type_id - type_id of type restriction; leave null if you want a non-type-specific restriction
* @return ca_metadata_type_restrictions instance - will be loaded with type restriction
*/
public function getTypeRestrictionInstance($pn_table_num, $pn_type_id) {
if (!($vn_element_id = $this->getPrimaryKey())) { return null; } // element must be loaded
if ($this->get('parent_id')) { return null; } // element must be root of hierarchy
$t_restriction = new ca_metadata_type_restrictions();
if (($pn_type_id > 0) && $t_restriction->load(array('table_num' => intval($pn_table_num), 'type_id' => intval($pn_type_id), 'element_id' => $vn_element_id))) {
return $t_restriction;
} else {
if ($t_restriction->load(array('table_num' => intval($pn_table_num), 'type_id' => null, 'element_id' => $vn_element_id))) {
return $t_restriction;
}
}
return null;
}
# ------------------------------------------------------
/**
*
*/
public function htmlFormElement($ps_field, $ps_format=null, $pa_options=null) {
if ($ps_field == 'list_id') {
// Custom list drop-down
$vs_format = $this->_CONFIG->get('form_element_display_format');
$t_list = new ca_lists();
$va_lists = caExtractValuesByUserLocale($t_list->getListOfLists());
$va_opts = array();
foreach($va_lists as $vn_list_id => $va_list_info) {
$va_opts[$va_list_info['name'].' ('.$va_list_info['list_code'].')'] = $vn_list_id;
}
ksort($va_opts);
$vs_format = str_replace('^LABEL', $vs_field_label = $this->getFieldInfo('list_id', 'LABEL'), $vs_format);
$vs_format = str_replace('^EXTRA', '', $vs_format);
$vs_format = str_replace('^ELEMENT', caHTMLSelect($ps_field, $va_opts, array('id' => $ps_field), array('value' => $this->get('list_id'))), $vs_format);
if (!isset($pa_options['no_tooltips']) || !$pa_options['no_tooltips']) {
TooltipManager::add('#list_id', "