From 582f6bd4a35a864d9425b7c912121346deebdc4b Mon Sep 17 00:00:00 2001
From: nupplaPhil <admin@philipp.info>
Date: Tue, 28 Jan 2020 21:28:57 +0100
Subject: [PATCH] Refactor API notification usage - Remove "mapFields()" from
 BaseModel - Add new Notification API entity (including collection) - Add new
 NotificationFactory method "getApiList()"

---
 include/api.php                           |  7 +-
 src/BaseCollection.php                    | 10 +--
 src/BaseEntity.php                        | 15 +++++
 src/BaseModel.php                         | 29 ++-------
 src/Collection/Api/Notifications.php      | 17 +++++
 src/DI.php                                | 16 ++---
 src/Factory/Notification/Notification.php | 24 +++++++
 src/Model/Notify.php                      | 76 +++-------------------
 src/Object/Api/Friendica/Notification.php | 79 +++++++++++++++++++++++
 9 files changed, 164 insertions(+), 109 deletions(-)
 create mode 100644 src/Collection/Api/Notifications.php
 create mode 100644 src/Object/Api/Friendica/Notification.php

diff --git a/include/api.php b/include/api.php
index a69bd01b49..3f4d5625b0 100644
--- a/include/api.php
+++ b/include/api.php
@@ -5892,10 +5892,11 @@ api_register_func('api/friendica/activity/unattendmaybe', 'api_friendica_activit
  * Returns notifications
  *
  * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+ *
  * @return string|array
- * @throws BadRequestException
  * @throws ForbiddenException
- * @throws InternalServerErrorException
+ * @throws BadRequestException
+ * @throws Exception
  */
 function api_friendica_notification($type)
 {
@@ -5908,7 +5909,7 @@ function api_friendica_notification($type)
 		throw new BadRequestException("Invalid argument count");
 	}
 
-	$notifications = DI::notify()->select(['uid' => api_user()], ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50]);
+	$notifications = DI::notification()->getApiList(local_user());
 
 	if ($type == "xml") {
 		$xmlnotes = false;
diff --git a/src/BaseCollection.php b/src/BaseCollection.php
index 5a20acee7f..9a9efdb068 100644
--- a/src/BaseCollection.php
+++ b/src/BaseCollection.php
@@ -17,14 +17,14 @@ abstract class BaseCollection extends \ArrayIterator
 	protected $totalCount = 0;
 
 	/**
-	 * @param BaseModel[] $models
-	 * @param int|null    $totalCount
+	 * @param BaseEntity[] $entities
+	 * @param int|null     $totalCount
 	 */
-	public function __construct(array $models = [], int $totalCount = null)
+	public function __construct(array $entities = [], int $totalCount = null)
 	{
-		parent::__construct($models);
+		parent::__construct($entities);
 
-		$this->totalCount = $totalCount ?? count($models);
+		$this->totalCount = $totalCount ?? count($entities);
 	}
 
 	/**
diff --git a/src/BaseEntity.php b/src/BaseEntity.php
index 9f0cb31f8e..14f95c197e 100644
--- a/src/BaseEntity.php
+++ b/src/BaseEntity.php
@@ -11,7 +11,22 @@ namespace Friendica;
  */
 abstract class BaseEntity implements \JsonSerializable
 {
+	/**
+	 * Returns the current entity as an json array
+	 *
+	 * @return array
+	 */
 	public function jsonSerialize()
+	{
+		return $this->toArray();
+	}
+
+	/**
+	 * Returns the current entity as an array
+	 *
+	 * @return array
+	 */
+	public function toArray()
 	{
 		return get_object_vars($this);
 	}
diff --git a/src/BaseModel.php b/src/BaseModel.php
index decc627521..2c952888b2 100644
--- a/src/BaseModel.php
+++ b/src/BaseModel.php
@@ -12,7 +12,7 @@ use Psr\Log\LoggerInterface;
  *
  * @property int id
  */
-abstract class BaseModel
+abstract class BaseModel extends BaseEntity
 {
 	/** @var Database */
 	protected $dba;
@@ -48,23 +48,9 @@ abstract class BaseModel
 		$this->originalData = $data;
 	}
 
-	/**
-	 * Maps a data array (original/current) to a known field list of the chosen model
-	 *
-	 * This is useful to filter out additional attributes, which aren't part of the db-table (like readonly cached fields)
-	 *
-	 * @param array $data The data array to map to db-fields
-	 *
-	 * @return array the mapped data array
-	 */
-	protected function mapFields(array $data)
-	{
-		return $data;
-	}
-
 	public function getOriginalData()
 	{
-		return $this->mapFields($this->originalData);
+		return $this->originalData;
 	}
 
 	public function resetOriginalData()
@@ -129,16 +115,9 @@ abstract class BaseModel
 		$this->data[$name] = $value;
 	}
 
-	/**
-	 * Returns the values of the current model as an array
-	 *
-	 * @param bool $dbOnly True, if just the db-relevant fields should be returned
-	 *
-	 * @return array The values of the current model
-	 */
-	public function toArray(bool $dbOnly = false)
+	public function toArray()
 	{
-		return $dbOnly ? $this->mapFields($this->data) : $this->data;
+		return $this->data;
 	}
 
 	protected function checkValid()
diff --git a/src/Collection/Api/Notifications.php b/src/Collection/Api/Notifications.php
new file mode 100644
index 0000000000..5bd8983ce6
--- /dev/null
+++ b/src/Collection/Api/Notifications.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Friendica\Collection\Api;
+
+use Friendica\BaseCollection;
+use Friendica\Object\Api\Friendica\Notification;
+
+class Notifications extends BaseCollection
+{
+	/**
+	 * @return Notification
+	 */
+	public function current()
+	{
+		return parent::current();
+	}
+}
diff --git a/src/DI.php b/src/DI.php
index fc18b30b8e..65c82225d5 100644
--- a/src/DI.php
+++ b/src/DI.php
@@ -280,14 +280,6 @@ abstract class DI
 		return self::$dice->create(Model\User\Cookie::class);
 	}
 
-	/**
-	 * @return Repository\Notify
-	 */
-	public static function notify()
-	{
-		return self::$dice->create(Repository\Notify::class);
-	}
-
 	/**
 	 * @return Model\Storage\IStorage
 	 */
@@ -324,6 +316,14 @@ abstract class DI
 		return self::$dice->create(Repository\ProfileField::class);
 	}
 
+	/**
+	 * @return Repository\Notify
+	 */
+	public static function notify()
+	{
+		return self::$dice->create(Repository\Notify::class);
+	}
+
 	//
 	// "Protocol" namespace instances
 	//
diff --git a/src/Factory/Notification/Notification.php b/src/Factory/Notification/Notification.php
index 5f2c2231b8..2b179cca53 100644
--- a/src/Factory/Notification/Notification.php
+++ b/src/Factory/Notification/Notification.php
@@ -6,6 +6,7 @@ use Exception;
 use Friendica\App;
 use Friendica\App\BaseURL;
 use Friendica\BaseFactory;
+use Friendica\Collection\Api\Notifications as ApiNotifications;
 use Friendica\Content\Text\BBCode;
 use Friendica\Core\L10n;
 use Friendica\Core\PConfig\IPConfig;
@@ -15,6 +16,7 @@ use Friendica\Database\Database;
 use Friendica\Model\Item;
 use Friendica\Module\BaseNotifications;
 use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Object\Api\Friendica\Notification as ApiNotification;
 use Friendica\Protocol\Activity;
 use Friendica\Repository;
 use Friendica\Util\DateTimeFormat;
@@ -352,4 +354,26 @@ class Notification extends BaseFactory
 
 		return $formattedNotifications;
 	}
