Merge pull request #10893 from nupplaphil/feat/depository_introduction
Move Introduction to Depository Paradigm
This commit is contained in:
		
				commit
				
					
						09a809aee5
					
				
			
		
					 27 changed files with 617 additions and 238 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
-- ------------------------------------------
 | 
			
		||||
-- Friendica 2021.12-dev (Siberian Iris)
 | 
			
		||||
-- DB_UPDATE_VERSION 1441
 | 
			
		||||
-- DB_UPDATE_VERSION 1442
 | 
			
		||||
-- ------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -715,11 +715,11 @@ CREATE TABLE IF NOT EXISTS `intro` (
 | 
			
		|||
	`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '',
 | 
			
		||||
	`suggest-cid` int unsigned COMMENT 'Suggested contact',
 | 
			
		||||
	`knowyou` boolean NOT NULL DEFAULT '0' COMMENT '',
 | 
			
		||||
	`duplex` boolean NOT NULL DEFAULT '0' COMMENT '',
 | 
			
		||||
	`duplex` boolean NOT NULL DEFAULT '0' COMMENT 'deprecated',
 | 
			
		||||
	`note` text COMMENT '',
 | 
			
		||||
	`hash` varchar(255) NOT NULL DEFAULT '' COMMENT '',
 | 
			
		||||
	`datetime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
 | 
			
		||||
	`blocked` boolean NOT NULL DEFAULT '1' COMMENT '',
 | 
			
		||||
	`blocked` boolean NOT NULL DEFAULT '0' COMMENT 'deprecated',
 | 
			
		||||
	`ignore` boolean NOT NULL DEFAULT '0' COMMENT '',
 | 
			
		||||
	 PRIMARY KEY(`id`),
 | 
			
		||||
	 INDEX `contact-id` (`contact-id`),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,17 +36,18 @@ doSomething($intros);
 | 
			
		|||
```
 | 
			
		||||
 | 
			
		||||
After:
 | 
			
		||||
 | 
			
		||||
```php
 | 
			
		||||
function doSomething(\Friendica\Collection\Introductions $intros)
 | 
			
		||||
function doSomething(\Friendica\Contact\Introductions\Collection\Introductions $intros)
 | 
			
		||||
{
 | 
			
		||||
    foreach ($intros as $intro) {
 | 
			
		||||
        /** @var $intro \Friendica\Model\Introduction */
 | 
			
		||||
        /** @var $intro \Friendica\Contact\Introductions\Entity\Introduction */
 | 
			
		||||
        $introId = $intro->id;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @var $intros \Friendica\Collection\Introductions */
 | 
			
		||||
$intros = \Friendica\DI::intro()->select(['uid' => local_user()]);
 | 
			
		||||
/** @var $intros \Friendica\Contact\Introductions\Collection\Introductions */
 | 
			
		||||
$intros = \Friendica\DI::intro()->selecForUser(local_user());
 | 
			
		||||
 | 
			
		||||
doSomething($intros);
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,11 +14,11 @@ Fields
 | 
			
		|||
| contact-id  |                   | int unsigned       | NO   |     | 0                   |                |
 | 
			
		||||
| suggest-cid | Suggested contact | int unsigned       | YES  |     | NULL                |                |
 | 
			
		||||
| knowyou     |                   | boolean            | NO   |     | 0                   |                |
 | 
			
		||||
| duplex      |                   | boolean            | NO   |     | 0                   |                |
 | 
			
		||||
| duplex      | deprecated        | boolean            | NO   |     | 0                   |                |
 | 
			
		||||
| note        |                   | text               | YES  |     | NULL                |                |
 | 
			
		||||
| hash        |                   | varchar(255)       | NO   |     |                     |                |
 | 
			
		||||
| datetime    |                   | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
 | 
			
		||||
| blocked     |                   | boolean            | NO   |     | 1                   |                |
 | 
			
		||||
| blocked     | deprecated        | boolean            | NO   |     | 0                   |                |
 | 
			
		||||
| ignore      |                   | boolean            | NO   |     | 0                   |                |
 | 
			
		||||
 | 
			
		||||
Indexes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
<?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\Collection;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseCollection;
 | 
			
		||||
 | 
			
		||||
class Introductions extends BaseCollection
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/Contact/Introduction/Collection/Introductions.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Contact/Introduction/Collection/Introductions.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Contact\Introduction\Collection;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseCollection;
 | 
			
		||||
 | 
			
		||||
class Introductions extends BaseCollection
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										209
									
								
								src/Contact/Introduction/Depository/Introduction.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/Contact/Introduction/Depository/Introduction.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,209 @@
 | 
			
		|||
<?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\Contact\Introduction\Depository;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseDepository;
 | 
			
		||||
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
 | 
			
		||||
use Friendica\Contact\Introduction\Exception\IntroductionPersistenceException;
 | 
			
		||||
use Friendica\Contact\Introduction\Collection;
 | 
			
		||||
use Friendica\Contact\Introduction\Entity;
 | 
			
		||||
use Friendica\Contact\Introduction\Factory;
 | 
			
		||||
use Friendica\Database\Database;
 | 
			
		||||
use Friendica\Network\HTTPException\NotFoundException;
 | 
			
		||||
use Friendica\Util\DateTimeFormat;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
 | 
			
		||||
class Introduction extends BaseDepository
 | 
			
		||||
{
 | 
			
		||||
	/** @var Factory\Introduction */
 | 
			
		||||
	protected $factory;
 | 
			
		||||
 | 
			
		||||
	protected static $table_name = 'intro';
 | 
			
		||||
 | 
			
		||||
	public function __construct(Database $database, LoggerInterface $logger, Factory\Introduction $factory)
 | 
			
		||||
	{
 | 
			
		||||
		parent::__construct($database, $logger, $factory);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param array $condition
 | 
			
		||||
	 * @param array $params
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return Entity\Introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws NotFoundException the underlying exception if there's no Introduction with the given conditions
 | 
			
		||||
	 */
 | 
			
		||||
	private function selectOne(array $condition, array $params = []): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		return parent::_selectOne($condition, $params);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Converts a given Introduction into a DB compatible row array
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param Entity\Introduction $introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return array
 | 
			
		||||
	 */
 | 
			
		||||
	protected function convertToTableRow(Entity\Introduction $introduction): array
 | 
			
		||||
	{
 | 
			
		||||
		return [
 | 
			
		||||
			'uid'         => $introduction->uid,
 | 
			
		||||
			'contact-id'  => $introduction->cid,
 | 
			
		||||
			'suggest-cid' => $introduction->sid,
 | 
			
		||||
			'knowyou'     => $introduction->knowyou ? 1 : 0,
 | 
			
		||||
			'note'        => $introduction->note,
 | 
			
		||||
			'hash'        => $introduction->hash,
 | 
			
		||||
			'ignore'      => $introduction->ignore ? 1 : 0,
 | 
			
		||||
			'datetime'    => $introduction->datetime->format(DateTimeFormat::MYSQL),
 | 
			
		||||
		];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param int $id
 | 
			
		||||
	 * @param int $uid
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return Entity\Introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws IntroductionNotFoundException in case there is no Introduction with this id
 | 
			
		||||
	 */
 | 
			
		||||
	public function selectOneById(int $id, int $uid): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			return $this->selectOne(['id' => $id, 'uid' => $uid]);
 | 
			
		||||
		} catch (NotFoundException $exception) {
 | 
			
		||||
			throw new IntroductionNotFoundException(sprintf('There is no Introduction with the ID %d for the user %d', $id, $uid), $exception);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Selects introductions for a given user
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int      $uid
 | 
			
		||||
	 * @param int|null $min_id
 | 
			
		||||
	 * @param int|null $max_id
 | 
			
		||||
	 * @param int      $limit
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return Collection\Introductions
 | 
			
		||||
	 */
 | 
			
		||||
	public function selectForUser(int $uid, int $min_id = null, int $max_id = null, int $limit = self::LIMIT): Collection\Introductions
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			$BaseCollection = parent::_selectByBoundaries(
 | 
			
		||||
				['`uid = ?` AND NOT `ignore`',$uid],
 | 
			
		||||
				['order' => ['id' => 'DESC']],
 | 
			
		||||
				$min_id, $max_id, $limit);
 | 
			
		||||
		} catch (\Exception $e) {
 | 
			
		||||
			throw new IntroductionPersistenceException(sprintf('Cannot select Introductions for used %d', $uid), $e);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return new Collection\Introductions($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Selects the introduction for a given contact
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int $cid
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return Entity\Introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws IntroductionNotFoundException in case there is not Introduction for this contact
 | 
			
		||||
	 */
 | 
			
		||||
	public function selectForContact(int $cid): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			return $this->selectOne(['contact-id' => $cid]);
 | 
			
		||||
		} catch (NotFoundException $exception) {
 | 
			
		||||
			throw new IntroductionNotFoundException(sprintf('There is no Introduction for the contact %d', $cid), $exception);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function countActiveForUser($uid, array $params = []): int
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			return $this->count(['ignore' => false, 'uid' => $uid], $params);
 | 
			
		||||
		} catch (\Exception $e) {
 | 
			
		||||
			throw new IntroductionPersistenceException(sprintf('Cannot count Introductions for used %d', $uid), $e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Checks, if the suggested contact already exists for the user
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int $sid
 | 
			
		||||
	 * @param int $uid
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 */
 | 
			
		||||
	public function suggestionExistsForUser(int $sid, int $uid): bool
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			return $this->exists(['uid' => $uid, 'suggest-cid' => $sid]);
 | 
			
		||||
		} catch (\Exception $e) {
 | 
			
		||||
			throw new IntroductionPersistenceException(sprintf('Cannot check suggested Introduction for contact %d and user %d', $sid, $uid), $e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param Entity\Introduction $introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws IntroductionPersistenceException in case the underlying storage cannot delete the Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	public function delete(Entity\Introduction $introduction): bool
 | 
			
		||||
	{
 | 
			
		||||
		if (!$introduction->id) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return $this->db->delete(self::$table_name, ['id' => $introduction->id]);
 | 
			
		||||
		} catch (\Exception $e) {
 | 
			
		||||
			throw new IntroductionPersistenceException(sprintf('Cannot delete Introduction with id %d', $introduction->id), $e);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param Entity\Introduction $introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return Entity\Introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws IntroductionPersistenceException In case the underlying storage cannot save the Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	public function save(Entity\Introduction $introduction): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			$fields = $this->convertToTableRow($introduction);
 | 
			
		||||
 | 
			
		||||
			if ($introduction->id) {
 | 
			
		||||
				$this->db->update(self::$table_name, $fields, ['id' => $introduction->id]);
 | 
			
		||||
				return $this->factory->createFromTableRow($fields);
 | 
			
		||||
			} else {
 | 
			
		||||
				$this->db->insert(self::$table_name, $fields);
 | 
			
		||||
				return $this->selectOneById($this->db->lastInsertId(), $introduction->uid);
 | 
			
		||||
			}
 | 
			
		||||
		} catch (\Exception $exception) {
 | 
			
		||||
			throw new IntroductionPersistenceException(sprintf('Cannot insert/update the Introduction %d for user %d', $introduction->id, $introduction->uid), $exception);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								src/Contact/Introduction/Entity/Introduction.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Contact/Introduction/Entity/Introduction.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
<?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\Contact\Introduction\Entity;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseEntity;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @property-read int $uid
 | 
			
		||||
 * @property-read int $cid
 | 
			
		||||
 * @property-read int|null $sid
 | 
			
		||||
 * @property-read bool $knowyou
 | 
			
		||||
 * @property-read string $note
 | 
			
		||||
 * @property-read string $hash
 | 
			
		||||
 * @property-read \DateTime $datetime
 | 
			
		||||
 * @property-read bool $ignore
 | 
			
		||||
 * @property-read int|null $id
 | 
			
		||||
 */
 | 
			
		||||
class Introduction extends BaseEntity
 | 
			
		||||
{
 | 
			
		||||
	/** @var int */
 | 
			
		||||
	protected $uid;
 | 
			
		||||
	/** @var int */
 | 
			
		||||
	protected $cid;
 | 
			
		||||
	/** @var int|null */
 | 
			
		||||
	protected $sid;
 | 
			
		||||
	/** @var bool */
 | 
			
		||||
	protected $knowyou;
 | 
			
		||||
	/** @var string */
 | 
			
		||||
	protected $note;
 | 
			
		||||
	/** @var string */
 | 
			
		||||
	protected $hash;
 | 
			
		||||
	/** @var \DateTime */
 | 
			
		||||
	protected $datetime;
 | 
			
		||||
	/** @var bool */
 | 
			
		||||
	protected $ignore;
 | 
			
		||||
	/** @var int|null */
 | 
			
		||||
	protected $id;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param int       $uid
 | 
			
		||||
	 * @param int       $cid
 | 
			
		||||
	 * @param int|null  $sid
 | 
			
		||||
	 * @param bool      $knowyou
 | 
			
		||||
	 * @param string    $note
 | 
			
		||||
	 * @param string    $hash
 | 
			
		||||
	 * @param \DateTime $datetime
 | 
			
		||||
	 * @param bool      $ignore
 | 
			
		||||
	 * @param int|null  $id
 | 
			
		||||
	 */
 | 
			
		||||
	public function __construct(int $uid, int $cid, ?int $sid, bool $knowyou, string $note, string $hash, \DateTime $datetime, bool $ignore, ?int $id)
 | 
			
		||||
	{
 | 
			
		||||
		$this->uid      = $uid;
 | 
			
		||||
		$this->cid      = $cid;
 | 
			
		||||
		$this->sid      = $sid;
 | 
			
		||||
		$this->knowyou  = $knowyou;
 | 
			
		||||
		$this->note     = $note;
 | 
			
		||||
		$this->hash     = $hash;
 | 
			
		||||
		$this->datetime = $datetime;
 | 
			
		||||
		$this->ignore   = $ignore;
 | 
			
		||||
		$this->id       = $id;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Ignore the current Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	public function ignore()
 | 
			
		||||
	{
 | 
			
		||||
		$this->ignore = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Contact\Introduction\Exception;
 | 
			
		||||
 | 
			
		||||
class IntroductionNotFoundException extends \OutOfBoundsException
 | 
			
		||||
{
 | 
			
		||||
	public function __construct($message = "", \Throwable $previous = null)
 | 
			
		||||
	{
 | 
			
		||||
		parent::__construct($message, 404, $previous);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Contact\Introduction\Exception;
 | 
			
		||||
 | 
			
		||||
class IntroductionPersistenceException extends \RuntimeException
 | 
			
		||||
{
 | 
			
		||||
	public function __construct($message = "", \Throwable $previous = null)
 | 
			
		||||
	{
 | 
			
		||||
		parent::__construct($message, 500, $previous);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								src/Contact/Introduction/Factory/Introduction.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/Contact/Introduction/Factory/Introduction.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
<?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\Contact\Introduction\Factory;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseFactory;
 | 
			
		||||
use Friendica\Contact\Introduction\Entity;
 | 
			
		||||
use Friendica\Capabilities\ICanCreateFromTableRow;
 | 
			
		||||
use Friendica\Util\DateTimeFormat;
 | 
			
		||||
use Friendica\Util\Strings;
 | 
			
		||||
 | 
			
		||||
class Introduction extends BaseFactory implements ICanCreateFromTableRow
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * @inheritDoc
 | 
			
		||||
	 */
 | 
			
		||||
	public function createFromTableRow(array $row): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		return new Entity\Introduction(
 | 
			
		||||
			$row['uid'] ?? 0,
 | 
			
		||||
			$row['contact-id'] ?? 0,
 | 
			
		||||
			$row['suggest-cid'] ?? null,
 | 
			
		||||
			!empty($row['knowyou']),
 | 
			
		||||
			$row['note'] ?? '',
 | 
			
		||||
			$row['hash'] ?? '',
 | 
			
		||||
			new \DateTime($row['datetime'] ?? 'now', new \DateTimeZone('UTC')),
 | 
			
		||||
			!empty($row['ignore']),
 | 
			
		||||
			$row['id'] ?? null
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function createNew(
 | 
			
		||||
		int $uid,
 | 
			
		||||
		int $cid,
 | 
			
		||||
		string $note,
 | 
			
		||||
		int $sid = null,
 | 
			
		||||
		bool $knowyou = false
 | 
			
		||||
	): Entity\Introduction {
 | 
			
		||||
		return $this->createFromTableRow([
 | 
			
		||||
			'uid'         => $uid,
 | 
			
		||||
			'suggest-cid' => $sid,
 | 
			
		||||
			'contact-id'  => $cid,
 | 
			
		||||
			'knowyou'     => $knowyou,
 | 
			
		||||
			'note'        => $note,
 | 
			
		||||
			'hash'        => Strings::getRandomHex(),
 | 
			
		||||
			'datetime'    => DateTimeFormat::utcNow(),
 | 
			
		||||
			'ignore'      => false,
 | 
			
		||||
		]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function createDummy(?int $id): Entity\Introduction
 | 
			
		||||
	{
 | 
			
		||||
		return $this->createFromTableRow(['id' => $id]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/DI.php
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								src/DI.php
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -435,11 +435,19 @@ abstract class DI
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return Repository\Introduction
 | 
			
		||||
	 * @return Contact\Introduction\Depository\Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	public static function intro()
 | 
			
		||||
	{
 | 
			
		||||
		return self::$dice->create(Repository\Introduction::class);
 | 
			
		||||
		return self::$dice->create(Contact\Introduction\Depository\Introduction::class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return Contact\Introduction\Factory\Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	public static function introFactory()
 | 
			
		||||
	{
 | 
			
		||||
		return self::$dice->create(Contact\Introduction\Factory\Introduction::class);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function permissionSet(): Security\PermissionSet\Depository\PermissionSet
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,9 +23,9 @@ namespace Friendica\Factory\Api\Mastodon;
 | 
			
		|||
 | 
			
		||||
use Friendica\App\BaseURL;
 | 
			
		||||
use Friendica\BaseFactory;
 | 
			
		||||
use Friendica\Contact\Introduction\Entity\Introduction;
 | 
			
		||||
use Friendica\Model\APContact;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Model\Introduction;
 | 
			
		||||
use Friendica\Network\HTTPException;
 | 
			
		||||
use ImagickException;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ class FollowRequest extends BaseFactory
 | 
			
		|||
	 */
 | 
			
		||||
	public function createFromIntroduction(Introduction $introduction): \Friendica\Object\Api\Mastodon\FollowRequest
 | 
			
		||||
	{
 | 
			
		||||
		$cdata = Contact::getPublicAndUserContactID($introduction->{'contact-id'}, $introduction->uid);
 | 
			
		||||
		$cdata = Contact::getPublicAndUserContactID($introduction->cid, $introduction->uid);
 | 
			
		||||
 | 
			
		||||
		if (empty($cdata)) {
 | 
			
		||||
			$this->logger->warning('Wrong introduction data', ['Introduction' => $introduction]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@
 | 
			
		|||
namespace Friendica\Model;
 | 
			
		||||
 | 
			
		||||
use Friendica\App\BaseURL;
 | 
			
		||||
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
 | 
			
		||||
use Friendica\Content\Pager;
 | 
			
		||||
use Friendica\Content\Text\HTML;
 | 
			
		||||
use Friendica\Core\Hook;
 | 
			
		||||
| 
						 | 
				
			
			@ -1085,9 +1086,11 @@ class Contact
 | 
			
		|||
			];
 | 
			
		||||
 | 
			
		||||
			if (!empty($contact['pending'])) {
 | 
			
		||||
				$intro = DBA::selectFirst('intro', ['id'], ['contact-id' => $contact['id']]);
 | 
			
		||||
				if (DBA::isResult($intro)) {
 | 
			
		||||
				try {
 | 
			
		||||
					$intro = DI::intro()->selectForContact($contact['id']);
 | 
			
		||||
					$menu['follow'] = [DI::l10n()->t('Approve'), 'notifications/intros/' . $intro['id'], true];
 | 
			
		||||
				} catch (IntroductionNotFoundException $exception) {
 | 
			
		||||
					DI::logger()->error('Pending contact doesn\'t have an introduction.', ['exception' => $exception]);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -2706,12 +2709,13 @@ class Contact
 | 
			
		|||
			$user = DBA::selectFirst('user', $fields, ['uid' => $importer['uid']]);
 | 
			
		||||
			if (DBA::isResult($user) && !in_array($user['page-flags'], [User::PAGE_FLAGS_SOAPBOX, User::PAGE_FLAGS_FREELOVE, User::PAGE_FLAGS_COMMUNITY])) {
 | 
			
		||||
				// create notification
 | 
			
		||||
				$hash = Strings::getRandomHex();
 | 
			
		||||
 | 
			
		||||
				if (is_array($contact_record)) {
 | 
			
		||||
					DBA::insert('intro', ['uid' => $importer['uid'], 'contact-id' => $contact_record['id'],
 | 
			
		||||
								'blocked' => false, 'knowyou' => false, 'note' => $note,
 | 
			
		||||
								'hash' => $hash, 'datetime' => DateTimeFormat::utcNow()]);
 | 
			
		||||
					$intro = DI::introFactory()->createNew(
 | 
			
		||||
						$importer['uid'],
 | 
			
		||||
						$contact_record['id'],
 | 
			
		||||
						$note
 | 
			
		||||
					);
 | 
			
		||||
					DI::intro()->save($intro);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Group::addMember(User::getDefaultGroup($importer['uid'], $contact_record["network"]), $contact_record['id']);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,64 +19,43 @@
 | 
			
		|||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Model;
 | 
			
		||||
namespace Friendica\Model\Contact;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseModel;
 | 
			
		||||
use Friendica\Contact\Introduction\Entity;
 | 
			
		||||
use Friendica\Core\Protocol;
 | 
			
		||||
use Friendica\Database\Database;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Network\HTTPException;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Model\User;
 | 
			
		||||
use Friendica\Protocol\ActivityPub;
 | 
			
		||||
use Friendica\Protocol\Diaspora;
 | 
			
		||||
use Friendica\Repository;
 | 
			
		||||
use Friendica\Util\DateTimeFormat;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @property int    uid
 | 
			
		||||
 * @property int    fid
 | 
			
		||||
 * @property int    contact-id
 | 
			
		||||
 * @property bool   knowyou
 | 
			
		||||
 * @property bool   duplex
 | 
			
		||||
 * @property string note
 | 
			
		||||
 * @property string hash
 | 
			
		||||
 * @property string datetime
 | 
			
		||||
 * @property bool   blocked
 | 
			
		||||
 * @property bool   ignore
 | 
			
		||||
 */
 | 
			
		||||
class Introduction extends BaseModel
 | 
			
		||||
class Introduction
 | 
			
		||||
{
 | 
			
		||||
	/** @var Repository\Introduction */
 | 
			
		||||
	protected $intro;
 | 
			
		||||
 | 
			
		||||
	public function __construct(Database $dba, LoggerInterface $logger, Repository\Introduction $intro, array $data = [])
 | 
			
		||||
	{
 | 
			
		||||
		parent::__construct($dba, $logger, $data);
 | 
			
		||||
 | 
			
		||||
		$this->intro = $intro;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Confirms a follow request and sends a notice to the remote contact.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param bool               $duplex       Is it a follow back?
 | 
			
		||||
	 * @param bool|null          $hidden       Should this contact be hidden? null = no change
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 * @param Entity\Introduction $introduction
 | 
			
		||||
	 * @param bool                $duplex       Is it a follow back?
 | 
			
		||||
	 * @param bool|null           $hidden       Should this contact be hidden? null = no change
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws HTTPException\InternalServerErrorException
 | 
			
		||||
	 * @throws HTTPException\NotFoundException
 | 
			
		||||
	 * @throws \ImagickException
 | 
			
		||||
	 */
 | 
			
		||||
	public function confirm(bool $duplex = false, bool $hidden = null)
 | 
			
		||||
	public static function confirm(Entity\Introduction $introduction, bool $duplex = false, ?bool $hidden = null): void
 | 
			
		||||
	{
 | 
			
		||||
		$this->logger->info('Confirming follower', ['cid' => $this->{'contact-id'}]);
 | 
			
		||||
		DI::logger()->info('Confirming follower', ['cid' => $introduction->cid]);
 | 
			
		||||
 | 
			
		||||
		$contact = Contact::selectFirst([], ['id' => $this->{'contact-id'}, 'uid' => $this->uid]);
 | 
			
		||||
		$contact = Contact::selectFirst([], ['id' => $introduction->cid, 'uid' => $introduction->uid]);
 | 
			
		||||
 | 
			
		||||
		if (!$contact) {
 | 
			
		||||
			throw new HTTPException\NotFoundException('Contact record not found.');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$newRelation = $contact['rel'];
 | 
			
		||||
		$writable = $contact['writable'];
 | 
			
		||||
		$writable    = $contact['writable'];
 | 
			
		||||
 | 
			
		||||
		if (!empty($contact['protocol'])) {
 | 
			
		||||
			$protocol = $contact['protocol'];
 | 
			
		||||
| 
						 | 
				
			
			@ -117,53 +96,24 @@ class Introduction extends BaseModel
 | 
			
		|||
		if ($newRelation == Contact::FRIEND) {
 | 
			
		||||
			if ($protocol == Protocol::DIASPORA) {
 | 
			
		||||
				$ret = Diaspora::sendShare(User::getById($contact['uid']), $contact);
 | 
			
		||||
				$this->logger->info('share returns', ['return' => $ret]);
 | 
			
		||||
				DI::logger()->info('share returns', ['return' => $ret]);
 | 
			
		||||
			} elseif ($protocol == Protocol::ACTIVITYPUB) {
 | 
			
		||||
				ActivityPub\Transmitter::sendActivity('Follow', $contact['url'], $contact['uid']);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return $this->intro->delete($this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Silently ignores the introduction, hides it from notifications and prevents the remote contact from submitting
 | 
			
		||||
	 * additional follow requests.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public function ignore()
 | 
			
		||||
	{
 | 
			
		||||
		$this->ignore = true;
 | 
			
		||||
 | 
			
		||||
		return $this->intro->update($this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Discards the introduction and sends a rejection message to AP contacts.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 * @param Entity\Introduction $introduction
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws HTTPException\InternalServerErrorException
 | 
			
		||||
	 * @throws HTTPException\NotFoundException
 | 
			
		||||
	 * @throws \ImagickException
 | 
			
		||||
	 */
 | 
			
		||||
	public function discard()
 | 
			
		||||
	public static function discard(Entity\Introduction $introduction): void
 | 
			
		||||
	{
 | 
			
		||||
		// If it is a friend suggestion, the contact is not a new friend but an existing friend
 | 
			
		||||
		// that should not be deleted.
 | 
			
		||||
		if (!$this->fid) {
 | 
			
		||||
			// When the contact entry had been created just for that intro, we want to get rid of it now
 | 
			
		||||
			$condition = ['id' => $this->{'contact-id'}, 'uid' => $this->uid,
 | 
			
		||||
				'self' => false, 'pending' => true, 'rel' => [0, Contact::FOLLOWER]];
 | 
			
		||||
			if ($this->dba->exists('contact', $condition)) {
 | 
			
		||||
				Contact::remove($this->{'contact-id'});
 | 
			
		||||
			} else {
 | 
			
		||||
				Contact::update(['pending' => false], ['id' => $this->{'contact-id'}]);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$contact = Contact::selectFirst([], ['id' => $this->{'contact-id'}, 'uid' => $this->uid]);
 | 
			
		||||
		$contact = Contact::selectFirst([], ['id' => $introduction->cid, 'uid' => $introduction->uid]);
 | 
			
		||||
		if (!empty($contact)) {
 | 
			
		||||
			if (!empty($contact['protocol'])) {
 | 
			
		||||
				$protocol = $contact['protocol'];
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +125,5 @@ class Introduction extends BaseModel
 | 
			
		|||
				ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $contact['uid']);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return $this->intro->delete($this);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ namespace Friendica\Module\Api\Mastodon;
 | 
			
		|||
 | 
			
		||||
use Friendica\Core\System;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Module\BaseApi;
 | 
			
		||||
use Friendica\Network\HTTPException;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +35,6 @@ class FollowRequests extends BaseApi
 | 
			
		|||
	/**
 | 
			
		||||
	 * @param array $parameters
 | 
			
		||||
	 * @throws HTTPException\BadRequestException
 | 
			
		||||
	 * @throws HTTPException\ForbiddenException
 | 
			
		||||
	 * @throws HTTPException\InternalServerErrorException
 | 
			
		||||
	 * @throws HTTPException\NotFoundException
 | 
			
		||||
	 * @throws HTTPException\UnauthorizedException
 | 
			
		||||
| 
						 | 
				
			
			@ -48,25 +48,28 @@ class FollowRequests extends BaseApi
 | 
			
		|||
		self::checkAllowedScope(self::SCOPE_FOLLOW);
 | 
			
		||||
		$uid = self::getCurrentUserID();
 | 
			
		||||
 | 
			
		||||
		$introduction = DI::intro()->selectFirst(['id' => $parameters['id'], 'uid' => $uid]);
 | 
			
		||||
		$introduction = DI::intro()->selectOneById($parameters['id'], $uid);
 | 
			
		||||
 | 
			
		||||
		$contactId = $introduction->{'contact-id'};
 | 
			
		||||
		$contactId = $introduction->cid;
 | 
			
		||||
 | 
			
		||||
		switch ($parameters['action']) {
 | 
			
		||||
			case 'authorize':
 | 
			
		||||
				$introduction->confirm();
 | 
			
		||||
 | 
			
		||||
				Contact\Introduction::confirm($introduction);
 | 
			
		||||
				$relationship = DI::mstdnRelationship()->createFromContactId($contactId, $uid);
 | 
			
		||||
 | 
			
		||||
				DI::intro()->delete($introduction);
 | 
			
		||||
				break;
 | 
			
		||||
			case 'ignore':
 | 
			
		||||
				$introduction->ignore();
 | 
			
		||||
 | 
			
		||||
				$relationship = DI::mstdnRelationship()->createFromContactId($contactId, $uid);
 | 
			
		||||
 | 
			
		||||
				DI::intro()->save($introduction);
 | 
			
		||||
				break;
 | 
			
		||||
			case 'reject':
 | 
			
		||||
				$introduction->discard();
 | 
			
		||||
 | 
			
		||||
				Contact\Introduction::discard($introduction);
 | 
			
		||||
				$relationship = DI::mstdnRelationship()->createFromContactId($contactId, $uid);
 | 
			
		||||
 | 
			
		||||
				DI::intro()->delete($introduction);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				throw new HTTPException\BadRequestException('Unexpected action parameter, expecting "authorize", "ignore" or "reject"');
 | 
			
		||||
| 
						 | 
				
			
			@ -92,13 +95,7 @@ class FollowRequests extends BaseApi
 | 
			
		|||
			'limit'  => 40, // Maximum number of results to return. Defaults to 40. Paginate using the HTTP Link header.
 | 
			
		||||
		]);
 | 
			
		||||
 | 
			
		||||
		$introductions = DI::intro()->selectByBoundaries(
 | 
			
		||||
			['`uid` = ? AND NOT `ignore`', $uid],
 | 
			
		||||
			['order' => ['id' => 'DESC']],
 | 
			
		||||
			$request['min_id'],
 | 
			
		||||
			$request['max_id'],
 | 
			
		||||
			$request['limit']
 | 
			
		||||
		);
 | 
			
		||||
		$introductions = DI::intro()->selectForUser($uid, $request['min_id'], $request['max_id'], $request['limit']);
 | 
			
		||||
 | 
			
		||||
		$return = [];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,7 +133,7 @@ class Delegation extends BaseModule
 | 
			
		|||
			$params = ['distinct' => true, 'expression' => 'convid'];
 | 
			
		||||
			$notifications += DBA::count('mail', ['uid' => $identity['uid'], 'seen' => false], $params);
 | 
			
		||||
 | 
			
		||||
			$notifications += DBA::count('intro', ['blocked' => false, 'ignore' => false, 'uid' => $identity['uid']]);
 | 
			
		||||
			$notifications += DI::intro()->countActiveForUser($identity['uid']);
 | 
			
		||||
 | 
			
		||||
			$identities[$key]['notifications'] = $notifications;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ namespace Friendica\Module;
 | 
			
		|||
 | 
			
		||||
use Friendica\BaseModule;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Process follow request confirmations
 | 
			
		||||
| 
						 | 
				
			
			@ -21,12 +22,11 @@ class FollowConfirm extends BaseModule
 | 
			
		|||
		$duplex   = intval($_POST['duplex']     ?? 0);
 | 
			
		||||
		$hidden   = intval($_POST['hidden']     ?? 0);
 | 
			
		||||
 | 
			
		||||
		$intro = DI::intro()->selectFirst(['id' => $intro_id, 'uid' => local_user()]);
 | 
			
		||||
		$intro = DI::intro()->selectOneById($intro_id, local_user());
 | 
			
		||||
 | 
			
		||||
		$cid = $intro->{'contact-id'};
 | 
			
		||||
		Contact\Introduction::confirm($intro, $duplex, $hidden);
 | 
			
		||||
		DI::intro()->delete($intro);
 | 
			
		||||
 | 
			
		||||
		$intro->confirm($duplex, $hidden);
 | 
			
		||||
 | 
			
		||||
		DI::baseUrl()->redirect('contact/' . intval($cid));
 | 
			
		||||
		DI::baseUrl()->redirect('contact/' .  $intro->cid);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ namespace Friendica\Module\Notifications;
 | 
			
		|||
use Friendica\BaseModule;
 | 
			
		||||
use Friendica\Core\System;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Module\Security\Login;
 | 
			
		||||
use Friendica\Network\HTTPException;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,14 +51,16 @@ class Notification extends BaseModule
 | 
			
		|||
		$request_id = $parameters['id'] ?? false;
 | 
			
		||||
 | 
			
		||||
		if ($request_id) {
 | 
			
		||||
			$intro = DI::intro()->selectFirst(['id' => $request_id, 'uid' => local_user()]);
 | 
			
		||||
			$intro = DI::intro()->selectOneById($request_id, local_user());
 | 
			
		||||
 | 
			
		||||
			switch ($_POST['submit']) {
 | 
			
		||||
				case DI::l10n()->t('Discard'):
 | 
			
		||||
					$intro->discard();
 | 
			
		||||
					Contact\Introduction::discard($intro);
 | 
			
		||||
					DI::intro()->delete($intro);
 | 
			
		||||
					break;
 | 
			
		||||
				case DI::l10n()->t('Ignore'):
 | 
			
		||||
					$intro->ignore();
 | 
			
		||||
					DI::intro()->save($intro);
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -485,9 +485,9 @@ class Notify extends BaseDepository
 | 
			
		|||
	private function storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page)
 | 
			
		||||
	{
 | 
			
		||||
		$item_id = $params['item']['id'] ?? 0;
 | 
			
		||||
		$uri_id = $params['item']['uri-id'] ?? 0;
 | 
			
		||||
		$uri_id = $params['item']['uri-id'] ?? null;
 | 
			
		||||
		$parent_id = $params['item']['parent'] ?? 0;
 | 
			
		||||
		$parent_uri_id = $params['item']['parent-uri-id'] ?? 0;
 | 
			
		||||
		$parent_uri_id = $params['item']['parent-uri-id'] ?? null;
 | 
			
		||||
 | 
			
		||||
		// Ensure that the important fields are set at any time
 | 
			
		||||
		$fields = ['nickname'];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,14 +60,14 @@ class Notify extends BaseEntity
 | 
			
		|||
	protected $name_cache;
 | 
			
		||||
	/** @var string */
 | 
			
		||||
	protected $msg_cache;
 | 
			
		||||
	/** @var int */
 | 
			
		||||
	/** @var int|null */
 | 
			
		||||
	protected $uriId;
 | 
			
		||||
	/** @var int */
 | 
			
		||||
	/** @var int|null */
 | 
			
		||||
	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)
 | 
			
		||||
	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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,7 +86,6 @@ class ProfileField extends BaseDepository
 | 
			
		|||
 | 
			
		||||
		$Entities = new Collection\ProfileFields();
 | 
			
		||||
		foreach ($rows as $fields) {
 | 
			
		||||
			$this->logger->warning('row', ['row' => $fields]);
 | 
			
		||||
			$Entities[] = $this->factory->createFromTableRow($fields);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1352,7 +1352,7 @@ class DFRN
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		// Quit if we already have an introduction for this person
 | 
			
		||||
		if (DBA::exists('intro', ['uid' => $uid, 'suggest-cid' => $cid])) {
 | 
			
		||||
		if (DI::intro()->suggestionExistsForUser($cid, $uid)) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1366,10 +1366,13 @@ class DFRN
 | 
			
		|||
		$suggest['title'] = '';
 | 
			
		||||
		$suggest['body'] = $note;
 | 
			
		||||
 | 
			
		||||
		$hash = Strings::getRandomHex();
 | 
			
		||||
		$fields = ['uid' => $suggest['uid'], 'suggest-cid' => $cid, 'contact-id' => $suggest['cid'],
 | 
			
		||||
			'note' => $suggest['body'], 'hash' => $hash, 'datetime' => DateTimeFormat::utcNow(), 'blocked' => false];
 | 
			
		||||
		DBA::insert('intro', $fields);
 | 
			
		||||
		DI::intro()->save(DI::introFactory()->createNew(
 | 
			
		||||
			$suggest['uid'],
 | 
			
		||||
			$suggest['cid'],
 | 
			
		||||
			$suggest['body'],
 | 
			
		||||
			null,
 | 
			
		||||
			$cid
 | 
			
		||||
		));
 | 
			
		||||
 | 
			
		||||
		DI::notify()->createFromArray([
 | 
			
		||||
			'type'  => Notification\Type::SUGGEST,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,79 +0,0 @@
 | 
			
		|||
<?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\Repository;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseRepository;
 | 
			
		||||
use Friendica\Collection;
 | 
			
		||||
use Friendica\Model;
 | 
			
		||||
 | 
			
		||||
class Introduction extends BaseRepository
 | 
			
		||||
{
 | 
			
		||||
	protected static $table_name = 'intro';
 | 
			
		||||
 | 
			
		||||
	protected static $model_class = Model\Introduction::class;
 | 
			
		||||
 | 
			
		||||
	protected static $collection_class = Collection\Introductions::class;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param array $data
 | 
			
		||||
	 * @return Model\Introduction
 | 
			
		||||
	 */
 | 
			
		||||
	protected function create(array $data)
 | 
			
		||||
	{
 | 
			
		||||
		return new Model\Introduction($this->dba, $this->logger, $this, $data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param array $condition
 | 
			
		||||
	 * @return Model\Introduction
 | 
			
		||||
	 * @throws \Friendica\Network\HTTPException\NotFoundException
 | 
			
		||||
	 */
 | 
			
		||||
	public function selectFirst(array $condition)
 | 
			
		||||
	{
 | 
			
		||||
		return parent::selectFirst($condition);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param array $condition
 | 
			
		||||
	 * @param array $params
 | 
			
		||||
	 * @return Collection\Introductions
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public function select(array $condition = [], array $params = [])
 | 
			
		||||
	{
 | 
			
		||||
		return parent::select($condition, $params);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param array    $condition
 | 
			
		||||
	 * @param array    $params
 | 
			
		||||
	 * @param int|null $min_id
 | 
			
		||||
	 * @param int|null $max_id
 | 
			
		||||
	 * @param int      $limit
 | 
			
		||||
	 * @return Collection\Introductions
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT)
 | 
			
		||||
	{
 | 
			
		||||
		return parent::selectByBoundaries($condition, $params, $min_id, $max_id, $limit);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ namespace Friendica\Worker\Contact;
 | 
			
		|||
use Friendica\Core\Logger;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\Database\DBStructure;
 | 
			
		||||
use Friendica\DI;
 | 
			
		||||
use Friendica\Model\Photo;
 | 
			
		||||
use Friendica\Model\Post;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +85,7 @@ class RemoveContent
 | 
			
		|||
		DBA::delete('user-contact', ['cid' => $id]);
 | 
			
		||||
 | 
			
		||||
		DBA::delete('group_member', ['contact-id' => $id]);
 | 
			
		||||
		DBA::delete('intro', ['contact-id' => $id]);
 | 
			
		||||
		DI::intro()->delete(DI::introFactory()->createDummy($id));
 | 
			
		||||
 | 
			
		||||
		return $contact;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@
 | 
			
		|||
use Friendica\Database\DBA;
 | 
			
		||||
 | 
			
		||||
if (!defined('DB_UPDATE_VERSION')) {
 | 
			
		||||
	define('DB_UPDATE_VERSION', 1441);
 | 
			
		||||
	define('DB_UPDATE_VERSION', 1442);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
return [
 | 
			
		||||
| 
						 | 
				
			
			@ -778,11 +778,11 @@ return [
 | 
			
		|||
			"contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => ""],
 | 
			
		||||
			"suggest-cid" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Suggested contact"],
 | 
			
		||||
			"knowyou" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
 | 
			
		||||
			"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
 | 
			
		||||
			"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "deprecated"],
 | 
			
		||||
			"note" => ["type" => "text", "comment" => ""],
 | 
			
		||||
			"hash" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
 | 
			
		||||
			"datetime" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
 | 
			
		||||
			"blocked" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => ""],
 | 
			
		||||
			"blocked" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "deprecated"],
 | 
			
		||||
			"ignore" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
 | 
			
		||||
		],
 | 
			
		||||
		"indexes" => [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										113
									
								
								tests/src/Contact/Introduction/Factory/IntroductionTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								tests/src/Contact/Introduction/Factory/IntroductionTest.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Test\src\Contact\Introduction\Factory;
 | 
			
		||||
 | 
			
		||||
use Friendica\Contact\Introduction\Factory\Introduction;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Psr\Log\NullLogger;
 | 
			
		||||
 | 
			
		||||
class IntroductionTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
	public function dataRow()
 | 
			
		||||
	{
 | 
			
		||||
		return [
 | 
			
		||||
			'default' => [
 | 
			
		||||
				'input' => [
 | 
			
		||||
					'uid'         => 42,
 | 
			
		||||
					'suggest-cid' => 13,
 | 
			
		||||
					'contact-id'  => 24,
 | 
			
		||||
					'knowyou'     => 1,
 | 
			
		||||
					'note'        => 'a note',
 | 
			
		||||
					'hash'        => '12345',
 | 
			
		||||
					'datetime'    => '1970-01-01 00:00:00',
 | 
			
		||||
					'ignore'      => 0,
 | 
			
		||||
					'id'          => 56,
 | 
			
		||||
				],
 | 
			
		||||
				'assertion' => [
 | 
			
		||||
					'uid'         => 42,
 | 
			
		||||
					'suggest-cid' => 13,
 | 
			
		||||
					'contact-id'  => 24,
 | 
			
		||||
					'knowyou'     => true,
 | 
			
		||||
					'note'        => 'a note',
 | 
			
		||||
					'hash'        => '12345',
 | 
			
		||||
					'datetime'    => new \DateTime('1970-01-01 00:00:00', new \DateTimeZone('UTC')),
 | 
			
		||||
					'ignore'      => false,
 | 
			
		||||
					'id'          => 56,
 | 
			
		||||
				]
 | 
			
		||||
			],
 | 
			
		||||
			'empty' => [
 | 
			
		||||
				'input' => [
 | 
			
		||||
				],
 | 
			
		||||
				'assertion' => [
 | 
			
		||||
					'uid'         => 0,
 | 
			
		||||
					'contact-id'  => 0,
 | 
			
		||||
					'suggest-cid' => null,
 | 
			
		||||
					'knowyou'     => false,
 | 
			
		||||
					'note'        => '',
 | 
			
		||||
					'ignore'      => false,
 | 
			
		||||
					'id'          => null,
 | 
			
		||||
				]
 | 
			
		||||
			],
 | 
			
		||||
		];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public function assertIntro(\Friendica\Contact\Introduction\Entity\Introduction $intro, array $assertion)
 | 
			
		||||
	{
 | 
			
		||||
		self::assertEquals($intro->id, $assertion['id'] ?? null);
 | 
			
		||||
		self::assertEquals($intro->uid, $assertion['uid'] ?? 0);
 | 
			
		||||
		self::assertEquals($intro->cid, $assertion['contact-id'] ?? 0);
 | 
			
		||||
		self::assertEquals($intro->sid, $assertion['suggest-cid'] ?? null);
 | 
			
		||||
		self::assertEquals($intro->knowyou, $assertion['knowyou'] ?? false);
 | 
			
		||||
		self::assertEquals($intro->note, $assertion['note'] ?? '');
 | 
			
		||||
		if (isset($assertion['hash'])) {
 | 
			
		||||
			self::assertEquals($intro->hash, $assertion['hash']);
 | 
			
		||||
		} else {
 | 
			
		||||
			self::assertIsString($intro->hash);
 | 
			
		||||
		}
 | 
			
		||||
		if (isset($assertion['datetime'])) {
 | 
			
		||||
			self::assertEquals($intro->datetime, $assertion['datetime']);
 | 
			
		||||
		} else {
 | 
			
		||||
			self::assertInstanceOf(\DateTime::class, $intro->datetime);
 | 
			
		||||
		}
 | 
			
		||||
		self::assertEquals($intro->ignore, $assertion['ignore'] ?? false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @dataProvider dataRow
 | 
			
		||||
	 */
 | 
			
		||||
	public function testCreateFromTableRow(array $input, array $assertion)
 | 
			
		||||
	{
 | 
			
		||||
		$factory = new Introduction(new NullLogger());
 | 
			
		||||
 | 
			
		||||
		$intro = $factory->createFromTableRow($input);
 | 
			
		||||
		$this->assertIntro($intro, $assertion);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @dataProvider dataRow
 | 
			
		||||
	 */
 | 
			
		||||
	public function testCreateNew(array $input, array $assertion)
 | 
			
		||||
	{
 | 
			
		||||
		$factory = new Introduction(new NullLogger());
 | 
			
		||||
 | 
			
		||||
		$intro = $factory->createNew($input['uid'] ?? 0, $input['cid'] ?? 0, $input['note'] ?? '');
 | 
			
		||||
 | 
			
		||||
		$this->assertIntro($intro, [
 | 
			
		||||
			'uid'        => $input['uid'] ?? 0,
 | 
			
		||||
			'contact-id' => $input['cid'] ?? 0,
 | 
			
		||||
			'note'       => $input['note'] ?? '',
 | 
			
		||||
		]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @dataProvider dataRow
 | 
			
		||||
	 */
 | 
			
		||||
	public function testCreateDummy(array $input, array $assertion)
 | 
			
		||||
	{
 | 
			
		||||
		$factory = new Introduction(new NullLogger());
 | 
			
		||||
 | 
			
		||||
		$intro = $factory->createDummy($input['id'] ?? null);
 | 
			
		||||
 | 
			
		||||
		$this->assertIntro($intro, ['id' => $input['id'] ?? null]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								update.php
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								update.php
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1039,7 +1039,7 @@ function update_1440()
 | 
			
		|||
	return Update::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update__1441()
 | 
			
		||||
function update_1441()
 | 
			
		||||
{
 | 
			
		||||
	$languages = DI::l10n()->getAvailableLanguages();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1053,3 +1053,11 @@ function update__1441()
 | 
			
		|||
 | 
			
		||||
	return Update::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update_1442()
 | 
			
		||||
{
 | 
			
		||||
	// transform blocked intros into ignored intros
 | 
			
		||||
	DBA::update('intro', ['ignore' => 1, 'blocked' => 0], ['blocked' => 1]);
 | 
			
		||||
 | 
			
		||||
	return Update::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue