Przeglądaj źródła

+ remove, restore msgs

Piotr Labudda 6 lat temu
rodzic
commit
4a8a5443be
2 zmienionych plików z 271 dodań i 180 usunięć
  1. 29 22
      SE/se-lib/Route/Msgs.php
  2. 242 158
      SE/se-lib/Route/UserMsgs.php

+ 29 - 22
SE/se-lib/Route/Msgs.php

@@ -377,19 +377,13 @@ SQL_QUERY;
 		$this->forceFinishMessage($id, $execNotes);
 	}
 
-	public function getMessage($id) {
+	function getMessage($id) {
 		if (empty($id)) return;
 		$id = intval($id);
 		if ($id <= 0) return;
 
-		$msg = null;
-		$sql = "select * from `CRM_UI_MSGS` where `ID`='{$id}' ";
-		$db = DB::getDB();
-		$res = $db->query($sql);
-		if ($r = $db->fetch($res)) {
-			$msg = $r;
-		}
-		return $msg;
+		$msg = DB::getPDO()->fetchFirst("select * from `CRM_UI_MSGS` where `ID` = :id ", [ ':id' => $id ]);
+		return ($msg) ? (object)$msg : null;
 	}
 
 	public function getActiveMessage($id) {
@@ -444,24 +438,37 @@ SQL_QUERY;
 		$db->query($sql);
 	}
 
-	public function removeTableRecordMsg($idMsg) {
+	function removeTableRecordMsg($idMsg) {
 		// IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
 		$idMsg = intval($idMsg);
 		if ($idMsg <= 0) throw new Exception("Brak wiadomości!");
 		$usrLogin = User::getLogin();
-		$db = DB::getDB();
-		if (!$db) throw new Exception("Brak dazy danych!");
-		if ($db->has_errors()) throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
 		$sqlTODO = "delete `CRM_UI_MSGS` where `ID`='{$idMsg}' ";
-		$sql = "update `CRM_UI_MSGS`
-			set `A_STATUS`=IF('{$usrLogin}'=`A_RECORD_CREATE_AUTHOR`, 'DELETED', 'OFF_SOFT')
-				, `A_RECORD_DELETE_AUTHOR`='{$usrLogin}'
-				, `A_RECORD_DELETE_DATE`=NOW()
-			where `ID`='{$idMsg}'
-		";
-		DBG::_('DBG_MSGS', '>1', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
-		$res = $db->query($sql);
-		if (!$res || $db->has_errors()) throw new Exception("Wystąpiły błędy podczas próby zapisu wiadomości: " . implode("\n<br>", $db->get_errors()));
+		DB::getPDO()->execSql("
+			update `CRM_UI_MSGS`
+			set `A_STATUS` = IF( :login = `A_RECORD_CREATE_AUTHOR`, 'DELETED', 'OFF_HARD')
+				, `A_RECORD_DELETE_AUTHOR` = :login
+				, `A_RECORD_DELETE_DATE` = NOW()
+			where `ID` = :id
+		", [
+			':id' => $idMsg,
+			':login' => $usrLogin,
+		]);
+	}
+
+	function restoreTableRecordMsg($idMsg) {
+		// IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
+		$idMsg = intval($idMsg);
+		if ($idMsg <= 0) throw new Exception("Brak wiadomości!");
+		$usrLogin = User::getLogin();
+		$sqlTODO = "delete `CRM_UI_MSGS` where `ID`='{$idMsg}' ";
+		DB::getPDO()->execSql("
+			update `CRM_UI_MSGS`
+			set `A_STATUS` = 'WAITING'
+			where `ID` = :id
+		", [
+			':id' => $idMsg,
+		]);
 	}
 
 }

+ 242 - 158
SE/se-lib/Route/UserMsgs.php

@@ -15,6 +15,15 @@ class Route_UserMsgs extends RouteBase {
 		if (!User::logged()) {
 			User::authByRequest();
 		}
+
+		if ($postTask = V::get('_postTask', '', $_POST)) {
+			$postFunction = "{$postTask}PostTask";
+			if (!method_exists($this, $postFunction)) {
+				S::saveUserMessage('AlertDangerException', "post task not exists '{$postTask}'");
+			} else {
+				$this->$postFunction($_POST);
+			}
+		}
 	}
 
 	function defaultAction() { UI::layout([ $this, 'defaultView' ]); }