+
+	/**
+	 * @param int   $uid    The user id of the API call
+	 * @param array $params Additional parameters
+	 *
+	 * @return ApiNotifications
+	 *
+	 * @throws Exception
+	 */
+	public function getApiList(int $uid, array $params = ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => 50])
+	{
+		$notifies = $this->notification->select(['uid' => $uid], $params);
+
+		/** @var ApiNotification[] $notifications */
+		$notifications = [];
+
+		foreach ($notifies as $notify) {
+			$notifications[] = new ApiNotification($notify);
+		}
+
+		return new ApiNotifications($notifications);
+	}
 }
diff --git a/src/Model/Notify.php b/src/Model/Notify.php
index c48fa0f679..b12eb341b0 100644
--- a/src/Model/Notify.php
+++ b/src/Model/Notify.php
@@ -5,19 +5,12 @@ namespace Friendica\Model;
 use Exception;
 use Friendica\BaseModel;
 use Friendica\Content\Text\BBCode;
-use Friendica\Content\Text\HTML;
 use Friendica\Database\Database;
 use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Util\DateTimeFormat;
-use Friendica\Util\Temporal;
 use Psr\Log\LoggerInterface;
 
 /**
  * Model for an entry in the notify table
- * - Including additional, calculated properties
- *
- * Is used either for frontend interactions or for API-based interaction
- * @see https://github.com/friendica/friendica/blob/develop/doc/API-Entities.md#notification
  *
  * @property string  hash
  * @property integer type
@@ -36,11 +29,6 @@ use Psr\Log\LoggerInterface;
  *
  * @property-read string name_cache Full name of the contact subject
  * @property-read string msg_cache  Plaintext version of the notification text with a placeholder (`{0}`) for the subject contact's name.
- *
- * @property-read integer timestamp  Unix timestamp
- * @property-read string  dateRel  	 Time since the note was posted, eg "1 hour ago"
- * @property-read string  $msg_html
- * @property-read string  $msg_plain
  */
 class Notify extends BaseModel
 {
@@ -59,8 +47,7 @@ class Notify extends BaseModel
 		$this->repo = $repo;
 
 		$this->setNameCache();
-		$this->setTimestamp();
-		$this->setMsg();
+		$this->setMsgCache();
 	}
 
 	/**
@@ -81,41 +68,23 @@ class Notify extends BaseModel
 		}
 	}
 
-	/**
-	 * Set some extra properties to the notification from db:
-	 *  - timestamp as int in default TZ
-	 *  - date_rel : relative date string
-	 */
-	private function setTimestamp()
-	{
-		try {
-			$this->timestamp = strtotime(DateTimeFormat::local($this->date));
-		} catch (Exception $e) {
-		}
-		$this->dateRel = Temporal::getRelativeDate($this->date);
-	}
-
 	/**
 	 * Sets the pre-formatted name (caching)
-	 *
-	 * @throws InternalServerErrorException
 	 */
 	private function setNameCache()
 	{
-		$this->name_cache = strip_tags(BBCode::convert($this->source_name ?? ''));
+		try {
+			$this->name_cache = strip_tags(BBCode::convert($this->source_name ?? ''));
+		} catch (InternalServerErrorException $e) {
+		}
 	}
 
 	/**
-	 * Set some extra properties to the notification from db:
-	 *  - msg_html: message as html string
-	 *  - msg_plain: message as plain text string
-	 *  - msg_cache: The pre-formatted message (caching)
+	 * Sets the pre-formatted msg (caching)
 	 */
