diff --git a/demo_data/db.sql b/demo_data/db.sql index 8e0a991212..3a17f076c0 100644 --- a/demo_data/db.sql +++ b/demo_data/db.sql @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS %PREFIX%_inventory_fields CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_field_select_options CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_item_data CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_items CASCADE; -DROP TABLE IF EXISTS %PREFIX%_inventory_item_lend_data CASCADE; +DROP TABLE IF EXISTS %PREFIX%_inventory_item_borrow_data CASCADE; diff --git a/install/db_scripts/db.sql b/install/db_scripts/db.sql index 45f25a2301..8995bbd4ae 100644 --- a/install/db_scripts/db.sql +++ b/install/db_scripts/db.sql @@ -54,7 +54,7 @@ DROP TABLE IF EXISTS %PREFIX%_menu CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_fields CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_field_select_options CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_item_data CASCADE; -DROP TABLE IF EXISTS %PREFIX%_inventory_item_lend_data CASCADE; +DROP TABLE IF EXISTS %PREFIX%_inventory_item_borrow_data CASCADE; DROP TABLE IF EXISTS %PREFIX%_inventory_items CASCADE; DROP TABLE IF EXISTS %PREFIX%_saml_clients CASCADE; DROP TABLE IF EXISTS %PREFIX%_sso_keys CASCADE; @@ -916,6 +916,23 @@ COLLATE = utf8_unicode_ci; CREATE UNIQUE INDEX %PREFIX%_idx_usf_name_intern ON %PREFIX%_user_fields (usf_name_intern); CREATE UNIQUE INDEX %PREFIX%_idx_usf_uuid ON %PREFIX%_user_fields (usf_uuid); +/*==============================================================*/ +/* Table: adm_user_field_select_options */ +/*==============================================================*/ +CREATE TABLE %PREFIX%_user_field_select_options +( + ufo_id integer unsigned NOT NULL AUTO_INCREMENT, + ufo_usf_id integer unsigned NOT NULL, -- Connected user field id + ufo_value varchar(255) NOT NULL, -- option value + ufo_system boolean NOT NULL DEFAULT false, -- If true, the option is a system option an not editable + ufo_sequence smallint NOT NULL, -- Position in the list + ufo_obsolete boolean NOT NULL DEFAULT false, -- If true, the option is not available for new entries, but still exists in the database + PRIMARY KEY (ufo_id) +) +ENGINE = InnoDB +DEFAULT character SET = utf8 +COLLATE = utf8_unicode_ci; + /*==============================================================*/ /* Table: adm_user_data */ /*==============================================================*/ @@ -1042,6 +1059,23 @@ COLLATE = utf8_unicode_ci; CREATE UNIQUE INDEX %PREFIX%_idx_inf_name_intern ON %PREFIX%_inventory_fields (inf_org_id, inf_name_intern); CREATE UNIQUE INDEX %PREFIX%_idx_inf_uuid ON %PREFIX%_inventory_fields (inf_uuid); +/*==============================================================*/ +/* Table: adm_inventory_field_select_options */ +/*==============================================================*/ +CREATE TABLE %PREFIX%_inventory_field_select_options +( + ifo_id integer unsigned NOT NULL AUTO_INCREMENT, + ifo_inf_id integer unsigned NOT NULL, -- Connected inventory field id + ifo_value varchar(255) NOT NULL, -- option value + ifo_system boolean NOT NULL DEFAULT false, -- If true, the option is a system option an not editable + ifo_sequence smallint NOT NULL, -- Position in the list + ifo_obsolete boolean NOT NULL DEFAULT false, -- If true, the option is not available for new entries, but still exists in the database + PRIMARY KEY (ifo_id) +) +ENGINE = InnoDB +DEFAULT character SET = utf8 +COLLATE = utf8_unicode_ci; + /*==============================================================*/ /* Table: adm_inventory_item_data */ /*==============================================================*/ @@ -1060,21 +1094,22 @@ COLLATE = utf8_unicode_ci; CREATE UNIQUE INDEX %PREFIX%_idx_ind_inf_ini_id ON %PREFIX%_inventory_item_data (ind_inf_id, ind_ini_id); /*==============================================================*/ -/* Table: adm_inventory_item_lend_data */ +/* Table: adm_inventory_item_borrow_data */ /*==============================================================*/ -CREATE TABLE %PREFIX%_inventory_item_lend_data +CREATE TABLE %PREFIX%_inventory_item_borrow_data ( - inl_id integer unsigned NOT NULL AUTO_INCREMENT, - inl_inf_id integer unsigned NOT NULL, - inl_ini_id integer unsigned NOT NULL, - inl_value varchar(4000), - PRIMARY KEY (inl_id) + inb_id integer unsigned NOT NULL AUTO_INCREMENT, + inb_ini_id integer unsigned NOT NULL, + inb_last_receiver varchar(255) NULL DEFAULT NULL, + inb_borrow_date varchar(100) NULL DEFAULT NULL, + inb_return_date varchar(100) NULL DEFAULT NULL, + PRIMARY KEY (inb_id) ) ENGINE = InnoDB DEFAULT character SET = utf8 COLLATE = utf8_unicode_ci; -CREATE UNIQUE INDEX %PREFIX%_idx_inl_inf_ini_id ON %PREFIX%_inventory_item_lend_data (inl_inf_id, inl_ini_id); +CREATE UNIQUE INDEX %PREFIX%_idx_inb_ini_id ON %PREFIX%_inventory_item_borrow_data (inb_ini_id); /*==============================================================*/ /* Table: adm_inventory_items */ @@ -1085,7 +1120,8 @@ CREATE TABLE %PREFIX%_inventory_items ini_uuid varchar(36) NOT NULL, ini_cat_id integer unsigned NOT NULL, ini_org_id integer unsigned NOT NULL, - ini_former boolean NOT NULL DEFAULT false, + ini_status integer unsigned NOT NULL, + ini_picture blob NULL DEFAULT NULL, ini_usr_id_create integer unsigned, ini_timestamp_create timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, ini_usr_id_change integer unsigned, @@ -1142,22 +1178,6 @@ ENGINE = InnoDB DEFAULT character SET = utf8 COLLATE = utf8_unicode_ci; -/*==============================================================*/ -/* Table: adm_user_field_select_options */ -/*==============================================================*/ -CREATE TABLE %PREFIX%_user_field_select_options -( - ufo_id integer unsigned NOT NULL AUTO_INCREMENT, - ufo_usf_id integer unsigned NOT NULL, -- Connected user field id - ufo_value varchar(255) NOT NULL, -- option value - ufo_sequence smallint NOT NULL, -- Position in the list - ufo_obsolete boolean NOT NULL DEFAULT false, -- If true, the option is not available for new entries, but still exists in the database - PRIMARY KEY (ufo_id) -) -ENGINE = InnoDB -DEFAULT character SET = utf8 -COLLATE = utf8_unicode_ci; - /*==============================================================*/ /* Foreign Key Constraints */ /*==============================================================*/ @@ -1324,6 +1344,9 @@ ALTER TABLE %PREFIX%_user_fields ADD CONSTRAINT %PREFIX%_fk_usf_usr_create FOREIGN KEY (usf_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_usf_usr_change FOREIGN KEY (usf_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; +ALTER TABLE %PREFIX%_user_field_select_options + ADD CONSTRAINT %PREFIX%_fk_ufo_usf FOREIGN KEY (ufo_usf_id) REFERENCES %PREFIX%_user_fields (usf_id) ON DELETE RESTRICT ON UPDATE RESTRICT; + ALTER TABLE %PREFIX%_user_data ADD CONSTRAINT %PREFIX%_fk_usd_usf FOREIGN KEY (usd_usf_id) REFERENCES %PREFIX%_user_fields (usf_id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_usd_usr FOREIGN KEY (usd_usr_id) REFERENCES %PREFIX%_users (usr_id) ON DELETE RESTRICT ON UPDATE RESTRICT; @@ -1344,23 +1367,22 @@ ALTER TABLE %PREFIX%_user_relations ADD CONSTRAINT %PREFIX%_fk_ure_usr_change FOREIGN KEY (ure_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_ure_usr_create FOREIGN KEY (ure_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; -ALTER TABLE %PREFIX%_user_field_select_options - ADD CONSTRAINT %PREFIX%_fk_ufo_usf FOREIGN KEY (ufo_usf_id) REFERENCES %PREFIX%_user_fields (usf_id) ON DELETE RESTRICT ON UPDATE RESTRICT; - ALTER TABLE %PREFIX%_inventory_fields ADD CONSTRAINT %PREFIX%_fk_inf_org FOREIGN KEY (inf_org_id) REFERENCES %PREFIX%_organizations (org_id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_inf_usr_create FOREIGN KEY (inf_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_inf_usr_change FOREIGN KEY (inf_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; +ALTER TABLE %PREFIX%_inventory_field_select_options + ADD CONSTRAINT %PREFIX%_fk_ifo_inf FOREIGN KEY (ifo_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE CASCADE ON UPDATE RESTRICT; + ALTER TABLE %PREFIX%_inventory_item_data ADD CONSTRAINT %PREFIX%_fk_ind_inf FOREIGN KEY (ind_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_ind_ini FOREIGN KEY (ind_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; -ALTER TABLE %PREFIX%_inventory_item_lend_data - ADD CONSTRAINT %PREFIX%_fk_inl_inf FOREIGN KEY (inl_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE RESTRICT ON UPDATE RESTRICT, - ADD CONSTRAINT %PREFIX%_fk_inl_ini FOREIGN KEY (inl_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; - +ALTER TABLE %PREFIX%_inventory_item_borrow_data + ADD CONSTRAINT %PREFIX%_fk_inb_ini FOREIGN KEY (inb_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; ALTER TABLE %PREFIX%_inventory_items ADD CONSTRAINT %PREFIX%_fk_ini_cat FOREIGN KEY (ini_cat_id) REFERENCES %PREFIX%_categories (cat_id) ON DELETE RESTRICT ON UPDATE RESTRICT, + ADD CONSTRAINT %PREFIX%_fk_ini_status FOREIGN KEY (ini_status) REFERENCES %PREFIX%_inventory_field_select_options (ifo_id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_ini_usr_create FOREIGN KEY (ini_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_ini_usr_change FOREIGN KEY (ini_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; diff --git a/install/db_scripts/preferences.php b/install/db_scripts/preferences.php index d95fb65dd3..183a73e29c 100644 --- a/install/db_scripts/preferences.php +++ b/install/db_scripts/preferences.php @@ -87,7 +87,7 @@ 'changelog_table_inventory_field_select_options' => '0', 'changelog_table_inventory_items' => '0', 'changelog_table_inventory_item_data' => '0', - 'changelog_table_inventory_item_lend_data' => '0', + 'changelog_table_inventory_item_borrow_data' => '0', 'changelog_table_saml_clients' => '0', 'changelog_table_oidc_clients' => '0', 'changelog_table_others' => '0', @@ -150,15 +150,19 @@ 'inventory_module_enabled' => '2', 'inventory_items_per_page' => '25', 'inventory_field_history_days' => '365', + 'inventory_item_picture_enabled' => '1', + 'inventory_item_picture_storage' => '0', + 'inventory_item_picture_width' => '130', + 'inventory_item_picture_height' => '170', 'inventory_show_obsolete_select_field_options' => '1', 'inventory_system_field_names_editable' => '0', 'inventory_allow_keeper_edit' => '0', - 'inventory_allowed_keeper_edit_fields' => 'IN_INVENTORY,LAST_RECEIVER,RECEIVED_ON,RECEIVED_BACK_ON', + 'inventory_allowed_keeper_edit_fields' => 'LAST_RECEIVER,BORROW_DATE,RETURN_DATE', 'inventory_current_user_default_keeper' => '0', 'inventory_allow_negative_numbers' => '1', 'inventory_decimal_places' => '1', 'inventory_field_date_time_format' => 'date', - 'inventory_items_disable_lending' => '0', + 'inventory_items_disable_borrowing' => '0', 'inventory_profile_view_enabled' => '1', 'inventory_profile_view' => 'LAST_RECEIVER', 'inventory_export_filename' => $GLOBALS['gL10n']->get('SYS_INVENTORY'), diff --git a/install/db_scripts/update_5_0.xml b/install/db_scripts/update_5_0.xml index 6aa81a6e3d..40e5c7580b 100644 --- a/install/db_scripts/update_5_0.xml +++ b/install/db_scripts/update_5_0.xml @@ -391,6 +391,7 @@ WHERE usf_fn.usf_name_intern = 'FIRST_NAME' AND usf_ln.usf_name_intern = 'LAST_N ufo_id integer unsigned NOT NULL AUTO_INCREMENT, ufo_usf_id integer unsigned NOT NULL, ufo_value varchar(255) NOT NULL, + ufo_system boolean NOT NULL DEFAULT false, ufo_sequence smallint NOT NULL, ufo_obsolete boolean NOT NULL DEFAULT false, PRIMARY KEY (ufo_id) @@ -444,26 +445,27 @@ WHERE usf_fn.usf_name_intern = 'FIRST_NAME' AND usf_ln.usf_name_intern = 'LAST_N ALTER TABLE %PREFIX%_inventory_item_data ADD CONSTRAINT %PREFIX%_fk_ind_inf FOREIGN KEY (ind_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD CONSTRAINT %PREFIX%_fk_ind_ini FOREIGN KEY (ind_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; - CREATE TABLE IF NOT EXISTS %PREFIX%_inventory_item_lend_data ( - inl_id integer unsigned NOT NULL AUTO_INCREMENT, - inl_inf_id integer unsigned NOT NULL, - inl_ini_id integer unsigned NOT NULL, - inl_value varchar(4000), - PRIMARY KEY (inl_id) + CREATE TABLE IF NOT EXISTS %PREFIX%_inventory_item_borrow_data ( + inb_id integer unsigned NOT NULL AUTO_INCREMENT, + inb_ini_id integer unsigned NOT NULL, + inb_last_receiver varchar(255) NULL DEFAULT NULL, + inb_borrow_date varchar(255) NULL DEFAULT NULL, + inb_return_date varchar(255) NULL DEFAULT NULL, + PRIMARY KEY (inb_id) ) ENGINE = InnoDB DEFAULT character SET = utf8 COLLATE = utf8_unicode_ci; - CREATE UNIQUE INDEX %PREFIX%_idx_inl_inf_ini_id ON %PREFIX%_inventory_item_lend_data (inl_inf_id, inl_ini_id); - ALTER TABLE %PREFIX%_inventory_item_lend_data - ADD CONSTRAINT %PREFIX%_fk_inl_inf FOREIGN KEY (inl_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE RESTRICT ON UPDATE RESTRICT, - ADD CONSTRAINT %PREFIX%_fk_inl_ini FOREIGN KEY (inl_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; + CREATE UNIQUE INDEX %PREFIX%_idx_inb_ini_id ON %PREFIX%_inventory_item_borrow_data (inb_ini_id); + ALTER TABLE %PREFIX%_inventory_item_borrow_data + ADD CONSTRAINT %PREFIX%_fk_inb_ini FOREIGN KEY (inb_ini_id) REFERENCES %PREFIX%_inventory_items (ini_id) ON DELETE RESTRICT ON UPDATE RESTRICT; CREATE TABLE IF NOT EXISTS %PREFIX%_inventory_items ( ini_id integer unsigned NOT NULL AUTO_INCREMENT, ini_uuid varchar(36) NOT NULL, ini_cat_id integer unsigned NOT NULL, ini_org_id integer unsigned NOT NULL, - ini_former boolean NOT NULL DEFAULT false, + ini_status integer unsigned NOT NULL, + ini_picture blob NULL DEFAULT NULL, ini_usr_id_create integer unsigned, ini_timestamp_create timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, ini_usr_id_change integer unsigned, @@ -475,9 +477,10 @@ WHERE usf_fn.usf_name_intern = 'FIRST_NAME' AND usf_ln.usf_name_intern = 'LAST_N COLLATE = utf8_unicode_ci; CREATE UNIQUE INDEX %PREFIX%_idx_ini_uuid ON %PREFIX%_inventory_items (ini_uuid); ALTER TABLE %PREFIX%_inventory_items - ADD CONSTRAINT %PREFIX%_fk_ini_cat FOREIGN KEY (ini_cat_id) REFERENCES %PREFIX%_categories (cat_id) ON DELETE RESTRICT ON UPDATE RESTRICT, - ADD CONSTRAINT %PREFIX%_fk_ini_usr_create FOREIGN KEY (ini_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, - ADD CONSTRAINT %PREFIX%_fk_ini_usr_change FOREIGN KEY (ini_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; + ADD CONSTRAINT %PREFIX%_fk_ini_cat FOREIGN KEY (ini_cat_id) REFERENCES %PREFIX%_categories (cat_id) ON DELETE RESTRICT ON UPDATE RESTRICT, + ADD CONSTRAINT %PREFIX%_fk_ini_status FOREIGN KEY (ini_status) REFERENCES %PREFIX%_inventory_field_select_options (ifo_id) ON DELETE RESTRICT ON UPDATE RESTRICT, + ADD CONSTRAINT %PREFIX%_fk_ini_usr_create FOREIGN KEY (ini_usr_id_create) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT, + ADD CONSTRAINT %PREFIX%_fk_ini_usr_change FOREIGN KEY (ini_usr_id_change) REFERENCES %PREFIX%_users (usr_id) ON DELETE SET NULL ON UPDATE RESTRICT; ALTER TABLE %PREFIX%_roles ADD COLUMN rol_inventory_admin boolean NOT NULL DEFAULT false UPDATE %PREFIX%_roles SET rol_inventory_admin = true WHERE rol_administrator = true INSERT INTO %PREFIX%_components (com_type, com_name, com_name_intern, com_version, com_beta) @@ -488,11 +491,11 @@ WHERE usf_fn.usf_name_intern = 'FIRST_NAME' AND usf_ln.usf_name_intern = 'LAST_N UpdateStepsCode::updateStep50InventoryCategories UPDATE %PREFIX%_preferences pr1 SET prf_value = 0 WHERE prf_name = 'inventory_module_enabled' - UpdateStepsCode::updateStep50AddInventoryFields - CREATE TABLE IF NOT EXISTS %PREFIX%_inventory_field_select_options ( + CREATE TABLE IF NOT EXISTS %PREFIX%_inventory_field_select_options ( ifo_id integer unsigned NOT NULL AUTO_INCREMENT, ifo_inf_id integer unsigned NOT NULL, ifo_value varchar(255) NOT NULL, + ifo_system boolean NOT NULL DEFAULT false, ifo_sequence smallint NOT NULL, ifo_obsolete boolean NOT NULL DEFAULT false, PRIMARY KEY (ifo_id) @@ -500,8 +503,9 @@ WHERE usf_fn.usf_name_intern = 'FIRST_NAME' AND usf_ln.usf_name_intern = 'LAST_N ENGINE = InnoDB DEFAULT character SET = utf8 COLLATE = utf8_unicode_ci; - ALTER TABLE %PREFIX%_inventory_field_select_options + ALTER TABLE %PREFIX%_inventory_field_select_options ADD CONSTRAINT %PREFIX%_fk_ifo_inf FOREIGN KEY (ifo_inf_id) REFERENCES %PREFIX%_inventory_fields (inf_id) ON DELETE CASCADE ON UPDATE RESTRICT; + UpdateStepsCode::updateStep50AddInventoryFields UPDATE %PREFIX%_texts SET txt_name = 'SYSMAIL_LOGIN_INFORMATION' WHERE txt_name = 'SYSMAIL_NEW_PASSWORD' stop diff --git a/languages/en.xml b/languages/en.xml index 2146a4b519..595c5c7373 100644 --- a/languages/en.xml +++ b/languages/en.xml @@ -776,12 +776,15 @@ Edit permission for the keeper When this option is enabled, the selected item fields can be modified by the item keeper. Otherwise, these fields are read-only and can only be changed by module administrators. (Default: no) Editable fields by the keeper - The item fields selected here can be edited by the item keeper when the option "Edit permission for the keeper" is enabled. All other fields are read-only and can only be changed by module administrators. (Default: "In Inventory", "Last Recipient", "Borrowed on", "Received back on") + The item fields selected here can be edited by the item keeper when the option "Edit permission for the keeper" is enabled. All other fields are read-only and can only be changed by module administrators. (Default: "Last receiver", "Borrowing date", "Return date") The inventory manager module can be completely disabled, made accessible only to logged-in users, or restricted solely to module administrators via this setting. If access is granted only to logged-in users, then the module is hidden from guests. If only module administrators have access, the module is visible only to users with the "manage inventory" permission. (Default: Enabled) Append date When this option is enabled, the current date in the format YYYY-MM-DD will be prefixed to the export file’s name. (Default: no) Allow negative numbers If this option is enabled, negative numbers can also be entered in fields of type "Number" or "Decimal number". Otherwise, only positive numbers are allowed. (Default: yes) + Borrowing date + The borrowing date of the item to the last recipient + The borrowing date of the item must be before the return date! The category of an item Settings for copying a item: Date representation @@ -794,14 +797,14 @@ Here a field of a consecutive number can be selected. In a selection, the current number is read out and increased according to the specified number.\n\nNOTE: InventoryManager can only tell if a data field is of type "number". Whether a serial number is stored in this data field can not be recognized. Filename The file name of the export file (without file extension). The file name is automatically appended with the export format’s extension. (Default: "Inventory manager") - Current - Former + In use + Retired In the left column of the following table all item fields are displayed. In the right column the columns from the file to be imported are displayed in a selection list. You should now assign all columns from the file you want to import to a item field. Here you can import items from a previous export file or your own file. Import items The following columns of the import file are not assigned to any item fields in InventoryManager: - In inventory - Item is in inventory + Borrow the item + Item borrowing data Item successfully copied Copy the item Create a item @@ -812,15 +815,28 @@ Item successfully deleted Change the item Item successfully changed - Lend item - Item lend data - Item was made to former - Make item former - You can #VAR1_BOLD#. This has the advantage that the data is preserved and you can later always see who has borrowed this item. - Return item - Undo made to former - Do you really want to undo the disposal of the item? - Undone item made to former + Item picture + Select item picture + Current item picture + Delete item picture + Item picture successfully deleted + Enable item pictures + If this option is enabled, item pictures can be uploaded and displayed. If disabled, no item pictures can be uploaded and existing item pictures will not be displayed. (Default: yes) + New item picture + The picture may have a maximum resolution of #VAR1# MegaPixels. The picture file must not be larger than #VAR1# MB and must be in JPG or PNG format. + Review new item picture + Item picture successfully saved + Upload item picture + Do you want to delete the item picture? + Storage location of Item pictures + Please define where to store item pictures (in the database or in folder adm_my_files). When a change is made, current pictures are not copied to new location. If Database is selected, the uploaded picture is scaled to 130 x 170 pixels. (default: Database) + Reinstate the item + Do you really want to reinstate the item? + Item successfully reinstated + Retire the item + You can #VAR1_BOLD#. This has the advantage that the data is preserved and you can later always see who has borrowed this item. + Item successfully retired + Return the item Item field Create new item field Delete the item field @@ -833,29 +849,29 @@ Item name The name of the item Items - Disable borrowing functionality - When this option is enabled, items can no longer be borrowed. Existing borrowing records will still be saved, but will no longer be displayed. (Default: no) + Disable borrowing functionality + When this option is enabled, items can no longer be borrowed. Existing borrowing records will still be saved, but will no longer be displayed. (Default: no) Change items The values of the item fields can be changed here.\n\nThe displayed values will be applied to ALL items! Number of items per page Keeper Keeper of the item - You can mark the item as former. This has the advantage that the data is preserved and you can later always see who has borrowed this item.\n\nIf you want to delete the item, please contact an administrator or the manager of the inventory manager! - You can reinstate the item into the inventory manager.\n\nIf you want to delete the item, please contact an administrator or the manager of the inventory manager!\n\n#VAR1# + You can mark the item as former.\n\nIf you want to delete the item, please contact an administrator or the manager of the inventory manager!\n\n#VAR1# + You can retire the item from the inventory manager. This has the advantage that the data is preserved and you can later always see who has borrowed this item.\n\nIf you want to delete the item, please contact an administrator or the manager of the inventory manager! Last receiver Last receiver of the item There was no new data in the import file! The item #VAR1_BOLD# was changed by #VAR2_BOLD#: The item #VAR1_BOLD# was created by #VAR2_BOLD#: The following item was deleted: - The following item was made to former: - The following item has been reinstated into the inventory: + The following item has been reinstated into the inventory: + The following item has been retired: The following items were imported by #VAR1_BOLD#: An item in the inventory has been changed An item has been added to the inventory An item in the inventory has been deleted - An item in the inventory has been made to former - An item has been reinstated to the inventory + An item has been reinstated to the inventory + An item in the inventory has been retired Items have been imported into the inventory Number Number of items to be added @@ -865,26 +881,26 @@ When this option is enabled, the items managed by a member and those borrowed by a member are displayed in the member's profile view. (Default: yes) Property fields to display in the profile view The table shows the items managed by you: - The table shows the items loaned to you: - Received back on - The date the item was returned to the keeper - Borrowed on - The borrowing date of the item to the last recipient + The table shows the items borrrowed to you: + Return date + The date the item was returned to the keeper + Delete selected items If you select #VAR1_BOLD#, the items along with their associated records will be irrevocably removed from the database, and it will not be possible to view the data of these items later. The selected items have been deleted. Items successfully changed - The selected items have been marked as former. - Mark selection as former - You can also mark the selected items as former by choosing #VAR1_BOLD#. This has the advantage that the data is preserved and you can always later see who borrowed the item. - Undo made selection former - You can reinstate the selected items back into the inventory manager by choosing #VAR1_BOLD#. - The marking as former of the selected items has been undone. + Reinstate selected items + You can reinstate the selected items back into the inventory manager by choosing #VAR1_BOLD#. + Selected items successfully reinstated. + Retire selected items + You can retire the item from the inventory manager by choosing #VAR1_BOLD#. This has the advantage that the data is preserved and you can always later see who borrowed the item. + The selected items have been retired. + Status + The current status of the item Permission to edit the display name of system fields If this option is enabled, the display names of the system item fields can be modified. (Default: no) Current user as default selection If this option is enabled, the current user is used as the default selection for the keeper when creating a new item. (Default: no) User-defined item fields - In this field you can enter the items for the dropdown list or options field. Each line can record one entry for the dropdown list or options field.\n\nFor the item, the text is not stored later; instead, the selected position from the list is saved. Therefore, if you change the text on one line, all items already assigned will immediately receive the new text. However, if you move an entry to a different line, a different entry may be displayed for the item.\n\nFor an options field, an icon can be displayed instead of text. You can specify the name of an icon from the theme folder (e.g.: ok.png), the URL of an external image (e.g.: https://www.example.com/example.jpg), or an icon from the web font “Bootstrap Icons” (#VAR1#https://icons.getbootstrap.com#VAR2#). Optionally, a tooltip for the icon can be set via a vertical bar (e.g.: female.png|female). is #VAR1# from ISO-8859-1 Save original files additionally diff --git a/modules/inventory.php b/modules/inventory.php index 4c8f889906..bd2ff8d122 100644 --- a/modules/inventory.php +++ b/modules/inventory.php @@ -10,6 +10,7 @@ use Admidio\Inventory\Service\ImportService; use Admidio\Inventory\Service\ItemFieldService; use Admidio\Inventory\Service\ItemService; +use Admidio\Infrastructure\Utils\FileSystemUtils; use Admidio\UI\Presenter\InventoryFieldsPresenter; use Admidio\UI\Presenter\InventoryImportPresenter; use Admidio\UI\Presenter\InventoryItemPresenter; @@ -39,7 +40,7 @@ require(__DIR__ . '/../system/login_valid.php'); // Initialize and check the parameters - $getMode = admFuncVariableIsValid($_GET, 'mode', 'string', array('defaultValue' => 'list', 'validValues' => array('list', 'field_list', 'field_edit', 'field_save', 'field_delete', 'check_option_entry_status', 'delete_option_entry', 'sequence', 'item_edit','item_edit_lend', 'item_save', 'item_delete_explain_msg', 'item_delete_keeper_explain_msg', 'item_make_former', 'item_undo_former', 'item_delete', 'import_file_selection', 'import_read_file', 'import_assign_fields', 'import_items', 'print_preview', 'print_xlsx', 'print_ods', 'print_csv-ms', 'print_csv-oo', 'print_pdf', 'print_pdfl'))); + $getMode = admFuncVariableIsValid($_GET, 'mode', 'string', array('defaultValue' => 'list', 'validValues' => array('list', 'field_list', 'field_edit', 'field_save', 'field_delete', 'check_option_entry_status', 'delete_option_entry', 'sequence', 'item_edit', 'item_edit_borrow', 'item_save', 'item_delete_explain_msg', 'item_delete_keeper_explain_msg', 'item_retire', 'item_reinstate', 'item_delete', 'item_picture_show', 'item_picture_show_modal', 'item_picture_choose', 'item_picture_upload', 'item_picture_review', 'item_picture_save', 'item_picture_delete', 'import_file_selection', 'import_read_file', 'import_assign_fields', 'import_items', 'print_preview', 'print_xlsx', 'print_ods', 'print_csv-ms', 'print_csv-oo', 'print_pdf', 'print_pdfl'))); $getinfUUID = admFuncVariableIsValid($_GET, 'uuid', 'uuid'); $getOptionID = admFuncVariableIsValid($_GET, 'option_id', 'int', array('defaultValue' => 0)); $getFieldName = admFuncVariableIsValid($_GET, 'field_name', 'string', array('defaultValue' => "", 'directOutput' => true)); @@ -49,7 +50,7 @@ $postRedirect = admFuncVariableIsValid($_POST, 'redirect', 'numeric', array('defaultValue' => 1)); $postImported = admFuncVariableIsValid($_POST, 'imported', 'numeric', array('defaultValue' => 0)); $getCopy = admFuncVariableIsValid($_GET, 'copy', 'bool', array('defaultValue' => false)); - $getFormer = admFuncVariableIsValid($_GET, 'item_former', 'bool', array('defaultValue' => false)); + $getRetired = admFuncVariableIsValid($_GET, 'item_retired', 'bool', array('defaultValue' => false)); $getRedirectToImport = admFuncVariableIsValid($_GET, 'redirect_to_import', 'bool', array('defaultValue' => false)); $getItemUUIDs = admFuncVariableIsValid($_GET, 'item_uuids', 'array', array('defaultValue' => array())); if (empty($getItemUUIDs)) { @@ -58,7 +59,8 @@ return preg_replace('/adm_inventory_item_/', '', $uuid); }, $getItemUUIDs); } - $getLended = admFuncVariableIsValid($_GET, 'item_lended', 'bool', array('defaultValue' => false)); + $getBorrowed = admFuncVariableIsValid($_GET, 'item_borrowed', 'bool', array('defaultValue' => false)); + $getNewPicture = admFuncVariableIsValid($_GET, 'new_picture', 'bool', array('defaultValue' => false)); // check if module is active if ($gSettingsManager->getInt('inventory_module_enabled') === 0) { @@ -69,6 +71,12 @@ throw new Exception('SYS_NO_RIGHTS'); } + // when saving folders, check whether the subfolder in adm_my_files exists with the corresponding rights + if ((int)$gSettingsManager->get('inventory_item_picture_storage') === 1) { + // Create folder for item pictures in adm_my_files if necessary + FileSystemUtils::createDirectoryIfNotExists(ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures'); + } + switch ($getMode) { case 'list': $headline = $gL10n->get('SYS_INVENTORY'); @@ -216,18 +224,18 @@ $item->show(); break; - case 'item_edit_lend': + case 'item_edit_borrow': // set headline of the script - if ($getLended) { + if ($getBorrowed) { $headline = $gL10n->get('SYS_INVENTORY_ITEM_RETURN'); } else { - $headline = $gL10n->get('SYS_INVENTORY_ITEM_LEND'); + $headline = $gL10n->get('SYS_INVENTORY_ITEM_BORROW'); } $gNavigation->addUrl(CURRENT_URL, $headline); - $item = new InventoryItemPresenter('adm_item_edit_lend'); + $item = new InventoryItemPresenter('adm_item_edit_borrow'); $item->setHeadline($headline); - $item->createEditLendForm($getiniUUID); + $item->createEditBorrowForm($getiniUUID); $item->show(); break; @@ -263,13 +271,13 @@ case 'item_delete_explain_msg': if (count($getItemUUIDs) > 0) { - $undoFormerOnClick = 'callUrlHideElements(\'adm_inventory_item_\', [\'' . implode('\', \'', $getItemUUIDs) . '\'], \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_undo_former')) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; - $formerOnClick = 'callUrlHideElements(\'adm_inventory_item_\', [\'' . implode('\', \'', $getItemUUIDs) . '\'], \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_make_former')) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; + $reinstateOnClick = 'callUrlHideElements(\'adm_inventory_item_\', [\'' . implode('\', \'', $getItemUUIDs) . '\'], \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_reinstate')) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; + $retireOnClick = 'callUrlHideElements(\'adm_inventory_item_\', [\'' . implode('\', \'', $getItemUUIDs) . '\'], \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_retire')) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; $deleteOnClick = 'callUrlHideElements(\'adm_inventory_item_\', [\'' . implode('\', \'', $getItemUUIDs) . '\'], \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete')) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; $headerMsg = $gL10n->get('SYS_NOTE'); } else { - $formerOnClick = 'callUrlHideElement(\'adm_inventory_item_' . $getiniUUID . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_make_former', 'item_uuid' => $getiniUUID)) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; + $retireOnClick = 'callUrlHideElement(\'adm_inventory_item_' . $getiniUUID . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_retire', 'item_uuid' => $getiniUUID)) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; $deleteOnClick = 'callUrlHideElement(\'adm_inventory_item_' . $getiniUUID . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete', 'item_uuid' => $getiniUUID)) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')'; $headerMsg = $gL10n->get('SYS_INVENTORY_ITEM_DELETE'); } @@ -281,13 +289,13 @@ '; echo $msg; break; - case 'item_make_former': + case 'item_retire': // check the CSRF token of the form against the session token SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); if (count($getItemUUIDs) > 0) { foreach ($getItemUUIDs as $itemUuid) { $itemModule = new ItemService($gDb, $itemUuid); - $itemModule->makeItemFormer(); + $itemModule->retireItem(); } - echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_SELECTION_MADE_FORMER'))); + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_SELECTION_RETIRED'))); } else { $itemModule = new ItemService($gDb, $getiniUUID); - $itemModule->makeItemFormer(); - echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_MADE_FORMER'))); + $itemModule->retireItem(); + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_RETIRED'))); } break; - case 'item_undo_former': + case 'item_reinstate': // check the CSRF token of the form against the session token SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); if (count($getItemUUIDs) > 0) { foreach ($getItemUUIDs as $itemUuid) { $itemModule = new ItemService($gDb, $itemUuid); - $itemModule->undoItemFormer(); + $itemModule->reinstateItem(); } - echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_SELECTION_UNDONE_FORMER'))); + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_SELECTION_REINSTATED'))); } else { $itemModule = new ItemService($gDb, $getiniUUID); - $itemModule->undoItemFormer(); - echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_UNDONE_FORMER'))); + $itemModule->reinstateItem(); + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_REINSTATED'))); } break; @@ -392,6 +400,74 @@ } break; #endregion +#region item pictures + case 'item_picture_show': + $itemModule = new ItemService($gDb, $getiniUUID); + $itemModule->showItemPicture($getNewPicture); + break; + + case 'item_picture_show_modal': + $msg = ' + + '; + + echo $msg; + break; + + case 'item_picture_choose': + $headline = $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CHOOSE'); + $gNavigation->addUrl(CURRENT_URL, $headline); + + $item = new InventoryItemPresenter('adm_item_picture_choose'); + $item->setHeadline($headline); + $item->createPictureChooseForm($getiniUUID); + $item->show(); + break; + + case 'item_picture_upload': + $itemModule = new ItemService($gDb, $getiniUUID); + $itemModule->uploadItemPicture(); + + echo json_encode(array('status' => 'success', 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_review', 'item_uuid' => $getiniUUID)))); + break; + + case 'item_picture_review': + $headline = $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_REVIEW'); + $gNavigation->addUrl(CURRENT_URL, $headline); + + $item = new InventoryItemPresenter('adm_item_picture_review'); + $item->setHeadline($headline); + $item->createPictureReviewForm($getiniUUID); + $item->show(); + break; + + case 'item_picture_save': + $itemModule = new ItemService($gDb, $getiniUUID); + $itemModule->saveItemPicture(); + + // back to the home page + // if url stack is bigger then 2 then delete until the edit page is reached + while (count($gNavigation->getStack()) > 2) { + $gNavigation->deleteLastUrl(); + } + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_SAVED'), 'url' => $gNavigation->getUrl())); + break; + + case 'item_picture_delete': + // check the CSRF token of the form against the session token + SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); + + $itemModule = new ItemService($gDb, $getiniUUID); + $itemModule->deleteItemPicture(); + + echo json_encode(array('status' => 'success', 'message' => $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_DELETED'), 'url' => $gNavigation->getUrl())); + break; +#endregion #region import case 'import_file_selection': // check if file_uploads is set to ON in the current server settings... @@ -413,10 +489,7 @@ case 'import_read_file': $import = new ImportService(); $import->readImportFile(); - echo json_encode(array( - 'status' => 'success', - 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'import_assign_fields')) - )); + echo json_encode(array('status' => 'success', 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'import_assign_fields')))); break; case 'import_assign_fields': @@ -479,9 +552,8 @@ break; } } catch (Throwable $e) { - if (in_array($getMode, array('field_save', 'field_delete', 'sequence', 'item_save', 'import_read_file', 'import_items'))) { - echo// PHP namespaces - json_encode(array('status' => 'error', 'message' => $e->getMessage())); + if (in_array($getMode, array('field_save', 'field_delete', 'check_option_entry_status', 'delete_option_entry', 'sequence', 'item_save', 'item_delete_explain_msg', 'item_delete_keeper_explain_msg', 'item_retire', 'item_reinstate', 'item_delete', 'item_picture_show', 'item_picture_show_modal', 'item_picture_upload', 'item_picture_save', 'item_picture_delete', 'import_read_file', 'import_items'))) { + echo json_encode(array('status' => 'error', 'message' => $e->getMessage())); } else { $gMessage->show($e->getMessage()); } diff --git a/modules/profile/profile.php b/modules/profile/profile.php index e0c46296c3..b897e47d69 100644 --- a/modules/profile/profile.php +++ b/modules/profile/profile.php @@ -397,7 +397,7 @@ function formSubmitEvent(rolesAreaId = "") { if ($gSettingsManager->getInt('inventory_module_enabled') > 0 && $gSettingsManager->getBool('inventory_profile_view_enabled')) { // ****************************************************************************** - // Block with inventory items (optimized) + // Block with inventory items // ****************************************************************************** $itemsKeeper = new ItemsData($gDb, $gCurrentOrgId); $itemsReceiver = new ItemsData($gDb, $gCurrentOrgId); @@ -408,11 +408,11 @@ function formSubmitEvent(rolesAreaId = "") { // Determine creation mode based on available items $creationMode = 'none'; - if (!empty($itemsKeeper->getItems()) && (empty($itemsReceiver->getItems()) || $gSettingsManager->GetBool('inventory_items_disable_lending'))) { + if (!empty($itemsKeeper->getItems()) && (empty($itemsReceiver->getItems()) || $gSettingsManager->GetBool('inventory_items_disable_borrowing'))) { $creationMode = 'keeper'; - } elseif (empty($itemsKeeper->getItems()) && (!empty($itemsReceiver->getItems()) && !$gSettingsManager->GetBool('inventory_items_disable_lending'))) { + } elseif (empty($itemsKeeper->getItems()) && (!empty($itemsReceiver->getItems()) && !$gSettingsManager->GetBool('inventory_items_disable_borrowing'))) { $creationMode = 'receiver'; - } elseif (!empty($itemsKeeper->getItems()) && (!empty($itemsReceiver->getItems()) && !$gSettingsManager->GetBool('inventory_items_disable_lending'))) { + } elseif (!empty($itemsKeeper->getItems()) && (!empty($itemsReceiver->getItems()) && !$gSettingsManager->GetBool('inventory_items_disable_borrowing'))) { $creationMode = 'both'; } @@ -440,7 +440,7 @@ function setupDataTable($page, $tableId, $templateData) $page->assignSmartyVariable('keeperList', $templateData); $page->assignSmartyVariable('keeperListHeader', $gL10n->get('SYS_INVENTORY') . ' (' . $gL10n->get('SYS_VIEW') . ': ' . $itemsKeeper->getProperty('KEEPER', 'inf_name') . ')'); if ($gSettingsManager->getInt('inventory_module_enabled') !== 3 || ($gSettingsManager->getInt('inventory_module_enabled') === 3 && $gCurrentUser->isAdministratorInventory())) { - $page->assignSmartyVariable('urlInventoryKeeper', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter' => 2, 'items_filter_keeper' => $user->getValue('usr_id')))); + $page->assignSmartyVariable('urlInventoryKeeper', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter_status' => 0, 'items_filter_keeper' => $user->getValue('usr_id')))); } break; @@ -451,7 +451,7 @@ function setupDataTable($page, $tableId, $templateData) $page->assignSmartyVariable('receiverList', $templateData); $page->assignSmartyVariable('receiverListHeader', $gL10n->get('SYS_INVENTORY') . ' (' . $gL10n->get('SYS_VIEW') . ': ' . $itemsReceiver->getProperty('LAST_RECEIVER', 'inf_name') . ')'); if ($gSettingsManager->getInt('inventory_module_enabled') !== 3 || ($gSettingsManager->getInt('inventory_module_enabled') === 3 && $gCurrentUser->isAdministratorInventory())) { - $page->assignSmartyVariable('urlInventoryReceiver', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter' => 2, 'items_filter_keeper' => $user->getValue('usr_id')))); + $page->assignSmartyVariable('urlInventoryReceiver', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter_status' => 0, 'items_filter_last_receiver' => $user->getValue('usr_id')))); } break; @@ -465,13 +465,13 @@ function setupDataTable($page, $tableId, $templateData) $page->assignSmartyVariable('keeperList', $templateDataKeeper); $page->assignSmartyVariable('keeperListHeader', $gL10n->get('SYS_INVENTORY') . ' (' . $gL10n->get('SYS_VIEW') . ': ' . $itemsKeeper->getProperty('KEEPER', 'inf_name') . ')'); if ($gSettingsManager->getInt('inventory_module_enabled') !== 3 || ($gSettingsManager->getInt('inventory_module_enabled') === 3 && $gCurrentUser->isAdministratorInventory())) { - $page->assignSmartyVariable('urlInventoryKeeper', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter' => 2, 'items_filter_keeper' => $user->getValue('usr_id')))); + $page->assignSmartyVariable('urlInventoryKeeper', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter_status' => 0, 'items_filter_keeper' => $user->getValue('usr_id')))); } $page->assignSmartyVariable('receiverList', $templateDataReceiver); $page->assignSmartyVariable('receiverListHeader', $gL10n->get('SYS_INVENTORY') . ' (' . $gL10n->get('SYS_VIEW') . ': ' . $itemsReceiver->getProperty('LAST_RECEIVER', 'inf_name') . ')'); if ($gSettingsManager->getInt('inventory_module_enabled') !== 3 || ($gSettingsManager->getInt('inventory_module_enabled') === 3 && $gCurrentUser->isAdministratorInventory())) { - $page->assignSmartyVariable('urlInventoryReceiver', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter' => 2, 'items_filter_keeper' => $user->getValue('usr_id')))); + $page->assignSmartyVariable('urlInventoryReceiver', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('items_filter_status' => 0, 'items_filter_last_receiver' => $user->getValue('usr_id')))); } break; diff --git a/src/Changelog/Entity/LogChanges.php b/src/Changelog/Entity/LogChanges.php index c18807bf66..f8a3453ccb 100644 --- a/src/Changelog/Entity/LogChanges.php +++ b/src/Changelog/Entity/LogChanges.php @@ -135,8 +135,8 @@ private function connectReferencedTable(string $table) case 'inventory_items': $this->connectAdditionalTable(TBL_INVENTORY_ITEMS, 'ini_id', 'log_record_id'); break; - case 'inventory_item_lend_data': - $this->connectAdditionalTable(TBL_INVENTORY_ITEM_LEND_DATA, 'inl_id', 'log_record_id'); + case 'inventory_item_borrow_data': + $this->connectAdditionalTable(TBL_INVENTORY_ITEM_BORROW_DATA, 'inb_id', 'log_record_id'); break; case 'inventory_item_data': $this->connectAdditionalTable(TBL_INVENTORY_ITEM_DATA, 'ind_id', 'log_record_id'); diff --git a/src/Changelog/Service/ChangelogService.php b/src/Changelog/Service/ChangelogService.php index 04937f9b0e..e489ef4660 100644 --- a/src/Changelog/Service/ChangelogService.php +++ b/src/Changelog/Service/ChangelogService.php @@ -17,8 +17,6 @@ use Admidio\Forum\Entity\Post; use Admidio\Inventory\Entity\ItemField; use Admidio\Inventory\Entity\Item; -use Admidio\Inventory\Entity\ItemData; -use Admidio\Inventory\Entity\ItemLendData; use Admidio\Roles\Entity\ListColumns; use Admidio\Roles\Entity\ListConfiguration; @@ -192,7 +190,7 @@ public static function getTableLabel(mixed $table = null): array|string { 'inventory_field_select_options' => 'SYS_INVENTORY_ITEMFIELD_SELECT_OPTIONS', 'inventory_items' => 'SYS_INVENTORY_ITEMS', 'inventory_item_data' => 'SYS_INVENTORY_ITEM_DATA', - 'inventory_item_lend_data' => 'SYS_INVENTORY_ITEM_LEND_DATA', + 'inventory_item_borrow_data' => 'SYS_INVENTORY_ITEM_BORROW_DATA', 'organizations' => 'SYS_ORGANIZATION', 'menu' => 'SYS_MENU_ITEM', @@ -325,7 +323,7 @@ public static function getObjectForTable(string $module): Entity | null { case 'inventory_fields': return new ItemField($gDb); case 'inventory_item_data': - case 'inventory_item_lend_data': + case 'inventory_item_borrow_data': case 'inventory_items': return new Item($gDb); default: @@ -525,7 +523,8 @@ public static function getFieldTranslations(): array 'inf_required_input' => array('name' => 'SYS_REQUIRED_INPUT', 'type' => 'BOOL'), 'inf_sequence' => 'SYS_ORDER', 'ini_cat_id' => array('name' => 'SYS_CATEGORY', 'type' => 'CATEGORY'), - 'ini_former' => array('name' => 'SYS_INVENTORY_ITEM_MADE_FORMER', 'type' => 'BOOL'), + 'ini_status' => array('name' => 'SYS_INVENTORY_STATUS'), + 'ini_picture' => array('name' => 'SYS_INVENTORY_ITEM_PICTURE'), 'ind_value_bool' => array('name' => 'SYS_VALUE', 'type' => 'BOOL'), 'ind_value_date' => array('name' => 'SYS_VALUE', 'type' => 'DATE'), 'ind_value_mail' => array('name' => 'SYS_VALUE', 'type' => 'EMAIL'), @@ -533,17 +532,13 @@ public static function getFieldTranslations(): array 'ind_value_icon' => array('name' => 'SYS_VALUE', 'type' => 'ICON'), 'ind_value_usr' => array('name' => 'SYS_VALUE', 'type' => 'USER'), 'ind_value' => 'SYS_VALUE', - 'inl_value_bool' => array('name' => 'SYS_VALUE', 'type' => 'BOOL'), - 'inl_value_date' => array('name' => 'SYS_VALUE', 'type' => 'DATE'), - 'inl_value_mail' => array('name' => 'SYS_VALUE', 'type' => 'EMAIL'), - 'inl_value_url' => array('name' => 'SYS_VALUE', 'type' => 'URL'), - 'inl_value_icon' => array('name' => 'SYS_VALUE', 'type' => 'ICON'), - 'inl_value_usr' => array('name' => 'SYS_VALUE', 'type' => 'USER'), - 'inl_value' => 'SYS_VALUE', + 'inb_last_receiver' => array('name' => 'SYS_INVENTORY_LAST_RECEIVER', 'type' => 'USER'), + 'inb_borrow_date' => array('name' => 'SYS_INVENTORY_BORROW_DATE', 'type' => 'DATE'), + 'inb_return_date' => array('name' => 'SYS_INVENTORY_RETURN_DATE', 'type' => 'DATE'), 'ifo_value' => 'SYS_VALUE', 'ifo_inf_id' => 'SYS_INVENTORY_ITEMFIELD', 'ifo_sequence' => 'SYS_ORDER', - 'ifo_obsolete' => array('name' => 'SYS_DELETED', 'type' => 'BOOL'), + 'ifo_obsolete' => array('name' => 'SYS_DELETED', 'type' => 'BOOL'), 'lnk_name' => 'SYS_LINK_NAME', 'lnk_description' => 'SYS_DESCRIPTION', @@ -623,7 +618,7 @@ public static function getFieldTranslations(): array 'cat_name_intern' => 'SYS_INTERNAL_NAME', 'cat_org_id' => array('name' => 'SYS_ORGANIZATION', 'type' => 'ORG'), //'cat_type' => '', // Holds indicators like USF, ROL, LNK, EVT, ANN, - 'cat_system' => array('name' => 'SYS_SSYSTEM', 'type' => 'BOOL'), + 'cat_system' => array('name' => 'SYS_SYSTEM', 'type' => 'BOOL'), 'cat_default' => array('name' => $gL10n->get('SYS_DEFAULT_VAR', array($gL10n->get('SYS_CATEGORY'))), 'type' => 'BOOL'), 'cat_sequence' => 'SYS_ORDER', @@ -732,8 +727,8 @@ public static function createLink(string $text, string $module, int|string $id, case 'inventory_item_data' : // Fall through case 'inventory_items' : $url = SecurityUtils::encodeUrl( ADMIDIO_URL.FOLDER_MODULES.'/inventory.php',array('mode' => 'item_edit', 'item_uuid' => $uuid)); break; - case 'inventory_item_lend_data' : - $url = SecurityUtils::encodeUrl( ADMIDIO_URL.FOLDER_MODULES.'/inventory.php', array('mode' => 'item_edit_lend', 'item_uuid' => $uuid)); break; + case 'inventory_item_borrow_data' : + $url = SecurityUtils::encodeUrl( ADMIDIO_URL.FOLDER_MODULES.'/inventory.php', array('mode' => 'item_edit_borrow', 'item_uuid' => $uuid)); break; case 'links' : $url = SecurityUtils::encodeUrl( ADMIDIO_URL.FOLDER_MODULES.'/links/links_new.php', array('link_uuid' => $uuid)); break; case 'lists' : diff --git a/src/InstallationUpdate/Service/UpdateStepsCode.php b/src/InstallationUpdate/Service/UpdateStepsCode.php index 91c13513a5..6adc6ece08 100644 --- a/src/InstallationUpdate/Service/UpdateStepsCode.php +++ b/src/InstallationUpdate/Service/UpdateStepsCode.php @@ -88,11 +88,11 @@ public static function updateStep50AddInventoryFields() $arrItemFields = array( array('inf_type' => 'TEXT', 'inf_name_intern' => 'ITEMNAME', 'inf_name' => 'SYS_INVENTORY_ITEMNAME', 'inf_description' => 'SYS_INVENTORY_ITEMNAME_DESC', 'inf_required_input' => 1, 'inf_sequence' => 0), array('inf_type' => 'CATEGORY', 'inf_name_intern' => 'CATEGORY', 'inf_name' => 'SYS_CATEGORY', 'inf_description' => 'SYS_INVENTORY_CATEGORY_DESC', 'inf_required_input' => 1, 'inf_sequence' => 1), - array('inf_type' => 'TEXT', 'inf_name_intern' => 'KEEPER', 'inf_name' => 'SYS_INVENTORY_KEEPER', 'inf_description' => 'SYS_INVENTORY_KEEPER_DESC', 'inf_required_input' => 0, 'inf_sequence' => 2), - array('inf_type' => 'CHECKBOX', 'inf_name_intern' => 'IN_INVENTORY', 'inf_name' => 'SYS_INVENTORY_IN_INVENTORY', 'inf_description' => 'SYS_INVENTORY_IN_INVENTORY_DESC', 'inf_required_input' => 0, 'inf_sequence' => 3), + array('inf_type' => 'DROPDOWN', 'inf_name_intern' => 'STATUS', 'inf_name' => 'SYS_INVENTORY_STATUS', 'inf_description' => 'SYS_INVENTORY_STATUS_DESC', 'inf_required_input' => 1, 'inf_sequence' => 2), + array('inf_type' => 'TEXT', 'inf_name_intern' => 'KEEPER', 'inf_name' => 'SYS_INVENTORY_KEEPER', 'inf_description' => 'SYS_INVENTORY_KEEPER_DESC', 'inf_required_input' => 0, 'inf_sequence' => 3), array('inf_type' => 'TEXT', 'inf_name_intern' => 'LAST_RECEIVER', 'inf_name' => 'SYS_INVENTORY_LAST_RECEIVER', 'inf_description' => 'SYS_INVENTORY_LAST_RECEIVER_DESC', 'inf_required_input' => 0, 'inf_sequence' => 4), - array('inf_type' => 'DATE', 'inf_name_intern' => 'RECEIVED_ON', 'inf_name' => 'SYS_INVENTORY_RECEIVED_ON', 'inf_description' => 'SYS_INVENTORY_RECEIVED_ON_DESC', 'inf_required_input' => 0, 'inf_sequence' => 5), - array('inf_type' => 'DATE', 'inf_name_intern' => 'RECEIVED_BACK_ON', 'inf_name' => 'SYS_INVENTORY_RECEIVED_BACK_ON', 'inf_description' => 'SYS_INVENTORY_RECEIVED_BACK_ON_DESC', 'inf_required_input' => 0, 'inf_sequence' => 6) + array('inf_type' => 'DATE', 'inf_name_intern' => 'BORROW_DATE', 'inf_name' => 'SYS_INVENTORY_BORROW_DATE', 'inf_description' => 'SYS_INVENTORY_BORROW_DATE_DESC', 'inf_required_input' => 0, 'inf_sequence' => 5), + array('inf_type' => 'DATE', 'inf_name_intern' => 'RETURN_DATE', 'inf_name' => 'SYS_INVENTORY_RETURN_DATE', 'inf_description' => 'SYS_INVENTORY_RETURN_DATE_DESC', 'inf_required_input' => 0, 'inf_sequence' => 6) ); $sql = 'SELECT org_id, org_shortname FROM ' . TBL_ORGANIZATIONS; @@ -114,6 +114,24 @@ public static function updateStep50AddInventoryFields() } } + // add default options for the status field + $sql = 'SELECT inf_id FROM ' . TBL_INVENTORY_FIELDS . ' + WHERE inf_name_intern = \'STATUS\''; + $statusFieldId = self::$db->queryPrepared($sql)->fetchColumn(); + + if ($statusFieldId !== false) { + $arrStatusOptions = array( + array('inf_name' => 'SYS_INVENTORY_FILTER_IN_USE_ITEMS', 'ifo_sequence' => 1), + array('inf_name' => 'SYS_INVENTORY_FILTER_RETIRED_ITEMS', 'ifo_sequence' => 2), + ); + + foreach ($arrStatusOptions as $statusOption) { + $sql = 'INSERT INTO ' . TBL_INVENTORY_FIELD_OPTIONS . ' + (ifo_inf_id, ifo_value, ifo_system, ifo_sequence) + VALUES (?, ?, ?, ?)'; + self::$db->queryPrepared($sql, array($statusFieldId, $statusOption['inf_name'], true, $statusOption['ifo_sequence'])); + } + } } /** diff --git a/src/Inventory/Entity/Item.php b/src/Inventory/Entity/Item.php index b7979531b2..e9565660a2 100644 --- a/src/Inventory/Entity/Item.php +++ b/src/Inventory/Entity/Item.php @@ -6,7 +6,10 @@ use Admidio\Infrastructure\Database; use Admidio\Infrastructure\Entity\Entity; use Admidio\Inventory\ValueObjects\ItemsData; +use Admidio\Inventory\Entity\ItemData; +use Admidio\Inventory\Entity\SelectOptions; use Admidio\Changelog\Entity\LogChanges; +use Admidio\Infrastructure\Language; /** * @brief Class manages access to database table adm_files @@ -129,6 +132,45 @@ public function readableName(): string $itemData->readDataByColumns(array('ind_ini_id' => $this->itemId, 'ind_inf_id' => $this->mItemsData->getProperty('ITEMNAME', 'inf_id'))); return $itemData->getValue('ind_value'); } + + /** + * Get the status of the item. + * @return int The status of the item. + */ + public function getStatus(): int + { + return $this->getValue('ini_status'); + } + + /** + * Check if the item is retired. + * @return bool Returns true if the item is retired, false otherwise. + */ + public function isRetired(): bool + { + global $gDb; + $optionId = $this->getStatus(); + $option = new SelectOptions($gDb, $this->mItemsData->getProperty('STATUS', 'inf_id')); + if ($option->readDataById($optionId)) { + return $option->getValue('ifo_value') === 'SYS_INVENTORY_FILTER_RETIRED_ITEMS'; + } + return false; + } + + /** + * Check if the item is in use. + * @return bool Returns true if the item is in use, false otherwise. + */ + public function isInUse(): bool + { + global $gDb; + $optionId = $this->getStatus(); + $option = new SelectOptions($gDb, $this->mItemsData->getProperty('STATUS', 'inf_id')); + if ($option->readDataById($optionId)) { + return $option->getValue('ifo_value') === 'SYS_INVENTORY_FILTER_IN_USE_ITEMS'; + } + return false; + } /** * Retrieve the list of database fields that are ignored for the changelog. @@ -153,7 +195,7 @@ public function getIgnoredLogColumns(): array * @throws Exception */ protected function adjustLogEntry(LogChanges $logEntry): void - { + { $itemName = $this->mItemsData->getValue('ITEMNAME', 'database'); if (isset($_POST['INF-ITEMNAME']) && $itemName === '') { $itemName = $_POST['INF-ITEMNAME']; @@ -161,6 +203,21 @@ protected function adjustLogEntry(LogChanges $logEntry): void elseif (!isset( $_POST['INF-ITEMNAME']) && $itemName === '') { $itemName = $logEntry->getValue('log_record_name'); } + + // If the item status is changed convert the status id to the actual status text + if ($logEntry->getValue('log_field') === 'ini_status') { + global $gDb; + $itemStatusIdNew = (int)$logEntry->getValue('log_value_new'); + $itemStatusIdOld = (int)$logEntry->getValue('log_value_old'); + $option = new SelectOptions($gDb, $this->mItemsData->getProperty('STATUS', 'inf_id')); + if ($option->readDataById($itemStatusIdNew)) { + $logEntry->setValue('log_value_new', Language::translateIfTranslationStrId($option->getValue('ifo_value'))); + } + if ($option->readDataById($itemStatusIdOld)) { + $logEntry->setValue('log_value_old', Language::translateIfTranslationStrId($option->getValue('ifo_value'))); + } + } + $logEntry->setValue('log_record_name', $itemName); $logEntry->setValue('log_related_id', $logEntry->getValue('log_record_id')); } diff --git a/src/Inventory/Entity/ItemBorrowData.php b/src/Inventory/Entity/ItemBorrowData.php new file mode 100644 index 0000000000..9ef653ec30 --- /dev/null +++ b/src/Inventory/Entity/ItemBorrowData.php @@ -0,0 +1,94 @@ +mItemsData = clone $itemsData; // create explicit a copy of the object (param is in PHP5 a reference) + } else { + $this->mItemsData = new ItemsData($database, $gCurrentOrgId); + } + + parent::__construct($database, TBL_INVENTORY_ITEM_BORROW_DATA, 'inb', $id); + } + + public function updateRecordId(int $recordId) : void + { + if ($recordId !== 0) { + $this->setValue('inb_id', $recordId); + $this->newRecord = false; + $this->insertRecord = false; + } + } + + /** + * Retrieve the list of database fields that are ignored for the changelog. + * Some tables contain columns _usr_id_create, timestamp_create, etc. We do not want + * to log changes to these columns. + * The guestbook table also contains gbc_org_id and gbc_ip_address columns, + * which we don't want to log. + * @return array Returns the list of database columns to be ignored for logging. + */ + public function getIgnoredLogColumns(): array + { + return array_merge(parent::getIgnoredLogColumns(), + ['inb_id', 'inb_ini_id']/* , + ($this->newRecord)?[$this->columnPrefix.'_text']:[] */ + ); + } + + /** + * Adjust the changelog entry for this db record: Add the first forum post as a related object + * @param LogChanges $logEntry The log entry to adjust + * @return void + * @throws Exception + */ + protected function adjustLogEntry(LogChanges $logEntry): void + { + global $gDb; + + $itemID = $this->getValue('inb_ini_id'); + $item = new Item($gDb, $this->mItemsData, $itemID); + $itemUUID = $item->getValue('ini_uuid'); + $itemName = $item->readableName(); + + $logEntry->setValue('log_record_name', $itemName); + $logEntry->setValue('log_record_uuid', $itemUUID); + } +} diff --git a/src/Inventory/Entity/ItemData.php b/src/Inventory/Entity/ItemData.php index 59aff3c6c5..8e1985728d 100644 --- a/src/Inventory/Entity/ItemData.php +++ b/src/Inventory/Entity/ItemData.php @@ -88,7 +88,7 @@ protected function logItemfieldChange(?string $oldval = null, ?string $newval = // Category changes are logged in the inventory items table return true; } - elseif ($infType === 'DROPDOWN' || $infType === 'RADIOBUTTON') { + elseif ($infType === 'DROPDOWN' || $infType === 'DROPDOWN_MULTISELECT' || $infType === 'RADIOBUTTON') { $vallist = $this->mItemsData->getProperty($fieldNameIntern, 'ifo_inf_options'); if (isset($vallist[$oldval])) { $oldval = $vallist[$oldval]; diff --git a/src/Inventory/Entity/ItemField.php b/src/Inventory/Entity/ItemField.php index 558b146234..dc4a77c443 100644 --- a/src/Inventory/Entity/ItemField.php +++ b/src/Inventory/Entity/ItemField.php @@ -81,11 +81,6 @@ public function delete(): bool WHERE ifo_inf_id = ? -- $infId'; $this->db->queryPrepared($sql, array($infId)); - // delete all data of this field in the item lend data table - $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_LEND_DATA . ' - WHERE inl_inf_id = ? -- $infId'; - $this->db->queryPrepared($sql, array($infId)); - $return = parent::delete(); $this->db->endTransaction(); @@ -140,7 +135,7 @@ public function getValue($fieldNameIntern, $format = '', bool $withObsoleteEnrie if (!isset($this->dbColumns['inf_description'])) { $value = ''; } elseif ($format === 'database') { - $value = html_entity_decode(StringUtils::strStripTags(Language::translateIfTranslationStrId($this->dbColumns['inf_description'])), ENT_QUOTES, 'UTF-8'); + $value = html_entity_decode(StringUtils::strStripTags($this->dbColumns['inf_description']), ENT_QUOTES, 'UTF-8'); } else { $value = Language::translateIfTranslationStrId($this->dbColumns['inf_description']); } @@ -167,7 +162,7 @@ public function getValue($fieldNameIntern, $format = '', bool $withObsoleteEnrie break; case 'ifo_inf_options': - if ($this->dbColumns['inf_type'] === 'DROPDOWN' || $this->dbColumns['inf_type'] === 'RADIO_BUTTON') { + if ($this->dbColumns['inf_type'] === 'DROPDOWN' || $this->dbColumns['inf_type'] === 'DROPDOWN_MULTISELECT' || $this->dbColumns['inf_type'] === 'RADIO_BUTTON') { $arrOptionValuesWithKeys = array(); // array with option values and keys that represents the internal value $arrOptions = $value; diff --git a/src/Inventory/Entity/ItemLendData.php b/src/Inventory/Entity/ItemLendData.php deleted file mode 100644 index ba80269f96..0000000000 --- a/src/Inventory/Entity/ItemLendData.php +++ /dev/null @@ -1,160 +0,0 @@ -mItemsData = clone $itemsData; // create explicit a copy of the object (param is in PHP5 a reference) - } else { - $this->mItemsData = new ItemsData($database, $gCurrentOrgId); - } - - $this->connectAdditionalTable(TBL_INVENTORY_FIELDS, 'inf_id', 'inl_inf_id'); - parent::__construct($database, TBL_INVENTORY_ITEM_LEND_DATA, 'inl', $id); - } - - /** - * Since creation means setting value from NULL to something, deletion mean setting the field to empty, - * we need one generic change log function that is called on creation, deletion and modification. - * - * The log entries are: record ID for inl_id, but uuid and link point to User id. - * log_field is the inf_id and log_field_name is the fields external name. - * - * @param string $oldval previous value before the change (can be null) - * @param string $newval new value after the change (can be null) - * @return true returns **true** if no error occurred - */ - protected function logLendChange(?string $oldval = null, ?string $newval = null) : bool { - global $gDb, $gProfileFields; - - if ($oldval === $newval) { - // No change, nothing to log - return true; - } - - $table = str_replace(TABLE_PREFIX . '_', '', $this->tableName); - - $itemID = $this->getValue('inl_ini_id'); - $item = new Item($gDb, $this->mItemsData, $itemID); - $itemUUID = $item->getValue('ini_uuid'); - - $id = $this->dbColumns[$this->keyColumnName]; - $field = $this->getValue('inl_inf_id'); - $fieldName = 'inl_value'; - $objectName = $this->mItemsData->getPropertyById($field, 'inf_name', 'database'); - $fieldNameIntern = $this->mItemsData->getPropertyById($field, 'inf_name_intern', 'database'); - $infType = $this->mItemsData->getPropertyById($field, 'inf_type'); - - if ($infType === 'TEXT') { - if ($fieldNameIntern === 'KEEPER') { - $fieldName = $fieldName . '_usr'; - } - elseif ($fieldNameIntern === 'LAST_RECEIVER') { - $user = new User($this->db, $gProfileFields); - if (is_numeric($oldval) && is_numeric($newval)) { - $foundOld = $user->readDataById($oldval); - $foundNew = $user->readDataById($newval); - if ($foundOld && $foundNew) { - $fieldName = $fieldName . '_usr'; - } - } - elseif (is_numeric($oldval)) { - if ($user->readDataById($oldval)) { - $oldval = '' . $user->getValue('LAST_NAME') . ', ' . $user->getValue('FIRST_NAME') . ''; - } - } elseif (is_numeric($newval)) { - if ($user->readDataById($newval)) { - $newval = '' . $user->getValue('LAST_NAME') . ', ' . $user->getValue('FIRST_NAME') . ''; - } - } - } - } - elseif ($infType === 'DATE') { - $fieldName = $fieldName . '_date'; - } - - $logEntry = new LogChanges($this->db, $table); - $logEntry->setLogModification($table, $id, $itemUUID, $objectName, $field, $fieldName, $oldval, $newval); - /* $logEntry->setLogRelated($itemUUID, $itemName); */ - $logEntry->setLogLinkID($itemID); - return $logEntry->save(); - } - - - /** - * Logs creation of the DB record -> For user fields, no need to log anything as - * the actual value change from NULL to something will be logged as a modification - * immediately after creation, anyway. - * - * @return true Returns **true** if no error occurred - * @throws Exception - */ - public function logCreation(): bool { return true; } - - /** - * Logs deletion of the DB record - * Deletion actually means setting the user field to an empty value, so log a change to empty instead of deletion! - * - * @return true Returns **true** if no error occurred - */ - public function logDeletion(): bool - { - $oldval = $this->columnsInfos['inl_value']['previousValue']; - return $this->logLendChange($oldval, null); - } - - - /** - * Logs all modifications of the DB record - * @param array $logChanges Array of all changes, generated by the save method - * @return true Returns **true** if no error occurred - * @throws Exception - */ - public function logModifications(array $logChanges): bool - { - if ($logChanges['inl_value']) { - return $this->logLendChange($logChanges['inl_value']['oldValue'], $logChanges['inl_value']['newValue']); - } else { - // Nothing to log at all! - return true; - } - } -} diff --git a/src/Inventory/Entity/SelectOptions.php b/src/Inventory/Entity/SelectOptions.php index 2eee461c64..71c525d630 100644 --- a/src/Inventory/Entity/SelectOptions.php +++ b/src/Inventory/Entity/SelectOptions.php @@ -55,7 +55,7 @@ public function readDataByFieldId(int $infId = 0) : void // if id is set than read the data of the recordset if ($this->infId > 0) { - $sql = 'SELECT ifo_id, ifo_value, ifo_sequence, ifo_obsolete + $sql = 'SELECT ifo_id, ifo_value, ifo_system, ifo_sequence, ifo_obsolete FROM ' . TBL_INVENTORY_FIELD_OPTIONS . ' WHERE ifo_inf_id = ? -- $infId ORDER BY ifo_sequence'; @@ -104,6 +104,7 @@ public function getAllOptions(bool $withObsoleteEnries = true) : array $values[$value['ifo_id']] = array( 'id' => $value['ifo_id'], 'value' => $value['ifo_value'], + 'system' => $value['ifo_system'], 'sequence' => $value['ifo_sequence'], 'obsolete' => $value['ifo_obsolete'] ); @@ -252,10 +253,15 @@ public function setOptionValues(array $newValues) : bool $currentSequence = array(); foreach ($allOptions as $option) { $currentSequence[$option['id']] = $option['sequence'] - 1; // -1 because sequence starts with 1 in database + if ($option['system'] == 1) { + $lastSystemSequence = $option['sequence']; + } } // determinate new sequence based on array position $newSequence = array(); - $sequence = 0; + + // check if there are system options, if so then the sequence must start with the sequence of the last system option + $sequence = $lastSystemSequence ?? 0; foreach ($arrValues as $id => $values) { $newSequence[$id] = $sequence++; } diff --git a/src/Inventory/Service/ExportService.php b/src/Inventory/Service/ExportService.php index 40f7f7fd03..0f35aa97ce 100644 --- a/src/Inventory/Service/ExportService.php +++ b/src/Inventory/Service/ExportService.php @@ -19,7 +19,6 @@ use Admidio\UI\Presenter\InventoryPresenter; // PHP namespaces -use HtmlTable; use InvalidArgumentException; /** * @brief Class with methods to display the module pages. @@ -52,7 +51,7 @@ public function createExport(string $mode = 'pdf'): void [$exportMode, $charset, $orientation] = $modeSettings[$mode]; } - $filename = $gSettingsManager->getString('inventory_export_filename'); + $filename = $gSettingsManager->getString('inventory_export_filename') . '.' . $exportMode; if ($gSettingsManager->getBool('inventory_add_date')) { // add system date format to filename $filename = date('Y-m-d') . '_' . $filename; @@ -104,20 +103,19 @@ public function createExport(string $mode = 'pdf'): void // add a page $pdf->AddPage(); - // Create table object for display - $exportTable = new HtmlTable('adm_inventory_table', $inventoryPage, false, false, 'table'); + // Using Smarty templating engine for exporting table in PDF + $smarty = $inventoryPage->createSmartyObject(); + $smarty->assign('attributes', array('border' => '1', 'cellpadding' => '1')); + $smarty->assign('column_align', $data['column_align']); + $smarty->assign('headers', $data['headers']); + $smarty->assign('headersStyle', 'font-size:10;font-weight:bold;background-color:#C7C7C7;'); + $smarty->assign('rows', $data['rows']); + $smarty->assign('rowsStyle', 'font-size:10;'); - $exportTable->addAttribute('border', '1'); - $exportTable->addAttribute('cellpadding', '1'); + // Fetch the HTML table from our Smarty template + $htmlTable = $smarty->fetch('modules/inventory.list.export.tpl'); - $exportTable->setColumnAlignByArray($data['column_align']); - $exportTable->addRowHeadingByArray($data['headers'],'', array('style' => 'font-size:10;font-weight:bold;background-color:#C7C7C7;')); - - foreach ($data['rows'] as $row) { - $exportTable->addRowByArray($row['data'], '', array('style' => 'font-size:10;')); - } - - $pdf->writeHTML($exportTable->getHtmlTable(), true, false, true); + $pdf->writeHTML($htmlTable, true, false, true); $file = ADMIDIO_PATH . FOLDER_DATA . '/temp/' . $filename; $pdf->Output($file, 'F'); header('Content-Type: application/pdf'); diff --git a/src/Inventory/Service/ImportService.php b/src/Inventory/Service/ImportService.php index be0c0b1432..70d037ab97 100644 --- a/src/Inventory/Service/ImportService.php +++ b/src/Inventory/Service/ImportService.php @@ -14,8 +14,10 @@ use Admidio\Categories\Service\CategoryService; use Admidio\Categories\Entity\Category; use Admidio\Infrastructure\Exception; +use Admidio\Infrastructure\Language; use Admidio\Inventory\Service\ItemService; use Admidio\Inventory\ValueObjects\ItemsData; +use Admidio\Inventory\Entity\SelectOptions; use Admidio\Inventory\Entity\Item; // PHP namespaces @@ -221,22 +223,20 @@ public function importItems(): array foreach ($items->getItemData() as $key => $itemData) { $itemValue = $itemData->getValue('ind_value'); if ($itemData->getValue('inf_name_intern') === 'KEEPER' || $itemData->getValue('inf_name_intern') === 'LAST_RECEIVER' || - $itemData->getValue('inf_name_intern') === 'IN_INVENTORY' || $itemData->getValue('inf_name_intern') === 'RECEIVED_ON' || - $itemData->getValue('inf_name_intern') === 'RECEIVED_BACK_ON') { + $itemData->getValue('inf_name_intern') === 'BORROW_DATE' || $itemData->getValue('inf_name_intern') === 'RETURN_DATE') { continue; } - - if ($itemData->getValue('inf_name_intern') === 'CATEGORY') { - $item = new Item($gDb, $items, $items->getItemId()); - $catID = $item->getValue('ini_cat_id'); - $category = new Category($gDb); - if ($category->readDataById($catID)); - $itemValues[] = array($itemData->getValue('inf_name_intern') => $category->getValue('cat_name')); - continue; - } - + $itemValues[] = array($itemData->getValue('inf_name_intern') => $itemValue); } + // also add a column with the category if it exists + $item = new Item($gDb, $items, $items->getItemId()); + $catID = $item->getValue('ini_cat_id'); + $category = new Category($gDb); + if ($category->readDataById($catID)) { + $itemValues[] = array('CATEGORY' => $category->getValue('cat_name')); + } + $itemValues = array_merge_recursive(...$itemValues); if (count($assignedFieldColumn) === 0) { @@ -254,15 +254,15 @@ public function importItems(): array // get all values of the item fields $importedItemData = array(); - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrowing fields + $borrowingFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); foreach ($assignedFieldColumn as $row => $values) { foreach ($items->getItemFields() as $fields){ $infId = $fields->getValue('inf_id'); $imfNameIntern = $fields->getValue('inf_name_intern'); - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($imfNameIntern, $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($imfNameIntern, $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } if (isset($values[$infId])) { @@ -325,7 +325,7 @@ public function importItems(): array $categoryService = new CategoryService($gDb, 'IVT'); $allCategories = $categoryService->getVisibleCategories(); foreach ($allCategories as $key => $category) { - if ($category['cat_name'] === $catName) { + if (Language::translateIfTranslationStrId($category['cat_name']) === $catName) { $val = $category['cat_uuid']; break; } @@ -341,9 +341,8 @@ public function importItems(): array $val = $category->getValue('cat_uuid'); } } - } - elseif($imfNameIntern === 'RECEIVED_ON' || $imfNameIntern === 'RECEIVED_BACK_ON') { + elseif($imfNameIntern === 'BORROW_DATE' || $imfNameIntern === 'RETURN_DATE') { $val = $values[$infId]; if ($val !== '') { // date must be formatted @@ -383,6 +382,34 @@ public function importItems(): array } } } + elseif($imfNameIntern === 'STATUS') { + $statusValue = $values[$infId]; + $val = ''; + if ($statusValue !== '') { + // if no status is given, set the default status + $option = new SelectOptions($gDb, $fields->getValue('inf_id')); + $optionValues = $option->getAllOptions(); + foreach ($optionValues as $optionData) { + if (Language::translateIfTranslationStrId($optionData['value']) === $statusValue) { + $val = $optionData['id']; + break; + } + } + if ($val === '') { + $option = new SelectOptions($gDb, $fields->getValue('inf_id')); + $options = $option->getAllOptions(); + $maxId = 0; + foreach ($options as $optionData) { + if ($optionData['id'] > $maxId) { + $maxId = $optionData['id']; + } + } + $newOption[$maxId + 1] = array('value' => $statusValue); + $option->setOptionValues($newOption); + $val = $option->getValue('ifo_id'); + } + } + } else { $val = $values[$infId]; } @@ -432,7 +459,7 @@ public function importItems(): array private function compareArrays(array $array1, array $array2) : bool { $array1 = array_filter($array1, function($key) { - return $key !== 'KEEPER' && $key !== 'LAST_RECEIVER' && $key !== 'IN_INVENTORY' && $key !== 'RECEIVED_ON' && $key !== 'RECEIVED_BACK_ON'; + return $key !== 'KEEPER' && $key !== 'LAST_RECEIVER' && $key !== 'BORROW_DATE' && $key !== 'RETURN_DATE'; }, ARRAY_FILTER_USE_KEY); foreach ($array1 as $value) { diff --git a/src/Inventory/Service/ItemService.php b/src/Inventory/Service/ItemService.php index ffc5fe857b..43e1141d2d 100644 --- a/src/Inventory/Service/ItemService.php +++ b/src/Inventory/Service/ItemService.php @@ -4,6 +4,11 @@ use Admidio\Infrastructure\Exception; use Admidio\Infrastructure\Database; +use Admidio\Infrastructure\Image; +use Admidio\Infrastructure\Utils\PhpIniUtils; +use Admidio\Infrastructure\Utils\SystemInfoUtils; +use Admidio\Infrastructure\Utils\FileSystemUtils; +use Admidio\Inventory\Entity\Item; use Admidio\Inventory\ValueObjects\ItemsData; /** @@ -45,13 +50,13 @@ public function __construct(Database $database, string $itemUUID = '', int $post } /** - * Marks the item as former. + * Marks the item as retired. * * @throws Exception */ - public function makeItemFormer(): void + public function retireItem(): void { - $this->itemRessource->makeItemFormer(); + $this->itemRessource->retireItem(); // Send notification to all users $this->itemRessource->sendNotification(); @@ -61,9 +66,9 @@ public function makeItemFormer(): void * Reverts the item to its previous state. * @throws Exception */ - public function undoItemFormer(): void + public function reinstateItem(): void { - $this->itemRessource->undoItemFormer(); + $this->itemRessource->reinstateItem(); // Send notification to all users $this->itemRessource->sendNotification(); @@ -134,7 +139,9 @@ public function save(bool $multiEdit = false): void $postKey = 'INF-' . $infNameIntern; if (isset($formValues[$postKey])) { - if (strlen($formValues[$postKey]) === 0 && $itemField->getValue('inf_required_input') == 1) { + if (is_array($formValues[$postKey]) && empty($formValues[$postKey])) { + throw new Exception($gL10n->get('SYS_FIELD_EMPTY', array($itemField->getValue('inf_name')))); + } elseif (is_string($formValues[$postKey]) && (strlen($formValues[$postKey]) === 0 && $itemField->getValue('inf_required_input') == 1)) { throw new Exception($gL10n->get('SYS_FIELD_EMPTY', array($itemField->getValue('inf_name')))); } @@ -143,14 +150,10 @@ public function save(bool $multiEdit = false): void isset($formValues[$postKey . '_time']) ? $dateValue = $formValues[$postKey] . ' ' . $formValues[$postKey . '_time'] : $dateValue = $formValues[$postKey]; // Write value from field to the item class object with time - if (!$this->itemRessource->setValue($infNameIntern, $dateValue)) { - throw new Exception($gL10n->get('SYS_DATABASE_ERROR'), $gL10n->get('SYS_ERROR')); - } + $this->itemRessource->setValue($infNameIntern, $dateValue); } else { // Write value from field to the item class object - if (!$this->itemRessource->setValue($infNameIntern, $formValues[$postKey])) { - throw new Exception($gL10n->get('SYS_DATABASE_ERROR'), $gL10n->get('SYS_ERROR')); - } + $this->itemRessource->setValue($infNameIntern, $formValues[$postKey]); } } elseif ($itemField->getValue('inf_type') === 'CHECKBOX' && !$multiEdit) { // Set value to '0' for unchecked checkboxes @@ -170,4 +173,192 @@ public function save(bool $multiEdit = false): void // Send notification to all users $this->itemRessource->sendNotification(); } + + /** + * Show the picture of the item. + * + * @throws Exception + */ + public function showItemPicture($getNewPicture = false) : void + { + global $gCurrentSession, $gSettingsManager; + $item = new Item($this->db, $this->itemRessource, $this->itemRessource->getItemId()); + + // Initialize default picture path + $picturePath = getThemedFile('/images/inventory-item-picture.png'); + $image = null; + + if ($item->getValue('ini_id') !== 0) { + if ($getNewPicture) { + // show temporary saved new picture from upload in database + if ($gSettingsManager->getInt('inventory_item_picture_storage') === 0) { + $image = new Image(); + $image->setImageFromData($gCurrentSession->getValue('ses_binary')); + } // show temporary saved new picture from upload in filesystem + else { + $picturePath = ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '_new.jpg'; + $image = new Image($picturePath); + } + } else { + // show picture from database + if ($gSettingsManager->getInt('inventory_item_picture_storage') === 0) { + if ((string)$item->getValue('ini_picture') !== '') { + $image = new Image(); + $image->setImageFromData($item->getValue('ini_picture')); + } else { + $image = new Image($picturePath); + } + } // show picture from folder adm_my_files + else { + $file = ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '.jpg'; + if (is_file($file)) { + $picturePath = $file; + } + $image = new Image($picturePath); + } + } + } else { + // If no item exists, show default picture + $image = new Image($picturePath); + } + + header('Content-Type: ' . $image->getMimeType()); + // Caching-Header setzen + header("Last-Modified: " . $item->getValue('ini_timestamp_changed', 'D, d M Y H:i:s') . " GMT"); + header("ETag: " . md5_file($picturePath)); + + $image->copyToBrowser(); + $image->delete(); + } + + /** + * Upload a new item picture. + * + * @throws Exception + */ + public function uploadItemPicture(): void + { + global $gCurrentSession, $gSettingsManager; + // Confirm cache picture + // check form field input and sanitized it from malicious content + $itemPictureUploadForm = $gCurrentSession->getFormObject($_POST['adm_csrf_token']); + $itemPictureUploadForm->validate($_POST); + + // File size + if ($_FILES['userfile']['error'][0] === UPLOAD_ERR_INI_SIZE) { + throw new Exception('SYS_PHOTO_FILE_TO_LARGE', array(round(PhpIniUtils::getUploadMaxSize() / 1024 ** 2))); + } + + // check if a file was really uploaded + if (!file_exists($_FILES['userfile']['tmp_name'][0]) || !is_uploaded_file($_FILES['userfile']['tmp_name'][0])) { + throw new Exception('SYS_NO_PICTURE_SELECTED'); + } + + // File ending + $imageProperties = getimagesize($_FILES['userfile']['tmp_name'][0]); + if ($imageProperties === false || !in_array($imageProperties['mime'], array('image/jpeg', 'image/png'), true)) { + throw new Exception('SYS_PHOTO_FORMAT_INVALID'); + } + + // Resolution control + $imageDimensions = $imageProperties[0] * $imageProperties[1]; + if ($imageDimensions > SystemInfoUtils::getProcessableImageSize()) { + throw new Exception('SYS_PHOTO_RESOLUTION_TO_LARGE', array(round(SystemInfoUtils::getProcessableImageSize() / 1000000, 2))); + } + + // Adjust picture to appropriate size + $itemImage = new Image($_FILES['userfile']['tmp_name'][0]); + $itemImage->setImageType('jpeg'); + + if ($gSettingsManager->getInt('inventory_item_picture_storage') === 1) { + // Folder storage + $itemImage->scale($gSettingsManager->getInt('inventory_item_picture_width'), $gSettingsManager->getInt('inventory_item_picture_height')); + $itemImage->copyToFile(null, ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '_new.jpg'); + } else { + // Database storage + $itemImage->scale(130, 170); + $itemImage->copyToFile(null, $_FILES['userfile']['tmp_name'][0]); + $itemImageData = fread(fopen($_FILES['userfile']['tmp_name'][0], 'rb'), $_FILES['userfile']['size'][0]); + + $gCurrentSession->setValue('ses_binary', $itemImageData); + $gCurrentSession->save(); + } + + // delete image object + $itemImage->delete(); + } + + /** + * Save the picture of the item. + * + * @throws Exception + */ + public function saveItemPicture(): void + { + global $gLogger, $gSettingsManager, $gCurrentSession; + + if ($gSettingsManager->getInt('inventory_item_picture_storage') === 1) { + // Save picture in the file system + + // Check if a picture was saved for the user + $fileOld = ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '_new.jpg'; + if (is_file($fileOld)) { + $fileNew = ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '.jpg'; + try { + FileSystemUtils::deleteFileIfExists($fileNew); + + try { + FileSystemUtils::moveFile($fileOld, $fileNew); + } catch (\RuntimeException $exception) { + $gLogger->error('Could not move file!', array('from' => $fileOld, 'to' => $fileNew)); + // TODO + } + } catch (\RuntimeException $exception) { + $gLogger->error('Could not delete file!', array('filePath' => $fileNew)); + // TODO + } + } + } else { + // Save picture in the database + $item = new Item($this->db, $this->itemRessource, $this->itemRessource->getItemId()); + + // Check if a picture was saved for the user + if (strlen($gCurrentSession->getValue('ses_binary')) > 0) { + $this->db->startTransaction(); + // write the picture data into the database + $item->setValue('ini_picture', $gCurrentSession->getValue('ses_binary')); + $item->save(); + + // remove temporary picture data from session + $gCurrentSession->setValue('ses_binary', ''); + $gCurrentSession->save(); + $this->db->endTransaction(); + } + } + } + + /** + * Delete the picture of the item. + * + * @throws Exception + */ + public function deleteItemPicture(): void + { + global $gLogger, $gSettingsManager; + if ($gSettingsManager->getInt('inventory_item_picture_storage') === 1) { + // Folder storage, delete file + $filePath = ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $this->itemRessource->getItemId() . '.jpg'; + try { + FileSystemUtils::deleteFileIfExists($filePath); + } catch (\RuntimeException $exception) { + $gLogger->error('Could not delete file!', array('filePath' => $filePath)); + // TODO + } + } else { + // Database storage, remove data from session + $item = new Item($this->db, $this->itemRessource, $this->itemRessource->getItemId()); + $item->setValue('ini_picture', ''); + $item->save(); + } + } } diff --git a/src/Inventory/ValueObjects/ItemsData.php b/src/Inventory/ValueObjects/ItemsData.php index 311941597f..79ca574a44 100644 --- a/src/Inventory/ValueObjects/ItemsData.php +++ b/src/Inventory/ValueObjects/ItemsData.php @@ -13,8 +13,9 @@ use Admidio\Inventory\Entity\Item; use Admidio\Inventory\Entity\ItemData; use Admidio\Inventory\Entity\ItemField; -use Admidio\Inventory\Entity\ItemLendData; +use Admidio\Inventory\Entity\ItemBorrowData; use Admidio\Categories\Entity\Category; +use Admidio\Inventory\Entity\SelectOptions; // PHP namespaces use DateTime; @@ -34,15 +35,15 @@ */ class ItemsData { - private bool $mItemCreated = false; ///< flag if a new item was created - private bool $mItemChanged = false; ///< flag if a new item was changed - private bool $mItemDeleted = false; ///< flag if a item was deleted - private bool $mItemMadeFormer = false; ///< flag if a item was made to former item - private bool $mItemUndoMadeFormer = false; ///< flag if a item was made to normal again - private bool $mItemImported = false; ///< flag if a item was imported - private bool $showFormerItems = true; ///< if true, than former items will be showed - private int $organizationId = -1; ///< ID of the organization for which the item field structure should be read - private array $lendFieldNames = array('LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); ///< array with the internal field names of the lend fields + private bool $mItemCreated = false; ///< flag if a new item was created + private bool $mItemChanged = false; ///< flag if a new item was changed + private bool $mItemDeleted = false; ///< flag if a item was deleted + private bool $mItemRetired = false; ///< flag if a item was retired + private bool $mItemReinstated = false; ///< flag if a item was made to normal again + private bool $mItemImported = false; ///< flag if a item was imported + private bool $showRetiredItems = true; ///< if true, than retired items will be showed + private int $organizationId = -1; ///< ID of the organization for which the item field structure should be read + private array $borrowFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); ///< array with the internal field names of the borrow fields /** * @var Database An object of the class Database for communication with the database @@ -163,7 +164,7 @@ public function readItemFields($orderBy = 'inf_id'): void /** * Reads the item data of all item fields out of database table @b adm_inventory_manager_data - * and @b adm_inventory_manager_items_lend + * and @b adm_inventory_manager_items_borrow * and adds an object for each field data to the @b mItemData array. * If profile fields structure wasn't read, this will be done before. * @@ -201,19 +202,27 @@ public function readItemData(string $itemUUID = ''): void $this->mItemData[$row['ind_inf_id']]->setArray($row); } - // read all item lend data - $sql = 'SELECT * FROM ' . TBL_INVENTORY_ITEM_LEND_DATA . ' + // read all item borrow data + $sql = 'SELECT * FROM ' . TBL_INVENTORY_ITEM_BORROW_DATA . ' INNER JOIN ' . TBL_INVENTORY_FIELDS . ' - ON inf_id = inl_inf_id - WHERE inl_ini_id = ?;'; - $itemLendStatement = $this->mDb->queryPrepared($sql, array($itemId)); - - while ($row = $itemLendStatement->fetch()) { - if (!array_key_exists($row['inl_inf_id'], $this->mItemData)) { - $this->mItemData[$row['inl_inf_id']] = new ItemLendData($this->mDb, $this, $row['inl_inf_id']); + ON inf_name_intern IN ( ?, ?, ? ) + WHERE inb_ini_id = ?;'; + $itemBorrowStatement = $this->mDb->queryPrepared($sql, array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE', $itemId)); + + while ($row = $itemBorrowStatement->fetch()) { + foreach ($this->getItemFields() as $itemField) { + $itemBorrowData = new ItemBorrowData($this->mDb, $this, $row['inb_ini_id']); + $fieldNameIntern = $itemField->getValue('inf_name_intern'); + $fieldId = $itemField->getValue('inf_id'); + if (in_array($fieldNameIntern, $this->borrowFieldNames)) { + if (!array_key_exists($fieldId, $this->mItemData)) { + $this->mItemData[$fieldId] = $itemBorrowData; + } + $this->mItemData[$fieldId]->setArray($row); + } } - $this->mItemData[$row['inl_inf_id']]->setArray($row); - } } else { + } + } else { $this->mItemCreated = true; } } @@ -230,11 +239,21 @@ public function readItems(): void $this->mItems = array(); $sqlWhereCondition = ''; - if (!$this->showFormerItems) { - $sqlWhereCondition .= 'AND ini_former = 0'; + if (!$this->showRetiredItems) { + // get the option id of the retired status + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $retiredId = 0; + foreach ($values as $value) { + if ($value['value'] === 'SYS_INVENTORY_FILTER_RETIRED_ITEMS') { + $retiredId = $value['id']; + break; + } + } + $sqlWhereCondition .= 'AND ini_status NOT IN (' . $retiredId . ')'; } - $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_former FROM ' . TBL_INVENTORY_ITEMS . ' + $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_status FROM ' . TBL_INVENTORY_ITEMS . ' INNER JOIN ' . TBL_INVENTORY_ITEM_DATA . ' ON ind_ini_id = ini_id WHERE ini_org_id IS NULL @@ -243,7 +262,7 @@ public function readItems(): void $statement = $this->mDb->queryPrepared($sql, array($this->organizationId)); while ($row = $statement->fetch()) { - $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_former' => $row['ini_former']); + $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_status' => $row['ini_status']); } } @@ -260,9 +279,19 @@ public function readItemsByUser($userId, $fieldNames = array('KEEPER')): void // first initialize existing data $this->mItems = array(); - $sqlWhereCondition = ''; - if (!$this->showFormerItems) { - $sqlWhereCondition .= 'AND ini_former = 0'; + $sqlStatusCondition = ''; + if (!$this->showRetiredItems) { + // get the option id of the retired status + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $retiredId = 0; + foreach ($values as $value) { + if ($value['value'] === 'SYS_INVENTORY_FILTER_RETIRED_ITEMS') { + $retiredId = $value['id']; + break; + } + } + $sqlStatusCondition .= 'AND ini_status = ' . $retiredId; } $sqlImfIds = 'AND ('; @@ -274,7 +303,7 @@ public function readItemsByUser($userId, $fieldNames = array('KEEPER')): void } // first read all item data for the given user - $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_former FROM ' . TBL_INVENTORY_ITEM_DATA . ' + $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_status FROM ' . TBL_INVENTORY_ITEM_DATA . ' INNER JOIN ' . TBL_INVENTORY_FIELDS . ' ON inf_id = ind_inf_id ' . $sqlImfIds . ' @@ -283,38 +312,38 @@ public function readItemsByUser($userId, $fieldNames = array('KEEPER')): void WHERE (ini_org_id IS NULL OR ini_org_id = ?) AND ind_value = ? - ' . $sqlWhereCondition . ';'; + ' . $sqlStatusCondition . ';'; $statement = $this->mDb->queryPrepared($sql, array($this->organizationId, $userId)); while ($row = $statement->fetch()) { - $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_former' => $row['ini_former']); + $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_status' => $row['ini_status']); } - // now read the item lend data for each item - $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_former FROM ' . TBL_INVENTORY_ITEM_LEND_DATA . ' - INNER JOIN ' . TBL_INVENTORY_FIELDS . ' - ON inf_id = inl_inf_id - ' . $sqlImfIds . ' - INNER JOIN ' . TBL_INVENTORY_ITEMS . ' - ON ini_id = inl_ini_id + // read the borrow data for the given user as receiver + if (in_array('LAST_RECEIVER', $fieldNames)) { + // now read the item borrow data for each item + $sql = 'SELECT DISTINCT ini_id, ini_uuid, ini_cat_id, ini_status FROM ' . TBL_INVENTORY_ITEM_BORROW_DATA . ' + INNER JOIN ' . TBL_INVENTORY_ITEMS . ' + ON ini_id = inb_ini_id WHERE (ini_org_id IS NULL OR ini_org_id = ?) - AND inl_value = ? - ' . $sqlWhereCondition . ';'; - $statement = $this->mDb->queryPrepared($sql, array($this->organizationId, $userId)); - // check if a item already exists in the items array - while ($row = $statement->fetch()) { - // check if item already exists in the items array - $itemExists = false; - foreach ($this->mItems as $item) { - if ($item['ini_id'] === $row['ini_id']) { - $itemExists = true; - break; + AND inb_last_receiver = ? + ' . $sqlStatusCondition . ';'; + $statement = $this->mDb->queryPrepared($sql, array($this->organizationId, $userId)); + // check if a item already exists in the items array + while ($row = $statement->fetch()) { + // check if item already exists in the items array + $itemExists = false; + foreach ($this->mItems as $item) { + if ($item['ini_id'] === $row['ini_id']) { + $itemExists = true; + break; + } + } + // if item doesn't exist, then add it to the items array + if (!$itemExists) { + $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_status' => $row['ini_status']); } - } - // if item doesn't exist, then add it to the items array - if (!$itemExists) { - $this->mItems[] = array('ini_id' => $row['ini_id'], 'ini_uuid' => $row['ini_uuid'], 'ini_cat_id' => $row['ini_cat_id'], 'ini_former' => $row['ini_former']); } } } @@ -536,6 +565,47 @@ public function getHtmlValue($fieldNameIntern, $value): string } break; + case 'DROPDOWN_MULTISELECT': + $arrOptionValuesWithKeys = array(); // array with option values and keys that represents the internal value + $arrOptions = $this->mItemFields[$fieldNameIntern]->getValue('ifo_inf_options', 'database', false); + + foreach ($arrOptions as $values) { + // if text is a translation-id then translate it + $values['value'] = Language::translateIfTranslationStrId($values['value']); + + // save values in new array that starts with key = 1 + $arrOptionValuesWithKeys[$values['id']] = $values['value']; + } + + if (count($arrOptionValuesWithKeys) > 0 && !empty($value)) { + // split value by comma and trim each value + $valueArray = explode(',', $value); + foreach ($valueArray as &$val) { + $val = trim($val); + } + unset($val); + + // now create html output for each value + $htmlValue = ''; + foreach ($valueArray as $val) { + if (array_key_exists($val, $arrOptionValuesWithKeys)) { + // if value is the index of the array then we can use it + if ($htmlValue !== '') { + $htmlValue .= ', '; + } + $htmlValue .= $arrOptionValuesWithKeys[$val]; + } else { + if ($htmlValue !== '') { + $htmlValue .= ', '; + } + $htmlValue .= '' . $gL10n->get('SYS_DELETED_ENTRY') . ''; + } + } + } else { + $htmlValue = ''; + } + break; + case 'TEXT_BIG': $htmlValue = nl2br($value); break; @@ -615,13 +685,29 @@ public function getValue($fieldNameIntern, $format = '') } } } + elseif ($fieldNameIntern === 'STATUS') { + // special case for status + $item = new Item($this->mDb, $this, $this->mItemId); + $statusId = $item->getValue('ini_status'); + if ($statusId > 0) { + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + foreach ($values as $valueArray) { + if ($valueArray['id'] === $statusId) { + if ($format === 'database') { + return $valueArray['id']; + } + return Language::translateIfTranslationStrId($valueArray['value']); + } + } + } + } elseif (array_key_exists($this->mItemFields[$fieldNameIntern]->getValue('inf_id'), $this->mItemData)) { - $prefix = 'ind'; - if ($this->mItemData[$this->mItemFields[$fieldNameIntern]->getValue('inf_id')] instanceof ItemLendData) { - // if field is a lend field then use 'inl_' as prefix - $prefix = 'inl'; + if ($this->mItemData[$this->mItemFields[$fieldNameIntern]->getValue('inf_id')] instanceof ItemBorrowData) { + $value = $this->mItemData[$this->mItemFields[$fieldNameIntern]->getValue('inf_id')]->getValue('inb_' . strtolower($fieldNameIntern), $format); + } else { + $value = $this->mItemData[$this->mItemFields[$fieldNameIntern]->getValue('inf_id')]->getValue('ind_value', $format); } - $value = $this->mItemData[$this->mItemFields[$fieldNameIntern]->getValue('inf_id')]->getValue($prefix . '_value', $format); if ($format === 'database') { return $value; @@ -670,6 +756,24 @@ public function getValue($fieldNameIntern, $format = '') $value = $arrOptions[$value]; } break; + + case 'DROPDOWN_MULTISELECT': + // the value in db is a comma separated list of positions, now search for the text + if ($value !== '' && $format !== 'html') { + $arrOptions = $this->mItemFields[$fieldNameIntern]->getValue('ifo_inf_options', $format, false); + $valueArray = explode(',', $value); + foreach ($valueArray as &$val) { + $val = trim($val); + if (array_key_exists($val, $arrOptions)) { + $val = $arrOptions[$val]; + } else { + $val = '' . $GLOBALS['gL10n']->get('SYS_DELETED_ENTRY') . ''; + } + } + unset($val); + $value = implode(', ', $valueArray); + } + break; } } } @@ -682,6 +786,19 @@ public function getValue($fieldNameIntern, $format = '') return $value; } + /** + * Returns the status of the item. + * + * @return int Returns the status of the item + */ + public function getStatus(): int + { + $item = new Item($this->mDb, $this); + $item->readDataByUuid($this->mItemUUID); + + return $item->getStatus(); + } + /** * Marks an item as imported. * @@ -693,18 +810,65 @@ public function setImportedItem(): void } /** - * This method reads or stores the variable for showing former items. + * This method reads or stores the variable for showing retired items. * The values will be stored in database without any inspections! * - * @param bool|null $newValue If set, then the new value will be stored in @b showFormerItems. - * @return bool Returns the current value of @b showFormerItems + * @param bool|null $newValue If set, then the new value will be stored in @b showRetiredItems. + * @return bool Returns the current value of @b showRetiredItems */ - public function showFormerItems($newValue = null): bool + public function showRetiredItems($newValue = null): bool { if ($newValue !== null) { - $this->showFormerItems = $newValue; + $this->showRetiredItems = $newValue; } - return $this->showFormerItems; + return $this->showRetiredItems; + } + + public function isRetired(): bool + { + global $gDb; + $optionId = $this->getStatus(); + $option = new SelectOptions($gDb, $this->getProperty('STATUS', 'inf_id')); + if ($option->readDataById($optionId)) { + return $option->getValue('ifo_value') === 'SYS_INVENTORY_FILTER_RETIRED_ITEMS'; + } + return false; + } + + /** + * Checks if the item is in use. + * + * @return bool Returns true if the item is in use, otherwise false + */ + public function isInUse(): bool + { + global $gDb; + $optionId = $this->getStatus(); + $option = new SelectOptions($gDb, $this->getProperty('STATUS', 'inf_id')); + if ($option->readDataById($optionId)) { + return $option->getValue('ifo_value') === 'SYS_INVENTORY_FILTER_IN_USE_ITEMS'; + } + return false; + } + + /** + * Checks if the item is borrowed. + * + * @return bool Returns true if the item is borrowed, otherwise false + */ + public function isBorrowed(): bool + { + // get Values of LAST_RECEIVER, BORROW_DATE and RETURN_DATE for current item + $borrowData = new ItemBorrowData($this->mDb, $this); + $borrowData->readDataByColumns(array('inb_ini_id' => $this->mItemId)); + $lastReceiver = $borrowData->getValue('inb_last_receiver'); + $borrowDate = $borrowData->getValue('inb_borrow_date'); + $returnDate = $borrowData->getValue('inb_return_date'); + // if last receiver is set and borrow date is set and return date is not set then item is borrowed + if ($lastReceiver !== '' && $borrowDate !== '' && $returnDate === '') { + return true; + } + return false; } /** @@ -744,14 +908,23 @@ public function setValue($fieldNameIntern, $newValue): bool $infId = $this->mItemFields[$fieldNameIntern]->getValue('inf_id'); $oldFieldValue = ''; // default prefix is 'ind_' for item data - // if field is a lend field then use 'inl_' as prefix + // if field is a borrow field then use 'inb_' as prefix $prefix = 'ind'; - if (in_array($fieldNameIntern, $this->lendFieldNames)) { - $prefix = 'inl'; + if (in_array($fieldNameIntern, $this->borrowFieldNames)) { + $prefix = 'inb'; } if (array_key_exists($infId, $this->mItemData)) { - $oldFieldValue = $this->mItemData[$infId]->getValue($prefix .'_value'); + if ($this->mItemData[$infId] instanceof ItemBorrowData) { + $oldFieldValue = $this->mItemData[$infId]->getValue($prefix . '_' . strtolower($fieldNameIntern)); + } else { + $oldFieldValue = $this->mItemData[$infId]->getValue($prefix . '_value'); + } + } + + if (is_array($newValue)) { + // if new value is an array then convert it to a string + $newValue = implode(',', $newValue); } // check if new value only contains spaces @@ -796,16 +969,23 @@ public function setValue($fieldNameIntern, $newValue): bool // if item data object for this field does not exist then create it if (!array_key_exists($infId, $this->mItemData)) { - if (in_array($fieldNameIntern, $this->lendFieldNames)) { - $this->mItemData[$infId] = new ItemLendData($this->mDb, $this); + if (in_array($fieldNameIntern, $this->borrowFieldNames)) { + $this->mItemData[$infId] = new ItemBorrowData($this->mDb, $this); } else { $this->mItemData[$infId] = new ItemData($this->mDb, $this); + $this->mItemData[$infId]->setValue($prefix . '_inf_id', $infId); } - $this->mItemData[$infId]->setValue($prefix . '_inf_id', $infId); $this->mItemData[$infId]->setValue($prefix . '_ini_id', $this->mItemId); } - return $this->mItemData[$infId]->setValue($prefix . '_value', $newValue); + $ret = false; + if ($this->mItemData[$infId] instanceof ItemBorrowData) { + $ret = $this->mItemData[$infId]->setValue($prefix . '_' . strtolower($fieldNameIntern), $newValue); + } else { + $ret = $this->mItemData[$infId]->setValue($prefix . '_value', $newValue); + } + + return $ret; } /** @@ -833,9 +1013,20 @@ public function createNewItem(string $catUUID): void $category = new Category($this->mDb); $category->readDataByUuid($catUUID); + // get the option id of the in use status + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $inUseId = 0; + foreach ($values as $value) { + if ($value['value'] === 'SYS_INVENTORY_FILTER_IN_USE_ITEMS') { + $inUseId = $value['id']; + break; + } + } + $newItem = new Item($this->mDb, $this, 0); $newItem->setValue('ini_org_id', $this->organizationId); - $newItem->setValue('ini_former', 0); + $newItem->setValue('ini_status', $inUseId); $newItem->setValue('ini_cat_id', $category->getValue('cat_id')); $newItem->save(); @@ -862,8 +1053,8 @@ public function deleteItem(): void // delete all item data $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_DATA . ' WHERE ind_ini_id = ?;'; $this->mDb->queryPrepared($sql, array($this->mItemId)); - // delete all item lend data - $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_LEND_DATA . ' WHERE inl_ini_id = ?;'; + // delete all item borrow data + $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_BORROW_DATA . ' WHERE inb_ini_id = ?;'; $this->mDb->queryPrepared($sql, array($this->mItemId)); // delete item $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEMS . ' WHERE ini_id = ? AND (ini_org_id = ? OR ini_org_id IS NULL);'; @@ -873,35 +1064,57 @@ public function deleteItem(): void } /** - * Marks an item as former + * Marks an item as retired * - * @param int $itemId The ID of the item to be marked as former. + * @param int $itemId The ID of the item to be retired. * @return void */ - public function makeItemFormer(): void + public function retireItem(): void { + // get the option id of the retired status + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $retiredId = 0; + foreach ($values as $value) { + if ($value['value'] === 'SYS_INVENTORY_FILTER_RETIRED_ITEMS') { + $retiredId = $value['id']; + break; + } + } + $item = new Item($this->mDb, $this, $this->mItemId); - $item->setValue('ini_former', 1); + $item->setValue('ini_status', $retiredId); $item->save(); - $this->mItemMadeFormer = true; - $this->mItemUndoMadeFormer = false; + $this->mItemRetired = true; + $this->mItemReinstated = false; } /** - * Marks an item as no longer former + * Marks an item as reinstated which means it is no longer retired. * - * @param int $itemId The ID of the item to be marked as no longer former. + * @param int $itemId The ID of the item to be marked as reinstated. * @return void */ - public function undoItemFormer(): void + public function reinstateItem(): void { + // get the option id of the in use status + $option = new SelectOptions($this->mDb, $this->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $inUseId = 0; + foreach ($values as $value) { + if ($value['value'] === 'SYS_INVENTORY_FILTER_IN_USE_ITEMS') { + $inUseId = $value['id']; + break; + } + } + $item = new Item($this->mDb, $this, $this->mItemId); - $item->setValue('ini_former', 0); + $item->setValue('ini_status', $inUseId); $item->save(); - $this->mItemMadeFormer = false; - $this->mItemUndoMadeFormer = true; + $this->mItemRetired = false; + $this->mItemReinstated = true; } /** @@ -913,7 +1126,7 @@ public function saveItemData(): void { global $gCurrentUser; $this->mDb->startTransaction(); - + $inbId = 0; // used for item borrow data // safe item data foreach ($this->mItemData as $value) { if ($value->hasColumnsValueChanged()) { @@ -922,7 +1135,7 @@ public function saveItemData(): void } // dont safe CATEGORY field to items data - if ($value instanceof ItemData && $value->getValue('ind_inf_id') === 2) { + if ($value instanceof ItemData && ($value->getValue('ind_inf_id') === 2 || $value->getValue('inf_name_intern') === 'CATEGORY')) { // 2 == CATEGORY field $category = new Category($this->mDb); $category->readDataByUuid($value->getValue('ind_value')); $catID = $category->getValue('cat_id'); @@ -932,6 +1145,12 @@ public function saveItemData(): void $item->save(); $value->delete(); } + elseif ($value instanceof ItemData && ($value->getValue('ind_inf_id') === 3 || $value->getValue('inf_name_intern') === 'STATUS')) { // 3 == STATUS field + $item = new Item($this->mDb, $this, $this->mItemId); + $item->setValue('ini_status', $value->getValue('ind_value')); + $item->save(); + $value->delete(); + } elseif ($value instanceof ItemData) { // if value exists and new value is empty then delete entry if ($value->getValue('ind_id') > 0 && $value->getValue('ind_value') === '') { @@ -940,13 +1159,12 @@ public function saveItemData(): void $value->save(); } } - elseif ($value instanceof ItemLendData) { - // if value exists and new value is empty then delete entry - if ($value->getValue('inl_id') > 0 && $value->getValue('inl_value') === '') { - $value->delete(); - } else { - $value->save(); + elseif ($value instanceof ItemBorrowData) { + if ($value->getValue('inb_id') === 0 && $inbId !== 0) { + $value->updateRecordId($inbId); } + $value->save(); + $inbId = $value->getValue('inb_id'); } } @@ -964,7 +1182,7 @@ public function saveItemData(): void } /** - * Send a notification email that a new item was created, changed, deleted, or marked as former + * Send a notification email that a new item was created, changed, deleted, retired, reinstated or imported. * to all members of the notification role. This role is configured within the global preference * **system_notifications_role**. The email contains the item name, the name of the current user, * the timestamp, and the details of the changes. @@ -994,12 +1212,12 @@ public function sendNotification($importData = null): bool } elseif ($this->mItemDeleted) { $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_DELETED'; $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_DELETED'; - } elseif ($this->mItemMadeFormer) { - $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_MADE_FORMER'; - $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_MADE_FORMER'; - } elseif ($this->mItemUndoMadeFormer) { - $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_UNDO_FORMER'; - $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_UNDO_FORMER'; + } elseif ($this->mItemRetired) { + $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_RETIRED'; + $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_RETIRED'; + } elseif ($this->mItemReinstated) { + $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_REINSTATED'; + $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_REINSTATED'; } elseif ($this->mItemChanged) { $messageTitleText = 'SYS_INVENTORY_NOTIFICATION_SUBJECT_ITEM_CHANGED'; $messageHead = 'SYS_INVENTORY_NOTIFICATION_MESSAGE_ITEM_CHANGED'; @@ -1009,7 +1227,7 @@ public function sendNotification($importData = null): bool // if items were imported then sent a message with all itemnames, the user and the date // if item was created or changed then sent a message with all changed fields in a table - // if item was deleted or made former then sent a message with the item name, the user and the date + // if item was deleted, retired or reinstated then sent a message with the item name, the user and the date if ($this->mItemImported || $this->mItemCreated || $this->mItemChanged) { $format_hdr = " %s %s %s \n"; $format_row = " %s %s %s \n"; @@ -1070,12 +1288,6 @@ public function sendNotification($importData = null): bool isset($users[$value['oldValue']]) ? $users[$value['oldValue']] : $value['oldValue'], isset($users[$value['newValue']]) ? $users[$value['newValue']] : $value['newValue'] ); - } elseif ($key === 'IN_INVENTORY') { - $changes[] = array( - $key, - $value['oldValue'] == 1 ? $gL10n->get('SYS_YES') : ($value['oldValue'] == 0 ? $gL10n->get('SYS_NO') : $value['oldValue']), - $value['newValue'] == 1 ? $gL10n->get('SYS_YES') : ($value['newValue'] == 0 ? $gL10n->get('SYS_NO') : $value['newValue']) - ); } elseif ($options !== '') { $changes[] = array( $key, diff --git a/src/Organizations/Entity/Organization.php b/src/Organizations/Entity/Organization.php index f8e5a0693c..59b1ce354e 100644 --- a/src/Organizations/Entity/Organization.php +++ b/src/Organizations/Entity/Organization.php @@ -228,11 +228,11 @@ public function createBasicData(int $userId) (inf_uuid, inf_org_id, inf_type, inf_name_intern, inf_name, inf_description, inf_system, inf_required_input, inf_sequence, inf_usr_id_create, inf_timestamp_create, inf_usr_id_change, inf_timestamp_change) VALUES (?, ?, \'TEXT\', \'ITEMNAME\', \'SYS_INVENTORY_ITEMNAME\', \'SYS_INVENTORY_ITEMNAME_DESC\', 1, 1, 0, ?, ?, NULL, NULL), (?, ?, \'CATEGORY\', \'CATEGORY\', \'SYS_CATEGORY\', \'SYS_INVENTORY_CATEGORY_DESC\', 1, 1, 1, ?, ?, NULL, NULL), - (?, ?, \'TEXT\', \'KEEPER\', \'SYS_INVENTORY_KEEPER\', \'SYS_INVENTORY_KEEPER_DESC\', 1, 0, 2, ?, ?, NULL, NULL), - (?, ?, \'CHECKBOX\', \'IN_INVENTORY\', \'SYS_INVENTORY_IN_INVENTORY\', \'SYS_INVENTORY_IN_INVENTORY_DESC\', 1, 0, 3, ?, ?, NULL, NULL), + (?, ?, \'DROPDOWN\', \'STATUS\', \'SYS_INVENTORY_STATUS\', \'SYS_INVENTORY_STATUS_DESC\', 1, 1, 2, ?, ?, NULL, NULL), + (?, ?, \'TEXT\', \'KEEPER\', \'SYS_INVENTORY_KEEPER\', \'SYS_INVENTORY_KEEPER_DESC\', 1, 0, 3, ?, ?, NULL, NULL), (?, ?, \'TEXT\', \'LAST_RECEIVER\', \'SYS_INVENTORY_LAST_RECEIVER\', \'SYS_INVENTORY_LAST_RECEIVER_DESC\', 1, 0, 4, ?, ?, NULL, NULL), - (?, ?, \'DATE\', \'RECEIVED_ON\', \'SYS_INVENTORY_RECEIVED_ON\', \'SYS_INVENTORY_RECEIVED_ON_DESC\', 1, 0, 5, ?, ?, NULL, NULL), - (?, ?, \'DATE\', \'RECEIVED_BACK_ON\', \'SYS_INVENTORY_RECEIVED_BACK_ON\', \'SYS_INVENTORY_RECEIVED_BACK_ON_DESC\', 1, 0, 6, ?, ?, NULL, NULL); + (?, ?, \'DATE\', \'BORROW_DATE\', \'SYS_INVENTORY_BORROW_DATE\', \'SYS_INVENTORY_BORROW_DATE_DESC\', 1, 0, 5, ?, ?, NULL, NULL), + (?, ?, \'DATE\', \'RETURN_DATE\', \'SYS_INVENTORY_RETURN_DATE\', \'SYS_INVENTORY_RETURN_DATE_DESC\', 1, 0, 6, ?, ?, NULL, NULL); '; $queryParams = array( Uuid::uuid4(), $orgId, $systemUserId, DATETIME_NOW, @@ -672,8 +672,8 @@ public function delete(): bool $this->db->queryPrepared($sql, array($this->getValue('org_id'))); // delete all inventory item lend data - $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_LEND_DATA . ' - WHERE inl_ini_id IN ( + $sql = 'DELETE FROM ' . TBL_INVENTORY_ITEM_BORROW_DATA . ' + WHERE inb_ini_id IN ( SELECT ini.ini_id FROM (SELECT ini_id FROM ' . TBL_INVENTORY_ITEMS . ' diff --git a/src/ProfileFields/Entity/SelectOptions.php b/src/ProfileFields/Entity/SelectOptions.php index 74996d0f74..1dbf751a0c 100644 --- a/src/ProfileFields/Entity/SelectOptions.php +++ b/src/ProfileFields/Entity/SelectOptions.php @@ -55,7 +55,7 @@ public function readDataByFieldId(int $usfId = 0) : void // if id is set than read the data of the recordset if ($this->usfId > 0) { - $sql = 'SELECT ufo_id, ufo_value, ufo_sequence, ufo_obsolete + $sql = 'SELECT ufo_id, ufo_value, ufo_system, ufo_sequence, ufo_obsolete FROM ' . TBL_USER_FIELD_OPTIONS . ' WHERE ufo_usf_id = ? -- $usfId ORDER BY ufo_sequence'; @@ -104,6 +104,7 @@ public function getAllOptions(bool $withObsoleteEnries = true) : array $values[$value['ufo_id']] = array( 'id' => $value['ufo_id'], 'value' => $value['ufo_value'], + 'system' => $value['ufo_system'], 'sequence' => $value['ufo_sequence'], 'obsolete' => $value['ufo_obsolete'] ); @@ -252,10 +253,15 @@ public function setOptionValues(array $newValues) : bool $currentSequence = array(); foreach ($allOptions as $option) { $currentSequence[$option['id']] = $option['sequence'] - 1; // -1 because sequence starts with 1 in database + if ($option['system'] == 1) { + $lastSystemSequence = $option['sequence']; + } } // determinate new sequence based on array position $newSequence = array(); - $sequence = 0; + + // check if there are system options, if so then the sequence must start with the sequence of the last system option + $sequence = $lastSystemSequence ?? 0; foreach ($arrValues as $id => $values) { $newSequence[$id] = $sequence++; } diff --git a/src/UI/Presenter/FormPresenter.php b/src/UI/Presenter/FormPresenter.php index 90b431155b..f00c28e6d3 100644 --- a/src/UI/Presenter/FormPresenter.php +++ b/src/UI/Presenter/FormPresenter.php @@ -465,10 +465,10 @@ public function addDescription(string $id, string $content, array $options = arr * If you don't need the field structure and want to add html then use the method addHtml() * @param string $label The label of the custom content. */ - public function addSeperator(string $id, string $label = '', array $options = array()): void + public function addSeparator(string $id, string $label = '', array $options = array()): void { $optionsAll = $this->buildOptionsArray(array_replace(array( - 'type' => 'seperator', + 'type' => 'separator', 'id' => $id, 'label' => $label ), $options)); @@ -998,11 +998,25 @@ public function addOptionEditor(string $id, string $label, array $values, array { global $gL10n; + // sort values based on sortable or not sortable (system) fields + $sortable = array(); + $notSortable = array(); + foreach ($values as $key => $value) { + if (!$value['system']) { + $sortable[$key] = $value; + } else { + $notSortable[$key] = $value; + } + } + $optionsAll = $this->buildOptionsArray(array_replace(array( 'type' => 'option-editor', 'id' => $id, 'label' => $label, - 'values' => $values, + 'values' => array( + 'sortable' => $sortable, + 'notSortable' => $notSortable + ), 'filename' => 'profile-fields' ), $options)); $attributes = array(); @@ -1037,10 +1051,11 @@ public function addOptionEditor(string $id, string $label, array $values, array $this->addJavascriptCode(' function addOptionRow(dataId, checkUrl, deleteUrl, csrfToken, translationStrings) { - const table = document.getElementById(dataId + "_table").getElementsByTagName("tbody")[0]; + const table = document.querySelector("#" + dataId + "_table tbody.admidio-sortable"); const newRow = document.createElement("tr"); const rows = table.querySelectorAll(\'tr[id^="\' + dataId + \'_option_"]\'); - let maxId = 0; + let notSortableContainer = document.querySelector("#" + dataId + "_table tbody.admidio-not-sortable"); + let maxId = notSortableContainer ? notSortableContainer.querySelectorAll(\'tr[id^="\' + dataId + \'_option_"]\').length : 0; rows.forEach(row => { const currentId = row.id.replace(dataId + "_option_", ""); const num = parseInt(currentId, 10); @@ -1100,7 +1115,7 @@ function deleteEntry(dataId, entryId, checkUrl, deleteUrl, deleteMsg, csrfToken) if (!row) return; const table = row.parentNode; - const countOptions = table.querySelectorAll(\'tr[id^="\' + dataId + \'_option_"]\').length; + const countOptions = table.querySelectorAll(\'tr[id^="\' + dataId + \'_option_"]\').length + document.querySelector("#" + dataId + "_table tbody.admidio-not-sortable").querySelectorAll(\'tr[id^="\' + dataId + \'_option_"]\').length; // If there is only one option left, do not delete it or mark it as obsolete if (countOptions <= 1) return; diff --git a/src/UI/Presenter/InventoryFieldsPresenter.php b/src/UI/Presenter/InventoryFieldsPresenter.php index d0ecaff3be..129ec8edf5 100644 --- a/src/UI/Presenter/InventoryFieldsPresenter.php +++ b/src/UI/Presenter/InventoryFieldsPresenter.php @@ -11,6 +11,7 @@ use Admidio\UI\Presenter\FormPresenter; use Admidio\UI\Presenter\PagePresenter; use Admidio\Changelog\Service\ChangelogService; +use Admidio\Infrastructure\Language; /** * @brief Class with methods to display the module pages. @@ -60,7 +61,7 @@ public function createEditForm(string $itemFieldUUID = '', string $itemFieldName $this->addJavascript(' $("#inf_type").change(function() { - if ($("#inf_type").val() === "DROPDOWN" || $("#inf_type").val() === "RADIO_BUTTON") { + if ($("#inf_type").val() === "DROPDOWN" || $("#inf_type").val() === "DROPDOWN_MULTISELECT" || $("#inf_type").val() === "RADIO_BUTTON") { $("#ifo_inf_options_table").attr("required", "required"); $("#ifo_inf_options_group").addClass("admidio-form-group-required"); $("#ifo_inf_options_group").show("slow"); @@ -122,6 +123,7 @@ public function createEditForm(string $itemFieldUUID = '', string $itemFieldName 'DATE' => $gL10n->get('SYS_DATE'), 'DECIMAL' => $gL10n->get('SYS_DECIMAL_NUMBER'), 'DROPDOWN' => $gL10n->get('SYS_DROPDOWN_LISTBOX'), + 'DROPDOWN_MULTISELECT' => $gL10n->get('SYS_DROPDOWN_MULTISELECT_LISTBOX'), 'EMAIL' => $gL10n->get('SYS_EMAIL'), 'NUMBER' => $gL10n->get('SYS_NUMBER'), 'PHONE' => $gL10n->get('SYS_PHONE'), @@ -151,17 +153,20 @@ public function createEditForm(string $itemFieldUUID = '', string $itemFieldName } $options = new SelectOptions($gDb, $itemField->getValue('inf_id')); - $optionValueList = $options->getAllOptions($gSettingsManager->getBool('inventory_show_obsolete_select_field_options')); + foreach ($options->getAllOptions($gSettingsManager->getBool('inventory_show_obsolete_select_field_options')) as $option) { + $option['value'] = Language::translateIfTranslationStrId($option['value']); + $optionValueList[] = $option; + } if (empty($optionValueList)) { $optionValueList = array( - 0 => array('id' => 1, 'value' => '', 'sequence' => 0, 'obsolete' => false) + 0 => array('id' => 1, 'value' => '', 'system' => false, 'sequence' => 0, 'obsolete' => false) ); } $form->addOptionEditor( 'ifo_inf_options', $gL10n->get('SYS_VALUE_LIST'), $optionValueList, - array('helpTextId' => array('SYS_VALUE_LIST_DESC' /* SYS_INVENTORY_VALUE_LIST_DESC */, array('', '')), 'filename' => 'inventory') + array('helpTextId' => array('SYS_VALUE_LIST_DESC', array('', '')), 'filename' => 'inventory') ); $mandatoryFieldValues = array(0 => 'SYS_NO', 1 => 'SYS_YES'); @@ -175,7 +180,7 @@ public function createEditForm(string $itemFieldUUID = '', string $itemFieldName $form->addEditor( 'inf_description', $gL10n->get('SYS_DESCRIPTION'), - $itemField->getValue('inf_description'), + $itemField->getValue('inf_description', 'database'), array('toolbar' => 'AdmidioComments')); $form->addSubmitButton( @@ -246,12 +251,12 @@ public function createList() $templateItemFields = array(); $itemFieldCategoryID = -1; $prevItemFieldCategoryID = -1; - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrowing fields + $borrowingFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); foreach ($items->getItemFields() as $itemField) { - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($itemField->getValue('inf_name_intern'), $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($itemField->getValue('inf_name_intern'), $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } $prevItemFieldCategoryID = $itemFieldCategoryID; $itemFieldCategoryID = ((bool)$itemField->getValue('inf_system')) ? 1 : 2; @@ -270,6 +275,7 @@ public function createList() 'CHECKBOX' => $gL10n->get('SYS_CHECKBOX'), 'DATE' => $gL10n->get('SYS_DATE'), 'DROPDOWN' => $gL10n->get('SYS_DROPDOWN_LISTBOX'), + 'DROPDOWN_MULTISELECT' => $gL10n->get('SYS_DROPDOWN_MULTISELECT_LISTBOX'), 'EMAIL' => $gL10n->get('SYS_EMAIL'), 'RADIO_BUTTON' => $gL10n->get('SYS_RADIO_BUTTON'), 'PHONE' => $gL10n->get('SYS_PHONE'), diff --git a/src/UI/Presenter/InventoryImportPresenter.php b/src/UI/Presenter/InventoryImportPresenter.php index 4c2c603af0..0b9fa8ad5f 100644 --- a/src/UI/Presenter/InventoryImportPresenter.php +++ b/src/UI/Presenter/InventoryImportPresenter.php @@ -268,12 +268,12 @@ public function createAssignFieldsForm(): void return !is_null($value); }); - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrowing fields + $borrowingFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); $items = new ItemsData($gDb, $gCurrentOrgId); foreach ($items->getItemFields() as $itemField) { - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($itemField->getValue('inf_name_intern'), $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($itemField->getValue('inf_name_intern'), $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } $fieldDefaultValue = -1; diff --git a/src/UI/Presenter/InventoryItemPresenter.php b/src/UI/Presenter/InventoryItemPresenter.php index 61e1d30671..1b9981ddea 100644 --- a/src/UI/Presenter/InventoryItemPresenter.php +++ b/src/UI/Presenter/InventoryItemPresenter.php @@ -7,11 +7,14 @@ use Admidio\Changelog\Service\ChangelogService; use Admidio\Infrastructure\Exception; use Admidio\Infrastructure\Utils\SecurityUtils; +use Admidio\Infrastructure\Utils\SystemInfoUtils; +use Admidio\Infrastructure\Utils\PhpIniUtils; use Admidio\Inventory\Entity\Item; use Admidio\Inventory\ValueObjects\ItemsData; use Admidio\UI\Presenter\FormPresenter; use Admidio\UI\Presenter\PagePresenter; use Admidio\Users\Entity\User; +use DateTime; use Ramsey\Uuid\Uuid; /** @@ -41,8 +44,8 @@ class InventoryItemPresenter extends PagePresenter public function createEditForm(string $itemUUID = '', bool $getCopy = false) { global $gCurrentSession, $gSettingsManager, $gCurrentUser, $gProfileFields, $gL10n, $gCurrentOrgId, $gDb; - //array with the internal field names of the lend fields not used in the edit form - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrow fields not used in the edit form + $borrowFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); // Create user-defined field object $items = new ItemsData($gDb, $gCurrentOrgId); @@ -67,6 +70,15 @@ public function createEditForm(string $itemUUID = '', bool $getCopy = false) } } + $this->addJavascript(' + function callbackItemPicture() { + var imgSrc = $("#adm_inventory_item_picture").attr("src"); + var timestamp = new Date().getTime(); + $("#adm_button_delete_picture").hide(); + $("#adm_inventory_item_picture").attr("src", imgSrc + "&" + timestamp); + } + '); + // show form $form = new FormPresenter( 'adm_item_edit_form', @@ -78,17 +90,8 @@ public function createEditForm(string $itemUUID = '', bool $getCopy = false) foreach ($items->getItemFields() as $itemField) { $helpId = ''; $infNameIntern = $itemField->getValue('inf_name_intern'); - // Skip lend fields that are not used in the edit form - if (in_array($itemField->getValue('inf_name_intern'), $lendFieldNames)) { - if ($infNameIntern === 'IN_INVENTORY') { - // we need to add the checkbox for IN_INVENTORY defaulting to true - $form->addInput( - 'INF-' . $infNameIntern, - $items->getProperty($infNameIntern, 'inf_name'), - ($itemUUID === '') ? true : (bool)$items->getValue($infNameIntern), - array('property' => FormPresenter::FIELD_HIDDEN) - ); - } + // Skip borrow fields that are not used in the edit form + if (in_array($itemField->getValue('inf_name_intern'), $borrowFieldNames)) { continue; } @@ -123,20 +126,31 @@ public function createEditForm(string $itemUUID = '', bool $getCopy = false) ); break; - case 'DROPDOWN': - $form->addSelectBox( + case 'DROPDOWN': // fallthrough + case 'DROPDOWN_MULTISELECT': + $arrOptions = $items->getProperty($infNameIntern, 'ifo_inf_options', '', false); + $defaultValue = $items->getValue($infNameIntern, 'database'); + // prevent adding an empty string to the selectbox + if ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') { + // prevent adding an empty string to the selectbox + $defaultValue = ($defaultValue !== "") ? explode(',', $defaultValue) : array(); + } + + $form->addSelectBox( 'INF-' . $infNameIntern, $items->getProperty($infNameIntern, 'inf_name'), - $items->getProperty($infNameIntern, 'ifo_inf_options'), + $arrOptions, array( 'property' => $fieldProperty, - 'defaultValue' => $items->getValue($infNameIntern, 'database'), + 'defaultValue' => $defaultValue, 'helpTextId' => $helpId, - 'icon' => $items->getProperty($infNameIntern, 'inf_icon', 'database') + 'icon' => $items->getProperty($infNameIntern, 'inf_icon', 'database'), + 'multiselect' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? true : false, + 'maximumSelectionNumber' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? count($arrOptions) : 0, ) ); break; - + case 'RADIO_BUTTON': $form->addRadioButton( 'INF-' . $infNameIntern, @@ -278,6 +292,19 @@ public function createEditForm(string $itemUUID = '', bool $getCopy = false) $this->assignSmartyVariable('lastUserEditedName', $item->getNameOfLastEditingUser()); $this->assignSmartyVariable('lastUserEditedTimestamp', $item->getValue('ini_timestamp_change')); + // only show the item picture if the module setting is enabled + if ($gSettingsManager->GetBool('inventory_item_picture_enabled')) { + $this->assignSmartyVariable('urlItemPicture', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_show', 'item_uuid' => $itemUUID))); + // the image can only be deleted if corresponding rights exist + if ($gCurrentUser->isAdministratorInventory() || in_array($itemField->getValue('inf_name_intern'), $allowedFields)) { + $this->assignSmartyVariable('urlItemPictureUpload', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_choose', 'item_uuid' => $itemUUID))); + if ((string)$item->getValue('ini_picture') !== '' && $gSettingsManager->getInt('inventory_item_picture_storage') === 0 + || is_file(ADMIDIO_PATH . FOLDER_DATA . '/inventory_item_pictures/' . $items->getItemId() . '.jpg') && $gSettingsManager->getInt('inventory_item_picture_storage') === 1) { + $this->assignSmartyVariable('urlItemPictureDelete', 'callUrlHideElement(\'no_element\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_delete', 'item_uuid' => $itemUUID)) . '\', \'' . $gCurrentSession->getCsrfToken() . '\', \'callbackItemPicture\')'); + } + } + } + $form->addToHtmlPage(); $gCurrentSession->addFormObject($form); } @@ -290,10 +317,10 @@ public function createEditForm(string $itemUUID = '', bool $getCopy = false) public function createEditItemsForm(array $itemUUIDs = array()) { global $gCurrentSession, $gSettingsManager, $gCurrentUser, $gProfileFields, $gL10n, $gCurrentOrgId, $gDb; - // array with the internal field names of the lend fields not used in the edit form + // array with the internal field names of the borrow fields not used in the edit form // we also exclude IITEMNAME from the edit form, because it is only used for displaying item values based on the first entry // and it is not wanted to change the item name for multiple items at once - $lendFieldNames = array('ITEMNAME', 'IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + $borrowFieldNames = array('ITEMNAME', 'LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); // Create user-defined field object $items = new ItemsData($gDb, $gCurrentOrgId); @@ -329,8 +356,8 @@ public function createEditItemsForm(array $itemUUIDs = array()) foreach ($items->getItemFields() as $itemField) { $helpId = ''; $infNameIntern = $itemField->getValue('inf_name_intern'); - // Skip lend fields that are not used in the edit form - if (in_array($itemField->getValue('inf_name_intern'), $lendFieldNames)) { + // Skip borrow fields that are not used in the edit form + if (in_array($itemField->getValue('inf_name_intern'), $borrowFieldNames)) { if ($infNameIntern === 'ITEMNAME') { // If the item is new, we need to add the input for ITEMNAME $itemNames = ''; @@ -385,17 +412,28 @@ public function createEditItemsForm(array $itemUUIDs = array()) ); break; - case 'DROPDOWN': - $form->addSelectBox( + case 'DROPDOWN': // fallthrough + case 'DROPDOWN_MULTISELECT': + $arrOptions = $items->getProperty($infNameIntern, 'ifo_inf_options', '', false); + $defaultValue = $items->getValue($infNameIntern, 'database'); + // prevent adding an empty string to the selectbox + if ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') { + // prevent adding an empty string to the selectbox + $defaultValue = ($defaultValue !== "") ? explode(',', $defaultValue) : array(); + } + + $form->addSelectBox( 'INF-' . $infNameIntern, $items->getProperty($infNameIntern, 'inf_name'), - $items->getProperty($infNameIntern, 'ifo_inf_options'), + $arrOptions, array( 'property' => $fieldProperty, - 'defaultValue' => $items->getValue($infNameIntern, 'database'), + 'defaultValue' => $defaultValue, 'helpTextId' => $helpId, 'icon' => $items->getProperty($infNameIntern, 'inf_icon', 'database'), - 'toggleable' => true + 'toggleable' => true, + 'multiselect' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? true : false, + 'maximumSelectionNumber' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? count($arrOptions) : 0, ) ); break; @@ -586,11 +624,11 @@ function toggleItemFields(fieldIdPrefix) { * @param string $itemFieldID ID of the item field that should be edited. * @throws Exception */ - public function createEditLendForm(string $itemUUID) + public function createEditBorrowForm(string $itemUUID) { global $gCurrentSession, $gSettingsManager, $gCurrentUser, $gL10n, $gCurrentOrgId, $gDb; - //array with the internal field names of the lend fields not used in the edit form - $lendFieldNames = array('ITEMNAME', 'IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrow fields not used in the edit form + $borrowFieldNames = array('ITEMNAME', 'LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); // Create user-defined field object $items = new ItemsData($gDb, $gCurrentOrgId); @@ -598,12 +636,12 @@ public function createEditLendForm(string $itemUUID) // Check if itemUUID is valid if (!Uuid::isValid($itemUUID)) { throw new Exception('The parameter "' . $itemUUID . '" is not a valid UUID!'); - } elseif ($gSettingsManager->GetBool('inventory_items_disable_lending')) { + } elseif ($gSettingsManager->GetBool('inventory_items_disable_borrowing')) { throw new Exception('SYS_INVALID_PAGE_VIEW'); } // display History button - ChangelogService::displayHistoryButton($this, 'inventory', 'inventory_item_lend_data', $gCurrentUser->isAdministratorInventory(), ['uuid' => $itemUUID]); + ChangelogService::displayHistoryButton($this, 'inventory', 'inventory_item_borrow_data', $gCurrentUser->isAdministratorInventory(), ['uuid' => $itemUUID]); // Read item data $items->readItemData($itemUUID); @@ -613,26 +651,10 @@ public function createEditLendForm(string $itemUUID) throw new Exception('SYS_NO_RIGHTS'); } - foreach ($items->getItemFields() as $itemField) { - $infNameIntern = $itemField->getValue('inf_name_intern'); - if($infNameIntern === 'IN_INVENTORY') { - $pimInInventory = $infNameIntern; - } - elseif($infNameIntern === 'LAST_RECEIVER') { - $pimLastReceiver = $infNameIntern; - } - elseif ($infNameIntern === 'RECEIVED_ON') { - $pimReceivedOn = $infNameIntern; - } - elseif ($infNameIntern === 'RECEIVED_BACK_ON') { - $pimReceivedBackOn = $infNameIntern; - } - } - // show form $form = new FormPresenter( - 'adm_item_edit_lend_form', - 'modules/inventory.item.edit.lend.tpl', + 'adm_item_edit_borrow_form', + 'modules/inventory.item.edit.borrow.tpl', SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('item_uuid' => $itemUUID, 'mode' => 'item_save')), $this ); @@ -641,8 +663,18 @@ public function createEditLendForm(string $itemUUID) $helpId = ''; $infNameIntern = $itemField->getValue('inf_name_intern'); - // Skip all fields not used in the lend form - if (!in_array($infNameIntern, $lendFieldNames)) { + if($infNameIntern === 'LAST_RECEIVER') { + $ivtLastReceiver = $infNameIntern; + } + elseif ($infNameIntern === 'BORROW_DATE') { + $ivtBorrowDate = $infNameIntern; + } + elseif ($infNameIntern === 'RETURN_DATE') { + $ivtReturnDate = $infNameIntern; + } + + // Skip all fields not used in the borrow form + if (!in_array($infNameIntern, $borrowFieldNames)) { continue; } @@ -657,32 +689,30 @@ public function createEditLendForm(string $itemUUID) if (!$gCurrentUser->isAdministratorInventory() && !in_array($itemField->getValue('inf_name_intern'), $allowedFields)) { $fieldProperty = FormPresenter::FIELD_DISABLED; } - - if (isset($pimInInventory, $pimLastReceiver, $pimReceivedOn, $pimReceivedBackOn) && $infNameIntern === 'IN_INVENTORY') { - // Add JavaScript to check the LAST_RECEIVER field and set the required attribute for pimReceivedOnId and pimReceivedBackOnId + + if (isset($ivtLastReceiver, $ivtBorrowDate, $ivtReturnDate)) { + // Add JavaScript to check the LAST_RECEIVER field and set the required attribute for ivtBorrowDate and ivtReturnDate $this->addJavascript(' document.addEventListener("DOMContentLoaded", function() { - if (document.querySelector("[id=\'INF-' . $pimReceivedOn . '_time\']")) { + if (document.querySelector("[id=\'INF-' . $ivtBorrowDate . '_time\']")) { var pDateTime = "true"; } else { var pDateTime = "false"; } - var pimInInventoryField = document.querySelector("[id=\'INF-' . $pimInInventory . '\']"); - var pimInInventoryGroup = document.getElementById("INF-' . $pimInInventory . '_group"); - var pimLastReceiverField = document.querySelector("[id=\'INF-' . $pimLastReceiver . '\']"); - var pimLastReceiverGroup = document.getElementById("INF-' . $pimLastReceiver . '_group"); - var pimReceivedOnField = document.querySelector("[id=\'INF-' . $pimReceivedOn . '\']"); - + var ivtLastReceiverField = document.querySelector("[id=\'INF-' . $ivtLastReceiver . '\']"); + var ivtBorrowDateField = document.querySelector("[id=\'INF-' . $ivtBorrowDate . '\']"); + var ivtReturnDateField = document.querySelector("[id=\'INF-' . $ivtReturnDate . '\']"); + if (pDateTime === "true") { - var pimReceivedOnFieldTime = document.querySelector("[id=\'INF-' . $pimReceivedOn . '_time\']"); - var pimReceivedBackOnFieldTime = document.querySelector("[id=\'INF-' . $pimReceivedBackOn . '_time\']"); + var ivtBorrowDateFieldTime = document.querySelector("[id=\'INF-' . $ivtBorrowDate . '_time\']"); + var ivtReturnDateFieldTime = document.querySelector("[id=\'INF-' . $ivtReturnDate . '_time\']"); } - var pimReceivedOnGroup = document.getElementById("INF-' . $pimReceivedOn . '_group"); - var pimReceivedBackOnField = document.querySelector("[id=\'INF-' . $pimReceivedBackOn . '\']"); - var pimReceivedBackOnGroup = document.getElementById("INF-' . $pimReceivedBackOn . '_group"); - + var ivtLastReceiverGroup = document.getElementById("INF-' . $ivtLastReceiver . '_group"); + var ivtBorrowDateGroup = document.getElementById("INF-' . $ivtBorrowDate . '_group"); + var ivtReturnDateGroup = document.getElementById("INF-' . $ivtReturnDate . '_group"); + function setRequired(field, group, required) { if (required) { field.setAttribute("required", "required"); @@ -692,91 +722,58 @@ function setRequired(field, group, required) { group.classList.remove("admidio-form-group-required"); } } - - window.checkPimInInventory = function() { - var isInInventoryChecked = pimInInventoryField.checked; - var lastReceiverValue = pimLastReceiverField.value; - var receivedBackOnValue = pimReceivedBackOnField.value; - - setRequired(pimReceivedOnField, pimReceivedOnGroup, isInInventoryChecked && (lastReceiverValue && lastReceiverValue !== "undefined")); - setRequired(pimReceivedBackOnField, pimReceivedBackOnGroup, isInInventoryChecked && (lastReceiverValue && lastReceiverValue !== "undefined")); - if (pDateTime === "true") { - setRequired(pimReceivedOnFieldTime, pimReceivedOnGroup, isInInventoryChecked && (lastReceiverValue && lastReceiverValue !== "undefined")); - setRequired(pimReceivedBackOnFieldTime, pimReceivedBackOnGroup, isInInventoryChecked && (lastReceiverValue && lastReceiverValue !== "undefined")); - } - - setRequired(pimLastReceiverField, pimLastReceiverGroup, !isInInventoryChecked); - setRequired(pimReceivedOnField, pimReceivedOnGroup, !isInInventoryChecked); + + window.checkItemBorrowState = function() { + var lastReceiverValue = ivtLastReceiverField.value; + var borrowDateValue = ivtBorrowDateField.value; + var returnDateValue = ivtReturnDateField.value; + var requiredLastReceiverCheck = borrowDateValue !== "" || returnDateValue !== ""; + var requiredBorrowDateCheck = (lastReceiverValue && lastReceiverValue !== "undefined") || returnDateValue !== ""; + + setRequired(ivtLastReceiverField, ivtLastReceiverGroup, requiredLastReceiverCheck); + setRequired(ivtBorrowDateField, ivtBorrowDateGroup, requiredBorrowDateCheck); if (pDateTime === "true") { - setRequired(pimReceivedOnFieldTime, pimReceivedOnGroup, !isInInventoryChecked); - } - - if (!isInInventoryChecked && (lastReceiverValue === "undefined" || !lastReceiverValue)) { - pimReceivedOnField.value = ""; - if (pDateTime === "true") { - pimReceivedOnFieldTime.value = ""; - } + setRequired(ivtBorrowDateFieldTime, ivtBorrowDateGroup, requiredBorrowDateCheck); } - if (receivedBackOnValue !== "") { - setRequired(pimLastReceiverField, pimLastReceiverGroup, true); - setRequired(pimReceivedOnField, pimReceivedOnGroup, true); + if (returnDateValue !== "") { + setRequired(ivtLastReceiverField, ivtLastReceiverGroup, true); + setRequired(ivtBorrowDateField, ivtBorrowDateGroup, true); if (pDateTime === "true") { - setRequired(pimReceivedOnFieldTime, pimReceivedOnGroup, true); - setRequired(pimReceivedBackOnFieldTime, pimReceivedBackOnGroup, true); + setRequired(ivtBorrowDateFieldTime, ivtBorrowDateGroup, true); + setRequired(ivtReturnDateFieldTime, ivtReturnDateGroup, true); } } - - var previousPimInInventoryState = isInInventoryChecked; - - pimInInventoryField.addEventListener("change", function() { - if (!pimInInventoryField.checked && previousPimInInventoryState) { - pimReceivedBackOnField.value = ""; - if (pDateTime === "true") { - pimReceivedBackOnFieldTime.value = ""; - } - } - previousPimInInventoryState = pimInInventoryField.checked; - window.checkPimInInventory(); - }); - - pimLastReceiverField.addEventListener("change", window.checkPimInInventory); - pimReceivedBackOnField.addEventListener("input", window.checkPimInInventory); - pimReceivedOnField.addEventListener("input", validateReceivedOnAndBackOn); - if (pDateTime === "true") { - pimReceivedOnFieldTime.addEventListener("input", validateReceivedOnAndBackOn); - pimReceivedBackOnFieldTime.addEventListener("input", validateReceivedOnAndBackOn); - } } function validateReceivedOnAndBackOn() { if (pDateTime === "true") { - var receivedOnDate = new Date(pimReceivedOnField.value + " " + pimReceivedOnFieldTime.value); - var receivedBackOnDate = new Date(pimReceivedBackOnField.value + " " + pimReceivedBackOnFieldTime.value); + var receivedOnDate = new Date(ivtBorrowDateField.value + " " + ivtBorrowDateFieldTime.value); + var receivedBackOnDate = new Date(ivtReturnDateField.value + " " + ivtReturnDateFieldTime.value); } else { - var receivedOnDate = new Date(pimReceivedOnField.value); - var receivedBackOnDate = new Date(pimReceivedBackOnField.value); + var receivedOnDate = new Date(ivtBorrowDateField.value); + var receivedBackOnDate = new Date(ivtReturnDateField.value); } if (receivedOnDate > receivedBackOnDate) { - pimReceivedOnField.setCustomValidity("ReceivedOn date cannot be after ReceivedBack date."); + ivtBorrowDateField.setCustomValidity("' . $gL10n->get('SYS_INVENTORY_BORROW_DATE_WARNING') . '"); } else { - pimReceivedOnField.setCustomValidity(""); + ivtBorrowDateField.setCustomValidity(""); } } - pimInInventoryField.addEventListener("change", window.checkPimInInventory); - pimLastReceiverField.addEventListener("change", window.checkPimInInventory); - - pimReceivedOnField.addEventListener("input", validateReceivedOnAndBackOn); - pimReceivedBackOnField.addEventListener("input", window.checkPimInInventory); - + ivtLastReceiverField.addEventListener("change", window.checkItemBorrowState); + ivtBorrowDateField.addEventListener("input", window.checkItemBorrowState); + ivtReturnDateField.addEventListener("input", window.checkItemBorrowState); + + ivtBorrowDateField.addEventListener("input", validateReceivedOnAndBackOn); + ivtReturnDateField.addEventListener("input", validateReceivedOnAndBackOn); if (pDateTime === "true") { - pimReceivedOnFieldTime.addEventListener("input", validateReceivedOnAndBackOn); - pimReceivedBackOnFieldTime.addEventListener("input", validateReceivedOnAndBackOn); + ivtBorrowDateFieldTime.addEventListener("input", validateReceivedOnAndBackOn); + ivtReturnDateFieldTime.addEventListener("input", validateReceivedOnAndBackOn); } - pimReceivedBackOnField.addEventListener("input", validateReceivedOnAndBackOn); - window.checkPimInInventory(); + + window.checkItemBorrowState(); }); '); } @@ -799,16 +796,27 @@ function validateReceivedOnAndBackOn() { ); break; - case 'DROPDOWN': - $form->addSelectBox( + case 'DROPDOWN': // fallthrough + case 'DROPDOWN_MULTISELECT': + $arrOptions = $items->getProperty($infNameIntern, 'ifo_inf_options', '', false); + $defaultValue = $items->getValue($infNameIntern, 'database'); + // prevent adding an empty string to the selectbox + if ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') { + // prevent adding an empty string to the selectbox + $defaultValue = ($defaultValue !== "") ? explode(',', $defaultValue) : array(); + } + + $form->addSelectBox( 'INF-' . $infNameIntern, $items->getProperty($infNameIntern, 'inf_name'), - $items->getProperty($infNameIntern, 'ifo_inf_options'), + $arrOptions, array( 'property' => $fieldProperty, - 'defaultValue' => $items->getValue($infNameIntern, 'database'), + 'defaultValue' => $defaultValue, 'helpTextId' => $helpId, - 'icon' => $items->getProperty($infNameIntern, 'inf_icon', 'database') + 'icon' => $items->getProperty($infNameIntern, 'inf_icon', 'database'), + 'multiselect' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? true : false, + 'maximumSelectionNumber' => ($items->getProperty($infNameIntern, 'inf_type') === 'DROPDOWN_MULTISELECT') ? count($arrOptions) : 0, ) ); break; @@ -835,16 +843,16 @@ function validateReceivedOnAndBackOn() { ); $this->addJavascript(' - var selectIdLastReceiver = "#INF-' . $pimLastReceiver . '"; + var selectIdLastReceiver = "#INF-' . $ivtLastReceiver . '"; var defaultValue = "' . htmlspecialchars($items->getValue($infNameIntern)) . '"; var defaultText = "' . htmlspecialchars($items->getValue($infNameIntern)) . '"; // Der Text für den Default-Wert function isSelect2Empty(selectId) { // Hole den aktuellen Wert des Select2-Feldes - var renderedElement = $("#select2-INF-' . $pimLastReceiver .'-container"); + var renderedElement = $("#select2-INF-' . $ivtLastReceiver .'-container"); if (renderedElement.length) { - window.checkPimInInventory(); + window.checkItemBorrowState(); } } // Prüfe, ob der Default-Wert in den Optionen enthalten ist @@ -854,7 +862,7 @@ function isSelect2Empty(selectId) { $(selectIdLastReceiver).append(newOption).trigger("change"); } - $("#INF-' . $pimLastReceiver .'").select2({ + $("#INF-' . $ivtLastReceiver .'").select2({ theme: "bootstrap-5", allowClear: true, placeholder: "", @@ -873,6 +881,13 @@ function isSelect2Empty(selectId) { if ($items->getProperty($infNameIntern, 'inf_type') === 'DATE') { $fieldType = $gSettingsManager->getString('inventory_field_date_time_format'); $maxlength = null; + $date = new DateTime('now'); + if ($fieldType === 'datetime') { + $defaultDate = $date->format('Y-m-d H:i'); + } else { + $defaultDate = $date->format('Y-m-d'); + } + } elseif ($infNameIntern === 'ITEMNAME') { $fieldProperty = FormPresenter::FIELD_DISABLED; @@ -884,7 +899,7 @@ function isSelect2Empty(selectId) { $form->addInput( 'INF-' . $infNameIntern, $items->getProperty($infNameIntern, 'inf_name'), - $items->getValue($infNameIntern), + ($items->getValue($infNameIntern) === '' && $infNameIntern === 'BORROW_DATE') ? $defaultDate : $items->getValue($infNameIntern), array( 'type' => $fieldType, 'maxLength' => isset($maxlength) ? $maxlength : null, @@ -924,4 +939,76 @@ function isSelect2Empty(selectId) { $form->addToHtmlPage(); $gCurrentSession->addFormObject($form); } + + /** + * Create the data for the picture upload form of a item. + * @param string $itemUUID UUID of the item that should be edited. + */ + public function createPictureChooseForm(string $itemUUID) + { + global $gCurrentSession, $gL10n; + // show form + $form = new FormPresenter( + 'adm_upload_picture_form', + 'modules/inventory.new-item-picture.upload.tpl', + SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_upload', 'item_uuid' => $itemUUID)), + $this, + array('enableFileUpload' => true) + ); + $form->addCustomContent( + 'item_picture_current', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT'), + '' . $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT') . '' + ); + $form->addFileUpload( + 'item_picture_upload_file', + $gL10n->get('SYS_SELECT_PHOTO'), + array( + 'property' => FormPresenter::FIELD_REQUIRED, + 'allowedMimeTypes' => array('image/jpeg', 'image/png'), + 'helpTextId' => array('SYS_INVENTORY_ITEM_PICTURE_RESTRICTIONS', array(round(SystemInfoUtils::getProcessableImageSize() / 1000000, 2), round(PhpIniUtils::getUploadMaxSize() / 1024 ** 2, 2))) + ) + ); + $form->addSubmitButton( + 'adm_button_upload', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_UPLOAD'), + array('icon' => 'bi-upload', 'class' => 'offset-sm-3') + ); + + $form->addToHtmlPage(); + $gCurrentSession->addFormObject($form); + } + + /** + * Create the data for the picture preview form of a item. + * @param string $itemUUID UUID of the item that should be edited. + */ + public function createPictureReviewForm(string $itemUUID) + { + global $gCurrentSession, $gL10n; + // show form + $form = new FormPresenter( + 'adm_review_picture_form', + 'modules/inventory.new-item-picture.tpl', + SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_save', 'item_uuid' => $itemUUID)), + $this + ); + $form->addCustomContent( + 'item_picture_current', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT'), + '' . $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT') . '' + ); + $form->addCustomContent( + 'item_picture_new', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_NEW'), + '' . $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_NEW') . '' + ); + $form->addSubmitButton( + 'adm_button_save', + $gL10n->get('SYS_APPLY'), + array('icon' => 'bi-upload', 'class' => 'offset-sm-3') + ); + $form->addToHtmlPage(); + $gCurrentSession->addFormObject($form); + } } diff --git a/src/UI/Presenter/InventoryPresenter.php b/src/UI/Presenter/InventoryPresenter.php index b9df01192e..a20cc84bd0 100644 --- a/src/UI/Presenter/InventoryPresenter.php +++ b/src/UI/Presenter/InventoryPresenter.php @@ -3,13 +3,12 @@ namespace Admidio\UI\Presenter; // Admidio namespaces -use Admidio\Categories\Entity\Category; use Admidio\Categories\Service\CategoryService; -use Admidio\Infrastructure\Language; use Admidio\Infrastructure\Exception; use Admidio\Infrastructure\Utils\SecurityUtils; use Admidio\Infrastructure\Utils\StringUtils; use Admidio\Inventory\ValueObjects\ItemsData; +use Admidio\Inventory\Entity\SelectOptions; use Admidio\Changelog\Service\ChangelogService; use Admidio\UI\Component\DataTables; use Admidio\UI\Presenter\FormPresenter; @@ -56,13 +55,17 @@ class InventoryPresenter extends PagePresenter */ protected int $getFilterKeeper = 0; /** - * @var int filter for all items + * @var bool true if the current user is the keeper of an item */ - protected int $getFilterItems = 0; + protected string $getFilterLastReceiver = ''; + /** + * @var int filter id for the status selection + */ + protected int $getFilterStatus = 0; /** * @var bool true if all items should be shown */ - protected bool $showFormerItems = false; + protected bool $showRetiredItems = false; /** @@ -77,12 +80,14 @@ public function __construct() $this->getFilterString = admFuncVariableIsValid($_GET, 'items_filter_string', 'string', array('defaultValue' => '')); $this->getFilterCategoryUUID = admFuncVariableIsValid($_GET, 'items_filter_category', 'string', array('defaultValue' => '')); $this->getFilterKeeper = admFuncVariableIsValid($_GET, 'items_filter_keeper', 'int', array('defaultValue' => 0)); - $this->getFilterItems = admFuncVariableIsValid($_GET, 'items_filter', 'int', array('defaultValue' => 0)); + $this->getFilterLastReceiver = admFuncVariableIsValid($_GET, 'items_filter_last_receiver', 'string', array('defaultValue' => '')); + $this->getFilterStatus = admFuncVariableIsValid($_GET, 'items_filter_status', 'int', array('defaultValue' => 1)); $this->itemsData = new ItemsData($gDb, $gCurrentOrgId); - $this->showFormerItems = ($this->getFilterItems >= 1) ? true : false; - $this->itemsData->showFormerItems($this->showFormerItems); + // check if the user has selected to show retired items + $this->showRetiredItems = ($this->getFilterStatus === 0 || $this->getFilterStatus === 2) ? true : false; + $this->itemsData->showRetiredItems($this->showRetiredItems); $this->itemsData->readItems(); $this->categoryService = new CategoryService($gDb, 'IVT'); @@ -101,7 +106,7 @@ protected function createHeader(): void if ($gCurrentUser->isAdministratorInventory()) { // show link to view inventory history - ChangelogService::displayHistoryButton($this, 'inventory', 'inventory_fields,inventory_field_select_options,inventory_items,inventory_item_data,inventory_item_lend_data'); + ChangelogService::displayHistoryButton($this, 'inventory', 'inventory_fields,inventory_field_select_options,inventory_items,inventory_item_data,inventory_item_borrow_data'); // show link to create new item $this->addPageFunctionsMenuItem( @@ -158,7 +163,7 @@ protected function createHeader(): void $this->addJavascript(' // only submit non-empty filter values - $("#items_filter_category, #items_filter_keeper, #items_filter").on("change", function(){ + $("#items_filter_category, #items_filter_keeper, #items_filter_last_receiver, #items_filter_status").on("change", function(){ var form = $("#adm_navbar_filter_form"); // Text-Filter @@ -185,12 +190,20 @@ protected function createHeader(): void keeperSelect.attr("name", "items_filter_keeper"); } - // items filter - var itemsSelect = $("#items_filter"); + // Last Receiver + var lastReceiverSelect = $("#items_filter_last_receiver"); + if (lastReceiverSelect.val() === "") { + lastReceiverSelect.removeAttr("name"); + } else { + lastReceiverSelect.attr("name", "items_filter_last_receiver"); + } + + // items status filter + var itemsSelect = $("#items_filter_status"); if (itemsSelect.val() === "") { itemsSelect.removeAttr("name"); } else { - itemsSelect.attr("name", "items_filter"); + itemsSelect.attr("name", "items_filter_status"); } form.submit(); @@ -218,15 +231,15 @@ protected function createHeader(): void // create the print view link with the current filter values $("#menu_item_lists_print_view").off("click").on("click", function(e){ e.preventDefault(); - var textFilter = $("#items_filter_string").val() || ""; - var category = $("#items_filter_category").val() || ""; - var keeper = $("#items_filter_keeper").val() || ""; - var filterItems = $("#items_filter").val() || ""; + var textFilter = $("#items_filter_string").val() || ""; + var category = $("#items_filter_category").val() || ""; + var keeper = $("#items_filter_keeper").val() || ""; + var filterItems = $("#items_filter_status").val() || ""; var url = "' . $printBaseUrl . '" + "&items_filter_string=" + encodeURIComponent(textFilter) + "&items_filter_category=" + encodeURIComponent(category) + "&items_filter_keeper=" + encodeURIComponent(keeper) - + "&items_filter=" + encodeURIComponent(filterItems); + + "&items_filter_status=" + encodeURIComponent(filterItems); window.open(url, "_blank"); });', @@ -279,19 +292,60 @@ protected function createHeader(): void ) ); - $selectBoxValues = array( - '0' => $gL10n->get('SYS_INVENTORY_FILTER_CURRENT_ITEMS'), - '1' => $gL10n->get('SYS_INVENTORY_FILTER_FORMER_ITEMS'), - '2' => $gL10n->get('SYS_ALL') + // get all last receivers + $sql = 'SELECT DISTINCT borrowData.inb_last_receiver, + CASE + WHEN borrowData.inb_last_receiver = \'-1\' + THEN \'n/a\' + WHEN last_name.usd_value IS NOT NULL AND last_name.usd_value <> \'\' AND first_name.usd_value IS NOT NULL AND first_name.usd_value <> \'\' + THEN CONCAT_WS(\', \', last_name.usd_value, first_name.usd_value) + ELSE + borrowData.inb_last_receiver + END AS receiver_name + FROM ' . TBL_INVENTORY_ITEM_BORROW_DATA . ' AS borrowData + INNER JOIN ' . TBL_INVENTORY_FIELDS . ' AS fields + ON fields.inf_name_intern = \'LAST_RECEIVER\' + AND (fields.inf_org_id = ' . $gCurrentOrgId . ' OR fields.inf_org_id IS NULL) + LEFT JOIN ' . TBL_USER_DATA . ' AS last_name + ON last_name.usd_usr_id = borrowData.inb_last_receiver + AND last_name.usd_usf_id = ' . $gProfileFields->getProperty('LAST_NAME','usf_id') . ' + LEFT JOIN ' . TBL_USER_DATA . ' AS first_name + ON first_name.usd_usr_id = borrowData.inb_last_receiver + AND first_name.usd_usf_id = ' . $gProfileFields->getProperty('FIRST_NAME','usf_id') . ' + WHERE fields.inf_name_intern = \'LAST_RECEIVER\' + ORDER BY receiver_name ASC;'; + + // filter last receiver + $form->addSelectBoxFromSql( + 'items_filter_last_receiver', + $gL10n->get('SYS_INVENTORY_LAST_RECEIVER'), + $gDb, + $sql, + array( + 'property' => $showFilterForm, + 'defaultValue' => $this->getFilterLastReceiver, + 'showContextDependentFirstEntry' => true + ) ); + + // get the status options for the filter + $option = new SelectOptions($gDb, $this->itemsData->getProperty('STATUS', 'inf_id')); + $values = $option->getAllOptions(); + $selectBoxValues = array(); + foreach ($values as $value) { + $selectBoxValues[$value['id']] = $value['value']; + } + // add select all items to select box values + $selectBoxValues[0] = $gL10n->get('SYS_ALL'); + // filter all items $form->addSelectBox( - 'items_filter', + 'items_filter_status', $gL10n->get('SYS_INVENTORY_ITEMS'), $selectBoxValues, array( 'property' => $showFilterForm, - 'defaultValue' => $this->getFilterItems, + 'defaultValue' => $this->getFilterStatus, 'showContextDependentFirstEntry' => false ) ); @@ -323,7 +377,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_xlsx' ) ), @@ -337,7 +392,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_ods' ) ), @@ -351,7 +407,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_csv-ms' ) ), @@ -365,7 +422,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_csv-oo' ) ), @@ -379,7 +437,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_pdf' ) ), @@ -393,7 +452,8 @@ protected function createExportDropdown() : void 'items_filter_string' => $this->getFilterString, 'items_filter_category' => $this->getFilterCategoryUUID, 'items_filter_keeper' => $this->getFilterKeeper, - 'items_filter' => $this->getFilterItems, + 'items_filter_last_receiver' => $this->getFilterLastReceiver, + 'items_filter_status' => $this->getFilterStatus, 'mode' => 'print_pdfl' ) ), @@ -415,16 +475,16 @@ protected function createExportDropdown() : void $.each(buttons, function(suffix, modeValue){ var selector = "#menu_item_lists_" + suffix; $(selector).on("click", function(e){ - var textFilter = $("#items_filter_string").val() || ""; - var category = $("#items_filter_category").val() || ""; - var keeper = $("#items_filter_keeper").val() || ""; - var filterItems = $("#items_filter").val() || ""; + var textFilter = $("#items_filter_string").val() || ""; + var category = $("#items_filter_category").val() || ""; + var keeper = $("#items_filter_keeper").val() || ""; + var filterItems = $("#items_filter_status").val() || ""; var base = this.href.split("?")[0]; var qs = [ "items_filter_string=" + encodeURIComponent(textFilter), "items_filter_category=" + encodeURIComponent(category), "items_filter_keeper=" + encodeURIComponent(keeper), - "items_filter=" + encodeURIComponent(filterItems), + "items_filter_status=" + encodeURIComponent(filterItems), "mode=" + modeValue ].join("&"); this.href = base + "?" + qs; @@ -708,20 +768,20 @@ public function prepareData(string $mode = 'html') : array ); // Set default alignment and headers for the first column (abbreviation) - $columnAlign[] = 'center'; - $headers = array(0 => ''); + ($mode === 'html') ? $columnAlign[] = 'center' : $columnAlign = array(); + $headers = ($mode === 'html') ? array(0 => '') : array(); $exportHeaders = array(); $columnNumber = 1; - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrowing fields + $borrowingFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); // Build headers and column alignment for each item field foreach ($this->itemsData->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); $columnHeader = $this->itemsData->getProperty($infNameIntern, 'inf_name'); - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } // For the first column, add specific header configurations for export modes @@ -733,6 +793,11 @@ public function prepareData(string $mode = 'html') : array } else { $headers[] = $gL10n->get('SYS_ABR_NO'); + if ($mode === 'html' && $gSettingsManager->GetBool('inventory_item_picture_enabled')) { + // photo column + $headers[] = $gL10n->get('SYS_INVENTORY_ITEM_PICTURE'); + $columnAlign[] = 'center'; + } } } @@ -779,30 +844,40 @@ public function prepareData(string $mode = 'html') : array $this->itemsData->readItemData($item['ini_uuid']); $rowValues = array(); $rowValues['item_uuid'] = $item['ini_uuid']; - $strikethrough = $item['ini_former']; + $strikethrough = $this->itemsData->isRetired(); $columnNumber = 1; foreach ($this->itemsData->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } // Apply filters for CATEGORY and KEEPER if ( ($this->getFilterCategoryUUID !== '' && $infNameIntern === 'CATEGORY' && $this->getFilterCategoryUUID != $this->itemsData->getValue($infNameIntern, 'database')) || ($this->getFilterKeeper !== 0 && $infNameIntern === 'KEEPER' && $this->getFilterKeeper != $this->itemsData->getValue($infNameIntern)) || - ($this->getFilterItems === 0 && $item['ini_former']) || - ($this->getFilterItems === 1 && !$item['ini_former']) + ($this->getFilterLastReceiver !== '' && $infNameIntern === 'LAST_RECEIVER' && $this->getFilterLastReceiver != $this->itemsData->getValue($infNameIntern)) || + ($this->getFilterStatus !== 0 && $this->getFilterStatus !== $this->itemsData->getStatus()) ) { // skip to the next iteration of the next-outer loop continue 2; } if ($columnNumber === 1) { - $rowValues['data'][] = ''; + if ($mode === 'html') { + $rowValues['data'][] = ''; + } $rowValues['data'][] = $listRowNumber; + if ($mode === 'html' && $gSettingsManager->GetBool('inventory_item_picture_enabled')) { + $itemPhotoUrl = SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_show', 'item_uuid'=> $item['ini_uuid'])); + $itemPhotoModalUrl = SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_picture_show_modal', 'item_uuid'=> $item['ini_uuid'])); + $itemPhotoContent = ' + ' . $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT') . ' + '; + $rowValues['data'][] = $itemPhotoContent; + } } $content = $this->itemsData->getValue($infNameIntern, 'database'); @@ -810,8 +885,8 @@ public function prepareData(string $mode = 'html') : array // Process ITEMNAME column if ($infNameIntern === 'ITEMNAME' && strlen($content) > 0) { - if ($mode === 'html' && (($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) && !$item['ini_former'])) { - $content = '' . SecurityUtils::encodeHTML($content) . ''; + if ($mode === 'html' && (($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) && !$this->itemsData->isRetired())) { + $content = '' . SecurityUtils::encodeHTML($content) . ''; } else { $content = SecurityUtils::encodeHTML($content); } @@ -874,7 +949,7 @@ public function prepareData(string $mode = 'html') : array $content = in_array($mode, ['csv', 'pdf', 'xlsx', 'ods']) ? ($content == 1 ? $gL10n->get('SYS_YES') : $gL10n->get('SYS_NO')) : $this->itemsData->getHtmlValue($infNameIntern, $content); - } elseif (in_array($infType, ['DATE', 'DROPDOWN'])) { + } elseif (in_array($infType, ['DATE', 'DROPDOWN', 'DROPDOWN_MULTISELECT'])) { $content = $this->itemsData->getHtmlValue($infNameIntern, $content); } elseif ($infType === 'RADIO_BUTTON') { $content = $mode === 'html' @@ -895,7 +970,7 @@ public function prepareData(string $mode = 'html') : array // Append admin action column for HTML mode if ($mode === 'html') { $historyButton = ChangelogService::displayHistoryButtonTable( - 'inventory_items,inventory_item_data,inventory_item_lend_data', + 'inventory_items,inventory_item_data,inventory_item_borrow_data', $gCurrentUser->isAdministratorInventory(), ['uuid' => $item['ini_uuid']] ); @@ -905,29 +980,29 @@ public function prepareData(string $mode = 'html') : array } if ($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) { - if (($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) && !$item['ini_former']) { + if (($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) && !$this->itemsData->isRetired()) { // Add edit action $rowValues['actions'][] = array( - 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])), + 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $this->itemsData->isRetired())), 'icon' => 'bi bi-pencil-square', 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_EDIT') ); - // Add lend action - if (!$item['ini_former'] && !$gSettingsManager->GetBool('inventory_items_disable_lending')) { + // Add borrow action + if (!$this->itemsData->isRetired() && !$gSettingsManager->GetBool('inventory_items_disable_borrowing')) { // check if the item is in inventory - if ($this->itemsData->getValue('IN_INVENTORY', 'database') === '1') { - $itemLended = false; + if (!$this->itemsData->isBorrowed()) { + $item_borrowed = false; $icon ='bi bi-box-arrow-right'; - $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_LEND'); + $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_BORROW'); } else { - $itemLended = true; + $item_borrowed = true; $icon = 'bi bi-box-arrow-in-left'; $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_RETURN'); } $rowValues['actions'][] = array( - 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit_lend', 'item_uuid' => $item['ini_uuid'], 'item_lended' => $itemLended)), + 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit_borrow', 'item_uuid' => $item['ini_uuid'], 'item_borrowed' => $item_borrowed)), 'icon' => $icon, 'tooltip' =>$tooltip ); @@ -941,20 +1016,20 @@ public function prepareData(string $mode = 'html') : array ); } - if ($item['ini_former']) { - $dataMessage = ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) ? $gL10n->get('SYS_INVENTORY_KEEPER_ITEM_UNDO_FORMER_DESC', array('SYS_INVENTORY_ITEM_UNDO_FORMER_CONFIRM')) : $gL10n->get('SYS_INVENTORY_ITEM_UNDO_FORMER_CONFIRM'); - // Add undo former action + if ($this->itemsData->isRetired()) { + $dataMessage = ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) ? $gL10n->get('SYS_INVENTORY_KEEPER_ITEM_REINSTATE_DESC', array('SYS_INVENTORY_ITEM_REINSTATE_CONFIRM')) : $gL10n->get('SYS_INVENTORY_ITEM_REINSTATE_CONFIRM'); + // Add reinstate action $rowValues['actions'][] = array( - 'dataHref' => 'callUrlHideElement(\'adm_inventory_item_' . $item['ini_uuid'] . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_undo_former', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')', + 'dataHref' => 'callUrlHideElement(\'adm_inventory_item_' . $item['ini_uuid'] . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_reinstate', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $this->itemsData->isRetired())) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')', 'dataMessage' => $dataMessage, 'icon' => 'bi bi-eye', - 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_UNDO_FORMER') + 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_REINSTATE') ); } if ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) { - if (!$item['ini_former']) { - // Add make former action + if (!$this->itemsData->isRetired()) { + // Addretire action $rowValues['actions'][] = array( 'popup' => true, 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_keeper_explain_msg', 'item_uuid' => $item['ini_uuid'])), @@ -964,10 +1039,10 @@ public function prepareData(string $mode = 'html') : array } } else { - // Add delete/make former action + // Add delete/retire action $rowValues['actions'][] = array( 'popup' => true, - 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_explain_msg', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])), + 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_explain_msg', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $this->itemsData->isRetired())), 'icon' => 'bi bi-trash', 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_DELETE') ); @@ -1068,8 +1143,8 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter $columnAlign[] = 'end'; // first column alignment $headers = array(); $columnNumber = 1; - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrow fields + $borrowFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); // create array with all column heading values $profileItemFields = array('ITEMNAME'); @@ -1084,7 +1159,7 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter foreach ($itemsData->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); - if (!in_array($infNameIntern, $profileItemFields, true) || ($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames))) { + if (!in_array($infNameIntern, $profileItemFields, true) || ($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowFieldNames))) { continue; } @@ -1116,7 +1191,7 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter } // Append the admin action column - if ($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) { + if ($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$itemsData->getValue('KEEPER', 'database'))) { $columnAlign[] = 'end'; $headers[] = ' '; } @@ -1133,13 +1208,13 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter $itemsData->readItemData($item['ini_uuid']); $rowValues = array(); $rowValues['item_uuid'] = $item['ini_uuid']; - $strikethrough = $item['ini_former']; + $strikethrough = $itemsData->isRetired(); $columnNumber = 1; foreach ($itemsData->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); - if (!in_array($infNameIntern, $profileItemFields, true) || ($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames))) { + if (!in_array($infNameIntern, $profileItemFields, true) || ($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowFieldNames))) { continue; } @@ -1180,7 +1255,7 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter if ($infType === 'CHECKBOX') { $content = ($content != 1) ? 0 : 1; $content = $itemsData->getHtmlValue($infNameIntern, $content); - } elseif (in_array($infType, ['DATE', 'DROPDOWN'])) { + } elseif (in_array($infType, ['DATE', 'DROPDOWN', 'DROPDOWN_MULTISELECT'])) { $content = $itemsData->getHtmlValue($infNameIntern, $content); } elseif ($infType === 'RADIO_BUTTON') { $content = $itemsData->getHtmlValue($infNameIntern, $content); @@ -1194,7 +1269,7 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter // Append admin action column $historyButton = ChangelogService::displayHistoryButtonTable( - 'inventory_items,inventory_item_data,inventory_item_lend_data', + 'inventory_items,inventory_item_data,inventory_item_borrow_data', $gCurrentUser->isAdministratorInventory(), ['uuid' => $item['ini_uuid']] ); @@ -1203,30 +1278,30 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter $rowValues['actions'][] = $historyButton; } - if ($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) { - if ($gCurrentUser->isAdministratorInventory() || ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database')) && !$item['ini_former'])) { + if ($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$itemsData->getValue('KEEPER', 'database'))) { + if (($gCurrentUser->isAdministratorInventory() || $this->isKeeperAuthorizedToEdit((int)$itemsData->getValue('KEEPER', 'database'))) && !$itemsData->isRetired()) { // Add edit action $rowValues['actions'][] = array( - 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])), + 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $itemsData->isRetired())), 'icon' => 'bi bi-pencil-square', 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_EDIT') ); // Add lend action - if (!$item['ini_former'] && !$gSettingsManager->GetBool('inventory_items_disable_lending')) { + if (!$itemsData->isRetired() && !$gSettingsManager->GetBool('inventory_items_disable_borrowing')) { // check if the item is in inventory - if ($this->itemsData->getValue('IN_INVENTORY', 'database') === '1') { - $itemLended = false; + if (!$itemsData->isBorrowed()) { + $item_borrowed = false; $icon ='bi bi-box-arrow-right'; - $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_LEND'); + $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_BORROW'); } else { - $itemLended = true; + $item_borrowed = true; $icon = 'bi bi-box-arrow-in-left'; $tooltip = $gL10n->get('SYS_INVENTORY_ITEM_RETURN'); } $rowValues['actions'][] = array( - 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit_lend', 'item_uuid' => $item['ini_uuid'], 'item_lended' => $itemLended)), + 'url' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php',array('mode' => 'item_edit_borrow', 'item_uuid' => $item['ini_uuid'], 'item_borrowed' => $item_borrowed)), 'icon' => $icon, 'tooltip' =>$tooltip ); @@ -1240,20 +1315,20 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter ); } - if ($item['ini_former']) { - $dataMessage = ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) ? $gL10n->get('SYS_INVENTORY_KEEPER_ITEM_UNDO_FORMER_DESC', array('SYS_INVENTORY_ITEM_UNDO_FORMER_CONFIRM')) : $gL10n->get('SYS_INVENTORY_ITEM_UNDO_FORMER_CONFIRM'); - // Add undo former action + if ($itemsData->isRetired()) { + $dataMessage = ($this->isKeeperAuthorizedToEdit((int)$itemsData->getValue('KEEPER', 'database'))) ? $gL10n->get('SYS_INVENTORY_KEEPER_ITEM_REINSTATE_DESC', array('SYS_INVENTORY_ITEM_REINSTATE_CONFIRM')) : $gL10n->get('SYS_INVENTORY_ITEM_REINSTATE_CONFIRM'); + // Add reinstate action $rowValues['actions'][] = array( - 'dataHref' => 'callUrlHideElement(\'adm_inventory_item_' . $item['ini_uuid'] . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_undo_former', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')', + 'dataHref' => 'callUrlHideElement(\'adm_inventory_item_' . $item['ini_uuid'] . '\', \'' . SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_reinstate', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $itemsData->isRetired())) . '\', \'' . $gCurrentSession->getCsrfToken() . '\')', 'dataMessage' => $dataMessage, 'icon' => 'bi bi-eye', - 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_UNDO_FORMER') + 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_REINSTATE') ); } - if ($this->isKeeperAuthorizedToEdit((int)$this->itemsData->getValue('KEEPER', 'database'))) { - if (!$item['ini_former']) { - // Add make former action + if ($this->isKeeperAuthorizedToEdit((int)$itemsData->getValue('KEEPER', 'database'))) { + if (!$itemsData->isRetired()) { + // Add retire action $rowValues['actions'][] = array( 'popup' => true, 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_keeper_explain_msg', 'item_uuid' => $item['ini_uuid'])), @@ -1263,10 +1338,10 @@ public function prepareDataProfile(ItemsData $itemsData, string $itemFieldFilter } } else { - // Add delete/make former action + // Add delete/retire action $rowValues['actions'][] = array( 'popup' => true, - 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_explain_msg', 'item_uuid' => $item['ini_uuid'], 'item_former' => $item['ini_former'])), + 'dataHref' => SecurityUtils::encodeUrl(ADMIDIO_URL . FOLDER_MODULES . '/inventory.php', array('mode' => 'item_delete_explain_msg', 'item_uuid' => $item['ini_uuid'], 'item_retired' => $itemsData->isRetired())), 'icon' => 'bi bi-trash', 'tooltip' => $gL10n->get('SYS_INVENTORY_ITEM_DELETE') ); diff --git a/src/UI/Presenter/PreferencesPresenter.php b/src/UI/Presenter/PreferencesPresenter.php index 4411b78b99..44542e43aa 100644 --- a/src/UI/Presenter/PreferencesPresenter.php +++ b/src/UI/Presenter/PreferencesPresenter.php @@ -427,7 +427,7 @@ public function createChangelogForm(): string array( 'title' => $gL10n->get('SYS_HEADER_CONTENT_MODULES'), 'id' => 'content_modules', - 'tables' => array('files', 'folders', 'photos', 'announcements', 'events', 'rooms', 'forum_topics', 'forum_posts', 'inventory_fields', 'inventory_field_select_options', 'inventory_items', 'inventory_item_data', 'inventory_item_lend_data', 'links', 'others') + 'tables' => array('files', 'folders', 'photos', 'announcements', 'events', 'rooms', 'forum_topics', 'forum_posts', 'inventory_fields', 'inventory_field_select_options', 'inventory_items', 'inventory_item_data', 'inventory_item_borrow_data', 'links', 'others') ), array( 'title' => $gL10n->get('SYS_HEADER_PREFERENCES'), @@ -782,8 +782,8 @@ public function createInventoryForm(): string { global $gL10n, $gSettingsManager, $gDb, $gCurrentOrgId, $gCurrentSession, $gCurrentUser; $formValues = $gSettingsManager->getAll(); - //array with the internal field names of the lend fields - $lendFieldNames = array('IN_INVENTORY', 'LAST_RECEIVER', 'RECEIVED_ON', 'RECEIVED_BACK_ON'); + //array with the internal field names of the borrowing fields + $borrowingFieldNames = array('LAST_RECEIVER', 'BORROW_DATE', 'RETURN_DATE'); $formInventory = new FormPresenter( 'adm_preferences_form_inventory', @@ -823,11 +823,40 @@ public function createInventoryForm(): string ); // general settings - $formInventory->addSeperator( - 'inventory_seperator_general_settings', + $formInventory->addSeparator( + 'inventory_separator_general_settings', $gL10n->get('SYS_COMMON') ); + $formInventory->addCheckbox( + 'inventory_item_picture_enabled', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURE_ENABLED'), + (bool) $formValues['inventory_item_picture_enabled'], + array('helpTextId' => 'SYS_INVENTORY_ITEM_PICTURE_ENABLED_DESC') + ); + + $selectBoxEntries = array('0' => $gL10n->get('SYS_DATABASE'), '1' => $gL10n->get('SYS_FOLDER')); + $formInventory->addSelectBox( + 'inventory_item_picture_storage', + $gL10n->get('SYS_INVENTORY_ITEM_PICTURES_LOCATION'), + $selectBoxEntries, + array('defaultValue' => $formValues['inventory_item_picture_storage'], 'showContextDependentFirstEntry' => false, 'helpTextId' => 'SYS_INVENTORY_ITEM_PICTURES_LOCATION_DESC') + ); + + $formInventory->addInput( + 'inventory_item_picture_width', + $gL10n->get('SYS_MAX_PHOTO_SIZE_WIDTH'), + $formValues['inventory_item_picture_width'], + array('type' => 'number', 'minNumber' => 1, 'maxNumber' => 9999, 'step' => 1) + ); + + $formInventory->addInput( + 'inventory_item_picture_height', + $gL10n->get('SYS_MAX_PHOTO_SIZE_HEIGHT'), + $formValues['inventory_item_picture_height'], + array('type' => 'number', 'minNumber' => 1, 'maxNumber' => 9999, 'step' => 1, 'helpTextId' => array('SYS_MAX_PHOTO_SIZE_DESC', array(130, 170))) + ); + $formInventory->addCheckbox( 'inventory_show_obsolete_select_field_options', $gL10n->get('SYS_SHOW_OBSOLETE_SELECT_FIELD_OPTIONS'), @@ -836,10 +865,10 @@ public function createInventoryForm(): string ); $formInventory->addCheckbox( - 'inventory_items_disable_lending', - $gL10n->get('SYS_INVENTORY_ITEMS_DISABLE_LENDING'), - (bool) $formValues['inventory_items_disable_lending'], - array('helpTextId' => 'SYS_INVENTORY_ITEMS_DISABLE_LENDING_DESC') + 'inventory_items_disable_borrowing', + $gL10n->get('SYS_INVENTORY_ITEMS_DISABLE_BORROWING'), + (bool) $formValues['inventory_items_disable_borrowing'], + array('helpTextId' => 'SYS_INVENTORY_ITEMS_DISABLE_BORROWING_DESC') ); $formInventory->addCheckbox( @@ -862,8 +891,8 @@ public function createInventoryForm(): string $selectBoxEntries = array(); foreach ($items->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); - if($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames)) { - continue; // skip lending fields if lending is disabled + if($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowingFieldNames)) { + continue; // skip borrowing fields if borrowing is disabled } $selectBoxEntries[$infNameIntern] = $itemField->getValue('inf_name'); } @@ -906,8 +935,8 @@ public function createInventoryForm(): string ); // profile view settings - $formInventory->addSeperator( - 'inventory_seperator_profile_view_settings', + $formInventory->addSeparator( + 'inventory_separator_profile_view_settings', $gL10n->get('SYS_INVENTORY_PROFILE_VIEW') ); @@ -921,7 +950,7 @@ public function createInventoryForm(): string $selectBoxEntries = array(); foreach ($items->getItemFields() as $itemField) { $infNameIntern = $itemField->getValue('inf_name_intern'); - if ($itemField->getValue('inf_name_intern') == 'ITEMNAME' || ($gSettingsManager->GetBool('inventory_items_disable_lending') && in_array($infNameIntern, $lendFieldNames))) { + if ($itemField->getValue('inf_name_intern') == 'ITEMNAME' || ($gSettingsManager->GetBool('inventory_items_disable_borrowing') && in_array($infNameIntern, $borrowingFieldNames))) { continue; } $selectBoxEntries[$infNameIntern] = $itemField->getValue('inf_name'); @@ -935,8 +964,8 @@ public function createInventoryForm(): string ); // export settings - $formInventory->addSeperator( - 'inventory_seperator_export_settings', + $formInventory->addSeparator( + 'inventory_separator_export_settings', $gL10n->get('SYS_INVENTORY_EXPORT') ); diff --git a/src/UI/Presenter/ProfileFieldsPresenter.php b/src/UI/Presenter/ProfileFieldsPresenter.php index d998d3cb62..86e3215ec1 100644 --- a/src/UI/Presenter/ProfileFieldsPresenter.php +++ b/src/UI/Presenter/ProfileFieldsPresenter.php @@ -176,7 +176,7 @@ public function createEditForm(string $profileFieldUUID = '') $optionValueList = $options->getAllOptions($gSettingsManager->getBool('profile_show_obsolete_select_field_options')); if (empty($optionValueList)) { $optionValueList = array( - 0 => array('id' => 1, 'value' => '', 'sequence' => 0, 'obsolete' => false) + 0 => array('id' => 1, 'value' => '', 'system' => false, 'sequence' => 0, 'obsolete' => false) ); } $form->addOptionEditor( diff --git a/system/bootstrap/constants.php b/system/bootstrap/constants.php index 53faa0a264..93aaf57023 100755 --- a/system/bootstrap/constants.php +++ b/system/bootstrap/constants.php @@ -160,7 +160,7 @@ const TBL_INVENTORY_FIELDS = TABLE_PREFIX . '_inventory_fields'; const TBL_INVENTORY_FIELD_OPTIONS = TABLE_PREFIX . '_inventory_field_select_options'; const TBL_INVENTORY_ITEMS = TABLE_PREFIX . '_inventory_items'; -const TBL_INVENTORY_ITEM_LEND_DATA = TABLE_PREFIX . '_inventory_item_lend_data'; +const TBL_INVENTORY_ITEM_BORROW_DATA = TABLE_PREFIX . '_inventory_item_borrow_data'; // ##################### // ### OTHER STUFF ### diff --git a/system/js/common_functions.js b/system/js/common_functions.js index a03d913ad3..d0cdd78eb0 100644 --- a/system/js/common_functions.js +++ b/system/js/common_functions.js @@ -127,6 +127,8 @@ function callUrlHideElement(elementId, url, csrfToken, callback) { $(entryDeleted).fadeOut("slow", callbackFutureRoles); } else if (callback === "callbackProfilePhoto") { callbackProfilePhoto(); + } else if (callback === "callbackItemPicture") { + callbackItemPicture(); } else { $(entryDeleted).fadeOut("slow"); } @@ -142,16 +144,22 @@ function callUrlHideElement(elementId, url, csrfToken, callback) { $(entryDeleted).fadeOut("slow", callbackFutureRoles); } else if (callback === 'callbackProfilePhoto') { callbackProfilePhoto(); + } else if (callback === "callbackItemPicture") { + callbackItemPicture(); } else { $(entryDeleted).fadeOut("slow"); } } - var tbodyElement = entryDeleted.closest("tbody"); - if (isTbodyEmpty(tbodyElement)) { - $(tbodyElement).fadeOut("slow"); - var tbodyElement2 = tbodyElement.previousElementSibling; - $(tbodyElement2).fadeOut("slow"); + if (entryDeleted) { + var tbodyElement = entryDeleted.closest("tbody"); + if (isTbodyEmpty(tbodyElement)) { + $(tbodyElement).fadeOut("slow"); + var tbodyElement2 = tbodyElement.previousElementSibling; + if (isTbodyEmpty(tbodyElement2)) { + $(tbodyElement2).fadeOut("slow"); + } + } } } else { // entry could not be deleted, then show content of data or a common error message @@ -193,10 +201,13 @@ function callUrlHideElements(elementPrefix, elementIds, url, csrfToken) { $(entry).fadeOut("slow"); // then check if its is now empty - var tb = entry.closest("tbody"); - if (tb && tb.children.length === 0) { - $(tb).fadeOut("slow"); - $(tb.previousElementSibling).fadeOut("slow"); + var tbodyElement = entry.closest("tbody"); + if (isTbodyEmpty(tbodyElement)) { + $(tbodyElement).fadeOut("slow"); + var tbodyElement2 = tbodyElement.previousElementSibling; + if (isTbodyEmpty(tbodyElement2)) { + $(tbodyElement2).fadeOut("slow"); + } } } diff --git a/themes/simple/css/admidio.css b/themes/simple/css/admidio.css index c6223bc17b..af447e5f0f 100644 --- a/themes/simple/css/admidio.css +++ b/themes/simple/css/admidio.css @@ -672,7 +672,7 @@ label { margin-left: 0.5rem; } -#adm_profile_photo { +#adm_profile_photo, #adm_inventory_item_picture { max-width: 300px; width: 100% !important; object-fit: cover !important; diff --git a/themes/simple/images/inventory-item-picture.png b/themes/simple/images/inventory-item-picture.png new file mode 100644 index 0000000000..e3eb804cc2 Binary files /dev/null and b/themes/simple/images/inventory-item-picture.png differ diff --git a/themes/simple/templates/modules/inventory.item.edit.lend.tpl b/themes/simple/templates/modules/inventory.item.edit.borrow.tpl similarity index 100% rename from themes/simple/templates/modules/inventory.item.edit.lend.tpl rename to themes/simple/templates/modules/inventory.item.edit.borrow.tpl diff --git a/themes/simple/templates/modules/inventory.item.edit.tpl b/themes/simple/templates/modules/inventory.item.edit.tpl index c4699ff60f..bd3a6b231a 100644 --- a/themes/simple/templates/modules/inventory.item.edit.tpl +++ b/themes/simple/templates/modules/inventory.item.edit.tpl @@ -21,23 +21,45 @@
{$l10n->get('SYS_PROPERTIES')}
- {foreach $elements as $key => $itemField} - {if {string_contains haystack=$key needle="INF-"} && $key != "INF-ITEMNAME"} - {if $itemField.type == 'checkbox'} - {include 'sys-template-parts/form.checkbox.tpl' data=$itemField} - {elseif $itemField.type == 'multiline'} - {include 'sys-template-parts/form.multiline.tpl' data=$itemField} - {elseif $itemField.type == 'radio'} - {include 'sys-template-parts/form.radio.tpl' data=$itemField} - {elseif $itemField.type == 'select'} - {include 'sys-template-parts/form.select.tpl' data=$itemField} - {else} - {if !{string_contains haystack=$key needle="_time"}} - {include 'sys-template-parts/form.input.tpl' data=$itemField} + {if isset($urlItemPicture)} +
+
+ {/if} + {foreach $elements as $key => $itemField} + {if {string_contains haystack=$key needle="INF-"} && $key != "INF-ITEMNAME"} + {if $itemField.type == 'checkbox'} + {include 'sys-template-parts/form.checkbox.tpl' data=$itemField} + {elseif $itemField.type == 'multiline'} + {include 'sys-template-parts/form.multiline.tpl' data=$itemField} + {elseif $itemField.type == 'radio'} + {include 'sys-template-parts/form.radio.tpl' data=$itemField} + {elseif $itemField.type == 'select'} + {include 'sys-template-parts/form.select.tpl' data=$itemField} + {else} + {if !{string_contains haystack=$key needle="_time"}} + {include 'sys-template-parts/form.input.tpl' data=$itemField} + {/if} + {/if} + {/if} + {/foreach} + {if isset($urlItemPicture)} +
+
+ {$l10n->get('SYS_INVENTORY_ITEM_PICTURE_CURRENT')} + {if isset($urlItemPictureUpload)} + {/if} - {/if} - {/if} - {/foreach} +
+
+ {/if}
{if {array_key_exists array=$elements key='item_copy_number'}} diff --git a/themes/simple/templates/modules/inventory.list.export.tpl b/themes/simple/templates/modules/inventory.list.export.tpl new file mode 100644 index 0000000000..795daea963 --- /dev/null +++ b/themes/simple/templates/modules/inventory.list.export.tpl @@ -0,0 +1,18 @@ + + + + {foreach $headers as $key => $header} + + {/foreach} + + + + {foreach $rows as $row} + + {foreach $row.data as $key => $cell} + + {/foreach} + + {/foreach} + +
{$header}
{$cell}
\ No newline at end of file diff --git a/themes/simple/templates/modules/inventory.list.tpl b/themes/simple/templates/modules/inventory.list.tpl index 24a3109f36..2980f245dc 100644 --- a/themes/simple/templates/modules/inventory.list.tpl +++ b/themes/simple/templates/modules/inventory.list.tpl @@ -1,18 +1,20 @@
-
- -
+ {if !$print} +
+ +
+ {/if} diff --git a/themes/simple/templates/modules/inventory.new-item-picture.tpl b/themes/simple/templates/modules/inventory.new-item-picture.tpl new file mode 100644 index 0000000000..8b628f5dcd --- /dev/null +++ b/themes/simple/templates/modules/inventory.new-item-picture.tpl @@ -0,0 +1,9 @@ + + {include 'sys-template-parts/form.custom-content.tpl' data=$elements['item_picture_current']} + {include 'sys-template-parts/form.custom-content.tpl' data=$elements['item_picture_new']} + + + {include 'sys-template-parts/form.button.tpl' data=$elements['adm_button_save']} + diff --git a/themes/simple/templates/modules/inventory.new-item-picture.upload.tpl b/themes/simple/templates/modules/inventory.new-item-picture.upload.tpl new file mode 100644 index 0000000000..ddc876b734 --- /dev/null +++ b/themes/simple/templates/modules/inventory.new-item-picture.upload.tpl @@ -0,0 +1,11 @@ + +
{$l10n->get('SYS_REQUIRED_INPUT')}
+ + {include 'sys-template-parts/form.input.tpl' data=$elements['adm_csrf_token']} + {include 'sys-template-parts/form.custom-content.tpl' data=$elements['item_picture_current']} + {include 'sys-template-parts/form.file.tpl' data=$elements['item_picture_upload_file']} + + {include 'sys-template-parts/form.button.tpl' data=$elements['adm_button_upload']} + diff --git a/themes/simple/templates/preferences/preferences.inventory.tpl b/themes/simple/templates/preferences/preferences.inventory.tpl index 462aca6e64..716445298b 100644 --- a/themes/simple/templates/preferences/preferences.inventory.tpl +++ b/themes/simple/templates/preferences/preferences.inventory.tpl @@ -1,10 +1,34 @@ @@ -34,9 +82,13 @@ {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_module_enabled']} {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_items_per_page']} {include 'sys-template-parts/form.input.tpl' data=$elements['inventory_field_history_days']} - {include 'sys-template-parts/form.seperator.tpl' data=$elements['inventory_seperator_general_settings']} + {include 'sys-template-parts/form.separator.tpl' data=$elements['inventory_separator_general_settings']} + {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_item_picture_enabled']} + {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_item_picture_storage']} + {include 'sys-template-parts/form.input.tpl' data=$elements['inventory_item_picture_width']} + {include 'sys-template-parts/form.input.tpl' data=$elements['inventory_item_picture_height']} {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_show_obsolete_select_field_options']} - {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_items_disable_lending']} + {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_items_disable_borrowing']} {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_system_field_names_editable']} {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_allow_keeper_edit']} {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_allowed_keeper_edit_fields']} @@ -44,10 +96,10 @@ {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_allow_negative_numbers']} {include 'sys-template-parts/form.input.tpl' data=$elements['inventory_decimal_places']} {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_field_date_time_format']} - {include 'sys-template-parts/form.seperator.tpl' data=$elements['inventory_seperator_profile_view_settings']} + {include 'sys-template-parts/form.separator.tpl' data=$elements['inventory_separator_profile_view_settings']} {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_profile_view_enabled']} {include 'sys-template-parts/form.select.tpl' data=$elements['inventory_profile_view']} - {include 'sys-template-parts/form.seperator.tpl' data=$elements['inventory_seperator_export_settings']} + {include 'sys-template-parts/form.separator.tpl' data=$elements['inventory_separator_export_settings']} {include 'sys-template-parts/form.input.tpl' data=$elements['inventory_export_filename']} {include 'sys-template-parts/form.checkbox.tpl' data=$elements['inventory_add_date']} {include 'sys-template-parts/form.button.tpl' data=$elements['adm_button_save_inventory']} diff --git a/themes/simple/templates/sys-template-parts/form.option-editor.tpl b/themes/simple/templates/sys-template-parts/form.option-editor.tpl index 817d929f9b..36d1be96ec 100644 --- a/themes/simple/templates/sys-template-parts/form.option-editor.tpl +++ b/themes/simple/templates/sys-template-parts/form.option-editor.tpl @@ -36,8 +36,26 @@ + {if isset($data.values.notSortable) && count($data.values.notSortable) > 0} + + {foreach $data.values.notSortable as $option} + + + + + + + {/foreach} + + {/if} - {foreach $data.values as $option} + {foreach $data.values.sortable as $option}
 
+ +
diff --git a/themes/simple/templates/sys-template-parts/form.seperator.tpl b/themes/simple/templates/sys-template-parts/form.separator.tpl similarity index 90% rename from themes/simple/templates/sys-template-parts/form.seperator.tpl rename to themes/simple/templates/sys-template-parts/form.separator.tpl index 58288032e6..e2d9b648d1 100644 --- a/themes/simple/templates/sys-template-parts/form.seperator.tpl +++ b/themes/simple/templates/sys-template-parts/form.separator.tpl @@ -1,4 +1,4 @@ -