Add new paradigm classes for notify
- Create BaseDepository class - Create Entity, Collection, Factory and Depository classes - Create FormattedNotification Entity, Collection and Factory to remove business logic from Notify repository - Create new NotificationCreationIntercepted exception to allow addons to cancel notification creation - Remove unused frio notifications/notify.tpl template
This commit is contained in:
parent
810699b454
commit
1b4e3564a5
|
@ -22,12 +22,11 @@
|
||||||
namespace Friendica;
|
namespace Friendica;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Collection classes inheriting from this abstract class are meant to represent a list of database record.
|
* The Collection classes inheriting from this class are meant to represent a list of structured objects of a single type.
|
||||||
* The associated model class has to be provided in the child classes.
|
|
||||||
*
|
*
|
||||||
* Collections can be used with foreach(), accessed like an array and counted.
|
* Collections can be used with foreach(), accessed like an array and counted.
|
||||||
*/
|
*/
|
||||||
abstract class BaseCollection extends \ArrayIterator
|
class BaseCollection extends \ArrayIterator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* This property is used with paginated results to hold the total number of items satisfying the paginated request.
|
* This property is used with paginated results to hold the total number of items satisfying the paginated request.
|
||||||
|
@ -115,4 +114,14 @@ abstract class BaseCollection extends \ArrayIterator
|
||||||
{
|
{
|
||||||
return new static(array_filter($this->getArrayCopy(), $callback, $flag));
|
return new static(array_filter($this->getArrayCopy(), $callback, $flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the orders of the elements in the collection
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function reverse(): BaseCollection
|
||||||
|
{
|
||||||
|
return new static(array_reverse($this->getArrayCopy()), $this->getTotalCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\Network\HTTPException\NotFoundException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depositories are meant to store and retrieve Entities from the database.
|
||||||
|
*
|
||||||
|
* The reason why there are methods prefixed with an underscore is because PHP doesn't support generic polymorphism
|
||||||
|
* which means we can't direcly overload base methods and make parameters more strict (from a parent class to a child
|
||||||
|
* class for example)
|
||||||
|
*
|
||||||
|
* Similarly, we can't make an overloaded method return type more strict until we only support PHP version 7.4 but this
|
||||||
|
* is less pressing.
|
||||||
|
*/
|
||||||
|
abstract class BaseDepository
|
||||||
|
{
|
||||||
|
const LIMIT = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string This should be set to the main database table name the depository is using
|
||||||
|
*/
|
||||||
|
protected static $table_name;
|
||||||
|
|
||||||
|
/** @var Database */
|
||||||
|
protected $db;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
/** @var ICanCreateFromTableRow */
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
public function __construct(Database $database, LoggerInterface $logger, ICanCreateFromTableRow $factory)
|
||||||
|
{
|
||||||
|
$this->db = $database;
|
||||||
|
$this->logger = $logger;
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $condition
|
||||||
|
* @param array $params
|
||||||
|
* @return BaseCollection
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function _select(array $condition, array $params = []): BaseCollection
|
||||||
|
{
|
||||||
|
$rows = $this->db->selectToArray(static::$table_name, [], $condition, $params);
|
||||||
|
|
||||||
|
$Entities = new BaseCollection();
|
||||||
|
foreach ($rows as $fields) {
|
||||||
|
$Entities[] = $this->factory->createFromTableRow($fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $condition
|
||||||
|
* @param array $params
|
||||||
|
* @return BaseEntity
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
protected function _selectOne(array $condition, array $params = []): BaseEntity
|
||||||
|
{
|
||||||
|
$fields = $this->db->selectFirst(static::$table_name, [], $condition, $params);
|
||||||
|
if (!$this->db->isResult($fields)) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->factory->createFromTableRow($fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $condition
|
||||||
|
* @param array $params
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function count(array $condition, array $params = []): int
|
||||||
|
{
|
||||||
|
return $this->db->count(static::$table_name, $condition, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $condition
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function exists(array $condition): bool
|
||||||
|
{
|
||||||
|
return $this->db->exists(static::$table_name, $condition);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Capabilities;
|
||||||
|
|
||||||
|
use Friendica\BaseEntity;
|
||||||
|
|
||||||
|
interface ICanCreateFromTableRow
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the correcponding Entity given a table row record
|
||||||
|
*
|
||||||
|
* @param array $row
|
||||||
|
* @return BaseEntity
|
||||||
|
*/
|
||||||
|
public function createFromTableRow(array $row);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Collection;
|
||||||
|
|
||||||
|
use Friendica\BaseCollection;
|
||||||
|
use Friendica\Navigation\Notifications\ValueObject;
|
||||||
|
|
||||||
|
class FormattedNotifications extends BaseCollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return ValueObject\FormattedNotification
|
||||||
|
*/
|
||||||
|
public function current(): ValueObject\FormattedNotification
|
||||||
|
{
|
||||||
|
return parent::current();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Collection;
|
||||||
|
|
||||||
|
use Friendica\BaseCollection;
|
||||||
|
use Friendica\Navigation\Notifications\Entity;
|
||||||
|
|
||||||
|
class Notifies extends BaseCollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return Entity\Notify
|
||||||
|
*/
|
||||||
|
public function current(): Entity\Notify
|
||||||
|
{
|
||||||
|
return parent::current();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSeen(): Notifies
|
||||||
|
{
|
||||||
|
return $this->map(function (Entity\Notify $Notify) {
|
||||||
|
$Notify->setSeen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Depository;
|
||||||
|
|
||||||
|
use Friendica\BaseDepository;
|
||||||
|
use Friendica\Core\Hook;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\Navigation\Notifications\Collection;
|
||||||
|
use Friendica\Navigation\Notifications\Entity;
|
||||||
|
use Friendica\Navigation\Notifications\Exception;
|
||||||
|
use Friendica\Navigation\Notifications\Factory;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class Notify extends BaseDepository
|
||||||
|
{
|
||||||
|
/** @var Factory\Notify */
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
protected static $table_name = 'notify';
|
||||||
|
|
||||||
|
public function __construct(Database $database, LoggerInterface $logger, Factory\Notify $factory = null)
|
||||||
|
{
|
||||||
|
parent::__construct($database, $logger, $factory ?? new Factory\Notify($logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $condition
|
||||||
|
* @param array $params
|
||||||
|
* @return Entity\Notify
|
||||||
|
* @throws HTTPException\NotFoundException
|
||||||
|
*/
|
||||||
|
private function selectOne(array $condition, array $params = []): Entity\Notify
|
||||||
|
{
|
||||||
|
return parent::_selectOne($condition, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function select(array $condition, array $params = []): Collection\Notifies
|
||||||
|
{
|
||||||
|
return new Collection\Notifies(parent::_select($condition, $params)->getArrayCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countForUser($uid, array $condition, array $params = []): int
|
||||||
|
{
|
||||||
|
$condition = DBA::mergeConditions($condition, ['uid' => $uid]);
|
||||||
|
|
||||||
|
return $this->count($condition, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function existsForUser($uid, array $condition): bool
|
||||||
|
{
|
||||||
|
$condition = DBA::mergeConditions($condition, ['uid' => $uid]);
|
||||||
|
|
||||||
|
return $this->exists($condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Entity\Notify
|
||||||
|
* @throws HTTPException\NotFoundException
|
||||||
|
*/
|
||||||
|
public function selectOneById(int $id): Entity\Notify
|
||||||
|
{
|
||||||
|
return $this->selectOne(['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectForUser(int $uid, array $condition, array $params): Collection\Notifies
|
||||||
|
{
|
||||||
|
$condition = DBA::mergeConditions($condition, ['uid' => $uid]);
|
||||||
|
|
||||||
|
return $this->select($condition, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectAllForUser(int $uid, array $params = []): Collection\Notifies
|
||||||
|
{
|
||||||
|
return $this->selectForUser($uid, [], $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAllSeenForUser(int $uid, array $condition = []): bool
|
||||||
|
{
|
||||||
|
$condition = DBA::mergeConditions($condition, ['uid' => $uid]);
|
||||||
|
|
||||||
|
return $this->db->update(self::$table_name, ['seen' => true], $condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Entity\Notify $Notify
|
||||||
|
* @return Entity\Notify
|
||||||
|
* @throws HTTPException\NotFoundException
|
||||||
|
* @throws HTTPException\InternalServerErrorException
|
||||||
|
* @throws Exception\NotificationCreationInterceptedException
|
||||||
|
*/
|
||||||
|
public function save(Entity\Notify $Notify): Entity\Notify
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'type' => $Notify->type,
|
||||||
|
'name' => $Notify->name,
|
||||||
|
'url' => $Notify->url,
|
||||||
|
'photo' => $Notify->photo,
|
||||||
|
'msg' => $Notify->msg,
|
||||||
|
'uid' => $Notify->uid,
|
||||||
|
'link' => $Notify->link,
|
||||||
|
'iid' => $Notify->itemId,
|
||||||
|
'parent' => $Notify->parent,
|
||||||
|
'seen' => $Notify->seen,
|
||||||
|
'verb' => $Notify->verb,
|
||||||
|
'otype' => $Notify->otype,
|
||||||
|
'name_cache' => $Notify->name_cache,
|
||||||
|
'msg_cache' => $Notify->msg_cache,
|
||||||
|
'uri-id' => $Notify->uriId,
|
||||||
|
'parent-uri-id' => $Notify->parentUriId,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($Notify->id) {
|
||||||
|
$this->db->update(self::$table_name, $fields, ['id' => $Notify->id]);
|
||||||
|
} else {
|
||||||
|
$fields['date'] = DateTimeFormat::utcNow();
|
||||||
|
Hook::callAll('enotify_store', $fields);
|
||||||
|
|
||||||
|
$this->db->insert(self::$table_name, $fields);
|
||||||
|
|
||||||
|
$Notify = $this->selectOneById($this->db->lastInsertId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAllSeenForRelatedNotify(Entity\Notify $Notify): bool
|
||||||
|
{
|
||||||
|
$condition = [
|
||||||
|
'(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?',
|
||||||
|
$Notify->link,
|
||||||
|
$Notify->parent,
|
||||||
|
$Notify->otype,
|
||||||
|
$Notify->uid
|
||||||
|
];
|
||||||
|
return $this->db->update(self::$table_name, ['seen' => true], $condition);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Entity;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Friendica\BaseEntity;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
|
use Friendica\Core\Renderer;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property-read $type
|
||||||
|
* @property-read $name
|
||||||
|
* @property-read $url
|
||||||
|
* @property-read $photo
|
||||||
|
* @property-read $date
|
||||||
|
* @property-read $msg
|
||||||
|
* @property-read $uid
|
||||||
|
* @property-read $link
|
||||||
|
* @property-read $itemId
|
||||||
|
* @property-read $parent
|
||||||
|
* @property-read $seen
|
||||||
|
* @property-read $verb
|
||||||
|
* @property-read $otype
|
||||||
|
* @property-read $name_cache
|
||||||
|
* @property-read $msg_cache
|
||||||
|
* @property-read $uriId
|
||||||
|
* @property-read $parentUriId
|
||||||
|
* @property-read $id
|
||||||
|
*/
|
||||||
|
class Notify extends BaseEntity
|
||||||
|
{
|
||||||
|
/** @var int */
|
||||||
|
protected $type;
|
||||||
|
/** @var string */
|
||||||
|
protected $name;
|
||||||
|
/** @var UriInterface */
|
||||||
|
protected $url;
|
||||||
|
/** @var UriInterface */
|
||||||
|
protected $photo;
|
||||||
|
/** @var DateTime */
|
||||||
|
protected $date;
|
||||||
|
/** @var string */
|
||||||
|
protected $msg;
|
||||||
|
/** @var int */
|
||||||
|
protected $uid;
|
||||||
|
/** @var UriInterface */
|
||||||
|
protected $link;
|
||||||
|
/** @var int */
|
||||||
|
protected $itemId;
|
||||||
|
/** @var int */
|
||||||
|
protected $parent;
|
||||||
|
/** @var bool */
|
||||||
|
protected $seen;
|
||||||
|
/** @var string */
|
||||||
|
protected $verb;
|
||||||
|
/** @var string */
|
||||||
|
protected $otype;
|
||||||
|
/** @var string */
|
||||||
|
protected $name_cache;
|
||||||
|
/** @var string */
|
||||||
|
protected $msg_cache;
|
||||||
|
/** @var int */
|
||||||
|
protected $uriId;
|
||||||
|
/** @var int */
|
||||||
|
protected $parentUriId;
|
||||||
|
/** @var int */
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
public function __construct(int $type, string $name, UriInterface $url, UriInterface $photo, DateTime $date, int $uid, UriInterface $link, bool $seen, string $verb, string $otype, string $name_cache, string $msg = null, string $msg_cache = null, int $itemId = null, int $uriId = null, int $parent = null, int $parentUriId = null, int $id = null)
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->url = $url;
|
||||||
|
$this->photo = $photo;
|
||||||
|
$this->date = $date;
|
||||||
|
$this->msg = $msg;
|
||||||
|
$this->uid = $uid;
|
||||||
|
$this->link = $link;
|
||||||
|
$this->itemId = $itemId;
|
||||||
|
$this->parent = $parent;
|
||||||
|
$this->seen = $seen;
|
||||||
|
$this->verb = $verb;
|
||||||
|
$this->otype = $otype;
|
||||||
|
$this->name_cache = $name_cache;
|
||||||
|
$this->msg_cache = $msg_cache;
|
||||||
|
$this->uriId = $uriId;
|
||||||
|
$this->parentUriId = $parentUriId;
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSeen()
|
||||||
|
{
|
||||||
|
$this->seen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMsgFromPreamble($epreamble)
|
||||||
|
{
|
||||||
|
$this->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $this->link->__toString()]);
|
||||||
|
$this->msg_cache = self::formatMessage($this->name_cache, strip_tags(BBCode::convert($this->msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a notification message with the notification author
|
||||||
|
*
|
||||||
|
* Replace the name with {0} but ensure to make that only once. The {0} is used
|
||||||
|
* later and prints the name in bold.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $message
|
||||||
|
*
|
||||||
|
* @return string Formatted message
|
||||||
|
*/
|
||||||
|
public static function formatMessage(string $name, string $message): string
|
||||||
|
{
|
||||||
|
if ($name != '') {
|
||||||
|
$pos = strpos($message, $name);
|
||||||
|
} else {
|
||||||
|
$pos = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pos !== false) {
|
||||||
|
$message = substr_replace($message, '{0}', $pos, strlen($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Exception;
|
||||||
|
|
||||||
|
class NotificationCreationInterceptedException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,376 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Factory;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Friendica\App\BaseURL;
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
|
use Friendica\Core\L10n;
|
||||||
|
use Friendica\Core\Protocol;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Module\BaseNotifications;
|
||||||
|
use Friendica\Navigation\Notifications\Collection\FormattedNotifications;
|
||||||
|
use Friendica\Navigation\Notifications\Depository;
|
||||||
|
use Friendica\Navigation\Notifications\ValueObject;
|
||||||
|
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||||
|
use Friendica\Protocol\Activity;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
use Friendica\Util\Proxy;
|
||||||
|
use Friendica\Util\Temporal;
|
||||||
|
use Friendica\Util\XML;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating notification objects based on items
|
||||||
|
* Currently, there are the following types of item based notifications:
|
||||||
|
* - network
|
||||||
|
* - system
|
||||||
|
* - home
|
||||||
|
* - personal
|
||||||
|
*/
|
||||||
|
class FormattedNotification extends BaseFactory
|
||||||
|
{
|
||||||
|
/** @var Database */
|
||||||
|
private $dba;
|
||||||
|
/** @var Depository\Notify */
|
||||||
|
private $notify;
|
||||||
|
/** @var BaseURL */
|
||||||
|
private $baseUrl;
|
||||||
|
/** @var L10n */
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
public function __construct(LoggerInterface $logger, Database $dba, Depository\Notify $notify, BaseURL $baseUrl, L10n $l10n)
|
||||||
|
{
|
||||||
|
parent::__construct($logger);
|
||||||
|
|
||||||
|
$this->dba = $dba;
|
||||||
|
$this->notify = $notify;
|
||||||
|
$this->baseUrl = $baseUrl;
|
||||||
|
$this->l10n = $l10n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $formattedItem The return of $this->formatItem
|
||||||
|
*
|
||||||
|
* @return ValueObject\FormattedNotification
|
||||||
|
*/
|
||||||
|
private function createFromFormattedItem(array $formattedItem): ValueObject\FormattedNotification
|
||||||
|
{
|
||||||
|
// Transform the different types of notification in a usable array
|
||||||
|
switch ($formattedItem['verb'] ?? '') {
|
||||||
|
case Activity::LIKE:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'like',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s liked %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
case Activity::DISLIKE:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'dislike',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s disliked %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
case Activity::ATTEND:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'attend',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s is attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
case Activity::ATTENDNO:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'attendno',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s is not attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
case Activity::ATTENDMAYBE:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'attendmaybe',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s may attending %s's event", $formattedItem['author-name'], $formattedItem['parent-author-name']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
case Activity::FRIEND:
|
||||||
|
if (!isset($formattedItem['object'])) {
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'friend',
|
||||||
|
$formattedItem['link'],
|
||||||
|
$formattedItem['image'],
|
||||||
|
$formattedItem['url'],
|
||||||
|
$formattedItem['text'],
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">";
|
||||||
|
$obj = XML::parseString($xmlHead . $formattedItem['object']);
|
||||||
|
|
||||||
|
$formattedItem['fname'] = $obj->title;
|
||||||
|
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
'friend',
|
||||||
|
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||||
|
$formattedItem['author-avatar'],
|
||||||
|
$formattedItem['author-link'],
|
||||||
|
$this->l10n->t("%s is now friends with %s", $formattedItem['author-name'], $formattedItem['fname']),
|
||||||
|
$formattedItem['when'],
|
||||||
|
$formattedItem['ago'],
|
||||||
|
$formattedItem['seen']
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new ValueObject\FormattedNotification(
|
||||||
|
$formattedItem['label'] ?? '',
|
||||||
|
$formattedItem['link'] ?? '',
|
||||||
|
$formattedItem['image'] ?? '',
|
||||||
|
$formattedItem['url'] ?? '',
|
||||||
|
$formattedItem['text'] ?? '',
|
||||||
|
$formattedItem['when'] ?? '',
|
||||||
|
$formattedItem['ago'] ?? '',
|
||||||
|
$formattedItem['seen'] ?? false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get system notifications
|
||||||
|
*
|
||||||
|
* @param bool $seen False => only include notifications into the query
|
||||||
|
* which aren't marked as "seen"
|
||||||
|
* @param int $start Start the query at this point
|
||||||
|
* @param int $limit Maximum number of query results
|
||||||
|
*
|
||||||
|
* @return FormattedNotifications
|
||||||
|
*/
|
||||||
|
public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||||
|
{
|
||||||
|
$conditions = [];
|
||||||
|
if (!$seen) {
|
||||||
|
$conditions['seen'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
$params['order'] = ['date' => 'DESC'];
|
||||||
|
$params['limit'] = [$start, $limit];
|
||||||
|
|
||||||
|
$formattedNotifications = new FormattedNotifications();
|
||||||
|
try {
|
||||||
|
$Notifies = $this->notify->selectForUser(local_user(), $conditions, $params);
|
||||||
|
|
||||||
|
foreach ($Notifies as $Notify) {
|
||||||
|
$formattedNotifications[] = new ValueObject\FormattedNotification(
|
||||||
|
'notification',
|
||||||
|
$this->baseUrl->get(true) . '/notification/' . $Notify->id,
|
||||||
|
Contact::getAvatarUrlForUrl($Notify->url, $Notify->uid, Proxy::SIZE_MICRO),
|
||||||
|
$Notify->url,
|
||||||
|
strip_tags(BBCode::toPlaintext($Notify->msg)),
|
||||||
|
DateTimeFormat::local($Notify->date->format(DateTimeFormat::MYSQL), 'r'),
|
||||||
|
Temporal::getRelativeDate($Notify->date->format(DateTimeFormat::MYSQL)),
|
||||||
|
$Notify->seen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logger->warning('Select failed.', ['conditions' => $conditions, 'exception' => $e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get network notifications
|
||||||
|
*
|
||||||
|
* @param bool $seen False => only include notifications into the query
|
||||||
|
* which aren't marked as "seen"
|
||||||
|
* @param int $start Start the query at this point
|
||||||
|
* @param int $limit Maximum number of query results
|
||||||
|
*
|
||||||
|
* @return FormattedNotifications
|
||||||
|
*/
|
||||||
|
public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||||
|
{
|
||||||
|
$condition = ['wall' => false, 'uid' => local_user()];
|
||||||
|
|
||||||
|
if (!$seen) {
|
||||||
|
$condition['unseen'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
|
||||||
|
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||||
|
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||||
|
|
||||||
|
$formattedNotifications = new FormattedNotifications();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||||
|
while ($userPost = $this->dba->fetch($userPosts)) {
|
||||||
|
$formattedNotifications[] = $this->createFromFormattedItem($this->formatItem($userPost));
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logger->warning('Select failed.', ['condition' => $condition, 'exception' => $e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get personal notifications
|
||||||
|
*
|
||||||
|
* @param bool $seen False => only include notifications into the query
|
||||||
|
* which aren't marked as "seen"
|
||||||
|
* @param int $start Start the query at this point
|
||||||
|
* @param int $limit Maximum number of query results
|
||||||
|
*
|
||||||
|
* @return FormattedNotifications
|
||||||
|
*/
|
||||||
|
public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||||
|
{
|
||||||
|
$condition = ['wall' => false, 'uid' => local_user(), 'author-id' => public_contact()];
|
||||||
|
|
||||||
|
if (!$seen) {
|
||||||
|
$condition['unseen'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
|
||||||
|
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||||
|
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||||
|
|
||||||
|
$formattedNotifications = new FormattedNotifications();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||||
|
while ($userPost = $this->dba->fetch($userPosts)) {
|
||||||
|
$formattedNotifications[] = $this->createFromFormattedItem($this->formatItem($userPost));
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get home notifications
|
||||||
|
*
|
||||||
|
* @param bool $seen False => only include notifications into the query
|
||||||
|
* which aren't marked as "seen"
|
||||||
|
* @param int $start Start the query at this point
|
||||||
|
* @param int $limit Maximum number of query results
|
||||||
|
*
|
||||||
|
* @return FormattedNotifications
|
||||||
|
*/
|
||||||
|
public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||||
|
{
|
||||||
|
$condition = ['wall' => true, 'uid' => local_user()];
|
||||||
|
|
||||||
|
if (!$seen) {
|
||||||
|
$condition['unseen'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar',
|
||||||
|
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||||
|
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||||
|
|
||||||
|
$formattedNotifications = new FormattedNotifications();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||||
|
while ($userPost = $this->dba->fetch($userPosts)) {
|
||||||
|
$formattedItem = $this->formatItem($userPost);
|
||||||
|
|
||||||
|
// Overwrite specific fields, not default item format
|
||||||
|
$formattedItem['label'] = 'comment';
|
||||||
|
$formattedItem['text'] = $this->l10n->t("%s commented on %s's post", $formattedItem['author-name'], $formattedItem['parent-author-name']);
|
||||||
|
|
||||||
|
$formattedNotifications[] = $this->createFromFormattedItem($formattedItem);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->logger->warning('Select failed.', ['conditions' => $condition, 'exception' => $e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the item query in a usable array
|
||||||
|
*
|
||||||
|
* @param array $item The item from the db query
|
||||||
|
*
|
||||||
|
* @return array The item, extended with the notification-specific information
|
||||||
|
*
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function formatItem(array $item): array
|
||||||
|
{
|
||||||
|
$item['seen'] = !($item['unseen'] > 0);
|
||||||
|
|
||||||
|
// For feed items we use the user's contact, since the avatar is mostly self choosen.
|
||||||
|
if (!empty($item['network']) && $item['network'] == Protocol::FEED) {
|
||||||
|
$item['author-avatar'] = $item['contact-avatar'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$item['label'] = (($item['gravity'] == GRAVITY_PARENT) ? 'post' : 'comment');
|
||||||
|
$item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid'];
|
||||||
|
$item['image'] = $item['author-avatar'];
|
||||||
|
$item['url'] = $item['author-link'];
|
||||||
|
$item['when'] = DateTimeFormat::local($item['created'], 'r');
|
||||||
|
$item['ago'] = Temporal::getRelativeDate($item['created']);
|
||||||
|
$item['text'] = (($item['gravity'] == GRAVITY_PARENT)
|
||||||
|
? $this->l10n->t("%s created a new post", $item['author-name'])
|
||||||
|
: $this->l10n->t("%s commented on %s's post", $item['author-name'], $item['parent-author-name']));
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\Factory;
|
||||||
|
|
||||||
|
use Friendica\BaseFactory;
|
||||||
|
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
|
use GuzzleHttp\Psr7\Uri;
|
||||||
|
|
||||||
|
class Notify extends BaseFactory implements ICanCreateFromTableRow
|
||||||
|
{
|
||||||
|
public function createFromTableRow(array $row): \Friendica\Navigation\Notifications\Entity\Notify
|
||||||
|
{
|
||||||
|
return new \Friendica\Navigation\Notifications\Entity\Notify(
|
||||||
|
$row['type'],
|
||||||
|
$row['name'],
|
||||||
|
new Uri($row['url']),
|
||||||
|
new Uri($row['photo']),
|
||||||
|
new \DateTime($row['date'], new \DateTimeZone('UTC')),
|
||||||
|
$row['uid'],
|
||||||
|
new Uri($row['link']),
|
||||||
|
$row['seen'],
|
||||||
|
$row['verb'],
|
||||||
|
$row['otype'],
|
||||||
|
$row['name_cache'],
|
||||||
|
$row['msg'],
|
||||||
|
$row['msg_cache'],
|
||||||
|
$row['iid'],
|
||||||
|
$row['uri-id'],
|
||||||
|
$row['parent'],
|
||||||
|
$row['parent-uri-id'],
|
||||||
|
$row['id']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createFromParams($params, $itemlink = null, $item_id = null, $uri_id = null, $parent_id = null, $parent_uri_id = null): \Friendica\Navigation\Notifications\Entity\Notify
|
||||||
|
{
|
||||||
|
return new \Friendica\Navigation\Notifications\Entity\Notify(
|
||||||
|
$params['type'] ?? '',
|
||||||
|
$params['source_name'] ?? '',
|
||||||
|
new Uri($params['source_link'] ?? ''),
|
||||||
|
new Uri($params['source_photo'] ?? ''),
|
||||||
|
new \DateTime(),
|
||||||
|
$params['uid'] ?? 0,
|
||||||
|
new Uri($itemlink ?? ''),
|
||||||
|
false,
|
||||||
|
$params['verb'] ?? '',
|
||||||
|
$params['otype'] ?? '',
|
||||||
|
substr(strip_tags(BBCode::convertForUriId($uri_id, $params['source_name'])), 0, 255),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$item_id,
|
||||||
|
$uri_id,
|
||||||
|
$parent_id,
|
||||||
|
$parent_uri_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2021, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Navigation\Notifications\ValueObject;
|
||||||
|
|
||||||
|
use Friendica\BaseDataTransferObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view-only object for printing item notifications to the frontend
|
||||||
|
*/
|
||||||
|
class FormattedNotification extends BaseDataTransferObject
|
||||||
|
{
|
||||||
|
const SYSTEM = 'system';
|
||||||
|
const PERSONAL = 'personal';
|
||||||
|
const NETWORK = 'network';
|
||||||
|
const INTRO = 'intro';
|
||||||
|
const HOME = 'home';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $label = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $link = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $image = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $url = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $text = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $when = '';
|
||||||
|
/** @var string */
|
||||||
|
protected $ago = '';
|
||||||
|
/** @var boolean */
|
||||||
|
protected $seen = false;
|
||||||
|
|
||||||
|
public function __construct(string $label, string $link, string $image, string $url, string $text, string $when, string $ago, bool $seen)
|
||||||
|
{
|
||||||
|
$this->label = $label ?? '';
|
||||||
|
$this->link = $link ?? '';
|
||||||
|
$this->image = $image ?? '';
|
||||||
|
$this->url = $url ?? '';
|
||||||
|
$this->text = $text ?? '';
|
||||||
|
$this->when = $when ?? '';
|
||||||
|
$this->ago = $ago ?? '';
|
||||||
|
$this->seen = $seen ?? false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user