-	private function setMsg()
+	private function setMsgCache()
 	{
 		try {
-			$this->msg_html  = BBCode::convert($this->msg, false);
-			$this->msg_plain = explode("\n", trim(HTML::toPlaintext($this->msg_html, 0)))[0];
 			$this->msg_cache = self::formatMessage($this->name_cache, strip_tags(BBCode::convert($this->msg)));
 		} catch (InternalServerErrorException $e) {
 		}
@@ -125,12 +94,8 @@ class Notify extends BaseModel
 	{
 		parent::__set($name, $value);
 
-		if ($name == 'date') {
-			$this->setTimestamp();
-		}
-
 		if ($name == 'msg') {
-			$this->setMsg();
+			$this->setMsgCache();
 		}
 
 		if ($name == 'source_name') {
@@ -163,29 +128,4 @@ class Notify extends BaseModel
 
 		return $message;
 	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	protected function mapFields(array $data)
-	{
-		return [
-			'hash'       => $data['hash'] ?? '',
-			'type'       => $data['type'] ?? 0,
-			'name'       => $data['name'] ?? '',
-			'url'        => $data['url'] ?? '',
-			'photo'      => $data['photo'] ?? '',
-			'date'       => $data['date'] ?? DateTimeFormat::utcNow(),
-			'msg'        => $data['msg'] ?? '',
-			'uid'        => $data['uid'] ?? 0,
-			'link'       => $data['link'] ?? '',
-			'iid'        => $data['iid'] ?? 0,
-			'parent'     => $data['parent'] ?? 0,
-			'seen'       => $data['seen'] ?? false,
-			'verb'       => $data['verb'] ?? '',
-			'otype'      => $data['otype'] ?? '',
-			'name_cache' => $data['name_cache'] ?? null,
-			'msg_cache'  => $data['msg_cache'] ?? null,
-		];
-	}
 }
diff --git a/src/Object/Api/Friendica/Notification.php b/src/Object/Api/Friendica/Notification.php
new file mode 100644
index 0000000000..910d5ec682
--- /dev/null
+++ b/src/Object/Api/Friendica/Notification.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Friendica\Object\Api\Friendica;
+
+use Friendica\BaseEntity;
+use Friendica\Content\Text\BBCode;
+use Friendica\Content\Text\HTML;
+use Friendica\Model\Notify;
+use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Temporal;
+
+/**
+ * Friendica Notification
+ *
+ * @see https://github.com/friendica/friendica/blob/develop/doc/API-Entities.md#notification
+ */
+class Notification extends BaseEntity
+{
+	/** @var integer */
+	protected $id;
+	/** @var string */
+	protected $hash;
+	/** @var integer */
+	protected $type;
+	/** @var string Full name of the contact subject */
+	protected $name;
+	/** @var string Profile page URL of the contact subject */
+	protected $url;
+	/** @var string Profile photo URL of the contact subject */
+	protected $photo;
+	/** @var string YYYY-MM-DD hh:mm:ss local server time */
+	protected $date;
+	/** @var string The message (BBCode) */
+	protected $msg;
+	/** @var integer Owner User Id */
+	protected $uid;
+	/** @var string Notification URL */
+	protected $link;
+	/** @var integer Item Id */
+	protected $iid;
+	/** @var integer Parent Item Id */
+	protected $parent;
+	/** @var boolean  Whether the notification was read or not. */
+	protected $seen;
+	/** @var string Verb URL @see http://activitystrea.ms */
+	protected $verb;
+	/** @var string Subject type (`item`, `intro` or `mail`) */
+	protected $otype;
+	/** @var string Full name of the contact subject (HTML) */
+	protected $name_cache;
+	/** @var string Plaintext version of the notification text with a placeholder (`{0}`) for the subject contact's name. (Plaintext) */
+	protected $msg_cache;
+	/** @var integer  Unix timestamp */
+	protected $timestamp;
+	/** @var string Time since the note was posted, eg "1 hour ago" */
+	protected $date_rel;
+	/** @var string Message (HTML) */
+	protected $msg_html;
+	/** @var string Message (Plaintext) */
+	protected $msg_plain;
+
+	public function __construct(Notify $notify)
+	{
+		// map each notify attribute to the entity
+		foreach ($notify->toArray() as $key => $value) {
+			$this->{$key} = $value;
+		}
+
+		// add additional attributes for the API
+		try {
+			$this->timestamp = strtotime(DateTimeFormat::local($this->date));
+			$this->msg_html  = BBCode::convert($this->msg, false);
+			$this->msg_plain = explode("\n", trim(HTML::toPlaintext($this->msg_html, 0)))[0];
+		} catch (\Exception $e) {
+		}
+
+		$this->date_rel = Temporal::getRelativeDate($this->date);
+	}
+}