Jelajahi Sumber

added route UserMsgs, with link in menu

Piotr Labudda 10 tahun lalu
induk
melakukan
6f19b30142
3 mengubah file dengan 739 tambahan dan 3 penghapusan
  1. 0 2
      SE/se-lib/ProcesMenu.php
  2. 0 1
      SE/se-lib/Route/TableMsgs.php
  3. 739 0
      SE/se-lib/Route/UserMsgs.php

+ 0 - 2
SE/se-lib/ProcesMenu.php

@@ -760,9 +760,7 @@ jQuery(document).ready(function() {
 			<div class="navbar-form navbar-right">
 				<div class="btn-group">
 					<button type="button" class="btn btn-link" style="margin:0; padding:6px 8px 4px 8px;"><?php S::show_session_timer(); ?></button>
-<!--
 					<a href="index.php?_route=UserMsgs" class="btn btn-link" style="margin:0; padding:6px 8px 0 8px; font-size:20px; line-height:24px;"><i class="glyphicon glyphicon-envelope"></i></a>
--->
 					<div class="btn-group">
 						<button id="ProcesMenuLoginDropdownLink" type="button" class="btn btn-link dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><?php echo User::getName(); ?> <span class="caret"></span></button>
 						<ul class="dropdown-menu" role="menu">

+ 0 - 1
SE/se-lib/Route/TableMsgs.php

@@ -345,7 +345,6 @@ function tblMsgsLoadMoreRows(n) {
 	public function _getMsgs($filterType, $tableName, $idRow, $lastMsgId = null) {
 		$lastMsgId = (int)$lastMsgId;
 		$msgsRoute = Router::getRoute('Msgs');
-		//$msgsList = $msgsRoute->getMessagesForTableRecord($tableName, $idRow);
 		$msgsList = array();
 		if (empty($tableName)) throw new Exception("No table name!");
 		$sqlWhereAddFilter = "";

+ 739 - 0
SE/se-lib/Route/UserMsgs.php

@@ -0,0 +1,739 @@
+<?php
+
+Lib::loadClass('Router');
+Lib::loadClass('RouteBase');
+Lib::loadClass('TypespecialVariable');
+Lib::loadClass('ProcesHelper');
+
+class Route_UserMsgs extends RouteBase {
+
+	var $_listLimit = 4;
+
+	public function handleAuth() {
+		if (!User::logged()) {
+			User::authByRequest();
+		}
+	}
+
+	public function defaultAction() {
+		SE_Layout::gora();
+		SE_Layout::menu();
+
+		try {
+			$usrLogin = User::getLogin();
+			//$this->menu();
+			$this->userMsgs($usrLogin);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', $e->getMessage() . ' #' . $e->getLine());
+		}
+
+		SE_Layout::dol();
+	}
+
+	public function menu() {
+		$usrLogin = User::getLogin();
+?>
+<ul>
+	<li>TODO: ...</li>
+</ul>
+<?php
+	}
+
+	public function userMsgs($usrLogin) {
+		$msgsList = $this->_getMsgs('inbox', $usrLogin);
+		$totalReadMsgs = 0;
+		$totalUnreadMsgs = 0;
+		foreach ($msgsList as $ind => $msg) {
+			if ($msg['_read']) {
+				$totalReadMsgs++;
+			} else {
+				$totalUnreadMsgs++;
+			}
+		}
+		$sentMsgsList = $this->_getMsgs('sent', $usrLogin);
+		$removedMsgsList = $this->_getMsgs('removed', $usrLogin);
+		?>
+<style type="text/css">
+.tblMsgsListItem { cursor:pointer; }
+</style>
+<div class="container">
+	<h3><i class="glyphicon glyphicon-envelope"></i> Wiadomości <code><?php echo $usrLogin; ?></code></h3>
+	<div>
+		<ul class="nav nav-tabs" role="tablist">
+<!--
+			<li>
+				<a href="#tbl-msgs-compose"><i class="glyphicon glyphicon-plus"></i> Nowa wiadomość</a>
+			</li>
+-->
+			<li role="presentation" class="active"><a href="#odebrane" aria-controls="odebrane" role="tab" data-toggle="tab">Odebrane <em>(<?php echo $totalUnreadMsgs; ?>)</em></a></li>
+			<li role="presentation"><a href="#wyslane" aria-controls="wyslane" role="tab" data-toggle="tab">Wysłane</em></a></li>
+			<li role="presentation"><a href="#kosz" aria-controls="kosz" role="tab" data-toggle="tab">Kosz</em></a></li>
+		</ul>
+		<div class="tab-content" style="margin-bottom:15px">
+			<div role="tabpanel" class="tab-pane active" id="odebrane" style="border-style:none solid solid solid; border-width:1px; border-color:#ddd;">
+				<?php $this->_printUserMsgsList('inbox', $msgsList, $usrLogin); ?>
+			</div>
+			<div role="tabpanel" class="tab-pane" id="wyslane" style="border-style:none solid solid solid; border-width:1px; border-color:#ddd;">
+				<?php $this->_printUserMsgsList('sent', $sentMsgsList, $usrLogin); ?>
+			</div>
+			<div role="tabpanel" class="tab-pane" id="kosz" style="border-style:none solid solid solid; border-width:1px; border-color:#ddd;">
+				<?php $this->_printUserMsgsList('removed', $removedMsgsList, $usrLogin); ?>
+			</div>
+		</div>
+	</div>
+<!--
+	<div class="panel panel-default" id="tbl-msgs-compose">
+		<div class="panel-heading">Wyślij nową wiadomość</div>
+		<div class="panel-body">
+			<?php if (!empty($arrorsList)) : ?>
+				<?php foreach ($arrorsList as $errMsg) : ?>
+					<div class="alert alert-danger"><?php echo $errMsg; ?></div>
+				<?php endforeach; ?>
+			<?php endif; ?>
+			<?php $this->_printMsgForm($args); ?>
+		</div>
+	</div>
+-->
+</div>
+<script>
+function tblMsgsLoadMoreRows(n) {
+	var nNode = jQuery(n),
+			lastMsgId = nNode.data('last_msg_id'),
+			listType = nNode.data('list_type')
+	;
+	nNode.blur();
+
+	function tblMsgsSetNoMoreRows(btnLoadMoreNode) {
+		btnLoadMoreNode.closest('td').css({color:'silver'}).html('Brak starszych wiadomości');
+	}
+
+	if (lastMsgId <= 0) {
+		tblMsgsSetNoMoreRows(nNode);
+	}
+
+	function tblMsgsAddMsgToList(msg, btnLoadMoreNode, listType) {
+		var tbodyNode = btnLoadMoreNode.closest('tfoot').prev('tbody'),
+				trNode = jQuery('<tr></tr>'),
+				tdIdNode = jQuery('<td></td>'),
+				tdMsgNode = jQuery('<td></td>'),
+				tdDateNode = jQuery('<td style="white-space:nowrap;"></td>'),
+				actionTask = (listType == 'inbox')? 'read' : 'view',
+				msgLink = ''
+		;
+		trNode.addClass('tblMsgsListItem');
+		if (msg['_read']) trNode.addClass('active');
+		if ('read' === actionTask || 'view' === actionTask) {
+			msgLink = '<?php echo Request::getPathUri() . 'index.php?_route=UserMsgs'; ?>';
+			msgLink += '&usrLogin=<?php echo $usrLogin; ?>';
+			msgLink += '&id=' + msg['_raw']['ID'];
+			msgLink += '&_task=' + actionTask;
+			trNode.attr('onclick', "window.location.href='" + msgLink + "'");
+		}
+
+		tdIdNode.append(msg['_raw']['ID']);
+		tdIdNode.appendTo(trNode);
+
+		tdMsgNode.append('<div style="overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">' + msg['message'] + '</div>');
+		msgMetaInfo = 'od ' + msg['_raw']['A_RECORD_CREATE_AUTHOR'] + ' do ';
+		if ('everyone' == msg['_raw']['userTargetType']) {
+			msgMetaInfo += 'wszystkich';
+		} else if ('user' == msg['_raw']['userTargetType']) {
+			msgMetaInfo += msg['_raw']['userTargetName'];
+		} else if ('group' == msg['_raw']['userTargetType']) {
+			msgMetaInfo += 'grupy ' + msg['_raw']['userTargetName'];
+		}
+		tdMsgNode.append('<div class="text-muted" style="font-style:italic;">' + msgMetaInfo + '</div>');
+		tdMsgNode.appendTo(trNode);
+
+		tdDateNode.append(msg['_raw']['A_RECORD_CREATE_DATE']);
+		if (msg['_readDate']) {
+			tdDateNode.append('<div class="text-muted" style="font-style:italic" title="Przeczytano ' + msg['_readDate'] + '">' + msg['_readDate'] + '</div>');
+		} else {
+			tdDateNode.append('<div class="text-muted" style="font-style:italic" title="Wiadomość nie została jeszcze odczytana">nieodczytana</div>');
+		}
+		tdDateNode.appendTo(trNode);
+
+		trNode.hide();
+		trNode.appendTo(tbodyNode);
+		trNode.show('slow');
+	};
+
+	jQuery.ajax({
+		data: {},
+		dataType: 'json',
+		type: "POST",
+		url: 'index.php?_route=UserMsgs&_task=loadMoreRows&listType=' + listType + '&lastMsgId=' + lastMsgId + '&usrLogin=<?php echo $usrLogin; ?>'
+	})
+	.done(function(data, textStatus, jqXHR) {
+		var listLimit = <?php echo $this->_listLimit; ?>,
+				i = 0,
+				lastMsgId = 0,
+				hasMore = false
+		;
+		if (!data || !data.msgs || !data.keysOrder) {
+			jQuery.notify('Wystąpiły błędy podczas pobierania listy wiadomości', 'error');
+			return false;
+		}
+		data.keysOrder.forEach(function(key) {
+			if (i < listLimit) {
+				lastMsgId = key;
+				tblMsgsAddMsgToList(data.msgs[key], nNode, listType);
+			} else {
+				hasMore = true;
+			}
+			i++;
+		});
+		if (!hasMore) {
+			tblMsgsSetNoMoreRows(nNode);
+		}
+		nNode.data('last_msg_id', lastMsgId);
+	})
+	.fail(function(jqXHR) {
+		if (jqXHR.responseJSON) {
+			jQuery.notify('Nie udało się pobrać listy wiadomości', 'error');
+		}
+		else {
+			var txt = jqXHR.responseText || 'Nie udało się pobrać listy wiadomości';
+			if (jqXHR.status == 404) {
+				jQuery.notify(jqXHR.responseText, 'error');
+			} else {
+				jQuery.notify(jqXHR.responseText, 'warn');
+			}
+		}
+	});
+}
+</script>
+<?php
+		//DBG::_(true, true, "_POST", $_POST, __CLASS__, __FUNCTION__, __LINE__);
+		//DBG::_(true, true, "tblAcl", $tblAcl, __CLASS__, __FUNCTION__, __LINE__);
+		//DBG::_(true, true, "record", $record, __CLASS__, __FUNCTION__, __LINE__);
+		//DBG::_(true, true, "msgsList", $msgsList, __CLASS__, __FUNCTION__, __LINE__);
+		//throw new Exception("TODO: ...");
+	}
+
+	public function loadMoreRowsAction() {
+		$usrLogin = V::get('usrLogin', '', $_GET, 'word');
+		$lastMsgId = V::get('lastMsgId', 0, $_GET, 'int');
+		$listType = V::get('listType', '', $_GET, 'word');
+		if (!$usrLogin) throw new HttpException("Wrong param login", 404);
+		if ($lastMsgId <= 0) throw new HttpException("Wrong param lastMsgId", 404);
+		if (!in_array($listType, array('inbox','sent','removed'))) throw new HttpException("Wrong param listType", 404);
+
+		$resultData = new stdClass();
+		$resultData->msgs = $this->_getMsgs($listType, $usrLogin, $lastMsgId);
+		$resultData->keysOrder = array_keys($resultData->msgs);
+		echo json_encode($resultData);
+	}
+
+	public function _printUserMsgsList($listType, $msgsList, $usrLogin) {
+		$msgsTotal = count($msgsList);
+		$listLimit = $this->_listLimit;
+		$lastMsgId = 0;
+		$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
+	}
+
+	public function _getMsgs($filterType, $usrLogin, $lastMsgId = null) {
+		$lastMsgId = (int)$lastMsgId;
+		$msgsRoute = Router::getRoute('Msgs');
+		$msgsList = array();
+		if (empty($usrLogin)) throw new Exception("No user login!");
+		$sqlWhereAddFilter = "";
+		{// TODO: fetch groups ids for another user - $usrLogin
+			$usrLogin = User::getLogin();
+			$userGroupIds = User::getGroupsIds();
+		}
+		$sqlFilerMsgsForUser = "
+			m.`userTargetType` in('everyone')
+			or (m.`userTargetType`='user' and m.`userTargetName`='{$usrLogin}')
+			or (m.`userTargetType`='group' and m.`userTargetName` in(" . implode(",", $userGroupIds) . "))
+		";
+		switch ($filterType) {
+			case 'inbox':
+				$sqlWhereAddFilter = "
+					and ({$sqlFilerMsgsForUser})
+					and m.`A_STATUS` not in('DELETED')
+				";
+				break;
+			case 'sent':
+				$sqlWhereAddFilter = "
+					and m.`A_RECORD_CREATE_AUTHOR`='{$usrLogin}'
+					and m.`A_STATUS` not in('DELETED')
+				";
+				break;
+			case 'removed':
+				$sqlWhereAddFilter = "
+					and (m.`A_RECORD_CREATE_AUTHOR`='{$usrLogin}'
+						or ({$sqlFilerMsgsForUser})
+					)
+					and m.`A_STATUS` in('DELETED')
+				";
+				break;
+			default: throw new Exception("Unknown filter type");
+		}
+		$db = DB::getDB();
+		$tableName = $db->_($tableName);
+
+		if ($lastMsgId > 0) {
+			$sqlWhereAddFilter .= "\n  and m.`ID`<{$lastMsgId}";
+		}
+		$sqlLimit = $this->_listLimit + 1;
+		$sql = "select m.*
+			from `CRM_UI_MSGS` m
+			where m.`uiTargetType`='default_db_table_record'
+		--		and m.`uiTargetName`='{$tableName}.{$idRow}'
+				{$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;
+	}
+
+	public function _validate($args) {
+		$toType = V::get('to_type', '', $args);
+		$to = V::get('to', '', $args);
+		$msg = V::get('msg', '', $args);
+		if (!in_array($toType, array('everyone', 'user', 'group'))) {
+			throw new Exception("Niedozwolony typ odbiorcy");
+		}
+		if (empty($to) && 'everyone' != $toType) {
+			throw new Exception("Proszę podać odbiorcę wiadomości");
+		}
+		if (empty($msg)) {
+			throw new Exception("Proszę podać treść wiadomości");
+		}
+	}
+
+	public function _create($args, $tableName, $idRow) {
+		$toType = V::get('to_type', '', $args);
+		$to = V::get('to', '', $args);
+		$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.");
+		return $createdId;
+	}
+
+	public function _printMsgForm($args) {
+		$toType = V::get('to_type', '', $args);
+		$to = V::get('to', '', $args);
+		$msg = V::get('msg', '', $args);
+		$listTo = array();
+		$listTo['everyone'] = 'Wszyscy';
+		$listTo['user'] = 'Użytkownik';
+		$listTo['group'] = 'Grupa';
+		$toType = (array_key_exists($toType, $listTo))? $toType : 'everyone';
+		$typeSpecialGroupId = TypespecialVariable::getInstance(-1, '__ZASOB');
+		$typeSpecialUserLogin = TypespecialVariable::getInstance(-1, '__USER_LOGIN');
+		$selectedLogin = ('user' == $toType)? $to : '';
+		$selectedGroupId = ('group' == $toType)? $to : '';
+		?>
+		<form class="form-horizontal" action="" method="post">
+			<div class="form-group">
+				<label class="col-sm-2 control-label" for="to">Do:</label>
+				<div class="col-sm-3">
+					<select name="to_type" class="form-control" onChange="return selectTblMsgsToType(this);">
+						<?php foreach ($listTo as $type => $typeLabel) : ?>
+							<option <?php echo ($type == $toType)? 'selected' : ''; ?>
+											 value="<?php echo $type; ?>"><?php echo $typeLabel; ?></option>
+						<?php endforeach; ?>
+					</select>
+				</div>
+				<div class="col-sm-7">
+					<div id="tblMsgsTo-everyone" style="<?php echo ('everyone' == $toType)? '' : 'display:none'; ?>">
+						<input name="to-everyone" type="text" class="form-control" disabled>
+					</div>
+					<div id="tblMsgsTo-group" style="<?php echo ('group' == $toType)? '' : 'display:none'; ?>">
+						<?php if ($typeSpecialGroupId) : ?>
+							<?php
+								$fldName = 'to-group';
+								$fldParams = array();
+								$fldParams['allowCreate'] = false;
+								$fldParams['ajaxDataUrlBase'] = "index.php?_route=TableMsgs&_task=typeSpecialGroupId";
+								$fldParams['placeholder'] = 'Grupa...';
+								//$fldParams['ajaxDataUrlBase'] .= "&DBG_TS=3";
+								echo $typeSpecialUserLogin->showFormItem($tblID = -1, $fldName, $selectedGroupId, $fldParams);
+							?>
+						<?php else : ?>
+							<input name="to-group" type="text" class="form-control" placeholder="Grupa">
+						<?php endif; ?>
+					</div>
+					<div id="tblMsgsTo-user" style="<?php echo ('user' == $toType)? '' : 'display:none'; ?>">
+						<?php if ($typeSpecialUserLogin) : ?>
+							<?php
+								$fldName = 'to-user';
+								$fldParams = array();
+								$fldParams['allowCreate'] = false;
+								$fldParams['ajaxDataUrlBase'] = "index.php?_route=TableMsgs&_task=typeSpecialUserLogin";
+								$fldParams['placeholder'] = 'Użytkownik...';
+								//$fldParams['ajaxDataUrlBase'] .= "&DBG_TS=3";
+								echo $typeSpecialUserLogin->showFormItem($tblID = -1, $fldName, $selectedLogin, $fldParams);
+							?>
+						<?php else : ?>
+							<input name="to-user" type="text" class="form-control" placeholder="Użytkownik">
+						<?php endif; ?>
+					</div>
+				</div>
+			</div>
+			<div class="form-group">
+				<label for="to" class="col-sm-2 control-label">Wiadomość:</label>
+				<div class="col-sm-10">
+					<textarea name="msg" class="form-control"><?php echo htmlspecialchars($msg); ?></textarea>
+				</div>
+			</div>
+			<div class="form-group">
+				<div class="col-sm-10 col-sm-offset-2">
+					<input class="btn btn-primary" type="submit" value="Wyślij">
+				</div>
+			</div>
+		</form>
+<script>
+	function selectTblMsgsToType(n) {
+		var toTypes = <?php echo json_encode(array_keys($listTo)); ?>,
+				selectedType = n.value
+		;
+		if (-1 !== toTypes.indexOf(n.value)) {
+			toTypes.forEach(function(type) {
+				if (type == selectedType) {
+					document.getElementById('tblMsgsTo-' + type).style.display = 'block';
+				} else {
+					document.getElementById('tblMsgsTo-' + type).style.display = 'none';
+				}
+			});
+		}
+	}
+</script>
+<?php
+	}
+
+	public function typeSpecialUserLoginAction() {
+		header("Content-type: application/json");
+		$typeSpecialUserId = TypespecialVariable::getInstance(-1, '__USER_LOGIN');
+		if (!$typeSpecialUserId) {
+			$jsonData = new stdClass();
+			$jsonData->message = "TypeSpecial '__USER_LOGIN' not exists";
+			echo json_encode($jsonData);
+			exit;
+		}
+
+		$query = V::get('q', '', $_REQUEST);
+		$rawRows = null;
+		$jsonData = array();
+		$queryParams = array();
+		$rows = $typeSpecialUserId->getValuesWithExports($query, $queryParams);
+		foreach ($rows as $kID => $vItem) {
+			$itemJson = new stdClass();
+			$itemJson->id = $vItem->id;
+			$itemJson->name = $vItem->param_out;
+			if (!empty($vItem->exports)) {
+				$itemJson->exports = $vItem->exports;
+			}
+			$jsonData[] = $itemJson;
+		}
+		echo json_encode($jsonData);
+	}
+
+	public function typeSpecialGroupIdAction() {
+		header("Content-type: application/json");
+		Lib::loadClass('TypespecialVariable');
+		$typeSpecialZasob = TypespecialVariable::getInstance(-1, '__ZASOB');
+		if (!$typeSpecialZasob) {
+			$jsonData = new stdClass();
+			$jsonData->message = "TypeSpecial '__ZASOB' not exists";
+			echo json_encode($jsonData);
+			exit;
+		}
+
+		$query = V::get('q', '', $_REQUEST);
+		$rawRows = null;
+		$jsonData = array();
+		$queryParams = array();
+		$queryParams['zasob_type_in'] = array('STANOWISKO', 'PODMIOT', 'DZIAL');
+		$rows = $typeSpecialZasob->getValuesWithExports($query, $queryParams);
+		DBG::_('DBG_TS', '>1', "rows({$query})", $rows, __CLASS__, __FUNCTION__, __LINE__);
+		foreach ($rows as $kID => $vItem) {
+			$itemJson = new stdClass();
+			$itemJson->id = $vItem->id;
+			$itemJson->name = $vItem->param_out;
+			if (!empty($vItem->exports)) {
+				$itemJson->exports = $vItem->exports;
+			}
+			$jsonData[] = $itemJson;
+		}
+		echo json_encode($jsonData);
+	}
+
+	public function readAction() {
+		$idMsg = V::get('id', 0, $_GET, 'int');
+		$usrLogin = V::get('usrLogin', '', $_REQUEST, 'word');
+		if ($idMsg <= 0) throw new HttpException("Wiadomość nie istnieje!", 404);
+		if (empty($usrLogin)) throw new HttpException("Błęny user login!", 404);
+
+		SE_Layout::gora();
+		SE_Layout::menu();
+
+		try {
+			$msg = $this->_getMsg($idMsg, $usrLogin);
+			$this->_markAsRead($msg);
+			$this->tableRowMsg($msg);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', $e->getMessage() . ' #' . $e->getLine());
+		}
+
+		SE_Layout::dol();
+	}
+
+	public function viewAction() {
+		$idMsg = V::get('id', 0, $_GET, 'int');
+		$usrLogin = V::get('usrLogin', 0, $_REQUEST, 'word');
+		if ($idMsg <= 0) throw new HttpException("Wiadomość nie istnieje!", 404);
+		if (empty($usrLogin)) throw new HttpException("Błęny user login", 404);
+
+		SE_Layout::gora();
+		SE_Layout::menu();
+
+		try {
+			$msg = $this->_getMsg($idMsg, $usrLogin);
+			$this->tableRowMsg($msg);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', $e->getMessage() . ' #' . $e->getLine());
+		}
+
+		SE_Layout::dol();
+	}
+
+	public function _getMsg($idMsg, $usrLogin) {
+		$msgsRoute = Router::getRoute('Msgs');
+		$msg['_raw'] = $msgsRoute->getMessage($idMsg);
+		if (!$msg['_raw']) throw new HttpException("Wiadomość nie istnieje!", 404);
+		$msg['usrLogin'] = $usrLogin;
+		$msg['message'] = $msg['_raw']->msg;
+		$msg['type'] = $msg['_raw']->msgType;
+		$msg['_read'] = ('WAITING' != $msg['_raw']->A_STATUS);
+
+		// $msg['_raw']->uiTargetType => default_db_table_record
+		// $msg['_raw']->uiTargetName => TEST_PERMS.31
+		if ('default_db_table_record' !== $msg['_raw']->uiTargetType) {
+			throw new Exception("Parse message target type error!");
+		}
+		$parts = explode('.', $msg['_raw']->uiTargetName);
+		if (2 !== count($parts)) throw new Exception("Parse message target type error!");
+		$msg['tblName'] = $parts[0];
+		$msg['idRow'] = $parts[1];
+		if (!is_numeric($msg['idRow'])) throw new Exception("Parse message target type - id row type error!");
+		return $msg;
+	}
+
+	public function _markAsRead($msg) {
+		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()
+			where `ID`='{$msg['_raw']->ID}'
+		";
+		$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()));
+	}
+
+	public function tableRowMsg($msg) {
+		$tblName = $msg['tblName'];
+		$usrLogin = $msg['usrLogin'];
+		$idTable = ProcesHelper::getZasobTableID($tblName);
+		if (!$idTable) throw new Exception("Nie udało się odnaleźć nr tabeli '{$tblName}'");
+		$idRow = $msg['idRow'];
+		$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; ?>
+		<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>
+	</h3>
+	<div class="panel panel-<?php echo $msg['type']; ?>">
+		<div class="panel-heading">
+			<h3 class="panel-title">Wiadomość wysłana przez <?php echo $msg['_raw']->A_RECORD_CREATE_AUTHOR; ?>
+				<span class="pull-right"><?php echo $msg['_raw']->A_RECORD_CREATE_DATE; ?></span></h3>
+		</div>
+		<div class="panel-body">
+			<?php echo htmlspecialchars($msg['message']); ?>
+		</div>
+		<div class="panel-footer" style="overflow:hidden">
+			<?php if (!empty($msg['_raw']->A_RECORD_UPDATE_DATE) && !empty($msg['_raw']->A_RECORD_UPDATE_AUTHOR)) : ?>
+				<em style="margin-left:20px" class="text-muted">odczytana <?php echo $msg['_raw']->A_RECORD_UPDATE_DATE; ?> przez <?php echo $msg['_raw']->A_RECORD_UPDATE_AUTHOR; ?></em>
+			<?php endif; ?>
+			<?php if (!empty($msg['_raw']->A_RECORD_DELETE_DATE) && !empty($msg['_raw']->A_RECORD_DELETE_AUTHOR)) : ?>
+				<em style="margin-left:20px" class="text-muted">usunięta <?php echo $msg['_raw']->A_RECORD_DELETE_DATE; ?> przez <?php echo $msg['_raw']->A_RECORD_DELETE_AUTHOR; ?></em>
+			<?php endif; ?>
+			<?php if ('DELETED' != $msg['_raw']->A_STATUS) : ?>
+				<a href="<?php echo $rmMsgLink; ?>" class="btn btn-xs btn-default pull-right" title="usuń wiadomość" onclick="return confirm('Czy jesteś pewien że chcesz usunąć wiadomość?');"><i class="glyphicon glyphicon-remove"></i> Usuń</a>
+			<?php endif; ?>
+		</div>
+	</div>
+</div>
+<?php
+		// TODO: odpisz
+	}
+
+	public function removeMsgAction() {
+		$idMsg = V::get('id', 0, $_GET, 'int');
+		$idMsg = V::get('id', 0, $_GET, 'int');
+		$usrLogin = V::get('usrLogin', 0, $_REQUEST, 'word');
+		if ($idMsg <= 0) throw new HttpException("Wiadomość nie istnieje!", 404);
+		if (empty($usrLogin)) throw new HttpException("Błęny user login", 404);
+
+		SE_Layout::gora();
+		SE_Layout::menu();
+
+		try {
+			$msg = $this->_getMsg($idMsg, $usrLogin);
+			$this->_removeRowMsg($msg);
+		} catch (Exception $e) {
+			SE_Layout::alert('danger', $e->getMessage() . ' #' . $e->getLine());
+			SE_Layout::dol();
+			exit;
+		}
+		?>
+<div class="container">
+	<div class="alert alert-success">
+		Wiadomość została usunięta <a class="btn btn-xs btn-default" href="index.php?_route=UserMsgs&usrLogin=<?php echo $usrLogin; ?>">wróć</a>
+	</div>
+</div>
+<?php
+		SE_Layout::dol();
+	}
+
+	public function _removeRowMsg($msg) {
+		// IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
+		if (empty($msg['_raw']) || empty($msg['_raw']->ID)) 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`='{$msg['_raw']->ID}' ";
+		$sql = "update `CRM_UI_MSGS`
+			set `A_STATUS`='DELETED'
+				, `A_RECORD_DELETE_AUTHOR`='{$usrLogin}'
+				, `A_RECORD_DELETE_DATE`=NOW()
+			where `ID`='{$msg['_raw']->ID}'
+		";
+		$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()));
+	}
+
+}