grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026. -------------------------------------------------------------------------- */ /** @file * @brief */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * Document_Item Class * * Relation between Documents and Items **/ class Document_Item extends CommonDBRelation{ // From CommonDBRelation static public $itemtype_1 = 'Document'; static public $items_id_1 = 'documents_id'; static public $take_entity_1 = true ; static public $itemtype_2 = 'itemtype'; static public $items_id_2 = 'items_id'; static public $take_entity_2 = false ; /** * @since version 0.84 **/ function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); $forbidden[] = 'update'; return $forbidden; } /** * @since version 0.85.5 * @see CommonDBRelation::canCreateItem() **/ function canCreateItem() { if ($this->fields['itemtype'] == 'Ticket') { $ticket = new Ticket(); // Not item linked for closed tickets if ($ticket->getFromDB($this->fields['items_id']) && in_array($ticket->fields['status'],$ticket->getClosedStatusArray())) { return false; } } return parent::canCreateItem(); } function prepareInputForAdd($input) { if ((empty($input['items_id']) || ($input['items_id'] == 0)) && ($input['itemtype'] != 'Entity')) { return false; } // Do not insert circular link for document if (($input['itemtype'] == 'Document') && ($input['items_id'] == $input['documents_id'])) { return false; } // Avoid duplicate entry $restrict = "`documents_id` = '".$input['documents_id']."' AND `itemtype` = '".$input['itemtype']."' AND `items_id` = '".$input['items_id']."'"; if (countElementsInTable($this->getTable(),$restrict) > 0) { return false; } return parent::prepareInputForAdd($input); } /** * @since version 0.90.2 * * @see CommonDBTM::pre_deleteItem() **/ function pre_deleteItem() { global $DB; // fordocument mandatory if ($this->fields['itemtype'] == 'Ticket') { $ticket = new Ticket(); $ticket->getFromDB($this->fields['items_id']); $tt = $ticket->getTicketTemplateToUse(0, $ticket->fields['type'], $ticket->fields['itilcategories_id'], $ticket->fields['entities_id']); if (isset($tt->mandatory['_documents_id'])) { // refuse delete if only one document if (countElementsInTable($this->getTable(), "`items_id` =". $this->fields['items_id'] ." AND `itemtype` = 'Ticket'") == 1) { $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'), _n('Document', 'Documents', 2)); Session::addMessageAfterRedirect($message, false, ERROR); return false; } } } return true; } function post_addItem() { if ($this->fields['itemtype'] == 'Ticket') { $ticket = new Ticket(); $input = array('id' => $this->fields['items_id'], 'date_mod' => $_SESSION["glpi_currenttime"], '_donotadddocs' => true); if (!isset($this->input['_do_notif']) || $this->input['_do_notif']) { $input['_forcenotif'] = true; } if (isset($this->input['_disablenotif']) && $this->input['_disablenotif']) { $input['_disablenotif'] = true; } $ticket->update($input); } parent::post_addItem(); } /** * @since version 0.83 * * @see CommonDBTM::post_purgeItem() **/ function post_purgeItem() { if ($this->fields['itemtype'] == 'Ticket') { $ticket = new Ticket(); $input = array('id' => $this->fields['items_id'], 'date_mod' => $_SESSION["glpi_currenttime"], '_donotadddocs' => true); if (!isset($this->input['_do_notif']) || $this->input['_do_notif']) { $input['_forcenotif'] = true; } //Clean ticket description if an image is in it $doc = new Document(); $doc->getFromDB($this->fields['documents_id']); if (!empty($doc->fields['tag'])) { $ticket->getFromDB($this->fields['items_id']); $input['content'] = $ticket->cleanTagOrImage($ticket->fields['content'], array($doc->fields['tag'])); } $ticket->update($input); } parent::post_purgeItem(); } /** * @param $item CommonDBTM object **/ static function countForItem(CommonDBTM $item) { $restrict = "`glpi_documents_items`.`items_id` = '".$item->getField('id')."' AND `glpi_documents_items`.`itemtype` = '".$item->getType()."'"; if (Session::getLoginUserID()) { $restrict .= getEntitiesRestrictRequest(" AND ", "glpi_documents_items", '', '', true); } else { // Anonymous access from FAQ $restrict .= " AND `glpi_documents_items`.`entities_id` = '0' "; } $nb = countElementsInTable(array('glpi_documents_items'), $restrict); // Document case : search in both if ($item->getType() == 'Document') { $restrict = "`glpi_documents_items`.`documents_id` = '".$item->getField('id')."' AND `glpi_documents_items`.`itemtype` = '".$item->getType()."'"; if (Session::getLoginUserID()) { $restrict .= getEntitiesRestrictRequest(" AND ", "glpi_documents_items", '', '', true); } else { // Anonymous access from FAQ $restrict .= " AND `glpi_documents_items`.`entities_id` = '0' "; } $nb += countElementsInTable(array('glpi_documents_items'), $restrict); } return $nb ; } /** * @param $item Document object **/ static function countForDocument(Document $item) { $restrict = "`glpi_documents_items`.`documents_id` = '".$item->getField('id')."' AND `glpi_documents_items`.`itemtype` != '".$item->getType()."'"; return countElementsInTable(array('glpi_documents_items'), $restrict); } function getTabNameForItem(CommonGLPI $item, $withtemplate=0) { $nbdoc = $nbitem = 0; switch ($item->getType()) { case 'Document' : $ong = array(); if ($_SESSION['glpishow_count_on_tabs']) { $nbdoc = self::countForDocument($item); $nbitem = self::countForItem($item); } $ong[1] = self::createTabEntry(_n('Associated item', 'Associated items', Session::getPluralNumber()), $nbdoc); $ong[2] = self::createTabEntry(Document::getTypeName(Session::getPluralNumber()), $nbitem); return $ong; default : // Can exist for template if (Document::canView() || ($item->getType() == 'Ticket') || ($item->getType() == 'Reminder') || ($item->getType() == 'KnowbaseItem')) { if ($_SESSION['glpishow_count_on_tabs']) { $nbitem = self::countForItem($item); } return self::createTabEntry(Document::getTypeName(Session::getPluralNumber()), $nbitem); } } return ''; } static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0) { switch ($item->getType()) { case 'Document' : switch ($tabnum) { case 1 : self::showForDocument($item); break; case 2 : self::showForItem($item, $withtemplate); break; } return true; default : self::showForitem($item, $withtemplate); } } /** * Duplicate documents from an item template to its clone * * @since version 0.84 * * @param $itemtype itemtype of the item * @param $oldid ID of the item to clone * @param $newid ID of the item cloned * @param $newitemtype itemtype of the new item (= $itemtype if empty) (default '') **/ static function cloneItem($itemtype, $oldid, $newid, $newitemtype='') { global $DB; if (empty($newitemtype)) { $newitemtype = $itemtype; } foreach ($DB->request('glpi_documents_items', array('FIELDS' => 'documents_id', 'WHERE' => "`items_id` = '$oldid' AND `itemtype` = '$itemtype'")) as $data) { $docitem = new self(); $docitem->add(array('documents_id' => $data["documents_id"], 'itemtype' => $newitemtype, 'items_id' => $newid)); } } /** * Show items links to a document * * @since version 0.84 * * @param $doc Document object * * @return nothing (HTML display) **/ static function showForDocument(Document $doc) { global $DB, $CFG_GLPI; $instID = $doc->fields['id']; if (!$doc->can($instID, READ)) { return false; } $canedit = $doc->can($instID, UPDATE); // for a document, // don't show here others documents associated to this one, // it's done for both directions in self::showAssociated $query = "SELECT DISTINCT `itemtype` FROM `glpi_documents_items` WHERE `glpi_documents_items`.`documents_id` = '$instID' AND `glpi_documents_items`.`itemtype` != 'Document' ORDER BY `itemtype`"; $result = $DB->query($query); $number = $DB->numrows($result); $rand = mt_rand(); if ($canedit) { echo "
"; echo "
"; echo ""; echo ""; echo ""; echo "
".__('Add an item')."
"; Dropdown::showSelectItemFromItemtypes(array('itemtypes' => Document::getItemtypesThatCanHave(), 'entity_restrict' => ($doc->fields['is_recursive'] ?getSonsOf('glpi_entities', $doc->fields['entities_id']) :$doc->fields['entities_id']), 'checkright' => true)); echo ""; echo ""; echo ""; echo "
"; Html::closeForm(); echo "
"; } echo "
"; if ($canedit && $number) { Html::openMassiveActionsForm('mass'.__CLASS__.$rand); $massiveactionparams = array('container' => 'mass'.__CLASS__.$rand); Html::showMassiveActions($massiveactionparams); } echo ""; $header_begin = ""; $header_top = ''; $header_bottom = ''; $header_end = ''; if ($canedit && $number) { $header_top .= ""; $header_bottom .= ""; } $header_end .= ""; $header_end .= ""; $header_end .= ""; $header_end .= ""; $header_end .= ""; $header_end .= ""; echo $header_begin.$header_top.$header_end; for ($i=0 ; $i < $number ; $i++) { $itemtype=$DB->result($result, $i, "itemtype"); if (!($item = getItemForItemtype($itemtype))) { continue; } if ($item->canView()) { if ($item instanceof CommonDevice) { $column = "designation"; } else if ($item instanceof Item_Devices) { $column = "itemtype"; } else { $column = "name"; } if ($itemtype == 'Ticket') { $column = "id"; } $itemtable = getTableForItemType($itemtype); $query = "SELECT `$itemtable`.*, `glpi_documents_items`.`id` AS IDD, "; if ($itemtype == 'KnowbaseItem') { $query .= "-1 AS entity FROM `glpi_documents_items`, `$itemtable` ".KnowbaseItem::addVisibilityJoins()." WHERE `$itemtable`.`id` = `glpi_documents_items`.`items_id` AND "; } else { $query .= "`glpi_entities`.`id` AS entity FROM `glpi_documents_items`, `$itemtable`"; if ($itemtype != 'Entity') { $query .= " LEFT JOIN `glpi_entities` ON (`glpi_entities`.`id` = `$itemtable`.`entities_id`)"; } $query .= " WHERE `$itemtable`.`id` = `glpi_documents_items`.`items_id` AND "; } $query .= "`glpi_documents_items`.`itemtype` = '$itemtype' AND `glpi_documents_items`.`documents_id` = '$instID' "; if ($itemtype =='KnowbaseItem') { if (Session::getLoginUserID()) { $where = "AND ".KnowbaseItem::addVisibilityRestrict(); } else { // Anonymous access if (Session::isMultiEntitiesMode()) { $where = " AND (`glpi_entities_knowbaseitems`.`entities_id` = '0' AND `glpi_entities_knowbaseitems`.`is_recursive` = '1')"; } } } else { $query .= getEntitiesRestrictRequest(" AND ", $itemtable, '', '', $item->maybeRecursive()); } if ($item->maybeTemplate()) { $query .= " AND `$itemtable`.`is_template` = '0'"; } if ($itemtype == 'KnowbaseItem') { $query .= " ORDER BY `$itemtable`.`$column`"; } else { $query .= " ORDER BY `glpi_entities`.`completename`, `$itemtable`.`$column`"; } if ($itemtype == 'SoftwareLicense') { $soft = new Software(); } if ($result_linked = $DB->query($query)) { if ($DB->numrows($result_linked)) { while ($data = $DB->fetch_assoc($result_linked)) { if ($itemtype == 'Ticket') { $data["name"] = sprintf(__('%1$s: %2$s'), __('Ticket'), $data["id"]); } if ($itemtype == 'SoftwareLicense') { $soft->getFromDB($data['softwares_id']); $data["name"] = sprintf(__('%1$s - %2$s'), $data["name"], $soft->fields['name']); } if ($item instanceof CommonDevice) { $linkname = $data["designation"]; } else if ($item instanceof Item_Devices) { $linkname = $data["itemtype"]; } else { $linkname = $data["name"]; } if ($_SESSION["glpiis_ids_visible"] || empty($data["name"])) { $linkname = sprintf(__('%1$s (%2$s)'), $linkname, $data["id"]); } if ($item instanceof Item_Devices) { $tmpitem = new $item::$itemtype_2(); if ($tmpitem->getFromDB($data[$item::$items_id_2])) { $linkname = $tmpitem->getLink(); } } $link = $itemtype::getFormURLWithID($data['id']); $name = "".$linkname.""; echo ""; if ($canedit) { echo ""; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } } } } } if ($number) { echo $header_begin.$header_bottom.$header_end; } echo "
".Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand); $header_top .= "".Html::getCheckAllAsCheckbox('mass'.__CLASS__.$rand); $header_bottom .= "".__('Type')."".__('Name')."".__('Entity')."".__('Serial number')."".__('Inventory number')."
"; Html::showMassiveActionCheckBox(__CLASS__, $data["IDD"]); echo "".$item->getTypeName(1)."".$name."".Dropdown::getDropdownName("glpi_entities", $data['entity']); echo "". (isset($data["serial"])? "".$data["serial"]."" :"-")."". (isset($data["otherserial"])? "".$data["otherserial"]."" :"-")."
"; if ($canedit && $number) { $massiveactionparams['ontop'] =false; Html::showMassiveActions($massiveactionparams); Html::closeForm(); } echo "
"; } /** * Show documents associated to an item * * @since version 0.84 * * @param $item CommonDBTM object for which associated documents must be displayed * @param $withtemplate (default '') **/ static function showForItem(CommonDBTM $item, $withtemplate='') { $ID = $item->getField('id'); if ($item->isNewID($ID)) { return false; } if (($item->getType() != 'Ticket') && ($item->getType() != 'KnowbaseItem') && ($item->getType() != 'Reminder') && !Document::canView()) { return false; } $params = array(); $params['rand'] = mt_rand(); self::showAddFormForItem($item, $withtemplate, $params); self::showListForItem($item, $withtemplate, $params); } /** * @since version 0.90 * * @param $item * @param $withtemplate (default '') * @param $colspan */ static function showSimpleAddForItem(CommonDBTM $item, $withtemplate='', $colspan=1) { $entity = $_SESSION["glpiactive_entity"]; if ($item->isEntityAssign()) { /// Case of personal items : entity = -1 : create on active entity (Reminder case)) if ($item->getEntityID() >=0 ) { $entity = $item->getEntityID(); } } echo ""; echo "".__('Add a document').""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($item->getType() == 'Ticket') { echo ""; } echo Html::file(array('multiple' => true)); echo "(".Document::getMaxUploadSize().") "; echo ""; } /** * @since version 0.90 * * @param $item * @param $withtemplate (default '') * @param $options array * * @return boolean **/ static function showAddFormForItem(CommonDBTM $item, $withtemplate='', $options=array()) { global $DB, $CFG_GLPI; //default options $params['rand'] = mt_rand(); if (is_array($options) && count($options)) { foreach ($options as $key => $val) { $params[$key] = $val; } } if (!$item->can($item->fields['id'], READ)) { return false; } if (empty($withtemplate)) { $withtemplate = 0; } // find documents already associated to the item $doc_item = new self(); $used_found = $doc_item->find("`items_id` = '".$item->getID()."' AND `itemtype` = '".$item->getType()."'"); $used = array_keys($used_found); $used = array_combine($used, $used); if (((($item->getType() == 'Ticket') && $item->canAddFollowups()) || $item->canAddItem('Document')) && ($withtemplate < 2)) { // Restrict entity for knowbase $entities = ""; $entity = $_SESSION["glpiactive_entity"]; if ($item->isEntityAssign()) { /// Case of personal items : entity = -1 : create on active entity (Reminder case)) if ($item->getEntityID() >=0 ) { $entity = $item->getEntityID(); } if ($item->isRecursive()) { $entities = getSonsOf('glpi_entities',$entity); } else { $entities = $entity; } } $limit = getEntitiesRestrictRequest(" AND ","glpi_documents",'',$entities,true); $q = "SELECT COUNT(*) FROM `glpi_documents` WHERE `is_deleted` = '0' $limit"; $result = $DB->query($q); $nb = $DB->result($result,0,0); if ($item->getType() == 'Document') { $used[$item->getID()] = $item->getID(); } echo "
"; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo "
".__('Add a document')."
"; _e('Heading'); echo ""; DocumentCategory::dropdown(array('entity' => $entities)); echo ""; echo ""; echo ""; echo ""; echo ""; if ($item->getType() == 'Ticket') { echo ""; } echo Html::file(array('multiple' => true)); echo "(".Document::getMaxUploadSize().") "; echo ""; echo "
"; Html::closeForm(); if (Document::canView() && ($nb > count($used))) { echo ""; echo ""; echo ""; echo ""; echo ""; echo "
"; echo ""; echo ""; if ($item->getType() == 'Ticket') { echo ""; echo ""; } Document::dropdown(array('entity' => $entities , 'used' => $used)); echo ""; echo ""; echo "
"; Html::closeForm(); } echo "
"; } } /** * @since version 0.90 * * @param $item * @param $withtemplate (default '') * @param $options array */ static function showListForItem(CommonDBTM $item, $withtemplate='', $options=array()) { global $DB, $CFG_GLPI; //default options $params['rand'] = mt_rand(); if (is_array($options) && count($options)) { foreach ($options as $key => $val) { $params[$key] = $val; } } $canedit = $item->canAddItem('Document') && Document::canView(); $columns = array('name' => __('Name'), 'entity' => __('Entity'), 'filename' => __('File'), 'link' => __('Web link'), 'headings' => __('Heading'), 'mime' => __('MIME type')); if ($CFG_GLPI['use_rich_text']) { $columns['tag'] = __('Tag'); } $columns['assocdate'] = __('Date'); if (isset($_GET["order"]) && ($_GET["order"] == "ASC")) { $order = "ASC"; } else { $order = "DESC"; } if ((isset($_GET["sort"]) && !empty($_GET["sort"])) && isset($columns[$_GET["sort"]])) { $sort = "`".$_GET["sort"]."`"; } else { $sort = "`assocdate`"; } if (empty($withtemplate)) { $withtemplate = 0; } $linkparam = ''; if (get_class($item) == 'Ticket') { $linkparam = "&tickets_id=".$item->fields['id']; } $query = "SELECT `glpi_documents_items`.`id` AS assocID, `glpi_documents_items`.`date_mod` AS assocdate, `glpi_entities`.`id` AS entityID, `glpi_entities`.`completename` AS entity, `glpi_documentcategories`.`completename` AS headings, `glpi_documents`.* FROM `glpi_documents_items` LEFT JOIN `glpi_documents` ON (`glpi_documents_items`.`documents_id`=`glpi_documents`.`id`) LEFT JOIN `glpi_entities` ON (`glpi_documents`.`entities_id`=`glpi_entities`.`id`) LEFT JOIN `glpi_documentcategories` ON (`glpi_documents`.`documentcategories_id`=`glpi_documentcategories`.`id`) WHERE `glpi_documents_items`.`items_id` = '".$item->getID()."' AND `glpi_documents_items`.`itemtype` = '".$item->getType()."' "; if (Session::getLoginUserID()) { $query .= getEntitiesRestrictRequest(" AND","glpi_documents",'','',true); } else { // Anonymous access from FAQ $query .= " AND `glpi_documents`.`entities_id`= '0' "; } // Document : search links in both order using union if ($item->getType() == 'Document') { $query .= "UNION SELECT `glpi_documents_items`.`id` AS assocID, `glpi_documents_items`.`date_mod` AS assocdate, `glpi_entities`.`id` AS entityID, `glpi_entities`.`completename` AS entity, `glpi_documentcategories`.`completename` AS headings, `glpi_documents`.* FROM `glpi_documents_items` LEFT JOIN `glpi_documents` ON (`glpi_documents_items`.`items_id`=`glpi_documents`.`id`) LEFT JOIN `glpi_entities` ON (`glpi_documents`.`entities_id`=`glpi_entities`.`id`) LEFT JOIN `glpi_documentcategories` ON (`glpi_documents`.`documentcategories_id`=`glpi_documentcategories`.`id`) WHERE `glpi_documents_items`.`documents_id` = '".$item->getID()."' AND `glpi_documents_items`.`itemtype` = '".$item->getType()."' "; if (Session::getLoginUserID()) { $query .= getEntitiesRestrictRequest(" AND","glpi_documents",'','',true); } else { // Anonymous access from FAQ $query .= " AND `glpi_documents`.`entities_id`='0' "; } } $query .= " ORDER BY $sort $order"; $result = $DB->query($query); $number = $DB->numrows($result); $i = 0; $documents = array(); $used = array(); if ($numrows = $DB->numrows($result)) { while ($data = $DB->fetch_assoc($result)) { $documents[$data['assocID']] = $data; $used[$data['id']] = $data['id']; } } echo "
"; if ($canedit && $number && ($withtemplate < 2)) { Html::openMassiveActionsForm('mass'.__CLASS__.$params['rand']); $massiveactionparams = array('num_displayed' => min($_SESSION['glpilist_limit'], $number), 'container' => 'mass'.__CLASS__.$params['rand']); Html::showMassiveActions($massiveactionparams); } $sort_img = ""; echo ""; $header_begin = ""; $header_top = ''; $header_bottom = ''; $header_end = ''; if ($canedit && $number && ($withtemplate < 2)) { $header_top .= ""; $header_bottom .= ""; } foreach ($columns as $key => $val) { $header_end .= ""; } $header_end .= ""; echo $header_begin.$header_top.$header_end; $used = array(); if ($number) { // Don't use this for document associated to document // To not loose navigation list for current document if ($item->getType() != 'Document') { Session::initNavigateListItems('Document', //TRANS : %1$s is the itemtype name, // %2$s is the name of the item (used for headings of a list) sprintf(__('%1$s = %2$s'), $item->getTypeName(1), $item->getName())); } $document = new Document(); foreach ($documents as $data) { $docID = $data["id"]; $link = NOT_AVAILABLE; $downloadlink = NOT_AVAILABLE; if ($document->getFromDB($docID)) { $link = $document->getLink(); $downloadlink = $document->getDownloadLink($linkparam); } if ($item->getType() != 'Document') { Session::addToNavigateListItems('Document', $docID); } $used[$docID] = $docID; $assocID = $data["assocID"]; echo ""; if ($canedit && ($withtemplate < 2)) { echo ""; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($CFG_GLPI['use_rich_text']) { echo ""; } echo ""; echo ""; $i++; } echo $header_begin.$header_bottom.$header_end; } echo "
".Html::getCheckAllAsCheckbox('mass'.__CLASS__.$params['rand']); $header_top .= "".Html::getCheckAllAsCheckbox('mass'.__CLASS__.$params['rand']); $header_bottom .= "".(($sort == "`$key`") ?$sort_img:""). "$val
"; Html::showMassiveActionCheckBox(__CLASS__, $data["assocID"]); echo "$link".$data['entity']."$downloadlink"; if (!empty($data["link"])) { echo "".$data["link"]; echo ""; } else {; echo " "; } echo "".Dropdown::getDropdownName("glpi_documentcategories", $data["documentcategories_id"]); echo "".$data["mime"].""; echo !empty($data["tag"]) ? Document::getImageTag($data["tag"]) : ''; echo "".Html::convDateTime($data["assocdate"])."
"; if ($canedit && $number && ($withtemplate < 2)) { $massiveactionparams['ontop'] = false; Html::showMassiveActions($massiveactionparams); Html::closeForm(); } echo "
"; } /** * @since version 0.85 * * @see CommonDBRelation::getRelationMassiveActionsPeerForSubForm() **/ static function getRelationMassiveActionsPeerForSubForm(MassiveAction $ma) { switch ($ma->getAction()) { case 'add' : case 'remove' : return 1; case 'add_item' : case 'remove_item' : return 2; } return 0; } /** * @since version 0.85 * * @see CommonDBRelation::getRelationMassiveActionsSpecificities() **/ static function getRelationMassiveActionsSpecificities() { global $CFG_GLPI; $specificities = parent::getRelationMassiveActionsSpecificities(); $specificities['itemtypes'] = Document::getItemtypesThatCanHave(); // Define normalized action for add_item and remove_item $specificities['normalized']['add'][] = 'add_item'; $specificities['normalized']['remove'][] = 'remove_item'; // Set the labels for add_item and remove_item $specificities['button_labels']['add_item'] = $specificities['button_labels']['add']; $specificities['button_labels']['remove_item'] = $specificities['button_labels']['remove']; return $specificities; } } ?>