diff --git a/src/cfg.c b/src/cfg.c index 4ef4132d..47984645 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -69,6 +69,7 @@ struct _parse_rule config_parse_rules[] = { "debug", "integer", (void*) int_parser, offsetof(struct __config, debug), "0", sizeof(int)}, { "default_retention_days", "integer", (void*) int_parser, offsetof(struct __config, default_retention_days), "2557", sizeof(int)}, { "enable_cjk", "integer", (void*) int_parser, offsetof(struct __config, enable_cjk), "0", sizeof(int)}, + { "enable_folders", "integer", (void*) int_parser, offsetof(struct __config, enable_folders), "0", sizeof(int)}, { "encrypt_messages", "integer", (void*) int_parser, offsetof(struct __config, encrypt_messages), "1", sizeof(int)}, { "extra_to_field", "string", (void*) string_parser, offsetof(struct __config, extra_to_field), "", MAXVAL-1}, { "extract_attachments", "integer", (void*) int_parser, offsetof(struct __config, extract_attachments), "1", sizeof(int)}, diff --git a/src/cfg.h b/src/cfg.h index 7be6746e..413c4de5 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -93,6 +93,8 @@ struct __config { int mmap_dedup_test; + int enable_folders; + int debug; }; diff --git a/src/config.h b/src/config.h index cf86155c..a8ad232b 100644 --- a/src/config.h +++ b/src/config.h @@ -14,7 +14,7 @@ #define VERSION "1.2.0-master" -#define BUILD 919 +#define BUILD 921 #define HOSTID "mailarchiver" @@ -91,6 +91,7 @@ #define SQL_CUSTOMER_TABLE "customer" #define SQL_IMPORT_TABLE "import" #define SQL_LEGAL_HOLD_TABLE "legal_hold" +#define SQL_FOLDER_MESSAGE_TABLE "folder_message" #define SQL_MESSAGES_VIEW "v_messages" #define SQL_ATTACHMENTS_VIEW "v_attachment" @@ -107,7 +108,7 @@ #define SQL_PREPARED_STMT_INSERT_INTO_FOLDER_TABLE "INSERT INTO `" SQL_FOLDER_TABLE "` (`name`, `parent_id`) VALUES(?,?)" #define SQL_PREPARED_STMT_UPDATE_METADATA_REFERENCE "UPDATE " SQL_METADATA_TABLE " SET reference=? WHERE message_id=? AND reference=''" #define SQL_PREPARED_STMT_GET_GUI_IMPORT_JOBS "SELECT id, type, username, password, server FROM " SQL_IMPORT_TABLE " WHERE started=0 ORDER BY id LIMIT 0,1" - +#define SQL_PREPARED_STMT_INSERT_FOLDER_MESSAGE "INSERT INTO " SQL_FOLDER_MESSAGE_TABLE " (`folder_id`, `id`) VALUES(?,?)" /* Error codes */ diff --git a/src/defs.h b/src/defs.h index 336603aa..62d20209 100644 --- a/src/defs.h +++ b/src/defs.h @@ -315,6 +315,7 @@ struct __data { MYSQL_STMT *stmt_insert_into_sphinx_table; MYSQL_STMT *stmt_insert_into_meta_table; MYSQL_STMT *stmt_insert_into_attachment_table; + MYSQL_STMT *stmt_insert_into_folder_message_table; MYSQL_STMT *stmt_get_attachment_id_by_signature; MYSQL_STMT *stmt_get_attachment_pointer; MYSQL_STMT *stmt_query_attachment; diff --git a/src/message.c b/src/message.c index 389c2677..d811f9dc 100644 --- a/src/message.c +++ b/src/message.c @@ -129,6 +129,23 @@ int store_recipients(struct session_data *sdata, struct __data *data, char *to, } +int store_folder_id(struct session_data *sdata, struct __data *data, uint64 id, struct __config *cfg){ + int rc = ERR; + + if(prepare_sql_statement(sdata, &(data->stmt_insert_into_folder_message_table), SQL_PREPARED_STMT_INSERT_FOLDER_MESSAGE, cfg) == ERR) return rc; + + p_bind_init(data); + + data->sql[data->pos] = (char *)&data->folder; data->type[data->pos] = TYPE_LONGLONG; data->pos++; + data->sql[data->pos] = (char *)&id; data->type[data->pos] = TYPE_LONGLONG; data->pos++; + + if(p_exec_query(sdata, data->stmt_insert_into_folder_message_table, data) == OK) rc = OK; + close_prepared_statement(data->stmt_insert_into_folder_message_table); + + return rc; +} + + int update_metadata_reference(struct session_data *sdata, struct _state *state, struct __data *data, char *ref, struct __config *cfg){ int ret = ERR; @@ -223,6 +240,11 @@ int store_meta_data(struct session_data *sdata, struct _state *state, struct __d if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored indexdata, rc=%d", sdata->ttmpfile, rc); + if(cfg->enable_folders == 1){ + rc = store_folder_id(sdata, data, id, cfg); + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored folderdata, rc=%d", sdata->ttmpfile, rc); + } + ret = OK; } diff --git a/webui/controller/folder/list.php b/webui/controller/folder/list.php index 1f6f00d5..12473b23 100644 --- a/webui/controller/folder/list.php +++ b/webui/controller/folder/list.php @@ -19,7 +19,7 @@ class ControllerFolderList extends Controller { $this->document->title = $this->data['text_folders']; if(isset($this->request->post['name']) && $this->request->post['name']) { - $this->model_folder_folder->add_extra_folder($this->request->post['name']); + $this->model_folder_folder->add_folder($this->request->post['name']); Header("Location: folders.php"); return; @@ -27,7 +27,7 @@ class ControllerFolderList extends Controller { if(isset($this->request->get['id']) && $this->request->get['id'] > 0) { - $this->model_folder_folder->remove_extra_folder($this->request->get['id']); + $this->model_folder_folder->remove_folder($this->request->get['id']); Header("Location: folders.php"); return; @@ -36,7 +36,7 @@ class ControllerFolderList extends Controller { $this->data['page_len'] = get_page_length(); - $this->data['extra_folders'] = $this->model_folder_folder->get_extra_folders_for_user(); + $this->data['extra_folders'] = $this->model_folder_folder->get_folders_for_user(); $this->render(); } diff --git a/webui/controller/message/folder.php b/webui/controller/message/folder.php new file mode 100644 index 00000000..a348cdde --- /dev/null +++ b/webui/controller/message/folder.php @@ -0,0 +1,31 @@ +id = "content"; + $this->template = "message/note.tpl"; + $this->layout = "common/layout-empty"; + + $session = Registry::get('session'); + $request = Registry::get('request'); + $db = Registry::get('db'); + $sphx = Registry::get('sphx'); + + $this->load->model('search/search'); + $this->load->model('folder/folder'); + + if(isset($this->request->post['folder_id']) && isset($this->request->post['id'])) { + + if($this->model_search_search->check_your_permission_by_id($this->request->post['id']) == 1) { + $this->model_folder_folder->update_message_folder($this->request->post['id'], $this->request->post['folder_id']); + } + } + } + +} + +?> diff --git a/webui/controller/message/view.php b/webui/controller/message/view.php index 0659a2a9..81fd7588 100644 --- a/webui/controller/message/view.php +++ b/webui/controller/message/view.php @@ -19,6 +19,10 @@ class ControllerMessageView extends Controller { $this->load->model('user/user'); + if(ENABLE_FOLDER_RESTRICTIONS == 1) { + $this->load->model('folder/folder'); + } + $this->document->title = $this->data['text_message']; $this->data['id'] = ''; @@ -83,6 +87,11 @@ class ControllerMessageView extends Controller { $this->data['can_download'] = $this->model_audit_audit->can_download(); $this->data['can_restore'] = $this->model_audit_audit->can_restore(); + if(ENABLE_FOLDER_RESTRICTIONS == 1) { + $this->data['folders'] = $this->model_folder_folder->get_folders_for_user(); + $this->data['folder_id'] = $this->model_folder_folder->get_folder_id_by_id($this->data['id']); + } + foreach($this->data['attachments'] as $a) { if(preg_match("/image/", $a['type'])) { $attachment = $this->model_search_message->get_attachment_by_id($a['id']); diff --git a/webui/controller/search/folder.php b/webui/controller/search/folder.php index 9ae43329..a19b36a2 100644 --- a/webui/controller/search/folder.php +++ b/webui/controller/search/folder.php @@ -14,8 +14,8 @@ class ControllerSearchFolder extends Controller { $this->load->model('folder/folder'); - $this->data['folders'] = $this->model_folder_folder->get_folders_for_user(); - $this->data['extra_folders'] = $this->model_folder_folder->get_extra_folders_for_user(); + $this->data['extra_folders'] = $this->model_folder_folder->get_folders_for_user(); + array_unshift($this->data['extra_folders'], array('id' => 0, 'name' => '---')); $this->data['folders_by_hier'] = $this->model_folder_folder->get_all_folder_ids_hier($session->get("uid")); diff --git a/webui/model/folder/folder.php b/webui/model/folder/folder.php index 17f8ecaa..6306874d 100644 --- a/webui/model/folder/folder.php +++ b/webui/model/folder/folder.php @@ -61,7 +61,11 @@ class ModelFolderFolder extends Model { $q = str_repeat("?,", count($session->get("folders"))); $q = preg_replace("/\,$/", "", $q); - $query = $this->db->query("SELECT `id`, `name` FROM `" . TABLE_FOLDER . "` WHERE id IN ($q)", $session->get("folders")); + if(isAuditorUser() == 1) { + $query = $this->db->query("SELECT `id`, `name` FROM `" . TABLE_FOLDER); + } else { + $query = $this->db->query("SELECT `id`, `name` FROM `" . TABLE_FOLDER . "` WHERE id IN ($q)", $session->get("folders")); + } if(isset($query->rows)) { return $query->rows; } @@ -69,21 +73,12 @@ class ModelFolderFolder extends Model { } - public function get_extra_folders_for_user() { + private function is_your_folder($folder_id = 0) { $session = Registry::get('session'); - $query = $this->db->query("SELECT `id`, `name` FROM `" . TABLE_FOLDER_EXTRA . "` WHERE uid=? ORDER BY name", array($session->get("uid"))); + if(isAuditorUser() == 1) { return 1; } - if(isset($query->rows)) { return $query->rows; } - - return array(); - } - - - private function is_your_extra_folder($folder_id = 0) { - $session = Registry::get('session'); - - $query = $this->db->query("SELECT `id` FROM `" . TABLE_FOLDER_EXTRA . "` WHERE uid=? AND id=?", array($session->get("uid"), $folder_id)); + $query = $this->db->query("SELECT f.id AS id FROM `" . TABLE_FOLDER . "` f, " . TABLE_FOLDER_USER . "` fu WHERE f.id=fu.id AND fu.uid=? AND f.id=?", array($session->get("uid"), $folder_id)); if(isset($query->row['id'])) { return 1; } return 0; @@ -91,17 +86,21 @@ class ModelFolderFolder extends Model { public function copy_message_to_folder_by_id($folder_id = 0, $meta_id = 0) { - if(!$this->is_your_extra_folder($folder_id)) { return -1; } + if(!$this->is_your_folder($folder_id)) { return -1; } $query = $this->db->query("INSERT INTO " . TABLE_FOLDER_MESSAGE . " (folder_id, id) VALUES(?,?)", array($folder_id, $meta_id)); return $this->db->countAffected(); } - public function get_all_folder_ids($uid = 0) { + public function get_folder_id_array_for_user($uid = 0, $is_admin = 0) { $arr = array(); - $query = $this->db->query("SELECT id FROM `" . TABLE_FOLDER_USER . "` WHERE uid=?", array($uid)); + if($is_admin == 2) { + $query = $this->db->query("SELECT id FROM `" . TABLE_FOLDER); + } else { + $query = $this->db->query("SELECT id FROM `" . TABLE_FOLDER_USER . "` WHERE uid=?", array($uid)); + } if(isset($query->rows)) { foreach ($query->rows as $q) { @@ -132,21 +131,6 @@ class ModelFolderFolder extends Model { } - public function get_all_extra_folder_ids($uid = 0) { - $arr = array(); - - $query = $this->db->query("SELECT id FROM `" . TABLE_FOLDER_EXTRA . "` WHERE uid=?", array($uid)); - - if(isset($query->rows)) { - foreach ($query->rows as $q) { - array_push($arr, $q['id']); - } - } - - return $arr; - } - - private function get_sub_folders($id = 0, &$arr = array()) { $query = $this->db->query("SELECT id FROM `" . TABLE_FOLDER . "` WHERE parent_id=?", array($id)); @@ -203,32 +187,55 @@ class ModelFolderFolder extends Model { } - public function add_extra_folder($name = '') { + public function get_folder_id_by_id($id = 0) { + $query = $this->db->query("SELECT folder_id FROM `" . TABLE_FOLDER_MESSAGE . "` WHERE id=?", array($id)); + + if(isset($query->row)) { return $query->row['folder_id']; } + + return 0; + } + + + public function update_message_folder($id = 0, $folder_id = 0) { + $query = $this->db->query("UPDATE `" . TABLE_FOLDER_MESSAGE . "` SET folder_id=? WHERE id=?", array($folder_id, $id)); + + //$query = $this->sphx->query("UPDATE " . SPHINX_MAIN_INDEX . " SET folder=? WHERE id=?", array($folder_id, $id)); + $query = $this->sphx->query("UPDATE " . SPHINX_MAIN_INDEX . " SET folder=$folder_id WHERE id=$id"); + } + + + public function add_folder($name = '') { if($name == '') { return -1; } $session = Registry::get('session'); - $query = $this->db->query("INSERT INTO " . TABLE_FOLDER_EXTRA . " (uid, name) VALUES(?,?)", array($session->get("uid"), $name)); + $query = $this->db->query("INSERT INTO " . TABLE_FOLDER . " (name) VALUES(?)", array($name)); - $last_id = $this->db->getLastId(); + if(isAuditorUser() == 0) { + $last_id = $this->db->getLastId(); + $query = $this->db->query("INSERT INTO " . TABLE_FOLDER_USER . " (id, uid) VALUES(?,?)", array($last_id, $session->get("uid"))); - $extra_folders = $session->get("extra_folders"); - - if(!isset($extra_folders[$last_id])) { array_push($extra_folders, $last_id); } + $folders = $session->get("folders"); + if(!isset($folders[$last_id])) { array_push($folders, $last_id); $session->set("folders", $folders); } + } return $this->db->countAffected(); } - public function remove_extra_folder($id = 0) { - if($id == 0) { return -1; } + public function remove_folder($id = 0) { + if($id <= 0) { return -1; } $session = Registry::get('session'); - $query = $this->db->query("DELETE FROM " . TABLE_FOLDER_EXTRA . " WHERE id=? AND uid=?", array($id, $session->get("uid"))); - if($this->db->countAffected() == 1) { - $query = $this->db->query("DELETE FROM " . TABLE_FOLDER_MESSAGE . " WHERE folder_id=?", array($id)); - return $this->db->countAffected(); + if($this->is_your_folder($id) == 1) { + $query = $this->db->query("DELETE FROM " . TABLE_FOLDER . " WHERE id=?", array($id)); + $query = $this->db->query("DELETE FROM " . TABLE_FOLDER_USER . " WHERE id=? AND uid=?", array($id, $session->get("uid"))); + + $folders = $session->get("folders"); + if(isset($folders[$id])) { unset($folders[$id]); $session->set("folders", $folders); } + + // shall we delete the existing message - folder id assignments from folder_message? } return 0; diff --git a/webui/model/search/search.php b/webui/model/search/search.php index 1d6b103f..dbe0cd11 100644 --- a/webui/model/search/search.php +++ b/webui/model/search/search.php @@ -223,9 +223,10 @@ class ModelSearchSearch extends Model { list ($total_found, $num_rows, $id_list) = $this->get_sphinx_id_list($data['note'], SPHINX_NOTE_INDEX, 'note', $page); $query = $this->sphx->query("SELECT id FROM " . SPHINX_MAIN_INDEX . " WHERE $folders id IN ($id_list) $sortorder LIMIT 0,$pagelen OPTION max_matches=" . MAX_SEARCH_HITS); } - else if(ENABLE_FOLDER_RESTRICTIONS == 1 && isset($data['extra_folders']) && $data['extra_folders']) { - list ($total_found, $num_rows, $ids_in_extra_folders) = $this->get_sphinx_id_list_by_extra_folders($data['extra_folders'], $page); - $query = $this->sphx->query("SELECT id FROM " . SPHINX_MAIN_INDEX . " WHERE $a $id $date $attachment $direction $size MATCH('$match') AND id IN ($ids_in_extra_folders) $sortorder LIMIT 0,$pagelen OPTION max_matches=" . MAX_SEARCH_HITS); + else if(ENABLE_FOLDER_RESTRICTIONS == 1 && isset($data['extra_folders']) && strlen($data['extra_folders']) > 0) { + $query = $this->sphx->query("SELECT id FROM " . SPHINX_MAIN_INDEX . " WHERE $a $id $date $attachment $direction $size folder IN (" . preg_replace("/ /", ",", $data['extra_folders']) . ") AND MATCH('$match') $sortorder LIMIT $offset,$pagelen OPTION max_matches=" . MAX_SEARCH_HITS); + $total_found = $query->total_found; + $num_rows = $query->num_rows; } else { $query = $this->sphx->query("SELECT id FROM " . SPHINX_MAIN_INDEX . " WHERE $a $id $date $attachment $direction $size $folders MATCH('$match') $sortorder LIMIT $offset,$pagelen OPTION max_matches=" . MAX_SEARCH_HITS); diff --git a/webui/model/user/auth.php b/webui/model/user/auth.php index b117185d..230193a6 100644 --- a/webui/model/user/auth.php +++ b/webui/model/user/auth.php @@ -16,7 +16,6 @@ class ModelUserAuth extends Model { $session->set("emails", $data['emails']); $session->set("folders", $data['folders']); - $session->set("extra_folders", $data['extra_folders']); } @@ -35,7 +34,6 @@ class ModelUserAuth extends Model { $data['auditdomains'] = array(); $data['emails'] = array(); $data['folders'] = array(); - $data['extra_folders'] = array(); if($username == '' || $password == '') { return 0; } @@ -114,8 +112,7 @@ class ModelUserAuth extends Model { $extra_emails = $this->model_user_user->get_email_addresses_from_groups($data['emails']); $data['emails'] = array_merge($data['emails'], $extra_emails); - $data['folders'] = $this->model_folder_folder->get_all_folder_ids($query->row['uid']); - $data['extra_folders'] = $this->model_folder_folder->get_all_extra_folder_ids($query->row['uid']); + $data['folders'] = $this->model_folder_folder->get_folder_id_array_for_user($query->row['uid'], $data['admin_user']); $session->set("auth_data", $data); @@ -323,7 +320,6 @@ class ModelUserAuth extends Model { $data['auditdomains'] = $this->model_domain_domain->get_your_all_domains_by_email($email); $data['emails'] = $emails; $data['folders'] = array(); - $data['extra_folders'] = array(); $uid = $this->model_user_user->get_uid_by_email($email); if($uid < 1) { diff --git a/webui/model/user/google.php b/webui/model/user/google.php index d675252a..7a375f9b 100644 --- a/webui/model/user/google.php +++ b/webui/model/user/google.php @@ -41,8 +41,7 @@ class ModelUserGoogle extends Model { $session->set("realname", $query->row['realname']); $session->set("emails", $this->model_user_user->get_users_all_email_addresses($user['uid'])); - $session->set("folders", $this->model_folder_folder->get_all_folder_ids($user['uid'])); - $session->set("extra_folders", $this->model_folder_folder->get_all_extra_folder_ids($user['uid'])); + $session->set("folders", $this->model_folder_folder->get_folder_id_array_for_user($user['uid'])); AUDIT(ACTION_LOGIN, $user['username'], '', '', 'successful auth against Google'); diff --git a/webui/view/javascript/piler-in.js b/webui/view/javascript/piler-in.js index db1c65ce..d7c2794f 100644 --- a/webui/view/javascript/piler-in.js +++ b/webui/view/javascript/piler-in.js @@ -425,6 +425,23 @@ var Piler = }, + update_message_folder:function(id, msg) + { + Piler.log("[update_message_folder]", id, msg); + + Piler.poor_mans_keepalive_for_dummy_browsers(); + + jQuery.ajax('index.php?route=message/folder', { + data: { id: id, folder_id: $('#folder_id').val() }, + type: "POST" + }) + .done( function(a) {}) + .fail(function(a, b) { alert("Problem retrieving XML data:" + b) }); + + Piler.show_message('messagebox1', msg, 0.85); + }, + + tag_search_results:function(msg) { Piler.log("[tag_search_results]", msg); diff --git a/webui/view/theme/default/assets/css/metro-bootstrap.css b/webui/view/theme/default/assets/css/metro-bootstrap.css index a94fb0c3..2b175e5c 100644 --- a/webui/view/theme/default/assets/css/metro-bootstrap.css +++ b/webui/view/theme/default/assets/css/metro-bootstrap.css @@ -1658,6 +1658,7 @@ html,body{height:auto !important;height:100%;min-height:100%;} #popupx{position:absolute;right:10px;top:5px;margin:10px;cursor:pointer;} #restorebox{position:absolute;top:40px;left:15%;display:none;font-weight:bold;padding:15px;z-index:1;} #notesbox{position:absolute;top:8px;right:8px;} +#folderbox{position:absolute;top:8px;right:380px;} #sspinner{display:none;} .message_highlight{background:lightblue;} #searchcontainer{text-align:center;min-width:320px;} @@ -1669,7 +1670,7 @@ html,body{height:auto !important;height:100%;min-height:100%;} #searchcontainer #button_search,#searchcontainer #button_options{width:30%;} #mainscreen{position:absolute;top:120px;right:20px;bottom:20px;left:20px;min-width:320px;z-index:0;} #mailleftcontainer{position:absolute;top:0;left:0;bottom:0;width:195px;} -#mailboxlist-container{position:absolute;top:0;left:0;width:100%;bottom:0;border:1px solid #999999;border-top:none;background-color:#f9f9f9;overflow:hidden;} +#mailboxlist-container{position:absolute;top:0;left:0;width:100%;bottom:0;border:1px solid #999999;border-top:none;background-color:#f9f9f9;overflow:auto;} .boxlistcontent{position:absolute;top:0px;bottom:34px;left:0;right:0;width:100%;overflow-y:auto;overflow-x:hidden;height:auto;} .boxfooter{position:absolute;bottom:0px;left:0px;right:0px;overflow:hidden;height:33px;border-top:1px solid #ccc;background-color:#f6f6f6;min-width:320px;} #mailboxlist{position:relative;height:auto;margin:0px;padding:0px;list-style-image:none;list-style-type:none;overflow:hidden;white-space:nowrap;background-color:#FFF;} @@ -1730,4 +1731,6 @@ body#loginpage{background-color:#fcfcfc;padding-top:40px;padding-bottom:40px;} .control-group.success .fileupload .fileupload-preview{color:#468847;} .control-group.success .fileupload .thumbnail{border-color:#468847;} .bottomborder { border-bottom: 1px solid black; } +.folderlabel{display: block; padding-left: 35px; text-indent: -15px;} +.foldercheckbox{vertical-align: center; position: relative; top: -1px; margin:0; padding: 0; height: 13px; width: 13px;} @media (min-width:980px){body{padding-top:65px;}}@media (max-width:980px){.navbar-fixed-top{margin-bottom:10px;} .nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{font-weight:normal;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .nav-collapse .nav>li>a:hover,.nav-collapse ul.nav li.dropdown ul.dropdown-menu li a:hover{color:#fff;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;background-color:#555555;} .navbar .nav li.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;} .dropdown-menu li>a:hover{background-color:#00f;}}@media (max-width:767px){#mainscreen{top:162px;} #pagingrow{border-bottom:1px solid #ccc;} #pagingbox{border-right:none;} #functionbox{border-left:none;} .boxlistcontent{bottom:68px;overflow:scroll;} .boxfooter{height:67px;}} diff --git a/webui/view/theme/default/templates/message/view.tpl b/webui/view/theme/default/templates/message/view.tpl index aa13db92..12e5625f 100644 --- a/webui/view/theme/default/templates/message/view.tpl +++ b/webui/view/theme/default/templates/message/view.tpl @@ -43,6 +43,18 @@ + +
- -+