@@ -88,11 +97,15 @@ class Route_UserMsgs extends RouteBase {
 -->
 </div>
 <script>
+var DBG = 0;
+var DBG1 = 1;
+
 function tblMsgsLoadMoreRows(n) {
 	var nNode = jQuery(n),
 			lastMsgId = nNode.data('last_msg_id'),
 			listType = nNode.data('list_type')
 	;
+	DBG && console.log("DBG:tblMsgsLoadMoreRows ", { lastMsgId, listType });
 	nNode.blur();
 
 	function tblMsgsSetNoMoreRows(btnLoadMoreNode) {
@@ -104,7 +117,7 @@ function tblMsgsLoadMoreRows(n) {
 	}
 
 	function tblMsgsAddMsgToList(msg, btnLoadMoreNode, listType) {
-		var tbodyNode = btnLoadMoreNode.closest('tfoot').prev('tbody'),
+		var tbodyNode = btnLoadMoreNode.closest('table').children('tbody'),
 				trNode = jQuery('<tr></tr>'),
 				tdIdNode = jQuery('<td></td>'),
 				tdMsgNode = jQuery('<td></td>'),
@@ -162,6 +175,7 @@ function tblMsgsLoadMoreRows(n) {
 				lastMsgId = 0,
 				hasMore = false
 		;
+		DBG && console.log("DBG:tblMsgsLoadMoreRows fetched ", { data });
 		if (!data || !data.msgs || !data.keysOrder) {
 			jQuery.notify('Wystąpiły błędy podczas pobierania listy wiadomości', 'error');
 			return false;
@@ -221,80 +235,80 @@ function tblMsgsLoadMoreRows(n) {
 		$msgsTotal = count($msgsList);
 		$listLimit = $this->_listLimit;
 		$lastMsgId = 0;
+		$viewMsgList = array_slice($msgsList, 0, $this->_listLimit, $preserve_keys = true);
+
+		if ($msgsTotal > $listLimit) {
+			$msgIds = array_slice(array_keys($msgsList), 0, $listLimit);
+			$lastMsgId = array_pop($msgIds);
+		}
 		$actionTask = ($listType == 'inbox')? 'read' : 'view';
-		?>
-	<table class="tblMsgsList table table-hovered" style="margin-bottom:0; table-layout:fixed;">
-		<thead>
-			<tr>
-				<th style="width:60px">#</th>
-				<th>wiadomość</th>
-				<th style="width:130px">data</th>
-			</tr>
-		</thead>
-		<tbody>
-			<?php if ($msgsTotal <= 0) : ?>
-				<tr>
-					<td colspan="3"><em class="text-muted" style="padding-left:60px;">Brak wiadomości</em></td>
-				</tr>
-			<?php else : ?>
-				<?php $i = 0; foreach ($msgsList as $idMsg => $msg) : $i++; if ($i > $listLimit) break; $lastMsgId = $idMsg; ?>
-					<?php
-						$onClick = '';
-						$msgLink = Request::getPathUri() . 'index.php?_route=UserMsgs&id=' . $msg['_raw']->ID;
-						$msgLink .= "&usrLogin={$usrLogin}";
-						if ('read' == $actionTask || 'view' == $actionTask) {
-							$msgLink .= '&_task=' . $actionTask;
-						} else {
-							$msgLink = null;
-						}
-						if ($msgLink) {
-							$jsOnClick = "window.location.href='{$msgLink}'";
-							$onClick = 'onclick="' . $jsOnClick . '"';
-						}
-					?>
-					<tr <?php echo $onClick; ?>
-							class="tblMsgsListItem <?php echo ($msg['_read'])? 'active' : ''; ?>">
-						<td><?php echo $msg['_raw']->ID; ?></td>
-						<td>
-							<div style="overflow:hidden; white-space:nowrap; text-overflow:ellipsis;"><?php echo htmlspecialchars($msg['message']); ?></div>
-							<div class="text-muted" style="font-style:italic;">
-								od <?php echo $msg['_raw']->A_RECORD_CREATE_AUTHOR; ?> do <?php
-									if ('everyone' == $msg['_raw']->userTargetType) {
-										echo "wszystkich";
-									} else if ('user' == $msg['_raw']->userTargetType) {
-										echo "{$msg['_raw']->userTargetName}";
-									} else if ('group' == $msg['_raw']->userTargetType) {
-										echo "grupy {$msg['_raw']->userTargetName}";
-									}
-								?>
-							</div>
-						</td>
-						<td style="white-space:nowrap;">
-							<?php echo $msg['_raw']->A_RECORD_CREATE_DATE; ?>
-							<?php if ($msg['_readDate']) : ?>
-								<div class="text-muted" style="font-style:italic" title="Przeczytano <?php echo $msg['_readDate']; ?>"><?php echo $msg['_readDate']; ?></div>
-							<?php else : ?>
-								<div class="text-muted" style="font-style:italic" title="Wiadomość nie została jeszcze odczytana">nieodczytana</div>
-							<?php endif; ?>
-						</td>
-					</tr>
-				<?php endforeach; ?>
-			<?php endif; ?>
-		</tbody>
-		<tfoot>
-			<?php if ($msgsTotal > $listLimit) : ?>
-				<tr class="active">
-					<td colspan="3" style="text-align:center">
-						<button class="btn btn-link"
-										data-last_msg_id="<?php echo $lastMsgId; ?>"
-										data-list_type="<?php echo $listType; ?>"
-										onclick="return tblMsgsLoadMoreRows(this);">pobierz starsze wiadomości ...</button>
-					</td>
-				</tr>
-			<?php endif; ?>
-		</tfoot>
-	</table>
-<?php
+		echo UI::h('table', [ 'class' => "tblMsgsList table table-hovered", 'style' => "margin-bottom:0; table-layout:fixed" ], [
+			UI::h('thead', [], [
+				UI::h('tr', [], [
+					UI::h('th', [ 'style' => "width:60px" ], "#"),
+					UI::h('th', [], "wiadomość"),
+					UI::h('th', [ 'style' => "width:130px" ], "data"),
+				]),
+			]),
+			($msgsTotal > $listLimit)
+				?	UI::h('tfoot', [], [
+						UI::h('tr', [ 'class' => "active" ], [
+							UI::h('td', [ 'colspan' => "3", 'style' => "text-align:center" ], [
+								UI::h('button', [
+									'class' => "btn btn-link",
+									'data-last_msg_id' => $lastMsgId,
+									'data-list_type' => $listType,
+									'onclick' => "return tblMsgsLoadMoreRows(this);",
+								], "pobierz starsze wiadomości ..."),
+							]),
+						]),
+					])
+				:	''
+			,
+			UI::h('tbody', [],
+				($msgsTotal <= 0)
+				?	UI::h('tr', [], [
+						UI::h('td', [ 'colspan' => "3" ], [
+							UI::h('em', [ 'class' => "text-muted", 'style' => "padding-left:60px;" ], "Brak wiadomości"),
+						]),
+					])
+				:	array_map(function ($msg) use ($actionTask, $usrLogin) {
+						$msgLink = ('read' == $actionTask || 'view' == $actionTask)
+							?	$this->getLink($actionTask, [ 'id' => $msg['_raw']->ID, 'usrLogin' => $usrLogin ])
+							:	null
+						;
+						$jsOnClick = ($msgLink)
+							?	"window.location.href='{$msgLink}'"
+							:	''
+						;
+
+						return UI::h('tr', [ 'onclick' => $jsOnClick, 'class' => "tblMsgsListItem " . ($msg['_read'] ? 'active' : '') ], [
+							UI::h('td', [], $msg['_raw']->ID),
+							UI::h('td', [], [
+								UI::h('div', [ 'style' => "overflow:hidden; white-space:nowrap; text-overflow:ellipsis" ], htmlspecialchars($msg['message'])),
+								UI::h('div', [ 'class' => "text-muted", 'style' => "font-style:italic"], [
+									"od {$msg['_raw']->A_RECORD_CREATE_AUTHOR} do " . $this->getOutMsgTarget($msg['_raw']),
+								]),
+							]),
+							UI::h('td', [ 'style' => "white-space:nowrap" ], [
+								$msg['_raw']->A_RECORD_CREATE_DATE,
+								($msg['_readDate'])
+									?	UI::h('div', [ 'class' => "text-muted", 'style' => "font-style:italic", 'title' => "Przeczytano {$msg['_readDate']}" ], $msg['_readDate'])
+									:	UI::h('div', [ 'class' => "text-muted", 'style' => "font-style:italic", 'title' => "Wiadomość nie została jeszcze odczytana" ], "nieodczytana")
+								,
+							]),
+						]);
+					}, $viewMsgList)
+			),
+		]);
+	}
+	function getOutMsgTarget($msg) {
+		switch ($msg->userTargetType) {
+			case 'everyone': return "wszystkich";
+			case 'user': return $msg->userTargetName;
+			case 'group': return "grupy {$msg->userTargetName}";
+			default: return '???';
+		}
 	}
 
 	function getMsgs($filterType, $usrLogin, $lastMsgId = null, $fromTime = null) {
@@ -345,8 +359,6 @@ function tblMsgsLoadMoreRows(n) {
 				break;
 			default: throw new Exception("Unknown filter type");
 		}
-		$db = DB::getDB();
-		$tableName = $db->_($tableName);
 
 		if ($lastMsgId > 0) {
 			$sqlWhereAddFilter .= "\n  and m.`ID`<{$lastMsgId}";
@@ -355,26 +367,23 @@ function tblMsgsLoadMoreRows(n) {
 			$sqlWhereAddFilter .= "\n  and m.`A_RECORD_CREATE_DATE`>='{$fromTime}'";
 		}
 		$sqlLimit = $this->_listLimit + 1;
-		$sql = "select m.*
+		$sql = "
+			select m.*
 			from `CRM_UI_MSGS` m
-			where m.`uiTargetType`='default_db_table_record'
-		--		and m.`uiTargetName`='{$tableName}.{$idRow}'
+			where m.`uiTargetType` = 'default_db_table_record'
 				{$sqlWhereAddFilter}
 			order by m.`ID` DESC
 			limit {$sqlLimit}
 		";
-		DBG::_('DBG_MSGS', '>1', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
-		$db = DB::getDB();
-		$res = $db->query($sql);
-		while ($r = $db->fetch($res)) {
-			$msg['message'] = $r->msg;
-			$msg['type'] = $r->msgType;
-			$msg['_raw'] = $r;
-			$msg['_read'] = ('WAITING' != $r->A_STATUS);
-			$msg['_readDate'] = $r->actionExecutedTime;
-			$msgsList[$r->ID] = $msg;
-		}
-		return $msgsList;
+		return array_map(function ($row) {
+			return [
+				'message' => $row['msg'],
+				'type' => $row['msgType'],
+				'_raw' => (object)$row,
+				'_read' => ('WAITING' != $row['A_STATUS']),
+				'_readDate' => $row['actionExecutedTime'],
+			];
+		}, DB::getPDO()->fetchAllByKey($sql, 'ID'));
 	}
 
 	function _validate($args) {
@@ -398,26 +407,22 @@ function tblMsgsLoadMoreRows(n) {
 		$msg = V::get('msg', '', $args);
 		$usrLogin = User::getLogin();
 
-		$db = DB::getDB();
-		if (!$db) throw new Exception("Brak dazy danych!");
-		if ($db->has_errors()) throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
-		$item = array();
-		$item['`uiTargetType`'] = "'default_db_table_record'";
-		$item['`uiTargetName`'] = "'{$tableName}.{$idRow}'";
-		$item['`userTargetType`'] = "'{$toType}'";
-		$item['`userTargetName`'] = "'{$to}'";
-		$item['`msg`'] = "'" . $db->_($msg) . "'";
-		$item['`A_RECORD_CREATE_DATE`'] = "NOW()";
-		$item['`A_RECORD_CREATE_AUTHOR`'] = "'{$usrLogin}'";
-		$item['`A_STATUS`'] = "'WAITING'";
-		$item['`app_className`'] = "'TableMsgs'";
-		$sql = "insert into `CRM_UI_MSGS` (" . implode(",", array_keys($item)) . ")
-			values (" . implode(",", array_values($item)) . ")
-		";
-		$res = $db->query($sql);
-		if (!$res || $db->has_errors()) throw new Exception("Wystąpiły błędy podczas próby zapisu wiadomości: " . implode("\n<br>", $db->get_errors()));
-		$createdId = $db->insert_id();
-		if ($createdId <= 0) throw new Exception("Nie udało się zapisać wiadomości.");
+		try {
+			$createdId = DB::getPDO()->insert('CRM_UI_MSGS', [
+				'uiTargetType' => "default_db_table_record",
+				'uiTargetName' => "{$tableName}.{$idRow}",
+				'userTargetType' => $toType,
+				'userTargetName' => $to,
+				'msg' => $msg,
+				'A_RECORD_CREATE_DATE' => "NOW()",
+				'A_RECORD_CREATE_AUTHOR' => $usrLogin,
+				'A_STATUS' => "WAITING",
+				'app_className' => "TableMsgs",
+			]);
+		} catch (Exception $e) {
+			DBG::log($e);
+			throw new Exception("Nie udało się zapisać wiadomości.");
+		}
 		return $createdId;
 	}
 
@@ -618,6 +623,7 @@ function tblMsgsLoadMoreRows(n) {
 		$msg['type'] = $msg['_raw']->msgType;
 		$msg['_read'] = ('WAITING' != $msg['_raw']->A_STATUS);
 
+		//   `uiTargetType` enum('default_db_table','default_db_table_record','after_login','everywhere') NOT NULL, // TODO: add namespace, featureID
 		// $msg['_raw']->uiTargetType => default_db_table_record
 		// $msg['_raw']->uiTargetName => TEST_PERMS.31
 		if ('default_db_table_record' !== $msg['_raw']->uiTargetType) {
@@ -635,65 +641,111 @@ function tblMsgsLoadMoreRows(n) {
 		if ($msg['_read']) return;
 
 		$usrLogin = User::getLogin();
-		$db = DB::getDB();
-		if (!$db) throw new Exception("Brak dazy danych!");
-		if ($db->has_errors()) throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
-		$sql = "update `CRM_UI_MSGS`
-			set `A_STATUS`='NORMAL'
-				, `A_RECORD_UPDATE_AUTHOR`='{$usrLogin}'
-				, `A_RECORD_UPDATE_DATE`=NOW()
-				, `actionExecutedTime`=NOW()
+		$sql = "
+			update `CRM_UI_MSGS`
+			set `A_STATUS` = 'NORMAL'
+				, `A_RECORD_UPDATE_AUTHOR` = '{$usrLogin}'
+				, `A_RECORD_UPDATE_DATE` = NOW()
+				, `actionExecutedTime` = NOW()
 			where `ID`='{$msg['_raw']->ID}'
-				and `A_STATUS`='WAITING'
-				and `A_RECORD_UPDATE_AUTHOR`=''
+				and `A_STATUS` = 'WAITING'
+				and `A_RECORD_UPDATE_AUTHOR` = ''
 				and `A_RECORD_UPDATE_DATE` is null
 				and (
-					('{$usrLogin}'!=`A_RECORD_CREATE_AUTHOR`)
-					or ('{$usrLogin}'=`A_RECORD_CREATE_AUTHOR`
-						and 'user'=`userTargetType`
-						and '{$usrLogin}'=`userTargetName`
+					('{$usrLogin}' != `A_RECORD_CREATE_AUTHOR`)
+					or ('{$usrLogin}' = `A_RECORD_CREATE_AUTHOR`
+						and 'user' = `userTargetType`
+						and '{$usrLogin}' = `userTargetName`
 					)
 				)
 		";
-		$res = $db->query($sql);
-		if (!$res || $db->has_errors()) throw new Exception("Wystąpiły błędy podczas próby zapisu wiadomości: " . implode("\n<br>", $db->get_errors()));
+		try {
+			DB::getPDO()->execSql($sql);
+		} catch (Exception $e) {
+			DBG::log($e);
+			throw new Exception("Wystąpiły błędy podczas próby zapisu wiadomości: " . $e->getMessage());
+		}
 	}
 
 	function viewMsg($msg) {
 		$usrLogin = User::getLogin();
 		$idTable = 0;
-		if (!empty($msg['tblName'])) {
-			$tblName = $msg['tblName'];
-			$idTable = ProcesHelper::getZasobTableID($tblName);
-			if (!$idTable) throw new Exception("Nie udało się odnaleźć nr tabeli '{$tblName}'");
-			$idRow = $msg['idRow'];
-			if (!$idRow) throw new Exception("Brak numeru powiązanego rekordu!");
-			$usrAcl = User::getAcl();
-			$tblAcl = $usrAcl->getTableAcl($idTable);
-		}
-		$linkBase = "index.php?_route=UserMsgs";
-		$linkBase .= "&usrLogin={$usrLogin}";
 		//$rmMsgLink = "{$linkBase}&_task=removeMsg&id={$msg['_raw']->ID}";
-		$backLink = "{$linkBase}";
-		?>
-<div class="container">
-	<h3><i class="glyphicon glyphicon-envelope"></i> <a href="<?php echo $backLink; ?>">Wiadomości <?php echo $usrLogin; ?></a>
-		&raquo; Wiadomość nr <?php echo $msg['_raw']->ID; ?>
-		<?php if ($idTable > 0) : ?>
-<!--			<br><small>wiadomość powiązana z rekordem <a href="index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=<?php echo $idTable; ?>#EDIT/<?php echo $idRow; ?>"><?php echo $idRow; ?></a>
-				z tabeli <a href="index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=<?php echo $idTable; ?>"><?php echo $tblAcl->getLabel(); ?></a>
-			</small>
--->
-			<small style="display:block; text-align:right">
-				<a style="font-size:12px; line-height:15px; vertical-align:text-bottom;" title="Edytuj rekord" href="index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=<?php echo $idTable; ?>#EDIT/<?php echo $idRow; ?>"><i class="glyphicon glyphicon-pencil"></i> Edytuj rekord <?php echo $idRow; ?></a>
-				<span style="font-size:12px; line-height:15px; vertical-align:text-bottom;">z tabeli</span>
-				<a style="font-size:12px; line-height:15px; vertical-align:text-bottom;" href="index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=<?php echo $idTable; ?>"><?php echo $tblAcl->getRawLabel(); ?></a>
-			</small>
-		<?php endif; ?>
-	</h3>
-</div>
-<?php $this->printWidgetViewMsg($msg); ?>
-<?php
+		$targetNamespace = (!empty($msg['tblName'])) ? "default_db/{$msg['tblName']}" : ""; // default_db/CRM_LISTA_ZASOBOW
+		$idRow = (!empty($msg['idRow'])) ? $msg['idRow'] : "";
+		$targetLabel = ($targetNamespace) ? $this->getOutTargetLabel($targetNamespace) : ""; // $tblAcl->getRawLabel()
+
+		echo UI::h('div', [ 'class' => "container" ], [
+			UI::h('h3', [], [
+				UI::h('i', [ 'class' => "glyphicon glyphicon-envelope" ]),
+				" ",
+				UI::h('a', [ 'href' => $this->getLink('', [ 'usrLogin' => $usrLogin ]) ], "Wiadomości {$usrLogin}"),
+				" &raquo; ",
+				" Wiadomość nr {$msg['_raw']->ID}",
+				($targetNamespace && $idRow)
+					?	UI::h('small', [ 'style' => "display:block; text-align:right" ], [
+							UI::h('a', [
+								'style' => "font-size:12px; line-height:15px; vertical-align:text-bottom",
+								'title' => "Edytuj rekord",
+								'href' => "index.php?_route=ViewTableAjax&namespace={$targetNamespace}#EDIT/{$idRow}",
+							], [
+								UI::h('i', [ 'class' => "glyphicon glyphicon-pencil" ]),
+								" Edytuj rekord {$idRow}",
+							]),
+							UI::h('span', [ 'style' => "font-size:12px; line-height:15px; vertical-align:text-bottom" ], " z tabeli "),
+							UI::h('a', [
+								'style' => "font-size:12px; line-height:15px; vertical-align:text-bottom",
+								'href' => "index.php?_route=ViewTableAjax&namespace={$targetNamespace}",
+							], $targetLabel),
+							" ",
+							$this->printMsgDropdownMenu($msg['_raw']),
+						])
+					:	''
+				,
+			]),
+		]);
+		$this->printWidgetViewMsg($msg);
+	}
+	function getOutTargetLabel($ns) {
+		try {
+			$acl = ACL::getAclByNamespace($ns);
+			return $acl->getRawLabel();
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+		return $ns;
+	}
+	function printMsgDropdownMenu($rawMsg) {
+		$isRemoved = ('DELETED' === $rawMsg->A_STATUS || 'OFF_HARD' == $rawMsg->A_STATUS);
+
+		return UI::h('div', [ 'class' => "dropdown", 'style' => "display:inline" ], [
+			UI::h('button', [ 'class' => "btn btn-xs btn-default dropdown-toggle", 'title' => "Message Menu", 'data-toggle' => "dropdown" ], [
+				UI::h('i', [ 'class' => "glyphicon glyphicon-menu-hamburger" ]),
+				" Menu",
+			]),
+			UI::h('ul', [ 'class' => "dropdown-menu dropdown-menu-right" ], [
+				UI::h('li', [], [
+					($isRemoved)
+						?	UI::h('form', [ 'method' => "POST" ], [
+								UI::h('input', [ 'type' => 'hidden', 'name' => "_postTask", 'value' => "restoreMsg" ]),
+								UI::h('input', [ 'type' => 'hidden', 'name' => "id", 'value' => $rawMsg->ID ]),
+								UI::h('button', [ 'type' => "submit", 'class' => "btn btn-link" ], [
+									UI::h('i', [ 'class' => "glyphicon glyphicon-inbox" ]),
+									" Przywróć",
+								]),
+							])
+						:	UI::h('form', [ 'method' => "POST" ], [
+								UI::h('input', [ 'type' => 'hidden', 'name' => "_postTask", 'value' => "removeMsg" ]),
+								UI::h('input', [ 'type' => 'hidden', 'name' => "id", 'value' => $rawMsg->ID ]),
+								UI::h('button', [ 'type' => "submit", 'class' => "btn btn-link", 'style' => "color:red" ], [
+									UI::h('i', [ 'class' => "glyphicon glyphicon-remove" ]),
+									" Usuń"
+								]),
+							])
+					,
+				]),
+			]),
+		]);
 	}
 
 	function printWidgetViewMsg($msg) {
@@ -1070,4 +1122,36 @@ function tblMsgsLoadMoreRows(n) {
 		SE_Layout::dol();
 	}
 
+	function removeMsgPostTask($args) {
+		try {
+			$id = V::get('id', 0, $args, 'int');
+			if ($id <= 0) throw new AlertDangerException("Missing message id!");
+			$usrLogin = V::get('usrLogin', User::getLogin(), $_REQUEST, 'word');
+			$msg = $this->_getMsg($id, $usrLogin);
+			$isRemoved = ('DELETED' === $msg['_raw']->A_STATUS || 'OFF_HARD' == $msg['_raw']->A_STATUS);
+			if ($isRemoved) throw new AlertInfoException("Wiadomość nr {$id} została usunięta wcześniej");
+			Router::getRoute('Msgs')->removeTableRecordMsg($id);
+			throw new AlertInfoException("Usunięto wiadomość nr {$id}"); // TODO: przywróć btn require global post task class
+		} catch (Exception $e) {
+			DBG::log($e);
+			S::saveUserMessage(get_class($e), $e->getMessage());
+		}
+	}
+
+	function restoreMsgPostTask($args) {
+		try {
+			$id = V::get('id', 0, $args, 'int');
+			if ($id <= 0) throw new AlertDangerException("Missing message id!");
+			$usrLogin = V::get('usrLogin', User::getLogin(), $_REQUEST, 'word');
+			$msg = $this->_getMsg($id, $usrLogin);
+			$isRemoved = ('DELETED' === $msg['_raw']->A_STATUS || 'OFF_HARD' == $msg['_raw']->A_STATUS);
+			if (!$isRemoved) throw new AlertInfoException("Wiadomość nr {$id} nie jest usunięta");
+			Router::getRoute('Msgs')->restoreTableRecordMsg($id);
+			throw new AlertInfoException("Przywrócono wiadomość nr {$id}"); // TODO: przywróć btn require global post task class
+		} catch (Exception $e) {
+			DBG::log($e);
+			S::saveUserMessage(get_class($e), $e->getMessage());
+		}
+	}
+
 }