Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification;
|
||||
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
use Xentral\Modules\SystemNotification\Gateway\NotificationGateway;
|
||||
use Xentral\Modules\SystemNotification\Service\NotificationService;
|
||||
use Xentral\Modules\SystemNotification\Service\NotificationServiceInterface;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices()
|
||||
{
|
||||
return [
|
||||
'NotificationService' => 'onInitNotificationService',
|
||||
'NotificationGateway' => 'onInitNotificationGateway',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerJavascript()
|
||||
{
|
||||
$baseDir = './classes/Modules/SystemNotification/www/js/';
|
||||
|
||||
return [
|
||||
'pushjs' => [
|
||||
$baseDir . 'pushjs_1.0.8/push.min.js',
|
||||
$baseDir . 'pushjs.js',
|
||||
],
|
||||
'pushjs_serviceworker.js' => [
|
||||
$baseDir . 'pushjs_1.0.8/serviceWorker.min.js',
|
||||
],
|
||||
'noty' => [
|
||||
$baseDir . 'noty_2.4.1/jquery.noty.packaged.min.js',
|
||||
$baseDir . 'notify.js',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerStylesheets()
|
||||
{
|
||||
return [
|
||||
'notification' => [
|
||||
'./classes/Modules/SystemNotification/www/css/notification.css',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return NotificationServiceInterface
|
||||
*/
|
||||
public static function onInitNotificationService(ContainerInterface $container)
|
||||
{
|
||||
return new NotificationService($container->get('Database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return NotificationGateway
|
||||
*/
|
||||
public static function onInitNotificationGateway(ContainerInterface $container)
|
||||
{
|
||||
return new NotificationGateway($container->get('Database'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements NotificationExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ModuleExceptionInterface;
|
||||
|
||||
interface NotificationExceptionInterface extends ModuleExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Exception;
|
||||
|
||||
use RuntimeException as SplRuntimeException;
|
||||
|
||||
class RuntimeException extends SplRuntimeException implements NotificationExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Gateway;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
final class NotificationGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findByUserId($userId, $offset = 0, $limit = 500)
|
||||
{
|
||||
$sql = 'SELECT n.id, n.type, n.title, n.message, n.options_json, n.priority
|
||||
FROM notification_message AS n
|
||||
WHERE n.user_id = :user_id
|
||||
ORDER BY n.created_at ASC
|
||||
LIMIT :offset, :limit';
|
||||
$result = $this->db->fetchAll($sql, [
|
||||
'user_id' => (int)$userId,
|
||||
'offset' => (int)$offset,
|
||||
'limit' => (int)$limit,
|
||||
]);
|
||||
|
||||
if (empty($result)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param string $title
|
||||
* @param string $message
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDuplicatedMessage($userId, $title, $message)
|
||||
{
|
||||
$result = $this->db->fetchValue(
|
||||
'SELECT COUNT(n.id) AS num
|
||||
FROM notification_message AS n
|
||||
WHERE n.user_id = :user_id
|
||||
AND n.title = :title
|
||||
AND n.message = :message
|
||||
LIMIT 1',
|
||||
[
|
||||
'user_id' => (int)$userId,
|
||||
'message' => $message,
|
||||
'title' => $title,
|
||||
]
|
||||
);
|
||||
|
||||
return (int)$result > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Service;
|
||||
|
||||
use Xentral\Modules\SystemNotification\Exception\InvalidArgumentException;
|
||||
|
||||
final class NotificationMessageData
|
||||
{
|
||||
/** @var array $validMessageTypes */
|
||||
private static $validMessageTypes = [
|
||||
NotificationServiceInterface::TYPE_DEFAULT,
|
||||
NotificationServiceInterface::TYPE_NOTICE,
|
||||
NotificationServiceInterface::TYPE_SUCESS,
|
||||
NotificationServiceInterface::TYPE_WARNING,
|
||||
NotificationServiceInterface::TYPE_ERROR,
|
||||
NotificationServiceInterface::TYPE_PUSH,
|
||||
];
|
||||
|
||||
/** @var string $type */
|
||||
private $type;
|
||||
|
||||
/** @var string $title */
|
||||
private $title;
|
||||
|
||||
/** @var string|null $message */
|
||||
private $message;
|
||||
|
||||
/** @var bool $priority */
|
||||
private $priority;
|
||||
|
||||
/** @var array $options */
|
||||
private $options = [];
|
||||
|
||||
/** @var array $tags */
|
||||
private $tags = [];
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $title
|
||||
* @param string|null $message
|
||||
* @param bool $priority
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($type, $title, $message = null, $priority = false)
|
||||
{
|
||||
if (!in_array($type, self::$validMessageTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Message type "%s" is invalid. Valid types are: %s', $type, implode(', ', self::$validMessageTypes)
|
||||
));
|
||||
}
|
||||
if (empty($title)) {
|
||||
throw new InvalidArgumentException('Title is empty.');
|
||||
}
|
||||
if (mb_strlen($title) > 64) {
|
||||
throw new InvalidArgumentException(sprintf('Message title "%s" is longer than 64 characters.', $title));
|
||||
}
|
||||
|
||||
$this->type = (string)$type;
|
||||
$this->title = (string)$title;
|
||||
|
||||
$this->setMessage($message);
|
||||
$this->setPriority($priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $text
|
||||
* @param string $link
|
||||
* @param string|null $htmlId Html id attribute (<button id="{$htmlId}">)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addButton($text, $link, $htmlId = null)
|
||||
{
|
||||
if (!isset($this->options['buttons'])) {
|
||||
$this->options['buttons'] = [];
|
||||
}
|
||||
|
||||
$this->options['buttons'][] = [
|
||||
'text' => $text,
|
||||
'link' => $link,
|
||||
'id' => !empty($htmlId) ? $htmlId : null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTag($tag)
|
||||
{
|
||||
$this->tags[] = (string)$tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tags
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTags(array $tags)
|
||||
{
|
||||
foreach ($tags as $tag) {
|
||||
$this->addTag($tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isPriority()
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMessage($message)
|
||||
{
|
||||
$this->message = !empty($message) ? (string)$message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $priority
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPriority($priority)
|
||||
{
|
||||
$this->priority = (bool)$priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($property, $value)
|
||||
{
|
||||
$this->options[(string)$property] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOptions(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\SystemNotification\Exception\InvalidArgumentException;
|
||||
use Xentral\Modules\SystemNotification\Exception\RuntimeException;
|
||||
|
||||
final class NotificationService implements NotificationServiceInterface
|
||||
{
|
||||
/** @var array $validMessageTypes */
|
||||
private static $validMessageTypes = [
|
||||
self::TYPE_DEFAULT,
|
||||
self::TYPE_NOTICE,
|
||||
self::TYPE_SUCESS,
|
||||
self::TYPE_WARNING,
|
||||
self::TYPE_ERROR,
|
||||
self::TYPE_PUSH,
|
||||
];
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a notification
|
||||
*
|
||||
* @param int $userId
|
||||
* @param string $type
|
||||
* @param string $title
|
||||
* @param string|null $message
|
||||
* @param bool $priority Play sound and make notification sticky
|
||||
* @param array $options
|
||||
* @param array $tags
|
||||
*
|
||||
* @throws InvalidArgumentException|RuntimeException
|
||||
*
|
||||
* @return int|false Created Notification-ID
|
||||
*/
|
||||
public function create(
|
||||
$userId,
|
||||
$type,
|
||||
$title,
|
||||
$message = null,
|
||||
$priority = false,
|
||||
$options = [],
|
||||
$tags = []
|
||||
) {
|
||||
if (!in_array($type, self::$validMessageTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'"%s" is not a valid message type. Valid types are: %s', $type, implode(', ', self::$validMessageTypes)
|
||||
));
|
||||
}
|
||||
if (!$this->isValidUser($userId)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'User #%s is not a valid user.', $userId
|
||||
));
|
||||
}
|
||||
|
||||
// Truncate long messages
|
||||
$message = $this->truncateMessage($message);
|
||||
|
||||
// Sanitize buttons
|
||||
if (is_array($options['buttons']) && count($options['buttons']) > 0) {
|
||||
foreach ($options['buttons'] as $index => $button) {
|
||||
if (empty($button['text']) || empty($button['link'])) {
|
||||
unset($options['buttons'][$index]);
|
||||
}
|
||||
if (empty($button['id'])) {
|
||||
$options['buttons'][$index]['id'] = uniqid('notification-button-', false); // Set Html-Id attribute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create notification
|
||||
$this->db->perform(
|
||||
'INSERT INTO `notification_message` (`user_id`, `type`, `title`, `message`, `options_json`, `tags`, `priority`, `created_at`)
|
||||
VALUES (:user_id, :type, :title, :message, :options_json, :tags, :priority, NOW())',
|
||||
[
|
||||
'user_id' => (int)$userId,
|
||||
'type' => $type,
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'priority' => (int)$priority,
|
||||
'options_json' => !empty($options) ? json_encode($options) : null,
|
||||
'tags' => !empty($tags) ? $this->transformTagsArrayToString($tags) : null,
|
||||
]
|
||||
);
|
||||
$insertId = (int)$this->db->lastInsertId();
|
||||
if ($insertId === 0) {
|
||||
throw new RuntimeException('Notification message could not be created.');
|
||||
}
|
||||
|
||||
return $insertId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create push notification
|
||||
*
|
||||
* @param int $userId
|
||||
* @param string $title
|
||||
* @param string $message
|
||||
* @param bool $priority
|
||||
*
|
||||
* @return int Created ID
|
||||
*/
|
||||
public function createPushNotification($userId, $title, $message, $priority = false)
|
||||
{
|
||||
// strip_tags ist notwendig, da HTML von Browser-Benachrichtigungen nicht unterstützt wird.
|
||||
return $this->create($userId, self::TYPE_PUSH, strip_tags($title), strip_tags($message), $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param NotificationMessageData $data
|
||||
*
|
||||
* @return int|false Created ID
|
||||
*/
|
||||
public function createFromData($userId, NotificationMessageData $data)
|
||||
{
|
||||
return $this->create(
|
||||
$userId,
|
||||
$data->getType(),
|
||||
$data->getTitle(),
|
||||
$data->getMessage(),
|
||||
$data->isPriority(),
|
||||
$data->getOptions(),
|
||||
$data->getTags()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $notificationId
|
||||
* @param array $tags
|
||||
*
|
||||
* @return bool Returns true on success
|
||||
*/
|
||||
public function addTags($notificationId, array $tags)
|
||||
{
|
||||
try {
|
||||
$tagsArray = [];
|
||||
|
||||
// Fetch existing tags
|
||||
$tagsExisting = $this->db->fetchValue(
|
||||
'SELECT n.tags FROM notification_message AS n WHERE n.id = :id',
|
||||
['id' => (int)$notificationId]
|
||||
);
|
||||
if (!empty($tagsExisting)) {
|
||||
$tagsArray = $this->transformTagsStringToArray($tagsExisting);
|
||||
}
|
||||
|
||||
// Update notification
|
||||
$tagsMerged = array_merge($tagsArray, $tags);
|
||||
$tagsString = $this->transformTagsArrayToString($tagsMerged);
|
||||
$this->db->perform(
|
||||
'UPDATE notification_message SET tags = :tags WHERE id = :id',
|
||||
['id' => (int)$notificationId, 'tags' => $tagsString]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $notificationId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($notificationId)
|
||||
{
|
||||
$numRows = (int)$this->db->fetchAffected(
|
||||
'DELETE FROM notification_message WHERE id = :id LIMIT 1',
|
||||
['id' => (int)$notificationId]
|
||||
);
|
||||
|
||||
return $numRows === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete notification messages by UserID
|
||||
*
|
||||
* @param int $userId
|
||||
* @param bool $keepPriorityMessages If true, high priority messages will not be deleted
|
||||
*
|
||||
* @return int Number of deleted messages
|
||||
*/
|
||||
public function deleteByUser($userId, $keepPriorityMessages = true)
|
||||
{
|
||||
$delete = $this->db->delete()
|
||||
->from('notification_message')
|
||||
->where('user_id = ?', (int)$userId);
|
||||
|
||||
if ((bool)$keepPriorityMessages === true) {
|
||||
$delete->where('priority <> ?', 1);
|
||||
}
|
||||
|
||||
$numRows = (int)$this->db->fetchAffected(
|
||||
$delete->getStatement(),
|
||||
$delete->getBindValues()
|
||||
);
|
||||
|
||||
return $numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete notification messages by tags
|
||||
*
|
||||
* If multiple tags submitted, all tags must occur in the same message.
|
||||
*
|
||||
* @example deleteByTags(['callcenter','incomingcall'])
|
||||
*
|
||||
* @param array $tags
|
||||
* @param int $userId
|
||||
* @param bool $keepPriorityMessages If true, high priority messages will not be deleted
|
||||
*
|
||||
* @return int Number of deleted messages
|
||||
*/
|
||||
public function deleteByTags(array $tags, $userId = null, $keepPriorityMessages = true)
|
||||
{
|
||||
$delete = $this->db->delete()->from('notification_message');
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$tag = $this->normalizeTag($tag);
|
||||
$delete->where('tags LIKE ?', "%|{$tag}|%");
|
||||
}
|
||||
if ($userId !== null) {
|
||||
$delete->where('user_id = ?', (int)$userId);
|
||||
}
|
||||
if ((bool)$keepPriorityMessages === true) {
|
||||
$delete->where('priority <> ?', 1);
|
||||
}
|
||||
|
||||
$numRows = (int)$this->db->fetchAffected(
|
||||
$delete->getStatement(),
|
||||
$delete->getBindValues()
|
||||
);
|
||||
|
||||
return $numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getValidTypes()
|
||||
{
|
||||
return self::$validMessageTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidUser($userId)
|
||||
{
|
||||
// @todo @refactor Move to UserGateway
|
||||
$userCheck = (int)$this->db->fetchValue(
|
||||
'SELECT u.id FROM `user` AS u WHERE u.id = :user_id AND u.activ = 1',
|
||||
['user_id' => (int)$userId]
|
||||
);
|
||||
|
||||
return $userCheck === (int)$userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tags
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function transformTagsArrayToString(array $tags)
|
||||
{
|
||||
if (empty($tags)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
sort($tags);
|
||||
$tags = array_unique($tags);
|
||||
|
||||
$string = '|';
|
||||
foreach ($tags as $tag) {
|
||||
$tag = $this->normalizeTag($tag);
|
||||
$string .= $tag . '|';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function transformTagsStringToArray($string)
|
||||
{
|
||||
$tags = [];
|
||||
$parts = explode('|', $string);
|
||||
foreach ($parts as $tag) {
|
||||
$tag = $this->normalizeTag($tag);
|
||||
if (!empty($tag)) {
|
||||
$tags[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
sort($tags);
|
||||
$tags = array_unique($tags);
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function normalizeTag($tag)
|
||||
{
|
||||
$tag = strtolower(trim($tag));
|
||||
$tag = preg_replace('/[^a-z0-9\-]/', '', $tag); // Remove invalid chars
|
||||
$tag = preg_replace('/[-]+/', '-', $tag); // Replace multiple dashes
|
||||
|
||||
return (string)$tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $message
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function truncateMessage($message = null)
|
||||
{
|
||||
if ($message !== null && mb_strlen($message) > 1024) {
|
||||
$message = mb_substr($message, 0, 1020) . ' ...';
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $title
|
||||
* @param null|string $message
|
||||
* @param bool $priority
|
||||
* @param array $options
|
||||
* @param array $tags
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function createPushNotificationForConnectedUsers(
|
||||
$type,
|
||||
$title,
|
||||
$message = null,
|
||||
$priority = false,
|
||||
$options = [],
|
||||
$tags = []
|
||||
) {
|
||||
if (!in_array($type, self::$validMessageTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'"%s" is not a valid message type. Valid types are: %s', $type, implode(', ', self::$validMessageTypes)
|
||||
));
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO notification_message (user_id, type, title, message, tags, options_json, priority, created_at)
|
||||
SELECT u.id, :type, :title, :msg,:tags,'',:priority,NOW()
|
||||
FROM `user` AS u
|
||||
INNER JOIN useronline uo on u.id = uo.user_id AND uo.login = 1";
|
||||
|
||||
return (int)$this->db->fetchAffected($sql, [
|
||||
'title' => strip_tags($title),
|
||||
'msg' => strip_tags($message),
|
||||
'type' => $type,
|
||||
'priority' => (int)$priority,
|
||||
'options_json' => !empty($options) ? json_encode($options) : null,
|
||||
'tags' => !empty($tags) ? $this->transformTagsArrayToString($tags) : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\SystemNotification\Service;
|
||||
|
||||
interface NotificationServiceInterface
|
||||
{
|
||||
const TYPE_DEFAULT = 'default'; // White
|
||||
const TYPE_SUCESS = 'success'; // Green
|
||||
const TYPE_NOTICE = 'notice'; // Blue
|
||||
const TYPE_WARNING = 'warning'; // Yellow
|
||||
const TYPE_ERROR = 'error'; // Red
|
||||
const TYPE_PUSH = 'push'; // Browser push notificaion
|
||||
|
||||
/**
|
||||
* Create a notification
|
||||
*
|
||||
* @param int $recipientId
|
||||
* @param string $type
|
||||
* @param string $title
|
||||
* @param string|null $message
|
||||
* @param bool $priority Play sound and make notification sticky
|
||||
* @param array|null $buttons
|
||||
*
|
||||
* @return int Created Notification-ID
|
||||
*/
|
||||
public function create($recipientId, $type, $title, $message = null, $priority = false, $buttons = null);
|
||||
|
||||
/**
|
||||
* Create browser push notification
|
||||
*
|
||||
* @param int $recipientId
|
||||
* @param string $title
|
||||
* @param string $message
|
||||
* @param bool $priority
|
||||
*
|
||||
* @return int Created ID
|
||||
*/
|
||||
public function createPushNotification($recipientId, $title, $message, $priority = false);
|
||||
|
||||
/**
|
||||
* Delete notification by ID
|
||||
*
|
||||
* @param int $notificationId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($notificationId);
|
||||
|
||||
/**
|
||||
* Returns valid notification types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValidTypes();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
|
||||
#noty_topRight_layout_container {
|
||||
z-index: 100000;
|
||||
position: fixed;
|
||||
top: 65px;
|
||||
right: 65px;
|
||||
width: 300px;
|
||||
height: auto;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.notification {
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
width: 300px;
|
||||
margin: 5px 0;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
border: medium none;
|
||||
box-shadow: rgba(0, 0, 0, 0.3) 0 0 5px 0;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: #FFF;
|
||||
border-left: 10px solid transparent;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.notification .noty_bar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.notification .close-icon {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-image: url(../themes/new/images/x-icon.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 12px 12px;
|
||||
background-position: center 14px;
|
||||
}
|
||||
|
||||
.notification.sticky .close-icon,
|
||||
.notification:hover .close-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notification .noty_message {
|
||||
position: relative;
|
||||
width: auto;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.notification .noty_text {
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.notification .noty_text h6 {
|
||||
width: 92%;
|
||||
line-height: 19px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.notification .noty_text a {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification .noty_buttons {
|
||||
padding: 10px;
|
||||
padding-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.notification .noty_buttons button {
|
||||
margin: 0 6px 0 0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 5px;
|
||||
color: #48494B;
|
||||
border: 1px solid #D8D8D8;
|
||||
background-color: #FCFCFC;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.notification .noty_progress_bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: #000;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/**
|
||||
TYPE DEFAULT
|
||||
*/
|
||||
|
||||
.notification.noty_container_type_default,
|
||||
.notification.noty_container_type_alert {
|
||||
color: #48494B;
|
||||
background-color: #FCFCFC;
|
||||
border-left: 10px solid #CCC;
|
||||
}
|
||||
|
||||
.notification.noty_container_type_default .noty_buttons button,
|
||||
.notification.noty_container_type_alert .noty_buttons button {
|
||||
color: #48494B;
|
||||
border-color: #CCC;
|
||||
}
|
||||
|
||||
/**
|
||||
TYPE INFORMATION
|
||||
*/
|
||||
.notification.noty_container_type_information {
|
||||
color: #FFF;
|
||||
background-color: #42B8C4;
|
||||
border-left: 10px solid #34929B;
|
||||
}
|
||||
|
||||
.notification.noty_container_type_information .noty_buttons button {
|
||||
color: #34929B;
|
||||
border-color: #34929B;
|
||||
}
|
||||
|
||||
/**
|
||||
TYPE SUCCESS
|
||||
*/
|
||||
|
||||
.notification.noty_container_type_success {
|
||||
color: #FFF;
|
||||
background-color: #A2D624;
|
||||
border-left: 10px solid #82AA1C;
|
||||
}
|
||||
|
||||
.notification.noty_container_type_success .noty_buttons button {
|
||||
color: #82AA1C;
|
||||
border-color: #82AA1C;
|
||||
}
|
||||
|
||||
/**
|
||||
TYPE WARNING
|
||||
*/
|
||||
|
||||
.notification.noty_container_type_warning {
|
||||
color: #FFF;
|
||||
background-color: #F0A52E;
|
||||
border-left: 10px solid #BE8024;
|
||||
}
|
||||
|
||||
.notification.noty_container_type_warning .noty_buttons button {
|
||||
color: #BE8024;
|
||||
border-color: #BE8024;
|
||||
}
|
||||
|
||||
/**
|
||||
TYPE ERROR
|
||||
*/
|
||||
|
||||
.notification.noty_container_type_error {
|
||||
color: #FFF;
|
||||
background-color: #FD5653;
|
||||
border-left: 10px solid #C84441;
|
||||
}
|
||||
|
||||
.notification.noty_container_type_error .noty_buttons button {
|
||||
color: #C84441;
|
||||
border-color: #C84441;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,662 @@
|
||||
/**
|
||||
* @example Notify.create('Normale Nachricht');
|
||||
* @example Notify.create('Fehler Benachrichtgung', 'error', true);
|
||||
*/
|
||||
var Notify = function ($, PushJS) {
|
||||
'use strict';
|
||||
|
||||
var me = {
|
||||
|
||||
validTypes: ['default', 'notice', 'success', 'warning', 'error', 'push'],
|
||||
|
||||
settings: {
|
||||
storageKeyPrefix: 'notification_',
|
||||
storageKeyProgressBar: 'notification_progressbar'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
layout: 'topRight',
|
||||
theme: 'notification',
|
||||
maxVisible: 5,
|
||||
timeout: 10000,
|
||||
progressBar: true,
|
||||
animation: {
|
||||
open: {height: 'toggle'},
|
||||
close: {height: 'toggle'},
|
||||
easing: 'swing',
|
||||
speed: 250
|
||||
},
|
||||
closeWith: [], // Überschreiben
|
||||
template: '<div class="noty_message noselect"><div class="noty_text"></div><div class="close-icon"></div></div>'
|
||||
},
|
||||
|
||||
init: function () {
|
||||
// Notifications nicht in IFrames anzeigen
|
||||
if (me.isIframe()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Eigene Default-Einstellungen in Noty-Defaults integrieren
|
||||
$.noty.defaults = $.extend({}, $.noty.defaults, me.defaults);
|
||||
$.noty.defaults.callback.onClose = function () {
|
||||
me.closeNotificationInOtherTabs(this.options.id);
|
||||
};
|
||||
|
||||
// Init abbrechen, wenn in Beleg-Positionen oder Positionen-Popup
|
||||
var action = $('body').data('action');
|
||||
if (typeof action === 'undefined' || action === 'positionen' || action === 'positioneneditpopup') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wenn Seite geladen wird > Geöffnete Benachrichtigungen aus LocalStorage wiederherstellen
|
||||
me.restoreFromLocalStorage();
|
||||
|
||||
// Auf Änderungen im LocalStorage horchen
|
||||
window.addEventListener('storage', me.storageHandler, false);
|
||||
|
||||
$(document).on('click', '.notification .close-icon', function () {
|
||||
var notiId = $(this).parents('.noty_bar').prop('id');
|
||||
me.close(notiId);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
has: function (key) {
|
||||
return $.noty.get(key) !== false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
*
|
||||
* @return {object} noty-Objekt
|
||||
*/
|
||||
get: function (key) {
|
||||
return $.noty.get(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string[]}
|
||||
*/
|
||||
keys: function () {
|
||||
return Object.keys($.noty.store);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string|null} type [default|notice|success|warning|error|push]
|
||||
* @param {string|null} title
|
||||
* @param {string|null} message
|
||||
* @param {boolean|null} hasPriority
|
||||
* @param {object|null} options
|
||||
*/
|
||||
create: function (type, title, message, hasPriority, options) {
|
||||
if (typeof options !== 'object' || options === null) {
|
||||
options = {};
|
||||
}
|
||||
var data = options;
|
||||
|
||||
if (typeof type === 'undefined' || type === null) {
|
||||
type = 'default';
|
||||
}
|
||||
if (me.validTypes.indexOf(type) === -1) {
|
||||
type = 'default';
|
||||
}
|
||||
if (typeof title === 'undefined' || title === null) {
|
||||
title = '';
|
||||
}
|
||||
if (typeof message === 'undefined' || message === null) {
|
||||
message = '';
|
||||
}
|
||||
if (typeof hasPriority === 'undefined' || hasPriority === null) {
|
||||
hasPriority = false;
|
||||
}
|
||||
if (title === '' && message === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPriority === true) {
|
||||
me.playSound();
|
||||
data.progressBar = false;
|
||||
data.timeout = false; // Sticky machen
|
||||
data.sticky = true;
|
||||
data.force = true; // An den Anfang setzen
|
||||
}
|
||||
|
||||
data.text = '';
|
||||
if (title !== '') {
|
||||
data.text += '<h6>' + title + '</h6>';
|
||||
}
|
||||
if (message !== '') {
|
||||
data.text += message;
|
||||
}
|
||||
|
||||
data.type = type;
|
||||
|
||||
// Buttons aufbereiten
|
||||
if (typeof data.buttons !== 'undefined' && typeof data.buttons === 'object') {
|
||||
data.buttons.forEach(function (button) {
|
||||
if (typeof button.text === 'undefined' || typeof button.link === 'undefined') {
|
||||
console.warn('Could not create Notify button. Required property \'text\' oder \'link\' is missing');
|
||||
return;
|
||||
}
|
||||
if (typeof button.addClass === 'undefined') {
|
||||
button.addClass = 'btn notification-button';
|
||||
} else {
|
||||
button.addClass += ' btn notification-button';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data.type === 'push') {
|
||||
me.createPushNotification(title, message, hasPriority);
|
||||
} else {
|
||||
me.createFromData(data);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung erzeugen
|
||||
*
|
||||
* @param {object} data
|
||||
*/
|
||||
createFromData: function (data) {
|
||||
// ID, zur Wiedererkennung über alle Tabs/Fenster, generieren und zuweisen
|
||||
if (typeof data.id === 'undefined' || data.id === null) {
|
||||
data.id = me.generateRandomId();
|
||||
}
|
||||
if (typeof data.timestamp === 'undefined' || data.timestamp === null) {
|
||||
data.timestamp = Date.now();
|
||||
}
|
||||
if (typeof data.type === 'undefined') {
|
||||
data.type = 'default';
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'default':
|
||||
data.type = 'alert';
|
||||
break;
|
||||
case 'notice':
|
||||
data.type = 'information';
|
||||
break;
|
||||
case 'push':
|
||||
return;
|
||||
}
|
||||
|
||||
if (me.has(data.id)) {
|
||||
// Es gibt schon eine Notification mit dieser ID > Notification aktualisieren
|
||||
me.updateNotificationInOwnTab(data.id, data);
|
||||
me.updateNotificationInOtherTabs(data.id, data);
|
||||
} else {
|
||||
// Neue Notification anlegen
|
||||
me.createNotificationInOwnTab(data.id, data);
|
||||
me.createNotificationInOtherTabs(data.id, data);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung schließen
|
||||
*
|
||||
* @param {string} key
|
||||
*/
|
||||
close: function (key) {
|
||||
me.closeNotificationInOwnTab(key);
|
||||
me.closeNotificationInOtherTabs(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Alle Benachrichtigungen schließen
|
||||
*/
|
||||
closeAll: function () {
|
||||
me.closeAllNotificationsInOwnTab();
|
||||
me.closeAllNotificationsInOtherTabs();
|
||||
},
|
||||
|
||||
/* ------\/------ Private Methoden ------\/------ */
|
||||
|
||||
|
||||
/**
|
||||
* Geöffnete Benachrichtigungen wiederherstellen
|
||||
*/
|
||||
restoreFromLocalStorage: function () {
|
||||
var restored = me.collectFromLocalStorage();
|
||||
|
||||
// Zeitliche Reihenfolge wiederherstellen
|
||||
restored.sort(function (a, b) {
|
||||
return a.timestamp - b.timestamp;
|
||||
});
|
||||
|
||||
// Benachrichtigungen erzeugen
|
||||
restored.forEach(function (data) {
|
||||
me.createNotificationInOwnTab(data.id, data);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigungen aus LocalStorage holen
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
collectFromLocalStorage: function () {
|
||||
var notifications = [];
|
||||
|
||||
for (var key in localStorage) {
|
||||
if (key === me.settings.storageKeyProgressBar) {
|
||||
continue;
|
||||
}
|
||||
if (key.substr(0, 13) !== me.settings.storageKeyPrefix) {
|
||||
continue;
|
||||
}
|
||||
if (localStorage.hasOwnProperty(key)) {
|
||||
var store = localStorage.getItem(key);
|
||||
var data = JSON.parse(store);
|
||||
|
||||
// Push-Benachrichtigungen nicht wiederherstellen
|
||||
if (typeof data.type !== 'undefined' && data.type === 'push') {
|
||||
continue;
|
||||
}
|
||||
|
||||
notifications.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
return notifications;
|
||||
},
|
||||
|
||||
/**
|
||||
* Ton abspielen
|
||||
*/
|
||||
playSound: function () {
|
||||
try {
|
||||
var bell = new Audio('./sound/pling.mp3');
|
||||
bell.play();
|
||||
} catch (e) {
|
||||
// Sound abspielen funktioniert auf neueren Chromes nicht mehr:
|
||||
// https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung im eigenen Fenster/Tab erstellen
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {object} data
|
||||
*/
|
||||
createNotificationInOwnTab: function (key, data) {
|
||||
if (typeof key === 'string' && typeof data === 'object') {
|
||||
var item = noty(data);
|
||||
if (data.sticky === true) {
|
||||
item.$bar.addClass('sticky');
|
||||
}
|
||||
|
||||
// Events für Fortschrittsbalken
|
||||
if (item.$progressBar && item.options.progressBar) {
|
||||
item.$bar.on('mouseenter', function () {
|
||||
me.resetProgressBarInOtherTabs(item.options.id);
|
||||
});
|
||||
item.$bar.on('mouseleave', function () {
|
||||
me.startProgressBarInOtherTabs(item.options.id);
|
||||
});
|
||||
}
|
||||
|
||||
// Buttons wiederherstellen
|
||||
if (typeof item.options.buttons !== 'undefined' && typeof item.options.buttons === 'object') {
|
||||
item.options.buttons.forEach(function (button) {
|
||||
|
||||
// Data-Attribute wiederherstellen
|
||||
var $button = $('#' + button.id);
|
||||
$.each(button, function (property, value) {
|
||||
if (property.substr(0, 5) !== 'data-') {
|
||||
return;
|
||||
}
|
||||
var dataName = property.substr(5);
|
||||
$button.data(dataName, value);
|
||||
});
|
||||
|
||||
// onClick-Methode wiederherstellen
|
||||
button.onClick = function ($noty) {
|
||||
var event = jQuery.Event('notification-button:clicked');
|
||||
$(document).trigger(event, button);
|
||||
|
||||
if (!event.isDefaultPrevented()) {
|
||||
$noty.close();
|
||||
window.location.href = button.link;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Custom Event feuern
|
||||
$(document).trigger('notification:created', data);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung in allen anderen Fenstern/Tabs erstellen
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {object} data
|
||||
*/
|
||||
createNotificationInOtherTabs: function (key, data) {
|
||||
if (typeof key === 'string' && typeof data === 'object') {
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Browser-Benachrichtigung erzeugen
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {string|null} message
|
||||
* @param {boolean} hasPriority
|
||||
*/
|
||||
createPushNotification: function (title, message, hasPriority) {
|
||||
if (typeof PushJS === 'undefined') {
|
||||
throw 'push.js wurde nicht gefunden!';
|
||||
}
|
||||
if (typeof title === 'undefined' || title === null) {
|
||||
message = '';
|
||||
}
|
||||
if (typeof message === 'undefined' || message === null) {
|
||||
message = '';
|
||||
}
|
||||
if (typeof hasPriority === 'undefined') {
|
||||
hasPriority = false;
|
||||
}
|
||||
if (title === '' && message === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = {
|
||||
icon: './js/pushjs/icon.png',
|
||||
onClick: function () {
|
||||
window.focus();
|
||||
this.close();
|
||||
}
|
||||
};
|
||||
if (message !== '') {
|
||||
data.body = message;
|
||||
}
|
||||
if (hasPriority === false) {
|
||||
data.tag = 'default';
|
||||
}
|
||||
|
||||
// Nicht-Prio-Nachrichten löschen
|
||||
// (Prio-Nachrichten werden gestacked)
|
||||
PushJS.close('default');
|
||||
|
||||
// Push-Nachricht erzeugen
|
||||
PushJS.create(title, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Vorhandene Benachrichtigung aktualisieren
|
||||
*
|
||||
* @param {string} notifyId ID der Notification
|
||||
* @param {object} notifyData
|
||||
*/
|
||||
updateNotificationInOwnTab: function (notifyId, notifyData) {
|
||||
me.updateNotificationType(notifyId, notifyData.type);
|
||||
me.updateNotificationText(notifyId, notifyData.text);
|
||||
// @todo me.updateNotificationButtons(notifyId, notifyData.buttons);
|
||||
},
|
||||
|
||||
/**
|
||||
* Vorhandene Benachrichtigung aktualisieren
|
||||
*
|
||||
* @param {string} key ID der Notification
|
||||
* @param {object} data
|
||||
*/
|
||||
updateNotificationInOtherTabs: function (key, data) {
|
||||
if (typeof key === 'string' && typeof data === 'object') {
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung im eigenen Fenster/Tab schließen
|
||||
*
|
||||
* @param {string} key
|
||||
*/
|
||||
closeNotificationInOwnTab: function (key) {
|
||||
if (typeof key === 'undefined') {
|
||||
return;
|
||||
}
|
||||
$.noty.close(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Benachrichtigung in allen anderen Fenstern/Tabs schließen
|
||||
*
|
||||
* @param {string} key
|
||||
*/
|
||||
closeNotificationInOtherTabs: function (key) {
|
||||
localStorage.removeItem(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* Alle Benachrichtigungen im eigenen Fenster/Tab schließen
|
||||
*/
|
||||
closeAllNotificationsInOwnTab: function () {
|
||||
$.noty.closeAll();
|
||||
},
|
||||
|
||||
/**
|
||||
* Alle Benachrichtigungen in allen anderen Fenstern/Tabs schließen
|
||||
*/
|
||||
closeAllNotificationsInOtherTabs: function () {
|
||||
for (var key in localStorage) {
|
||||
if (key.substr(0, 13) !== me.settings.storageKeyPrefix) {
|
||||
continue;
|
||||
}
|
||||
if (localStorage.hasOwnProperty(key)) {
|
||||
me.closeNotificationInOtherTabs(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Text einer vorhandenen Benachrichtigung aktualisieren
|
||||
*
|
||||
* @param {string} notifyId
|
||||
* @param {string} text
|
||||
*/
|
||||
updateNotificationText: function(notifyId, text) {
|
||||
var existing = me.get(notifyId);
|
||||
if (existing === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
existing.$message.find('.noty_text').html(text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Typ einer vorhandenen Benachrichtigung aktualisieren
|
||||
*
|
||||
* @param {string} notifyId
|
||||
* @param {string} type
|
||||
*/
|
||||
updateNotificationType: function(notifyId, type) {
|
||||
var existing = me.get(notifyId);
|
||||
if (existing === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newOuterClassName = 'noty_container_type_' + type;
|
||||
var $outer = existing.$bar;
|
||||
if (!$outer.hasClass(newOuterClassName)) {
|
||||
var classList = $outer.attr('class').split(/\s+/);
|
||||
$.each(classList, function(index, className) {
|
||||
if (className.substring(0, 20) === 'noty_container_type_') {
|
||||
$outer.removeClass(className);
|
||||
}
|
||||
});
|
||||
$outer.addClass(newOuterClassName);
|
||||
}
|
||||
|
||||
var newInnerClassName = 'noty_type_' + type;
|
||||
var $inner = existing.$bar.find('.noty_bar');
|
||||
if (!$inner.hasClass(newInnerClassName)) {
|
||||
var innerClasses = $inner.attr('class').split(/\s+/);
|
||||
$.each(innerClasses, function (index, className) {
|
||||
if (className.substring(0, 10) === 'noty_type_') {
|
||||
$inner.removeClass(className);
|
||||
}
|
||||
});
|
||||
$inner.addClass(newInnerClassName);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fortschrittsbalken im eigenen Tab/Fenster zurücksetzen
|
||||
*
|
||||
* @param {string} notifyId
|
||||
*/
|
||||
resetProgressBarInOwnTab: function (notifyId) {
|
||||
var $noty = $.noty.get(notifyId);
|
||||
if (typeof $noty !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Nicht alle Benachrichtigungen haben einen Fortschrittsbalken
|
||||
if ($noty.options.progressBar && $noty.$progressBar) {
|
||||
$noty.dequeueClose();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fortschrittsbalken im eigenen Tab/Fenster wieder starten
|
||||
*
|
||||
* @param {string} notifyId
|
||||
*/
|
||||
startProgressBarInOwnTab: function (notifyId) {
|
||||
var $noty = $.noty.get(notifyId);
|
||||
if (typeof $noty !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Nicht alle Benachrichtigungen haben einen Fortschrittsbalken
|
||||
if ($noty.options.progressBar && $noty.$progressBar) {
|
||||
$noty.queueClose($noty.options.timeout);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fortschrittsbalken in anderen Tabs/Fenstern zurücksetzen
|
||||
*
|
||||
* @param {string} notifyId
|
||||
*/
|
||||
resetProgressBarInOtherTabs: function (notifyId) {
|
||||
var data = {
|
||||
id: notifyId,
|
||||
date: Date.now(),
|
||||
action: 'reset'
|
||||
};
|
||||
|
||||
localStorage.setItem(me.settings.storageKeyProgressBar, JSON.stringify(data));
|
||||
},
|
||||
|
||||
/**
|
||||
* Fortschrittsbalken in anderen Tabs/Fenstern wieder starten
|
||||
*
|
||||
* @param {string} notifyId
|
||||
*/
|
||||
startProgressBarInOtherTabs: function (notifyId) {
|
||||
var data = {
|
||||
id: notifyId,
|
||||
date: Date.now(),
|
||||
action: 'start'
|
||||
};
|
||||
|
||||
localStorage.setItem(me.settings.storageKeyProgressBar, JSON.stringify(data));
|
||||
},
|
||||
|
||||
/**
|
||||
* Horcht auf Änderungen im LocalStorage
|
||||
*
|
||||
* @param {StorageEvent} e
|
||||
*/
|
||||
storageHandler: function (e) {
|
||||
// LocalStorage wurde komplett geleert
|
||||
if (typeof e === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fortschrittsbalken zurücksetzen/neustarten
|
||||
if (e.key === me.settings.storageKeyProgressBar) {
|
||||
if (e.newValue === null) {
|
||||
return;
|
||||
}
|
||||
var progressData = JSON.parse(e.newValue);
|
||||
if (progressData.action === 'reset') {
|
||||
me.resetProgressBarInOwnTab(progressData.id);
|
||||
}
|
||||
if (progressData.action === 'start') {
|
||||
me.startProgressBarInOwnTab(progressData.id);
|
||||
}
|
||||
|
||||
localStorage.removeItem(me.settings.storageKeyProgressBar);
|
||||
return;
|
||||
}
|
||||
|
||||
// Nur auf bestimmten Key horchen
|
||||
if (e.key.substr(0, 13) !== me.settings.storageKeyPrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
// LocalStorage(-Key) wurde gelöscht > Notification schließen
|
||||
if (e.newValue === null) {
|
||||
me.close(e.key);
|
||||
return;
|
||||
}
|
||||
|
||||
var received = JSON.parse(e.newValue);
|
||||
if (received === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Daten wurden empfangen
|
||||
if (typeof received === 'object' && typeof received.id === 'string') {
|
||||
if (me.has(received.id)) {
|
||||
// Vorhandene Notification aktualisieren
|
||||
me.updateNotificationInOwnTab(received.id, received);
|
||||
} else {
|
||||
// Notification erstellen
|
||||
me.createNotificationInOwnTab(received.id, received);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Zufällige ID generieren
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
generateRandomId: function () {
|
||||
return me.settings.storageKeyPrefix + Math.floor(Math.random() * Math.floor(9999999999));
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
isIframe: function () {
|
||||
return window.location !== window.parent.location;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
has: me.has,
|
||||
get: me.get,
|
||||
keys: me.keys,
|
||||
init: me.init,
|
||||
create: me.create,
|
||||
createFromData: me.createFromData,
|
||||
close: me.close,
|
||||
closeAll: me.closeAll
|
||||
};
|
||||
|
||||
}(jQuery, Push);
|
||||
|
||||
$(document).ready(Notify.init);
|
||||
File diff suppressed because it is too large
Load Diff
+2
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
$(document).ready(function () {
|
||||
Push.config({serviceWorker: './www/cache/pushjs_serviceworker.js'});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
"use strict";function isFunction(obj){return obj&&{}.toString.call(obj)==="[object Function]"}function runFunctionString(funcStr){if(funcStr.trim().length>0){var func=new Function(funcStr);if(isFunction(func)){func()}}}self.addEventListener("message",function(event){self.client=event.source});self.onnotificationclose=function(event){runFunctionString(event.notification.data.onClose);self.client.postMessage(JSON.stringify({id:event.notification.data.id,action:"close"}))};self.onnotificationclick=function(event){var link,origin,href;if(typeof event.notification.data.link!=="undefined"&&event.notification.data.link!==null){origin=event.notification.data.origin;link=event.notification.data.link;href=origin.substring(0,origin.indexOf("/",8))+"/";if(link[0]==="/"){link=link.length>1?link.substring(1,link.length):""}event.notification.close();event.waitUntil(clients.matchAll({type:"window"}).then(function(clientList){var client,full_url;for(var i=0;i<clientList.length;i++){client=clientList[i];full_url=href+link;if(full_url[full_url.length-1]!=="/"&&client.url[client.url.length-1]==="/"){full_url+="/"}if(client.url===full_url&&"focus"in client){return client.focus()}}if(clients.openWindow){return clients.openWindow("/"+link)}}).catch(function(error){throw new Error("A ServiceWorker error occurred: "+error.message)}))}runFunctionString(event.notification.data.onClick)};
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["src/serviceWorker.js"],"names":["isFunction","obj","toString","call","runFunctionString","funcStr","trim","length","func","Function","self","addEventListener","event","client","source","onnotificationclose","notification","data","onClose","postMessage","JSON","stringify","id","action","onnotificationclick","link","origin","href","substring","indexOf","close","waitUntil","clients","matchAll","type","then","clientList","full_url","i","url","focus","openWindow","catch","error","Error","message","onClick"],"mappings":"AAAA,aAEA,SAASA,WAAWC,KAChB,OAAOA,QAAUC,SAASC,KAAKF,OAAS,oBAG5C,SAASG,kBAAkBC,SACvB,GAAIA,QAAQC,OAAOC,OAAS,EAAG,CAC3B,IAAIC,KAAO,IAAIC,SAASJ,SACxB,GAAIL,WAAWQ,MAAO,CAClBA,SAKZE,KAAKC,iBAAiB,UAAW,SAASC,OACtCF,KAAKG,OAASD,MAAME,SAGxBJ,KAAKK,oBAAsB,SAASH,OAChCR,kBAAkBQ,MAAMI,aAAaC,KAAKC,SAG1CR,KAAKG,OAAOM,YACRC,KAAKC,WACDC,GAAIV,MAAMI,aAAaC,KAAKK,GAC5BC,OAAQ,YAKpBb,KAAKc,oBAAsB,SAASZ,OAChC,IAAIa,KAAMC,OAAQC,KAElB,UACWf,MAAMI,aAAaC,KAAKQ,OAAS,aACxCb,MAAMI,aAAaC,KAAKQ,OAAS,KACnC,CACEC,OAASd,MAAMI,aAAaC,KAAKS,OACjCD,KAAOb,MAAMI,aAAaC,KAAKQ,KAC/BE,KAAOD,OAAOE,UAAU,EAAGF,OAAOG,QAAQ,IAAK,IAAM,IAGrD,GAAIJ,KAAK,KAAO,IAAK,CACjBA,KAAOA,KAAKlB,OAAS,EAAIkB,KAAKG,UAAU,EAAGH,KAAKlB,QAAU,GAG9DK,MAAMI,aAAac,QAGnBlB,MAAMmB,UACFC,QACKC,UACGC,KAAM,WAETC,KAAK,SAASC,YACX,IAAIvB,OAAQwB,SAEZ,IAAK,IAAIC,EAAI,EAAGA,EAAIF,WAAW7B,OAAQ+B,IAAK,CACxCzB,OAASuB,WAAWE,GACpBD,SAAWV,KAAOF,KAGlB,GACIY,SAASA,SAAS9B,OAAS,KAAO,KAClCM,OAAO0B,IAAI1B,OAAO0B,IAAIhC,OAAS,KAAO,IACxC,CACE8B,UAAY,IAGhB,GAAIxB,OAAO0B,MAAQF,UAAY,UAAWxB,OAAQ,CAC9C,OAAOA,OAAO2B,SAItB,GAAIR,QAAQS,WAAY,CACpB,OAAOT,QAAQS,WAAW,IAAMhB,SAGvCiB,MAAM,SAASC,OACZ,MAAM,IAAIC,MACN,mCAAqCD,MAAME,YAM/DzC,kBAAkBQ,MAAMI,aAAaC,KAAK6B"}
|
||||
Reference in New Issue
Block a user