diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 47c025b0c0a..de4f2bc367c 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -2954,18 +2954,25 @@ abstract class CommonObject // Special cas if ($this->table_element == 'product' && $newsuffix == '_private') $newsuffix = ''; - + if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) { + $fieldusermod = "fk_user_mod"; + } elseif ($this->table_element == 'ecm_files') { + $fieldusermod = "fk_user_m"; + } else { + $fieldusermod = "fk_user_modif"; + } $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL"); - $sql .= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment')) ? "fk_user_mod" : "fk_user_modif")." = ".$user->id; + $sql .= " ,".$fieldusermod." = ".$user->id; $sql .= " WHERE rowid =".$this->id; dol_syslog(get_class($this)."::update_note", LOG_DEBUG); - if ($this->db->query($sql)) - { - if ($suffix == '_public') $this->note_public = $note; - elseif ($suffix == '_private') $this->note_private = $note; - else { + if ($this->db->query($sql)) { + if ($suffix == '_public') { + $this->note_public = $note; + } elseif ($suffix == '_private') { + $this->note_private = $note; + } else { $this->note = $note; // deprecated $this->note_private = $note; } diff --git a/htdocs/core/lib/ecm.lib.php b/htdocs/core/lib/ecm.lib.php index 1f22e464358..2c1edc5e675 100644 --- a/htdocs/core/lib/ecm.lib.php +++ b/htdocs/core/lib/ecm.lib.php @@ -111,6 +111,16 @@ function ecm_file_prepare_head($object) $head[$h][2] = 'card'; $h++; + // Notes + $head[$h][0] = DOL_URL_ROOT.'/ecm/file_note.php?section='.$object->section_id.'&urlfile='.urlencode($object->label); + $head[$h][1] = $langs->trans("Notes"); + $nbNote = 0; + if (!empty($object->note_private)) $nbNote++; + if (!empty($object->note_public)) $nbNote++; + if ($nbNote > 0) $head[$h][1] .= ''.$nbNote.''; + $head[$h][2] = 'note'; + $h++; + return $head; } diff --git a/htdocs/core/tpl/notes.tpl.php b/htdocs/core/tpl/notes.tpl.php index 283f9eeb873..ec098be9db7 100644 --- a/htdocs/core/tpl/notes.tpl.php +++ b/htdocs/core/tpl/notes.tpl.php @@ -79,11 +79,16 @@ if ($module == 'propal') { $permission = $user->rights->expedition->creer; } elseif ($module == 'product') { $permission = $user->rights->produit->creer; +} elseif ($module == 'ecmfiles') { + $permission = $user->rights->ecm->setup; } //else dol_print_error('','Bad value '.$module.' for param module'); -if (!empty($conf->fckeditor->enabled) && !empty($conf->global->FCKEDITOR_ENABLE_SOCIETE)) $typeofdata = 'ckeditor:dolibarr_notes:100%:200::1:12:95%:0'; // Rem: This var is for all notes, not only thirdparties note. -else $typeofdata = 'textarea:12:95%'; +if (!empty($conf->fckeditor->enabled) && !empty($conf->global->FCKEDITOR_ENABLE_SOCIETE)) { + $typeofdata = 'ckeditor:dolibarr_notes:100%:200::1:12:95%:0'; // Rem: This var is for all notes, not only thirdparties note. +} else { + $typeofdata = 'textarea:12:95%'; +} print ''."\n"; print '
'."\n"; diff --git a/htdocs/ecm/class/ecmfiles.class.php b/htdocs/ecm/class/ecmfiles.class.php index c096bc27869..8701e6ed7d6 100644 --- a/htdocs/ecm/class/ecmfiles.class.php +++ b/htdocs/ecm/class/ecmfiles.class.php @@ -389,6 +389,8 @@ class EcmFiles extends CommonObject $sql .= " t.tms as date_m,"; $sql .= " t.fk_user_c,"; $sql .= " t.fk_user_m,"; + $sql .= ' t.note_private,'; + $sql .= ' t.note_public,'; $sql .= " t.acl,"; $sql .= " t.src_object_type,"; $sql .= " t.src_object_id"; @@ -450,6 +452,8 @@ class EcmFiles extends CommonObject $this->date_m = $this->db->jdate($obj->date_m); $this->fk_user_c = $obj->fk_user_c; $this->fk_user_m = $obj->fk_user_m; + $this->note_private = $obj->note_private; + $this->note_public = $obj->note_public; $this->acl = $obj->acl; $this->src_object_type = $obj->src_object_type; $this->src_object_id = $obj->src_object_id; diff --git a/htdocs/ecm/file_card.php b/htdocs/ecm/file_card.php index 496afbf8568..d89e94861c0 100644 --- a/htdocs/ecm/file_card.php +++ b/htdocs/ecm/file_card.php @@ -36,14 +36,15 @@ $action = GETPOST('action', 'aZ09'); $cancel = GETPOST('cancel', 'alpha'); $backtopage = GETPOST('backtopage', 'alpha'); -if (!$user->rights->ecm->setup) accessforbidden(); +if (!$user->rights->ecm->setup) { + accessforbidden(); +} // Get parameters $socid = GETPOST("socid", "int"); // Security check -if ($user->socid > 0) -{ +if ($user->socid > 0) { $action = ''; $socid = $user->socid; } @@ -52,22 +53,26 @@ $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; $sortfield = GETPOST("sortfield", 'alpha'); $sortorder = GETPOST("sortorder", 'alpha'); $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); -if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +if (empty($page) || $page == -1) { + $page = 0; +} // If $page is not defined, or '' or -1 $offset = $limit * $page; $pageprev = $page - 1; $pagenext = $page + 1; -if (!$sortorder) $sortorder = "ASC"; -if (!$sortfield) $sortfield = "label"; +if (!$sortorder) { + $sortorder = "ASC"; +} +if (!$sortfield) { + $sortfield = "label"; +} $section = GETPOST("section", 'alpha'); -if (!$section) -{ +if (!$section) { dol_print_error('', 'Error, section parameter missing'); exit; } $urlfile = GETPOST("urlfile"); -if (!$urlfile) -{ +if (!$urlfile) { dol_print_error('', "ErrorParamNotDefined"); exit; } @@ -75,8 +80,7 @@ if (!$urlfile) // Load ecm object $ecmdir = new EcmDirectory($db); $result = $ecmdir->fetch(GETPOST("section", 'alpha')); -if (!$result > 0) -{ +if (!$result > 0) { dol_print_error($db, $ecmdir->error); exit; } @@ -85,13 +89,9 @@ $upload_dir = $conf->ecm->dir_output.'/'.$relativepath; $fullpath = $conf->ecm->dir_output.'/'.$relativepath.$urlfile; -$file = new stdClass(); -$file->section_id = $ecmdir->id; -$file->label = $urlfile; - $relativetodocument = 'ecm/'.$relativepath; // $relativepath is relative to ECM dir, we need relative to document -$filepath = $relativepath.$file->label; -$filepathtodocument = $relativetodocument.$file->label; +$filepath = $relativepath.$urlfile; +$filepathtodocument = $relativetodocument.$urlfile; // Try to load object from index $object = new ECMFiles($db); @@ -100,8 +100,7 @@ $extrafields = new ExtraFields($db); $extrafields->fetch_name_optionals_label($object->table_element); $result = $object->fetch(0, '', $filepathtodocument); -if ($result < 0) -{ +if ($result < 0) { dol_print_error($db, $object->error, $object->errors); exit; } @@ -112,11 +111,9 @@ if ($result < 0) * Actions */ -if ($cancel) -{ +if ($cancel) { $action = ''; - if ($backtopage) - { + if ($backtopage) { header("Location: ".$backtopage); exit; } else { @@ -126,8 +123,7 @@ if ($cancel) } // Rename file -if ($action == 'update') -{ +if ($action == 'update') { $error = 0; $oldlabel = GETPOST('urlfile', 'alpha'); @@ -154,11 +150,9 @@ if ($action == 'update') // Now we update index of file $db->begin(); //print $oldfile.' - '.$newfile; - if ($newlabel != $oldlabel) - { + if ($newlabel != $oldlabel) { $result = dol_move($oldfile, $newfileformove); // This include update of database - if (!$result) - { + if (!$result) { $langs->load('errors'); setEventMessages($langs->trans('ErrorFailToRenameFile', $oldfile, $newfile), null, 'errors'); $error++; @@ -166,27 +160,25 @@ if ($action == 'update') // Reload object after the move $result = $object->fetch(0, '', $newdirrelativetodocument.$newlabel); - if ($result < 0) - { + if ($result < 0) { dol_print_error($db, $object->error, $object->errors); exit; } } - if (!$error) - { - if ($shareenabled) - { + if (!$error) { + if ($shareenabled) { require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; $object->share = getRandomPassword(true); } else { $object->share = ''; } - if ($object->id > 0) - { + if ($object->id > 0) { $ret = $extrafields->setOptionalsFromPost(null, $object); - if ($ret < 0) $error++; + if ($ret < 0) { + $error++; + } if (!$error) { // Actions on extra fields $result = $object->insertExtraFields(); @@ -197,8 +189,7 @@ if ($action == 'update') } // Call update to set the share key $result = $object->update($user); - if ($result < 0) - { + if ($result < 0) { setEventMessages($object->error, $object->errors, 'warnings'); } } else { @@ -212,15 +203,13 @@ if ($action == 'update') $object->description = ''; // indexed content $object->keyword = ''; // keyword content $result = $object->create($user); - if ($result < 0) - { + if ($result < 0) { setEventMessages($object->error, $object->errors, 'warnings'); } } } - if (!$error) - { + if (!$error) { $db->commit(); $urlfile = $newlabel; @@ -246,10 +235,11 @@ $form = new Form($db); llxHeader(); -$head = ecm_file_prepare_head($file); +$object->section_id = $ecmdir->id; +$object->label = $urlfile; +$head = ecm_file_prepare_head($object); -if ($action == 'edit') -{ +if ($action == 'edit') { print '
'; print ''; print ''; @@ -267,12 +257,10 @@ $tmpecmdir = new EcmDirectory($db); // Need to create a new one $tmpecmdir->fetch($ecmdir->id); $result = 1; $i = 0; -while ($tmpecmdir && $result > 0) -{ +while ($tmpecmdir && $result > 0) { $tmpecmdir->ref = $tmpecmdir->label; $s = $tmpecmdir->getNomUrl(1).$s; - if ($tmpecmdir->fk_parent) - { + if ($tmpecmdir->fk_parent) { $s = ' -> '.$s; $result = $tmpecmdir->fetch($tmpecmdir->fk_parent); } else { @@ -284,8 +272,11 @@ while ($tmpecmdir && $result > 0) $urlfiletoshow = preg_replace('/\.noexe$/', '', $urlfile); $s = img_picto('', 'object_dir').' '.$langs->trans("ECMRoot").' -> '.$s.' -> '; -if ($action == 'edit') $s .= ''; -else $s .= $urlfiletoshow; +if ($action == 'edit') { + $s .= ''; +} else { + $s .= $urlfiletoshow; +} $linkback = ''; if ($backtopage) { @@ -317,8 +308,7 @@ print ''; print ''.$langs->trans("HashOfFileContent").''; $object = new EcmFiles($db); $object->fetch(0, '', $filepathtodocument); -if (!empty($object->label)) -{ +if (!empty($object->label)) { print $object->label; } else { print img_warning().' '.$langs->trans("FileNotYetIndexedInDatabase"); @@ -335,45 +325,63 @@ print ''.$langs->trans("DirectDownloadInternalLink").''; $modulepart = 'ecm'; $forcedownload = 1; $rellink = '/document.php?modulepart='.$modulepart; -if ($forcedownload) $rellink .= '&attachment=1'; -if (!empty($object->entity)) $rellink .= '&entity='.$object->entity; +if ($forcedownload) { + $rellink .= '&attachment=1'; +} +if (!empty($object->entity)) { + $rellink .= '&entity='.$object->entity; +} $rellink .= '&file='.urlencode($filepath); $fulllink = $urlwithroot.$rellink; print img_picto('', 'globe').' '; -if ($action != 'edit') print ''; -else print $fulllink; -if ($action != 'edit') print ' '.$langs->trans("Download").''; // No target here. +if ($action != 'edit') { + print ''; +} else { + print $fulllink; +} +if ($action != 'edit') { + print ' '.$langs->trans("Download").''; // No target here. +} print ''; // Link for direct external download print ''; -if ($action != 'edit') print $langs->trans("DirectDownloadLink"); -else print $langs->trans("FileSharedViaALink"); +if ($action != 'edit') { + print $langs->trans("DirectDownloadLink"); +} else { + print $langs->trans("FileSharedViaALink"); +} print ''; -if (!empty($object->share)) -{ - if ($action != 'edit') - { +if (!empty($object->share)) { + if ($action != 'edit') { $forcedownload = 0; $paramlink = ''; - if (!empty($object->share)) $paramlink .= ($paramlink ? '&' : '').'hashp='.$object->share; // Hash for public share - if ($forcedownload) $paramlink .= ($paramlink ? '&' : '').'attachment=1'; + if (!empty($object->share)) { + $paramlink .= ($paramlink ? '&' : '').'hashp='.$object->share; // Hash for public share + } + if ($forcedownload) { + $paramlink .= ($paramlink ? '&' : '').'attachment=1'; + } $fulllink = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : ''); //if (! empty($object->ref)) $fulllink.='&hashn='.$object->ref; // Hash of file path //elseif (! empty($object->label)) $fulllink.='&hashc='.$object->label; // Hash of file content print img_picto('', 'globe').' '; - if ($action != 'edit') print ''; - else print $fulllink; - if ($action != 'edit') print ' '.$langs->trans("Download").''; // No target here + if ($action != 'edit') { + print ''; + } else { + print $fulllink; + } + if ($action != 'edit') { + print ' '.$langs->trans("Download").''; // No target here + } } else { print 'share ? ' checked="checked"' : '').' /> '; } } else { - if ($action != 'edit') - { + if ($action != 'edit') { print ''.$langs->trans("FileNotShared").''; } else { print 'share ? ' checked="checked"' : '').' /> '; @@ -390,8 +398,7 @@ print ajax_autoselect('downloadlink'); print dol_get_fiche_end(); -if ($action == 'edit') -{ +if ($action == 'edit') { print '
'; print ''; print '     '; @@ -403,18 +410,15 @@ if ($action == 'edit') // Confirmation de la suppression d'une ligne categorie -if ($action == 'delete_file') -{ +if ($action == 'delete_file') { print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode($section), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile', $urlfile), 'confirm_deletefile', '', 1, 1); } -if ($action != 'edit') -{ +if ($action != 'edit') { // Actions buttons print '
'; - if ($user->rights->ecm->setup) - { + if ($user->rights->ecm->setup) { print ''.$langs->trans('Edit').''; } /* @@ -426,7 +430,7 @@ if ($action != 'edit') { print ''.$langs->trans('Delete').''; } - */ + */ print '
'; } diff --git a/htdocs/ecm/file_note.php b/htdocs/ecm/file_note.php new file mode 100644 index 00000000000..dba96710f60 --- /dev/null +++ b/htdocs/ecm/file_note.php @@ -0,0 +1,184 @@ + + * Copyright (C) 2004-2016 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2017 Ferran Marcet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ecm/file_note.php + * \ingroup ecm + * \brief Fiche de notes sur une ecm file + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/ecm.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; + +// Load translation files required by the page +$langs->loadLangs(array('ecm')); + +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$socid = GETPOST('socid', 'int'); +$action = GETPOST('action', 'aZ09'); + +if (!$user->rights->ecm->setup) { + accessforbidden(); +} + +// Get parameters +$socid = GETPOST("socid", "int"); +// Security check +if ($user->socid > 0) { + $action = ''; + $socid = $user->socid; +} + +$limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; +$sortfield = GETPOST("sortfield", 'alpha'); +$sortorder = GETPOST("sortorder", 'alpha'); +$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); +if (empty($page) || $page == -1) { + $page = 0; +} // If $page is not defined, or '' or -1 +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (!$sortorder) { + $sortorder = "ASC"; +} +if (!$sortfield) { + $sortfield = "label"; +} + +$section = GETPOST("section", 'alpha'); +if (!$section) { + dol_print_error('', 'Error, section parameter missing'); + exit; +} +$urlfile = GETPOST("urlfile"); +if (!$urlfile) { + dol_print_error('', "ErrorParamNotDefined"); + exit; +} + +// Load ecm object +$ecmdir = new EcmDirectory($db); +$result = $ecmdir->fetch(GETPOST("section", 'alpha')); +if (!$result > 0) { + dol_print_error($db, $ecmdir->error); + exit; +} +$relativepath = $ecmdir->getRelativePath(); +$upload_dir = $conf->ecm->dir_output.'/'.$relativepath; + +$fullpath = $conf->ecm->dir_output.'/'.$relativepath.$urlfile; + +$relativetodocument = 'ecm/'.$relativepath; // $relativepath is relative to ECM dir, we need relative to document +$filepath = $relativepath.$urlfile; +$filepathtodocument = $relativetodocument.$urlfile; + +// Try to load object from index +$object = new ECMFiles($db); +$extrafields = new ExtraFields($db); +// fetch optionals attributes and labels +$extrafields->fetch_name_optionals_label($object->table_element); + +$result = $object->fetch(0, '', $filepathtodocument); +if ($result < 0) { + dol_print_error($db, $object->error, $object->errors); + exit; +} + +$permissionnote = $user->rights->ecm->setup; // Used by the include of actions_setnotes.inc.php + +/* + * Actions + */ + +include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not include_once + + +/* + * View + */ + +llxHeader('', $langs->trans('EcmFiles')); + +$form = new Form($db); + +$object->section_id = $ecmdir->id; +$object->label = $urlfile; +$head = ecm_file_prepare_head($object); + +print dol_get_fiche_head($head, 'note', $langs->trans("File"), -1, 'generic'); + +$s = ''; +$tmpecmdir = new EcmDirectory($db); // Need to create a new one +$tmpecmdir->fetch($ecmdir->id); +$result = 1; +$i = 0; +while ($tmpecmdir && $result > 0) { + $tmpecmdir->ref = $tmpecmdir->label; + $s = $tmpecmdir->getNomUrl(1).$s; + if ($tmpecmdir->fk_parent) { + $s = ' -> '.$s; + $result = $tmpecmdir->fetch($tmpecmdir->fk_parent); + } else { + $tmpecmdir = 0; + } + $i++; +} + +$urlfiletoshow = preg_replace('/\.noexe$/', '', $urlfile); + +$s = img_picto('', 'object_dir').' '.$langs->trans("ECMRoot").' -> '.$s.' -> '; +if ($action == 'edit') { + $s .= ''; +} else { + $s .= $urlfiletoshow; +} + +$linkback = ''; +if ($backtopage) { + $linkback = ''.$langs->trans("BackToTree").''; +} + +$object->ref = ''; // Force to hide ref +dol_banner_tab($object, '', $linkback, 0, '', '', $s); + + + +print '
'; +print '
'; + + +$cssclass = "titlefield"; +$moreparam = '&section='.$section.'&urlfile='.$urlfile; +include DOL_DOCUMENT_ROOT.'/core/tpl/notes.tpl.php'; + +print '
'; + +print dol_get_fiche_end(); + + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql index 309d4d4b170..4cdc36195db 100644 --- a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql +++ b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql @@ -399,6 +399,8 @@ CREATE TABLE llx_ecm_files_extrafields ) ENGINE=innodb; ALTER TABLE llx_ecm_files_extrafields ADD INDEX idx_ecm_files_extrafields (fk_object); +ALTER TABLE llx_ecm_files ADD COLUMN note_private text AFTER fk_user_m; +ALTER TABLE llx_ecm_files ADD COLUMN note_public text AFTER note_private; CREATE TABLE llx_ecm_directories_extrafields ( @@ -409,6 +411,9 @@ CREATE TABLE llx_ecm_directories_extrafields ) ENGINE=innodb; ALTER TABLE llx_ecm_directories_extrafields ADD INDEX idx_ecm_directories_extrafields (fk_object); +ALTER TABLE llx_ecm_directories ADD COLUMN note_private text AFTER fk_user_m; +ALTER TABLE llx_ecm_directories ADD COLUMN note_public text AFTER note_private; + ALTER TABLE llx_website_page ADD COLUMN allowed_in_frames integer DEFAULT 0; ALTER TABLE llx_website_page ADD COLUMN object_type varchar(255); ALTER TABLE llx_website_page ADD COLUMN fk_object varchar(255); diff --git a/htdocs/install/mysql/tables/llx_ecm_directories.sql b/htdocs/install/mysql/tables/llx_ecm_directories.sql index 4616a763b81..3ef599df05e 100644 --- a/htdocs/install/mysql/tables/llx_ecm_directories.sql +++ b/htdocs/install/mysql/tables/llx_ecm_directories.sql @@ -33,5 +33,7 @@ CREATE TABLE llx_ecm_directories tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, fk_user_c integer, fk_user_m integer, + note_private text, + note_public text, acl text ) ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_ecm_files.sql b/htdocs/install/mysql/tables/llx_ecm_files.sql index 635945a256d..cf9f3a8f148 100644 --- a/htdocs/install/mysql/tables/llx_ecm_files.sql +++ b/htdocs/install/mysql/tables/llx_ecm_files.sql @@ -38,5 +38,7 @@ CREATE TABLE llx_ecm_files tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, fk_user_c integer, fk_user_m integer, + note_private text, + note_public text, acl text -- for future permission 'per file' ) ENGINE=innodb;