diff --git a/src/Entity/Attention.php b/src/Entity/Attention.php
index 2d0d19efc9..0c12c0a963 100644
--- a/src/Entity/Attention.php
+++ b/src/Entity/Attention.php
@@ -1,80 +1,65 @@
.
-/*
- * Data class for Attentions
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
*
- * @category Data
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for attentions
+ *
+ * @category DB
* @package GNUsocial
- * @copyright 2014 Free Software Foundation, Inc http://www.fsf.org
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Attention extends Managed_DataObject
+class Attention
{
- public $__table = 'attention'; // table name
- public $notice_id; // int(4) primary_key not_null
- public $profile_id; // int(4) primary_key not_null
- public $reason; // varchar(191)
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE BEGIN
- public static function schemaDef()
+ // AUTOCODE END
+
+ public static function schemaDef(): array
{
- return array(
+ return [
+ 'name' => 'attention',
'description' => 'Notice attentions to profiles (that are not a mention and not result of a subscription)',
- 'fields' => array(
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'),
- 'reason' => array('type' => 'varchar', 'length' => 191, 'description' => 'Optional reason why this was brought to the attention of profile_id'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('notice_id', 'profile_id'),
- 'foreign keys' => array(
- 'attention_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- 'attention_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array(
- 'attention_profile_id_idx' => array('profile_id'),
- ),
- );
- }
-
- public static function saveNew(Notice $notice, Profile $target, $reason=null)
- {
- try {
- $att = Attention::getByKeys(['notice_id'=>$notice->getID(), 'profile_id'=>$target->getID()]);
- throw new AlreadyFulfilledException('Attention already exists with reason: '._ve($att->reason));
- } catch (NoResultException $e) {
- $att = new Attention();
-
- $att->notice_id = $notice->getID();
- $att->profile_id = $target->getID();
- $att->reason = $reason;
- $att->created = common_sql_now();
- $result = $att->insert();
-
- if ($result === false) {
- throw new Exception('Failed Attention::saveNew for notice id=='.$notice->getID().' target id=='.$target->getID().', reason=="'.$reason.'"');
- }
- }
- self::blow('attention:stream:%d', $target->getID());
- return $att;
+ 'fields' => [
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'],
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'],
+ 'reason' => ['type' => 'varchar', 'length' => 191, 'description' => 'Optional reason why this was brought to the attention of profile_id'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['notice_id', 'profile_id'],
+ 'foreign keys' => [
+ 'attention_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ 'attention_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'attention_notice_id_idx' => ['notice_id'],
+ 'attention_profile_id_idx' => ['profile_id'],
+ ],
+ ];
}
}
diff --git a/src/Entity/Avatar.php b/src/Entity/Avatar.php
index 5860a3f5fc..8266d8a785 100644
--- a/src/Entity/Avatar.php
+++ b/src/Entity/Avatar.php
@@ -1,277 +1,68 @@
.
-defined('GNUSOCIAL') || die();
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for avatar
+ * Entity for user's avatar
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-class Avatar extends Managed_DataObject
+class Avatar
{
- public $__table = 'avatar'; // table name
- public $profile_id; // int(4) primary_key not_null
- public $original; // bool default_false
- public $width; // int(4) primary_key not_null
- public $height; // int(4) primary_key not_null
- public $mediatype; // varchar(32) not_null
- public $filename; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE BEGIN
- public static function schemaDef()
+ // AUTOCODE END
+
+ public static function schemaDef(): array
{
- return array(
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
- 'original' => array('type' => 'bool', 'default' => false, 'description' => 'uploaded by user or generated?'),
- 'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'),
- 'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'),
- 'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'),
- 'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('profile_id', 'width', 'height'),
- 'unique keys' => array(
-// 'avatar_filename_key' => array('filename'),
- ),
- 'foreign keys' => array(
- 'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- );
- }
-
- // We clean up the file, too
- public function delete($useWhere = false)
- {
- $filename = $this->filename;
- if (file_exists(Avatar::path($filename))) {
- @unlink(Avatar::path($filename));
- }
-
- return parent::delete($useWhere);
- }
-
- /*
- * Deletes all avatars (but may spare the original) from a profile.
- *
- * @param Profile $target The profile we're deleting avatars of.
- * @param boolean $original Whether original should be removed or not.
- */
- public static function deleteFromProfile(Profile $target, $original = true)
- {
- try {
- $avatars = self::getProfileAvatars($target);
- foreach ($avatars as $avatar) {
- if ($avatar->original && !$original) {
- continue;
- }
- $avatar->delete();
- }
- } catch (NoAvatarException $e) {
- // There are no avatars to delete, a sort of success.
- }
-
- return true;
- }
-
- protected static $_avatars = [];
-
- /*
- * Get an avatar by profile. Currently can't call newSize with $height
- */
- public static function byProfile(Profile $target, $width=null, $height=null)
- {
- $width = intval($width);
- $height = !is_null($height) ? intval($height) : null;
- if (is_null($height)) {
- $height = $width;
- }
-
- $size = "{$width}x{$height}";
- if (!isset(self::$_avatars[$target->id])) {
- self::$_avatars[$target->id] = array();
- } elseif (isset(self::$_avatars[$target->id][$size])) {
- return self::$_avatars[$target->id][$size];
- }
-
- $avatar = null;
- if (Event::handle('StartProfileGetAvatar', array($target, $width, &$avatar))) {
- $avatar = self::pkeyGet(
- array(
- 'profile_id' => $target->id,
- 'width' => $width,
- 'height' => $height,
- )
- );
- Event::handle('EndProfileGetAvatar', array($target, $width, &$avatar));
- }
-
- if (is_null($avatar)) {
- // Obviously we can't find an avatar, so let's resize the original!
- $avatar = Avatar::newSize($target, $width);
- } elseif (!($avatar instanceof Avatar)) {
- throw new NoAvatarException($target, $avatar);
- }
-
- self::$_avatars[$target->id]["{$avatar->width}x{$avatar->height}"] = $avatar;
- return $avatar;
- }
-
- public static function getUploaded(Profile $target)
- {
- $avatar = new Avatar();
- $avatar->profile_id = $target->id;
- $avatar->original = true;
- if (!$avatar->find(true)) {
- throw new NoAvatarException($target, $avatar);
- }
- if (!file_exists(Avatar::path($avatar->filename))) {
- // The delete call may be odd for, say, unmounted filesystems
- // that cause a file to currently not exist, but actually it does...
- $avatar->delete();
- throw new NoAvatarException($target, $avatar);
- }
- return $avatar;
- }
-
- public static function getProfileAvatars(Profile $target)
- {
- $avatar = new Avatar();
- $avatar->profile_id = $target->id;
- if (!$avatar->find()) {
- throw new NoAvatarException($target, $avatar);
- }
- return $avatar->fetchAll();
- }
-
- /**
- * Where should the avatar go for this user?
- * @param int $id user id
- * @param string $extension file extension
- * @param int|null $size file size
- * @param string|null $extra extra bit for the filename
- * @return string
- */
- public static function filename(int $id, string $extension, ?int $size = null, ?string $extra = null)
- {
- if ($size) {
- return $id . '-' . $size . (($extra) ? ('-' . $extra) : '') . $extension;
- } else {
- return $id . '-original' . (($extra) ? ('-' . $extra) : '') . $extension;
- }
- }
-
- public static function path($filename)
- {
- $dir = common_config('avatar', 'dir');
-
- if ($dir[strlen($dir)-1] != '/') {
- $dir .= '/';
- }
-
- return $dir . $filename;
- }
-
- public static function url($filename)
- {
- $path = common_config('avatar', 'url_base');
-
- if ($path[strlen($path)-1] != '/') {
- $path .= '/';
- }
-
- if ($path[0] != '/') {
- $path = '/'.$path;
- }
-
- $server = common_config('avatar', 'server');
-
- if (empty($server)) {
- $server = common_config('site', 'server');
- }
-
- $ssl = (common_config('avatar', 'ssl') || GNUsocial::useHTTPS());
-
- $protocol = ($ssl) ? 'https' : 'http';
-
- return $protocol.'://'.$server.$path.$filename;
- }
-
- public function displayUrl()
- {
- return Avatar::url($this->filename);
- }
-
- public static function urlByProfile(Profile $target, $width = null, $height = null)
- {
- try {
- return self::byProfile($target, $width, $height)->displayUrl();
- } catch (Exception $e) {
- return self::defaultImage($width);
- }
- }
-
- public static function defaultImage($size = null)
- {
- if (is_null($size)) {
- $size = AVATAR_PROFILE_SIZE;
- }
- static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile',
- AVATAR_STREAM_SIZE => 'stream',
- AVATAR_MINI_SIZE => 'mini');
- return Theme::path('default-avatar-'.$sizenames[$size].'.png');
- }
-
- public static function newSize(Profile $target, $width)
- {
- $width = intval($width);
- if ($width < 1 || $width > common_config('avatar', 'maxsize')) {
- // TRANS: An error message when avatar size is unreasonable
- throw new Exception(_m('Avatar size too large'));
- }
- // So far we only have square avatars and I don't have time to
- // rewrite support for non-square ones right now ;)
- $height = $width;
-
- $original = Avatar::getUploaded($target);
-
- $imagefile = new ImageFile(null, Avatar::path($original->filename));
- $filename = Avatar::filename(
- $target->getID(),
- image_type_to_extension($imagefile->preferredType()),
- $width,
- common_timestamp()
- );
- $imagefile->resizeTo(Avatar::path($filename), array('width'=>$width, 'height'=>$height));
-
- $scaled = clone($original);
- $scaled->original = false;
- $scaled->width = $width;
- $scaled->height = $height;
- $scaled->filename = $filename;
- $scaled->created = common_sql_now();
-
- if (!$scaled->insert()) {
- // TRANS: An error message when unable to insert avatar data into the db
- throw new Exception(_m('Could not insert new avatar data to database'));
- }
-
- // Return the new avatar object
- return $scaled;
+ return [
+ 'name' => 'avatar',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'],
+ 'original' => ['type' => 'bool', 'default' => false, 'description' => 'uploaded by user or generated?'],
+ 'width' => ['type' => 'int', 'not null' => true, 'description' => 'image width'],
+ 'height' => ['type' => 'int', 'not null' => true, 'description' => 'image height'],
+ 'mediatype' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'],
+ 'filename' => ['type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['profile_id', 'width', 'height'],
+ 'unique keys' => [
+ // 'avatar_filename_key' => array('filename'),
+ ],
+ 'foreign keys' => [
+ 'avatar_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'avatar_profile_id_idx' => ['profile_id'],
+ ],
+ ];
}
}
diff --git a/src/Entity/Config.php b/src/Entity/Config.php
index c457da1a5a..e989b6a5bf 100644
--- a/src/Entity/Config.php
+++ b/src/Entity/Config.php
@@ -1,164 +1,54 @@
.
- */
+ * along with GNU social. If not, see .
+ }}} */
-if (!defined('GNUSOCIAL')) { exit(1); }
+namespace App\Entity;
/**
- * Table Definition for config
+ * Entity for app configuration
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-class Config extends Managed_DataObject
+class Config
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'config'; // table name
- public $section; // varchar(32) primary_key not_null
- public $setting; // varchar(32) primary_key not_null
- public $value; // text
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
+ public static function schemaDef(): array
{
- return array(
- 'fields' => array(
- 'section' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'),
- 'setting' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'),
- 'value' => array('type' => 'text', 'description' => 'configuration value'),
- ),
- 'primary key' => array('section', 'setting'),
- );
+ return [
+ 'name' => 'config',
+ 'fields' => [
+ 'section' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'],
+ 'setting' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'],
+ 'value' => ['type' => 'text', 'description' => 'configuration value'],
+ ],
+ 'primary key' => ['section', 'setting'],
+ ];
}
-
- const settingsKey = 'config:settings';
-
- static function loadSettings()
- {
- try {
- $settings = self::_getSettings();
- if (!empty($settings)) {
- self::_applySettings($settings);
- }
- } catch (Exception $e) {
- return;
- }
- }
-
- static function _getSettings()
- {
- $c = self::memcache();
-
- if (!empty($c)) {
- $settings = $c->get(Cache::key(self::settingsKey));
- if ($settings !== false) {
- return $settings;
- }
- }
-
- $settings = array();
-
- $config = new Config();
-
- $config->find();
-
- while ($config->fetch()) {
- $settings[] = array($config->section, $config->setting, $config->value);
- }
-
- $config->free();
-
- if (!empty($c)) {
- $c->set(Cache::key(self::settingsKey), $settings);
- }
-
- return $settings;
- }
-
- static function _applySettings($settings)
- {
- global $config;
-
- foreach ($settings as $s) {
- list($section, $setting, $value) = $s;
- $config[$section][$setting] = $value;
- }
- }
-
- function insert()
- {
- $result = parent::insert();
- if ($result) {
- Config::_blowSettingsCache();
- }
- return $result;
- }
-
- function delete($useWhere=false)
- {
- $result = parent::delete($useWhere);
- if ($result !== false) {
- Config::_blowSettingsCache();
- }
- return $result;
- }
-
- function update($dataObject=false)
- {
- $result = parent::update($dataObject);
- if ($result !== false) {
- Config::_blowSettingsCache();
- }
- return $result;
- }
-
- static function save($section, $setting, $value)
- {
- $result = null;
-
- $config = Config::pkeyGet(array('section' => $section,
- 'setting' => $setting));
-
- if (!empty($config)) {
- $orig = clone($config);
- $config->value = $value;
- $result = $config->update($orig);
- } else {
- $config = new Config();
-
- $config->section = $section;
- $config->setting = $setting;
- $config->value = $value;
-
- $result = $config->insert();
- }
-
- return $result;
- }
-
- function _blowSettingsCache()
- {
- $c = self::memcache();
-
- if (!empty($c)) {
- $c->delete(Cache::key(self::settingsKey));
- }
- }
-}
+}
\ No newline at end of file
diff --git a/src/Entity/ConfirmAddress.php b/src/Entity/ConfirmAddress.php
new file mode 100644
index 0000000000..26fe73df88
--- /dev/null
+++ b/src/Entity/ConfirmAddress.php
@@ -0,0 +1,62 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user's email confimation
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ConfirmAddress
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'confirm_address',
+ 'fields' => [
+ 'code' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'],
+ 'user_id' => ['type' => 'int', 'default' => 0, 'description' => 'user who requested confirmation'],
+ 'address' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'],
+ 'address_extra' => ['type' => 'varchar', 'length' => 191, 'description' => 'carrier ID, for SMS'],
+ 'address_type' => ['type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'],
+ 'claimed' => ['type' => 'datetime', 'description' => 'date this was claimed for queueing'],
+ 'sent' => ['type' => 'datetime', 'description' => 'date this was sent for queueing'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['code'],
+ 'foreign keys' => [
+ 'confirm_address_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Confirm_address.php b/src/Entity/Confirm_address.php
deleted file mode 100644
index 2f92e0a59a..0000000000
--- a/src/Entity/Confirm_address.php
+++ /dev/null
@@ -1,183 +0,0 @@
-.
-
-/**
- * Table Definition for confirm_address
- */
-
-defined('GNUSOCIAL') || die();
-
-class Confirm_address extends Managed_DataObject
-{
- public $__table = 'confirm_address'; // table name
- public $code; // varchar(32) primary_key not_null
- public $user_id; // int()
- public $address; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $address_extra; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $address_type; // varchar(8) not_null
- public $claimed; // datetime()
- public $sent; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
- 'user_id' => array('type' => 'int', 'description' => 'user who requested confirmation'),
- 'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
- 'address_extra' => array('type' => 'varchar', 'length' => 191, 'description' => 'carrier ID, for SMS'),
- 'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
- 'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'),
- 'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('code'),
- 'foreign keys' => array(
- 'confirm_address_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- 'indexes' => array(
- 'confirm_address_user_id_idx' => array('user_id'),
- ),
- );
- }
-
- public static function getByAddress($address, $addressType)
- {
- $ca = new Confirm_address();
-
- $ca->address = $address;
- $ca->address_type = $addressType;
-
- if (!$ca->find(true)) {
- throw new NoResultException($ca);
- }
-
- return $ca;
- }
-
- public static function saveNew($user, $address, $addressType, $extra = null)
- {
- $ca = new Confirm_address();
-
- if (!empty($user)) {
- $ca->user_id = $user->id;
- }
-
- $ca->address = $address;
- $ca->address_type = $addressType;
- $ca->address_extra = $extra;
- $ca->code = common_confirmation_code(64);
-
- $ca->insert();
-
- return $ca;
- }
-
- public function getAddress()
- {
- return $this->address;
- }
-
- public function getAddressType()
- {
- return $this->address_type;
- }
-
- public function getCode()
- {
- return $this->code;
- }
-
- public function getProfile()
- {
- return Profile::getByID($this->user_id);
- }
-
- public function getUrl()
- {
- return common_local_url('confirmaddress', array('code' => $this->code));
- }
-
- /**
- * Supply arguments in $args. Currently known args:
- * headers Array with headers (only used for email)
- * nickname How we great the user (defaults to nickname, but can be any string)
- * sitename Name we sign the email with (defaults to sitename, but can be any string)
- * url The confirmation address URL.
- */
- public function sendConfirmation(array $args = [])
- {
- common_debug('Sending confirmation URL for user '._ve($this->user_id).' using '._ve($this->address_type));
-
- $defaults = [
- 'headers' => [],
- 'nickname' => $this->getProfile()->getNickname(),
- 'sitename' => common_config('site', 'name'),
- 'url' => $this->getUrl(),
- ];
- foreach (array_keys($defaults) as $key) {
- if (!isset($args[$key])) {
- $args[$key] = $defaults[$key];
- }
- }
-
- switch ($this->getAddressType()) {
- case 'email':
- $this->sendEmailConfirmation($args);
- break;
- default:
- throw ServerException('Unable to handle confirm_address address type: '._ve($this->address_type));
- }
- }
-
- public function sendEmailConfirmation(array $args = [])
- {
- // TRANS: Subject for address confirmation email.
- $subject = _('Email address confirmation');
-
- // TRANS: Body for address confirmation email.
- // TRANS: %1$s is the addressed user's nickname, %2$s is the StatusNet sitename,
- // TRANS: %3$s is the URL to confirm at.
- $body = sprintf(
- _("Hey, %1\$s.\n\n" .
- "Someone just entered this email address on %2\$s.\n\n" .
- "If it was you, and you want to confirm your entry, ".
- "use the URL below:\n\n\t%3\$s\n\n" .
- "If not, just ignore this message.\n\n".
- "Thanks for your time, \n%2\$s\n"),
- $args['nickname'],
- $args['sitename'],
- $args['url']
- );
-
- require_once INSTALLDIR . '/lib/util/mail.php';
- return mail_to_user($this->getProfile()->getUser(), $subject, $body, $args['headers'], $this->getAddress());
- }
-
- public function delete($useWhere = false)
- {
- $result = parent::delete($useWhere);
-
- if ($result === false) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error displayed when an address confirmation code deletion from the
- // TRANS: database fails in the contact address confirmation action.
- throw new ServerException(_('Could not delete address confirmation.'));
- }
- return $result;
- }
-}
diff --git a/src/Entity/Consumer.php b/src/Entity/Consumer.php
index 1489cee071..78fa7da276 100644
--- a/src/Entity/Consumer.php
+++ b/src/Entity/Consumer.php
@@ -1,95 +1,57 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for consumer
+ * Entity for OAuth consumer
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Consumer extends Managed_DataObject
+class Consumer
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'consumer'; // table name
- public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
- public $consumer_secret; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $seed; // char(32) not_null
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
+ public static function schemaDef(): array
{
- return array(
+ return [
+ 'name' => 'consumer',
'description' => 'OAuth consumer record',
- 'fields' => array(
- 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
- 'consumer_secret' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'secret value'),
- 'seed' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'seed for new tokens by this consumer'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('consumer_key'),
- );
- }
-
- public static function generateNew()
- {
- $cons = new Consumer();
- $rand = common_random_hexstr(16);
-
- $cons->seed = $rand;
- $cons->consumer_key = md5(time() + $rand);
- $cons->consumer_secret = md5(md5(time() + time() + $rand));
- $cons->created = common_sql_now();
-
- return $cons;
- }
-
- /**
- * Delete a Consumer and related tokens and nonces
- *
- * XXX: Should this happen in an OAuthDataStore instead?
- *
- */
- public function delete($useWhere = false)
- {
- // XXX: Is there any reason NOT to do this kind of cleanup?
-
- $this->deleteTokens();
- $this->deleteNonces();
-
- return parent::delete($useWhere);
- }
-
- private function deleteTokens()
- {
- $token = new Token();
- $token->consumer_key = $this->consumer_key;
- $token->delete();
- }
-
- private function deleteNonces()
- {
- $nonce = new Nonce();
- $nonce->consumer_key = $this->consumer_key;
- $nonce->delete();
+ 'fields' => [
+ 'consumer_key' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'],
+ 'consumer_secret' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'secret value'],
+ 'seed' => ['type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'seed for new tokens by this consumer'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['consumer_key'],
+ ];
}
}
diff --git a/src/Entity/Conversation.php b/src/Entity/Conversation.php
index 6929fee4e4..55bf7f349f 100644
--- a/src/Entity/Conversation.php
+++ b/src/Entity/Conversation.php
@@ -1,172 +1,57 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
* Data class for Conversations
*
* @category Data
* @package GNUsocial
+ *
* @author Zach Copley
* @author Mikael Nordfeldth
* @copyright 2010 StatusNet Inc.
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Conversation extends Managed_DataObject
+class Conversation
{
- public $__table = 'conversation'; // table name
- public $id; // int(4) primary_key not_null auto_increment
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE BEGIN
- public static function schemaDef()
+ // AUTOCODE END
+
+ public static function schemaDef(): array
{
- return array(
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
- 'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
- 'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'Resolvable URL, preferrably remote (local can be generated on the fly)'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'conversation_uri_key' => array('uri'),
- ),
- );
- }
-
- public static function beforeSchemaUpdate()
- {
- $table = strtolower(get_called_class());
- $schema = Schema::get();
- $schemadef = $schema->getTableDef($table);
-
- // 2016-01-06 We have to make sure there is no conversation with id==0 since it will screw up auto increment resequencing
- if ($schemadef['fields']['id']['auto_increment'] ?? false) {
- // since we already have auto incrementing ('serial') we can continue
- return;
- }
-
- // The conversation will be recreated in upgrade.php, which will
- // generate a new URI, but that's collateral damage for you.
- $conv = new Conversation();
- $conv->id = 0;
- if ($conv->find()) {
- while ($conv->fetch()) {
- // Since we have filtered on 0 this only deletes such entries
- // which I have been afraid wouldn't work, but apparently does!
- // (I thought it would act as null or something and find _all_ conversation entries)
- $conv->delete();
- }
- }
- }
-
- /**
- * Factory method for creating a new conversation.
- *
- * Use this for locally initiated conversations. Remote notices should
- * preferrably supply their own conversation URIs in the OStatus feed.
- *
- * @return Conversation the new conversation DO
- */
- public static function create(ActivityContext $ctx = null, $created = null)
- {
- // Be aware that the Notice does not have an id yet since it's not inserted!
- $conv = new Conversation();
- $conv->created = $created ?: common_sql_now();
- if ($ctx instanceof ActivityContext) {
- $conv->uri = $ctx->conversation;
- $conv->url = $ctx->conversation_url;
- } else {
- $conv->uri = sprintf(
- '%s%s=%s:%s=%s',
- TagURI::mint(),
- 'objectType',
- 'thread',
- 'nonce',
- common_random_hexstr(8)
- );
- // locally generated Conversation objects don't get static URLs stored
- $conv->url = $conv->sqlValue('NULL');
- }
- // This insert throws exceptions on failure
- $conv->insert();
-
- return $conv;
- }
-
- public static function noticeCount($id)
- {
- $keypart = sprintf('conversation:notice_count:%d', $id);
-
- $cnt = self::cacheGet($keypart);
-
- if ($cnt !== false) {
- return $cnt;
- }
-
- $notice = new Notice();
- $notice->conversation = $id;
- $notice->whereAddIn('verb', array(ActivityVerb::POST, ActivityUtils::resolveUri(ActivityVerb::POST, true)), $notice->columnType('verb'));
- $cnt = $notice->count();
-
- self::cacheSet($keypart, $cnt);
-
- return $cnt;
- }
-
- public static function getUrlFromNotice(Notice $notice, $anchor = true)
- {
- $conv = Conversation::getByID($notice->conversation);
- return $conv->getUrl($anchor ? $notice->getID() : null);
- }
-
- public function getUri()
- {
- return $this->uri;
- }
-
- public function getUrl($noticeId=null)
- {
- // FIXME: the URL router should take notice-id as an argument...
- return common_local_url('conversation', array('id' => $this->getID())) .
- ($noticeId===null ? '' : "#notice-{$noticeId}");
- }
-
- // FIXME: ...will 500 ever be too low? Taken from ConversationAction::MAX_NOTICES
- public function getNotices(Profile $scoped=null, $offset=0, $limit=500)
- {
- $stream = new ConversationNoticeStream($this->getID(), $scoped);
- $notices = $stream->getNotices($offset, $limit);
- return $notices;
- }
-
- public function insert()
- {
- $result = parent::insert();
- if ($result === false) {
- common_log_db_error($this, 'INSERT', __FILE__);
- throw new ServerException(_('Failed to insert Conversation into database'));
- }
- return $result;
+ return [
+ 'name' => 'conversation',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'],
+ 'uri' => ['type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'URI of the conversation'],
+ 'url' => ['type' => 'varchar', 'length' => 191, 'description' => 'Resolvable URL, preferrably remote (local can be generated on the fly)'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'conversation_uri_key' => ['uri'],
+ ],
+ ];
}
}
diff --git a/src/Entity/File.php b/src/Entity/File.php
index 2dc832edd6..a5b964a383 100644
--- a/src/Entity/File.php
+++ b/src/Entity/File.php
@@ -1,35 +1,41 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * @category Files
+ * Entity for uploaded files
+ *
+ * @category DB
* @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
* @author Mikael Nordfeldth
- * @author Miguel Dantas
- * @copyright 2008-2009, 2019 Free Software Foundation http://fsf.org
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for file
- */
-class File extends Managed_DataObject
+class File
{
+<<<<<<< HEAD
public $__table = 'file'; // table name
public $id; // int(4) primary_key not_null
public $urlhash; // varchar(64) unique_key
@@ -882,132 +888,38 @@ class File extends Managed_DataObject
'File_redirection',
'File_thumbnail',
'File_to_post'
+=======
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'file',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true],
+ 'urlhash' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 of destination URL (url field)'],
+ 'url' => ['type' => 'text', 'description' => 'destination URL after following possible redirections'],
+ 'filehash' => ['type' => 'varchar', 'length' => 64, 'not null' => false, 'description' => 'sha256 of the file contents, only for locally stored files of course'],
+ 'mimetype' => ['type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'],
+ 'size' => ['type' => 'int', 'description' => 'size of resource when available'],
+ 'title' => ['type' => 'text', 'description' => 'title of resource when available'],
+ 'date' => ['type' => 'int', 'description' => 'date of resource according to http query'],
+ 'protected' => ['type' => 'int', 'description' => 'true when URL is private (needs login)'],
+ 'filename' => ['type' => 'text', 'description' => 'if file is stored locally (too) this is the filename'],
+ 'width' => ['type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'],
+ 'height' => ['type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'file_urlhash_key' => ['urlhash'],
+ ],
+ 'indexes' => [
+ 'file_filehash_idx' => ['filehash'],
+ ],
+>>>>>>> ecc5139ce5 ([DATABASE] Extracted schemaDef method from old files and refactored onto new files)
];
- Event::handle('FileDeleteRelated', [$this, &$related]);
-
- foreach ($related as $cls) {
- $inst = new $cls();
- $inst->file_id = $this->id;
- if ($inst->find()) {
- while ($inst->fetch()) {
- $inst->delete();
- }
- }
- }
-
- // And finally remove the entry from the database
- return parent::delete($useWhere);
- }
-
- public function getTitle()
- {
- $title = $this->title ?: MediaFile::getDisplayName($this);
-
- return $title ?: null;
- }
-
- public function setTitle($title)
- {
- $orig = clone($this);
- $this->title = mb_strlen($title) > 0 ? $title : null;
- return $this->update($orig);
- }
-
- public static function hashurl($url)
- {
- if (empty($url)) {
- throw new Exception('No URL provided to hash algorithm.');
- }
- return hash(self::URLHASH_ALG, $url);
- }
-
- public static function beforeSchemaUpdate()
- {
- $table = strtolower(get_called_class());
- $schema = Schema::get();
- $schemadef = $schema->getTableDef($table);
-
- // 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
- if (isset($schemadef['fields']['urlhash']) && isset($schemadef['unique keys']['file_urlhash_key'])) {
- // We already have the urlhash field, so no need to migrate it.
- return;
- }
- echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
-
- $file = new File();
- $file->query(sprintf(
- 'SELECT id, LEFT(url, 191) AS shortenedurl, COUNT(*) FROM %1$s ' .
- 'WHERE LENGTH(url) > 191 GROUP BY id, shortenedurl HAVING COUNT(*) > 1',
- common_database_tablename($table)
- ));
- print "\nFound {$file->N} URLs with too long entries in file table\n";
- while ($file->fetch()) {
- // We've got a URL that is too long for our future file table
- // so we'll cut it. We could save the original URL, but there is
- // no guarantee it is complete anyway since the previous max was 255 chars.
- $dupfile = new File();
- // First we find file entries that would be duplicates of this when shortened
- // ... and we'll just throw the dupes out the window for now! It's already so borken.
- $dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = %1$s', $dupfile->_quote($file->shortenedurl)));
- // Leave one of the URLs in the database by using ->find(true) (fetches first entry)
- if ($dupfile->find(true)) {
- print "\nShortening url entry for $table id: {$file->id} [";
- $orig = clone($dupfile);
- $origurl = $dupfile->url; // save for logging purposes
- $dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
- $dupfile->update($orig);
- print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
- // only start deleting with this fetch.
- while ($dupfile->fetch()) {
- common_log(LOG_INFO, sprintf('Deleting duplicate File entry of %1$d: %2$d (original URL: %3$s collides with these first 191 characters: %4$s', $dupfile->id, $file->id, $origurl, $file->shortenedurl));
- print ".";
- $dupfile->delete();
- }
- print "]\n";
- } else {
- print "\nWarning! URL suddenly disappeared from database: {$file->url}\n";
- }
- }
- echo "...and now all the non-duplicates which are longer than 191 characters...\n";
- $file->query('UPDATE file SET url = LEFT(url, 191) WHERE LENGTH(url) > 191');
-
- echo "\n...now running hacky pre-schemaupdate change for $table:";
- // We have to create a urlhash that is _not_ the primary key,
- // transfer data and THEN run checkSchema
- $schemadef['fields']['urlhash'] = array(
- 'type' => 'varchar',
- 'length' => 64,
- 'not null' => false, // this is because when adding column, all entries will _be_ NULL!
- 'description' => 'sha256 of destination URL (url field)',
- );
- $schemadef['fields']['url'] = array(
- 'type' => 'text',
- 'description' => 'destination URL after following possible redirections',
- );
- unset($schemadef['unique keys']);
- $schema->ensureTable($table, $schemadef);
- echo "DONE.\n";
-
- $classname = ucfirst($table);
- $tablefix = new $classname;
- // urlhash is hash('sha256', $url) in the File table
- echo "Updating urlhash fields in $table table...";
- switch (common_config('db', 'type')) {
- case 'pgsql':
- $url_sha256 = 'encode(sha256(CAST("url" AS bytea)), \'hex\')';
- break;
- case 'mysql':
- $url_sha256 = 'sha2(`url`, 256)';
- break;
- default:
- throw new ServerException('Unknown DB type selected.');
- }
- $tablefix->query(sprintf(
- 'UPDATE %1$s SET urlhash = %2$s, modified = CURRENT_TIMESTAMP;',
- $tablefix->escapedTableName(),
- $url_sha256
- ));
- echo "DONE.\n";
- echo "Resuming core schema upgrade...";
}
}
diff --git a/src/Entity/FileRedirection.php b/src/Entity/FileRedirection.php
new file mode 100644
index 0000000000..aaa46aeb2f
--- /dev/null
+++ b/src/Entity/FileRedirection.php
@@ -0,0 +1,60 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for File redirects
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class FileRedirection
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'file_redirection',
+ 'fields' => [
+ 'urlhash' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 hash of the URL'],
+ 'url' => ['type' => 'text', 'description' => 'short URL (or any other kind of redirect) for file (id)'],
+ 'file_id' => ['type' => 'int', 'description' => 'short URL for what URL/file'],
+ 'redirections' => ['type' => 'int', 'description' => 'redirect count'],
+ 'httpcode' => ['type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['urlhash'],
+ 'foreign keys' => [
+ 'file_redirection_file_id_fkey' => ['file', ['file_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/FileThumbnail.php b/src/Entity/FileThumbnail.php
new file mode 100644
index 0000000000..d30c7916a0
--- /dev/null
+++ b/src/Entity/FileThumbnail.php
@@ -0,0 +1,65 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for File thumbnails
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class FileThumbnail
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'file_thumbnail',
+ 'fields' => [
+ 'file_id' => ['type' => 'int', 'not null' => true, 'description' => 'thumbnail for what URL/file'],
+ 'urlhash' => ['type' => 'varchar', 'length' => 64, 'description' => 'sha256 of url field if non-empty'],
+ 'url' => ['type' => 'text', 'description' => 'URL of thumbnail'],
+ 'filename' => ['type' => 'text', 'description' => 'if stored locally, filename is put here'],
+ 'width' => ['type' => 'int', 'not null' => true, 'description' => 'width of thumbnail'],
+ 'height' => ['type' => 'int', 'not null' => true, 'description' => 'height of thumbnail'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['file_id', 'width', 'height'],
+ 'indexes' => [
+ 'file_thumbnail_file_id_idx' => ['file_id'],
+ 'file_thumbnail_urlhash_idx' => ['urlhash'],
+ ],
+ 'foreign keys' => [
+ 'file_thumbnail_file_id_fkey' => ['file', ['file_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/FileToPost.php b/src/Entity/FileToPost.php
new file mode 100644
index 0000000000..2cfcbd14e5
--- /dev/null
+++ b/src/Entity/FileToPost.php
@@ -0,0 +1,62 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for relating a file to a post
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class FileToPost
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'file_to_post',
+ 'fields' => [
+ 'file_id' => ['type' => 'int', 'not null' => true, 'description' => 'id of URL/file'],
+ 'post_id' => ['type' => 'int', 'not null' => true, 'description' => 'id of the notice it belongs to'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['file_id', 'post_id'],
+ 'foreign keys' => [
+ 'file_to_post_file_id_fkey' => ['file', ['file_id' => 'id']],
+ 'file_to_post_post_id_fkey' => ['notice', ['post_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'file_id_idx' => ['file_id'],
+ 'post_id_idx' => ['post_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/File_redirection.php b/src/Entity/File_redirection.php
deleted file mode 100644
index 50c6df4bc7..0000000000
--- a/src/Entity/File_redirection.php
+++ /dev/null
@@ -1,490 +0,0 @@
-.
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for file_redirection
- */
-class File_redirection extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'file_redirection'; // table name
- public $urlhash; // varchar(64) primary_key not_null
- public $url; // text
- public $file_id; // int(4)
- public $redirections; // int(4)
- public $httpcode; // int(4)
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- protected $file; /* Cache the associated file sometimes */
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 hash of the URL'),
- 'url' => array('type' => 'text', 'description' => 'short URL (or any other kind of redirect) for file (id)'),
- 'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'),
- 'redirections' => array('type' => 'int', 'description' => 'redirect count'),
- 'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('urlhash'),
- 'foreign keys' => array(
- 'file_redirection_file_id_fkey' => array('file', array('file_id' => 'id')),
- ),
- 'indexes' => array(
- 'file_redirection_file_id_idx' => array('file_id'),
- ),
- );
- }
-
- public static function getByUrl($url)
- {
- return self::getByPK(array('urlhash' => File::hashurl($url)));
- }
-
- public static function _commonHttp($url, $redirs)
- {
- $request = new HTTPClient($url);
- $request->setConfig(array(
- 'connect_timeout' => 10, // # seconds to wait
- 'max_redirs' => $redirs, // # max number of http redirections to follow
- 'follow_redirects' => false, // We follow redirects ourselves in lib/httpclient.php
- 'store_body' => false, // We won't need body content here.
- ));
- return $request;
- }
-
- /**
- * Check if this URL is a redirect and return redir info.
- *
- * Most code should call File_redirection::where instead, to check if we
- * already know that redirection and avoid extra hits to the web.
- *
- * The URL is hit and any redirects are followed, up to 10 levels or until
- * a protected URL is reached.
- *
- * @param string $in_url
- * @return mixed one of:
- * string - target URL, if this is a direct link or can't be followed
- * array - redirect info if this is an *unknown* redirect:
- * associative array with the following elements:
- * code: HTTP status code
- * redirects: count of redirects followed
- * url: URL string of final target
- * type (optional): MIME type from Content-Type header
- * size (optional): byte size from Content-Length header
- * time (optional): timestamp from Last-Modified header
- */
- public static function lookupWhere($short_url, $redirs = 10, $protected = false)
- {
- if ($redirs < 0) {
- return false;
- }
-
- if (strpos($short_url, '://') === false) {
- return $short_url;
- }
- try {
- $request = self::_commonHttp($short_url, $redirs);
- // Don't include body in output
- $request->setMethod(HTTP_Request2::METHOD_HEAD);
- $response = $request->send();
-
- if (405 == $response->getStatus() || 204 == $response->getStatus()) {
- // HTTP 405 Unsupported Method
- // Server doesn't support HEAD method? Can this really happen?
- // We'll try again as a GET and ignore the response data.
- //
- // HTTP 204 No Content
- // YFrog sends 204 responses back for our HEAD checks, which
- // seems like it may be a logic error in their servers. If
- // we get a 204 back, re-run it as a GET... if there's really
- // no content it'll be cheap. :)
- $request = self::_commonHttp($short_url, $redirs);
- $response = $request->send();
- } elseif (400 == $response->getStatus()) {
- throw new Exception('Got error 400 on HEAD request, will not go further.');
- }
- } catch (Exception $e) {
- // Invalid URL or failure to reach server
- common_log(LOG_ERR, "Error while following redirects for $short_url: " . $e->getMessage());
- return $short_url;
- }
-
- // if last url after all redirections is protected,
- // use the url before it in the redirection chain
- if ($response->getRedirectCount() && File::isProtected($response->getEffectiveUrl())) {
- $return_url = $response->redirUrls[$response->getRedirectCount() - 1];
- } else {
- $return_url = $response->getEffectiveUrl();
- }
-
- $ret = array('code' => $response->getStatus()
- , 'redirects' => $response->getRedirectCount()
- , 'url' => $return_url);
-
- $type = $response->getHeader('Content-Type');
- if ($type) {
- $ret['type'] = $type;
- }
- if ($protected) {
- $ret['protected'] = true;
- }
- $size = $response->getHeader('Content-Length'); // @fixme bytes?
- if ($size) {
- $ret['size'] = $size;
- }
- $time = $response->getHeader('Last-Modified');
- if ($time) {
- $ret['time'] = strtotime($time);
- }
- return $ret;
- }
-
- /**
- * Check if this URL is a redirect and return redir info.
- * If a File record is present for this URL, it is not considered a redirect.
- * If a File_redirection record is present for this URL, the recorded target is returned.
- *
- * If no File or File_redirect record is present, the URL is hit and any
- * redirects are followed, up to 10 levels or until a protected URL is
- * reached.
- *
- * @param string $in_url
- * @param boolean $discover true to attempt dereferencing the redirect if we don't know it already
- * @return File_redirection
- */
- public static function where($in_url, $discover = true)
- {
- $redir = new File_redirection();
- $redir->url = $in_url;
- $redir->urlhash = File::hashurl($redir->url);
- $redir->redirections = 0;
-
- try {
- $r = File_redirection::getByUrl($in_url);
-
- try {
- $f = File::getByID($r->file_id);
- $r->file = $f;
- $r->redir_url = $f->url;
- } catch (NoResultException $e) {
- // Invalid entry, delete and run again
- common_log(
- LOG_ERR,
- 'Could not find File with id=' . $r->file_id . ' referenced in File_redirection, deleting File redirection entry and and trying again...'
- );
- $r->delete();
- return self::where($in_url);
- }
-
- // File_redirecion and File record found, return both
- return $r;
- } catch (NoResultException $e) {
- // File_redirecion record not found, but this might be a direct link to a file
- try {
- $f = File::getByUrl($in_url);
- $redir->file_id = $f->id;
- $redir->file = $f;
- return $redir;
- } catch (NoResultException $e) {
- // nope, this was not a direct link to a file either, let's keep going
- }
- }
-
- if ($discover) {
- // try to follow redirects and get the final url
- $redir_info = File_redirection::lookupWhere($in_url);
- if (is_string($redir_info)) {
- $redir_info = array('url' => $redir_info);
- }
-
- // the last url in the redirection chain can actually be a redirect!
- // this is the case with local /attachment/{file_id} links
- // in that case we have the file id already
- try {
- $r = File_redirection::getByUrl($redir_info['url']);
-
- $f = File::getKV('id', $r->file_id);
-
- if ($f instanceof File) {
- $redir->file = $f;
- $redir->redir_url = $f->url;
- } else {
- // Invalid entry in File_redirection, delete and run again
- common_log(
- LOG_ERR,
- 'Could not find File with id=' . $r->file_id . ' referenced in File_redirection, deleting File_redirection entry and trying again...'
- );
- $r->delete();
- return self::where($in_url);
- }
- } catch (NoResultException $e) {
- // save the file now when we know that we don't have it in File_redirection
- try {
- $redir->file = File::saveNew($redir_info, $redir_info['url']);
- } catch (ServerException $e) {
- common_log(LOG_ERR, $e);
- }
- }
-
- // If this is a redirection and we have a file to redirect to, save it
- // (if it doesn't exist in File_redirection already)
- if ($redir->file instanceof File && $redir_info['url'] != $in_url) {
- try {
- $file_redir = File_redirection::getByUrl($in_url);
- } catch (NoResultException $e) {
- $file_redir = new File_redirection();
- $file_redir->urlhash = File::hashurl($in_url);
- $file_redir->url = $in_url;
- $file_redir->file_id = $redir->file->getID();
- $file_redir->insert();
- $file_redir->redir_url = $redir->file->url;
- }
-
- $file_redir->file = $redir->file;
- return $file_redir;
- }
- }
-
- return $redir;
- }
-
- /**
- * Shorten a URL with the current user's configured shortening
- * options, if applicable.
- *
- * If it cannot be shortened or the "short" URL is longer than the
- * original, the original is returned.
- *
- * If the referenced item has not been seen before, embedding data
- * may be saved.
- *
- * @param string $long_url
- * @param User $user whose shortening options to use; defaults to the current web session user
- * @return string
- */
- public static function makeShort($long_url, $user = null)
- {
- $canon = File_redirection::_canonUrl($long_url);
-
- $short_url = File_redirection::_userMakeShort($canon, $user);
-
- // Did we get one? Is it shorter?
-
- return !empty($short_url) ? $short_url : $long_url;
- }
-
- /**
- * Shorten a URL with the current user's configured shortening
- * options, if applicable.
- *
- * If it cannot be shortened or the "short" URL is longer than the
- * original, the original is returned.
- *
- * If the referenced item has not been seen before, embedding data
- * may be saved.
- *
- * @param string $long_url
- * @return string
- */
-
- public static function forceShort($long_url, $user)
- {
- $canon = File_redirection::_canonUrl($long_url);
-
- $short_url = File_redirection::_userMakeShort($canon, $user, true);
-
- // Did we get one? Is it shorter?
- return !empty($short_url) ? $short_url : $long_url;
- }
-
- public static function _userMakeShort($long_url, User $user = null, $force = false)
- {
- $short_url = common_shorten_url($long_url, $user, $force);
- if (!empty($short_url) && $short_url != $long_url) {
- $short_url = (string)$short_url;
- // store it
- try {
- $file = File::getByUrl($long_url);
- } catch (NoResultException $e) {
- // Check if the target URL is itself a redirect...
- // This should already have happened in processNew in common_shorten_url()
- $redir = File_redirection::where($long_url);
- $file = $redir->file;
- }
- // Now we definitely have a File object in $file
- try {
- $file_redir = File_redirection::getByUrl($short_url);
- } catch (NoResultException $e) {
- $file_redir = new File_redirection();
- $file_redir->urlhash = File::hashurl($short_url);
- $file_redir->url = $short_url;
- $file_redir->file_id = $file->getID();
- $file_redir->insert();
- }
- return $short_url;
- }
- return null;
- }
-
- /**
- * Basic attempt to canonicalize a URL, cleaning up some standard variants
- * such as funny syntax or a missing path. Used internally when cleaning
- * up URLs for storage and following redirect chains.
- *
- * Note that despite being on File_redirect, this function DOES NOT perform
- * any dereferencing of redirects.
- *
- * @param string $in_url input URL
- * @param string $default_scheme if given a bare link; defaults to 'http://'
- * @return string
- */
- public static function _canonUrl($in_url, $default_scheme = 'http://')
- {
- if (empty($in_url)) {
- return false;
- }
- $out_url = $in_url;
- $p = parse_url($out_url);
- if (empty($p['host']) || empty($p['scheme'])) {
- list($scheme) = explode(':', $in_url, 2);
- switch (strtolower($scheme)) {
- case 'fax':
- case 'tel':
- $out_url = str_replace('.-()', '', $out_url);
- break;
-
- // non-HTTP schemes, so no redirects
- case 'bitcoin':
- case 'mailto':
- case 'aim':
- case 'jabber':
- case 'xmpp':
- // don't touch anything
- break;
-
- // URLs without domain name, so no redirects
- case 'magnet':
- // don't touch anything
- break;
-
- // URLs with coordinates, not browsable domain names
- case 'geo':
- // don't touch anything
- break;
-
- default:
- $out_url = $default_scheme . ltrim($out_url, '/');
- $p = parse_url($out_url);
- if (empty($p['scheme'])) {
- return false;
- }
- break;
- }
- }
-
- if (('ftp' == $p['scheme']) || ('ftps' == $p['scheme']) || ('http' == $p['scheme']) || ('https' == $p['scheme'])) {
- if (empty($p['host'])) {
- return false;
- }
- if (empty($p['path'])) {
- $out_url .= '/';
- }
- }
-
- return $out_url;
- }
-
- public static function saveNew($data, $file_id, $url)
- {
- $file_redir = new File_redirection;
- $file_redir->urlhash = File::hashurl($url);
- $file_redir->url = $url;
- $file_redir->file_id = $file_id;
- $file_redir->redirections = intval($data['redirects']);
- $file_redir->httpcode = intval($data['code']);
- $file_redir->insert();
- }
-
- public static function beforeSchemaUpdate()
- {
- $table = strtolower(get_called_class());
- $schema = Schema::get();
- $schemadef = $schema->getTableDef($table);
-
- // 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
- if (isset($schemadef['fields']['urlhash']) && in_array('urlhash', $schemadef['primary key'])) {
- // We already have the urlhash field, so no need to migrate it.
- return;
- }
- echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
- // We have to create a urlhash that is _not_ the primary key,
- // transfer data and THEN run checkSchema
- $schemadef['fields']['urlhash'] = [
- 'type' => 'varchar',
- 'length' => 64,
- 'not null' => true,
- 'description' => 'sha256 hash of the URL',
- ];
- $schemadef['fields']['url'] = [
- 'type' => 'text',
- 'description' => 'short URL (or any other kind of redirect) for file (id)',
- ];
- unset($schemadef['primary key']);
- $schema->ensureTable($table, $schemadef);
- echo "DONE.\n";
-
- $classname = ucfirst($table);
- $tablefix = new $classname;
- // urlhash is hash('sha256', $url) in the File table
- echo "Updating urlhash fields in $table table...";
- switch (common_config('db', 'type')) {
- case 'pgsql':
- $url_sha256 = 'encode(sha256(CAST("url" AS bytea)), \'hex\')';
- break;
- case 'mysql':
- $url_sha256 = 'sha2(`url`, 256)';
- break;
- default:
- throw new ServerException('Unknown DB type selected.');
- }
- $tablefix->query(sprintf(
- 'UPDATE %1$s SET urlhash = %2$s, modified = CURRENT_TIMESTAMP;',
- $tablefix->escapedTableName(),
- $url_sha256
- ));
- echo "DONE.\n";
- echo "Resuming core schema upgrade...";
- }
-
- public function getFile()
- {
- if (!$this->file instanceof File) {
- $this->file = File::getByID($this->file_id);
- }
-
- return $this->file;
- }
-}
diff --git a/src/Entity/File_to_post.php b/src/Entity/File_to_post.php
deleted file mode 100644
index 8906a0e6e2..0000000000
--- a/src/Entity/File_to_post.php
+++ /dev/null
@@ -1,114 +0,0 @@
-.
-
-/**
- * Table Definition for file_to_post
- *
- * @copyright 2008, 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class File_to_post extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'file_to_post'; // table name
- public $file_id; // int(4) primary_key not_null
- public $post_id; // int(4) primary_key not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'id of URL/file'),
- 'post_id' => array('type' => 'int', 'not null' => true, 'description' => 'id of the notice it belongs to'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('file_id', 'post_id'),
- 'foreign keys' => array(
- 'file_to_post_file_id_fkey' => array('file', array('file_id' => 'id')),
- 'file_to_post_post_id_fkey' => array('notice', array('post_id' => 'id')),
- ),
- 'indexes' => array(
- 'file_to_post_post_id_idx' => array('post_id'),
- ),
- );
- }
-
- public static function processNew(File $file, Notice $notice)
- {
- static $seen = array();
-
- $file_id = $file->getID();
- $notice_id = $notice->getID();
- if (!array_key_exists($notice_id, $seen)) {
- $seen[$notice_id] = array();
- }
-
- if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
- try {
- $f2p = File_to_post::getByPK(array('post_id' => $notice_id,
- 'file_id' => $file_id));
- } catch (NoResultException $e) {
- $f2p = new File_to_post;
- $f2p->file_id = $file_id;
- $f2p->post_id = $notice_id;
- $f2p->insert();
-
- $file->blowCache();
- }
-
- $seen[$notice_id][] = $file_id;
- }
- }
-
- public static function getNoticeIDsByFile(File $file)
- {
- $f2p = new File_to_post();
-
- $f2p->selectAdd();
- $f2p->selectAdd('post_id');
-
- $f2p->file_id = $file->getID();
-
- $ids = array();
-
- if (!$f2p->find()) {
- throw new NoResultException($f2p);
- }
-
- return $f2p->fetchAll('post_id');
- }
-
- public function delete($useWhere = false)
- {
- try {
- $f = File::getByID($this->file_id);
- $f->blowCache();
- } catch (NoResultException $e) {
- // ...alright, that's weird, but no File to delete anyway.
- }
-
- return parent::delete($useWhere);
- }
-}
diff --git a/src/Entity/ForeignLink.php b/src/Entity/ForeignLink.php
new file mode 100644
index 0000000000..2981526006
--- /dev/null
+++ b/src/Entity/ForeignLink.php
@@ -0,0 +1,70 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user's foreign profile
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ForeignLink
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'foreign_link',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'link to user on this system, if exists'],
+ 'foreign_id' => ['type' => 'int', 'size' => 'big', 'unsigned' => true, 'not null' => true, 'description' => 'link to user on foreign service, if exists'],
+ 'service' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to service'],
+ 'credentials' => ['type' => 'varchar', 'length' => 191, 'description' => 'authc credentials, typically a password'],
+ 'noticesync' => ['type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies'],
+ 'friendsync' => ['type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 2, 'description' => 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'],
+ 'profilesync' => ['type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'],
+ 'last_noticesync' => ['type' => 'datetime', 'description' => 'last time notices were imported'],
+ 'last_friendsync' => ['type' => 'datetime', 'description' => 'last time friends were imported'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id', 'foreign_id', 'service'],
+ 'foreign keys' => [
+ 'foreign_link_user_id_fkey' => ['user', ['user_id' => 'id']],
+ 'foreign_link_foreign_id_fkey' => ['foreign_user', ['foreign_id' => 'id', 'service' => 'service']],
+ 'foreign_link_service_fkey' => ['foreign_service', ['service' => 'id']],
+ ],
+ 'indexes' => [
+ 'foreign_user_user_id_idx' => ['user_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ForeignService.php b/src/Entity/ForeignService.php
new file mode 100644
index 0000000000..8e64542926
--- /dev/null
+++ b/src/Entity/ForeignService.php
@@ -0,0 +1,59 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for foreign services
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ForeignService
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'foreign_service',
+ 'fields' => [
+ 'id' => ['type' => 'int', 'not null' => true, 'description' => 'numeric key for service'],
+ 'name' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'name of the service'],
+ 'description' => ['type' => 'varchar', 'length' => 191, 'description' => 'description'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'foreign_service_name_key' => ['name'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ForeignSubscription.php b/src/Entity/ForeignSubscription.php
new file mode 100644
index 0000000000..6c5c953140
--- /dev/null
+++ b/src/Entity/ForeignSubscription.php
@@ -0,0 +1,65 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user's foreign subscriptions
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ForeignSubscription
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'foreign_subscription',
+
+ 'fields' => [
+ 'service' => ['type' => 'int', 'not null' => true, 'description' => 'service where relationship happens'],
+ 'subscriber' => ['type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'subscriber on foreign service'],
+ 'subscribed' => ['type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'subscribed user'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ ],
+ 'primary key' => ['service', 'subscriber', 'subscribed'],
+ 'foreign keys' => [
+ 'foreign_subscription_service_fkey' => ['foreign_service', ['service' => 'id']],
+ 'foreign_subscription_subscriber_fkey' => ['foreign_user', ['subscriber' => 'id', 'service' => 'service']],
+ 'foreign_subscription_subscribed_fkey' => ['foreign_user', ['subscribed' => 'id', 'service' => 'service']],
+ ],
+ 'indexes' => [
+ 'foreign_subscription_subscriber_idx' => ['service', 'subscriber'],
+ 'foreign_subscription_subscribed_idx' => ['service', 'subscribed'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ForeignUser.php b/src/Entity/ForeignUser.php
new file mode 100644
index 0000000000..a00c8e8e15
--- /dev/null
+++ b/src/Entity/ForeignUser.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Foreign Users
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ForeignUser
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'foreign_user',
+ 'fields' => [
+ 'id' => ['type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'unique numeric key on foreign service'],
+ 'service' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to service'],
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'identifying URI'],
+ 'nickname' => ['type' => 'varchar', 'length' => 191, 'description' => 'nickname on foreign service'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id', 'service'],
+ 'foreign keys' => [
+ 'foreign_user_service_fkey' => ['foreign_service', ['service' => 'id']],
+ ],
+ 'unique keys' => [
+ 'foreign_user_uri_key' => ['uri'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Foreign_link.php b/src/Entity/Foreign_link.php
deleted file mode 100644
index a12a5f4a69..0000000000
--- a/src/Entity/Foreign_link.php
+++ /dev/null
@@ -1,187 +0,0 @@
-.
-
-/**
- * Table Definition for foreign_link
- */
-
-defined('GNUSOCIAL') || die();
-
-class Foreign_link extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'foreign_link'; // table name
- public $user_id; // int(4) primary_key not_null
- public $foreign_id; // bigint(8) primary_key not_null unsigned
- public $service; // int(4) primary_key not_null
- public $credentials; // blob
- public $noticesync; // tinyint(1) not_null default_1
- public $friendsync; // tinyint(1) not_null default_2
- public $profilesync; // tinyint(1) not_null default_1
- public $last_noticesync; // datetime()
- public $last_friendsync; // datetime()
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'link to user on this system, if exists'),
- 'foreign_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'link to user on foreign service, if exists'),
- 'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
- 'credentials' => array('type' => 'blob', 'description' => 'authc credentials, typically a password'),
- 'noticesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies'),
- 'friendsync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 2, 'description' => 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),
- 'profilesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),
- 'last_noticesync' => array('type' => 'datetime', 'description' => 'last time notices were imported'),
- 'last_friendsync' => array('type' => 'datetime', 'description' => 'last time friends were imported'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id', 'foreign_id', 'service'),
- 'foreign keys' => array(
- 'foreign_link_user_id_fkey' => array('user', array('user_id' => 'id')),
- 'foreign_link_foreign_id_service_fkey' => array('foreign_user', array('foreign_id' => 'id', 'service' => 'service')),
- 'foreign_link_service_fkey' => array('foreign_service', array('service' => 'id')),
- ),
- 'indexes' => array(
- 'foreign_link_foreign_id_service_idx' => array('foreign_id', 'service'),
- 'foreign_link_service_idx' => array('service'),
- ),
- );
- }
-
- public static function getByUserID($user_id, $service)
- {
- if (empty($user_id) || empty($service)) {
- throw new ServerException('Empty user_id or service for Foreign_link::getByUserID');
- }
-
- $flink = new Foreign_link();
- $flink->service = $service;
- $flink->user_id = $user_id;
- $flink->limit(1);
-
- if (!$flink->find(true)) {
- throw new NoResultException($flink);
- }
-
- return $flink;
- }
-
- public static function getByForeignID($foreign_id, $service)
- {
- if (empty($foreign_id) || empty($service)) {
- throw new ServerException('Empty foreign_id or service for Foreign_link::getByForeignID');
- }
-
- $flink = new Foreign_link();
- $flink->service = $service;
- $flink->foreign_id = $foreign_id;
- $flink->limit(1);
-
- if (!$flink->find(true)) {
- throw new NoResultException($flink);
- }
-
- return $flink;
- }
-
- public function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
- {
- if ($noticesend) {
- $this->noticesync |= FOREIGN_NOTICE_SEND;
- } else {
- $this->noticesync &= ~FOREIGN_NOTICE_SEND;
- }
-
- if ($noticerecv) {
- $this->noticesync |= FOREIGN_NOTICE_RECV;
- } else {
- $this->noticesync &= ~FOREIGN_NOTICE_RECV;
- }
-
- if ($replysync) {
- $this->noticesync |= FOREIGN_NOTICE_SEND_REPLY;
- } else {
- $this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
- }
-
- if ($repeatsync) {
- $this->noticesync |= FOREIGN_NOTICE_SEND_REPEAT;
- } else {
- $this->noticesync &= ~FOREIGN_NOTICE_SEND_REPEAT;
- }
-
- if ($friendsync) {
- $this->friendsync |= FOREIGN_FRIEND_RECV;
- } else {
- $this->friendsync &= ~FOREIGN_FRIEND_RECV;
- }
-
- $this->profilesync = 0;
- }
-
- // Convenience methods
- public function getForeignUser()
- {
- $fuser = new Foreign_user();
- $fuser->service = $this->service;
- $fuser->id = $this->foreign_id;
-
- $fuser->limit(1);
-
- if (!$fuser->find(true)) {
- throw new NoResultException($fuser);
- }
-
- return $fuser;
- }
-
- public function getUser()
- {
- return Profile::getByID($this->user_id)->getUser();
- }
-
- public function getProfile()
- {
- return Profile::getByID($this->user_id);
- }
-
- // Make sure we only ever delete one record at a time
- public function safeDelete()
- {
- if (!empty($this->user_id)
- && !empty($this->foreign_id)
- && !empty($this->service)) {
- return $this->delete();
- } else {
- common_debug(
- LOG_WARNING,
- 'Foreign_link::safeDelete() tried to delete a '
- . 'Foreign_link without a fully specified compound key: '
- . var_export($this, true)
- );
- return false;
- }
- }
-}
diff --git a/src/Entity/Foreign_service.php b/src/Entity/Foreign_service.php
deleted file mode 100644
index 64ea7cbf01..0000000000
--- a/src/Entity/Foreign_service.php
+++ /dev/null
@@ -1,54 +0,0 @@
-.
-
-/**
- * Table Definition for foreign_service
- */
-
-defined('GNUSOCIAL') || die();
-
-class Foreign_service extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'foreign_service'; // table name
- public $id; // int(4) primary_key not_null
- public $name; // varchar(32) unique_key not_null
- public $description; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'not null' => true, 'description' => 'numeric key for service'),
- 'name' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'name of the service'),
- 'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'foreign_service_name_key' => array('name'),
- ),
- );
- }
-}
diff --git a/src/Entity/Foreign_subscription.php b/src/Entity/Foreign_subscription.php
deleted file mode 100644
index e1f14fc846..0000000000
--- a/src/Entity/Foreign_subscription.php
+++ /dev/null
@@ -1,60 +0,0 @@
-.
-
-/**
- * Table Definition for foreign_subscription
- */
-
-defined('GNUSOCIAL') || die();
-
-class Foreign_subscription extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'foreign_subscription'; // table name
- public $service; // int(4) primary_key not_null
- public $subscriber; // int(4) primary_key not_null
- public $subscribed; // int(4) primary_key not_null
- public $created; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
-
- 'fields' => array(
- 'service' => array('type' => 'int', 'not null' => true, 'description' => 'service where relationship happens'),
- 'subscriber' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'subscriber on foreign service'),
- 'subscribed' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'subscribed user'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- ),
- 'primary key' => array('service', 'subscriber', 'subscribed'),
- 'foreign keys' => array(
- 'foreign_subscription_service_fkey' => array('foreign_service', array('service' => 'id')),
- 'foreign_subscription_subscriber_service_fkey' => array('foreign_user', array('subscriber' => 'id', 'service' => 'service')),
- 'foreign_subscription_subscribed_service_fkey' => array('foreign_user', array('subscribed' => 'id', 'service' => 'service')),
- ),
- 'indexes' => array(
- 'foreign_subscription_subscriber_service_idx' => array('subscriber', 'service'),
- 'foreign_subscription_subscribed_service_idx' => array('subscribed', 'service'),
- 'foreign_subscription_service_subscribed_idx' => array('service', 'subscribed'),
- ),
- );
- }
-}
diff --git a/src/Entity/Foreign_user.php b/src/Entity/Foreign_user.php
deleted file mode 100644
index dfe65b8c4f..0000000000
--- a/src/Entity/Foreign_user.php
+++ /dev/null
@@ -1,98 +0,0 @@
-.
-
-/**
- * Table Definition for foreign_user
- */
-
-defined('GNUSOCIAL') || die();
-
-class Foreign_user extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'foreign_user'; // table name
- public $id; // bigint(8) primary_key not_null
- public $service; // int(4) primary_key not_null
- public $uri; // varchar(191) unique_key not_null not 255 because utf8mb4 takes more space
- public $nickname; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'unique numeric key on foreign service'),
- 'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
- 'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'identifying URI'),
- 'nickname' => array('type' => 'varchar', 'length' => 191, 'description' => 'nickname on foreign service'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id', 'service'),
- 'foreign keys' => array(
- 'foreign_user_service_fkey' => array('foreign_service', array('service' => 'id')),
- ),
- 'unique keys' => array(
- 'foreign_user_uri_key' => array('uri'),
- ),
- 'indexes' => array(
- 'foreign_user_service_idx' => array('service'),
- ),
- );
- }
-
- public static function getForeignUser($id, $service)
- {
- if (empty($id) || empty($service)) {
- throw new ServerException('Empty foreign user id or service for Foreign_user::getForeignUser');
- }
-
- $fuser = new Foreign_user();
- $fuser->id = $id;
- $fuser->service = $service;
- $fuser->limit(1);
-
- if (!$fuser->find(true)) {
- throw new NoResultException($fuser);
- }
-
- return $fuser;
- }
-
- public static function getByNickname($nickname, $service)
- {
- if (empty($nickname) || empty($service)) {
- throw new ServerException('Empty nickname or service for Foreign_user::getByNickname');
- }
-
- $fuser = new Foreign_user();
- $fuser->service = $service;
- $fuser->nickname = $nickname;
- $fuser->limit(1);
-
- if (!$fuser->find(true)) {
- throw new NoResultException($fuser);
- }
-
- return $fuser;
- }
-}
diff --git a/src/Entity/GS_DataObject.php b/src/Entity/GS_DataObject.php
deleted file mode 100644
index 4ca2436eec..0000000000
--- a/src/Entity/GS_DataObject.php
+++ /dev/null
@@ -1,234 +0,0 @@
-.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Group Alias
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class GroupAlias
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'group_alias',
+ 'fields' => [
+ 'alias' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'additional nickname for the group'],
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'group profile is blocked from'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date alias was created'],
+ ],
+ 'primary key' => ['alias'],
+ 'foreign keys' => [
+ 'group_alias_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'group_alias_group_id_idx' => ['group_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/GroupBlock.php b/src/Entity/GroupBlock.php
new file mode 100644
index 0000000000..1c17464c19
--- /dev/null
+++ b/src/Entity/GroupBlock.php
@@ -0,0 +1,60 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Group Block
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class GroupBlock
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'group_block',
+ 'fields' => [
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'group profile is blocked from'],
+ 'blocked' => ['type' => 'int', 'not null' => true, 'description' => 'profile that is blocked'],
+ 'blocker' => ['type' => 'int', 'not null' => true, 'description' => 'user making the block'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date of blocking'],
+ ],
+ 'primary key' => ['group_id', 'blocked'],
+ 'foreign keys' => [
+ 'group_block_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ 'group_block_blocked_fkey' => ['profile', ['blocked' => 'id']],
+ 'group_block_blocker_fkey' => ['user', ['blocker' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/GroupInbox.php b/src/Entity/GroupInbox.php
new file mode 100644
index 0000000000..fc08908c62
--- /dev/null
+++ b/src/Entity/GroupInbox.php
@@ -0,0 +1,64 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Group's inbox
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class GroupInbox
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'group_inbox',
+ 'description' => 'Many-many table listing notices posted to a given group, or which groups a given notice was posted to.',
+ 'fields' => [
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'group receiving the message'],
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice received'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date the notice was created'],
+ ],
+ 'primary key' => ['group_id', 'notice_id'],
+ 'foreign keys' => [
+ 'group_inbox_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ 'group_inbox_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'group_inbox_created_idx' => ['created'],
+ 'group_inbox_notice_id_idx' => ['notice_id'],
+ 'group_inbox_group_id_created_notice_id_idx' => ['group_id', 'created', 'notice_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/GroupJoinQueue.php b/src/Entity/GroupJoinQueue.php
new file mode 100644
index 0000000000..cfad97f130
--- /dev/null
+++ b/src/Entity/GroupJoinQueue.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Queue on joining a group
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class GroupJoinQueue
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'group_join_queue',
+ 'description' => 'Holder for group join requests awaiting moderation.',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'],
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'remote or local group to join, if any'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ ],
+ 'primary key' => ['profile_id', 'group_id'],
+ 'indexes' => [
+ 'group_join_queue_profile_id_created_idx' => ['profile_id', 'created'],
+ 'group_join_queue_group_id_created_idx' => ['group_id', 'created'],
+ ],
+ 'foreign keys' => [
+ 'group_join_queue_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ 'group_join_queue_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/GroupMember.php b/src/Entity/GroupMember.php
new file mode 100644
index 0000000000..ec5d2bae2c
--- /dev/null
+++ b/src/Entity/GroupMember.php
@@ -0,0 +1,71 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for a Group Member
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class GroupMember
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'group_member',
+ 'fields' => [
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'],
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'],
+ 'is_admin' => ['type' => 'bool', 'default' => false, 'description' => 'is this user an admin?'],
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['group_id', 'profile_id'],
+ 'unique keys' => [
+ 'group_member_uri_key' => ['uri'],
+ ],
+ 'foreign keys' => [
+ 'group_member_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ 'group_member_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ // @fixme probably we want a (profile_id, created) index here?
+ 'group_member_profile_id_idx' => ['profile_id'],
+ 'group_member_created_idx' => ['created'],
+ 'group_member_profile_id_created_idx' => ['profile_id', 'created'],
+ 'group_member_group_id_created_idx' => ['group_id', 'created'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Group_alias.php b/src/Entity/Group_alias.php
deleted file mode 100644
index 9ee773d5b7..0000000000
--- a/src/Entity/Group_alias.php
+++ /dev/null
@@ -1,65 +0,0 @@
-.
-
-/**
- * Table Definition for group_alias
- *
- * @copyright 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Group_alias extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'group_alias'; // table name
- public $alias; // varchar(64) primary_key not_null
- public $group_id; // int(4) not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'alias' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'additional nickname for the group'),
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group profile is blocked from'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date alias was created'),
- ),
- 'primary key' => array('alias'),
- 'foreign keys' => array(
- 'group_alias_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- ),
- 'indexes' => array(
- 'group_alias_group_id_idx' => array('group_id'),
- ),
- );
- }
-
- public function getProfile()
- {
- $group = User_group::getKV('id', $this->group_id);
- if (!($group instanceof User_group)) {
- return null; // TODO: Throw exception when other code is ready
- }
- return $group->getProfile();
- }
-}
diff --git a/src/Entity/Group_block.php b/src/Entity/Group_block.php
deleted file mode 100644
index 03dac31e19..0000000000
--- a/src/Entity/Group_block.php
+++ /dev/null
@@ -1,132 +0,0 @@
-.
-
-/**
- * Table Definition for group_block
- *
- * @package GNUsocial
- * @author Evan Prodromou
- * @copyright 2008, 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Group_block extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'group_block'; // table name
- public $group_id; // int(4) primary_key not_null
- public $blocked; // int(4) primary_key not_null
- public $blocker; // int(4) not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group profile is blocked from'),
- 'blocked' => array('type' => 'int', 'not null' => true, 'description' => 'profile that is blocked'),
- 'blocker' => array('type' => 'int', 'not null' => true, 'description' => 'user making the block'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date of blocking'),
- ),
- 'primary key' => array('group_id', 'blocked'),
- 'foreign keys' => array(
- 'group_block_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- 'group_block_blocked_fkey' => array('profile', array('blocked' => 'id')),
- 'group_block_blocker_fkey' => array('user', array('blocker' => 'id')),
- ),
- 'indexes' => array(
- 'group_block_blocked_idx' => array('blocked'),
- 'group_block_blocker_idx' => array('blocker'),
- ),
- );
- }
-
- public static function isBlocked($group, $profile)
- {
- $block = Group_block::pkeyGet([
- 'group_id' => $group->id,
- 'blocked' => $profile->id,
- ]);
- return !empty($block);
- }
-
- public static function blockProfile($group, $profile, $blocker)
- {
- // Insert the block
-
- $block = new Group_block();
-
- $block->query('START TRANSACTION');
-
- $block->group_id = $group->id;
- $block->blocked = $profile->id;
- $block->blocker = $blocker->id;
-
- $result = $block->insert();
-
- if ($result === false) {
- common_log_db_error($block, 'INSERT', __FILE__);
- return null;
- }
-
- // Delete membership if any
-
- $member = new Group_member();
-
- $member->group_id = $group->id;
- $member->profile_id = $profile->id;
-
- if ($member->find(true)) {
- $result = $member->delete();
- if ($result === false) {
- common_log_db_error($member, 'DELETE', __FILE__);
- return null;
- }
- }
-
- // Commit, since both have been done
-
- $block->query('COMMIT');
-
- return $block;
- }
-
- public static function unblockProfile($group, $profile)
- {
- $block = Group_block::pkeyGet(array('group_id' => $group->id,
- 'blocked' => $profile->id));
-
- if (empty($block)) {
- return null;
- }
-
- $result = $block->delete();
-
- if (!$result) {
- common_log_db_error($block, 'DELETE', __FILE__);
- return null;
- }
-
- return true;
- }
-}
diff --git a/src/Entity/Group_inbox.php b/src/Entity/Group_inbox.php
deleted file mode 100644
index 36333226df..0000000000
--- a/src/Entity/Group_inbox.php
+++ /dev/null
@@ -1,57 +0,0 @@
-.
-
-/**
- * Table Definition for group_inbox
- */
-
-defined('GNUSOCIAL') || die();
-
-class Group_inbox extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'group_inbox'; // table name
- public $group_id; // int(4) primary_key not_null
- public $notice_id; // int(4) primary_key not_null
- public $created; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Many-many table listing notices posted to a given group, or which groups a given notice was posted to.',
- 'fields' => array(
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group receiving the message'),
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice received'),
- 'created' => array('type' => 'datetime', 'description' => 'date the notice was created'),
- ),
- 'primary key' => array('group_id', 'notice_id'),
- 'foreign keys' => array(
- 'group_inbox_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- 'group_inbox_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- ),
- 'indexes' => array(
- 'group_inbox_created_idx' => array('created'),
- 'group_inbox_notice_id_idx' => array('notice_id'),
- 'group_inbox_group_id_created_notice_id_idx' => array('group_id', 'created', 'notice_id'),
- ),
- );
- }
-}
diff --git a/src/Entity/Group_join_queue.php b/src/Entity/Group_join_queue.php
deleted file mode 100644
index 6517ce2cf7..0000000000
--- a/src/Entity/Group_join_queue.php
+++ /dev/null
@@ -1,139 +0,0 @@
-.
-
-/**
- * Table Definition for request_queue
- */
-
-defined('GNUSOCIAL') || die();
-
-class Group_join_queue extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'group_join_queue'; // table name
- public $profile_id;
- public $group_id;
- public $created;
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Holder for group join requests awaiting moderation.',
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'),
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local group to join, if any'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- ),
- 'primary key' => array('profile_id', 'group_id'),
- 'indexes' => array(
- 'group_join_queue_profile_id_created_idx' => array('profile_id', 'created'),
- 'group_join_queue_group_id_created_idx' => array('group_id', 'created'),
- ),
- 'foreign keys' => array(
- 'group_join_queue_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- 'group_join_queue_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- )
- );
- }
-
- public static function saveNew(Profile $profile, User_group $group)
- {
- $rq = new Group_join_queue();
- $rq->profile_id = $profile->id;
- $rq->group_id = $group->id;
- $rq->created = common_sql_now();
- $rq->insert();
- return $rq;
- }
-
- public function getMember()
- {
- $member = Profile::getKV('id', $this->profile_id);
-
- if (empty($member)) {
- // TRANS: Exception thrown providing an invalid profile ID.
- // TRANS: %s is the invalid profile ID.
- throw new Exception(sprintf(_('Profile ID %s is invalid.'), $this->profile_id));
- }
-
- return $member;
- }
-
- public function getGroup()
- {
- $group = User_group::getKV('id', $this->group_id);
-
- if (empty($group)) {
- // TRANS: Exception thrown providing an invalid group ID.
- // TRANS: %s is the invalid group ID.
- throw new Exception(sprintf(_('Group ID %s is invalid.'), $this->group_id));
- }
-
- return $group;
- }
-
- /**
- * Abort the pending group join...
- */
- public function abort()
- {
- $profile = $this->getMember();
- $group = $this->getGroup();
-
- if (Event::handle('StartCancelJoinGroup', array($profile, $group))) {
- $this->delete();
- Event::handle('EndCancelJoinGroup', array($profile, $group));
- }
- }
-
- /**
- * Complete a pending group join...
- *
- * @return Group_member object on success
- */
- public function complete()
- {
- $join = null;
- $profile = $this->getMember();
- $group = $this->getGroup();
- if (Event::handle('StartJoinGroup', array($profile, $group))) {
- $join = Group_member::join($group->id, $profile->id);
- $this->delete();
- Event::handle('EndJoinGroup', array($profile, $group));
- }
- if (!$join) {
- throw new Exception('Internal error: group join failed.');
- }
- $join->notify();
- return $join;
- }
-
- /**
- * Send notifications via email etc to group administrators about
- * this exciting new pending moderation queue item!
- */
- public function notify()
- {
- $joiner = Profile::getKV('id', $this->profile_id);
- $group = User_group::getKV('id', $this->group_id);
- mail_notify_group_join_pending($group, $joiner);
- }
-}
diff --git a/src/Entity/Group_member.php b/src/Entity/Group_member.php
deleted file mode 100644
index 5816ed0850..0000000000
--- a/src/Entity/Group_member.php
+++ /dev/null
@@ -1,232 +0,0 @@
-.
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for group_member
- */
-
-class Group_member extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'group_member'; // table name
- public $group_id; // int(4) primary_key not_null
- public $profile_id; // int(4) primary_key not_null
- public $is_admin; // bool default_false
- public $uri; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
- 'is_admin' => array('type' => 'bool', 'default' => false, 'description' => 'is this user an admin?'),
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('group_id', 'profile_id'),
- 'unique keys' => array(
- 'group_member_uri_key' => array('uri'),
- ),
- 'foreign keys' => array(
- 'group_member_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- 'group_member_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array(
- 'group_member_profile_id_created_group_id_idx' => array('profile_id', 'created', 'group_id'),
- 'group_member_group_id_created_profile_id_idx' => array('profile_id', 'created', 'group_id'),
- ),
- );
- }
-
- /**
- * Method to add a user to a group.
- * In most cases, you should call Profile->joinGroup() instead.
- *
- * @param integer $group_id Group to add to
- * @param integer $profile_id Profile being added
- *
- * @return Group_member new membership object
- */
-
- public static function join($group_id, $profile_id)
- {
- $member = new Group_member();
-
- $member->group_id = $group_id;
- $member->profile_id = $profile_id;
- $member->created = common_sql_now();
- $member->uri = self::newUri(
- Profile::getByID($profile_id),
- User_group::getByID($group_id),
- $member->created
- );
-
- $result = $member->insert();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- // TRANS: Exception thrown when joining a group fails.
- throw new Exception(_("Group join failed."));
- }
-
- return $member;
- }
-
- public static function leave($group_id, $profile_id)
- {
- $member = Group_member::pkeyGet(array('group_id' => $group_id,
- 'profile_id' => $profile_id));
-
- if (empty($member)) {
- // TRANS: Exception thrown when trying to leave a group the user is not a member of.
- throw new Exception(_("Not part of group."));
- }
-
- $result = $member->delete();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- // TRANS: Exception thrown when trying to leave a group fails.
- throw new Exception(_("Group leave failed."));
- }
-
- return true;
- }
-
- public function getMember()
- {
- $member = Profile::getKV('id', $this->profile_id);
-
- if (empty($member)) {
- // TRANS: Exception thrown providing an invalid profile ID.
- // TRANS: %s is the invalid profile ID.
- throw new Exception(sprintf(_("Profile ID %s is invalid."), $this->profile_id));
- }
-
- return $member;
- }
-
- public function getGroup()
- {
- $group = User_group::getKV('id', $this->group_id);
-
- if (empty($group)) {
- // TRANS: Exception thrown providing an invalid group ID.
- // TRANS: %s is the invalid group ID.
- throw new Exception(sprintf(_('Group ID %s is invalid.'), $this->group_id));
- }
-
- return $group;
- }
-
- /**
- * Get stream of memberships by member
- *
- * @param integer $memberId profile ID of the member to fetch for
- * @param integer $offset offset from start of stream to get
- * @param integer $limit number of memberships to get
- *
- * @return Group_member stream of memberships, use fetch() to iterate
- */
-
- public static function byMember($memberId, $offset = 0, $limit = GROUPS_PER_PAGE)
- {
- $membership = new Group_member();
-
- $membership->profile_id = $memberId;
-
- $membership->orderBy('created DESC, group_id DESC');
-
- $membership->limit($offset, $limit);
-
- $membership->find();
-
- return $membership;
- }
-
- public function asActivity()
- {
- $member = $this->getMember();
-
- if (!$member) {
- throw new Exception("No such member: " . $this->profile_id);
- }
-
- $group = $this->getGroup();
-
- if (!$group) {
- throw new Exception("No such group: " . $this->group_id);
- }
-
- $act = new Activity();
-
- $act->id = $this->getUri();
-
- $act->actor = $member->asActivityObject();
- $act->verb = ActivityVerb::JOIN;
- $act->objects[] = ActivityObject::fromGroup($group);
-
- $act->time = strtotime($this->created);
- // TRANS: Activity title.
- $act->title = _("Join");
-
- // TRANS: Success message for subscribe to group attempt through OStatus.
- // TRANS: %1$s is the member name, %2$s is the subscribed group's name.
- $act->content = sprintf(
- _('%1$s has joined group %2$s.'),
- $member->getBestName(),
- $group->getBestName()
- );
-
- $url = common_local_url(
- 'AtomPubShowMembership',
- [
- 'profile' => $member->id,
- 'group' => $group->id,
- ]
- );
-
- $act->selfLink = $url;
- $act->editLink = $url;
-
- return $act;
- }
-
- /**
- * Send notifications via email etc to group administrators about
- * this exciting new membership!
- */
- public function notify()
- {
- mail_notify_group_join($this->getGroup(), $this->getMember());
- }
-
- public function getUri()
- {
- return $this->uri ?: self::newUri($this->getMember(), $this->getGroup()->getProfile(), $this->created);
- }
-}
diff --git a/src/Entity/Invitation.php b/src/Entity/Invitation.php
index 2f992f3b3d..6765028ea1 100644
--- a/src/Entity/Invitation.php
+++ b/src/Entity/Invitation.php
@@ -1,70 +1,66 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for invitation
+ * Entity for user invitations
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Invitation extends Managed_DataObject
+class Invitation
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'invitation'; // table name
- public $code; // varchar(32) primary_key not_null
- public $user_id; // int(4) not_null
- public $address; // varchar(191) multiple_key not_null not 255 because utf8mb4 takes more space
- public $address_type; // varchar(8) multiple_key not_null
- public $registered_user_id; // int(4) not_null
- public $created; // datetime()
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public function convert($user)
+ public static function schemaDef(): array
{
- $orig = clone($this);
- $this->registered_user_id = $user->id;
- return $this->update($orig);
- }
-
- public static function schemaDef()
- {
- return array(
-
- 'fields' => array(
- 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'random code for an invitation'),
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'who sent the invitation'),
- 'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'invitation sent to'),
- 'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'registered_user_id' => array('type' => 'int', 'not null' => false, 'description' => 'if the invitation is converted, who the new user is'),
- ),
- 'primary key' => array('code'),
- 'foreign keys' => array(
- 'invitation_user_id_fkey' => array('user', array('user_id' => 'id')),
- 'invitation_registered_user_id_fkey' => array('user', array('registered_user_id' => 'id')),
- ),
- 'indexes' => array(
- 'invitation_address_address_type_idx' => array('address', 'address_type'),
- 'invitation_user_id_idx' => array('user_id'),
- 'invitation_registered_user_id_idx' => array('registered_user_id'),
- ),
- );
+ return [
+ 'name' => 'invitation',
+ 'fields' => [
+ 'code' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'random code for an invitation'],
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'who sent the invitation'],
+ 'address' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'invitation sent to'],
+ 'address_type' => ['type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'registered_user_id' => ['type' => 'int', 'not null' => false, 'description' => 'if the invitation is converted, who the new user is'],
+ ],
+ 'primary key' => ['code'],
+ 'foreign keys' => [
+ 'invitation_user_id_fkey' => ['user', ['user_id' => 'id']],
+ 'invitation_registered_user_id_fkey' => ['user', ['registered_user_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'invitation_address_idx' => ['address', 'address_type'],
+ 'invitation_user_id_idx' => ['user_id'],
+ 'invitation_registered_user_id_idx' => ['registered_user_id'],
+ ],
+ ];
}
}
diff --git a/src/Entity/LocalGroup.php b/src/Entity/LocalGroup.php
new file mode 100644
index 0000000000..6825483b5a
--- /dev/null
+++ b/src/Entity/LocalGroup.php
@@ -0,0 +1,62 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for local groups
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class LocalGroup
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'local_group',
+ 'description' => 'Record for a user group on the local site, with some additional info not in user_group',
+ 'fields' => [
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'group represented'],
+ 'nickname' => ['type' => 'varchar', 'length' => 64, 'description' => 'group represented'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['group_id'],
+ 'foreign keys' => [
+ 'local_group_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ ],
+ 'unique keys' => [
+ 'local_group_nickname_key' => ['nickname'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Local_group.php b/src/Entity/Local_group.php
deleted file mode 100644
index 2a1ff71dfc..0000000000
--- a/src/Entity/Local_group.php
+++ /dev/null
@@ -1,100 +0,0 @@
-.
-
-/**
- * Table Definition for local_group
- */
-
-defined('GNUSOCIAL') || die();
-
-class Local_group extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'local_group'; // table name
- public $group_id; // int(4) primary_key not_null
- public $nickname; // varchar(64) unique_key
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Record for a user group on the local site, with some additional info not in user_group',
- 'fields' => array(
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group represented'),
- 'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'group represented'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('group_id'),
- 'foreign keys' => array(
- 'local_group_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- ),
- 'unique keys' => array(
- 'local_group_nickname_key' => array('nickname'),
- ),
- );
- }
-
- public function getProfile()
- {
- return $this->getGroup()->getProfile();
- }
-
- public function getGroup()
- {
- $group = new User_group();
- $group->id = $this->group_id;
- $group->find(true);
- if (!$group instanceof User_group) {
- common_log(LOG_ERR, 'User_group does not exist for Local_group: '.$this->group_id);
- throw new NoSuchGroupException(array('id' => $this->group_id));
- }
- return $group;
- }
-
- public function setNickname($nickname)
- {
- $this->decache();
- $modified = common_sql_now();
- $result = $this->query(sprintf(
- <<<'END'
- UPDATE local_group SET nickname = %1$s, modified = %2$s
- WHERE group_id = %3$d;
- END,
- $this->_quote($nickname),
- $this->_quote($modified),
- $this->group_id
- ));
-
- if ($result) {
- $this->nickname = $nickname;
- $this->modified = $modified;
- $this->encache();
- } else {
- common_log_db_error($local, 'UPDATE', __FILE__);
- // TRANS: Server exception thrown when updating a local group fails.
- throw new ServerException(_('Could not update local group.'));
- }
-
- return $result;
- }
-}
diff --git a/src/Entity/LocationNamespace.php b/src/Entity/LocationNamespace.php
new file mode 100644
index 0000000000..34cc7eb70f
--- /dev/null
+++ b/src/Entity/LocationNamespace.php
@@ -0,0 +1,55 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for location namespace
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class LocationNamespace
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'location_namespace',
+ 'fields' => [
+ 'id' => ['type' => 'int', 'not null' => true, 'description' => 'identity for this namespace'],
+ 'description' => ['type' => 'varchar', 'length' => 191, 'description' => 'description of the namespace'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date the record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Location_namespace.php b/src/Entity/Location_namespace.php
deleted file mode 100644
index 0004bc630c..0000000000
--- a/src/Entity/Location_namespace.php
+++ /dev/null
@@ -1,52 +0,0 @@
-.
-
-/*
- * Table Definition for location_namespace
- *
- * @copyright 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Location_namespace extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'location_namespace'; // table name
- public $id; // int(4) primary_key not_null
- public $description; // varchar(191)
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity for this namespace'),
- 'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the namespace'),
- 'created' => array('type' => 'datetime', 'description' => 'date the record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- );
- }
-}
diff --git a/src/Entity/LoginToken.php b/src/Entity/LoginToken.php
new file mode 100644
index 0000000000..6cf8ae23e9
--- /dev/null
+++ b/src/Entity/LoginToken.php
@@ -0,0 +1,58 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Login tokens
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class LoginToken
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'login_token',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user owning this token'],
+ 'token' => ['type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'token useable for logging in'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id'],
+ 'foreign keys' => [
+ 'login_token_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Login_token.php b/src/Entity/Login_token.php
deleted file mode 100644
index 9e3ace1633..0000000000
--- a/src/Entity/Login_token.php
+++ /dev/null
@@ -1,86 +0,0 @@
-.
-
-/**
- * Table Definition for login_token
- *
- * @copyright 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Login_token extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'login_token'; // table name
- public $user_id; // int(4) primary_key not_null
- public $token; // char(32) not_null
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user owning this token'),
- 'token' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'token useable for logging in'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id'),
- 'foreign keys' => array(
- 'login_token_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- );
- }
-
- const TIMEOUT = 120; // seconds after which to timeout the token
-
- public function makeNew($user)
- {
- $login_token = Login_token::getKV('user_id', $user->id);
-
- if (!empty($login_token)) {
- $login_token->delete();
- }
-
- $login_token = new Login_token();
-
- $login_token->user_id = $user->id;
- $login_token->token = common_random_hexstr(16);
- $login_token->created = common_sql_now();
-
- $result = $login_token->insert();
-
- if (!$result) {
- common_log_db_error($login_token, 'INSERT', __FILE__);
- // TRANS: Exception thrown when trying creating a login token failed.
- // TRANS: %s is the user nickname for which token creation failed.
- throw new Exception(sprintf(
- _('Could not create login token for %s'),
- $user->nickname
- ));
- }
-
- return $login_token;
- }
-}
diff --git a/src/Entity/Managed_DataObject.php b/src/Entity/Managed_DataObject.php
deleted file mode 100644
index 3aead06a57..0000000000
--- a/src/Entity/Managed_DataObject.php
+++ /dev/null
@@ -1,715 +0,0 @@
-.
-
-/**
- * Wrapper for Memcached_DataObject which knows its own schema definition.
- * Builds its own damn settings from a schema definition.
- *
- * @package GNUsocial
- * @author Brion Vibber
- * @copyright 2010 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-abstract class Managed_DataObject extends Memcached_DataObject
-{
- /**
- * The One True Thingy that must be defined and declared.
- */
- public static function schemaDef()
- {
- throw new MethodNotImplementedException(__METHOD__);
- }
-
- /**
- * Get an instance by key
- *
- * @param string $k Key to use to lookup (usually 'id' for this class)
- * @param mixed $v Value to lookup
- *
- * @return get_called_class() object if found, or null for no hits
- *
- */
- public static function getKV($k, $v = null)
- {
- return parent::getClassKV(get_called_class(), $k, $v);
- }
-
- /**
- * Get an instance by compound key
- *
- * This is a utility method to get a single instance with a given set of
- * key-value pairs. Usually used for the primary key for a compound key; thus
- * the name.
- *
- * @param array $kv array of key-value mappings
- *
- * @return get_called_class() object if found, or null for no hits
- *
- */
- public static function pkeyGet(array $kv)
- {
- return parent::pkeyGetClass(get_called_class(), $kv);
- }
-
- public static function pkeyCols()
- {
- return parent::pkeyColsClass(get_called_class());
- }
-
- /**
- * Get multiple items from the database by key
- *
- * @param string $keyCol name of column for key
- * @param array $keyVals key values to fetch
- * @param bool $skipNulls return only non-null results
- * @param bool $preserve return the same tuples as input
- * @return object An object with tuples to be fetched, in order
- */
- public static function multiGet(
- string $keyCol,
- array $keyVals,
- bool $skipNulls = true,
- bool $preserve = false
- ): object {
- return parent::multiGetClass(
- get_called_class(),
- $keyCol,
- $keyVals,
- $skipNulls,
- $preserve
- );
- }
-
- /**
- * Get multiple items from the database by key
- *
- * @param string $keyCol name of column for key
- * @param array $keyVals key values to fetch
- * @param array $otherCols Other columns to hold fixed
- *
- * @return array Array mapping $keyVals to objects, or null if not found
- */
- public static function pivotGet($keyCol, array $keyVals, array $otherCols = [])
- {
- return parent::pivotGetClass(get_called_class(), $keyCol, $keyVals, $otherCols);
- }
-
- /**
- * Get a multi-instance object
- *
- * This is a utility method to get multiple instances with a given set of
- * values for a specific column.
- *
- * @param string $keyCol key column name
- * @param array $keyVals array of key values
- *
- * @return get_called_class() object with multiple instances if found,
- * Exception is thrown when no entries are found.
- *
- */
- public static function listFind($keyCol, array $keyVals)
- {
- return parent::listFindClass(get_called_class(), $keyCol, $keyVals);
- }
-
- /**
- * Get a multi-instance object separated into an array
- *
- * This is a utility method to get multiple instances with a given set of
- * values for a specific key column. Usually used for the primary key when
- * multiple values are desired. Result is an array.
- *
- * @param string $keyCol key column name
- * @param array $keyVals array of key values
- *
- * @return array with an get_called_class() object for each $keyVals entry
- *
- */
- public static function listGet($keyCol, array $keyVals)
- {
- return parent::listGetClass(get_called_class(), $keyCol, $keyVals);
- }
-
- /**
- * get/set an associative array of table columns
- *
- * @access public
- * @return array (associative)
- */
- public function table()
- {
- $table = static::schemaDef();
- return array_map(array($this, 'columnBitmap'), $table['fields']);
- }
-
- /**
- * get/set an array of table primary keys
- *
- * Key info is pulled from the table definition array.
- *
- * @access private
- * @return array
- */
- public function keys()
- {
- return array_keys($this->keyTypes());
- }
-
- /**
- * Get a sequence key
- *
- * Returns the first serial column defined in the table, if any.
- *
- * @access private
- * @return array (column,use_native,sequence_name)
- */
-
- public function sequenceKey()
- {
- $table = static::schemaDef();
- foreach ($table['fields'] as $name => $column) {
- if ($column['type'] == 'serial') {
- // We have a serial/autoincrement column.
- // Declare it to be a native sequence!
- return array($name, true, false);
- }
- }
-
- // No sequence key on this table.
- return array(false, false, false);
- }
-
- /**
- * Return key definitions for DB_DataObject and Memcache_DataObject.
- *
- * DB_DataObject needs to know about keys that the table has; this function
- * defines them.
- *
- * @return array key definitions
- */
-
- public function keyTypes()
- {
- $table = static::schemaDef();
- $keys = array();
-
- if (!empty($table['unique keys'])) {
- foreach ($table['unique keys'] as $idx => $fields) {
- foreach ($fields as $name) {
- $keys[$name] = 'U';
- }
- }
- }
-
- if (!empty($table['primary key'])) {
- foreach ($table['primary key'] as $name) {
- $keys[$name] = 'K';
- }
- }
- return $keys;
- }
-
- /**
- * Build the appropriate DB_DataObject bitfield map for this field.
- *
- * @param array $column
- * @return int
- */
- public function columnBitmap($column)
- {
- $type = $column['type'];
-
- // For quoting style...
- $intTypes = [
- 'int',
- 'float',
- 'serial',
- 'numeric'
- ];
- if (in_array($type, $intTypes)) {
- $style = DB_DATAOBJECT_INT;
- } else {
- $style = DB_DATAOBJECT_STR;
- }
-
- // Data type formatting style...
- $formatStyles = [
- 'blob' => DB_DATAOBJECT_BLOB,
- 'text' => DB_DATAOBJECT_TXT,
- 'bool' => DB_DATAOBJECT_BOOL,
- 'date' => DB_DATAOBJECT_DATE,
- 'time' => DB_DATAOBJECT_TIME,
- 'datetime' => DB_DATAOBJECT_DATE | DB_DATAOBJECT_TIME,
- ];
-
- if (isset($formatStyles[$type])) {
- $style |= $formatStyles[$type];
- }
-
- // Nullable?
- if (!empty($column['not null'])) {
- $style |= DB_DATAOBJECT_NOTNULL;
- }
-
- return $style;
- }
-
- public function links()
- {
- $links = array();
-
- $table = static::schemaDef();
-
- foreach ($table['foreign keys'] as $keyname => $keydef) {
- if (count($keydef) == 2 && is_string($keydef[0]) && is_array($keydef[1]) && count($keydef[1]) == 1) {
- if (isset($keydef[1][0])) {
- $links[$keydef[1][0]] = $keydef[0].':'.$keydef[1][1];
- }
- }
- }
- return $links;
- }
-
- /**
- * Return a list of all primary/unique keys / vals that will be used for
- * caching. This will understand compound unique keys, which
- * Memcached_DataObject doesn't have enough info to handle properly.
- *
- * @return array of strings
- * @throws MethodNotImplementedException
- * @throws ServerException
- */
- public function _allCacheKeys()
- {
- $table = static::schemaDef();
- $ckeys = array();
-
- if (!empty($table['unique keys'])) {
- $keyNames = $table['unique keys'];
- foreach ($keyNames as $idx => $fields) {
- $val = array();
- foreach ($fields as $name) {
- $val[$name] = self::valueString($this->$name);
- }
- $ckeys[] = self::multicacheKey($this->tableName(), $val);
- }
- }
-
- if (!empty($table['primary key'])) {
- $fields = $table['primary key'];
- $val = array();
- foreach ($fields as $name) {
- $val[$name] = self::valueString($this->$name);
- }
- $ckeys[] = self::multicacheKey($this->tableName(), $val);
- }
- return $ckeys;
- }
-
- /**
- * Returns an object by looking at the primary key column(s).
- *
- * Will require all primary key columns to be defined in an associative array
- * and ignore any keys which are not part of the primary key.
- *
- * Will NOT accept NULL values as part of primary key.
- *
- * @param array $vals Must match all primary key columns for the dataobject.
- *
- * @return Managed_DataObject of the get_called_class() type
- * @throws NoResultException if no object with that primary key
- */
- public static function getByPK(array $vals)
- {
- $classname = get_called_class();
-
- $pkey = static::pkeyCols();
- if (is_null($pkey)) {
- throw new ServerException("Failed to get primary key columns for class '{$classname}'");
- }
-
- $object = new $classname();
- foreach ($pkey as $col) {
- if (!array_key_exists($col, $vals)) {
- throw new ServerException("Missing primary key column '{$col}' for ".get_called_class()." among provided keys: ".implode(',', array_keys($vals)));
- } elseif (is_null($vals[$col])) {
- throw new ServerException("NULL values not allowed in getByPK for column '{$col}'");
- }
- $object->$col = $vals[$col];
- }
- if (!$object->find(true)) {
- throw new NoResultException($object);
- }
- return $object;
- }
-
- /**
- * Returns an object by looking at given unique key columns.
- *
- * Will NOT accept NULL values for a unique key column. Ignores non-key values.
- *
- * @param array $vals All array keys which are set must be non-null.
- *
- * @return Managed_DataObject of the get_called_class() type
- * @throws NoResultException if no object with that primary key
- */
- public static function getByKeys(array $vals)
- {
- $classname = get_called_class();
-
- $object = new $classname();
-
- $keys = $object->keys();
- if (is_null($keys)) {
- throw new ServerException("Failed to get key columns for class '{$classname}'");
- }
-
- foreach ($keys as $col) {
- if (!array_key_exists($col, $vals)) {
- continue;
- } elseif (is_null($vals[$col])) {
- throw new ServerException("NULL values not allowed in getByKeys for column '{$col}'");
- }
- $object->$col = $vals[$col];
- }
- if (!$object->find(true)) {
- throw new NoResultException($object);
- }
- return $object;
- }
-
- public static function getByID($id)
- {
- if (!property_exists(get_called_class(), 'id')) {
- throw new ServerException('Trying to get undefined property of dataobject class.');
- }
- if (empty($id)) {
- throw new EmptyPkeyValueException(get_called_class(), 'id');
- }
- // getByPK throws exception if id is null
- // or if the class does not have a single 'id' column as primary key
- return static::getByPK(array('id' => $id));
- }
-
- public static function getByUri($uri)
- {
- if (!property_exists(get_called_class(), 'uri')) {
- throw new ServerException('Trying to get undefined property of dataobject class.');
- }
- if (empty($uri)) {
- throw new EmptyPkeyValueException(get_called_class(), 'uri');
- }
-
- $class = get_called_class();
- $obj = new $class();
- $obj->uri = $uri;
- if (!$obj->find(true)) {
- throw new NoResultException($obj);
- }
- return $obj;
- }
-
- /**
- * Returns an ID, checked that it is set and reasonably valid
- *
- * If this dataobject uses a special id field (not 'id'), just
- * implement your ID getting method in the child class.
- *
- * @return int ID of dataobject
- * @throws Exception (when ID is not available or not set yet)
- */
- public function getID()
- {
- // FIXME: Make these exceptions more specific (their own classes)
- if (!isset($this->id)) {
- throw new Exception('No ID set.');
- } elseif (empty($this->id)) {
- throw new Exception('Empty ID for object! (not inserted yet?).');
- }
-
- return intval($this->id);
- }
-
- /**
- * Check whether the column is NULL in SQL
- *
- * @param string $key column property name
- *
- * @return bool
- */
- public function isNull(string $key): bool
- {
- if (array_key_exists($key, get_object_vars($this))
- && is_null($this->$key)) {
- // If there was no fetch, this is a false positive.
- return true;
- } elseif (is_object($this->$key)
- && $this->$key instanceof DB_DataObject_Cast
- && $this->$key->type === 'sql') {
- // This is cast to raw SQL, let's see if it's NULL.
- return (strcasecmp($this->$key->value, 'NULL') == 0);
- } elseif (DB_DataObject::_is_null($this, $key)) {
- // DataObject's NULL magic should be disabled,
- // this is just for completeness.
- return true;
- }
- return false;
- }
-
- /**
- * WARNING: Only use this on Profile and Notice. We should probably do
- * this with traits/"implements" or whatever, but that's over the top
- * right now, I'm just throwing this in here to avoid code duplication
- * in Profile and Notice classes.
- */
- public function getAliases()
- {
- return array_keys($this->getAliasesWithIDs());
- }
-
- public function getAliasesWithIDs()
- {
- $aliases = array();
- $aliases[$this->getUri()] = $this->getID();
-
- try {
- $aliases[$this->getUrl()] = $this->getID();
- } catch (InvalidUrlException $e) {
- // getUrl failed because no valid URL could be returned, just ignore it
- }
-
- if (common_config('fix', 'fancyurls')) {
- /**
- * Here we add some hacky hotfixes for remote lookups that have been taught the
- * (at least now) wrong URI but it's still obviously the same user. Such as:
- * - https://site.example/user/1 even if the client requests https://site.example/index.php/user/1
- * - https://site.example/user/1 even if the client requests https://site.example//index.php/user/1
- * - https://site.example/index.php/user/1 even if the client requests https://site.example/user/1
- * - https://site.example/index.php/user/1 even if the client requests https://site.example///index.php/user/1
- */
- foreach ($aliases as $alias=>$id) {
- try {
- // get a "fancy url" version of the alias, even without index.php/
- $alt_url = common_fake_local_fancy_url($alias);
- // store this as well so remote sites can be sure we really are the same profile
- $aliases[$alt_url] = $id;
- } catch (Exception $e) {
- // Apparently we couldn't rewrite that, the $alias was as the function wanted it to be
- }
-
- try {
- // get a non-"fancy url" version of the alias, i.e. add index.php/
- $alt_url = common_fake_local_nonfancy_url($alias);
- // store this as well so remote sites can be sure we really are the same profile
- $aliases[$alt_url] = $id;
- } catch (Exception $e) {
- // Apparently we couldn't rewrite that, the $alias was as the function wanted it to be
- }
- }
- }
- return $aliases;
- }
-
- /**
- * Set the attribute defined as "timestamp" to CURRENT_TIMESTAMP.
- * This is hooked in update() and updateWithKeys() to update "modified".
- *
- * @access private
- * @return void
- */
- private function updateAutoTimestamps(): void
- {
- $table = static::schemaDef();
- foreach ($table['fields'] as $name => $col) {
- if ($col['type'] === 'timestamp'
- && !array_key_exists('default', $col)
- && !isset($this->$name)) {
- $this->$name = common_sql_now();
- }
- }
- }
-
- /**
- * update() won't write key columns, so we have to do it ourselves.
- * This also automatically calls "update" _before_ it sets the keys.
- * FIXME: This only works with single-column primary keys so far! Beware!
- *
- * @param Managed_DataObject $orig Must be "instanceof" $this
- * @param string $pid Primary ID column (no escaping is done on column name!)
- * @return bool|void
- * @throws MethodNotImplementedException
- * @throws ServerException
- */
- public function updateWithKeys(Managed_DataObject $orig, ?string $pid = null)
- {
- if (!$orig instanceof $this) {
- throw new ServerException('Tried updating a DataObject with a different class than itself.');
- }
-
- if ($this->N <1) {
- throw new ServerException('DataObject must be the result of a query (N>=1) before updateWithKeys()');
- }
-
- $this->onUpdateKeys($orig);
-
- // do it in a transaction
- $this->query('START TRANSACTION');
-
- // ON UPDATE CURRENT_TIMESTAMP behaviour
- // @fixme Should the value be reverted back if transaction failed?
- $this->updateAutoTimestamps();
-
- $parts = [];
- foreach ($this->keys() as $k) {
- $v = $this->table()[$k];
- if ($this->$k !== $orig->$k) {
- if (is_object($this->$k) && $this->$k instanceof DB_DataObject_Cast) {
- $value = $this->$k->toString($v, $this->getDatabaseConnection());
- } elseif (DB_DataObject::_is_null($this, $k)) {
- $value = 'NULL';
- } elseif ($v & DB_DATAOBJECT_STR) { // if a string
- $value = $this->_quote((string) $this->$k);
- } else {
- $value = (int) $this->$k;
- }
- $parts[] = "{$k} = {$value}";
- }
- }
- if (count($parts) == 0) {
- // No changes to keys, it's safe to run ->update(...)
- if ($this->update($orig) === false) {
- common_log_db_error($this, 'UPDATE', __FILE__);
- // rollback as something bad occurred
- $this->query('ROLLBACK');
- throw new ServerException("Could not UPDATE non-keys for {$this->tableName()}");
- }
- $orig->decache();
- $this->encache();
-
- // commit our db transaction since we won't reach the COMMIT below
- $this->query('COMMIT');
- // @FIXME return true only if something changed (otherwise 0)
- return true;
- }
-
- if ($pid === null) {
- $schema = static::schemaDef();
- $pid = $schema['primary key'];
- unset($schema);
- }
- $pidWhere = [];
- foreach ((array) $pid as $pidCol) {
- $pidWhere[] = sprintf('%1$s = %2$s', $pidCol, $this->_quote($orig->$pidCol));
- }
- if (empty($pidWhere)) {
- throw new ServerException('No primary ID column(s) set for updateWithKeys');
- }
-
- $qry = sprintf(
- 'UPDATE %1$s SET %2$s WHERE %3$s',
- $this->escapedTableName(),
- implode(', ', $parts),
- implode(' AND ', $pidWhere)
- );
-
- $result = $this->query($qry);
- if ($result === false) {
- common_log_db_error($this, 'UPDATE', __FILE__);
- // rollback as something bad occurred
- $this->query('ROLLBACK');
- throw new ServerException("Could not UPDATE key fields for {$this->tableName()}");
- }
-
- // Update non-keys too, if the previous endeavour worked.
- // The ->update call uses "$this" values for keys, that's why we can't do this until
- // the keys are updated (because they might differ from $orig and update the wrong entries).
- if ($this->update($orig) === false) {
- common_log_db_error($this, 'UPDATE', __FILE__);
- // rollback as something bad occurred
- $this->query('ROLLBACK');
- throw new ServerException("Could not UPDATE non-keys for {$this->tableName()}");
- }
- $orig->decache();
- $this->encache();
-
- // commit our db transaction
- $this->query('COMMIT');
- // @FIXME return true only if something changed (otherwise 0)
- return $result;
- }
-
- public static function beforeSchemaUpdate()
- {
- // NOOP
- }
-
- public static function newUri(Profile $actor, Managed_DataObject $object, $created = null)
- {
- if (is_null($created)) {
- $created = common_sql_now();
- }
- return TagURI::mint(
- strtolower(get_called_class()) . ':%d:%s:%d:%s',
- $actor->getID(),
- ActivityUtils::resolveUri($object->getObjectType(), true),
- $object->getID(),
- common_date_iso8601($created)
- );
- }
-
- protected function onInsert()
- {
- // NOOP by default
- }
-
- protected function onUpdate($dataObject=false)
- {
- // NOOP by default
- }
-
- protected function onUpdateKeys(Managed_DataObject $orig)
- {
- // NOOP by default
- }
-
- public function insert()
- {
- $this->onInsert();
- $result = parent::insert();
-
- // Make this object aware of the changed "modified" attribute.
- // Sets it approximately to the same value as DEFAULT CURRENT_TIMESTAMP
- // just did (@fixme).
- if ($result) {
- $this->updateAutoTimestamps();
- }
- return $result;
- }
-
- public function update($dataObject = false)
- {
- $this->onUpdate($dataObject);
-
- // ON UPDATE CURRENT_TIMESTAMP behaviour
- // @fixme Should the value be reverted back if transaction failed?
- $this->updateAutoTimestamps();
-
- return parent::update($dataObject);
- }
-}
diff --git a/src/Entity/Memcached_DataObject.php b/src/Entity/Memcached_DataObject.php
deleted file mode 100644
index d2140e3497..0000000000
--- a/src/Entity/Memcached_DataObject.php
+++ /dev/null
@@ -1,1058 +0,0 @@
-.
-
-/**
- * @copyright 2008, 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Memcached_DataObject extends Safe_DataObject
-{
- /**
- * Wrapper for DB_DataObject's static lookup using memcached
- * as backing instead of an in-process cache array.
- *
- * @param string $cls classname of object type to load
- * @param mixed $k key field name, or value for primary key
- * @param mixed $v key field value, or leave out for primary key lookup
- * @return mixed Memcached_DataObject subtype or false
- */
- public static function getClassKV($cls, $k, $v = null)
- {
- if (is_null($v)) {
- $v = $k;
- $keys = static::pkeyCols();
- if (count($keys) > 1) {
- // FIXME: maybe call pkeyGetClass() ourselves?
- throw new Exception('Use pkeyGetClass() for compound primary keys');
- }
- $k = $keys[0];
- }
- $i = self::getcached($cls, $k, $v);
- if ($i === false) { // false == cache miss
- $i = new $cls;
- $result = $i->get($k, $v);
- if ($result) {
- // Hit!
- $i->encache();
- } else {
- // save the fact that no such row exists
- $c = self::memcache();
- if (!empty($c)) {
- $ck = self::cachekey($cls, $k, $v);
- $c->set($ck, null);
- }
- $i = false;
- }
- }
- return $i;
- }
-
- /**
- * Get multiple items from the database by key
- *
- * @param string $cls Class to fetch
- * @param string $keyCol name of column for key
- * @param array $keyVals key values to fetch
- * @param bool $skipNulls return only non-null results
- * @param bool $preserve return the same tuples as input
- * @return object An object with tuples to be fetched, in order
- */
- public static function multiGetClass(
- string $cls,
- string $keyCol,
- array $keyVals,
- bool $skipNulls,
- bool $preserve
- ): object {
- $obj = new $cls();
-
- // Do not select anything extra
- $obj->selectAdd();
- $obj->selectAdd($obj->escapedTableName() . '.*');
-
- // A PHP-compatible datatype to check against
- $col_type = $obj->columnType($keyCol);
-
- // The code below assumes one of the two results
- if (!in_array($col_type, ['int', 'string'])) {
- throw new ServerException(
- 'Cannot do multiGet on anything but integer or string columns'
- );
- }
-
- // Actually need to know if MariaDB or Oracle MySQL this time
- $db_type = common_config('db', 'type');
- if ($db_type === 'mysql') {
- $tmp_obj = new $cls();
- $tmp_obj->query('SELECT 0 /*M! + 1 */ AS is_mariadb;');
- if ($tmp_obj->fetch() && $tmp_obj->is_mariadb) {
- $db_type = 'mariadb';
- }
- }
-
- // Since we're inputting straight to a query: format and escape
- $vals_escaped = [];
- foreach (array_values($keyVals) as $i => $val) {
- if (is_null($val)) {
- $val_escaped = 'NULL';
- } elseif ($col_type === 'int') {
- $val_escaped = (string)(int) $val;
- } else {
- $val_escaped = "'{$obj->escape($val)}'";
- }
- if ($db_type !== 'mariadb') {
- $vals_escaped[] = $val_escaped;
- } else {
- // A completely different approach for MariaDB (see below)
- $vals_escaped[] = "({$val_escaped},{$i})";
- }
- }
-
- // One way to guarantee that there is no name collision
- $join_tablename = common_database_tablename(
- $obj->tableName() . '_vals'
- );
- $join_keyword = ($preserve ? 'RIGHT' : 'INNER') . ' JOIN';
- $vals_cast_type = ($col_type === 'int') ? 'INTEGER' : 'TEXT';
-
- // A lot of magic to ensure we get an ordered reply with the same exact
- // values as on input.
- switch ($db_type) {
- case 'pgsql':
- // Explicit casting is done to cast empty arrays
- $obj->_join = "\n" . sprintf(
- <<_join = "\n" . sprintf(
- <<_join = "\n" . sprintf(
- <<whereAdd("{$obj->escapedTableName()}.{$keyCol} IS NOT NULL");
- }
-
- $obj->orderBy("{$join_tablename}.{$keyCol}_pos");
-
- $obj->find();
- return $obj;
- }
-
- /**
- * Get multiple items from the database by key
- *
- * @param string $cls Class to fetch
- * @param string $keyCol name of column for key
- * @param array $keyVals key values to fetch
- * @param boolean $otherCols Other columns to hold fixed
- *
- * @return array Array mapping $keyVals to objects, or null if not found
- */
- public static function pivotGetClass(
- $cls,
- $keyCol,
- array $keyVals,
- array $otherCols = []
- ) {
- if (is_array($keyCol)) {
- foreach ($keyVals as $keyVal) {
- if (!is_array($keyVal)) {
- throw new ServerException(
- 'keyVals passed to pivotGet must be an array of arrays '
- . 'if keyCol is an array'
- );
- }
- $result[implode(',', $keyVal)] = null;
- }
- } else {
- $result = array_fill_keys($keyVals, null);
- }
-
- $toFetch = array();
-
- foreach ($keyVals as $keyVal) {
- if (is_array($keyCol)) {
- $kv = array_combine($keyCol, $keyVal);
- } else {
- $kv = array($keyCol => $keyVal);
- }
-
- $kv = array_merge($otherCols, $kv);
-
- $i = self::multicache($cls, $kv);
-
- if ($i !== false) {
- if (is_array($keyCol)) {
- $result[implode(',', $keyVal)] = $i;
- } else {
- $result[$keyVal] = $i;
- }
- } elseif (!empty($keyVal)) {
- $toFetch[] = $keyVal;
- }
- }
-
- if (count($toFetch) > 0) {
- $i = new $cls;
- foreach ($otherCols as $otherKeyCol => $otherKeyVal) {
- $i->$otherKeyCol = $otherKeyVal;
- }
- if (is_array($keyCol)) {
- $i->whereAdd(self::_inMultiKey($i, $keyCol, $toFetch));
- } else {
- $i->whereAddIn($keyCol, $toFetch, $i->columnType($keyCol));
- }
- if ($i->find()) {
- while ($i->fetch()) {
- $copy = clone($i);
- $copy->encache();
- if (is_array($keyCol)) {
- $vals = array();
- foreach ($keyCol as $k) {
- $vals[] = $i->$k;
- }
- $result[implode(',', $vals)] = $copy;
- } else {
- $result[$i->$keyCol] = $copy;
- }
- }
- }
-
- // Save state of DB misses
-
- foreach ($toFetch as $keyVal) {
- $r = null;
- if (is_array($keyCol)) {
- $r = $result[implode(',', $keyVal)];
- } else {
- $r = $result[$keyVal];
- }
- if (empty($r)) {
- if (is_array($keyCol)) {
- $kv = array_combine($keyCol, $keyVal);
- } else {
- $kv = array($keyCol => $keyVal);
- }
- $kv = array_merge($otherCols, $kv);
- // save the fact that no such row exists
- $c = self::memcache();
- if (!empty($c)) {
- $ck = self::multicacheKey($cls, $kv);
- $c->set($ck, null);
- }
- }
- }
- }
-
- return $result;
- }
-
- public static function _inMultiKey($i, $cols, $values)
- {
- $types = array();
-
- foreach ($cols as $col) {
- $types[$col] = $i->columnType($col);
- }
-
- $first = true;
-
- $query = '';
-
- foreach ($values as $value) {
- if ($first) {
- $query .= '( ';
- $first = false;
- } else {
- $query .= ' OR ';
- }
- $query .= '( ';
- $i = 0;
- $firstc = true;
- foreach ($cols as $col) {
- if (!$firstc) {
- $query .= ' AND ';
- } else {
- $firstc = false;
- }
- switch ($types[$col]) {
- case 'string':
- case 'datetime':
- $query .= sprintf("%s = %s", $col, $i->_quote($value[$i]));
- break;
- default:
- $query .= sprintf("%s = %s", $col, $value[$i]);
- break;
- }
- }
- $query .= ') ';
- }
-
- if (!$first) {
- $query .= ' )';
- }
-
- return $query;
- }
-
- public static function pkeyColsClass($cls)
- {
- $i = new $cls;
- $types = $i->keyTypes();
- ksort($types);
-
- $pkey = array();
-
- foreach ($types as $key => $type) {
- if ($type == 'K' || $type == 'N') {
- $pkey[] = $key;
- }
- }
-
- return $pkey;
- }
-
- public static function listFindClass($cls, $keyCol, array $keyVals)
- {
- $i = new $cls;
- $i->whereAddIn($keyCol, $keyVals, $i->columnType($keyCol));
- if (!$i->find()) {
- throw new NoResultException($i);
- }
-
- return $i;
- }
-
- public static function listGetClass($cls, $keyCol, array $keyVals)
- {
- $pkeyMap = array_fill_keys($keyVals, array());
- $result = array_fill_keys($keyVals, array());
-
- $pkeyCols = static::pkeyCols();
-
- $toFetch = array();
- $allPkeys = array();
-
- // We only cache keys -- not objects!
-
- foreach ($keyVals as $keyVal) {
- $l = self::cacheGet(sprintf('%s:list-ids:%s:%s', strtolower($cls), $keyCol, $keyVal));
- if ($l !== false) {
- $pkeyMap[$keyVal] = $l;
- foreach ($l as $pkey) {
- $allPkeys[] = $pkey;
- }
- } else {
- $toFetch[] = $keyVal;
- }
- }
-
- if (count($allPkeys) > 0) {
- $keyResults = self::pivotGetClass($cls, $pkeyCols, $allPkeys);
-
- foreach ($pkeyMap as $keyVal => $pkeyList) {
- foreach ($pkeyList as $pkeyVal) {
- $i = $keyResults[implode(',', $pkeyVal)];
- if (!empty($i)) {
- $result[$keyVal][] = $i;
- }
- }
- }
- }
-
- if (count($toFetch) > 0) {
- try {
- $i = self::listFindClass($cls, $keyCol, $toFetch);
-
- while ($i->fetch()) {
- $copy = clone($i);
- $copy->encache();
- $result[$i->$keyCol][] = $copy;
- $pkeyVal = array();
- foreach ($pkeyCols as $pkeyCol) {
- $pkeyVal[] = $i->$pkeyCol;
- }
- $pkeyMap[$i->$keyCol][] = $pkeyVal;
- }
- } catch (NoResultException $e) {
- // no results found for our keyVals, so we leave them as empty arrays
- }
- foreach ($toFetch as $keyVal) {
- self::cacheSet(
- sprintf("%s:list-ids:%s:%s", strtolower($cls), $keyCol, $keyVal),
- $pkeyMap[$keyVal]
- );
- }
- }
-
- return $result;
- }
-
- public function escapedTableName()
- {
- return common_database_tablename($this->tableName());
- }
-
- public function columnType($columnName)
- {
- $keys = $this->table();
- if (!array_key_exists($columnName, $keys)) {
- throw new Exception('Unknown key column ' . $columnName . ' in ' . join(',', array_keys($keys)));
- }
-
- $def = $keys[$columnName];
-
- if ($def & DB_DATAOBJECT_INT) {
- return 'int';
- } else {
- return 'string';
- }
- }
-
- /**
- * @todo FIXME: Should this return false on lookup fail to match getKV?
- */
- public static function pkeyGetClass($cls, array $kv)
- {
- $i = self::multicache($cls, $kv);
- if ($i !== false) { // false == cache miss
- return $i;
- } else {
- $i = new $cls;
- foreach ($kv as $k => $v) {
- if (is_null($v)) {
- // XXX: possible SQL injection...? Don't
- // pass keys from the browser, eh.
- $i->whereAdd("$k is null");
- } else {
- $i->$k = $v;
- }
- }
- if ($i->find(true)) {
- $i->encache();
- } else {
- $i = null;
- $c = self::memcache();
- if (!empty($c)) {
- $ck = self::multicacheKey($cls, $kv);
- $c->set($ck, null);
- }
- }
- return $i;
- }
- }
-
- public function insert()
- {
- $result = parent::insert();
- if ($result !== false) {
- // In case of cached negative lookups
- $this->decache();
- }
- return $result;
- }
-
- public function update($dataObject = false)
- {
- if (is_object($dataObject) && $dataObject instanceof Memcached_DataObject) {
- $dataObject->decache(); // might be different keys
- }
- $result = parent::update($dataObject);
- if ($result !== false) {
- // Cannot encache yet, so decache instead
- $this->decache();
- }
- return $result;
- }
-
- public function delete($useWhere = false)
- {
- $this->decache(); # while we still have the values!
- return parent::delete($useWhere);
- }
-
- public static function memcache()
- {
- return Cache::instance();
- }
-
- public static function cacheKey($cls, $k, $v)
- {
- if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
- $e = new Exception();
- common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
- str_replace("\n", " ", $e->getTraceAsString()));
- }
- $vstr = self::valueString($v);
- return Cache::key(strtolower($cls).':'.$k.':'.$vstr);
- }
-
- public static function getcached($cls, $k, $v)
- {
- $c = self::memcache();
- if (!$c) {
- return false;
- } else {
- $obj = $c->get(self::cacheKey($cls, $k, $v));
- if (0 == strcasecmp($cls, 'User')) {
- // Special case for User
- if (is_object($obj) && is_object($obj->id)) {
- common_log(LOG_ERR, "User " . $obj->nickname . " was cached with User as ID; deleting");
- $c->delete(self::cacheKey($cls, $k, $v));
- return false;
- }
- }
- return $obj;
- }
- }
-
- public function keyTypes()
- {
- // ini-based classes return number-indexed arrays. handbuilt
- // classes return column => keytype. Make this uniform.
-
- $keys = $this->keys();
-
- $keyskeys = array_keys($keys);
-
- if (is_string($keyskeys[0])) {
- return $keys;
- }
-
- global $_DB_DATAOBJECT;
- if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
- $this->databaseStructure();
- }
- return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"];
- }
-
- public function encache()
- {
- if ($this->N < 1) {
- // Caching breaks when it is too early.
- $e = new Exception();
- common_log(
- LOG_ERR,
- 'DataObject must be the result of a query (N>=1) before encache() '
- . str_replace("\n", ' ', $e->getTraceAsString())
- );
- return false;
- }
-
- $c = self::memcache();
-
- if (!$c) {
- return false;
- } elseif ($this->tableName() === 'user' && is_object($this->id)) {
- // Special case for User bug
- $e = new Exception();
- common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' .
- str_replace("\n", " ", $e->getTraceAsString()));
- return false;
- } else {
- $keys = $this->_allCacheKeys();
-
- foreach ($keys as $key) {
- $c->set($key, $this);
- }
- }
- }
-
- public function decache()
- {
- $c = self::memcache();
-
- if (!$c) {
- return false;
- }
-
- $keys = $this->_allCacheKeys();
-
- foreach ($keys as $key) {
- $c->delete($key, $this);
- }
- }
-
- public function _allCacheKeys()
- {
- $ckeys = array();
-
- $types = $this->keyTypes();
- ksort($types);
-
- $pkey = array();
- $pval = array();
-
- foreach ($types as $key => $type) {
- assert(!empty($key));
-
- if ($type == 'U') {
- if (empty($this->$key)) {
- continue;
- }
- $ckeys[] = self::cacheKey($this->tableName(), $key, self::valueString($this->$key));
- } elseif (in_array($type, ['K', 'N'])) {
- $pkey[] = $key;
- $pval[] = self::valueString($this->$key);
- } else {
- // Low level exception. No need for i18n as discussed with Brion.
- throw new Exception("Unknown key type $key => $type for " . $this->tableName());
- }
- }
-
- assert(count($pkey) > 0);
-
- // XXX: should work for both compound and scalar pkeys
- $pvals = implode(',', $pval);
- $pkeys = implode(',', $pkey);
-
- $ckeys[] = self::cacheKey($this->tableName(), $pkeys, $pvals);
-
- return $ckeys;
- }
-
- public static function multicache($cls, array $kv)
- {
- ksort($kv);
- $c = self::memcache();
- if (!$c) {
- return false;
- } else {
- return $c->get(self::multicacheKey($cls, $kv));
- }
- }
-
- public static function multicacheKey($cls, array $kv)
- {
- ksort($kv);
- $pkeys = implode(',', array_keys($kv));
- $pvals = implode(',', array_values($kv));
- return self::cacheKey($cls, $pkeys, $pvals);
- }
-
- public function getSearchEngine($table)
- {
- require_once INSTALLDIR . '/lib/search/search_engines.php';
-
- if (Event::handle('GetSearchEngine', [$this, $table, &$search_engine])) {
- $type = common_config('search', 'type');
- if ($type === 'like') {
- $search_engine = new SQLLikeSearch($this, $table);
- } elseif ($type === 'fulltext') {
- switch (common_config('db', 'type')) {
- case 'pgsql':
- $search_engine = new PostgreSQLSearch($this, $table);
- break;
- case 'mysql':
- $search_engine = new MySQLSearch($this, $table);
- break;
- default:
- throw new ServerException('Unknown DB type selected.');
- }
- } else {
- // Low level exception. No need for i18n as discussed with Brion.
- throw new ServerException('Unknown search type: ' . $type);
- }
- }
-
- return $search_engine;
- }
-
- public static function cachedQuery($cls, $qry, $expiry = 3600)
- {
- $c = self::memcache();
- if (!$c) {
- $inst = new $cls();
- $inst->query($qry);
- return $inst;
- }
- $key_part = Cache::keyize($cls).':'.md5($qry);
- $ckey = Cache::key($key_part);
- $stored = $c->get($ckey);
-
- if ($stored !== false) {
- return new ArrayWrapper($stored);
- }
-
- $inst = new $cls();
- $inst->query($qry);
- $cached = array();
- while ($inst->fetch()) {
- $cached[] = clone($inst);
- }
- $inst->free();
- $c->set($ckey, $cached, Cache::COMPRESSED, $expiry);
- return new ArrayWrapper($cached);
- }
-
- /**
- * sends query to database - this is the private one that must work
- * - internal functions use this rather than $this->query()
- *
- * Overridden to do logging.
- *
- * @param string $string
- * @access private
- * @return mixed none or PEAR_Error
- */
- public function _query($string)
- {
- if (common_config('db', 'annotate_queries')) {
- $string = $this->annotateQuery($string);
- }
-
- $start = hrtime(true);
- $fail = false;
- $result = null;
- if (Event::handle('StartDBQuery', array($this, $string, &$result))) {
- common_perf_counter('query', $string);
- try {
- $result = parent::_query($string);
- } catch (Exception $e) {
- $fail = $e;
- }
- Event::handle('EndDBQuery', array($this, $string, &$result));
- }
- $delta = (hrtime(true) - $start) / 1000000000;
-
- $limit = common_config('db', 'log_slow_queries');
- if (($limit > 0 && $delta >= $limit) || common_config('db', 'log_queries')) {
- $clean = $this->sanitizeQuery($string);
- if ($fail) {
- $msg = sprintf("FAILED DB query (%0.3fs): %s - %s", $delta, $fail->getMessage(), $clean);
- } else {
- $msg = sprintf("DB query (%0.3fs): %s", $delta, $clean);
- }
- common_log(LOG_DEBUG, $msg);
- }
-
- if ($fail) {
- throw $fail;
- }
- return $result;
- }
-
- /**
- * Find the first caller in the stack trace that's not a
- * low-level database function and add a comment to the
- * query string. This should then be visible in process lists
- * and slow query logs, to help identify problem areas.
- *
- * Also marks whether this was a web GET/POST or which daemon
- * was running it.
- *
- * @param string $string SQL query string
- * @return string SQL query string, with a comment in it
- */
- public function annotateQuery($string)
- {
- $ignore = array('annotateQuery',
- '_query',
- 'query',
- 'get',
- 'insert',
- 'delete',
- 'update',
- 'find');
- $ignoreStatic = array('getKV',
- 'getClassKV',
- 'pkeyGet',
- 'pkeyGetClass',
- 'cachedQuery');
- $here = get_class($this); // if we get confused
- $bt = debug_backtrace();
-
- // Find the first caller that's not us?
- foreach ($bt as $frame) {
- $func = $frame['function'];
- if (isset($frame['type']) && $frame['type'] == '::') {
- if (in_array($func, $ignoreStatic)) {
- continue;
- }
- $here = $frame['class'] . '::' . $func;
- break;
- } elseif (isset($frame['type']) && $frame['type'] === '->') {
- if ($frame['object'] === $this && in_array($func, $ignore)) {
- continue;
- }
- if (in_array($func, $ignoreStatic)) {
- continue; // @todo FIXME: This shouldn't be needed?
- }
- $here = get_class($frame['object']) . '->' . $func;
- break;
- }
- $here = $func;
- break;
- }
-
- if (php_sapi_name() == 'cli') {
- $context = basename($_SERVER['PHP_SELF']);
- } else {
- $context = $_SERVER['REQUEST_METHOD'];
- }
-
- // Slip the comment in after the first command,
- // or DB_DataObject gets confused about handling inserts and such.
- $parts = explode(' ', $string, 2);
- $parts[0] .= " /* $context $here */";
- return implode(' ', $parts);
- }
-
- // Sanitize a query for logging
- // @fixme don't trim spaces in string literals
- public function sanitizeQuery($string)
- {
- $string = preg_replace('/\s+/', ' ', $string);
- $string = trim($string);
- return $string;
- }
-
- public function _connect()
- {
- global $_DB_DATAOBJECT, $_PEAR;
-
- $sum = $this->_getDbDsnMD5();
-
- if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$sum]) &&
- !$_PEAR->isError($_DB_DATAOBJECT['CONNECTIONS'][$sum])) {
- $exists = true;
- } else {
- $exists = false;
- }
-
- // @fixme horrible evil hack!
- //
- // In multisite configuration we don't want to keep around a separate
- // connection for every database; we could end up with thousands of
- // connections open per thread. In an ideal world we might keep
- // a connection per server and select different databases, but that'd
- // be reliant on having the same db username/pass as well.
- //
- // MySQL connections are cheap enough we're going to try just
- // closing out the old connection and reopening when we encounter
- // a new DSN.
- //
- // WARNING WARNING if we end up actually using multiple DBs at a time
- // we'll need some fancier logic here.
- if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS']) && php_sapi_name() == 'cli') {
- foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) {
- if ($_PEAR->isError($conn)) {
- common_log(LOG_WARNING, __METHOD__ . " cannot disconnect failed DB connection: '".$conn->getMessage()."'.");
- } elseif (!empty($conn)) {
- $conn->disconnect();
- }
- unset($_DB_DATAOBJECT['CONNECTIONS'][$index]);
- }
- }
-
- $result = parent::_connect();
-
- if ($result && !$exists) {
- // Required to make timestamp values usefully comparable.
- if (common_config('db', 'type') !== 'mysql') {
- parent::_query("SET TIME ZONE INTERVAL '+00:00' HOUR TO MINUTE");
- } else {
- parent::_query("SET time_zone = '+0:00'");
- }
- }
-
- return $result;
- }
-
- // XXX: largely cadged from DB_DataObject
-
- public function _getDbDsnMD5()
- {
- if ($this->_database_dsn_md5) {
- return $this->_database_dsn_md5;
- }
-
- $dsn = $this->_getDbDsn();
-
- if (is_string($dsn)) {
- $sum = md5($dsn);
- } else {
- /// support array based dsn's
- $sum = md5(serialize($dsn));
- }
-
- return $sum;
- }
-
- public function _getDbDsn()
- {
- global $_DB_DATAOBJECT;
-
- if (empty($_DB_DATAOBJECT['CONFIG'])) {
- self::_loadConfig();
- }
-
- $options = &$_DB_DATAOBJECT['CONFIG'];
-
- // if the databse dsn dis defined in the object..
-
- $dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
-
- if (!$dsn) {
- if (!$this->_database) {
- $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
- }
-
- if ($this->_database && !empty($options["database_{$this->_database}"])) {
- $dsn = $options["database_{$this->_database}"];
- } elseif (!empty($options['database'])) {
- $dsn = $options['database'];
- }
- }
-
- if (!$dsn) {
- // TRANS: Exception thrown when database name or Data Source Name could not be found.
- throw new Exception(_('No database name or DSN found anywhere.'));
- }
-
- return $dsn;
- }
-
- public static function blow()
- {
- $c = self::memcache();
-
- if (empty($c)) {
- return false;
- }
-
- $args = func_get_args();
-
- $format = array_shift($args);
-
- $keyPart = vsprintf($format, $args);
-
- $cacheKey = Cache::key($keyPart);
-
- return $c->delete($cacheKey);
- }
-
- public function raiseError($message, $type = null, $behavior = null)
- {
- $id = get_class($this);
- if (!empty($this->id)) {
- $id .= ':' . $this->id;
- }
- if ($message instanceof PEAR_Error) {
- $message = $message->getMessage();
- }
- // Low level exception. No need for i18n as discussed with Brion.
- throw new ServerException("[$id] DB_DataObject error [$type]: $message");
- }
-
- public static function cacheGet($keyPart)
- {
- $c = self::memcache();
-
- if (empty($c)) {
- return false;
- }
-
- $cacheKey = Cache::key($keyPart);
-
- return $c->get($cacheKey);
- }
-
- public static function cacheSet($keyPart, $value, $flag = null, $expiry = null)
- {
- $c = self::memcache();
-
- if (empty($c)) {
- return false;
- }
-
- $cacheKey = Cache::key($keyPart);
-
- return $c->set($cacheKey, $value, $flag, $expiry);
- }
-
- public static function valueString($v)
- {
- $vstr = null;
- if (is_object($v) && $v instanceof DB_DataObject_Cast) {
- switch ($v->type) {
- case 'date':
- $vstr = "{$v->year} - {$v->month} - {$v->day}";
- break;
- case 'sql':
- if (strcasecmp($v->value, 'NULL') == 0) {
- // Very selectively handling NULLs.
- $vstr = '';
- break;
- }
- // no break
- case 'blob':
- case 'string':
- case 'datetime':
- case 'time':
- // Low level exception. No need for i18n as discussed with Brion.
- throw new ServerException("Unhandled DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
- break;
- default:
- // Low level exception. No need for i18n as discussed with Brion.
- throw new ServerException("Unknown DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
- break;
- }
- } else {
- $vstr = strval($v);
- }
- return $vstr;
- }
-}
diff --git a/src/Entity/Nonce.php b/src/Entity/Nonce.php
index 05f892b4dd..4e63b45f30 100644
--- a/src/Entity/Nonce.php
+++ b/src/Entity/Nonce.php
@@ -1,68 +1,58 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for nonce
+ * Entity for nonce
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Nonce extends Managed_DataObject
+class Nonce
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'nonce'; // table name
- public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
- public $tok; // char(32)
- public $nonce; // char(32) primary_key not_null
- public $ts; // datetime() primary_key not_null
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- /**
- * Compatibility hack for PHP 5.3
- *
- * The statusnet.links.ini entry cannot be read because "," is no longer
- * allowed in key names when read by parse_ini_file().
- *
- * @return array
- * @access public
- */
- public function links()
+ public static function schemaDef(): array
{
- return array('consumer_key,token' => 'token:consumer_key,token');
- }
-
- public static function schemaDef()
- {
- return array(
+ return [
+ 'name' => 'nonce',
'description' => 'OAuth nonce record',
- 'fields' => array(
- 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
- 'tok' => array('type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'),
- 'nonce' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'),
- 'ts' => array('type' => 'datetime', 'not null' => true, 'description' => 'timestamp sent'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('consumer_key', 'ts', 'nonce'),
- );
+ 'fields' => [
+ 'consumer_key' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'],
+ 'tok' => ['type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'],
+ 'nonce' => ['type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'],
+ 'ts' => ['type' => 'datetime', 'not null' => true, 'description' => 'timestamp sent'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['consumer_key', 'ts', 'nonce'],
+ ];
}
}
diff --git a/src/Entity/Notice.php b/src/Entity/Notice.php
deleted file mode 100644
index f2c210e59a..0000000000
--- a/src/Entity/Notice.php
+++ /dev/null
@@ -1,3302 +0,0 @@
-.
-
-/**
- * @category Notices
- * @package GNUsocial
- * @author Brenda Wallace
- * @author Christopher Vollick
- * @author CiaranG
- * @author Craig Andrews
- * @author Evan Prodromou
- * @author Gina Haeussge
- * @author Jeffery To
- * @author Mike Cochrane
- * @author Robin Millette
- * @author Sarven Capadisli
- * @author Tom Adams
- * @author Mikael Nordfeldth
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for notice
- */
-
-/* We keep 200 notices, the max number of notices available per API request,
- * in the memcached cache. */
-
-define('NOTICE_CACHE_WINDOW', CachingNoticeStream::CACHE_WINDOW);
-
-define('MAX_BOXCARS', 128);
-
-class Notice extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'notice'; // table name
- public $id; // int(4) primary_key not_null
- public $profile_id; // int(4) multiple_key not_null
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $content; // text
- public $rendered; // text
- public $url; // text
- public $created; // datetime() multiple_key
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $reply_to; // int(4)
- public $is_local; // int(4)
- public $source; // varchar(32)
- public $conversation; // int(4)
- public $repeat_of; // int(4)
- public $verb; // varchar(191) not 255 because utf8mb4 takes more space
- public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
- public $scope; // int(4)
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- $def = array(
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'),
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
- 'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
- 'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
- 'url' => array('type' => 'text', 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- 'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'),
- 'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'),
- 'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
- 'conversation' => array('type' => 'int', 'description' => 'the local numerical conversation id'),
- 'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
- 'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => null),
- 'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
- 'scope' => array('type' => 'int',
- 'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = groups; 8 = followers; 16 = messages; null = default'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'notice_uri_key' => array('uri'),
- ),
- 'foreign keys' => array(
- 'notice_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- 'notice_reply_to_fkey' => array('notice', array('reply_to' => 'id')),
- 'notice_conversation_fkey' => array('conversation', array('conversation' => 'id')), # note... used to refer to notice.id
- 'notice_repeat_of_fkey' => array('notice', array('repeat_of' => 'id')), # @fixme: what about repeats of deleted notices?
- ),
- 'indexes' => array(
- 'notice_is_local_created_id_idx' => array('is_local', 'created', 'id'),
- 'notice_profile_id_created_id_idx' => array('profile_id', 'created', 'id'),
- 'notice_profile_id_verb_scope_created_id_idx' => array('profile_id', 'verb', 'scope', 'created', 'id'),
- 'notice_is_local_created_profile_id_idx' => array('is_local', 'created', 'profile_id'),
- 'notice_repeat_of_created_id_idx' => array('repeat_of', 'created', 'id'),
- 'notice_conversation_created_id_idx' => array('conversation', 'created', 'id'),
- 'notice_object_type_idx' => array('object_type'),
- 'notice_verb_idx' => array('verb'),
- 'notice_url_idx' => array(array('url', 191)), // Qvitter wants this
- 'notice_reply_to_idx' => array('reply_to')
- ),
- 'fulltext indexes' => array(
- 'notice_fulltext_idx' => array('content'),
- ),
- );
-
- return $def;
- }
-
- /* Notice types */
- const LOCAL_PUBLIC = 1;
- const REMOTE = 0;
- const LOCAL_NONPUBLIC = -1;
- const GATEWAY = -2;
-
- const PUBLIC_SCOPE = 0; // Useful fake constant
- const SITE_SCOPE = 1;
- const ADDRESSEE_SCOPE = 2;
- const GROUP_SCOPE = 4;
- const FOLLOWER_SCOPE = 8;
- const MESSAGE_SCOPE = 16;
-
- protected $_profile = array();
-
- /**
- * Will always return a profile, if anything fails it will
- * (through _setProfile) throw a NoProfileException.
- */
- public function getProfile()
- {
- if (!isset($this->_profile[$this->profile_id])) {
- // We could've sent getKV directly to _setProfile, but occasionally we get
- // a "false" (instead of null), likely because it indicates a cache miss.
- $profile = Profile::getKV('id', $this->profile_id);
- $this->_setProfile($profile instanceof Profile ? $profile : null);
- }
- return $this->_profile[$this->profile_id];
- }
-
- public function _setProfile(Profile $profile=null)
- {
- if (!$profile instanceof Profile) {
- throw new NoProfileException($this->profile_id);
- }
- $this->_profile[$this->profile_id] = $profile;
- }
-
- public function deleteAs(Profile $actor, $delete_event=true)
- {
- if (!$this->getProfile()->sameAs($actor) && !$actor->hasRight(Right::DELETEOTHERSNOTICE)) {
- throw new AuthorizationException(_('You are not allowed to delete another user\'s notice.'));
- }
-
- $result = null;
- if (!$delete_event || Event::handle('DeleteNoticeAsProfile', array($this, $actor, &$result))) {
- // If $delete_event is true, we run the event. If the Event then
- // returns false it is assumed everything was handled properly
- // and the notice was deleted.
- $result = $this->delete();
- }
- return $result;
- }
-
- protected function deleteRelated()
- {
- if (Event::handle('NoticeDeleteRelated', array($this))) {
- // Clear related records
- $this->clearReplies();
- $this->clearLocation();
- $this->clearPrefs();
- $this->clearRepeats();
- $this->clearTags();
- $this->clearGroupInboxes();
- $this->clearFiles();
- $this->clearAttentions();
- // NOTE: we don't clear queue items
- }
- }
-
- public function delete($useWhere=false)
- {
- $this->deleteRelated();
-
- $result = parent::delete($useWhere);
-
- $this->blowOnDelete();
- return $result;
- }
-
- public function getUri()
- {
- return $this->uri;
- }
-
- /*
- * Get a Notice object by URI. Will call external plugins for help
- * using the event StartGetNoticeFromURI.
- *
- * @param string $uri A unique identifier for a resource (notice in this case)
- */
- public static function fromUri($uri)
- {
- $notice = null;
-
- if (Event::handle('StartGetNoticeFromUri', array($uri, &$notice))) {
- $notice = Notice::getKV('uri', $uri);
- Event::handle('EndGetNoticeFromUri', array($uri, $notice));
- }
-
- if (!$notice instanceof Notice) {
- throw new UnknownUriException($uri);
- }
-
- return $notice;
- }
-
- /**
- * @param bool $anchor If false, link to just the conversation root.
- *
- * @return string URL to conversation
- */
- public function getConversationUrl(bool $anchor = true): string
- {
- return Conversation::getUrlFromNotice($this, $anchor);
- }
-
- /*
- * Get the local representation URL of this notice.
- */
- public function getLocalUrl()
- {
- return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
- }
-
- public function getTitle($imply=true)
- {
- $title = null;
- if (Event::handle('GetNoticeTitle', array($this, &$title)) && $imply) {
- // TRANS: Title of a notice posted without a title value.
- // TRANS: %1$s is a user name, %2$s is the notice creation date/time.
- $title = sprintf(
- _('%1$s\'s status on %2$s'),
- $this->getProfile()->getFancyName(),
- common_exact_date($this->created)
- );
- }
- return $title;
- }
-
- public function getContent()
- {
- return $this->content;
- }
-
- public function getRendered()
- {
- // we test $this->id because if it's not inserted yet, we can't update the field
- if (!empty($this->id) && (is_null($this->rendered) || $this->rendered === '')) {
- // update to include rendered content on-the-fly, so we don't have to have a fix-up script in upgrade.php
- common_debug('Rendering notice '.$this->getID().' as it had no rendered HTML content.');
- $orig = clone($this);
- $this->rendered = common_render_content(
- $this->getContent(),
- $this->getProfile(),
- ($this->hasParent() ? $this->getParent() : null)
- );
- $this->update($orig);
- }
- return $this->rendered;
- }
-
- public function getCreated()
- {
- return $this->created;
- }
-
- public function getVerb($make_relative=false)
- {
- return ActivityUtils::resolveUri($this->verb, $make_relative);
- }
-
- public function isVerb(array $verbs)
- {
- return ActivityUtils::compareVerbs($this->getVerb(), $verbs);
- }
-
- /*
- * Get the original representation URL of this notice.
- *
- * @param boolean $fallback Whether to fall back to generate a local URL or throw InvalidUrlException
- */
- public function getUrl($fallback=false)
- {
- // The risk is we start having empty urls and non-http uris...
- // and we can't really handle any other protocol right now.
- switch (true) {
- case $this->isLocal():
- return common_local_url('shownotice', array('notice' => $this->getID()), null, null, false);
- case common_valid_http_url($this->url): // should we allow non-http/https URLs?
- return $this->url;
- case common_valid_http_url($this->uri): // Sometimes we only have the URI for remote posts.
- return $this->uri;
- case $fallback:
- // let's generate a valid link to our locally available notice on demand
- return common_local_url('shownotice', array('notice' => $this->getID()), null, null, false);
- default:
- throw new InvalidUrlException($this->url);
- }
- }
-
- public function getSelfLink()
- {
- if ($this->isLocal()) {
- return common_local_url('ApiStatusesShow', array('id' => $this->getID(), 'format' => 'atom'));
- }
-
- $selfLink = $this->getPref('ostatus', 'self');
-
- if (!common_valid_http_url($selfLink)) {
- throw new InvalidUrlException($selfLink);
- }
-
- return $selfLink;
- }
-
- public function getObjectType($canonical = false)
- {
- if (is_null($this->object_type) || $this->object_type==='') {
- throw new NoObjectTypeException($this);
- }
- return ActivityUtils::resolveUri($this->object_type, $canonical);
- }
-
- public function isObjectType(array $types)
- {
- try {
- return ActivityUtils::compareTypes($this->getObjectType(), $types);
- } catch (NoObjectTypeException $e) {
- return false;
- }
- }
-
- /**
- * Extract #hashtags from this notice's content and save them to the database.
- */
- public function saveTags()
- {
- /* extract all #hastags */
- $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/u', strtolower($this->content), $match);
- if (!$count) {
- return true;
- }
-
- /* Add them to the database */
- return $this->saveKnownTags($match[1]);
- }
-
- /**
- * Record the given set of hash tags in the db for this notice.
- * Given tag strings will be normalized and checked for dupes.
- */
- public function saveKnownTags($hashtags)
- {
- //turn each into their canonical tag
- //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag
- for ($i = 0; $i < count($hashtags); ++$i) {
- /* elide characters we don't want in the tag */
- $hashtags[$i] = common_canonical_tag($hashtags[$i]);
- }
-
- foreach (array_unique($hashtags) as $hashtag) {
- $this->saveTag($hashtag);
- self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $hashtag);
- }
- return true;
- }
-
- /**
- * Record a single hash tag as associated with this notice.
- * Tag format and uniqueness must be validated by caller.
- */
- public function saveTag($hashtag)
- {
- $tag = new Notice_tag();
- $tag->notice_id = $this->id;
- $tag->tag = $hashtag;
- $tag->created = $this->created;
- $id = $tag->insert();
-
- if (!$id) {
- // TRANS: Server exception. %s are the error details.
- throw new ServerException(sprintf(
- _('Database error inserting hashtag: %s.'),
- $last_error->message
- ));
- return;
- }
-
- // if it's saved, blow its cache
- $tag->blowCache(false);
- }
-
- /**
- * Save a new notice and push it out to subscribers' inboxes.
- * Poster's permissions are checked before sending.
- *
- * @param int $profile_id Profile ID of the poster
- * @param string $content source message text; links may be shortened
- * per current user's preference
- * @param string $source source key ('web', 'api', etc)
- * @param array $options Associative array of optional properties:
- * string 'created' timestamp of notice; defaults to now
- * int 'is_local' source/gateway ID, one of:
- * Notice::LOCAL_PUBLIC - Local, ok to appear in public timeline
- * Notice::REMOTE - Sent from a remote service;
- * hide from public timeline but show in
- * local "and friends" timelines
- * Notice::LOCAL_NONPUBLIC - Local, but hide from public timeline
- * Notice::GATEWAY - From another non-OStatus service;
- * will not appear in public views
- * float 'lat' decimal latitude for geolocation
- * float 'lon' decimal longitude for geolocation
- * int 'location_id' geoname identifier
- * int 'location_ns' geoname namespace to interpret location_id
- * int 'reply_to'; notice ID this is a reply to
- * int 'repeat_of'; notice ID this is a repeat of
- * string 'uri' unique ID for notice; a unique tag uri (can be url or anything too)
- * string 'url' permalink to notice; defaults to local notice URL
- * string 'rendered' rendered HTML version of content
- * array 'replies' list of profile URIs for reply delivery in
- * place of extracting @-replies from content.
- * array 'groups' list of group IDs to deliver to, in place of
- * extracting ! tags from content
- * array 'tags' list of hashtag strings to save with the notice
- * in place of extracting # tags from content
- * array 'urls' list of attached/referred URLs to save with the
- * notice in place of extracting links from content
- * boolean 'distribute' whether to distribute the notice, default true
- * string 'object_type' URL of the associated object type (default ActivityObject::NOTE)
- * string 'verb' URL of the associated verb (default ActivityVerb::POST)
- * int 'scope' Scope bitmask; default to SITE_SCOPE on private sites, 0 otherwise
- *
- * @fixme tag override
- *
- * @return Notice
- * @throws ClientException
- */
- public static function saveNew($profile_id, $content, $source, array $options = null)
- {
- $defaults = array('uri' => null,
- 'url' => null,
- 'self' => null,
- 'conversation' => null, // URI of conversation
- 'reply_to' => null, // This will override convo URI if the parent is known
- 'repeat_of' => null, // This will override convo URI if the repeated notice is known
- 'scope' => null,
- 'distribute' => true,
- 'object_type' => null,
- 'verb' => null);
-
- if (!empty($options) && is_array($options)) {
- $options = array_merge($defaults, $options);
- extract($options);
- } else {
- extract($defaults);
- }
-
- if (!isset($is_local)) {
- $is_local = Notice::LOCAL_PUBLIC;
- }
-
- $profile = Profile::getKV('id', $profile_id);
- if (!$profile instanceof Profile) {
- // TRANS: Client exception thrown when trying to save a notice for an unknown user.
- throw new ClientException(_('Problem saving notice. Unknown user.'));
- }
-
- $user = User::getKV('id', $profile_id);
- if ($user instanceof User) {
- // Use the local user's shortening preferences, if applicable.
- $final = $user->shortenLinks($content);
- } else {
- $final = common_shorten_links($content);
- }
-
- if (Notice::contentTooLong($final)) {
- // TRANS: Client exception thrown if a notice contains too many characters.
- throw new ClientException(_('Problem saving notice. Too long.'));
- }
-
- if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) {
- common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.');
- // TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
- throw new ClientException(_('Too many notices too fast; take a breather '.
- 'and post again in a few minutes.'));
- }
-
- if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
- common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
- // TRANS: Client exception thrown when a user tries to post too many duplicate notices in a given time frame.
- throw new ClientException(_('Too many duplicate messages too quickly;'.
- ' take a breather and post again in a few minutes.'));
- }
-
- if (!$profile->hasRight(Right::NEWNOTICE)) {
- common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $profile->nickname);
-
- // TRANS: Client exception thrown when a user tries to post while being banned.
- throw new ClientException(_('You are banned from posting notices on this site.'), 403);
- }
-
- $notice = new Notice();
- $notice->profile_id = $profile_id;
-
- if ($source && in_array($source, common_config('public', 'autosource'))) {
- $notice->is_local = Notice::LOCAL_NONPUBLIC;
- } else {
- $notice->is_local = $is_local;
- }
-
- if (!empty($created)) {
- $notice->created = $created;
- } else {
- $notice->created = common_sql_now();
- }
-
- if (!$notice->isLocal()) {
- // Only do these checks for non-local notices. Local notices will generate these values later.
- if (empty($uri)) {
- throw new ServerException('No URI for remote notice. Cannot accept that.');
- }
- }
-
- $notice->content = $final;
-
- $notice->source = $source;
- $notice->uri = $uri;
- $notice->url = $url;
-
- // Get the groups here so we can figure out replies and such
- if (!isset($groups)) {
- $groups = User_group::idsFromText($notice->content, $profile);
- }
-
- $reply = null;
-
- // Handle repeat case
-
- if (!empty($options['repeat_of'])) {
-
- // Check for a private one
-
- $repeat = Notice::getByID($options['repeat_of']);
-
- if ($profile->sameAs($repeat->getProfile())) {
- // TRANS: Client error displayed when trying to repeat an own notice.
- throw new ClientException(_('You cannot repeat your own notice.'));
- }
-
- if ($repeat->scope != Notice::SITE_SCOPE &&
- $repeat->scope != Notice::PUBLIC_SCOPE) {
- // TRANS: Client error displayed when trying to repeat a non-public notice.
- throw new ClientException(_('Cannot repeat a private notice.'), 403);
- }
-
- if (!$repeat->inScope($profile)) {
- // The generic checks above should cover this, but let's be sure!
- // TRANS: Client error displayed when trying to repeat a notice you cannot access.
- throw new ClientException(_('Cannot repeat a notice you cannot read.'), 403);
- }
-
- if ($profile->hasRepeated($repeat)) {
- // TRANS: Client error displayed when trying to repeat an already repeated notice.
- throw new ClientException(_('You already repeated that notice.'));
- }
-
- $notice->repeat_of = $repeat->id;
- $notice->conversation = $repeat->conversation;
- } else {
- $reply = null;
-
- // If $reply_to is specified, we check that it exists, and then
- // return it if it does
- if (!empty($reply_to)) {
- $reply = Notice::getKV('id', $reply_to);
- } elseif (in_array($source, array('xmpp', 'mail', 'sms'))) {
- // If the source lacks capability of sending the "reply_to"
- // metadata, let's try to find an inline replyto-reference.
- $reply = self::getInlineReplyTo($profile, $final);
- }
-
- if ($reply instanceof Notice) {
- if (!$reply->inScope($profile)) {
- // TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
- // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
- throw new ClientException(sprintf(
- _('%1$s has no access to notice %2$d.'),
- $profile->nickname,
- $reply->id
- ), 403);
- }
-
- // If it's a repeat, the reply_to should be to the original
- if ($reply->isRepeat()) {
- $notice->reply_to = $reply->repeat_of;
- } else {
- $notice->reply_to = $reply->id;
- }
- // But the conversation ought to be the same :)
- $notice->conversation = $reply->conversation;
-
- // If the original is private to a group, and notice has
- // no group specified, make it to the same group(s)
-
- if (empty($groups) && ($reply->scope & Notice::GROUP_SCOPE)) {
- $groups = array();
- $replyGroups = $reply->getGroups();
- foreach ($replyGroups as $group) {
- if ($profile->isMember($group)) {
- $groups[] = $group->id;
- }
- }
- }
-
- // Scope set below
- }
-
- // If we don't know the reply, we might know the conversation!
- // This will happen if a known remote user replies to an
- // unknown remote user - within a known conversation.
- if (empty($notice->conversation) and !empty($options['conversation'])) {
- $conv = Conversation::getKV('uri', $options['conversation']);
- if ($conv instanceof Conversation) {
- common_debug('Conversation stitched together from (probably) a reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
- } else {
- // Conversation entry with specified URI was not found, so we must create it.
- common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']);
- $convctx = new ActivityContext();
- $convctx->conversation = $options['conversation'];
- if (array_key_exists('conversation_url', $options)) {
- $convctx->conversation_url = $options['conversation_url'];
- }
- // The insert in Conversation::create throws exception on failure
- $conv = Conversation::create($convctx, $notice->created);
- }
- $notice->conversation = $conv->getID();
- unset($conv);
- }
- }
-
- // If it's not part of a conversation, it's the beginning of a new conversation.
- if (empty($notice->conversation)) {
- $conv = Conversation::create();
- $notice->conversation = $conv->getID();
- unset($conv);
- }
-
-
- $notloc = new Notice_location();
- if (!empty($lat) && !empty($lon)) {
- $notloc->lat = $lat;
- $notloc->lon = $lon;
- }
-
- if (!empty($location_ns) && !empty($location_id)) {
- $notloc->location_id = $location_id;
- $notloc->location_ns = $location_ns;
- }
-
- if (!empty($rendered)) {
- $notice->rendered = $rendered;
- } else {
- $notice->rendered = common_render_content(
- $final,
- $notice->getProfile(),
- ($notice->hasParent() ? $notice->getParent() : null)
- );
- }
-
- if (empty($verb)) {
- if ($notice->isRepeat()) {
- $notice->verb = ActivityVerb::SHARE;
- $notice->object_type = ActivityObject::ACTIVITY;
- } else {
- $notice->verb = ActivityVerb::POST;
- }
- } else {
- $notice->verb = $verb;
- }
-
- if (empty($object_type)) {
- $notice->object_type = (empty($notice->reply_to)) ? ActivityObject::NOTE : ActivityObject::COMMENT;
- } else {
- $notice->object_type = $object_type;
- }
-
- if (is_null($scope) && $reply instanceof Notice) {
- $notice->scope = $reply->scope;
- } else {
- $notice->scope = $scope;
- }
-
- $notice->scope = self::figureOutScope($profile, $groups, $notice->scope);
-
- if (Event::handle('StartNoticeSave', array(&$notice))) {
-
- // XXX: some of these functions write to the DB
-
- try {
- $notice->insert(); // throws exception on failure, if successful we have an ->id
-
- if (($notloc->lat && $notloc->lon) || ($notloc->location_id && $notloc->location_ns)) {
- $notloc->notice_id = $notice->getID();
- $notloc->insert(); // store the notice location if it had any information
- }
- } catch (Exception $e) {
- // Let's test if we managed initial insert, which would imply
- // failing on some update-part (check 'insert()'). Delete if
- // something had been stored to the database.
- if (!empty($notice->id)) {
- $notice->delete();
- }
- throw $e;
- }
- }
-
- if ($self && common_valid_http_url($self)) {
- $notice->setPref('ostatus', 'self', $self);
- }
-
- // Only save 'attention' and metadata stuff (URLs, tags...) stuff if
- // the activityverb is a POST (since stuff like repeat, favorite etc.
- // reasonably handle notifications themselves.
- if (ActivityUtils::compareVerbs($notice->verb, array(ActivityVerb::POST))) {
- if (isset($replies)) {
- $notice->saveKnownReplies($replies);
- } else {
- $notice->saveReplies();
- }
-
- if (isset($tags)) {
- $notice->saveKnownTags($tags);
- } else {
- $notice->saveTags();
- }
-
- // Note: groups may save tags, so must be run after tags are saved
- // to avoid errors on duplicates.
- // Note: groups should always be set.
-
- $notice->saveKnownGroups($groups);
-
- if (isset($urls)) {
- $notice->saveKnownUrls($urls);
- } else {
- $notice->saveUrls();
- }
- }
-
- if ($distribute) {
- // Prepare inbox delivery, may be queued to background.
- $notice->distribute();
- }
-
- return $notice;
- }
-
- public static function saveActivity(Activity $act, Profile $actor, array $options = [])
- {
- // First check if we're going to let this Activity through from the specific actor
- if (!$actor->hasRight(Right::NEWNOTICE)) {
- common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $actor->getNickname());
-
- // TRANS: Client exception thrown when a user tries to post while being banned.
- throw new ClientException(_m('You are banned from posting notices on this site.'), 403);
- }
- if (common_config('throttle', 'enabled') && !self::checkEditThrottle($actor->id)) {
- common_log(LOG_WARNING, 'Excessive posting by profile #' . $actor->id . '; throttled.');
- // TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
- throw new ClientException(_m('Too many notices too fast; take a breather '.
- 'and post again in a few minutes.'));
- }
-
- // Get ActivityObject properties
- $actobj = null;
- if (!empty($act->id)) {
- // implied object
- $options['uri'] = $act->id;
- $options['url'] = $act->link;
- if ($act->selfLink) {
- $options['self'] = $act->selfLink;
- }
- } else {
- $actobj = count($act->objects)===1 ? $act->objects[0] : null;
- if (!is_null($actobj) && !empty($actobj->id)) {
- $options['uri'] = $actobj->id;
- if ($actobj->link) {
- $options['url'] = $actobj->link;
- } elseif (preg_match('!^https?://!', $actobj->id)) {
- $options['url'] = $actobj->id;
- }
- }
- if ($actobj->selfLink) {
- $options['self'] = $actobj->selfLink;
- }
- }
-
- $defaults = array(
- 'groups' => array(),
- 'is_local' => $actor->isLocal() ? self::LOCAL_PUBLIC : self::REMOTE,
- 'mentions' => array(),
- 'reply_to' => null,
- 'repeat_of' => null,
- 'scope' => null,
- 'self' => null,
- 'source' => 'unknown',
- 'tags' => array(),
- 'uri' => null,
- 'url' => null,
- 'urls' => array(),
- 'distribute' => true);
-
- // options will have default values when nothing has been supplied
- $options = array_merge($defaults, $options);
- foreach (array_keys($defaults) as $key) {
- // Only convert the keynames we specify ourselves from 'defaults' array into variables
- $$key = $options[$key];
- }
- extract($options, EXTR_SKIP);
-
- // dupe check
- $stored = new Notice();
- if (!empty($uri) && !ActivityUtils::compareVerbs($act->verb, array(ActivityVerb::DELETE))) {
- $stored->uri = $uri;
- if ($stored->find()) {
- common_debug('cannot create duplicate Notice URI: '.$stored->uri);
- // I _assume_ saving a Notice with a colliding URI means we're really trying to
- // save the same notice again...
- throw new AlreadyFulfilledException('Notice URI already exists');
- }
- }
-
- // NOTE: Sandboxed users previously got all the notices _during_
- // sandbox period set to to is_local=Notice::LOCAL_NONPUBLIC here.
- // Since then we have started just filtering _when_ it gets shown
- // instead of creating a mixed jumble of differently scoped notices.
-
- if ($source && in_array($source, common_config('public', 'autosource'))) {
- $stored->is_local = Notice::LOCAL_NONPUBLIC;
- } else {
- $stored->is_local = intval($is_local);
- }
-
- if (!$stored->isLocal()) {
- // Only do these checks for non-local notices. Local notices will generate these values later.
- if (!common_valid_http_url($url)) {
- common_debug('Bad notice URL: ['.$url.'], URI: ['.$uri.']. Cannot link back to original! This is normal for shared notices etc.');
- }
- if (empty($uri)) {
- throw new ServerException('No URI for remote notice. Cannot accept that.');
- }
- }
-
- $stored->profile_id = $actor->getID();
- $stored->source = $source;
- $stored->uri = $uri;
- $stored->url = $url;
- $stored->verb = $act->verb;
-
- // we use mb_strlen because it _might_ be that the content is just the string "0"...
- $content = mb_strlen($act->content) ? $act->content : $act->summary;
- if (mb_strlen($content)===0 && !is_null($actobj)) {
- $content = mb_strlen($actobj->content) ? $actobj->content : $actobj->summary;
- }
- // Strip out any bad HTML from $content. URI.Base is used to sort out relative URLs.
- $stored->rendered = common_purify($content, ['URI.Base' => $stored->url ?: null]);
- $stored->content = common_strip_html($stored->getRendered(), true, true);
- if (trim($stored->content) === '') {
- // TRANS: Error message when the plain text content of a notice has zero length.
- throw new ClientException(_('Empty notice content, will not save this.'));
- }
- unset($content); // garbage collect
-
- // Maybe a missing act-time should be fatal if the actor is not local?
- if (!empty($act->time)) {
- $stored->created = common_sql_date($act->time);
- } else {
- $stored->created = common_sql_now();
- }
-
- $reply = null; // this will store the in-reply-to Notice if found
- $replyUris = []; // this keeps a list of possible URIs to look up
- if ($act->context instanceof ActivityContext && !empty($act->context->replyToID)) {
- $replyUris[] = $act->context->replyToID;
- }
- if ($act->target instanceof ActivityObject && !empty($act->target->id)) {
- $replyUris[] = $act->target->id;
- }
- foreach (array_unique($replyUris) as $replyUri) {
- $reply = self::getKV('uri', $replyUri);
- // Only do remote fetching if we're not a private site
- if (!common_config('site', 'private') && !$reply instanceof Notice) {
- // the URI is the object we're looking for, $actor is a
- // Profile that surely knows of it and &$reply where it
- // will be stored when fetched
- Event::handle('FetchRemoteNotice', array($replyUri, $actor, &$reply));
- }
- // we got what we're in-reply-to now, so let's move on
- if ($reply instanceof Notice) {
- break;
- }
- // otherwise reset whatever we might've gotten from the event
- $reply = null;
- }
- unset($replyUris); // garbage collect
-
- if ($reply instanceof Notice) {
- if (!$reply->inScope($actor)) {
- // TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
- // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
- throw new ClientException(sprintf(_m('%1$s has no right to reply to notice %2$d.'), $actor->getNickname(), $reply->id), 403);
- }
-
- $stored->reply_to = $reply->id;
- $stored->conversation = $reply->conversation;
-
- // If the original is private to a group, and notice has no group specified,
- // make it to the same group(s)
- if (empty($groups) && ($reply->scope & Notice::GROUP_SCOPE)) {
- $replyGroups = $reply->getGroups();
- foreach ($replyGroups as $group) {
- if ($actor->isMember($group)) {
- $groups[] = $group->id;
- }
- }
- }
-
- if (is_null($scope)) {
- $scope = $reply->scope;
- }
- } else {
- // If we don't know the reply, we might know the conversation!
- // This will happen if a known remote user replies to an
- // unknown remote user - within a known conversation.
- if (empty($stored->conversation) and !empty($act->context->conversation)) {
- $conv = Conversation::getKV('uri', $act->context->conversation);
- if ($conv instanceof Conversation) {
- common_debug('Conversation stitched together from (probably) a reply activity to unknown remote user. Activity creation time ('.$stored->created.') should maybe be compared to conversation creation time ('.$conv->created.').');
- } else {
- // Conversation entry with specified URI was not found, so we must create it.
- common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: '.$act->context->conversation);
- // The insert in Conversation::create throws exception on failure
- $conv = Conversation::create($act->context, $stored->created);
- }
- $stored->conversation = $conv->getID();
- unset($conv);
- }
- }
- unset($reply); // garbage collect
-
- // If it's not part of a conversation, it's the beginning of a new conversation.
- if (empty($stored->conversation)) {
- $conv = Conversation::create();
- $stored->conversation = $conv->getID();
- unset($conv);
- }
-
- $notloc = null;
- if ($act->context instanceof ActivityContext) {
- if ($act->context->location instanceof Location) {
- $notloc = Notice_location::fromLocation($act->context->location);
- }
- } else {
- $act->context = new ActivityContext();
- }
-
- if (array_key_exists(ActivityContext::ATTN_PUBLIC, $act->context->attention)) {
- $stored->scope = Notice::PUBLIC_SCOPE;
- // TODO: maybe we should actually keep this? if the saveAttentions thing wants to use it...
- unset($act->context->attention[ActivityContext::ATTN_PUBLIC]);
- } else {
- $stored->scope = self::figureOutScope($actor, $groups, $scope);
- }
-
- foreach ($act->categories as $cat) {
- if ($cat->term) {
- $term = common_canonical_tag($cat->term);
- if (!empty($term)) {
- $tags[] = $term;
- }
- }
- }
-
- foreach ($act->enclosures as $href) {
- // @todo FIXME: Save these locally or....?
- $urls[] = $href;
- }
-
- if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) {
- if (empty($act->objects[0]->type)) {
- // Default type for the post verb is 'note', but we know it's
- // a 'comment' if it is in reply to something.
- $stored->object_type = empty($stored->reply_to) ? ActivityObject::NOTE : ActivityObject::COMMENT;
- } else {
- //TODO: Is it safe to always return a relative URI? The
- // JSON version of ActivityStreams always use it, so we
- // should definitely be able to handle it...
- $stored->object_type = ActivityUtils::resolveUri($act->objects[0]->type, true);
- }
- }
-
- if (Event::handle('StartNoticeSave', array(&$stored))) {
- // XXX: some of these functions write to the DB
-
- try {
- $result = $stored->insert(); // throws exception on error
-
- if ($notloc instanceof Notice_location) {
- $notloc->notice_id = $stored->getID();
- $notloc->insert();
- }
-
- $orig = clone($stored); // for updating later in this try clause
-
- $object = null;
- Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
- if (empty($object)) {
- throw new NoticeSaveException('Unsuccessful call to StoreActivityObject '._ve($stored->getUri()) . ': '._ve($act->asString()));
- }
- unset($object);
-
- // If something changed in the Notice during StoreActivityObject
- $stored->update($orig);
- } catch (Exception $e) {
- if (empty($stored->id)) {
- common_debug('Failed to save stored object entry in database ('.$e->getMessage().')');
- } else {
- common_debug('Failed to store activity object in database ('.$e->getMessage().'), deleting notice id '.$stored->id);
- $stored->delete();
- }
- throw $e;
- }
- }
- unset($notloc); // garbage collect
-
- if (!$stored instanceof Notice) {
- throw new ServerException('StartNoticeSave did not give back a Notice.');
- } elseif (empty($stored->id)) {
- throw new ServerException('Supposedly saved Notice has no ID.');
- }
-
- if ($self && common_valid_http_url($self)) {
- $stored->setPref('ostatus', 'self', $self);
- }
-
- if ($self && common_valid_http_url($self)) {
- $stored->setPref('ostatus', 'self', $self);
- }
-
- // Only save 'attention' and metadata stuff (URLs, tags...) stuff if
- // the activityverb is a POST (since stuff like repeat, favorite etc.
- // reasonably handle notifications themselves.
- if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) {
- if (!empty($tags)) {
- $stored->saveKnownTags($tags);
- } else {
- $stored->saveTags();
- }
-
- // Note: groups may save tags, so must be run after tags are saved
- // to avoid errors on duplicates.
- $stored->saveAttentions($act->context->attention);
-
- if (!empty($urls)) {
- $stored->saveKnownUrls($urls);
- } else {
- $stored->saveUrls();
- }
- }
-
- if ($distribute) {
- // Prepare inbox delivery, may be queued to background.
- $stored->distribute();
- }
-
- return $stored;
- }
-
- public static function figureOutScope(Profile $actor, array $groups, $scope = null)
- {
- $scope = is_null($scope) ? self::defaultScope() : intval($scope);
-
- // For private streams
- try {
- $user = $actor->getUser();
- // FIXME: We can't do bit comparison with == (Legacy StatusNet thing. Let's keep it for now.)
- if ($user->private_stream && ($scope === Notice::PUBLIC_SCOPE || $scope === Notice::SITE_SCOPE)) {
- $scope |= Notice::FOLLOWER_SCOPE;
- }
- } catch (NoSuchUserException $e) {
- // TODO: Not a local user, so we don't know about scope preferences... yet!
- }
-
- // Force the scope for private groups
- foreach ($groups as $group_id) {
- try {
- $group = User_group::getByID($group_id);
- if ($group->force_scope) {
- $scope |= Notice::GROUP_SCOPE;
- break;
- }
- } catch (Exception $e) {
- common_log(LOG_ERR, 'Notice figureOutScope threw exception: '.$e->getMessage());
- }
- }
-
- return $scope;
- }
-
- public function blowOnInsert($conversation = false)
- {
- $this->blowStream('profile:notice_ids:%d', $this->profile_id);
-
- if ($this->isPublic()) {
- $this->blowStream('public');
- $this->blowStream('networkpublic');
- }
-
- if ($this->conversation) {
- self::blow('notice:list-ids:conversation:%s', $this->conversation);
- self::blow('conversation:notice_count:%d', $this->conversation);
- }
-
- if ($this->isRepeat()) {
- // XXX: we should probably only use one of these
- $this->blowStream('notice:repeats:%d', $this->repeat_of);
- self::blow('notice:list-ids:repeat_of:%d', $this->repeat_of);
- }
-
- $original = Notice::getKV('id', $this->repeat_of);
-
- if ($original instanceof Notice) {
- $originalUser = User::getKV('id', $original->profile_id);
- if ($originalUser instanceof User) {
- $this->blowStream('user:repeats_of_me:%d', $originalUser->id);
- }
- }
-
- $profile = Profile::getKV($this->profile_id);
-
- if ($profile instanceof Profile) {
- $profile->blowNoticeCount();
- }
-
- $ptags = $this->getProfileTags();
- foreach ($ptags as $ptag) {
- $ptag->blowNoticeStreamCache();
- }
- }
-
- /**
- * Clear cache entries related to this notice at delete time.
- * Necessary to avoid breaking paging on public, profile timelines.
- */
- public function blowOnDelete()
- {
- $this->blowOnInsert();
-
- self::blow('profile:notice_ids:%d;last', $this->profile_id);
-
- if ($this->isPublic()) {
- self::blow('public;last');
- self::blow('networkpublic;last');
- }
-
- self::blow('fave:by_notice', $this->id);
-
- if ($this->conversation) {
- // In case we're the first, will need to calc a new root.
- self::blow('notice:conversation_root:%d', $this->conversation);
- }
-
- $ptags = $this->getProfileTags();
- foreach ($ptags as $ptag) {
- $ptag->blowNoticeStreamCache(true);
- }
- }
-
- public function blowStream()
- {
- $c = self::memcache();
-
- if (empty($c)) {
- return false;
- }
-
- $args = func_get_args();
- $format = array_shift($args);
- $keyPart = vsprintf($format, $args);
- $cacheKey = Cache::key($keyPart);
- $c->delete($cacheKey);
-
- // delete the "last" stream, too, if this notice is
- // older than the top of that stream
-
- $lastKey = $cacheKey.';last';
-
- $lastStr = $c->get($lastKey);
-
- if ($lastStr !== false) {
- $window = explode(',', $lastStr);
- $lastID = $window[0];
- $lastNotice = Notice::getKV('id', $lastID);
- if (!$lastNotice instanceof Notice // just weird
- || strtotime($lastNotice->created) >= strtotime($this->created)) {
- $c->delete($lastKey);
- }
- }
- }
-
- /** save all urls in the notice to the db
- *
- * follow redirects and save all available file information
- * (mimetype, date, size, oembed, etc.)
- *
- * @return void
- */
- public function saveUrls()
- {
- if (common_config('attachments', 'process_links')) {
- common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this);
- }
- }
-
- /**
- * Save the given URLs as related links/attachments to the db
- *
- * follow redirects and save all available file information
- * (mimetype, date, size, oembed, etc.)
- *
- * @return void
- */
- public function saveKnownUrls($urls)
- {
- if (common_config('attachments', 'process_links')) {
- // @fixme validation?
- foreach (array_unique($urls) as $url) {
- $this->saveUrl($url, $this);
- }
- }
- }
-
- /**
- * @private callback
- */
- public function saveUrl($url, Notice $notice)
- {
- try {
- File::processNew($url, $notice);
- } catch (ServerException $e) {
- // Could not save URL. Log it?
- }
- }
-
- public static function checkDupes($profile_id, $content)
- {
- $profile = Profile::getKV($profile_id);
- if (!$profile instanceof Profile) {
- return false;
- }
- $notice = $profile->getNotices(0, CachingNoticeStream::CACHE_WINDOW);
- if (!empty($notice)) {
- $last = 0;
- while ($notice->fetch()) {
- if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
- return true;
- } elseif ($notice->content === $content) {
- return false;
- }
- }
- }
- // If we get here, oldest item in cache window is not
- // old enough for dupe limit; do direct check against DB
- $notice = new Notice();
- $notice->profile_id = $profile_id;
- $notice->content = $content;
- $threshold = common_sql_date(time() - common_config('site', 'dupelimit'));
- $notice->whereAdd(sprintf("created > '%s'", $notice->escape($threshold)));
-
- $cnt = $notice->count();
- return ($cnt == 0);
- }
-
- public static function checkEditThrottle($profile_id)
- {
- $profile = Profile::getKV($profile_id);
- if (!$profile instanceof Profile) {
- return false;
- }
- // Get the Nth notice
- $notice = $profile->getNotices(common_config('throttle', 'count') - 1, 1);
- if ($notice && $notice->fetch()) {
- // If the Nth notice was posted less than timespan seconds ago
- if (time() - strtotime($notice->created) <= common_config('throttle', 'timespan')) {
- // Then we throttle
- return false;
- }
- }
- // Either not N notices in the stream, OR the Nth was not posted within timespan seconds
- return true;
- }
-
- protected $_attachments = [];
-
- public function attachments()
- {
- if (isset($this->_attachments[$this->id])) {
- return $this->_attachments[$this->id];
- }
-
- $f2ps = File_to_post::listGet('post_id', array($this->id));
- $ids = [];
- foreach ($f2ps[$this->id] as $f2p) {
- $ids[] = $f2p->file_id;
- }
-
- return $this->_setAttachments(File::multiGet('id', $ids)->fetchAll());
- }
-
- public function _setAttachments(array $attachments)
- {
- return $this->_attachments[$this->id] = $attachments;
- }
-
- public static function publicStream($offset = 0, $limit = 20, $since_id = null, $max_id = null)
- {
- $stream = new PublicNoticeStream();
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- public static function conversationStream($id, $offset = 0, $limit = 20, $since_id = null, $max_id = null, Profile $scoped = null)
- {
- $stream = new ConversationNoticeStream($id, $scoped);
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- /**
- * Is this notice part of an active conversation?
- *
- * @return boolean true if other messages exist in the same
- * conversation, false if this is the only one
- */
- public function hasConversation()
- {
- if (empty($this->conversation)) {
- // this notice is not part of a conversation apparently
- // FIXME: all notices should have a conversation value, right?
- return false;
- }
-
- //FIXME: Get the Profile::current() stuff some other way
- // to avoid confusion between queue processing and session.
- $notice = self::conversationStream($this->conversation, 1, 1, null, null, Profile::current());
-
- // if our "offset 1, limit 1" query got a result, return true else false
- return $notice->N > 0;
- }
-
- /**
- * Grab the earliest notice from this conversation.
- *
- * @return Notice or null
- */
- public function conversationRoot($profile = -1)
- {
- // XXX: can this happen?
-
- if (empty($this->conversation)) {
- return null;
- }
-
- // Get the current profile if not specified
-
- if (is_int($profile) && $profile == -1) {
- $profile = Profile::current();
- }
-
- // If this notice is out of scope, no root for you!
-
- if (!$this->inScope($profile)) {
- return null;
- }
-
- // If this isn't a reply to anything, then it's its own
- // root if it's the earliest notice in the conversation:
-
- if (empty($this->reply_to)) {
- $root = new Notice;
- $root->conversation = $this->conversation;
- $root->orderBy('created, id');
- $root->limit(0, 1);
- $root->find(true); // true means "fetch first result"
- $root->free();
- return $root;
- }
-
- if (is_null($profile)) {
- $keypart = sprintf('notice:conversation_root:%d:null', $this->id);
- } else {
- $keypart = sprintf(
- 'notice:conversation_root:%d:%d',
- $this->id,
- $profile->id
- );
- }
-
- $root = self::cacheGet($keypart);
-
- if ($root !== false && $root->inScope($profile)) {
- return $root;
- }
-
- $last = $this;
- while (true) {
- try {
- $parent = $last->getParent();
- if ($parent->inScope($profile)) {
- $last = $parent;
- continue;
- }
- } catch (NoParentNoticeException $e) {
- // Latest notice has no parent
- } catch (NoResultException $e) {
- // Notice was not found, so we can't go further up in the tree.
- // FIXME: Maybe we should do this in a more stable way where deleted
- // notices won't break conversation chains?
- }
- // No parent, or parent out of scope
- $root = $last;
- break;
- }
-
- self::cacheSet($keypart, $root);
-
- return $root;
- }
-
- /**
- * Pull up a full list of local recipients who will be getting
- * this notice in their inbox. Results will be cached, so don't
- * change the input data wily-nilly!
- *
- * @param array $groups optional list of Group objects;
- * if left empty, will be loaded from group_inbox records
- * @param array $recipient optional list of reply profile ids
- * if left empty, will be loaded from reply records
- * @return array associating recipient user IDs with an inbox source constant
- */
- public function whoGets(array $groups = null, array $recipients = null)
- {
- $c = self::memcache();
-
- if (!empty($c)) {
- $ni = $c->get(Cache::key('notice:who_gets:'.$this->id));
- if ($ni !== false) {
- return $ni;
- }
- }
-
- if (is_null($recipients)) {
- $recipients = $this->getReplies();
- }
-
- $ni = array();
-
- // Give plugins a chance to add folks in at start...
- if (Event::handle('StartNoticeWhoGets', array($this, &$ni))) {
- $users = $this->getSubscribedUsers();
- foreach ($users as $id) {
- $ni[$id] = NOTICE_INBOX_SOURCE_SUB;
- }
-
- if (is_null($groups)) {
- $groups = $this->getGroups();
- }
- foreach ($groups as $group) {
- $users = $group->getUserMembers();
- foreach ($users as $id) {
- if (!array_key_exists($id, $ni)) {
- $ni[$id] = NOTICE_INBOX_SOURCE_GROUP;
- }
- }
- }
-
- $ptAtts = $this->getAttentionsFromProfileTags();
- foreach ($ptAtts as $key=>$val) {
- if (!array_key_exists($key, $ni)) {
- $ni[$key] = $val;
- }
- }
-
- foreach ($recipients as $recipient) {
- if (!array_key_exists($recipient, $ni)) {
- $ni[$recipient] = NOTICE_INBOX_SOURCE_REPLY;
- }
- }
-
- // Exclude any deleted, non-local, or blocking recipients.
- $profile = $this->getProfile();
- $originalProfile = null;
- if ($this->isRepeat()) {
- // Check blocks against the original notice's poster as well.
- $original = Notice::getKV('id', $this->repeat_of);
- if ($original instanceof Notice) {
- $originalProfile = $original->getProfile();
- }
- }
-
- foreach ($ni as $id => $source) {
- try {
- $user = User::getKV('id', $id);
- if (!$user instanceof User ||
- $user->hasBlocked($profile) ||
- ($originalProfile && $user->hasBlocked($originalProfile))) {
- unset($ni[$id]);
- }
- } catch (UserNoProfileException $e) {
- // User doesn't have a profile; invalid; skip them.
- unset($ni[$id]);
- }
- }
-
- // Give plugins a chance to filter out...
- Event::handle('EndNoticeWhoGets', array($this, &$ni));
- }
-
- if (!empty($c)) {
- // XXX: pack this data better
- $c->set(Cache::key('notice:who_gets:'.$this->id), $ni);
- }
-
- return $ni;
- }
-
- public function getSubscribedUsers()
- {
- $user = new User();
-
- $user->query(sprintf(
- 'SELECT id FROM %1$s INNER JOIN subscription ' .
- 'ON %1$s.id = subscription.subscriber ' .
- 'WHERE subscription.subscribed = %2$d ',
- $user->escapedTableName(),
- $this->profile_id
- ));
-
- $ids = [];
-
- while ($user->fetch()) {
- $ids[] = $user->id;
- }
-
- $user->free();
-
- return $ids;
- }
-
- public function getProfileTags()
- {
- $ptags = array();
- try {
- $profile = $this->getProfile();
- $list = $profile->getOtherTags($profile);
-
- while ($list->fetch()) {
- $ptags[] = clone($list);
- }
- } catch (Exception $e) {
- common_log(LOG_ERR, "Error during Notice->getProfileTags() for id=={$this->getID()}: {$e->getMessage()}");
- }
-
- return $ptags;
- }
-
- public function getAttentionsFromProfileTags()
- {
- $ni = array();
- $ptags = $this->getProfileTags();
- foreach ($ptags as $ptag) {
- $users = $ptag->getUserSubscribers();
- foreach ($users as $id) {
- $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
- }
- }
- return $ni;
- }
-
- /**
- * Record this notice to the given group inboxes for delivery.
- * Overrides the regular parsing of !group markup.
- *
- * @param string $group_ids
- * @fixme might prefer URIs as identifiers, as for replies?
- * best with generalizations on user_group to support
- * remote groups better.
- */
- public function saveKnownGroups(array $group_ids)
- {
- $groups = array();
- foreach (array_unique($group_ids) as $id) {
- $group = User_group::getKV('id', $id);
- if ($group instanceof User_group) {
- common_log(LOG_DEBUG, "Local delivery to group id $id, $group->nickname");
- $result = $this->addToGroupInbox($group);
- if (!$result) {
- common_log_db_error($gi, 'INSERT', __FILE__);
- }
-
- if (common_config('group', 'addtag')) {
- // we automatically add a tag for every group name, too
- common_debug('Adding hashtag matching group nickname: '._ve($group->getNickname()));
- $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->getNickname()),
- 'notice_id' => $this->getID()));
-
- if (is_null($tag)) {
- $this->saveTag($group->getNickname());
- }
- }
-
- $groups[] = clone($group);
- } else {
- common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist");
- }
- }
-
- return $groups;
- }
-
- public function addToGroupInbox(User_group $group)
- {
- $gi = Group_inbox::pkeyGet(array('group_id' => $group->id,
- 'notice_id' => $this->id));
-
- if (!$gi instanceof Group_inbox) {
- $gi = new Group_inbox();
-
- $gi->group_id = $group->id;
- $gi->notice_id = $this->id;
- $gi->created = $this->created;
-
- $result = $gi->insert();
-
- if (!$result) {
- common_log_db_error($gi, 'INSERT', __FILE__);
- // TRANS: Server exception thrown when an update for a group inbox fails.
- throw new ServerException(_('Problem saving group inbox.'));
- }
-
- self::blow('user_group:notice_ids:%d', $gi->group_id);
- }
-
- return true;
- }
-
- public function saveAttentions(array $uris)
- {
- foreach ($uris as $uri=>$type) {
- try {
- $target = Profile::fromUri($uri);
- } catch (UnknownUriException $e) {
- common_log(LOG_WARNING, "Unable to determine profile for URI '$uri'");
- continue;
- }
-
- try {
- $this->saveAttention($target);
- } catch (AlreadyFulfilledException $e) {
- common_debug('Attention already exists: ' . var_export($e->getMessage(), true));
- } catch (Exception $e) {
- common_log(LOG_ERR, "Could not save notice id=={$this->getID()} attention for profile id=={$target->getID()}: {$e->getMessage()}");
- }
- }
- }
-
- /**
- * Saves an attention for a profile (user or group) which means
- * it shows up in their home feed and such.
- */
- public function saveAttention(Profile $target, $reason = null)
- {
- if ($target->isGroup()) {
- // FIXME: Make sure we check (for both local and remote) users are in the groups they send to!
-
- // legacy notification method, will still be in use for quite a while I think
- $this->addToGroupInbox($target->getGroup());
- } else {
- if ($target->hasBlocked($this->getProfile())) {
- common_log(LOG_INFO, "Not saving reply to profile {$target->id} ($uri) from sender {$sender->id} because of a block.");
- return false;
- }
- }
-
- if ($target->isLocal()) {
- // legacy notification method, will still be in use for quite a while I think
- $this->saveReply($target->getID());
- }
-
- $att = Attention::saveNew($this, $target, $reason);
- return true;
- }
-
- /**
- * Save reply records indicating that this notice needs to be
- * delivered to the local users with the given URIs.
- *
- * Since this is expected to be used when saving foreign-sourced
- * messages, we won't deliver to any remote targets as that's the
- * source service's responsibility.
- *
- * Mail notifications etc will be handled later.
- *
- * @param array $uris Array of unique identifier URIs for recipients
- */
- public function saveKnownReplies(array $uris)
- {
- if (empty($uris)) {
- return;
- }
-
- $sender = $this->getProfile();
-
- foreach (array_unique($uris) as $uri) {
- try {
- $profile = Profile::fromUri($uri);
- } catch (UnknownUriException $e) {
- common_log(LOG_WARNING, "Unable to determine profile for URI '$uri'");
- continue;
- }
-
- if ($profile->hasBlocked($sender)) {
- common_log(LOG_INFO, "Not saving reply to profile {$profile->id} ($uri) from sender {$sender->id} because of a block.");
- continue;
- }
-
- $this->saveReply($profile->getID());
- self::blow('reply:stream:%d', $profile->getID());
- }
- }
-
- /**
- * Pull @-replies from this message's content in StatusNet markup format
- * and save reply records indicating that this message needs to be
- * delivered to those users.
- *
- * Mail notifications to local profiles will be sent later.
- *
- * @return array of integer profile IDs
- */
-
- public function saveReplies()
- {
- $sender = $this->getProfile();
-
- $replied = array();
-
- // If it's a reply, save for the replied-to author
- try {
- $parent = $this->getParent();
- $parentauthor = $parent->getProfile();
- $this->saveReply($parentauthor->getID());
- $replied[$parentauthor->getID()] = 1;
- self::blow('reply:stream:%d', $parentauthor->getID());
- } catch (NoParentNoticeException $e) {
- // Not a reply, since it has no parent!
- $parent = null;
- } catch (NoResultException $e) {
- // Parent notice was probably deleted
- $parent = null;
- }
-
- // @todo ideally this parser information would only
- // be calculated once.
-
- $mentions = common_find_mentions($this->content, $sender, $parent);
-
- foreach ($mentions as $mention) {
- foreach ($mention['mentioned'] as $mentioned) {
-
- // skip if they're already covered
- if (array_key_exists($mentioned->id, $replied)) {
- continue;
- }
-
- // Don't save replies from blocked profile to local user
- if ($mentioned->hasBlocked($sender)) {
- continue;
- }
-
- $this->saveReply($mentioned->id);
- $replied[$mentioned->id] = 1;
- self::blow('reply:stream:%d', $mentioned->id);
- }
- }
-
- $recipientIds = array_keys($replied);
-
- return $recipientIds;
- }
-
- public function saveReply($profileId)
- {
- $reply = new Reply();
-
- $reply->notice_id = $this->id;
- $reply->profile_id = $profileId;
- $reply->modified = $this->created;
-
- $reply->insert();
-
- return $reply;
- }
-
- protected $_attentionids = array();
-
- /**
- * Pull the complete list of known activity context attentions for this notice.
- *
- * @return array of integer profile ids (also group profiles)
- */
- public function getAttentionProfileIDs()
- {
- if (!isset($this->_attentionids[$this->getID()])) {
- $atts = Attention::multiGet('notice_id', array($this->getID()));
- // (array)null means empty array
- $this->_attentionids[$this->getID()] = (array)$atts->fetchAll('profile_id');
- }
- return $this->_attentionids[$this->getID()];
- }
-
- protected $_replies = array();
-
- /**
- * Pull the complete list of @-mentioned profile IDs for this notice.
- *
- * @return array of integer profile ids
- */
- public function getReplies()
- {
- if (!isset($this->_replies[$this->getID()])) {
- $mentions = Reply::multiGet('notice_id', array($this->getID()));
- $this->_replies[$this->getID()] = $mentions->fetchAll('profile_id');
- }
- return $this->_replies[$this->getID()];
- }
-
- public function _setReplies($replies)
- {
- $this->_replies[$this->getID()] = $replies;
- }
-
- /**
- * Pull the complete list of @-reply targets for this notice.
- *
- * @return array of Profiles
- */
- public function getAttentionProfiles()
- {
- $ids = array_unique(array_merge($this->getReplies(), $this->getGroupProfileIDs(), $this->getAttentionProfileIDs()));
-
- $profiles = Profile::multiGet('id', (array)$ids);
-
- return $profiles->fetchAll();
- }
-
- /**
- * Send e-mail notifications to local @-reply targets.
- *
- * Replies must already have been saved; this is expected to be run
- * from the distrib queue handler.
- */
- public function sendReplyNotifications()
- {
- // Don't send reply notifications for repeats
- if ($this->isRepeat()) {
- return array();
- }
-
- $recipientIds = $this->getReplies();
- if (Event::handle('StartNotifyMentioned', array($this, &$recipientIds))) {
- require_once INSTALLDIR . '/lib/util/mail.php';
-
- foreach ($recipientIds as $recipientId) {
- try {
- $user = User::getByID($recipientId);
- mail_notify_attn($user->getProfile(), $this);
- } catch (NoResultException $e) {
- // No such user
- }
- }
- Event::handle('EndNotifyMentioned', array($this, $recipientIds));
- }
- }
-
- /**
- * Pull list of Profile IDs of groups this notice addresses.
- *
- * @return array of Group _profile_ IDs
- */
-
- public function getGroupProfileIDs()
- {
- $ids = array();
-
- foreach ($this->getGroups() as $group) {
- $ids[] = $group->profile_id;
- }
-
- return $ids;
- }
-
- /**
- * Pull list of groups this notice needs to be delivered to,
- * as previously recorded by saveKnownGroups().
- *
- * @return array of Group objects
- */
-
- protected $_groups = array();
-
- public function getGroups()
- {
- // Don't save groups for repeats
-
- if (!empty($this->repeat_of)) {
- return array();
- }
-
- if (isset($this->_groups[$this->id])) {
- return $this->_groups[$this->id];
- }
-
- $gis = Group_inbox::listGet('notice_id', array($this->id));
-
- $ids = [];
-
- foreach ($gis[$this->id] as $gi) {
- $ids[] = $gi->group_id;
- }
-
- $groups = User_group::multiGet('id', $ids);
- $this->_groups[$this->id] = $groups->fetchAll();
- return $this->_groups[$this->id];
- }
-
- public function _setGroups($groups)
- {
- $this->_groups[$this->id] = $groups;
- }
-
- /**
- * Convert a notice into an activity for export.
- *
- * @param Profile $scoped The currently logged in/scoped profile
- *
- * @return Activity activity object representing this Notice.
- * @throws ClientException
- * @throws ServerException
- */
-
- public function asActivity(Profile $scoped = null)
- {
- $act = self::cacheGet(Cache::codeKey('notice:as-activity:'.$this->id));
-
- if ($act instanceof Activity) {
- return $act;
- }
- $act = new Activity();
-
- if (Event::handle('StartNoticeAsActivity', array($this, $act, $scoped))) {
- $act->id = $this->uri;
- $act->time = strtotime($this->created);
- try {
- $act->link = $this->getUrl();
- } catch (InvalidUrlException $e) {
- // The notice is probably a share or similar, which don't
- // have a representational URL of their own.
- }
- $act->content = common_xml_safe_str($this->getRendered());
-
- $profile = $this->getProfile();
-
- $act->actor = $profile->asActivityObject();
- $act->actor->extra[] = $profile->profileInfo($scoped);
-
- $act->verb = $this->verb;
-
- if (!$this->repeat_of) {
- $act->objects[] = $this->asActivityObject();
- }
-
- // XXX: should this be handled by default processing for object entry?
-
- // Categories
-
- $tags = $this->getTags();
-
- foreach ($tags as $tag) {
- $cat = new AtomCategory();
- $cat->term = $tag;
-
- $act->categories[] = $cat;
- }
-
- // Enclosures
- // XXX: use Atom Media and/or File activity objects instead
-
- $attachments = $this->attachments();
-
- foreach ($attachments as $attachment) {
- // Include local attachments in Activity
- if (!empty($attachment->filename)) {
- $act->enclosures[] = $attachment->getEnclosure();
- }
- }
-
- $ctx = new ActivityContext();
-
- try {
- $reply = $this->getParent();
- $ctx->replyToID = $reply->getUri();
- $ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy
- } catch (NoParentNoticeException $e) {
- // This is not a reply to something
- } catch (NoResultException $e) {
- // Parent notice was probably deleted
- }
-
- try {
- $ctx->location = Notice_location::locFromStored($this);
- } catch (ServerException $e) {
- $ctx->location = null;
- }
-
- $conv = null;
-
- if (!empty($this->conversation)) {
- $conv = Conversation::getKV('id', $this->conversation);
- if ($conv instanceof Conversation) {
- $ctx->conversation = $conv->uri;
- $ctx->conversation_url = $conv->url;
- }
- }
-
- // This covers the legacy getReplies and getGroups too which get their data
- // from entries stored via Notice::saveNew (which we want to move away from)...
- foreach ($this->getAttentionProfiles() as $target) {
- // User and group profiles which get the attention of this notice
- $ctx->attention[$target->getUri()] = $target->getObjectType();
- }
-
- switch ($this->scope) {
- case Notice::PUBLIC_SCOPE:
- $ctx->attention[ActivityContext::ATTN_PUBLIC] = ActivityObject::COLLECTION;
- break;
- case Notice::FOLLOWER_SCOPE:
- $surl = common_local_url("subscribers", array('nickname' => $profile->nickname));
- $ctx->attention[$surl] = ActivityObject::COLLECTION;
- break;
- }
-
- $act->context = $ctx;
-
- $source = $this->getSource();
-
- if ($source instanceof Notice_source) {
- $act->generator = ActivityObject::fromNoticeSource($source);
- }
-
- // Source
-
- $atom_feed = $profile->getAtomFeed();
-
- if (!empty($atom_feed)) {
- $act->source = new ActivitySource();
-
- // XXX: we should store the actual feed ID
-
- $act->source->id = $atom_feed;
-
- // XXX: we should store the actual feed title
-
- $act->source->title = $profile->getBestName();
-
- $act->source->links['alternate'] = $profile->profileurl;
- $act->source->links['self'] = $atom_feed;
-
- $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE);
-
- $notice = $profile->getCurrentNotice();
-
- if ($notice instanceof Notice) {
- $act->source->updated = self::utcDate($notice->created);
- }
-
- $user = User::getKV('id', $profile->id);
-
- if ($user instanceof User) {
- $act->source->links['license'] = common_config('license', 'url');
- }
- }
-
- try {
- $act->selfLink = $this->getSelfLink();
- } catch (InvalidUrlException $e) {
- $act->selfLink = null;
- }
- if ($this->isLocal()) {
- $act->editLink = $act->selfLink;
- }
-
- Event::handle('EndNoticeAsActivity', array($this, $act, $scoped));
- }
-
- self::cacheSet(Cache::codeKey('notice:as-activity:'.$this->id), $act);
-
- return $act;
- }
-
- // This has gotten way too long. Needs to be sliced up into functional bits
- // or ideally exported to a utility class.
-
- public function asAtomEntry($namespace = false, $source = false, $author = true, Profile $scoped = null)
- {
- $act = $this->asActivity($scoped);
- $act->extra[] = $this->noticeInfo($scoped);
- return $act->asString($namespace, $author, $source);
- }
-
- /**
- * Extra notice info for atom entries
- *
- * Clients use some extra notice info in the atom stream.
- * This gives it to them.
- *
- * @param Profile $scoped The currently logged in/scoped profile
- *
- * @return array representation of element
- */
-
- public function noticeInfo(Profile $scoped = null)
- {
- // local notice ID (useful to clients for ordering)
-
- $noticeInfoAttr = array('local_id' => $this->id);
-
- // notice source
-
- $ns = $this->getSource();
-
- if ($ns instanceof Notice_source) {
- $noticeInfoAttr['source'] = $ns->code;
- if (!empty($ns->url)) {
- $noticeInfoAttr['source_link'] = $ns->url;
- if (!empty($ns->name)) {
- $noticeInfoAttr['source'] = $ns->name;
- }
- }
- }
-
- // favorite and repeated
-
- if ($scoped instanceof Profile) {
- $noticeInfoAttr['repeated'] = ($scoped->hasRepeated($this)) ? "true" : "false";
- }
-
- if (!empty($this->repeat_of)) {
- $noticeInfoAttr['repeat_of'] = $this->repeat_of;
- }
-
- Event::handle('StatusNetApiNoticeInfo', array($this, &$noticeInfoAttr, $scoped));
-
- return array('statusnet:notice_info', $noticeInfoAttr, null);
- }
-
- /**
- * Returns an XML string fragment with a reference to a notice as an
- * Activity Streams noun object with the given element type.
- *
- * Assumes that 'activity' namespace has been previously defined.
- *
- * @param string $element one of 'subject', 'object', 'target'
- * @return string
- */
-
- public function asActivityNoun($element)
- {
- $noun = $this->asActivityObject();
- return $noun->asString('activity:' . $element);
- }
-
- public function asActivityObject()
- {
- $object = new ActivityObject();
-
- if (Event::handle('StartActivityObjectFromNotice', array($this, &$object))) {
- $object->type = $this->object_type ?: ActivityObject::NOTE;
- $object->id = $this->getUri();
- //FIXME: = $object->title ?: sprintf(... because we might get a title from StartActivityObjectFromNotice
- $object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname());
- $object->content = $this->getRendered();
- $object->link = $this->getUrl();
- try {
- $object->selfLink = $this->getSelfLink();
- } catch (InvalidUrlException $e) {
- $object->selfLink = null;
- }
-
- $object->extra[] = array('statusnet:notice_id', null, $this->id);
-
- Event::handle('EndActivityObjectFromNotice', array($this, &$object));
- }
-
- if (!$object instanceof ActivityObject) {
- common_log(LOG_ERR, 'Notice asActivityObject created something else for uri=='._ve($this->getUri()).': '._ve($object));
- throw new ServerException('Notice asActivityObject created something else.');
- }
-
- return $object;
- }
-
- /**
- * Determine which notice, if any, a new notice is in reply to.
- *
- * For conversation tracking, we try to see where this notice fits
- * in the tree. Beware that this may very well give false positives
- * and add replies to wrong threads (if there have been newer posts
- * by the same user as we're replying to).
- *
- * @param Profile $sender Author profile
- * @param string $content Final notice content
- *
- * @return integer ID of replied-to notice, or null for not a reply.
- */
-
- public static function getInlineReplyTo(Profile $sender, $content)
- {
- // Is there an initial @ or T?
- if (preg_match('/^T ([A-Z0-9]{1,64}) /', $content, $match)
- || preg_match('/^@([a-z0-9]{1,64})\s+/', $content, $match)) {
- $nickname = common_canonical_nickname($match[1]);
- } else {
- return null;
- }
-
- // Figure out who that is.
- $recipient = common_relative_profile($sender, $nickname, common_sql_now());
-
- if ($recipient instanceof Profile) {
- // Get their last notice
- $last = $recipient->getCurrentNotice();
- if ($last instanceof Notice) {
- return $last;
- }
- // Maybe in the future we want to handle something else below
- // so don't return getCurrentNotice() immediately.
- }
-
- return null;
- }
-
- public static function maxContent()
- {
- $contentlimit = common_config('notice', 'contentlimit');
- // null => use global limit (distinct from 0!)
- if (is_null($contentlimit)) {
- $contentlimit = common_config('site', 'textlimit');
- }
- return $contentlimit;
- }
-
- public static function contentTooLong($content)
- {
- $contentlimit = self::maxContent();
- return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
- }
-
- /**
- * Convenience function for posting a repeat of an existing message.
- *
- * @param Profile $repeater Profile which is doing the repeat
- * @param string $source: posting source key, eg 'web', 'api', etc
- * @return Notice
- *
- * @throws Exception on failure or permission problems
- */
- public function repeat(Profile $repeater, $source)
- {
- $author = $this->getProfile();
-
- // TRANS: Message used to repeat a notice. RT is the abbreviation of 'retweet'.
- // TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
- $content = sprintf(
- _('RT @%1$s %2$s'),
- $author->getNickname(),
- $this->content
- );
-
- $maxlen = self::maxContent();
- if ($maxlen > 0 && mb_strlen($content) > $maxlen) {
- // Web interface and current Twitter API clients will
- // pull the original notice's text, but some older
- // clients and RSS/Atom feeds will see this trimmed text.
- //
- // Unfortunately this is likely to lose tags or URLs
- // at the end of long notices.
- $content = mb_substr($content, 0, $maxlen - 4) . ' ...';
- }
-
-
- // Scope is same as this one's
- return self::saveNew(
- $repeater->id,
- $content,
- $source,
- ['repeat_of' => $this->id, 'scope' => $this->scope]
- );
- }
-
- // These are supposed to be in chron order!
-
- public function repeatStream($limit = 100)
- {
- $cache = Cache::instance();
-
- if (empty($cache)) {
- $ids = $this->_repeatStreamDirect($limit);
- } else {
- $idstr = $cache->get(Cache::key('notice:repeats:'.$this->id));
- if ($idstr !== false) {
- if (!empty($idstr)) {
- $ids = explode(',', $idstr);
- } else {
- $ids = [];
- }
- } else {
- $ids = $this->_repeatStreamDirect(100);
- $cache->set(Cache::key('notice:repeats:'.$this->id), implode(',', $ids));
- }
- if ($limit < 100) {
- // We do a max of 100, so slice down to limit
- $ids = array_slice($ids, 0, $limit);
- }
- }
-
- return NoticeStream::getStreamByIds($ids);
- }
-
- public function _repeatStreamDirect($limit)
- {
- $notice = new Notice();
-
- $notice->selectAdd(); // clears it
- $notice->selectAdd('id');
-
- $notice->repeat_of = $this->id;
-
- $notice->orderBy('created, id'); // NB: asc!
-
- if (!is_null($limit)) {
- $notice->limit(0, $limit);
- }
-
- return $notice->fetchAll('id');
- }
-
- public static function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
- {
- $options = array();
-
- if (!empty($location_id) && !empty($location_ns)) {
- $options['location_id'] = $location_id;
- $options['location_ns'] = $location_ns;
-
- $location = Location::fromId($location_id, $location_ns);
-
- if ($location instanceof Location) {
- $options['lat'] = $location->lat;
- $options['lon'] = $location->lon;
- }
- } elseif (!empty($lat) && !empty($lon)) {
- $options['lat'] = $lat;
- $options['lon'] = $lon;
-
- $location = Location::fromLatLon($lat, $lon);
-
- if ($location instanceof Location) {
- $options['location_id'] = $location->location_id;
- $options['location_ns'] = $location->location_ns;
- }
- } elseif (!empty($profile)) {
- if (isset($profile->lat) && isset($profile->lon)) {
- $options['lat'] = $profile->lat;
- $options['lon'] = $profile->lon;
- }
-
- if (isset($profile->location_id) && isset($profile->location_ns)) {
- $options['location_id'] = $profile->location_id;
- $options['location_ns'] = $profile->location_ns;
- }
- }
-
- return $options;
- }
-
- public function clearAttentions()
- {
- $att = new Attention();
- $att->notice_id = $this->getID();
-
- if ($att->find()) {
- while ($att->fetch()) {
- // Can't do delete() on the object directly since it won't remove all of it
- $other = clone($att);
- $other->delete();
- }
- }
- }
-
- public function clearReplies()
- {
- $replyNotice = new Notice();
- $replyNotice->reply_to = $this->id;
-
- //Null any notices that are replies to this notice
-
- if ($replyNotice->find()) {
- while ($replyNotice->fetch()) {
- $orig = clone($replyNotice);
- $replyNotice->reply_to = $replyNotice->sqlValue('NULL');
- $replyNotice->update($orig);
- }
- }
-
- // Reply records
-
- $reply = new Reply();
- $reply->notice_id = $this->id;
-
- if ($reply->find()) {
- while ($reply->fetch()) {
- self::blow('reply:stream:%d', $reply->profile_id);
- $reply->delete();
- }
- }
-
- $reply->free();
- }
-
- public function clearLocation()
- {
- $loc = new Notice_location();
- $loc->notice_id = $this->id;
-
- if ($loc->find()) {
- $loc->delete();
- }
- }
-
- private function clearPrefs(): void
- {
- $prefs = new Notice_prefs();
- $prefs->notice_id = $this->id;
-
- if ($prefs->find()) {
- $prefs->delete();
- }
- }
-
- public function clearFiles()
- {
- $f2p = new File_to_post();
-
- $f2p->post_id = $this->id;
-
- if ($f2p->find()) {
- while ($f2p->fetch()) {
- $f2p->delete();
- }
- }
- // FIXME: decide whether to delete File objects
- // ...and related (actual) files
- }
-
- public function clearRepeats()
- {
- $repeatNotice = new Notice();
- $repeatNotice->repeat_of = $this->id;
-
- //Null any notices that are repeats of this notice
-
- if ($repeatNotice->find()) {
- while ($repeatNotice->fetch()) {
- $orig = clone($repeatNotice);
- $repeatNotice->repeat_of = $repeatNotice->sqlValue('NULL');
- $repeatNotice->update($orig);
- }
- }
- }
-
- public function clearTags()
- {
- $tag = new Notice_tag();
- $tag->notice_id = $this->id;
-
- if ($tag->find()) {
- while ($tag->fetch()) {
- self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, Cache::keyize($tag->tag));
- self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, Cache::keyize($tag->tag));
- self::blow('notice_tag:notice_ids:%s', Cache::keyize($tag->tag));
- self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($tag->tag));
- $tag->delete();
- }
- }
-
- $tag->free();
- }
-
- public function clearGroupInboxes()
- {
- $gi = new Group_inbox();
-
- $gi->notice_id = $this->id;
-
- if ($gi->find()) {
- while ($gi->fetch()) {
- self::blow('user_group:notice_ids:%d', $gi->group_id);
- $gi->delete();
- }
- }
-
- $gi->free();
- }
-
- public function distribute()
- {
- // We always insert for the author so they don't
- // have to wait
- Event::handle('StartNoticeDistribute', array($this));
-
- // If there's a failure, we want to _force_
- // distribution at this point.
- try {
- $qm = QueueManager::get();
- $qm->enqueue($this, 'distrib');
- } catch (Exception $e) {
- // If the exception isn't transient, this
- // may throw more exceptions as DQH does
- // its own enqueueing. So, we ignore them!
- try {
- $handler = new DistribQueueHandler();
- $handler->handle($this);
- } catch (Exception $e) {
- common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage());
- }
- // Re-throw so somebody smarter can handle it.
- throw $e;
- }
- }
-
- public function insert()
- {
- $result = parent::insert();
-
- if ($result === false) {
- common_log_db_error($this, 'INSERT', __FILE__);
- // TRANS: Server exception thrown when a stored object entry cannot be saved.
- throw new ServerException('Could not save Notice');
- }
-
- // Profile::hasRepeated() abuses pkeyGet(), so we
- // have to clear manually
- if (!empty($this->repeat_of)) {
- $c = self::memcache();
- if (!empty($c)) {
- $ck = self::multicacheKey(
- 'Notice',
- ['profile_id' => $this->profile_id, 'repeat_of' => $this->repeat_of]
- );
- $c->delete($ck);
- }
- }
-
- // Update possibly ID-dependent columns: URI, conversation
- // (now that INSERT has added the notice's local id)
- $orig = clone($this);
- $changed = false;
-
- // We can only get here if it's a local notice, since remote notices
- // should've bailed out earlier due to lacking a URI.
- if (empty($this->uri)) {
- $this->uri = sprintf(
- '%s%s=%d:%s=%s',
- TagURI::mint(),
- 'noticeId',
- $this->id,
- 'objectType',
- $this->getObjectType(true)
- );
- $changed = true;
- }
-
- if ($changed && $this->update($orig) === false) {
- common_log_db_error($notice, 'UPDATE', __FILE__);
- // TRANS: Server exception thrown when a notice cannot be updated.
- throw new ServerException(_('Problem saving notice.'));
- }
-
- $this->blowOnInsert();
-
- return $result;
- }
-
- /**
- * Get the source of the notice
- *
- * @return Notice_source $ns A notice source object. 'code' is the only attribute
- * guaranteed to be populated.
- */
- public function getSource()
- {
- if (empty($this->source)) {
- return false;
- }
-
- $ns = new Notice_source();
- switch ($this->source) {
- case 'web':
- case 'xmpp':
- case 'mail':
- case 'omb':
- case 'system':
- case 'api':
- $ns->code = $this->source;
- break;
- default:
- $ns = Notice_source::getKV($this->source);
- if (!$ns) {
- $ns = new Notice_source();
- $ns->code = $this->source;
- $app = Oauth_application::getKV('name', $this->source);
- if ($app) {
- $ns->name = $app->name;
- $ns->url = $app->source_url;
- }
- }
- break;
- }
-
- return $ns;
- }
-
- /**
- * Determine whether the notice was locally created
- *
- * @return boolean locality
- */
-
- public function isLocal()
- {
- $is_local = intval($this->is_local);
- return ($is_local === self::LOCAL_PUBLIC || $is_local === self::LOCAL_NONPUBLIC);
- }
-
- public function getScope()
- {
- return intval($this->scope);
- }
-
- public function isRepeat()
- {
- return !empty($this->repeat_of);
- }
-
- public function isRepeated()
- {
- $n = new Notice();
- $n->repeat_of = $this->getID();
- return $n->find() && $n->N > 0;
- }
-
- /**
- * Get the list of hash tags saved with this notice.
- *
- * @return array of strings
- */
- public function getTags()
- {
- $tags = array();
-
- $keypart = sprintf('notice:tags:%d', $this->id);
-
- $tagstr = self::cacheGet($keypart);
-
- if ($tagstr !== false) {
- $tags = explode(',', $tagstr);
- } else {
- $tag = new Notice_tag();
- $tag->notice_id = $this->id;
- if ($tag->find()) {
- while ($tag->fetch()) {
- $tags[] = $tag->tag;
- }
- }
- self::cacheSet($keypart, implode(',', $tags));
- }
-
- return $tags;
- }
-
- private static function utcDate($dt)
- {
- $dateStr = date('d F Y H:i:s', strtotime($dt));
- $d = new DateTime($dateStr, new DateTimeZone('UTC'));
- return $d->format(DATE_W3C);
- }
-
- /**
- * Look up the creation timestamp for a given notice ID, even
- * if it's been deleted.
- *
- * @param int $id
- * @return mixed string recorded creation timestamp, or false if can't be found
- */
- public static function getAsTimestamp($id)
- {
- if (empty($id)) {
- throw new EmptyPkeyValueException('Notice', 'id');
- }
-
- $timestamp = null;
- if (Event::handle('GetNoticeSqlTimestamp', array($id, &$timestamp))) {
- // getByID throws exception if $id isn't found
- $notice = Notice::getByID($id);
- $timestamp = $notice->created;
- }
-
- if (empty($timestamp)) {
- throw new ServerException('No timestamp found for Notice with id=='._ve($id));
- }
- return $timestamp;
- }
-
- /**
- * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
- * parameter, matching notices posted after the given one (exclusive).
- *
- * If the referenced notice can't be found, will return false.
- *
- * @param int $id
- * @param string $idField
- * @param string $createdField
- * @return mixed string or false if no match
- */
- public static function whereSinceId($id, $idField='id', $createdField='created')
- {
- try {
- $since = Notice::getAsTimestamp($id);
- } catch (Exception $e) {
- return false;
- }
- return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
- }
-
- /**
- * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
- * parameter, matching notices posted after the given one (exclusive), and
- * if necessary add it to the data object's query.
- *
- * @param DB_DataObject $obj
- * @param int $id
- * @param string $idField
- * @param string $createdField
- * @return mixed string or false if no match
- */
- public static function addWhereSinceId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
- {
- $since = self::whereSinceId($id, $idField, $createdField);
- if ($since) {
- $obj->whereAdd($since);
- }
- }
-
- /**
- * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
- * parameter, matching notices posted before the given one (inclusive).
- *
- * If the referenced notice can't be found, will return false.
- *
- * @param int $id
- * @param string $idField
- * @param string $createdField
- * @return mixed string or false if no match
- */
- public static function whereMaxId($id, $idField='id', $createdField='created')
- {
- try {
- $max = Notice::getAsTimestamp($id);
- } catch (Exception $e) {
- return false;
- }
- return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
- }
-
- /**
- * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
- * parameter, matching notices posted before the given one (inclusive), and
- * if necessary add it to the data object's query.
- *
- * @param DB_DataObject $obj
- * @param int $id
- * @param string $idField
- * @param string $createdField
- * @return mixed string or false if no match
- */
- public static function addWhereMaxId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
- {
- $max = self::whereMaxId($id, $idField, $createdField);
- if ($max) {
- $obj->whereAdd($max);
- }
- }
-
- public function isPublic()
- {
- $is_local = intval($this->is_local);
- return !($is_local === Notice::LOCAL_NONPUBLIC || $is_local === Notice::GATEWAY);
- }
-
- /**
- * Check that the given profile is allowed to read, respond to, or otherwise
- * act on this notice.
- *
- * The $scope member is a bitmask of scopes, representing a logical AND of the
- * scope requirement. So, 0x03 (Notice::ADDRESSEE_SCOPE | Notice::SITE_SCOPE) means
- * "only visible to people who are mentioned in the notice AND are users on this site."
- * Users on the site who are not mentioned in the notice will not be able to see the
- * notice.
- *
- * @param Profile $profile The profile to check; pass null to check for public/unauthenticated users.
- *
- * @return boolean whether the profile is in the notice's scope
- */
- public function inScope($profile)
- {
- if (is_null($profile)) {
- $keypart = sprintf('notice:in-scope-for:%d:null', $this->id);
- } else {
- $keypart = sprintf('notice:in-scope-for:%d:%d', $this->id, $profile->id);
- }
-
- $result = self::cacheGet($keypart);
-
- if ($result === false) {
- $bResult = false;
- if (Event::handle('StartNoticeInScope', array($this, $profile, &$bResult))) {
- $bResult = $this->_inScope($profile);
- Event::handle('EndNoticeInScope', array($this, $profile, &$bResult));
- }
- $result = ($bResult) ? 1 : 0;
- self::cacheSet($keypart, $result, 0, 300);
- }
-
- return ($result == 1) ? true : false;
- }
-
- protected function _inScope($profile)
- {
- $scope = is_null($this->scope) ? self::defaultScope() : $this->getScope();
-
- if ($scope === 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
- return !$this->isHiddenSpam($profile);
- }
-
- // If there's scope, anon cannot be in scope
- if (empty($profile)) {
- return false;
- }
-
- // Author is always in scope
- if ($this->profile_id == $profile->id) {
- return true;
- }
-
- // Only for users on this site
- if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
- return false;
- }
-
- // Only for users mentioned in the notice
- if ($scope & Notice::ADDRESSEE_SCOPE) {
- $reply = Reply::pkeyGet(array('notice_id' => $this->id,
- 'profile_id' => $profile->id));
-
- if (!$reply instanceof Reply) {
- return false;
- }
- }
-
- // Only for members of the given group
- if ($scope & Notice::GROUP_SCOPE) {
-
- // XXX: just query for the single membership
-
- $groups = $this->getGroups();
-
- $foundOne = false;
-
- foreach ($groups as $group) {
- if ($profile->isMember($group)) {
- $foundOne = true;
- break;
- }
- }
-
- if (!$foundOne) {
- return false;
- }
- }
-
- if ($scope & Notice::FOLLOWER_SCOPE || $this->getProfile()->isPrivateStream()) {
- if (!Subscription::exists($profile, $this->getProfile())) {
- return false;
- }
- }
-
- return !$this->isHiddenSpam($profile);
- }
-
- public function isHiddenSpam($profile)
- {
-
- // Hide posts by silenced users from everyone but moderators.
-
- if (common_config('notice', 'hidespam')) {
- try {
- $author = $this->getProfile();
- } catch (Exception $e) {
- // If we can't get an author, keep it hidden.
- // XXX: technically not spam, but, whatever.
- return true;
- }
-
- if ($author->hasRole(Profile_role::SILENCED)) {
- if (!$profile instanceof Profile || (($profile->id !== $author->id) && (!$profile->hasRight(Right::REVIEWSPAM)))) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public function hasParent()
- {
- try {
- $this->getParent();
- } catch (NoParentNoticeException $e) {
- return false;
- }
- return true;
- }
-
- public function getParent()
- {
- $reply_to_id = null;
-
- if (empty($this->reply_to)) {
- throw new NoParentNoticeException($this);
- }
-
- // The reply_to ID in the table Notice could exist with a number
- // however, the replied to notice might not exist in the database.
- // Thus we need to catch the exception and throw the NoParentNoticeException else
- // the timeline will not display correctly.
- try {
- $reply_to_id = self::getByID($this->reply_to);
- } catch (Exception $e) {
- throw new NoParentNoticeException($this);
- }
-
- return $reply_to_id;
- }
-
- /**
- * Magic function called at serialize() time.
- *
- * We use this to drop a couple process-specific references
- * from DB_DataObject which can cause trouble in future
- * processes.
- *
- * @return array of variable names to include in serialization.
- */
-
- public function __sleep()
- {
- $vars = parent::__sleep();
- $skip = array('_profile', '_groups', '_attachments', '_faves', '_replies', '_repeats');
- return array_diff($vars, $skip);
- }
-
- public static function defaultScope()
- {
- $scope = common_config('notice', 'defaultscope');
- if (is_null($scope)) {
- if (common_config('site', 'private')) {
- $scope = 1;
- } else {
- $scope = 0;
- }
- }
- return $scope;
- }
-
- public static function fillProfiles($notices)
- {
- $map = self::getProfiles($notices);
- foreach ($notices as $entry => $notice) {
- try {
- if (array_key_exists($notice->profile_id, $map)) {
- $notice->_setProfile($map[$notice->profile_id]);
- }
- } catch (NoProfileException $e) {
- common_log(LOG_WARNING, "Failed to fill profile in Notice with non-existing entry for profile_id: {$e->profile_id}");
- unset($notices[$entry]);
- }
- }
-
- return array_values($map);
- }
-
- public static function getProfiles(&$notices)
- {
- $ids = [];
- foreach ($notices as $notice) {
- $ids[] = $notice->profile_id;
- }
- $ids = array_unique($ids);
- return Profile::pivotGet('id', $ids);
- }
-
- public static function fillGroups(&$notices)
- {
- $ids = self::_idsOf($notices);
- $gis = Group_inbox::listGet('notice_id', $ids);
- $gids = [];
-
- foreach ($gis as $id => $gi) {
- foreach ($gi as $g) {
- $gids[] = $g->group_id;
- }
- }
-
- $gids = array_unique($gids);
- $group = User_group::pivotGet('id', $gids);
- foreach ($notices as $notice) {
- $grps = [];
- $gi = $gis[$notice->id];
- foreach ($gi as $g) {
- $grps[] = $group[$g->group_id];
- }
- $notice->_setGroups($grps);
- }
- }
-
- public static function _idsOf(array &$notices)
- {
- $ids = [];
- foreach ($notices as $notice) {
- $ids[$notice->id] = true;
- }
- return array_keys($ids);
- }
-
- public static function fillAttachments(&$notices)
- {
- $ids = self::_idsOf($notices);
- $f2pMap = File_to_post::listGet('post_id', $ids);
- $fileIds = [];
- foreach ($f2pMap as $noticeId => $f2ps) {
- foreach ($f2ps as $f2p) {
- $fileIds[] = $f2p->file_id;
- }
- }
-
- $fileIds = array_unique($fileIds);
- $fileMap = File::pivotGet('id', $fileIds);
- foreach ($notices as $notice) {
- $files = [];
- $f2ps = $f2pMap[$notice->id];
- foreach ($f2ps as $f2p) {
- if (!isset($fileMap[$f2p->file_id])) {
- // We have probably deleted value from fileMap since
- // it as a NULL entry (see the following elseif).
- continue;
- } elseif (is_null($fileMap[$f2p->file_id])) {
- // If the file id lookup returned a NULL value, it doesn't
- // exist in our file table! So this is a remnant file_to_post
- // entry that is no longer valid and should be removed.
- common_debug('ATTACHMENT deleting f2p for post_id='.$f2p->post_id.' file_id='.$f2p->file_id);
- $f2p->delete();
- unset($fileMap[$f2p->file_id]);
- continue;
- }
- $files[] = $fileMap[$f2p->file_id];
- }
- $notice->_setAttachments($files);
- }
- }
-
- public static function fillReplies(&$notices)
- {
- $ids = self::_idsOf($notices);
- $replyMap = Reply::listGet('notice_id', $ids);
- foreach ($notices as $notice) {
- $replies = $replyMap[$notice->id];
- $ids = array();
- foreach ($replies as $reply) {
- $ids[] = $reply->profile_id;
- }
- $notice->_setReplies($ids);
- }
- }
-
- public static function beforeSchemaUpdate()
- {
- $table = strtolower(get_called_class());
- $schema = Schema::get();
- $schemadef = $schema->getTableDef($table);
-
- // 2015-09-04 We move Notice location data to Notice_location
- // First we see if we have to do this at all
- if (isset($schemadef['fields']['lat'])
- && isset($schemadef['fields']['lon'])
- && isset($schemadef['fields']['location_id'])
- && isset($schemadef['fields']['location_ns'])) {
- // Then we make sure the Notice_location table is created!
- $schema->ensureTable('notice_location', Notice_location::schemaDef());
-
- // Then we continue on our road to migration!
- echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)";
-
- $notice = new Notice();
- $notice->query(sprintf(
- 'SELECT id, lat, lon, location_id, location_ns FROM %1$s ' .
- 'WHERE lat IS NOT NULL ' .
- 'OR lon IS NOT NULL ' .
- 'OR location_id IS NOT NULL ' .
- 'OR location_ns IS NOT NULL',
- common_database_tablename($table)
- ));
- print "\nFound {$notice->N} notices with location data, inserting";
- while ($notice->fetch()) {
- $notloc = Notice_location::getKV('notice_id', $notice->id);
- if ($notloc instanceof Notice_location) {
- print "-";
- continue;
- }
- $notloc = new Notice_location();
- $notloc->notice_id = $notice->id;
- $notloc->lat= $notice->lat;
- $notloc->lon= $notice->lon;
- $notloc->location_id= $notice->location_id;
- $notloc->location_ns= $notice->location_ns;
- $notloc->insert();
- print ".";
- }
- print "\n";
- }
-
- /**
- * Make sure constraints are met before upgrading, if foreign keys
- * are not already in use.
- * 2016-03-31
- */
- if (!isset($schemadef['foreign keys'])) {
- $newschemadef = self::schemaDef();
- printfnq("\nConstraint checking Notice table...\n");
- /**
- * Improve typing and make sure no NULL values in any id-related columns are 0
- * 2016-03-31
- */
- foreach (['reply_to', 'repeat_of'] as $field) {
- $notice = new Notice(); // reset the object
- $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE %2$s=0', $notice->escapedTableName(), $field));
- // Now we're sure that no Notice entries have repeat_of=0, only an id > 0 or NULL
- unset($notice);
- }
-
- /**
- * This Will find foreign keys which do not fulfill the constraints and fix
- * where appropriate, such as delete when "repeat_of" ID not found in notice.id
- * or set to NULL for "reply_to" in the same case.
- * 2016-03-31
- *
- * XXX: How does this work if we would use multicolumn foreign keys?
- */
- foreach (['reply_to' => 'reset', 'repeat_of' => 'delete', 'profile_id' => 'delete'] as $field=>$action) {
- $notice = new Notice();
-
- $fkeyname = $notice->tableName().'_'.$field.'_fkey';
- assert(isset($newschemadef['foreign keys'][$fkeyname]));
- assert($newschemadef['foreign keys'][$fkeyname]);
-
- $foreign_key = $newschemadef['foreign keys'][$fkeyname];
- $fkeytable = $foreign_key[0];
- assert(isset($foreign_key[1][$field]));
- $fkeycol = $foreign_key[1][$field];
-
- printfnq("* {$fkeyname} ({$field} => {$fkeytable}.{$fkeycol})\n");
-
- // NOTE: Above we set all repeat_of to NULL if they were 0, so this really gets them all.
- $notice->whereAdd(sprintf('%1$s NOT IN (SELECT %2$s FROM %3$s)', $field, $fkeycol, $fkeytable));
- if ($notice->find()) {
- printfnq("\tFound {$notice->N} notices with {$field} NOT IN notice.id, {$action}ing...");
- switch ($action) {
- case 'delete': // since it's a directly dependant notice for an unknown ID we don't want it in our DB
- while ($notice->fetch()) {
- $notice->delete();
- }
- break;
- case 'reset': // just set it to NULL to be compatible with our constraints, if it was related to an unknown ID
- $ids = [];
- foreach ($notice->fetchAll('id') as $id) {
- settype($id, 'int');
- $ids[] = $id;
- }
- unset($notice);
- $notice = new Notice();
- $notice->query(sprintf(
- <<<'END'
- UPDATE %1$s
- SET %2$s = NULL, modified = CURRENT_TIMESTAMP
- WHERE id IN (%3$s)
- END,
- $notice->escapedTableName(),
- $field,
- implode(',', $ids)
- ));
- break;
- default:
- throw new ServerException('The programmer sucks, invalid action name when fixing table.');
- }
- printfnq("DONE.\n");
- }
- unset($notice);
- }
- }
- }
-
- public function delPref($namespace, $topic)
- {
- return Notice_prefs::setData($this, $namespace, $topic, null);
- }
-
- public function getPref($namespace, $topic, $default = null)
- {
- // If you want an exception to be thrown, call Notice_prefs::getData directly
- try {
- return Notice_prefs::getData($this, $namespace, $topic, $default);
- } catch (NoResultException $e) {
- return null;
- }
- }
-
- // The same as getPref but will fall back to common_config value for the same namespace/topic
- public function getConfigPref($namespace, $topic)
- {
- return Notice_prefs::getConfigData($this, $namespace, $topic);
- }
-
- public function setPref($namespace, $topic, $data)
- {
- return Notice_prefs::setData($this, $namespace, $topic, $data);
- }
-}
diff --git a/src/Entity/NoticeLocation.php b/src/Entity/NoticeLocation.php
new file mode 100644
index 0000000000..f3a7502f5c
--- /dev/null
+++ b/src/Entity/NoticeLocation.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Notice's location
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class NoticeLocation
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'notice_location',
+ 'fields' => [
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice that is the reply'],
+ 'lat' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'],
+ 'lon' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'],
+ 'location_id' => ['type' => 'int', 'description' => 'location id if possible'],
+ 'location_ns' => ['type' => 'int', 'description' => 'namespace for location'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['notice_id'],
+ 'foreign keys' => [
+ 'notice_location_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'notice_location_location_id_idx' => ['location_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/NoticePrefs.php b/src/Entity/NoticePrefs.php
new file mode 100644
index 0000000000..b4edf2bb6c
--- /dev/null
+++ b/src/Entity/NoticePrefs.php
@@ -0,0 +1,65 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Notice preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Diogo Cordeiro
+ * @license 2018 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class NoticePrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'notice_prefs',
+ 'fields' => [
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'user'],
+ 'namespace' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'],
+ 'topic' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'],
+ 'data' => ['type' => 'blob', 'description' => 'topic data, may be anything'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['notice_id', 'namespace', 'topic'],
+ 'foreign keys' => [
+ 'notice_prefs_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'notice_prefs_notice_id_idx' => ['notice_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/NoticeSource.php b/src/Entity/NoticeSource.php
new file mode 100644
index 0000000000..5ae316eb83
--- /dev/null
+++ b/src/Entity/NoticeSource.php
@@ -0,0 +1,57 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Notices sources
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class NoticeSource
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'notice_source',
+ 'fields' => [
+ 'code' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'source code'],
+ 'name' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the source'],
+ 'url' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'url to link to'],
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'default' => 0, 'description' => 'date this record was created'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['code'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/NoticeTag.php b/src/Entity/NoticeTag.php
new file mode 100644
index 0000000000..ac5c5b2897
--- /dev/null
+++ b/src/Entity/NoticeTag.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Notice Tag
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class NoticeTag
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'notice_tag',
+ 'description' => 'Hash tags',
+ 'fields' => [
+ 'tag' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'],
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice tagged'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ ],
+ 'primary key' => ['tag', 'notice_id'],
+ 'foreign keys' => [
+ 'notice_tag_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'notice_tag_created_idx' => ['created'],
+ 'notice_tag_notice_id_idx' => ['notice_id'],
+ 'notice_tag_tag_created_notice_id_idx' => ['tag', 'created', 'notice_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Notice_location.php b/src/Entity/Notice_location.php
deleted file mode 100644
index dd1b2dc066..0000000000
--- a/src/Entity/Notice_location.php
+++ /dev/null
@@ -1,92 +0,0 @@
-.
-
-/**
- * Table Definition for notice_location
- */
-
-defined('GNUSOCIAL') || die();
-
-class Notice_location extends Managed_DataObject
-{
- public $__table = 'notice_location'; // table name
- public $notice_id; // int(4) primary_key not_null
- public $lat; // decimal(10,7)
- public $lon; // decimal(10,7)
- public $location_id; // int(4)
- public $location_ns; // int(4)
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice that is the reply'),
- 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
- 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
- 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
- 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('notice_id'),
- 'foreign keys' => array(
- 'notice_location_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- ),
- 'indexes' => array(
- 'notice_location_location_id_idx' => array('location_id'),
- ),
- );
- }
-
- public static function locFromStored(Notice $stored)
- {
- $loc = new Notice_location();
- $loc->notice_id = $stored->getID();
- if (!$loc->find(true)) {
- throw new NoResultException($loc);
- }
- return $loc->asLocation();
- }
-
- public static function fromLocation(Location $location)
- {
- $notloc = new Notice_location();
- $notloc->lat = $location->lat;
- $notloc->lon = $location->lon;
- $notloc->location_ns = $location->location_ns;
- $notloc->location_id = $location->location_id;
- return $notloc;
- }
-
- public function asLocation()
- {
- $location = null;
-
- if (!empty($this->location_id) && !empty($this->location_ns)) {
- $location = Location::fromId($this->location_id, $this->location_ns);
- }
-
- if (is_null($location)) { // no ID, or Location::fromId() failed
- $location = Location::fromLatLon($this->lat, $this->lon);
- }
-
- if (is_null($location)) {
- throw new ServerException('Location could not be looked up from existing data.');
- }
-
- return $location;
- }
-}
diff --git a/src/Entity/Notice_prefs.php b/src/Entity/Notice_prefs.php
deleted file mode 100644
index 39ebe1c231..0000000000
--- a/src/Entity/Notice_prefs.php
+++ /dev/null
@@ -1,175 +0,0 @@
-.
-
-/**
- * Data class for Notice preferences
- *
- * @category Data
- * @package GNUsocial
- * @author Mikael Nordfeldth
- * @author Diogo Cordeiro
- * @copyright 2013 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Notice_prefs extends Managed_DataObject
-{
- public $__table = 'notice_prefs'; // table name
- public $notice_id; // int(4) primary_key not_null
- public $namespace; // varchar(191) not_null
- public $topic; // varchar(191) not_null
- public $data; // text
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
- 'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
- 'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
- 'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('notice_id', 'namespace', 'topic'),
- 'foreign keys' => array(
- 'notice_prefs_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- ),
- );
- }
-
- public static function getNamespacePrefs(Notice $notice, $namespace, array $topic = [])
- {
- if (empty($topic)) {
- $prefs = new Notice_prefs();
- $prefs->notice_id = $notice->getID();
- $prefs->namespace = $namespace;
- $prefs->find();
- } else {
- $prefs = self::pivotGet('notice_id', $notice->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
- }
-
- if (empty($prefs->N)) {
- throw new NoResultException($prefs);
- }
-
- return $prefs;
- }
-
- public static function getNamespace(Notice $notice, $namespace, array $topic = [])
- {
- $prefs = self::getNamespacePrefs($notice, $namespace, $topic);
- return $prefs->fetchAll();
- }
-
- public static function getAll(Notice $notice)
- {
- try {
- $prefs = self::listFind('notice_id', array($notice->getID()));
- } catch (NoResultException $e) {
- return array();
- }
-
- $list = array();
- while ($prefs->fetch()) {
- if (!isset($list[$prefs->namespace])) {
- $list[$prefs->namespace] = array();
- }
- $list[$prefs->namespace][$prefs->topic] = $prefs->data;
- }
- return $list;
- }
-
- public static function getTopic(Notice $notice, $namespace, $topic)
- {
- return self::getByPK([
- 'notice_id' => $notice->getID(),
- 'namespace' => $namespace,
- 'topic' => $topic,
- ]);
- }
-
- public static function getData(Notice $notice, $namespace, $topic, $def = null)
- {
- try {
- $pref = self::getTopic($notice, $namespace, $topic);
- } catch (NoResultException $e) {
- if ($def === null) {
- // If no default value was set, continue the exception.
- throw $e;
- }
- // If there was a default value, return that.
- return $def;
- }
- return $pref->data;
- }
-
- public static function getConfigData(Notice $notice, $namespace, $topic)
- {
- try {
- $data = self::getData($notice, $namespace, $topic);
- } catch (NoResultException $e) {
- $data = common_config($namespace, $topic);
- }
- return $data;
- }
-
- /*
- * Sets a notice preference based on Notice, namespace and topic
- *
- * @param Notice $notice Which notice this is for
- * @param string $namespace Under which namespace (pluginname etc.)
- * @param string $topic Preference name (think key in key-val store)
- * @param string $data Data to be put into preference storage, null means delete
- *
- * @return true if changes are made, false if no action taken
- * @throws ServerException if preference could not be saved
- */
- public static function setData(Notice $notice, $namespace, $topic, $data = null)
- {
- try {
- $pref = self::getTopic($notice, $namespace, $topic);
- if (is_null($data)) {
- $pref->delete();
- } else {
- $orig = clone($pref);
- $pref->data = DB_DataObject_Cast::blob($data);
- $pref->update($orig);
- }
- return true;
- } catch (NoResultException $e) {
- if (is_null($data)) {
- return false; // No action taken
- }
- }
-
- $pref = new Notice_prefs();
- $pref->notice_id = $notice->getID();
- $pref->namespace = $namespace;
- $pref->topic = $topic;
- $pref->data = DB_DataObject_Cast::blob($data);
- $pref->created = common_sql_now();
-
- if ($pref->insert() === false) {
- throw new ServerException('Could not save notice preference.');
- }
- return true;
- }
-}
diff --git a/src/Entity/Notice_source.php b/src/Entity/Notice_source.php
deleted file mode 100644
index 205c14b36d..0000000000
--- a/src/Entity/Notice_source.php
+++ /dev/null
@@ -1,52 +0,0 @@
-.
-
-/**
- * Table Definition for notice_source
- */
-
-defined('GNUSOCIAL') || die();
-
-class Notice_source extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'notice_source'; // table name
- public $code; // varchar(32) primary_key not_null
- public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $url; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'source code'),
- 'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the source'),
- 'url' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'url to link to'),
- 'notice_id' => array('type' => 'int', 'not null' => true, 'default' => 0, 'description' => 'notice id'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('code'),
- );
- }
-}
diff --git a/src/Entity/Notice_tag.php b/src/Entity/Notice_tag.php
deleted file mode 100644
index c742538f5e..0000000000
--- a/src/Entity/Notice_tag.php
+++ /dev/null
@@ -1,97 +0,0 @@
-.
-
-/*
- * @copyright 2008, 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Notice_tag extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'notice_tag'; // table name
- public $tag; // varchar(64) primary_key not_null
- public $notice_id; // int(4) primary_key not_null
- public $created; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Hash tags',
- 'fields' => array(
- 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'),
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice tagged'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- ),
- 'primary key' => array('tag', 'notice_id'),
- 'foreign keys' => array(
- 'notice_tag_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- ),
- 'indexes' => array(
- 'notice_tag_created_idx' => array('created'),
- 'notice_tag_notice_id_idx' => array('notice_id'),
- 'notice_tag_tag_created_notice_id_idx' => array('tag', 'created', 'notice_id')
- ),
- );
- }
-
- public static function getStream(
- $tag,
- $offset = 0,
- $limit = 20,
- $sinceId = 0,
- $maxId = 0
- ) {
- // FIXME: Get the Profile::current value some other way
- // to avoid confusino between queue processing and session.
- $stream = new TagNoticeStream($tag, Profile::current());
- return $stream;
- }
-
- public function blowCache($blowLast = false)
- {
- self::blow('notice_tag:notice_ids:%s', Cache::keyize($this->tag));
- if ($blowLast) {
- self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($this->tag));
- }
- }
-
- public static function url($tag)
- {
- if (common_config('singleuser', 'enabled')) {
- // Regular TagAction isn't set up in 1user mode
- $nickname = User::singleUserNickname();
- $url = common_local_url(
- 'showstream',
- [
- 'nickname' => $nickname,
- 'tag' => $tag,
- ]
- );
- } else {
- $url = common_local_url('tag', ['tag' => $tag]);
- }
-
- return $url;
- }
-}
diff --git a/src/Entity/OauthApplication.php b/src/Entity/OauthApplication.php
new file mode 100644
index 0000000000..ab44404cf6
--- /dev/null
+++ b/src/Entity/OauthApplication.php
@@ -0,0 +1,73 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for OAuth Application
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class OauthApplication
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'oauth_application',
+ 'description' => 'OAuth application registration record',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
+ 'owner' => ['type' => 'int', 'not null' => true, 'description' => 'owner of the application'],
+ 'consumer_key' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application consumer key'],
+ 'name' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'],
+ 'description' => ['type' => 'varchar', 'length' => 191, 'description' => 'description of the application'],
+ 'icon' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'default' => '/theme/base/default-avatar-stream.png', 'description' => 'application icon'],
+ 'source_url' => ['type' => 'varchar', 'length' => 191, 'description' => 'application homepage - used for source link'],
+ 'organization' => ['type' => 'varchar', 'length' => 191, 'description' => 'name of the organization running the application'],
+ 'homepage' => ['type' => 'varchar', 'length' => 191, 'description' => 'homepage for the organization'],
+ 'callback_url' => ['type' => 'varchar', 'length' => 191, 'description' => 'url to redirect to after authentication'],
+ 'type' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'type of app, 1 = browser, 2 = desktop'],
+ 'access_type' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'default access type, bit 1 = read, bit 2 = write'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'oauth_application_name_key' => ['name'], // in the long run, we should perhaps not force these unique, and use another source id
+ ],
+ 'foreign keys' => [
+ 'oauth_application_owner_fkey' => ['profile', ['owner' => 'id']], // Are remote users allowed to create oauth application records?
+ 'oauth_application_consumer_key_fkey' => ['consumer', ['consumer_key' => 'consumer_key']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/OauthApplicationUser.php b/src/Entity/OauthApplicationUser.php
new file mode 100644
index 0000000000..d576d9cb87
--- /dev/null
+++ b/src/Entity/OauthApplicationUser.php
@@ -0,0 +1,61 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for OAuth Application User
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class OauthApplicationUser
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'oauth_application_user',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'user of the application'],
+ 'application_id' => ['type' => 'int', 'not null' => true, 'description' => 'id of the application'],
+ 'access_type' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'access type, bit 1 = read, bit 2 = write'],
+ 'token' => ['type' => 'varchar', 'length' => 191, 'description' => 'request or access token'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['profile_id', 'application_id'],
+ 'foreign keys' => [
+ 'oauth_application_user_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ 'oauth_application_user_application_id_fkey' => ['oauth_application', ['application_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/OauthTokenAssociation.php b/src/Entity/OauthTokenAssociation.php
new file mode 100644
index 0000000000..9987c143c4
--- /dev/null
+++ b/src/Entity/OauthTokenAssociation.php
@@ -0,0 +1,61 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for association between OAuth and internal token
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class OauthTokenAssociation
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'oauth_token_association',
+ 'description' => 'Associate an application ID and profile ID with an OAuth token',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'associated user'],
+ 'application_id' => ['type' => 'int', 'not null' => true, 'description' => 'the application'],
+ 'token' => ['type' => 'varchar', 'length' => '191', 'not null' => true, 'description' => 'token used for this association'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['profile_id', 'application_id', 'token'],
+ 'foreign keys' => [
+ 'oauth_token_association_profile_fkey' => ['profile', ['profile_id' => 'id']],
+ 'oauth_token_association_application_fkey' => ['oauth_application', ['application_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Oauth_application.php b/src/Entity/Oauth_application.php
deleted file mode 100644
index aa43038806..0000000000
--- a/src/Entity/Oauth_application.php
+++ /dev/null
@@ -1,209 +0,0 @@
-.
-
-/**
- * Table Definition for oauth_application
- */
-
-defined('GNUSOCIAL') || die();
-
-class Oauth_application extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'oauth_application'; // table name
- public $id; // int(4) primary_key not_null
- public $owner; // int(4) not_null
- public $consumer_key; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $description; // varchar(191) not 255 because utf8mb4 takes more space
- public $icon; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $source_url; // varchar(191) not 255 because utf8mb4 takes more space
- public $organization; // varchar(191) not 255 because utf8mb4 takes more space
- public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
- public $callback_url; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $type; // tinyint(1)
- public $access_type; // tinyint(1)
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- // Bit flags
- public static $readAccess = 1;
- public static $writeAccess = 2;
-
- public static $browser = 1;
- public static $desktop = 2;
-
- public function getConsumer()
- {
- return Consumer::getKV('consumer_key', $this->consumer_key);
- }
-
- public static function maxDesc()
- {
- // This used to default to textlimit or allow unlimited descriptions,
- // but this isn't part of a notice and the field's limited to 191 chars
- // in the DB, so those seem silly. (utf8mb4 takes up more space, so can't use 255)
- //
- // Now just defaulting to 191 max unless a smaller application desclimit
- // is actually set. Setting to 0 will use the maximum.
- $max = 191;
- $desclimit = intval(common_config('application', 'desclimit'));
- if ($desclimit > 0 && $desclimit < $max) {
- return $desclimit;
- } else {
- return $max;
- }
- }
-
- public static function descriptionTooLong($desc)
- {
- $desclimit = self::maxDesc();
- return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
- }
-
- public function setAccessFlags($read, $write)
- {
- if ($read) {
- $this->access_type |= self::$readAccess;
- } else {
- $this->access_type &= ~self::$readAccess;
- }
-
- if ($write) {
- $this->access_type |= self::$writeAccess;
- } else {
- $this->access_type &= ~self::$writeAccess;
- }
- }
-
- public function setOriginal($filename)
- {
- $imagefile = new ImageFile(null, Avatar::path($filename));
-
- // XXX: Do we want to have a bunch of different size icons? homepage, stream, mini?
- // or just one and control size via CSS? --Zach
-
- $orig = clone($this);
- $this->icon = Avatar::url($filename);
- common_debug(common_log_objstring($this));
- return $this->update($orig);
- }
-
- public static function getByConsumerKey($key)
- {
- if (empty($key)) {
- return null;
- }
-
- $app = new Oauth_application();
- $app->consumer_key = $key;
- $app->limit(1);
- $result = $app->find(true);
-
- return empty($result) ? null : $app;
- }
-
- /**
- * Handle an image upload
- *
- * Does all the magic for handling an image upload, and crops the
- * image by default.
- *
- * @return void
- */
- public function uploadLogo()
- {
- if ($_FILES['app_icon']['error'] == UPLOAD_ERR_OK) {
- try {
- $imagefile = ImageFile::fromUpload('app_icon');
- } catch (Exception $e) {
- common_debug("damn that sucks");
- $this->showForm($e->getMessage());
- return;
- }
-
- $filename = Avatar::filename(
- $this->id,
- image_type_to_extension($imagefile->type),
- null,
- 'oauth-app-icon-' . common_timestamp()
- );
-
- $filepath = Avatar::path($filename);
-
- move_uploaded_file($imagefile->filepath, $filepath);
-
- $this->setOriginal($filename);
- }
- }
-
- public function delete($useWhere = false)
- {
- $this->deleteAppUsers();
-
- $consumer = $this->getConsumer();
- $consumer->delete();
-
- return parent::delete($useWhere);
- }
-
- private function deleteAppUsers()
- {
- $oauser = new Oauth_application_user();
- $oauser->application_id = $this->id;
- $oauser->delete();
- }
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'OAuth application registration record',
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'owner' => array('type' => 'int', 'not null' => true, 'description' => 'owner of the application'),
- 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application consumer key'),
- 'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'),
- 'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the application'),
- 'icon' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'default' => '/theme/base/default-avatar-stream.png', 'description' => 'application icon'),
- 'source_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'application homepage - used for source link'),
- 'organization' => array('type' => 'varchar', 'length' => 191, 'description' => 'name of the organization running the application'),
- 'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage for the organization'),
- 'callback_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'url to redirect to after authentication'),
- 'type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'type of app, 1 = browser, 2 = desktop'),
- 'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'default access type, bit 1 = read, bit 2 = write'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'oauth_application_name_key' => array('name'), // in the long run, we should perhaps not force these unique, and use another source id
- ),
- 'foreign keys' => array(
- 'oauth_application_owner_fkey' => array('profile', array('owner' => 'id')), // Are remote users allowed to create oauth application records?
- 'oauth_application_consumer_key_fkey' => array('consumer', array('consumer_key' => 'consumer_key')),
- ),
- 'indexes' => array(
- 'oauth_application_owner_idx' => array('owner'),
- 'oauth_application_consumer_key_idx' => array('consumer_key'),
- ),
- );
- }
-}
diff --git a/src/Entity/Oauth_application_user.php b/src/Entity/Oauth_application_user.php
deleted file mode 100644
index 2ca199683d..0000000000
--- a/src/Entity/Oauth_application_user.php
+++ /dev/null
@@ -1,107 +0,0 @@
-.
-
-/**
- * Table Definition for oauth_application_user
- */
-
-defined('GNUSOCIAL') || die();
-
-class Oauth_application_user extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'oauth_application_user'; // table name
- public $profile_id; // int(4) primary_key not_null
- public $application_id; // int(4) primary_key not_null
- public $access_type; // tinyint(1)
- public $token; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user of the application'),
- 'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'id of the application'),
- 'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'access type, bit 1 = read, bit 2 = write'),
- 'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'request or access token'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('profile_id', 'application_id'),
- 'foreign keys' => array(
- 'oauth_application_user_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- 'oauth_application_user_application_id_fkey' => array('oauth_application', array('application_id' => 'id')),
- ),
- 'indexes' => array(
- 'oauth_application_user_application_id_idx' => array('application_id'),
- ),
- );
- }
-
- public static function getByUserAndToken($user, $token)
- {
- if (empty($user) || empty($token)) {
- return null;
- }
-
- $oau = new Oauth_application_user();
-
- $oau->profile_id = $user->id;
- $oau->token = $token;
- $oau->limit(1);
-
- $result = $oau->find(true);
-
- return empty($result) ? null : $oau;
- }
-
- public function updateKeys(&$orig)
- {
- $this->_connect();
- $parts = array();
- foreach (array('profile_id', 'application_id', 'token', 'access_type') as $k) {
- if (strcmp($this->$k, $orig->$k) != 0) {
- $parts[] = $k . ' = ' . $this->_quote($this->$k);
- }
- }
- if (count($parts) == 0) {
- // No changes
- return true;
- }
- $toupdate = implode(', ', $parts);
- $toupdate .= ', modified = CURRENT_TIMESTAMP';
-
- $table = $this->tableName();
- $tableName = $this->escapedTableName();
- $qry = 'UPDATE ' . $tableName . ' SET ' . $toupdate .
- ' WHERE profile_id = ' . $orig->profile_id .
- ' AND application_id = ' . $orig->application_id .
- " AND token = '" . $orig->token . "'";
- $orig->decache();
- $result = $this->query($qry);
- if ($result) {
- $this->encache();
- }
- return $result;
- }
-}
diff --git a/src/Entity/Oauth_token_association.php b/src/Entity/Oauth_token_association.php
deleted file mode 100644
index 02191d8c10..0000000000
--- a/src/Entity/Oauth_token_association.php
+++ /dev/null
@@ -1,76 +0,0 @@
-.
-
-/**
- * Table Definition for oauth_association
- */
-
-defined('GNUSOCIAL') || die();
-
-class Oauth_token_association extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'oauth_token_association'; // table name
- public $profile_id; // int(4) primary_key not_null
- public $application_id; // int(4) primary_key not_null
- public $token; // varchar(191) primary key not null not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function getByUserAndToken($user, $token)
- {
- if (empty($user) || empty($token)) {
- return null;
- }
-
- $oau = new oauth_request_token();
-
- $oau->profile_id = $user->id;
- $oau->token = $token;
- $oau->limit(1);
-
- $result = $oau->find(true);
-
- return empty($result) ? null : $oau;
- }
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Associate an application ID and profile ID with an OAuth token',
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'associated user'),
- 'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'the application'),
- 'token' => array('type' => 'varchar', 'length' => '191', 'not null' => true, 'description' => 'token used for this association'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('profile_id', 'application_id', 'token'),
- 'foreign keys' => array(
- 'oauth_token_association_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- 'oauth_token_association_application_id_fkey' => array('oauth_application', array('application_id' => 'id')),
- ),
- 'indexes' => array(
- 'oauth_token_association_application_id_idx' => array('application_id'),
- ),
- );
- }
-}
diff --git a/src/Entity/OldSchoolPrefs.php b/src/Entity/OldSchoolPrefs.php
new file mode 100644
index 0000000000..c543f1c315
--- /dev/null
+++ b/src/Entity/OldSchoolPrefs.php
@@ -0,0 +1,66 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Separate table for storing UI preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @deprecated
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class OldSchoolPrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'old_school_prefs',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user who has the preference'],
+ 'stream_mode_only' => ['type' => 'bool',
+ 'default' => true,
+ 'description' => 'No conversation streams', ],
+ 'conversation_tree' => ['type' => 'bool',
+ 'default' => true,
+ 'description' => 'Hierarchical tree view for conversations', ],
+ 'stream_nicknames' => ['type' => 'bool',
+ 'default' => true,
+ 'description' => 'Show nicknames for authors and addressees in streams', ],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id'],
+ 'foreign keys' => [
+ 'old_school_prefs_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Old_school_prefs.php b/src/Entity/Old_school_prefs.php
deleted file mode 100644
index cf62acd752..0000000000
--- a/src/Entity/Old_school_prefs.php
+++ /dev/null
@@ -1,69 +0,0 @@
-.
-
-/**
- * Older-style UI preferences
- *
- * @category UI
- * @package GNUsocial
- * @author Evan Prodromou
- * @copyright 2011 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Separate table for storing UI preferences
- *
- * @copyright 2011 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-class Old_school_prefs extends Managed_DataObject
-{
- public $__table = 'old_school_prefs'; // table name
- public $user_id;
- public $stream_mode_only;
- public $conversation_tree;
- public $stream_nicknames;
- public $created;
- public $modified;
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who has the preference'),
- 'stream_mode_only' => array('type' => 'bool',
- 'default' => true,
- 'description' => 'No conversation streams'),
- 'conversation_tree' => array('type' => 'bool',
- 'default' => true,
- 'description' => 'Hierarchical tree view for conversations'),
- 'stream_nicknames' => array('type' => 'bool',
- 'default' => true,
- 'description' => 'Show nicknames for authors and addressees in streams'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id'),
- 'foreign keys' => array(
- 'old_school_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- );
- }
-}
diff --git a/src/Entity/Profile.php b/src/Entity/Profile.php
deleted file mode 100644
index 34823c26d8..0000000000
--- a/src/Entity/Profile.php
+++ /dev/null
@@ -1,1871 +0,0 @@
-.
-
-/**
- * Table Definition for profile
- *
- * @copyright 2008-2011 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile extends Managed_DataObject
-{
- public $__table = 'profile'; // table name
- public $id; // int(4) primary_key not_null
- public $nickname; // varchar(64) multiple_key not_null
- public $fullname; // text()
- public $profileurl; // text()
- public $homepage; // text()
- public $bio; // text() multiple_key
- public $location; // text()
- public $lat; // decimal(10,7)
- public $lon; // decimal(10,7)
- public $location_id; // int(4)
- public $location_ns; // int(4)
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- $def = array(
- 'description' => 'local and remote users have profiles',
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'nickname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8_general_ci'),
- 'fullname' => array('type' => 'text', 'description' => 'display name', 'collate' => 'utf8_general_ci'),
- 'profileurl' => array('type' => 'text', 'description' => 'URL, cached so we dont regenerate'),
- 'homepage' => array('type' => 'text', 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
- 'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8_general_ci'),
- 'location' => array('type' => 'text', 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
- 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
- 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
- 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
- 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- 'indexes' => array(
- 'profile_nickname_created_id_idx' => array('nickname', 'created', 'id'),
- ),
- 'fulltext indexes' => array(
- 'profile_fulltext_idx' => array('nickname', 'fullname', 'location', 'bio', 'homepage'),
- ),
- );
-
- return $def;
- }
-
- public static function getByEmail($email)
- {
- // in the future, profiles should have emails stored...
- $user = User::getKV('email', $email);
- if (!($user instanceof User)) {
- throw new NoSuchUserException(array('email'=>$email));
- }
- return $user->getProfile();
- }
-
- protected $_user = array();
-
- public function getUser()
- {
- if (!isset($this->_user[$this->id])) {
- $cur_user = common_current_user();
- if (($cur_user instanceof User) && $cur_user->sameAs($this)) {
- $user = $cur_user;
- } else {
- $user = User::getKV('id', $this->id);
- if (!$user instanceof User) {
- throw new NoSuchUserException(array('id'=>$this->id));
- }
- }
- $this->_user[$this->id] = $user;
- }
- return $this->_user[$this->id];
- }
-
- protected $_group = array();
-
- public function getGroup()
- {
- if (!isset($this->_group[$this->id])) {
- $group = User_group::getKV('profile_id', $this->id);
- if (!$group instanceof User_group) {
- throw new NoSuchGroupException(array('profile_id'=>$this->id));
- }
- $this->_group[$this->id] = $group;
- }
- return $this->_group[$this->id];
- }
-
- public function isGroup()
- {
- try {
- $this->getGroup();
- return true;
- } catch (NoSuchGroupException $e) {
- return false;
- }
- }
-
- public function isPerson()
- {
- // Maybe other things than PERSON and GROUP can have Profiles in the future?
- return !$this->isGroup();
- }
-
- public function isLocal()
- {
- try {
- $this->getUser();
- } catch (NoSuchUserException $e) {
- return false;
- }
- return true;
- }
-
- // Returns false if the user has no password (which will always
- // be the case for remote users). This can be the case for OpenID
- // logins or other mechanisms which don't store a password hash.
- public function hasPassword()
- {
- try {
- return $this->getUser()->hasPassword();
- } catch (NoSuchUserException $e) {
- return false;
- }
- }
-
- public function getObjectType()
- {
- // FIXME: More types... like peopletags and whatever
- if ($this->isGroup()) {
- return ActivityObject::GROUP;
- } else {
- return ActivityObject::PERSON;
- }
- }
-
- public function getAvatar($width, $height=null)
- {
- return Avatar::byProfile($this, $width, $height);
- }
-
- public function setOriginal($filename)
- {
- if ($this->isGroup()) {
- // Until Group avatars are handled just like profile avatars.
- return $this->getGroup()->setOriginal($filename);
- }
-
- $imagefile = new ImageFile(null, Avatar::path($filename));
-
- $avatar = new Avatar();
- $avatar->profile_id = $this->id;
- $avatar->width = $imagefile->width;
- $avatar->height = $imagefile->height;
- $avatar->mediatype = image_type_to_mime_type($imagefile->type);
- $avatar->filename = $filename;
- $avatar->original = true;
- $avatar->created = common_sql_now();
-
- // XXX: start a transaction here
- if (!Avatar::deleteFromProfile($this, true) || !$avatar->insert()) {
- // If we can't delete the old avatars, let's abort right here.
- @unlink(Avatar::path($filename));
- return null;
- }
-
- return $avatar;
- }
-
- /**
- * Gets either the full name (if filled) or the nickname.
- *
- * @return string
- */
- public function getBestName()
- {
- return ($this->fullname) ? $this->fullname : $this->nickname;
- }
-
- /**
- * Takes the currently scoped profile into account to give a name
- * to list in notice streams. Preferences may differ between profiles.
- */
- public function getStreamName()
- {
- $user = common_current_user();
- if ($user instanceof User && $user->streamNicknames()) {
- return $this->nickname;
- }
-
- return $this->getBestName();
- }
-
- /**
- * Gets the full name (if filled) with acct URI, URL, or URI as a
- * parenthetical (in that order, for each not found). If no full
- * name is found only the second part is returned, without ()s.
- *
- * @return string
- */
- public function getFancyName()
- {
- $uri = null;
- try {
- $uri = $this->getAcctUri(false);
- } catch (ProfileNoAcctUriException $e) {
- try {
- $uri = $this->getUrl();
- } catch (InvalidUrlException $e) {
- $uri = $this->getUri();
- }
- }
-
- if (mb_strlen($this->getFullname()) > 0) {
- // TRANS: The "fancy name": Full name of a profile or group (%1$s) followed by some URI (%2$s) in parentheses.
- return sprintf(_m('FANCYNAME', '%1$s (%2$s)'), $this->getFullname(), $uri);
- } else {
- return $uri;
- }
- }
-
- /**
- * Get the most recent notice posted by this user, if any.
- *
- * @return mixed Notice or null
- */
- public function getCurrentNotice(Profile $scoped = null)
- {
- try {
- $notice = $this->getNotices(0, 1, 0, 0, $scoped);
-
- if ($notice->fetch()) {
- if ($notice instanceof ArrayWrapper) {
- // hack for things trying to work with single notices
- // ...but this shouldn't happen anymore I think. Keeping it for safety...
- return $notice->_items[0];
- }
- return $notice;
- }
- } catch (PrivateStreamException $e) {
- // Maybe we should let this through if it's handled well upstream
- return null;
- }
-
- return null;
- }
-
- public function getReplies($offset = 0, $limit = NOTICES_PER_PAGE, $since_id = 0, $before_id = 0)
- {
- return Reply::stream($this->getID(), $offset, $limit, $since_id, $before_id);
- }
-
- public function getTaggedNotices($tag, $offset = 0, $limit = NOTICES_PER_PAGE, $since_id = 0, $max_id = 0)
- {
- //FIXME: Get Profile::current() some other way to avoid possible
- // confusion between current session profile and background processing.
- $stream = new TaggedProfileNoticeStream($this, $tag, Profile::current());
-
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- public function getNotices($offset = 0, $limit = NOTICES_PER_PAGE, $since_id = 0, $max_id = 0, Profile $scoped = null)
- {
- $stream = new ProfileNoticeStream($this, $scoped);
-
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- public function isMember(User_group $group)
- {
- $groups = $this->getGroups(0, null);
- while ($groups instanceof User_group && $groups->fetch()) {
- if ($groups->id == $group->id) {
- return true;
- }
- }
- return false;
- }
-
- public function isAdmin(User_group $group)
- {
- $gm = Group_member::pkeyGet(array('profile_id' => $this->id,
- 'group_id' => $group->id));
- return (!empty($gm) && $gm->is_admin);
- }
-
- public function isPendingMember($group)
- {
- $request = Group_join_queue::pkeyGet(array('profile_id' => $this->id,
- 'group_id' => $group->id));
- return !empty($request);
- }
-
- public function getGroups($offset = 0, $limit = PROFILES_PER_PAGE)
- {
- $ids = array();
-
- $keypart = sprintf('profile:groups:%d', $this->id);
-
- $idstring = self::cacheGet($keypart);
-
- if ($idstring !== false) {
- $ids = explode(',', $idstring);
- } else {
- $gm = new Group_member();
-
- $gm->profile_id = $this->id;
- $gm->orderBy('created DESC, group_id DESC');
-
- if ($gm->find()) {
- while ($gm->fetch()) {
- $ids[] = $gm->group_id;
- }
- }
-
- self::cacheSet($keypart, implode(',', $ids));
- }
-
- if (!is_null($offset) && !is_null($limit)) {
- $ids = array_slice($ids, $offset, $limit);
- }
-
- try {
- return User_group::multiGet('id', $ids);
- } catch (NoResultException $e) {
- return null; // throw exception when we handle it everywhere
- }
- }
-
- public function getGroupCount()
- {
- $groups = $this->getGroups(0, null);
- return $groups instanceof User_group
- ? $groups->N
- : 0;
- }
-
- public function isTagged($peopletag)
- {
- $tag = Profile_tag::pkeyGet(array('tagger' => $peopletag->tagger,
- 'tagged' => $this->id,
- 'tag' => $peopletag->tag));
- return !empty($tag);
- }
-
- public function canTag($tagged)
- {
- if (empty($tagged)) {
- return false;
- }
-
- if ($tagged->id == $this->id) {
- return true;
- }
-
- $all = common_config('peopletag', 'allow_tagging', 'all');
- $local = common_config('peopletag', 'allow_tagging', 'local');
- $remote = common_config('peopletag', 'allow_tagging', 'remote');
- $subs = common_config('peopletag', 'allow_tagging', 'subs');
-
- if ($all) {
- return true;
- }
-
- $tagged_user = $tagged->getUser();
- if (!empty($tagged_user)) {
- if ($local) {
- return true;
- }
- } elseif ($subs) {
- return (Subscription::exists($this, $tagged) ||
- Subscription::exists($tagged, $this));
- } elseif ($remote) {
- return true;
- }
- return false;
- }
-
- public function getLists(Profile $scoped = null, $offset = 0, $limit = null, $since_id = 0, $max_id = 0)
- {
- $ids = array();
-
- $keypart = sprintf('profile:lists:%d', $this->id);
-
- $idstr = self::cacheGet($keypart);
-
- if ($idstr !== false) {
- $ids = explode(',', $idstr);
- } else {
- $list = new Profile_list();
- $list->selectAdd();
- $list->selectAdd('id');
- $list->tagger = $this->id;
- $list->selectAdd('id as "cursor"');
-
- if ($since_id > 0) {
- $list->whereAdd('id > ' . $since_id);
- }
-
- if ($max_id > 0) {
- $list->whereAdd('id <= ' . $max_id);
- }
-
- if ($offset >= 0 && !is_null($limit)) {
- $list->limit($offset, $limit);
- }
-
- $list->orderBy('id DESC');
-
- if ($list->find()) {
- while ($list->fetch()) {
- $ids[] = $list->id;
- }
- }
-
- self::cacheSet($keypart, implode(',', $ids));
- }
-
- $showPrivate = $this->sameAs($scoped);
-
- $lists = array();
-
- foreach ($ids as $id) {
- $list = Profile_list::getKV('id', $id);
- if (!empty($list) &&
- ($showPrivate || !$list->private)) {
- if (!isset($list->cursor)) {
- $list->cursor = $list->id;
- }
-
- $lists[] = $list;
- }
- }
-
- return new ArrayWrapper($lists);
- }
-
- /**
- * Get tags that other people put on this profile, in reverse-chron order
- *
- * @param Profile $scoped User we are requesting as
- * @param int $offset Offset from latest
- * @param int $limit Max number to get
- * @param datetime $since_id max date
- * @param datetime $max_id min date
- *
- * @return Profile_list resulting lists
- */
-
- public function getOtherTags(Profile $scoped = null, int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
- {
- $list = new Profile_list();
-
- if (common_config('db', 'type') !== 'mysql') {
- $cursor = sprintf(
- '((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
- 'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
- "FROM (profile_tag.modified - TIMESTAMP '1970-01-01 00:00:00')"
- );
- } else {
- // The SQL/Foundation conforming implementation above doesn't work on MariaDB/MySQL
- $cursor = "timestampdiff(SECOND, '1970-01-01', profile_tag.modified) AS `cursor`";
- }
-
- $qry = sprintf(
- 'SELECT profile_list.*, ' . $cursor . ' ' .
- 'FROM profile_tag INNER JOIN profile_list ' .
- 'ON (profile_tag.tagger = profile_list.tagger ' .
- ' AND profile_tag.tag = profile_list.tag) ' .
- 'WHERE profile_tag.tagged = %d ',
- $this->id
- );
-
- if (!is_null($scoped)) {
- $qry .= sprintf(
- 'AND ( profile_list.private IS NOT TRUE ' .
- 'OR ( profile_list.tagger = %d AND ' .
- 'profile_list.private IS TRUE ) )',
- $scoped->getID()
- );
- } else {
- $qry .= 'AND profile_list.private IS NOT TRUE ';
- }
-
- if ($since > 0) {
- $qry .= 'AND cursor > ' . $since . ' ';
- }
-
- if ($upto > 0) {
- $qry .= 'AND cursor < ' . $upto . ' ';
- }
-
- $qry .= 'ORDER BY profile_tag.modified DESC, profile_tag.tagged DESC ';
-
- if ($offset >= 0 && !is_null($limit)) {
- $qry .= sprintf('LIMIT %d OFFSET %d ', $limit, $offset);
- }
-
- $list->query($qry);
- return $list;
- }
-
- public function getPrivateTags($offset = 0, $limit = null, $since_id = 0, $max_id = 0)
- {
- $tags = new Profile_list();
- $tags->private = true;
- $tags->tagger = $this->id;
-
- if ($since_id > 0) {
- $tags->whereAdd('id > ' . $since_id);
- }
-
- if ($max_id > 0) {
- $tags->whereAdd('id <= ' . $max_id);
- }
-
- if ($offset >= 0 && !is_null($limit)) {
- $tags->limit($offset, $limit);
- }
-
- $tags->orderBy('id DESC');
- $tags->find();
-
- return $tags;
- }
-
- public function hasLocalTags()
- {
- $tags = new Profile_tag();
-
- $tags->joinAdd(array('tagger', 'user:id'));
- $tags->whereAdd('tagged = ' . $this->id);
- $tags->whereAdd('tagger <> ' . $this->id);
-
- $tags->limit(0, 1);
- $tags->fetch();
-
- return ($tags->N == 0) ? false : true;
- }
-
- public function getTagSubscriptions(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
- {
- $lists = new Profile_list();
-
- $lists->joinAdd(['id', 'profile_tag_subscription:profile_tag_id']);
-
- if (common_config('db', 'type') !== 'mysql') {
- $lists->selectAdd(sprintf(
- '((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
- 'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
- "FROM (profile_tag_subscription.created - TIMESTAMP '1970-01-01 00:00:00')"
- ));
- } else {
- $lists->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag_subscription.created) AS `cursor`");
- }
-
- $lists->whereAdd('profile_tag_subscription.profile_id = '.$this->id);
-
- if ($since > 0) {
- $lists->whereAdd('cursor > ' . $since);
- }
-
- if ($upto > 0) {
- $lists->whereAdd('cursor <= ' . $upto);
- }
-
- if ($offset >= 0 && !is_null($limit)) {
- $lists->limit($offset, $limit);
- }
-
- $lists->orderBy('profile_tag_subscription.created DESC, profile_list.id DESC');
- $lists->find();
-
- return $lists;
- }
-
- /**
- * Request to join the given group.
- * May throw exceptions on failure.
- *
- * @param User_group $group
- * @return mixed: Group_member on success, Group_join_queue if pending approval, null on some cancels?
- */
- public function joinGroup(User_group $group)
- {
- $join = null;
- if ($group->join_policy == User_group::JOIN_POLICY_MODERATE) {
- $join = Group_join_queue::saveNew($this, $group);
- } else {
- if (Event::handle('StartJoinGroup', array($group, $this))) {
- $join = Group_member::join($group->id, $this->id);
- self::blow('profile:groups:%d', $this->id);
- self::blow('group:member_ids:%d', $group->id);
- self::blow('group:member_count:%d', $group->id);
- Event::handle('EndJoinGroup', array($group, $this));
- }
- }
- if ($join) {
- // Send any applicable notifications...
- $join->notify();
- }
- return $join;
- }
-
- /**
- * Leave a group that this profile is a member of.
- *
- * @param User_group $group
- */
- public function leaveGroup(User_group $group)
- {
- if (Event::handle('StartLeaveGroup', array($group, $this))) {
- Group_member::leave($group->id, $this->id);
- self::blow('profile:groups:%d', $this->id);
- self::blow('group:member_ids:%d', $group->id);
- self::blow('group:member_count:%d', $group->id);
- Event::handle('EndLeaveGroup', array($group, $this));
- }
- }
-
- public function avatarUrl($size = AVATAR_PROFILE_SIZE)
- {
- return Avatar::urlByProfile($this, $size);
- }
-
- public function getSubscribed($offset = 0, $limit = null)
- {
- $subs = Subscription::getSubscribedIDs($this->id, $offset, $limit);
- try {
- $profiles = Profile::multiGet('id', $subs);
- } catch (NoResultException $e) {
- return $e->obj;
- }
- return $profiles;
- }
-
- public function getSubscribers($offset = 0, $limit = null)
- {
- $subs = Subscription::getSubscriberIDs($this->id, $offset, $limit);
- try {
- $profiles = Profile::multiGet('id', $subs);
- } catch (NoResultException $e) {
- return $e->obj;
- }
- return $profiles;
- }
-
- public function getTaggedSubscribers($tag, $offset = 0, $limit = null)
- {
- $profile = new Profile();
-
- $qry = <<getID()}
- AND profile_tag.tag = '{$profile->escape($tag)}'
- AND subscription.subscribed <> subscription.subscriber
- ORDER BY subscription.created DESC, subscription.subscriber DESC
- END;
-
- if ($offset) {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- }
-
- $cnt = $profile->query($qry);
-
- return $profile;
- }
-
- public function getTaggedSubscriptions($tag, $offset = 0, $limit = null)
- {
- $profile = new Profile();
-
- $qry = <<getID()}
- AND profile_tag.tag = '{$profile->escape($tag)}'
- AND subscription.subscribed <> subscription.subscriber
- ORDER BY subscription.created DESC, subscription.subscribed DESC
- END;
-
- if ($offset) {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- }
-
- $profile->query($qry);
-
- return $profile;
- }
-
- /**
- * Get pending subscribers, who have not yet been approved.
- *
- * @param int $offset
- * @param int $limit
- * @return Profile
- */
- public function getRequests($offset = 0, $limit = null)
- {
- // FIXME: mysql only
- $subqueue = new Profile();
- $subqueue->joinAdd(array('id', 'subscription_queue:subscriber'));
- $subqueue->whereAdd(sprintf('subscription_queue.subscribed = %d', $this->getID()));
- $subqueue->limit($offset, $limit);
- $subqueue->orderBy(
- 'subscription_queue.created DESC, subscription_queue.subscriber DESC'
- );
- if (!$subqueue->find()) {
- throw new NoResultException($subqueue);
- }
- return $subqueue;
- }
-
- public function subscriptionCount()
- {
- $c = Cache::instance();
-
- if (!empty($c)) {
- $cnt = $c->get(Cache::key('profile:subscription_count:'.$this->id));
- if (is_integer($cnt)) {
- return (int) $cnt;
- }
- }
-
- $sub = new Subscription();
- $sub->subscriber = $this->id;
-
- $cnt = (int) $sub->count('distinct subscribed');
-
- // Local users are subscribed to themselves
- if ($this->isLocal()) {
- $cnt = ($cnt > 0) ? $cnt - 1 : $cnt;
- }
-
- if (!empty($c)) {
- $c->set(Cache::key('profile:subscription_count:'.$this->id), $cnt);
- }
-
- return $cnt;
- }
-
- public function subscriberCount()
- {
- $c = Cache::instance();
- if (!empty($c)) {
- $cnt = $c->get(Cache::key('profile:subscriber_count:'.$this->id));
- if (is_integer($cnt)) {
- return (int) $cnt;
- }
- }
-
- $sub = new Subscription();
- $sub->subscribed = $this->id;
- $sub->whereAdd('subscriber <> subscribed');
- $cnt = (int) $sub->count('DISTINCT subscriber');
-
- if (!empty($c)) {
- $c->set(Cache::key('profile:subscriber_count:'.$this->id), $cnt);
- }
-
- return $cnt;
- }
-
- /**
- * Is this profile subscribed to another profile?
- *
- * @param Profile $other
- * @return boolean
- */
- public function isSubscribed(Profile $other)
- {
- return Subscription::exists($this, $other);
- }
-
- public function readableBy(Profile $other = null)
- {
- // If it's not a private stream, it's readable by anyone
- if (!$this->isPrivateStream()) {
- return true;
- }
-
- // If it's a private stream, $other must be a subscriber to $this
- return is_null($other) ? false : $other->isSubscribed($this);
- }
-
- public function requiresSubscriptionApproval(Profile $other = null): bool
- {
- if (!$this->isLocal()) {
- // We don't know for remote users, and we'll always be able to send
- // the request. Whether it'll work immediately or require moderation
- // can be determined in another function.
- return false;
- }
-
- // Assume that profiles _we_ subscribe to are permitted. Could be made configurable.
- if (!is_null($other) && $this->isSubscribed($other)) {
- return false;
- }
-
- // If the local user either has a private stream (implies the following)
- // or user has a moderation policy for new subscriptions, return true.
- return $this->isPrivateStream() || $this->getUser()->subscribe_policy === User::SUBSCRIBE_POLICY_MODERATE;
- }
-
- /**
- * Check if a pending subscription request is outstanding for this...
- *
- * @param Profile $other
- * @return boolean
- */
- public function hasPendingSubscription(Profile $other)
- {
- return Subscription_queue::exists($this, $other);
- }
-
- /**
- * Are these two profiles subscribed to each other?
- *
- * @param Profile $other
- * @return boolean
- */
- public function mutuallySubscribed(Profile $other)
- {
- return $this->isSubscribed($other) &&
- $other->isSubscribed($this);
- }
-
- public function noticeCount()
- {
- $c = Cache::instance();
-
- if (!empty($c)) {
- $cnt = $c->get(Cache::key('profile:notice_count:'.$this->getID()));
- if (is_integer($cnt)) {
- return (int) $cnt;
- }
- }
-
- $notices = new Notice();
- $notices->profile_id = $this->getID();
- $notices->verb = ActivityVerb::POST;
- $cnt = (int) $notices->count('id'); // Not sure if I imagine this, but 'id' was faster than the defaulting 'uri'?
-
- if (!empty($c)) {
- $c->set(Cache::key('profile:notice_count:'.$this->getID()), $cnt);
- }
-
- return $cnt;
- }
-
- public function blowSubscriberCount()
- {
- $c = Cache::instance();
- if (!empty($c)) {
- $c->delete(Cache::key('profile:subscriber_count:'.$this->id));
- }
- }
-
- public function blowSubscriptionCount()
- {
- $c = Cache::instance();
- if (!empty($c)) {
- $c->delete(Cache::key('profile:subscription_count:'.$this->id));
- }
- }
-
- public function blowNoticeCount()
- {
- $c = Cache::instance();
- if (!empty($c)) {
- $c->delete(Cache::key('profile:notice_count:'.$this->id));
- }
- }
-
- public static function maxBio()
- {
- $biolimit = common_config('profile', 'biolimit');
- // null => use global limit (distinct from 0!)
- if (is_null($biolimit)) {
- $biolimit = common_config('site', 'textlimit');
- }
- return $biolimit;
- }
-
- public static function bioTooLong($bio)
- {
- $biolimit = self::maxBio();
- return ($biolimit > 0 && !empty($bio) && (mb_strlen($bio) > $biolimit));
- }
-
- public function update($dataObject = false)
- {
- if (is_object($dataObject) && $this->nickname != $dataObject->nickname) {
- try {
- $local = $this->getUser();
- common_debug("Updating User ({$this->id}) nickname from {$dataObject->nickname} to {$this->nickname}");
- $origuser = clone($local);
- $local->nickname = $this->nickname;
- // updateWithKeys throws exception on failure.
- $local->updateWithKeys($origuser);
-
- // Clear the site owner, in case nickname changed
- if ($local->hasRole(Profile_role::OWNER)) {
- User::blow('user:site_owner');
- }
- } catch (NoSuchUserException $e) {
- // Nevermind...
- }
- }
-
- return parent::update($dataObject);
- }
-
- public function getRelSelf()
- {
- return ['href' => $this->getUrl(),
- 'text' => common_config('site', 'name'),
- 'image' => Avatar::urlByProfile($this)];
- }
-
- // All the known rel="me", used for the IndieWeb audience
- public function getRelMes()
- {
- $relMes = array();
- try {
- $relMes[] = $this->getRelSelf();
- } catch (InvalidUrlException $e) {
- // no valid profile URL available
- }
- if (common_valid_http_url($this->getHomepage())) {
- $relMes[] = ['href' => $this->getHomepage(),
- 'text' => _('Homepage'),
- 'image' => null];
- }
- Event::handle('OtherAccountProfiles', array($this, &$relMes));
- return $relMes;
- }
-
- public function delete($useWhere = false)
- {
- $this->_deleteNotices();
- $this->_deleteSubscriptions();
- $this->_deleteTags();
- $this->_deleteBlocks();
- $this->_deleteAttentions();
- Avatar::deleteFromProfile($this, true);
-
- $this->grantRole(Profile_role::DELETED);
-
- $localuser = User::getKV('id', $this->id);
- if ($localuser instanceof User) {
- $localuser->delete();
- }
-
- // Warning: delete() will run on the batch objects,
- // not on individual objects.
- $related = [
- 'Reply',
- 'Group_member',
- 'Profile_role',
- ];
- Event::handle('ProfileDeleteRelated', array($this, &$related));
-
- foreach ($related as $cls) {
- $inst = new $cls();
- $inst->profile_id = $this->id;
- $inst->delete();
- }
-
- return parent::delete($useWhere);
- }
-
- public function _deleteNotices()
- {
- $notice = new Notice();
- $notice->profile_id = $this->id;
-
- if ($notice->find()) {
- while ($notice->fetch()) {
- $other = clone($notice);
- $other->delete();
- }
- }
- }
-
- public function _deleteSubscriptions()
- {
- $sub = new Subscription();
- $sub->subscriber = $this->getID();
- $sub->find();
-
- while ($sub->fetch()) {
- try {
- $other = $sub->getSubscribed();
- if (!$other->sameAs($this)) {
- Subscription::cancel($this, $other);
- }
- } catch (NoResultException $e) {
- // Profile not found
- common_log(LOG_INFO, 'Subscribed profile id=='.$sub->subscribed.' not found when deleting profile id=='.$this->getID().', ignoring...');
- } catch (ServerException $e) {
- // Subscription cancel failed
- common_log(LOG_INFO, 'Subscribed profile id=='.$other->getID().' could not be reached for unsubscription notice when deleting profile id=='.$this->getID().', ignoring...');
- }
- }
-
- $sub = new Subscription();
- $sub->subscribed = $this->getID();
- $sub->find();
-
- while ($sub->fetch()) {
- try {
- $other = $sub->getSubscriber();
- common_log(LOG_INFO, 'Subscriber profile id=='.$sub->subscribed.' not found when deleting profile id=='.$this->getID().', ignoring...');
- if (!$other->sameAs($this)) {
- Subscription::cancel($other, $this);
- }
- } catch (NoResultException $e) {
- // Profile not found
- common_log(LOG_INFO, 'Subscribed profile id=='.$sub->subscribed.' not found when deleting profile id=='.$this->getID().', ignoring...');
- } catch (ServerException $e) {
- // Subscription cancel failed
- common_log(LOG_INFO, 'Subscriber profile id=='.$other->getID().' could not be reached for unsubscription notice when deleting profile id=='.$this->getID().', ignoring...');
- }
- }
-
- // Finally delete self-subscription
- $self = new Subscription();
- $self->subscriber = $this->getID();
- $self->subscribed = $this->getID();
- $self->delete();
- }
-
- public function _deleteTags()
- {
- $tag = new Profile_tag();
- $tag->tagged = $this->id;
- $tag->delete();
- }
-
- public function _deleteBlocks()
- {
- $block = new Profile_block();
- $block->blocked = $this->id;
- $block->delete();
-
- $block = new Group_block();
- $block->blocked = $this->id;
- $block->delete();
- }
-
- public function _deleteAttentions()
- {
- $att = new Attention();
- $att->profile_id = $this->getID();
-
- if ($att->find()) {
- while ($att->fetch()) {
- // Can't do delete() on the object directly since it won't remove all of it
- $other = clone($att);
- $other->delete();
- }
- }
- }
-
- // XXX: identical to Notice::getLocation.
-
- public function getLocation()
- {
- $location = null;
-
- if (!empty($this->location_id) && !empty($this->location_ns)) {
- $location = Location::fromId($this->location_id, $this->location_ns);
- }
-
- if (is_null($location)) { // no ID, or Location::fromId() failed
- if (!empty($this->lat) && !empty($this->lon)) {
- $location = Location::fromLatLon($this->lat, $this->lon);
- }
- }
-
- if (is_null($location)) { // still haven't found it!
- if (!empty($this->location)) {
- $location = Location::fromName($this->location);
- }
- }
-
- return $location;
- }
-
- public function shareLocation()
- {
- $cfg = common_config('location', 'share');
-
- if ($cfg == 'always') {
- return true;
- } elseif ($cfg == 'never') {
- return false;
- } else { // user
- $share = common_config('location', 'sharedefault');
-
- // Check if user has a personal setting for this
- $prefs = User_location_prefs::getKV('user_id', $this->id);
-
- if (!empty($prefs)) {
- $share = $prefs->share_location;
- $prefs->free();
- }
-
- return $share;
- }
- }
-
- public function hasRole($name)
- {
- $has_role = false;
- if (Event::handle('StartHasRole', array($this, $name, &$has_role))) {
- $role = Profile_role::pkeyGet(array('profile_id' => $this->id,
- 'role' => $name));
- $has_role = !empty($role);
- Event::handle('EndHasRole', array($this, $name, $has_role));
- }
- return $has_role;
- }
-
- public function grantRole($name)
- {
- if (Event::handle('StartGrantRole', array($this, $name))) {
- $role = new Profile_role();
-
- $role->profile_id = $this->id;
- $role->role = $name;
- $role->created = common_sql_now();
-
- $result = $role->update();
- if ($result === 0 || $result === false) {
- $result = $role->insert();
- }
-
- if (!$result) {
- throw new Exception("Can't save role '$name' for profile '{$this->id}'");
- }
-
- if ($name == 'owner') {
- User::blow('user:site_owner');
- }
-
- Event::handle('EndGrantRole', array($this, $name));
- }
-
- return $result;
- }
-
- public function revokeRole($name)
- {
- if (Event::handle('StartRevokeRole', array($this, $name))) {
- $role = Profile_role::pkeyGet(array('profile_id' => $this->id,
- 'role' => $name));
-
- if (empty($role)) {
- // TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
- // TRANS: %1$s is the role name, %2$s is the user ID (number).
- throw new Exception(sprintf(
- _('Cannot revoke role "%1$s" for user #%2$d; does not exist.'),
- $name,
- $this->id
- ));
- }
-
- $result = $role->delete();
-
- if (!$result) {
- common_log_db_error($role, 'DELETE', __FILE__);
- // TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
- // TRANS: %1$s is the role name, %2$s is the user ID (number).
- throw new Exception(sprintf(
- _('Cannot revoke role "%1$s" for user #%2$d; database error.'),
- $name,
- $this->id
- ));
- }
-
- if ($name == 'owner') {
- User::blow('user:site_owner');
- }
-
- Event::handle('EndRevokeRole', array($this, $name));
-
- return true;
- }
- }
-
- public function isSandboxed()
- {
- return $this->hasRole(Profile_role::SANDBOXED);
- }
-
- public function isSilenced()
- {
- return $this->hasRole(Profile_role::SILENCED);
- }
-
- public function sandbox()
- {
- $this->grantRole(Profile_role::SANDBOXED);
- }
-
- public function unsandbox()
- {
- $this->revokeRole(Profile_role::SANDBOXED);
- }
-
- public function silence()
- {
- $this->grantRole(Profile_role::SILENCED);
- if (common_config('notice', 'hidespam')) {
- $this->flushVisibility();
- }
- }
-
- public function silenceAs(Profile $actor)
- {
- if (!$actor->hasRight(Right::SILENCEUSER)) {
- throw new AuthorizationException(_('You cannot silence users on this site.'));
- }
- // Only administrators can silence other privileged users (such as others who have the right to silence).
- if ($this->isPrivileged() && !$actor->hasRole(Profile_role::ADMINISTRATOR)) {
- throw new AuthorizationException(_('You cannot silence other privileged users.'));
- }
- if ($this->isSilenced()) {
- // TRANS: Client error displayed trying to silence an already silenced user.
- throw new AlreadyFulfilledException(_('User is already silenced.'));
- }
- return $this->silence();
- }
-
- public function unsilence()
- {
- $this->revokeRole(Profile_role::SILENCED);
- if (common_config('notice', 'hidespam')) {
- $this->flushVisibility();
- }
- }
-
- public function unsilenceAs(Profile $actor)
- {
- if (!$actor->hasRight(Right::SILENCEUSER)) {
- // TRANS: Client error displayed trying to unsilence a user when the user does not have the right.
- throw new AuthorizationException(_('You cannot unsilence users on this site.'));
- }
- if (!$this->isSilenced()) {
- // TRANS: Client error displayed trying to unsilence a user when the target user has not been silenced.
- throw new AlreadyFulfilledException(_('User is not silenced.'));
- }
- return $this->unsilence();
- }
-
- public function flushVisibility()
- {
- // Get all notices
- $stream = new ProfileNoticeStream($this, $this);
- $ids = $stream->getNoticeIds(0, CachingNoticeStream::CACHE_WINDOW);
- foreach ($ids as $id) {
- self::blow('notice:in-scope-for:%d:null', $id);
- }
- }
-
- public function isPrivileged()
- {
- // TODO: An Event::handle so plugins can report if users are privileged.
- // The ModHelper is the only one I care about when coding this, and that
- // can be tested with Right::SILENCEUSER which I do below:
- switch (true) {
- case $this->hasRight(Right::SILENCEUSER):
- case $this->hasRole(Profile_role::MODERATOR):
- case $this->hasRole(Profile_role::ADMINISTRATOR):
- case $this->hasRole(Profile_role::OWNER):
- return true;
- }
-
- return false;
- }
-
- /**
- * Does this user have the right to do X?
- *
- * With our role-based authorization, this is merely a lookup for whether the user
- * has a particular role. The implementation currently uses a switch statement
- * to determine if the user has the pre-defined role to exercise the right. Future
- * implementations may allow per-site roles, and different mappings of roles to rights.
- *
- * @param $right string Name of the right, usually a constant in class Right
- * @return boolean whether the user has the right in question
- */
- public function hasRight($right)
- {
- $result = false;
-
- if ($this->hasRole(Profile_role::DELETED)) {
- return false;
- }
-
- if (Event::handle('UserRightsCheck', array($this, $right, &$result))) {
- switch ($right) {
- case Right::DELETEOTHERSNOTICE:
- case Right::MAKEGROUPADMIN:
- case Right::SANDBOXUSER:
- case Right::SILENCEUSER:
- case Right::DELETEUSER:
- case Right::DELETEGROUP:
- case Right::TRAINSPAM:
- case Right::REVIEWSPAM:
- $result = $this->hasRole(Profile_role::MODERATOR);
- break;
- case Right::CONFIGURESITE:
- $result = $this->hasRole(Profile_role::ADMINISTRATOR);
- break;
- case Right::GRANTROLE:
- case Right::REVOKEROLE:
- $result = $this->hasRole(Profile_role::OWNER);
- break;
- case Right::NEWNOTICE:
- case Right::NEWMESSAGE:
- case Right::SUBSCRIBE:
- case Right::CREATEGROUP:
- $result = !$this->isSilenced();
- break;
- case Right::PUBLICNOTICE:
- case Right::EMAILONREPLY:
- case Right::EMAILONSUBSCRIBE:
- case Right::EMAILONFAVE:
- $result = !$this->isSandboxed() && !$this->isSilenced();
- break;
- case Right::WEBLOGIN:
- $result = !$this->isSilenced();
- break;
- case Right::API:
- $result = !$this->isSilenced();
- break;
- case Right::BACKUPACCOUNT:
- $result = common_config('profile', 'backup');
- break;
- case Right::RESTOREACCOUNT:
- $result = common_config('profile', 'restore');
- break;
- case Right::DELETEACCOUNT:
- $result = common_config('profile', 'delete');
- break;
- case Right::MOVEACCOUNT:
- $result = common_config('profile', 'move');
- break;
- default:
- $result = false;
- break;
- }
- }
- return $result;
- }
-
- // FIXME: Can't put Notice typing here due to ArrayWrapper
- public function hasRepeated($notice)
- {
- // XXX: not really a pkey, but should work
-
- $notice = Notice::pkeyGet(array('profile_id' => $this->getID(),
- 'repeat_of' => $notice->getID(),
- 'verb' => ActivityVerb::SHARE));
-
- return !empty($notice);
- }
-
- /**
- * Returns an XML string fragment with limited profile information
- * as an Atom element.
- *
- * Assumes that Atom has been previously set up as the base namespace.
- *
- * @param Profile $cur the current authenticated user
- *
- * @return string
- */
- public function asAtomAuthor($cur = null)
- {
- $xs = new XMLStringer(true);
-
- $xs->elementStart('author');
- $xs->element('name', null, $this->nickname);
- $xs->element('uri', null, $this->getUri());
- if ($cur != null) {
- $attrs = [];
- $attrs['following'] = $cur->isSubscribed($this) ? 'true' : 'false';
- $attrs['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false';
- $xs->element('statusnet:profile_info', $attrs, null);
- }
- $xs->elementEnd('author');
-
- return $xs->getString();
- }
-
- /**
- * Extra profile info for atom entries
- *
- * Clients use some extra profile info in the atom stream.
- * This gives it to them.
- *
- * @param Profile $scoped The currently logged in/scoped profile
- *
- * @return array representation of element or null
- */
-
- public function profileInfo(Profile $scoped = null)
- {
- $profileInfoAttr = array('local_id' => $this->id);
-
- if ($scoped instanceof Profile) {
- // Whether the current user is a subscribed to this profile
- $profileInfoAttr['following'] = $scoped->isSubscribed($this) ? 'true' : 'false';
- // Whether the current user is has blocked this profile
- $profileInfoAttr['blocking'] = $scoped->hasBlocked($this) ? 'true' : 'false';
- }
-
- return array('statusnet:profile_info', $profileInfoAttr, null);
- }
-
- /**
- * Returns an XML string fragment with profile information as an
- * Activity Streams element.
- *
- * Assumes that 'activity' namespace has been previously defined.
- *
- * @return string
- */
- public function asActivityActor()
- {
- return $this->asActivityNoun('actor');
- }
-
- /**
- * Returns an XML string fragment with profile information as an
- * Activity Streams noun object with the given element type.
- *
- * Assumes that 'activity', 'georss', and 'poco' namespace has been
- * previously defined.
- *
- * @param string $element one of 'actor', 'subject', 'object', 'target'
- *
- * @return string
- */
- public function asActivityNoun($element)
- {
- $noun = $this->asActivityObject();
- return $noun->asString('activity:' . $element);
- }
-
- public function asActivityObject()
- {
- $object = new ActivityObject();
-
- if (Event::handle('StartActivityObjectFromProfile', array($this, &$object))) {
- $object->type = $this->getObjectType();
- $object->id = $this->getUri();
- $object->title = $this->getBestName();
- $object->link = $this->getUrl();
- $object->summary = $this->getDescription();
-
- try {
- $avatar = Avatar::getUploaded($this);
- $object->avatarLinks[] = AvatarLink::fromAvatar($avatar);
- } catch (NoAvatarException $e) {
- // Could not find an original avatar to link
- }
-
- $sizes = array(
- AVATAR_PROFILE_SIZE,
- AVATAR_STREAM_SIZE,
- AVATAR_MINI_SIZE
- );
-
- foreach ($sizes as $size) {
- $alink = null;
- try {
- $avatar = Avatar::byProfile($this, $size);
- $alink = AvatarLink::fromAvatar($avatar);
- } catch (NoAvatarException $e) {
- $alink = new AvatarLink();
- $alink->type = 'image/png';
- $alink->height = $size;
- $alink->width = $size;
- $alink->url = Avatar::defaultImage($size);
- }
-
- $object->avatarLinks[] = $alink;
- }
-
- if (isset($this->lat) && isset($this->lon)) {
- $object->geopoint = (float)$this->lat
- . ' ' . (float)$this->lon;
- }
-
- $object->poco = PoCo::fromProfile($this);
-
- if ($this->isLocal()) {
- $object->extra[] = array('followers', array('url' => common_local_url('subscribers', array('nickname' => $this->getNickname()))));
- }
-
- Event::handle('EndActivityObjectFromProfile', array($this, &$object));
- }
-
- return $object;
- }
-
- /**
- * Returns the profile's canonical url, not necessarily a uri/unique id
- *
- * @return string $profileurl
- */
- public function getUrl()
- {
- $url = null;
- if ($this->isGroup()) {
- // FIXME: Get rid of this event, it fills no real purpose, data should be in Profile->profileurl (replaces User_group->mainpage)
- if (Event::handle('StartUserGroupHomeUrl', array($this->getGroup(), &$url))) {
- $url = $this->getGroup()->isLocal()
- ? common_local_url('showgroup', array('nickname' => $this->getNickname()))
- : $this->profileurl;
- }
- Event::handle('EndUserGroupHomeUrl', array($this->getGroup(), $url));
- } elseif ($this->isLocal()) {
- $url = common_local_url('showstream', array('nickname' => $this->getNickname()));
- } else {
- $url = $this->profileurl;
- }
- if (empty($url) ||
- !filter_var($url, FILTER_VALIDATE_URL)) {
- throw new InvalidUrlException($url);
- }
- return $url;
- }
- public function getHtmlTitle()
- {
- try {
- return $this->getAcctUri(false);
- } catch (ProfileNoAcctUriException $e) {
- return $this->getNickname();
- }
- }
-
- public function getNickname()
- {
- return $this->nickname;
- }
-
- public function getFullname()
- {
- return $this->fullname;
- }
-
- public function getHomepage()
- {
- return $this->homepage;
- }
-
- public function getDescription()
- {
- return $this->bio;
- }
-
- /**
- * Returns the best URI for a profile. Plugins may override.
- *
- * @return string $uri
- */
- public function getUri()
- {
- $uri = null;
-
- // give plugins a chance to set the URI
- if (Event::handle('StartGetProfileUri', array($this, &$uri))) {
-
- // check for a local user first
- $user = User::getKV('id', $this->id);
- if ($user instanceof User) {
- $uri = $user->getUri();
- } else {
- $group = User_group::getKV('profile_id', $this->id);
- if ($group instanceof User_group) {
- $uri = $group->getUri();
- }
- }
-
- Event::handle('EndGetProfileUri', array($this, &$uri));
- }
-
- return $uri;
- }
-
- /**
- * Returns an assumed acct: URI for a profile. Plugins are required.
- *
- * @return string $uri
- */
- public function getAcctUri($scheme=true)
- {
- $acct = null;
-
- if (Event::handle('StartGetProfileAcctUri', array($this, &$acct))) {
- Event::handle('EndGetProfileAcctUri', array($this, &$acct));
- }
-
- if ($acct === null) {
- throw new ProfileNoAcctUriException($this);
- }
- if (parse_url($acct, PHP_URL_SCHEME) !== 'acct') {
- throw new ServerException('Acct URI does not have acct: scheme');
- }
-
- // if we don't return the scheme, just remove the 'acct:' in the beginning
- return $scheme ? $acct : mb_substr($acct, 5);
- }
-
- public function hasBlocked(Profile $other)
- {
- $block = Profile_block::exists($this, $other);
- return !empty($block);
- }
-
- public function getAtomFeed()
- {
- $feed = null;
-
- if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
- if ($this->isLocal()) {
- $feed = common_local_url('ApiTimelineUser', array('id' => $this->getID(),
- 'format' => 'atom'));
- }
- Event::handle('EndProfileGetAtomFeed', array($this, $feed));
- }
-
- return $feed;
- }
-
- public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
- {
- // TRANS: Exception thrown when trying view "repeated to me".
- throw new Exception(_('Not implemented since inbox change.'));
- }
-
- /*
- * Get a Profile object by URI. Will call external plugins for help
- * using the event StartGetProfileFromURI.
- *
- * @param string $uri A unique identifier for a resource (profile/group/whatever)
- */
- public static function fromUri($uri)
- {
- $profile = null;
-
- if (Event::handle('StartGetProfileFromURI', array($uri, &$profile))) {
- // Get a local user when plugin lookup (like OStatus) fails
- $user = User::getKV('uri', $uri);
- if ($user instanceof User) {
- $profile = $user->getProfile();
- } else {
- $group = User_group::getKV('uri', $uri);
- if ($group instanceof User_group) {
- $profile = $group->getProfile();
- }
- }
- Event::handle('EndGetProfileFromURI', array($uri, $profile));
- }
-
- if (!$profile instanceof Profile) {
- throw new UnknownUriException($uri);
- }
-
- return $profile;
- }
-
- public function canRead(Notice $notice)
- {
- if ($notice->scope & Notice::SITE_SCOPE) {
- $user = $this->getUser();
- if (empty($user)) {
- return false;
- }
- }
-
- if ($notice->scope & Notice::ADDRESSEE_SCOPE) {
- $replies = $notice->getReplies();
-
- if (!in_array($this->id, $replies)) {
- $groups = $notice->getGroups();
-
- $foundOne = false;
-
- foreach ($groups as $group) {
- if ($this->isMember($group)) {
- $foundOne = true;
- break;
- }
- }
-
- if (!$foundOne) {
- return false;
- }
- }
- }
-
- if ($notice->scope & Notice::FOLLOWER_SCOPE) {
- $author = $notice->getProfile();
- if (!Subscription::exists($this, $author)) {
- return false;
- }
- }
-
- return true;
- }
-
- public static function current()
- {
- $user = common_current_user();
- if (empty($user)) {
- $profile = null;
- } else {
- $profile = $user->getProfile();
- }
- return $profile;
- }
-
- public static function ensureCurrent()
- {
- $profile = self::current();
- if (!$profile instanceof Profile) {
- throw new AuthorizationException('A currently scoped profile is required.');
- }
- return $profile;
- }
-
- /**
- * Magic function called at serialize() time.
- *
- * We use this to drop a couple process-specific references
- * from DB_DataObject which can cause trouble in future
- * processes.
- *
- * @return array of variable names to include in serialization.
- */
-
- public function __sleep()
- {
- $vars = parent::__sleep();
- $skip = array('_user', '_group');
- return array_diff($vars, $skip);
- }
-
- public function getProfile()
- {
- return $this;
- }
-
- /**
- * Test whether the given profile is the same as the current class,
- * for testing identities.
- *
- * @param Profile $other The other profile, usually from Action's $this->scoped
- *
- * @return boolean
- */
- public function sameAs(Profile $other=null)
- {
- if (is_null($other)) {
- // In case $this->scoped is null or something, i.e. not a current/legitimate profile.
- return false;
- }
- return $this->getID() === $other->getID();
- }
-
- /**
- * This will perform shortenLinks with the connected User object.
- *
- * Won't work on remote profiles or groups, so expect a
- * NoSuchUserException if you don't know it's a local User.
- *
- * @param string $text String to shorten
- * @param boolean $always Disrespect minimum length etc.
- *
- * @return string link-shortened $text
- */
- public function shortenLinks($text, $always=false)
- {
- return $this->getUser()->shortenLinks($text, $always);
- }
-
- public function isPrivateStream(): bool
- {
- // We only know of public remote users as of yet...
- if (!$this->isLocal()) {
- return false;
- }
- $private_stream = $this->getUser()->private_stream;
- return !is_null($private_stream) && $private_stream;
- }
-
- public function delPref($namespace, $topic)
- {
- return Profile_prefs::setData($this, $namespace, $topic, null);
- }
-
- public function getPref($namespace, $topic, $default = null)
- {
- // If you want an exception to be thrown, call Profile_prefs::getData directly
- try {
- return Profile_prefs::getData($this, $namespace, $topic, $default);
- } catch (NoResultException $e) {
- return null;
- }
- }
-
- // The same as getPref but will fall back to common_config value for the same namespace/topic
- public function getConfigPref($namespace, $topic)
- {
- return Profile_prefs::getConfigData($this, $namespace, $topic);
- }
-
- public function setPref($namespace, $topic, $data)
- {
- return Profile_prefs::setData($this, $namespace, $topic, $data);
- }
-
- public function getConnectedApps($offset=0, $limit=null)
- {
- return $this->getUser()->getConnectedApps($offset, $limit);
- }
-}
diff --git a/src/Entity/ProfileBlock.php b/src/Entity/ProfileBlock.php
new file mode 100644
index 0000000000..56bfd1ffd5
--- /dev/null
+++ b/src/Entity/ProfileBlock.php
@@ -0,0 +1,58 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for User's Profile Block
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfileBlock
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_block',
+ 'fields' => [
+ 'blocker' => ['type' => 'int', 'not null' => true, 'description' => 'user making the block'],
+ 'blocked' => ['type' => 'int', 'not null' => true, 'description' => 'profile that is blocked'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date of blocking'],
+ ],
+ 'foreign keys' => [
+ 'profile_block_blocker_fkey' => ['user', ['blocker' => 'id']],
+ 'profile_block_blocked_fkey' => ['profile', ['blocked' => 'id']],
+ ],
+ 'primary key' => ['blocker', 'blocked'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ProfileList.php b/src/Entity/ProfileList.php
new file mode 100644
index 0000000000..ad5e551e31
--- /dev/null
+++ b/src/Entity/ProfileList.php
@@ -0,0 +1,77 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for List of profiles
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfileList
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_list',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
+ 'tagger' => ['type' => 'int', 'not null' => true, 'description' => 'user making the tag'],
+ 'tag' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'people tag'],
+ 'description' => ['type' => 'text', 'description' => 'description of the people tag'],
+ 'private' => ['type' => 'bool', 'default' => false, 'description' => 'is this tag private'],
+
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date the tag was added'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date the tag was modified'],
+
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'],
+ 'mainpage' => ['type' => 'varchar', 'length' => 191, 'description' => 'page to link to'],
+ 'tagged_count' => ['type' => 'int', 'default' => 0, 'description' => 'number of people tagged with this tag by this user'],
+ 'subscriber_count' => ['type' => 'int', 'default' => 0, 'description' => 'number of subscribers to this tag'],
+ ],
+ 'primary key' => ['tagger', 'tag'],
+ 'unique keys' => [
+ 'profile_list_id_key' => ['id'],
+ ],
+ 'foreign keys' => [
+ 'profile_list_tagger_fkey' => ['profile', ['tagger' => 'id']],
+ ],
+ 'indexes' => [
+ 'profile_list_modified_idx' => ['modified'],
+ 'profile_list_tag_idx' => ['tag'],
+ 'profile_list_tagger_tag_idx' => ['tagger', 'tag'],
+ 'profile_list_tagged_count_idx' => ['tagged_count'],
+ 'profile_list_subscriber_count_idx' => ['subscriber_count'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ProfilePrefs.php b/src/Entity/ProfilePrefs.php
new file mode 100644
index 0000000000..0aa7ffc09f
--- /dev/null
+++ b/src/Entity/ProfilePrefs.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Data class for Profile preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfilePrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_prefs',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'user'],
+ 'namespace' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'],
+ 'topic' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'],
+ 'data' => ['type' => 'blob', 'description' => 'topic data, may be anything'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['profile_id', 'namespace', 'topic'],
+ 'foreign keys' => [
+ 'profile_prefs_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'profile_prefs_profile_id_idx' => ['profile_id'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ProfileRole.php b/src/Entity/ProfileRole.php
new file mode 100644
index 0000000000..bf7f570227
--- /dev/null
+++ b/src/Entity/ProfileRole.php
@@ -0,0 +1,58 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user profile role
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfileRole
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_role',
+ 'fields' => [
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'account having the role'],
+ 'role' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'string representing the role'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date the role was granted'],
+ ],
+ 'primary key' => ['profile_id', 'role'],
+ 'foreign keys' => [
+ 'profile_role_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => ['profile_role_role_created_profile_id_idx' => ['role', 'created', 'profile_id']],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ProfileTag.php b/src/Entity/ProfileTag.php
new file mode 100644
index 0000000000..00ac7b9105
--- /dev/null
+++ b/src/Entity/ProfileTag.php
@@ -0,0 +1,65 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Profile Tag
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfileTag
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_tag',
+
+ 'fields' => [
+ 'tagger' => ['type' => 'int', 'not null' => true, 'description' => 'user making the tag'],
+ 'tagged' => ['type' => 'int', 'not null' => true, 'description' => 'profile tagged'],
+ 'tag' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date the tag was added'],
+ ],
+ 'primary key' => ['tagger', 'tagged', 'tag'],
+ 'foreign keys' => [
+ 'profile_tag_tagger_fkey' => ['profile', ['tagger' => 'id']],
+ 'profile_tag_tagged_fkey' => ['profile', ['tagged' => 'id']],
+ ],
+ 'indexes' => [
+ 'profile_tag_modified_idx' => ['modified'],
+ 'profile_tag_tagger_tag_idx' => ['tagger', 'tag'],
+ 'profile_tag_tagged_idx' => ['tagged'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/ProfileTagSubscription.php b/src/Entity/ProfileTagSubscription.php
new file mode 100644
index 0000000000..b7f5ab8278
--- /dev/null
+++ b/src/Entity/ProfileTagSubscription.php
@@ -0,0 +1,65 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Profile Tag Subscription
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class ProfileTagSubscription
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'profile_tag_subscription',
+ 'fields' => [
+ 'profile_tag_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile_tag'],
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'],
+
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['profile_tag_id', 'profile_id'],
+ 'foreign keys' => [
+ 'profile_tag_subscription_profile_list_id_fkey' => ['profile_list', ['profile_tag_id' => 'id']],
+ 'profile_tag_subscription_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ // @fixme probably we want a (profile_id, created) index here?
+ 'profile_tag_subscription_profile_id_idx' => ['profile_id'],
+ 'profile_tag_subscription_created_idx' => ['created'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Profile_block.php b/src/Entity/Profile_block.php
deleted file mode 100644
index d4e7c83cf2..0000000000
--- a/src/Entity/Profile_block.php
+++ /dev/null
@@ -1,63 +0,0 @@
-.
-
-/*
- * Table Definition for profile_block
- *
- * @copyright 2008, 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile_block extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'profile_block'; // table name
- public $blocker; // int(4) primary_key not_null
- public $blocked; // int(4) primary_key not_null
- public $modified; // datetime() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'blocker' => array('type' => 'int', 'not null' => true, 'description' => 'user making the block'),
- 'blocked' => array('type' => 'int', 'not null' => true, 'description' => 'profile that is blocked'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date of blocking'),
- ),
- 'foreign keys' => array(
- 'profile_block_blocker_fkey' => array('user', array('blocker' => 'id')),
- 'profile_block_blocked_fkey' => array('profile', array('blocked' => 'id')),
- ),
- 'primary key' => array('blocker', 'blocked'),
- 'indexes' => array(
- 'profile_block_blocked_idx' => array('blocked'),
- ),
- );
- }
-
- public static function exists(Profile $blocker, Profile $blocked)
- {
- return Profile_block::pkeyGet(array('blocker' => $blocker->id,
- 'blocked' => $blocked->id));
- }
-}
diff --git a/src/Entity/Profile_list.php b/src/Entity/Profile_list.php
deleted file mode 100644
index e688969e20..0000000000
--- a/src/Entity/Profile_list.php
+++ /dev/null
@@ -1,949 +0,0 @@
-.
-
-/**
- * @category Notices
- * @package GNUsocial
- * @author Shashi Gowda
- * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile_list extends Managed_DataObject
-{
- public $__table = 'profile_list'; // table name
- public $id; // int(4) primary_key not_null
- public $tagger; // int(4)
- public $tag; // varchar(64)
- public $description; // text
- public $private; // bool default_false
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
- public $tagged_count; // smallint
- public $subscriber_count; // smallint
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'tagger' => array('type' => 'int', 'not null' => true, 'description' => 'user making the tag'),
- 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'people tag'),
- 'description' => array('type' => 'text', 'description' => 'description of the people tag'),
- 'private' => array('type' => 'bool', 'default' => false, 'description' => 'is this tag private'),
-
- 'created' => array('type' => 'datetime', 'description' => 'date the tag was added'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was modified'),
-
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
- 'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page to link to'),
- 'tagged_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of people tagged with this tag by this user'),
- 'subscriber_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of subscribers to this tag'),
- ),
- 'primary key' => array('tagger', 'tag'),
- 'unique keys' => array(
- 'profile_list_id_key' => array('id'),
- ),
- 'foreign keys' => array(
- 'profile_list_tagger_fkey' => array('profile', array('tagger' => 'id')),
- ),
- 'indexes' => array(
- 'profile_list_modified_id_idx' => array('modified', 'id'),
- 'profile_list_tag_idx' => array('tag'),
- 'profile_list_tagged_count_idx' => array('tagged_count'),
- 'profile_list_subscriber_count_idx' => array('subscriber_count'),
- ),
- );
- }
-
- /**
- * get the tagger of this profile_list object
- *
- * @return Profile the tagger
- */
-
- public function getTagger()
- {
- return Profile::getByID($this->tagger);
- }
-
- /**
- * return a string to identify this
- * profile_list in the user interface etc.
- *
- * @return String
- */
-
- public function getBestName()
- {
- return $this->tag;
- }
-
- /**
- * return a uri string for this profile_list
- *
- * @return String uri
- */
-
- public function getUri()
- {
- $uri = null;
- if (Event::handle('StartProfiletagGetUri', array($this, &$uri))) {
- if (!empty($this->uri)) {
- $uri = $this->uri;
- } else {
- $uri = common_local_url(
- 'profiletagbyid',
- ['id' => $this->id, 'tagger_id' => $this->tagger]
- );
- }
- }
- Event::handle('EndProfiletagGetUri', array($this, &$uri));
- return $uri;
- }
-
- /**
- * return a url to the homepage of this item
- *
- * @return String home url
- */
-
- public function homeUrl()
- {
- $url = null;
- if (Event::handle('StartUserPeopletagHomeUrl', array($this, &$url))) {
- // normally stored in mainpage, but older ones may be null
- if (!empty($this->mainpage)) {
- $url = $this->mainpage;
- } else {
- $url = common_local_url(
- 'showprofiletag',
- [
- 'nickname' => $this->getTagger()->nickname,
- 'tag' => $this->tag,
- ]
- );
- }
- }
- Event::handle('EndUserPeopletagHomeUrl', array($this, &$url));
- return $url;
- }
-
- /**
- * return an immutable url for this object
- *
- * @return String permalink
- */
-
- public function permalink()
- {
- $url = null;
- if (Event::handle('StartProfiletagPermalink', array($this, &$url))) {
- $url = common_local_url(
- 'profiletagbyid',
- ['id' => $this->id]
- );
- }
- Event::handle('EndProfiletagPermalink', array($this, &$url));
- return $url;
- }
-
- /**
- * Query notices by users associated with this tag,
- * but first check the cache before hitting the DB.
- *
- * @param integer $offset offset
- * @param integer $limit maximum no of results
- * @param integer $since_id=null since this id
- * @param integer $max_id=null maximum id in result
- *
- * @return Notice the query
- */
-
- public function getNotices($offset, $limit, $since_id = null, $max_id = null)
- {
- // FIXME: Use something else than Profile::current() to avoid
- // possible confusion between session user and queue processing.
- $stream = new PeopletagNoticeStream($this, Profile::current());
-
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- /**
- * Get subscribers (local and remote) to this people tag
- * Order by reverse chronology
- *
- * @param integer $offset offset
- * @param integer $limit maximum no of results
- * @param integer $since_id=null since unix timestamp
- * @param integer $upto=null maximum unix timestamp when subscription was made
- *
- * @return Profile results
- */
-
- public function getSubscribers(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
- {
- $subs = new Profile();
-
- $subs->joinAdd(
- array('id', 'profile_tag_subscription:profile_id')
- );
- $subs->whereAdd('profile_tag_subscription.profile_tag_id = ' . $this->id);
-
- if (common_config('db', 'type') !== 'mysql') {
- $subs->selectAdd(sprintf(
- '((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
- 'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
- "FROM (profile_tag_subscription.created - TIMESTAMP '1970-01-01 00:00:00')"
- ));
- } else {
- $subs->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag_subscription.created) AS `cursor`");
- }
-
- if ($since != 0) {
- $subs->whereAdd('cursor > ' . $since);
- }
-
- if ($upto != 0) {
- $subs->whereAdd('cursor <= ' . $upto);
- }
-
- if (!is_null($limit)) {
- $subs->limit($offset, $limit);
- }
-
- $subs->orderBy('profile_tag_subscription.created DESC, profile.id DESC');
- $subs->find();
-
- return $subs;
- }
-
- /**
- * Get all and only local subscribers to this people tag
- * used for distributing notices to user inboxes.
- *
- * @return array ids of users
- */
-
- public function getUserSubscribers()
- {
- // XXX: cache this
-
- $user = new User();
-
- $user->query(sprintf(
- 'SELECT id ' .
- 'FROM %1$s INNER JOIN profile_tag_subscription ' .
- 'ON %1$s.id = profile_tag_subscription.profile_id ' .
- 'WHERE profile_tag_subscription.profile_tag_id = %2$d ',
- $user->escapedTableName(),
- $this->id
- ));
-
- $ids = [];
-
- while ($user->fetch()) {
- $ids[] = $user->id;
- }
-
- $user->free();
-
- return $ids;
- }
-
- /**
- * Check to see if a given profile has
- * subscribed to this people tag's timeline
- *
- * @param mixed $id User or Profile object or integer id
- *
- * @return boolean subscription status
- */
-
- public function hasSubscriber($id)
- {
- if (!is_numeric($id)) {
- $id = $id->id;
- }
-
- $sub = Profile_tag_subscription::pkeyGet(array('profile_tag_id' => $this->id,
- 'profile_id' => $id));
- return !empty($sub);
- }
-
- /**
- * Get profiles tagged with this people tag,
- * include modified timestamp as a "cursor" field
- * order by descending order of modified time
- *
- * @param integer $offset offset
- * @param integer $limit maximum no of results
- * @param integer $since_id=null since unix timestamp
- * @param integer $upto=null maximum unix timestamp when subscription was made
- *
- * @return Profile results
- */
-
- public function getTagged(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
- {
- $tagged = new Profile();
- $tagged->joinAdd(['id', 'profile_tag:tagged']);
-
- if (common_config('db', 'type') !== 'mysql') {
- $tagged->selectAdd(sprintf(
- '((EXTRACT(DAY %1$s) * 24 + EXTRACT(HOUR %1$s)) * 60 + ' .
- 'EXTRACT(MINUTE %1$s)) * 60 + FLOOR(EXTRACT(SECOND %1$s)) AS "cursor"',
- "FROM (profile_tag.modified - TIMESTAMP '1970-01-01 00:00:00')"
- ));
- } else {
- $tagged->selectAdd("timestampdiff(SECOND, '1970-01-01', profile_tag.modified) AS `cursor`");
- }
-
- $tagged->whereAdd('profile_tag.tagger = '.$this->tagger);
- $tagged->whereAdd("profile_tag.tag = '{$this->tag}'");
-
- if ($since != 0) {
- $tagged->whereAdd('cursor > ' . $since);
- }
-
- if ($upto != 0) {
- $tagged->whereAdd('cursor <= ' . $upto);
- }
-
- if (!is_null($limit)) {
- $tagged->limit($offset, $limit);
- }
-
- $tagged->orderBy('profile_tag.modified DESC, profile_tag.tagged DESC');
- $tagged->find();
-
- return $tagged;
- }
-
- /**
- * Gracefully delete one or many people tags
- * along with their members and subscriptions data
- *
- * @return boolean success
- */
-
- public function delete($useWhere = false)
- {
- // force delete one item at a time.
- if (empty($this->id)) {
- $this->find();
- while ($this->fetch()) {
- $this->delete();
- }
- }
-
- Profile_tag::cleanup($this);
- Profile_tag_subscription::cleanup($this);
-
- self::blow('profile:lists:%d', $this->tagger);
-
- return parent::delete($useWhere);
- }
-
- /**
- * Update a people tag gracefully
- * also change "tag" fields in profile_tag table
- *
- * @param Profile_list $dataObject Object's original form
- *
- * @return boolean success
- */
-
- public function update($dataObject = false)
- {
- if (!is_object($dataObject) && !$dataObject instanceof Profile_list) {
- return parent::update($dataObject);
- }
-
- $result = true;
-
- // if original tag was different
- // check to see if the new tag already exists
- // if not, rename the tag correctly
- if ($dataObject->tag != $this->tag || $dataObject->tagger != $this->tagger) {
- $existing = Profile_list::getByTaggerAndTag($this->tagger, $this->tag);
- if (!empty($existing)) {
- // TRANS: Server exception.
- throw new ServerException(_('The tag you are trying to rename ' .
- 'to already exists.'));
- }
- // move the tag
- // XXX: allow OStatus plugin to send out profile tag
- $result = Profile_tag::moveTag($dataObject, $this);
- }
- return parent::update($dataObject);
- }
-
- /**
- * return an xml string representing this people tag
- * as the author of an atom feed
- *
- * @return string atom author element
- */
-
- public function asAtomAuthor()
- {
- $xs = new XMLStringer(true);
-
- $tagger = $this->getTagger();
- $xs->elementStart('author');
- $xs->element('name', null, '@' . $tagger->nickname . '/' . $this->tag);
- $xs->element('uri', null, $this->permalink());
- $xs->elementEnd('author');
-
- return $xs->getString();
- }
-
- /**
- * return an xml string to represent this people tag
- * as a noun in an activitystreams feed.
- *
- * @param string $element the xml tag
- *
- * @return string activitystreams noun
- */
-
- public function asActivityNoun($element)
- {
- $noun = ActivityObject::fromPeopletag($this);
- return $noun->asString('activity:' . $element);
- }
-
- /**
- * get the cached number of profiles tagged with this
- * people tag, re-count if the argument is true.
- *
- * @param boolean $recount whether to ignore cache
- *
- * @return integer count
- */
-
- public function taggedCount($recount = false)
- {
- $keypart = sprintf(
- 'profile_list:tagged_count:%d:%s',
- $this->tagger,
- $this->tag
- );
-
- $count = self::cacheGet($keypart);
-
- if ($count === false) {
- $tags = new Profile_tag();
-
- $tags->tag = $this->tag;
- $tags->tagger = $this->tagger;
-
- $count = $tags->count('distinct tagged');
-
- self::cacheSet($keypart, $count);
- }
-
- return $count;
- }
-
- /**
- * get the cached number of profiles subscribed to this
- * people tag, re-count if the argument is true.
- *
- * @param boolean $recount whether to ignore cache
- *
- * @return integer count
- */
-
- public function subscriberCount($recount = false)
- {
- $keypart = sprintf(
- 'profile_list:subscriber_count:%d',
- $this->id
- );
-
- $count = self::cacheGet($keypart);
-
- if ($count === false) {
- $sub = new Profile_tag_subscription();
- $sub->profile_tag_id = $this->id;
- $count = (int) $sub->count('distinct profile_id');
-
- self::cacheSet($keypart, $count);
- }
-
- return $count;
- }
-
- /**
- * get the cached number of profiles subscribed to this
- * people tag, re-count if the argument is true.
- *
- * @param boolean $recount whether to ignore cache
- *
- * @return integer count
- */
-
- public function blowNoticeStreamCache($all = false)
- {
- self::blow('profile_list:notice_ids:%d', $this->id);
- if ($all) {
- self::blow('profile_list:notice_ids:%d;last', $this->id);
- }
- }
-
- /**
- * get the Profile_list object by the
- * given tagger and with given tag
- *
- * @param integer $tagger the id of the creator profile
- * @param integer $tag the tag
- *
- * @return integer count
- */
-
- public static function getByTaggerAndTag($tagger, $tag)
- {
- $ptag = Profile_list::pkeyGet(array('tagger' => $tagger, 'tag' => $tag));
- return $ptag;
- }
-
- /**
- * create a profile_list record for a tag, tagger pair
- * if it doesn't exist, return it.
- *
- * @param integer $tagger the tagger
- * @param string $tag the tag
- * @param string $description description
- * @param boolean $private protected or not
- *
- * @return Profile_list the people tag object
- */
-
- public static function ensureTag($tagger, $tag, $description = null, $private = false)
- {
- $ptag = Profile_list::getByTaggerAndTag($tagger, $tag);
-
- if (empty($ptag->id)) {
- $args = array(
- 'tag' => $tag,
- 'tagger' => $tagger,
- 'description' => $description,
- 'private' => $private
- );
-
- $new_tag = Profile_list::saveNew($args);
-
- return $new_tag;
- }
- return $ptag;
- }
-
- /**
- * get the maximum number of characters
- * that can be used in the description of
- * a people tag.
- *
- * determined by $config['peopletag']['desclimit']
- * if not set, falls back to $config['site']['textlimit']
- *
- * @return integer maximum number of characters
- */
-
- public static function maxDescription()
- {
- $desclimit = common_config('peopletag', 'desclimit');
- // null => use global limit (distinct from 0!)
- if (is_null($desclimit)) {
- $desclimit = common_config('site', 'textlimit');
- }
- return $desclimit;
- }
-
- /**
- * check if the length of given text exceeds
- * character limit.
- *
- * @param string $desc the description
- *
- * @return boolean is the descripition too long?
- */
-
- public static function descriptionTooLong($desc)
- {
- $desclimit = self::maxDescription();
- return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
- }
-
- /**
- * save a new people tag, this should be always used
- * since it makes uri, homeurl, created and modified
- * timestamps and performs checks.
- *
- * @param array $fields an array with fields and their values
- *
- * @return mixed Profile_list on success, false on fail
- */
- public static function saveNew(array $fields)
- {
- extract($fields);
-
- $ptag = new Profile_list();
-
- $ptag->query('START TRANSACTION');
-
- if (empty($tagger)) {
- // TRANS: Server exception saving new tag without having a tagger specified.
- throw new Exception(_('No tagger specified.'));
- }
-
- if (empty($tag)) {
- // TRANS: Server exception saving new tag without having a tag specified.
- throw new Exception(_('No tag specified.'));
- }
-
- if (empty($mainpage)) {
- $mainpage = null;
- }
-
- if (empty($uri)) {
- // fill in later...
- $uri = null;
- }
-
- if (empty($mainpage)) {
- $mainpage = null;
- }
-
- if (empty($description)) {
- $description = null;
- }
-
- if (empty($private)) {
- $private = false;
- }
-
- $ptag->tagger = $tagger;
- $ptag->tag = $tag;
- $ptag->description = $description;
- $ptag->private = $private;
- $ptag->uri = $uri;
- $ptag->mainpage = $mainpage;
- $ptag->created = common_sql_now();
- $ptag->modified = common_sql_now();
-
- $result = $ptag->insert();
-
- if (!$result) {
- common_log_db_error($ptag, 'INSERT', __FILE__);
- // TRANS: Server exception saving new tag.
- throw new ServerException(_('Could not create profile tag.'));
- }
-
- if (!isset($uri) || empty($uri)) {
- $orig = clone($ptag);
- $ptag->uri = common_local_url('profiletagbyid', array('id' => $ptag->id, 'tagger_id' => $ptag->tagger));
- $result = $ptag->update($orig);
- if (!$result) {
- common_log_db_error($ptag, 'UPDATE', __FILE__);
- // TRANS: Server exception saving new tag.
- throw new ServerException(_('Could not set profile tag URI.'));
- }
- }
-
- if (!isset($mainpage) || empty($mainpage)) {
- $orig = clone($ptag);
- $user = User::getKV('id', $ptag->tagger);
- if (!empty($user)) {
- $ptag->mainpage = common_local_url('showprofiletag', array('tag' => $ptag->tag, 'nickname' => $user->getNickname()));
- } else {
- $ptag->mainpage = $uri; // assume this is a remote peopletag and the uri works
- }
-
- $result = $ptag->update($orig);
- if (!$result) {
- common_log_db_error($ptag, 'UPDATE', __FILE__);
- // TRANS: Server exception saving new tag.
- throw new ServerException(_('Could not set profile tag mainpage.'));
- }
- }
- return $ptag;
- }
-
- /**
- * get all items at given cursor position for api
- *
- * @param callback $fn a function that takes the following arguments in order:
- * $offset, $limit, $since_id, $max_id
- * and returns a Profile_list object after making the DB query
- * @param array $args arguments required for $fn
- * @param integer $cursor the cursor
- * @param integer $count max. number of results
- *
- * Algorithm:
- * - if cursor is 0, return empty list
- * - if cursor is -1, get first 21 items, next_cursor = 20th prev_cursor = 0
- * - if cursor is +ve get 22 consecutive items before starting at cursor
- * - return items[1..20] if items[0] == cursor else return items[0..21]
- * - prev_cursor = items[1]
- * - next_cursor = id of the last item being returned
- *
- * - if cursor is -ve get 22 consecutive items after cursor starting at cursor
- * - return items[1..20]
- *
- * @returns array (array (mixed items), int next_cursor, int previous_cursor)
- */
-
- // XXX: This should be in Memcached_DataObject... eventually
-
- public static function getAtCursor($fn, array $args, $cursor, $count = 20)
- {
- $items = array();
-
- $since_id = 0;
- $max_id = 0;
- $next_cursor = 0;
- $prev_cursor = 0;
-
- if ($cursor > 0) {
- // if cursor is +ve fetch $count+2 items before cursor starting at cursor
- $max_id = $cursor;
- $fn_args = array_merge($args, array(0, $count+2, 0, $max_id));
- $list = call_user_func_array($fn, $fn_args);
- while ($list->fetch()) {
- $items[] = clone($list);
- }
-
- if ((isset($items[0]->cursor) && $items[0]->cursor == $cursor) ||
- $items[0]->id == $cursor) {
- array_shift($items);
- $prev_cursor = isset($items[0]->cursor) ?
- -$items[0]->cursor : -$items[0]->id;
- } else {
- if (count($items) > $count+1) {
- array_shift($items);
- }
- // this means the cursor item has been deleted, check to see if there are more
- $fn_args = array_merge($args, array(0, 1, $cursor));
- $more = call_user_func($fn, $fn_args);
- if (!$more->fetch() || empty($more)) {
- // no more items.
- $prev_cursor = 0;
- } else {
- $prev_cursor = isset($items[0]->cursor) ?
- -$items[0]->cursor : -$items[0]->id;
- }
- }
-
- if (count($items)==$count+1) {
- // this means there is a next page.
- $next = array_pop($items);
- $next_cursor = isset($next->cursor) ?
- $items[$count-1]->cursor : $items[$count-1]->id;
- }
- } elseif ($cursor < -1) {
- // if cursor is -ve fetch $count+2 items created after -$cursor-1
- $cursor = abs($cursor);
- $since_id = $cursor-1;
-
- $fn_args = array_merge($args, array(0, $count+2, $since_id));
- $list = call_user_func_array($fn, $fn_args);
- while ($list->fetch()) {
- $items[] = clone($list);
- }
-
- $end = count($items)-1;
- if ((isset($items[$end]->cursor) && $items[$end]->cursor == $cursor) ||
- $items[$end]->id == $cursor) {
- array_pop($items);
- $next_cursor = isset($items[$end-1]->cursor) ?
- $items[$end-1]->cursor : $items[$end-1]->id;
- } else {
- $next_cursor = isset($items[$end]->cursor) ?
- $items[$end]->cursor : $items[$end]->id;
- if ($end > $count) {
- // excess item
- array_pop($items);
- }
-
- // check if there are more items for next page
- $fn_args = array_merge($args, array(0, 1, 0, $cursor));
- $more = call_user_func_array($fn, $fn_args);
- if (!$more->fetch() || empty($more)) {
- $next_cursor = 0;
- }
- }
-
- if (count($items) == $count+1) {
- // this means there is a previous page.
- $prev = array_shift($items);
- $prev_cursor = isset($prev->cursor) ?
- -$items[0]->cursor : -$items[0]->id;
- }
- } elseif ($cursor == -1) {
- $fn_args = array_merge($args, array(0, $count+1));
- $list = call_user_func_array($fn, $fn_args);
-
- while ($list->fetch()) {
- $items[] = clone($list);
- }
-
- if (count($items)==$count+1) {
- $next = array_pop($items);
- if (isset($next->cursor)) {
- $next_cursor = $items[$count-1]->cursor;
- } else {
- $next_cursor = $items[$count-1]->id;
- }
- }
- }
- return array($items, $next_cursor, $prev_cursor);
- }
-
- /**
- * save a collection of people tags into the cache
- *
- * @param string $ckey cache key
- * @param Profile_list &$tag the results to store
- * @param integer $offset offset for slicing results
- * @param integer $limit maximum number of results
- *
- * @return boolean success
- */
-
- public static function setCache($ckey, &$tag, $offset = 0, $limit = null)
- {
- $cache = Cache::instance();
- if (empty($cache)) {
- return false;
- }
- $str = '';
- $tags = array();
- while ($tag->fetch()) {
- $str .= $tag->tagger . ':' . $tag->tag . ';';
- $tags[] = clone($tag);
- }
- $str = substr($str, 0, -1);
- if ($offset>=0 && !is_null($limit)) {
- $tags = array_slice($tags, $offset, $limit);
- }
-
- $tag = new ArrayWrapper($tags);
-
- return self::cacheSet($ckey, $str);
- }
-
- /**
- * get people tags from the cache
- *
- * @param string $ckey cache key
- * @param integer $offset offset for slicing
- * @param integer $limit limit
- *
- * @return Profile_list results
- */
-
- public static function getCached($ckey, $offset = 0, $limit = null)
- {
- $keys_str = self::cacheGet($ckey);
- if ($keys_str === false) {
- return false;
- }
-
- $pairs = explode(';', $keys_str);
- $keys = array();
- foreach ($pairs as $pair) {
- $keys[] = explode(':', $pair);
- }
-
- if ($offset>=0 && !is_null($limit)) {
- $keys = array_slice($keys, $offset, $limit);
- }
- return self::getByKeys($keys);
- }
-
- /**
- * get Profile_list objects from the database
- * given their (tag, tagger) key pairs.
- *
- * @param array $keys array of array(tagger, tag)
- *
- * @return Profile_list results
- */
-
- public static function getByKeys(array $keys)
- {
- $cache = Cache::instance();
-
- if (!empty($cache)) {
- $tags = array();
-
- foreach ($keys as $key) {
- $t = Profile_list::getByTaggerAndTag($key[0], $key[1]);
- if (!empty($t)) {
- $tags[] = $t;
- }
- }
- return new ArrayWrapper($tags);
- } else {
- $tag = new Profile_list();
- if (empty($keys)) {
- //if no IDs requested, just return the tag object
- return $tag;
- }
-
- $pairs = array();
- foreach ($keys as $key) {
- $pairs[] = '(' . $key[0] . ', "' . $key[1] . '")';
- }
-
- $tag->whereAdd('(tagger, tag) in (' . implode(', ', $pairs) . ')');
-
- $tag->find();
-
- $temp = array();
-
- while ($tag->fetch()) {
- $temp[$tag->tagger.'-'.$tag->tag] = clone($tag);
- }
-
- $wrapped = array();
-
- foreach ($keys as $key) {
- $id = $key[0].'-'.$key[1];
- if (array_key_exists($id, $temp)) {
- $wrapped[] = $temp[$id];
- }
- }
-
- return new ArrayWrapper($wrapped);
- }
- }
-
- public function insert()
- {
- $result = parent::insert();
- if ($result) {
- self::blow('profile:lists:%d', $this->tagger);
- }
- return $result;
- }
-}
diff --git a/src/Entity/Profile_prefs.php b/src/Entity/Profile_prefs.php
deleted file mode 100644
index ff1e92dacf..0000000000
--- a/src/Entity/Profile_prefs.php
+++ /dev/null
@@ -1,172 +0,0 @@
-.
-
-/**
- * Data class for Profile preferences
- *
- * @category Data
- * @package GNUsocial
- * @author Mikael Nordfeldth
- * @copyright 2013 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile_prefs extends Managed_DataObject
-{
- public $__table = 'profile_prefs'; // table name
- public $profile_id; // int(4) primary_key not_null
- public $namespace; // varchar(191) not_null
- public $topic; // varchar(191) not_null
- public $data; // text
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
- 'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
- 'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
- 'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('profile_id', 'namespace', 'topic'),
- 'foreign keys' => array(
- 'profile_prefs_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- );
- }
-
- public static function getNamespacePrefs(Profile $profile, $namespace, array $topic = [])
- {
- if (empty($topic)) {
- $prefs = new Profile_prefs();
- $prefs->profile_id = $profile->getID();
- $prefs->namespace = $namespace;
- $prefs->find();
- } else {
- $prefs = self::pivotGet('profile_id', $profile->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
- }
-
- if (empty($prefs->N)) {
- throw new NoResultException($prefs);
- }
-
- return $prefs;
- }
-
- public static function getNamespace(Profile $profile, $namespace, array $topic = [])
- {
- $prefs = self::getNamespacePrefs($profile, $namespace, $topic);
- return $prefs->fetchAll();
- }
-
- public static function getAll(Profile $profile)
- {
- try {
- $prefs = self::listFind('profile_id', array($profile->getID()));
- } catch (NoResultException $e) {
- return array();
- }
-
- $list = array();
- while ($prefs->fetch()) {
- if (!isset($list[$prefs->namespace])) {
- $list[$prefs->namespace] = array();
- }
- $list[$prefs->namespace][$prefs->topic] = $prefs->data;
- }
- return $list;
- }
-
- public static function getTopic(Profile $profile, $namespace, $topic)
- {
- return Profile_prefs::getByPK(array('profile_id' => $profile->getID(),
- 'namespace' => $namespace,
- 'topic' => $topic));
- }
-
- public static function getData(Profile $profile, $namespace, $topic, $def = null)
- {
- try {
- $pref = self::getTopic($profile, $namespace, $topic);
- } catch (NoResultException $e) {
- if ($def === null) {
- // If no default value was set, continue the exception.
- throw $e;
- }
- // If there was a default value, return that.
- return $def;
- }
- return $pref->data;
- }
-
- public static function getConfigData(Profile $profile, $namespace, $topic)
- {
- try {
- $data = self::getData($profile, $namespace, $topic);
- } catch (NoResultException $e) {
- $data = common_config($namespace, $topic);
- }
- return $data;
- }
-
- /*
- * Sets a profile preference based on Profile, namespace and topic
- *
- * @param Profile $profile Which profile this is for
- * @param string $namespace Under which namespace (pluginname etc.)
- * @param string $topic Preference name (think key in key-val store)
- * @param string $data Data to be put into preference storage, null means delete
- *
- * @return true if changes are made, false if no action taken
- * @throws ServerException if preference could not be saved
- */
- public static function setData(Profile $profile, $namespace, $topic, $data = null)
- {
- try {
- $pref = self::getTopic($profile, $namespace, $topic);
- if (is_null($data)) {
- $pref->delete();
- } else {
- $orig = clone($pref);
- $pref->data = DB_DataObject_Cast::blob($data);
- $pref->update($orig);
- }
- return true;
- } catch (NoResultException $e) {
- if (is_null($data)) {
- return false; // No action taken
- }
- }
-
- $pref = new Profile_prefs();
- $pref->profile_id = $profile->getID();
- $pref->namespace = $namespace;
- $pref->topic = $topic;
- $pref->data = DB_DataObject_Cast::blob($data);
- $pref->created = common_sql_now();
-
- if ($pref->insert() === false) {
- throw new ServerException('Could not save profile preference.');
- }
- return true;
- }
-}
diff --git a/src/Entity/Profile_role.php b/src/Entity/Profile_role.php
deleted file mode 100644
index 38c846a8dc..0000000000
--- a/src/Entity/Profile_role.php
+++ /dev/null
@@ -1,78 +0,0 @@
-.
-
-/*
- * Table Definition for profile_role
- *
- * @copyright 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile_role extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'profile_role'; // table name
- public $profile_id; // int(4) primary_key not_null
- public $role; // varchar(32) primary_key not_null
- public $created; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'account having the role'),
- 'role' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'string representing the role'),
- 'created' => array('type' => 'datetime', 'description' => 'date the role was granted'),
- ),
- 'primary key' => array('profile_id', 'role'),
- 'foreign keys' => array(
- 'profile_role_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array('profile_role_role_created_profile_id_idx' => array('role', 'created', 'profile_id')),
- );
- }
-
- const OWNER = 'owner';
- const MODERATOR = 'moderator';
- const ADMINISTRATOR = 'administrator';
- const SANDBOXED = 'sandboxed';
- const SILENCED = 'silenced';
- const DELETED = 'deleted'; // Pending final deletion of notices...
-
- public static function isValid($role)
- {
- // @fixme could probably pull this from class constants
- $known = array(self::OWNER,
- self::MODERATOR,
- self::ADMINISTRATOR,
- self::SANDBOXED,
- self::SILENCED);
- return in_array($role, $known);
- }
-
- public static function isSettable($role)
- {
- $allowedRoles = array('administrator', 'moderator');
- return self::isValid($role) && in_array($role, $allowedRoles);
- }
-}
diff --git a/src/Entity/Profile_tag.php b/src/Entity/Profile_tag.php
deleted file mode 100644
index c9cdec3b3d..0000000000
--- a/src/Entity/Profile_tag.php
+++ /dev/null
@@ -1,363 +0,0 @@
-.
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for profile_tag
- */
-class Profile_tag extends Managed_DataObject
-{
- public $__table = 'profile_tag'; // table name
- public $tagger; // int(4) primary_key not_null
- public $tagged; // int(4) primary_key not_null
- public $tag; // varchar(64) primary_key not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- public static function schemaDef()
- {
- return array(
-
- 'fields' => array(
- 'tagger' => array('type' => 'int', 'not null' => true, 'description' => 'user making the tag'),
- 'tagged' => array('type' => 'int', 'not null' => true, 'description' => 'profile tagged'),
- 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was added'),
- ),
- 'primary key' => array('tagger', 'tagged', 'tag'),
- 'foreign keys' => array(
- 'profile_tag_tagger_fkey' => array('profile', array('tagger' => 'id')),
- 'profile_tag_tagged_fkey' => array('profile', array('tagged' => 'id')),
- ),
- 'indexes' => array(
- 'profile_tag_modified_tagged_idx' => array('modified', 'tagged'),
- 'profile_tag_tagger_tag_idx' => array('tagger', 'tag'),
- 'profile_tag_tagged_idx' => array('tagged'),
- ),
- );
- }
-
- public function links()
- {
- return array('tagger,tag' => 'profile_list:tagger,tag');
- }
-
- public function getMeta()
- {
- return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag));
- }
-
- public static function getSelfTagsArray(Profile $target)
- {
- return self::getTagsArray($target->getID(), $target->getID(), $target);
- }
-
- public static function setSelfTags(Profile $target, array $newtags, array $privacy = [])
- {
- return self::setTags($target->getID(), $target->getID(), $newtags, $privacy);
- }
-
- public static function getTags($tagger, $tagged, $auth_user = null)
- {
- $profile_list = new Profile_list();
- $include_priv = 1;
-
- if (!($auth_user instanceof User ||
- $auth_user instanceof Profile) ||
- ($auth_user->id !== $tagger)) {
- $profile_list->private = false;
- $include_priv = 0;
- }
-
- $key = sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $include_priv);
- $tags = Profile_list::getCached($key);
- if ($tags !== false) {
- return $tags;
- }
-
- $qry = 'select profile_list.* from profile_list left join '.
- 'profile_tag on (profile_list.tag = profile_tag.tag and '.
- 'profile_list.tagger = profile_tag.tagger) where '.
- 'profile_tag.tagger = %d and profile_tag.tagged = %d ';
- $qry = sprintf($qry, $tagger, $tagged);
-
- if (!$include_priv) {
- $qry .= ' AND profile_list.private IS NOT TRUE';
- }
-
- $profile_list->query($qry);
-
- Profile_list::setCache($key, $profile_list);
-
- return $profile_list;
- }
-
- public static function getTagsArray($tagger, $tagged, Profile $scoped = null)
- {
- $ptag = new Profile_tag();
-
- $qry = sprintf(
- 'SELECT profile_tag.tag '.
- 'FROM profile_tag INNER JOIN profile_list '.
- ' ON (profile_tag.tagger = profile_list.tagger ' .
- ' and profile_tag.tag = profile_list.tag) ' .
- 'WHERE profile_tag.tagger = %d ' .
- 'AND profile_tag.tagged = %d ',
- $tagger,
- $tagged
- );
-
- if (!$scoped instanceof Profile || $scoped->getID() !== $tagger) {
- $qry .= 'AND profile_list.private IS NOT TRUE';
- }
-
- $tags = array();
-
- $ptag->query($qry);
-
- while ($ptag->fetch()) {
- $tags[] = $ptag->tag;
- }
-
- return $tags;
- }
-
- public static function setTags($tagger, $tagged, array $newtags, array $privacy = [])
- {
- $newtags = array_unique($newtags);
- $oldtags = self::getTagsArray($tagger, $tagged, Profile::getByID($tagger));
-
- $ptag = new Profile_tag();
-
- // Delete stuff that's in old and not in new
-
- $to_delete = array_diff($oldtags, $newtags);
-
- // Insert stuff that's in new and not in old
-
- $to_insert = array_diff($newtags, $oldtags);
-
- foreach ($to_delete as $deltag) {
- self::unTag($tagger, $tagged, $deltag);
- }
-
- foreach ($to_insert as $instag) {
- $private = isset($privacy[$instag]) ? $privacy[$instag] : false;
- self::setTag($tagger, $tagged, $instag, null, $private);
- }
- return true;
- }
-
- # set a single tag
- public static function setTag($tagger, $tagged, $tag, $desc=null, $private = false)
- {
- $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
- 'tagged' => $tagged,
- 'tag' => $tag));
-
- # if tag already exists, return it
- if ($ptag instanceof Profile_tag) {
- return $ptag;
- }
-
- $tagger_profile = Profile::getByID($tagger);
- $tagged_profile = Profile::getByID($tagged);
-
- if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) {
- if (!$tagger_profile->canTag($tagged_profile)) {
- // TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged.
- throw new ClientException(_('You cannot tag this user.'));
- }
-
- $tags = new Profile_list();
- $tags->tagger = $tagger;
- $count = (int) $tags->count('distinct tag');
-
- if ($count >= common_config('peopletag', 'maxtags')) {
- // TRANS: Client exception thrown trying to set more tags than allowed.
- throw new ClientException(sprintf(
- _('You already have created %d or more tags ' .
- 'which is the maximum allowed number of tags. ' .
- 'Try using or deleting some existing tags.'),
- common_config('peopletag', 'maxtags')
- ));
- }
-
- $plist = new Profile_list();
- $plist->query('START TRANSACTION');
-
- $profile_list = Profile_list::ensureTag($tagger, $tag, $desc, $private);
-
- if ($profile_list->taggedCount() >= common_config('peopletag', 'maxpeople')) {
- // TRANS: Client exception thrown when trying to add more people than allowed to a list.
- throw new ClientException(sprintf(
- _('You already have %1$d or more people in list %2$s, ' .
- 'which is the maximum allowed number. ' .
- 'Try unlisting others first.'),
- common_config('peopletag', 'maxpeople'),
- $tag
- ));
- }
-
- $newtag = new Profile_tag();
-
- $newtag->tagger = $tagger;
- $newtag->tagged = $tagged;
- $newtag->tag = $tag;
-
- $result = $newtag->insert();
-
- if (!$result) {
- common_log_db_error($newtag, 'INSERT', __FILE__);
- $plist->query('ROLLBACK');
- return false;
- }
-
- try {
- $plist->query('COMMIT');
- Event::handle('EndTagProfile', array($newtag));
- } catch (Exception $e) {
- $newtag->delete();
- $profile_list->delete();
- throw $e;
- }
-
- $profile_list->taggedCount(true);
- self::blowCaches($tagger, $tagged);
- }
-
- return $newtag;
- }
-
- public static function unTag($tagger, $tagged, $tag)
- {
- $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
- 'tagged' => $tagged,
- 'tag' => $tag));
- if (!$ptag) {
- return true;
- }
-
- if (Event::handle('StartUntagProfile', array($ptag))) {
- $orig = clone($ptag);
- $result = $ptag->delete();
- if ($result === false) {
- common_log_db_error($this, 'DELETE', __FILE__);
- return false;
- }
- Event::handle('EndUntagProfile', array($orig));
- $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
- if (!empty($profile_list)) {
- $profile_list->taggedCount(true);
- }
- self::blowCaches($tagger, $tagged);
- return true;
- }
- }
-
- // @fixme: move this to Profile_list?
- public static function cleanup($profile_list)
- {
- $ptag = new Profile_tag();
- $ptag->tagger = $profile_list->tagger;
- $ptag->tag = $profile_list->tag;
- $ptag->find();
-
- while ($ptag->fetch()) {
- if (Event::handle('StartUntagProfile', array($ptag))) {
- $orig = clone($ptag);
- $result = $ptag->delete();
- if (!$result) {
- common_log_db_error($this, 'DELETE', __FILE__);
- }
- Event::handle('EndUntagProfile', array($orig));
- }
- }
- }
-
- // move a tag!
- public static function moveTag($orig, $new)
- {
- $tags = new Profile_tag();
- $result = $tags->query(sprintf(
- <<<'END'
- UPDATE profile_tag
- SET tag = %1$s, tagger = %2$s, modified = CURRENT_TIMESTAMP
- WHERE tag = %3$s AND tagger = %4$s
- END,
- $tags->_quote($new->tag),
- $tags->_quote($new->tagger),
- $tags->_quote($orig->tag),
- $tags->_quote($orig->tagger)
- ));
-
- if ($result === false) {
- common_log_db_error($tags, 'UPDATE', __FILE__);
- throw new Exception('Could not move Profile_tag, see db log for details.');
- }
- return $result;
- }
-
- public static function blowCaches($tagger, $tagged)
- {
- foreach (array(0, 1) as $perm) {
- self::blow(sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $perm));
- }
- return true;
- }
-
- // Return profiles with a given tag
- public static function getTagged($tagger, $tag)
- {
- $profile = new Profile();
- $profile->query('SELECT profile.* ' .
- 'FROM profile JOIN profile_tag ' .
- 'ON profile.id = profile_tag.tagged ' .
- 'WHERE profile_tag.tagger = ' . $profile->escape($tagger) . ' ' .
- "AND profile_tag.tag = '" . $profile->escape($tag) . "' ");
- $tagged = [];
- while ($profile->fetch()) {
- $tagged[] = clone($profile);
- }
- return true;
- }
-
- public function insert()
- {
- $result = parent::insert();
- if ($result) {
- self::blow(
- 'profile_list:tagged_count:%d:%s',
- $this->tagger,
- $this->tag
- );
- }
- return $result;
- }
-
- public function delete($useWhere = false)
- {
- $result = parent::delete($useWhere);
- if ($result !== false) {
- self::blow(
- 'profile_list:tagged_count:%d:%s',
- $this->tagger,
- $this->tag
- );
- }
- return $result;
- }
-}
diff --git a/src/Entity/Profile_tag_subscription.php b/src/Entity/Profile_tag_subscription.php
deleted file mode 100644
index 2eb4fed083..0000000000
--- a/src/Entity/Profile_tag_subscription.php
+++ /dev/null
@@ -1,158 +0,0 @@
-.
-
-/**
- * Table Definition for profile_tag_subscription
- */
-
-defined('GNUSOCIAL') || die();
-
-class Profile_tag_subscription extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'profile_tag_subscription'; // table name
- public $profile_tag_id; // int(4) not_null
- public $profile_id; // int(4) not_null
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'profile_tag_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile_tag'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
-
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('profile_tag_id', 'profile_id'),
- 'foreign keys' => array(
- 'profile_tag_subscription_profile_tag_id_fkey' => array('profile_list', array('profile_tag_id' => 'id')),
- 'profile_tag_subscription_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array(
- 'profile_tag_subscription_profile_id_created_profile_tag_id_idx' => array('profile_id', 'created', 'profile_tag_id'),
- ),
- );
- }
-
- public static function add($peopletag, $profile)
- {
- if ($peopletag->private) {
- return false;
- }
-
- if (Event::handle('StartSubscribePeopletag', array($peopletag, $profile))) {
- $args = array('profile_tag_id' => $peopletag->id,
- 'profile_id' => $profile->id);
- $existing = Profile_tag_subscription::pkeyGet($args);
- if (!empty($existing)) {
- return $existing;
- }
-
- $sub = new Profile_tag_subscription();
- $sub->profile_tag_id = $peopletag->id;
- $sub->profile_id = $profile->id;
- $sub->created = common_sql_now();
-
- $result = $sub->insert();
-
- if (!$result) {
- common_log_db_error($sub, 'INSERT', __FILE__);
- // TRANS: Exception thrown when inserting a list subscription in the database fails.
- throw new Exception(_('Adding list subscription failed.'));
- }
-
- $ptag = Profile_list::getKV('id', $peopletag->id);
- $ptag->subscriberCount(true);
-
- Event::handle('EndSubscribePeopletag', array($peopletag, $profile));
- return $ptag;
- }
- }
-
- public static function remove($peopletag, $profile)
- {
- $sub = Profile_tag_subscription::pkeyGet(array('profile_tag_id' => $peopletag->id,
- 'profile_id' => $profile->id));
-
- if (empty($sub)) {
- // silence is golden?
- return true;
- }
-
- if (Event::handle('StartUnsubscribePeopletag', array($peopletag, $profile))) {
- $result = $sub->delete();
-
- if (!$result) {
- common_log_db_error($sub, 'DELETE', __FILE__);
- // TRANS: Exception thrown when deleting a list subscription from the database fails.
- throw new Exception(_('Removing list subscription failed.'));
- }
-
- $peopletag->subscriberCount(true);
-
- Event::handle('EndUnsubscribePeopletag', array($peopletag, $profile));
- return true;
- }
- }
-
- // called if a tag gets deleted / made private
- public static function cleanup($profile_list)
- {
- $subs = new self();
- $subs->profile_tag_id = $profile_list->id;
- $subs->find();
-
- while ($subs->fetch()) {
- $profile = Profile::getKV('id', $subs->profile_id);
- Event::handle('StartUnsubscribePeopletag', array($profile_list, $profile));
- // Delete anyway
- $subs->delete();
- Event::handle('StartUnsubscribePeopletag', array($profile_list, $profile));
- }
- }
-
- public function insert()
- {
- $result = parent::insert();
- if ($result) {
- self::blow(
- 'profile_list:subscriber_count:%d',
- $this->profile_tag_id
- );
- }
- return $result;
- }
-
- public function delete($useWhere = false)
- {
- $result = parent::delete($useWhere);
- if ($result !== false) {
- self::blow(
- 'profile_list:subscriber_count:%d',
- $this->profile_tag_id
- );
- }
- return $result;
- }
-}
diff --git a/src/Entity/QueueItem.php b/src/Entity/QueueItem.php
new file mode 100644
index 0000000000..0a38076c81
--- /dev/null
+++ b/src/Entity/QueueItem.php
@@ -0,0 +1,59 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for a Queue Item
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class QueueItem
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'queue_item',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
+ 'frame' => ['type' => 'blob', 'not null' => true, 'description' => 'data: object reference or opaque string'],
+ 'transport' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'queue for what? "email", "xmpp", "sms", "irc", ...'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'claimed' => ['type' => 'datetime', 'description' => 'date this item was claimed'],
+ ],
+ 'primary key' => ['id'],
+ 'indexes' => [
+ 'queue_item_created_idx' => ['created'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Queue_item.php b/src/Entity/Queue_item.php
deleted file mode 100644
index 6401325cf2..0000000000
--- a/src/Entity/Queue_item.php
+++ /dev/null
@@ -1,122 +0,0 @@
-.
-
-/**
- * Table Definition for queue_item
- */
-
-defined('GNUSOCIAL') || die();
-
-class Queue_item extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'queue_item'; // table name
- public $id; // int(4) primary_key not_null
- public $frame; // blob not_null
- public $transport; // varchar(32)
- public $created; // datetime()
- public $claimed; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'frame' => array('type' => 'blob', 'not null' => true, 'description' => 'data: object reference or opaque string'),
- 'transport' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'queue for what? "email", "xmpp", "sms", "irc", ...'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'claimed' => array('type' => 'datetime', 'description' => 'date this item was claimed'),
- ),
- 'primary key' => array('id'),
- 'indexes' => array(
- 'queue_item_created_id_idx' => array('created', 'id'),
- ),
- );
- }
-
- /**
- * @param mixed $transports name of a single queue or array of queues to pull from
- * If not specified, checks all queues in the system.
- */
- public static function top($transports = null, array $ignored_transports = [])
- {
- $qi = new Queue_item();
- if ($transports) {
- if (is_array($transports)) {
- $qi->whereAddIn(
- 'transport',
- $transports,
- $qi->columnType('transport')
- );
- } else {
- $qi->transport = $transports;
- }
- }
- if (!empty($ignored_transports)) {
- $qi->whereAddIn(
- '!transport',
- $ignored_transports,
- $qi->columnType('transport')
- );
- }
- $qi->whereAdd('claimed IS NULL');
- $qi->orderBy('created, id');
-
- $qi->limit(1);
-
- $cnt = $qi->find(true);
-
- if ($cnt) {
- // XXX: potential race condition
- // can we force it to only update if claimed is still null
- // (or old)?
- common_log(LOG_INFO, 'claiming queue item id = ' . $qi->getID() . ' for transport ' . $qi->transport);
- $orig = clone($qi);
- $qi->claimed = common_sql_now();
- $result = $qi->update($orig);
- if ($result) {
- common_log(LOG_DEBUG, 'claim succeeded.');
- return $qi;
- } else {
- common_log(LOG_ERR, 'claim of queue item id= ' . $qi->getID() . ' for transport ' . $qi->transport . ' failed.');
- }
- }
- unset($qi);
- return null;
- }
-
- /**
- * Release a claimed item.
- */
- public function releaseClaim()
- {
- // @fixme Consider $this->sqlValue('NULL')
- $ret = $this->query(sprintf(
- 'UPDATE queue_item SET claimed = NULL WHERE id = %d',
- $this->getID()
- ));
-
- if ($ret) {
- $this->claimed = null;
- $this->encache();
- }
- }
-}
diff --git a/src/Entity/RelatedGroup.php b/src/Entity/RelatedGroup.php
new file mode 100644
index 0000000000..52377ba0b9
--- /dev/null
+++ b/src/Entity/RelatedGroup.php
@@ -0,0 +1,59 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for related groups
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class RelatedGroup
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'related_group',
+ // @fixme description for related_group?
+ 'fields' => [
+ 'group_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'],
+ 'related_group_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ ],
+ 'primary key' => ['group_id', 'related_group_id'],
+ 'foreign keys' => [
+ 'related_group_group_id_fkey' => ['user_group', ['group_id' => 'id']],
+ 'related_group_related_group_id_fkey' => ['user_group', ['related_group_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Related_group.php b/src/Entity/Related_group.php
deleted file mode 100644
index ff51e6cd20..0000000000
--- a/src/Entity/Related_group.php
+++ /dev/null
@@ -1,55 +0,0 @@
-.
-
-/**
- * Table Definition for related_group
- */
-
-defined('GNUSOCIAL') || die();
-
-class Related_group extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'related_group'; // table name
- public $group_id; // int(4) primary_key not_null
- public $related_group_id; // int(4) primary_key not_null
- public $created; // datetime()
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- // @fixme description for related_group?
- 'fields' => array(
- 'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'),
- 'related_group_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- ),
- 'primary key' => array('group_id', 'related_group_id'),
- 'foreign keys' => array(
- 'related_group_group_id_fkey' => array('user_group', array('group_id' => 'id')),
- 'related_group_related_group_id_fkey' => array('user_group', array('related_group_id' => 'id')),
- ),
- 'indexes' => array(
- 'related_group_related_group_id_idx' => array('related_group_id'),
- ),
- );
- }
-}
diff --git a/src/Entity/RememberMe.php b/src/Entity/RememberMe.php
new file mode 100644
index 0000000000..bb781e54dc
--- /dev/null
+++ b/src/Entity/RememberMe.php
@@ -0,0 +1,57 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user remember me
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class RememberMe
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'remember_me',
+ 'fields' => [
+ 'code' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'],
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user who is logged in'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['code'],
+ 'foreign keys' => [
+ 'remember_me_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Remember_me.php b/src/Entity/Remember_me.php
deleted file mode 100644
index f7cbee5f58..0000000000
--- a/src/Entity/Remember_me.php
+++ /dev/null
@@ -1,53 +0,0 @@
-.
-
-/**
- * Table Definition for remember_me
- */
-
-defined('GNUSOCIAL') || die();
-
-class Remember_me extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'remember_me'; // table name
- public $code; // varchar(32) primary_key not_null
- public $user_id; // int(4) not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who is logged in'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('code'),
- 'foreign keys' => array(
- 'remember_me_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- 'indexes' => array(
- 'remember_me_user_id_idx' => array('user_id'),
- ),
- );
- }
-}
diff --git a/src/Entity/Reply.php b/src/Entity/Reply.php
index 9baa0989c9..91f016450e 100644
--- a/src/Entity/Reply.php
+++ b/src/Entity/Reply.php
@@ -1,85 +1,65 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for reply
+ * Entity for Notice reply
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Reply extends Managed_DataObject
+class Reply
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'reply'; // table name
- public $notice_id; // int(4) primary_key not_null
- public $profile_id; // int(4) primary_key not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $replied_id; // int(4)
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
+ public static function schemaDef(): array
{
- return array(
- 'fields' => array(
- 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice that is the reply'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile replied to'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- 'replied_id' => array('type' => 'int', 'description' => 'notice replied to (not used, see notice.reply_to)'),
- ),
- 'primary key' => array('notice_id', 'profile_id'),
- 'foreign keys' => array(
- 'reply_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
- 'reply_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array(
- 'reply_profile_id_idx' => array('profile_id'),
- 'reply_replied_id_idx' => array('replied_id'),
- 'reply_profile_id_modified_notice_id_idx' => array('profile_id', 'modified', 'notice_id')
- ),
- );
- }
-
- /**
- * Wrapper for record insertion to update related caches
- */
- public function insert()
- {
- $result = parent::insert();
-
- if ($result) {
- self::blow('reply:stream:%d', $this->profile_id);
- }
-
- return $result;
- }
-
- public static function stream(
- $user_id,
- $offset = 0,
- $limit = NOTICES_PER_PAGE,
- $since_id = 0,
- $max_id = 0
- ) {
- // FIXME: Use some other method to get Profile::current() in order
- // to avoid confusion between background processing and session user.
- $stream = new ReplyNoticeStream($user_id, Profile::current());
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
+ return [
+ 'name' => 'reply',
+ 'fields' => [
+ 'notice_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice that is the reply'],
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'profile replied to'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ 'replied_id' => ['type' => 'int', 'description' => 'notice replied to (not used, see notice.reply_to)'],
+ ],
+ 'primary key' => ['notice_id', 'profile_id'],
+ 'foreign keys' => [
+ 'reply_notice_id_fkey' => ['notice', ['notice_id' => 'id']],
+ 'reply_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'reply_notice_id_idx' => ['notice_id'],
+ 'reply_profile_id_idx' => ['profile_id'],
+ 'reply_replied_id_idx' => ['replied_id'],
+ 'reply_profile_id_modified_notice_id_idx' => ['profile_id', 'modified', 'notice_id'],
+ ],
+ ];
}
}
diff --git a/src/Entity/Safe_DataObject.php b/src/Entity/Safe_DataObject.php
deleted file mode 100644
index 4473603724..0000000000
--- a/src/Entity/Safe_DataObject.php
+++ /dev/null
@@ -1,309 +0,0 @@
-.
-
-/*
- * @copyright 2010 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Extended DB_DataObject to improve a few things:
- * - free global resources from destructor
- * - remove bogus global references from serialized objects
- * - don't leak memory when loading already-used .ini files
- * (eg when using the same schema on thousands of databases)
- */
-class Safe_DataObject extends GS_DataObject
-{
- /**
- * Destructor to free global memory resources associated with
- * this data object when it's unset or goes out of scope.
- * DB_DataObject doesn't do this yet by itself.
- */
-
- public function __destruct()
- {
- $this->free();
- if (method_exists('DB_DataObject', '__destruct')) {
- parent::__destruct();
- }
- }
-
- /**
- * Magic function called at clone() time.
- *
- * We use this to drop connection with some global resources.
- * This supports the fairly common pattern where individual
- * items being read in a loop via a single object are cloned
- * for individual processing, then fall out of scope when the
- * loop comes around again.
- *
- * As that triggers the destructor, we want to make sure that
- * the original object doesn't have its database result killed.
- * It will still be freed properly when the original object
- * gets destroyed.
- */
- public function __clone()
- {
- $this->_DB_resultid = false;
- }
-
- /**
- * Magic function called at serialize() time.
- *
- * We use this to drop a couple process-specific references
- * from DB_DataObject which can cause trouble in future
- * processes.
- *
- * @return array of variable names to include in serialization.
- */
- public function __sleep()
- {
- $vars = array_keys(get_object_vars($this));
- $skip = array('_DB_resultid', '_link_loaded');
- return array_diff($vars, $skip);
- }
-
- /**
- * Magic function called at unserialize() time.
- *
- * Clean out some process-specific variables which might
- * be floating around from a previous process's cached
- * objects.
- *
- * Old cached objects may still have them.
- */
- public function __wakeup()
- {
- // Refers to global state info from a previous process.
- // Clear this out so we don't accidentally break global
- // state in *this* process.
- $this->_DB_resultid = null;
- // We don't have any local DBO refs, so clear these out.
- $this->_link_loaded = false;
- }
-
- /**
- * Magic function called when someone attempts to call a method
- * that doesn't exist. DB_DataObject uses this to implement
- * setters and getters for fields, but neglects to throw an error
- * when you just misspell an actual method name. This leads to
- * silent failures which can cause all kinds of havoc.
- *
- * @param string $method
- * @param array $params
- * @return mixed
- * @throws Exception
- */
- public function __call($method, $params)
- {
- $return = null;
- // Yes, that's _call with one underscore, which does the
- // actual implementation.
- if ($this->_call($method, $params, $return)) {
- return $return;
- } else {
- // Low level exception. No need for i18n as discussed with Brion.
- throw new Exception('Call to undefined method ' .
- get_class($this) . '::' . $method);
- }
- }
-
- /**
- * Work around memory-leak bugs...
- * Had to copy-paste the whole function in order to patch a couple lines of it.
- * Would be nice if this code was better factored.
- *
- * @param optional string name of database to assign / read
- * @param optional array structure of database, and keys
- * @param optional array table links
- *
- * @access public
- * @return true or PEAR:error on wrong paramenters.. or false if no file exists..
- * or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename)
- */
- public function databaseStructure()
- {
- global $_DB_DATAOBJECT;
-
- if (!empty($args = func_get_args())) {
- if (count($args) == 1) {
- // this returns all the tables and their structure..
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug(
- 'Loading Generator as databaseStructure called with args',
- 1
- );
- }
-
- $x = new DB_DataObject;
- $x->_database = $args[0];
- $this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
-
- $tables = $DB->getListOf('tables');
- class_exists('DB_DataObject_Generator') ? '' :
- require_once 'DB/DataObject/Generator.php';
-
- foreach ($tables as $table) {
- $y = new DB_DataObject_Generator;
- $y->fillTableSchema($x->_database, $table);
- }
- return $_DB_DATAOBJECT['INI'][$x->_database];
- } else {
- $_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
- $_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
-
- if (isset($args[1])) {
- $_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
- $_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
- }
- return true;
- }
- }
- if (!$this->_database) {
- $this->_connect();
- }
-
- // loaded already?
- if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
-
- // database loaded - but this is table is not available..
- if (
- empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])
- && !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
- ) {
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug('Loading Generator to fetch Schema', 1);
- }
- class_exists('DB_DataObject_Generator') ? '' :
- require_once 'DB/DataObject/Generator.php';
-
-
- $x = new DB_DataObject_Generator;
- $x->fillTableSchema($this->_database, $this->tableName());
- }
- return true;
- }
-
- if (empty($_DB_DATAOBJECT['CONFIG'])) {
- self::_loadConfig();
- }
-
- // if you supply this with arguments, then it will take those
- // as the database and links array...
-
- $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
- array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
- array() ;
-
- if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
- $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
- $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
- explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
- }
-
- /* BEGIN CHANGED FROM UPSTREAM */
- $_DB_DATAOBJECT['INI'][$this->_database] = $this->parseIniFiles($schemas);
- /* END CHANGED FROM UPSTREAM */
-
- // now have we loaded the structure..
-
- if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
- return true;
- }
- // - if not try building it..
- if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
- class_exists('DB_DataObject_Generator') ? '' :
- require_once 'DB/DataObject/Generator.php';
-
- $x = new DB_DataObject_Generator;
- $x->fillTableSchema($this->_database, $this->tableName());
- // should this fail!!!???
- return true;
- }
- $this->debug(
- "Can't find database schema: {$this->_database}/{$this->tableName()}\n"
- . 'in links file data: '
- . print_r($_DB_DATAOBJECT['INI'], true),
- 'databaseStructure',
- 5
- );
- // we have to die here!! - it causes chaos if we don't (including looping forever!)
- // Low level exception. No need for i18n as discussed with Brion.
- $this->raiseError(
- 'Unable to load schema for database and table '
- . '(turn debugging up to 5 for full error message)',
- DB_DATAOBJECT_ERROR_INVALIDARGS,
- PEAR_ERROR_DIE
- );
- return false;
- }
-
- /** For parseIniFiles */
- protected static $iniCache = array();
-
- /**
- * When switching site configurations, DB_DataObject was loading its
- * .ini files over and over, leaking gobs of memory.
- * This refactored helper function uses a local cache of .ini files
- * to minimize the leaks.
- *
- * @param array of .ini file names $schemas
- * @return array
- */
- protected function parseIniFiles(array $schemas)
- {
- $key = implode("|", $schemas);
- if (!isset(Safe_DataObject::$iniCache[$key])) {
- $data = array();
- foreach ($schemas as $ini) {
- if (file_exists($ini) && is_file($ini)) {
- $data = array_merge($data, parse_ini_file($ini, true));
-
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- if (!is_readable($ini)) {
- $this->debug(
- "ini file is not readable: {$ini}",
- 'databaseStructure',
- 1
- );
- } else {
- $this->debug(
- "Loaded ini file: {$ini}",
- 'databaseStructure',
- 1
- );
- }
- }
- } else {
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug(
- "Missing ini file: {$ini}",
- 'databaseStructure',
- 1
- );
- }
- }
- }
- Safe_DataObject::$iniCache[$key] = $data;
- }
-
- return Safe_DataObject::$iniCache[$key];
- }
-}
diff --git a/src/Entity/SchemaVersion.php b/src/Entity/SchemaVersion.php
new file mode 100644
index 0000000000..203f29e15a
--- /dev/null
+++ b/src/Entity/SchemaVersion.php
@@ -0,0 +1,55 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for the Schema Version
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class SchemaVersion
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'schema_version',
+ 'description' => 'To avoid checking database structure all the time, we store a checksum of the expected schema info for each table here. If it has not changed since the last time we checked the table, we can leave it as is.',
+ 'fields' => [
+ 'table_name' => ['type' => 'varchar', 'length' => '64', 'not null' => true, 'description' => 'Table name'],
+ 'checksum' => ['type' => 'varchar', 'length' => '64', 'not null' => true, 'description' => 'Checksum of schema array; a mismatch indicates we should check the table more thoroughly.'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['table_name'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Schema_version.php b/src/Entity/Schema_version.php
deleted file mode 100644
index fe89bb88fb..0000000000
--- a/src/Entity/Schema_version.php
+++ /dev/null
@@ -1,48 +0,0 @@
-.
-
-/**
- * Table Definition for schema_version
- */
-
-defined('GNUSOCIAL') || die();
-
-class Schema_version extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'schema_version'; // table name
- public $table_name; // varchar(64) primary_key not_null
- public $checksum; // varchar(128) not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'To avoid checking database structure all the time, we store a checksum of the expected schema info for each table here. If it has not changed since the last time we checked the table, we can leave it as is.',
- 'fields' => array(
- 'table_name' => array('type' => 'varchar', 'length' => '64', 'not null' => true, 'description' => 'Table name'),
- 'checksum' => array('type' => 'varchar', 'length' => '128', 'not null' => true, 'description' => 'Checksum of schema array; a mismatch indicates we should check the table more thoroughly.'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('table_name'),
- );
- }
-}
diff --git a/src/Entity/Session.php b/src/Entity/Session.php
index 1fc3c43a4b..d8ea561303 100644
--- a/src/Entity/Session.php
+++ b/src/Entity/Session.php
@@ -1,84 +1,59 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for session
+ * Entity for Superclass representing a saved session as it exists in the database.
*
+ * @category DB
* @package GNUsocial
- * @author Evan Prodromou
- * @author Brion Vibber
- * @author Mikael Nordfeldth
- * @author Sorokin Alexei
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @author Diogo Cordeiro
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Superclass representing a saved session as it exists in the database.
- *
- * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-class Session extends Managed_DataObject
+class Session
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'session'; // table name
- public $id; // varchar(32) primary_key not_null
- public $session_data; // text()
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- /**
- * Returns an array describing how the session is stored in the database.
- *
- * @return array
- */
- public static function schemaDef()
+ public static function schemaDef(): array
{
return [
'fields' => [
- 'id' => ['type' => 'varchar', 'length' => 128, 'not null' => true, 'description' => 'session ID'],
+ 'id' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'session ID'],
'session_data' => ['type' => 'text', 'description' => 'session data'],
- 'created' => ['type' => 'datetime', 'description' => 'date this record was created'],
- 'modified' => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
],
'primary key' => ['id'],
- 'indexes' => [
+ 'indexes' => [
'session_modified_idx' => ['modified'],
],
];
}
-
- /**
- * New code should NOT call this function.
- * Dummy function for backwards compatibility with older plugins like Qvitter.
- * Stuff to do before the request teardown.
- *
- * @return void
- */
- public static function cleanup()
- {
- session_write_close();
- }
}
diff --git a/src/Entity/SmsCarrier.php b/src/Entity/SmsCarrier.php
new file mode 100644
index 0000000000..4e5c91aec5
--- /dev/null
+++ b/src/Entity/SmsCarrier.php
@@ -0,0 +1,59 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for SMS carriers
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class SmsCarrier
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'sms_carrier',
+ 'fields' => [
+ 'id' => ['type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'],
+ 'name' => ['type' => 'varchar', 'length' => 64, 'description' => 'name of the carrier'],
+ 'email_pattern' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'sms_carrier_name_key' => ['name'],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Sms_carrier.php b/src/Entity/Sms_carrier.php
deleted file mode 100644
index 6b30b9265e..0000000000
--- a/src/Entity/Sms_carrier.php
+++ /dev/null
@@ -1,59 +0,0 @@
-.
-
-/**
- * Table Definition for sms_carrier
- */
-
-defined('GNUSOCIAL') || die();
-
-class Sms_carrier extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'sms_carrier'; // table name
- public $id; // int(4) primary_key not_null
- public $name; // varchar(64) unique_key
- public $email_pattern; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public function toEmailAddress($sms)
- {
- return sprintf($this->email_pattern, $sms);
- }
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'),
- 'name' => array('type' => 'varchar', 'length' => 64, 'description' => 'name of the carrier'),
- 'email_pattern' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'sms_carrier_name_key' => array('name'),
- ),
- );
- }
-}
diff --git a/src/Entity/Status_network.php b/src/Entity/Status_network.php
deleted file mode 100644
index f6812c175c..0000000000
--- a/src/Entity/Status_network.php
+++ /dev/null
@@ -1,388 +0,0 @@
-.
-
-/**
- * Table Definition for status_network
- *
- * @copyright 2009 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class Status_network extends Safe_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'status_network'; // table name
- public $site_id; // int(4) primary_key not_null
- public $nickname; // varchar(64) unique_key not_null
- public $hostname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $pathname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $dbhost; // varchar(191) not 255 because utf8mb4 takes more space
- public $dbuser; // varchar(191) not 255 because utf8mb4 takes more space
- public $dbpass; // varchar(191) not 255 because utf8mb4 takes more space
- public $dbname; // varchar(191) not 255 because utf8mb4 takes more space
- public $sitename; // varchar(191) not 255 because utf8mb4 takes more space
- public $theme; // varchar(191) not 255 because utf8mb4 takes more space
- public $logo; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime() not_null
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* Static get */
- public static function getKV($k, $v = null)
- {
- // TODO: This must probably be turned into a non-static call
- $i = DB_DataObject::staticGet('Status_network', $k, $v);
-
- // Don't use local process cache; if we're fetching multiple
- // times it's because we're reloading it in a long-running
- // process; we need a fresh copy!
- global $_DB_DATAOBJECT;
- unset($_DB_DATAOBJECT['CACHE']['status_network']);
- return $i;
- }
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- // XXX: made public so Status_network_tag can eff with it
- public static $cache = null;
- public static $cacheInitialized = false;
- public static $base = null;
- public static $wildcard = null;
-
- /**
- * @param string $dbhost
- * @param string $dbuser
- * @param string $dbpass
- * @param string $dbname
- * @param array $servers memcached servers to use for caching config info
- */
- public static function setupDB(
- $dbhost,
- $dbuser,
- $dbpass,
- $dbname,
- array $servers
- ) {
- global $config;
-
- $config['db']['database_'.$dbname] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
- $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/status_network.ini';
-
- foreach (array('status_network', 'status_network_tag', 'unavailable_status_network') as $table) {
- $config['db']['table_'.$table] = $dbname;
- }
-
- if (class_exists('Memcache')) {
- self::$cache = new Memcache();
-
- // If we're a parent command-line process we need
- // to be able to close out the connection after
- // forking, so disable persistence.
- //
- // We'll turn it back on again the second time
- // through which will either be in a child process,
- // or a single-process script which is switching
- // configurations.
- $persist = php_sapi_name() != 'cli' || self::$cacheInitialized;
- if (!is_array($servers)) {
- $servers = array($servers);
- }
- foreach ($servers as $server) {
- $parts = explode(':', $server);
- $server = $parts[0];
- if (count($parts) > 1) {
- $port = $parts[1];
- } else {
- $port = 11211;
- }
- self::$cache->addServer($server, $port, $persist);
- }
- self::$cacheInitialized = true;
- }
-
- self::$base = $dbname;
- }
-
- public static function cacheKey($k, $v)
- {
- return 'gnusocial:' . self::$base . ':status_network:'.$k.':'.$v;
- }
-
- public static function memGet($k, $v)
- {
- if (!self::$cache) {
- return self::getKV($k, $v);
- }
-
- $ck = self::cacheKey($k, $v);
-
- $sn = self::$cache->get($ck);
-
- if (empty($sn)) {
- $sn = self::getKV($k, $v);
- if (!empty($sn)) {
- self::$cache->set($ck, clone($sn));
- }
- }
-
- return $sn;
- }
-
- public function decache()
- {
- if (self::$cache) {
- $keys = array('nickname', 'hostname', 'pathname');
- foreach ($keys as $k) {
- $ck = self::cacheKey($k, $this->$k);
- self::$cache->delete($ck);
- }
- }
- }
-
- public function update($dataObject = false)
- {
- if (is_object($dataObject)) {
- // might be different keys
- $dataObject->decache();
- }
- return parent::update($dataObject);
- }
-
- /**
- * DB_DataObject doesn't allow updating keys (even non-primary)
- */
- public function updateKeys(&$orig)
- {
- $this->_connect();
- foreach (array('hostname', 'pathname') as $k) {
- if (strcmp($this->$k, $orig->$k) != 0) {
- $parts[] = $k . ' = ' . $this->_quote($this->$k);
- }
- }
- if (count($parts) == 0) {
- // No changes
- return true;
- }
-
- $toupdate = implode(', ', $parts);
-
- $table = common_database_tablename($this->tableName());
- $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
- ' WHERE nickname = ' . $this->_quote($this->nickname);
- $orig->decache();
- $result = $this->query($qry);
- $this->decache();
-
- return $result;
- }
-
- public function delete($useWhere = false)
- {
- // while we still have the values!
- $this->decache();
- return parent::delete($useWhere);
- }
-
- /**
- * @param string $servername hostname
- * @param string $wildcard hostname suffix to match wildcard config
- * @return mixed Status_network or null
- */
- public static function getFromHostname($servername, $wildcard)
- {
- $sn = null;
- if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) {
- // special case for exact match
- if (0 == strcasecmp($servername, $wildcard)) {
- $sn = self::memGet('nickname', '');
- } else {
- $parts = explode('.', $servername);
- $sn = self::memGet('nickname', strtolower($parts[0]));
- }
- } else {
- $sn = self::memGet('hostname', strtolower($servername));
-
- if (empty($sn)) {
- // Try for a no-www address
- if (0 == strncasecmp($servername, 'www.', 4)) {
- $sn = self::memGet('hostname', strtolower(substr($servername, 4)));
- }
- }
- }
- return $sn;
- }
-
- /**
- * @param string $servername hostname
- * @param string $pathname URL base path
- * @param string $wildcard hostname suffix to match wildcard config
- */
- public static function setupSite($servername, $pathname, $wildcard)
- {
- global $config;
-
- $sn = null;
-
- // XXX I18N, probably not crucial for hostnames
- // XXX This probably needs a tune up
- $sn = self::getFromHostname($servername, $wildcard);
-
- if (!empty($sn)) {
-
- // Redirect to the right URL
-
- if (!empty($sn->hostname) &&
- empty($_SERVER['HTTPS']) &&
- 0 != strcasecmp($sn->hostname, $servername)) {
- $sn->redirectTo('http://'.$sn->hostname.$_SERVER['REQUEST_URI']);
- } elseif (
- !empty($_SERVER['HTTPS'])
- && strcasecmp($sn->hostname, $servername) !== 0
- && strcasecmp($sn->nickname . '.' . $wildcard, $servername) !== 0
- ) {
- $sn->redirectTo(
- "https://{$sn->nickname}.{$wildcard}{$_SERVER['REQUEST_URI']}"
- );
- }
-
- $dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost;
- $dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser;
- $dbpass = $sn->dbpass;
- $dbname = (empty($sn->dbname)) ? $sn->nickname : $sn->dbname;
-
- $config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
-
- $config['site']['name'] = $sn->sitename;
- $config['site']['nickname'] = $sn->nickname;
-
- self::$wildcard = $wildcard;
-
- $config['site']['wildcard'] =& self::$wildcard;
-
- if (!empty($sn->hostname)) {
- $config['site']['server'] = $sn->hostname;
- }
-
- if (!empty($sn->theme)) {
- $config['site']['theme'] = $sn->theme;
- }
- if (!empty($sn->logo)) {
- $config['site']['logo'] = $sn->logo;
- }
-
- return $sn;
- } else {
- return null;
- }
- }
-
- // Code partially mooked from http://www.richler.de/en/php-redirect/
- // (C) 2006 by Heiko Richler http://www.richler.de/
- // LGPL
-
- public function redirectTo($destination)
- {
- $old = 'http'.
- (($_SERVER['HTTPS'] == 'on') ? 'S' : '').
- '://'.
- $_SERVER['HTTP_HOST'].
- $_SERVER['REQUEST_URI'].
- $_SERVER['QUERY_STRING'];
- if ($old == $destination) { // this would be a loop!
- // error_log(...) ?
- return false;
- }
-
- http_response_code(301);
- header("Location: {$destination}");
-
- echo "{$destination}\n";
-
- exit;
- }
-
- public function getServerName()
- {
- if (!empty($this->hostname)) {
- return $this->hostname;
- } else {
- return $this->nickname . '.' . self::$wildcard;
- }
- }
-
- /**
- * Return site meta-info tags as an array
- * @return array of strings
- */
- public function getTags()
- {
- return Status_network_tag::getTags($this->site_id);
- }
-
- /**
- * Save a given set of tags
- * @param array tags
- * @fixme only add/remove differentials
- */
- public function setTags(array $tags)
- {
- $this->clearTags();
- foreach ($tags as $tag) {
- if (!empty($tag)) {
- $snt = new Status_network_tag();
- $snt->site_id = $this->site_id;
- $snt->tag = $tag;
- $snt->created = common_sql_now();
-
- $id = $snt->insert();
- if (!$id) {
- // TRANS: Exception thrown when a tag cannot be saved.
- throw new Exception(_("Unable to save tag."));
- }
- }
- }
-
- return true;
- }
-
- public function clearTags()
- {
- $tag = new Status_network_tag();
- $tag->site_id = $this->site_id;
-
- if ($tag->find()) {
- while ($tag->fetch()) {
- $tag->delete();
- }
- }
-
- $tag->free();
- }
-
- /**
- * Check if this site record has a particular meta-info tag attached.
- * @param string $tag
- * @return bool
- */
- public function hasTag($tag)
- {
- return in_array($tag, $this->getTags());
- }
-}
diff --git a/src/Entity/Status_network_tag.php b/src/Entity/Status_network_tag.php
deleted file mode 100644
index ba73e7ba1b..0000000000
--- a/src/Entity/Status_network_tag.php
+++ /dev/null
@@ -1,144 +0,0 @@
-.
- */
-
-if (!defined('STATUSNET')) { exit(1); }
-
-class Status_network_tag extends Safe_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'status_network_tag'; // table name
- public $site_id; // int(4) primary_key not_null
- public $tag; // varchar(64) primary_key not_null
- public $created; // datetime() not_null default_0000-00-00%2000%3A00%3A00
-
-
- function __construct()
- {
- global $config;
- global $_DB_DATAOBJECT;
-
- $sn = new Status_network();
- $sn->_connect();
-
- $config['db']['table_'. $this->tableName()] = $sn->_database;
-
- $this->_connect();
- }
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- /* Static get */
- static function getKV($k,$v=null)
- {
- // TODO: This probably has to be converted to a non-static call
- $i = DB_DataObject::staticGet('Status_network_tag',$k,$v);
-
- // Don't use local process cache; if we're fetching multiple
- // times it's because we're reloading it in a long-running
- // process; we need a fresh copy!
- global $_DB_DATAOBJECT;
- unset($_DB_DATAOBJECT['CACHE']['status_network_tag']);
- return $i;
- }
-
- static function pkeyGet($kv)
- {
- return Memcached_DataObject::pkeyGetClass('Status_network_tag', $kv);
- }
-
- /**
- * Fetch the (possibly cached) tag entries for the given site id.
- * Uses status_network's cache settings.
- *
- * @param string $site_id
- * @return array of strings
- */
- static function getTags($site_id)
- {
- $key = 'status_network_tags:' . $site_id;
- if (Status_network::$cache) {
- $packed = Status_network::$cache->get($key);
- if (is_string($packed)) {
- if ($packed == '') {
- return array();
- } else {
- return explode('|', $packed);
- }
- }
- }
-
- $result = array();
-
- $tags = new Status_network_tag();
- $tags->site_id = $site_id;
- if ($tags->find()) {
- while ($tags->fetch()) {
- $result[] = $tags->tag;
- }
- }
-
- if (Status_network::$cache) {
- $packed = implode('|', $result);
- Status_network::$cache->set($key, $packed, 0, 3600);
- }
-
- return $result;
- }
-
- /**
- * Drop the cached tag entries for this site.
- * Needed after inserting/deleting a tag entry.
- */
- function decache()
- {
- $key = 'status_network_tags:' . $this->site_id;
- if (Status_network::$cache || Status_network::$cacheInitialized) {
- // FIXME: this was causing errors, so I'm hiding them.
- // I'm a big chicken and lazy.
- @Status_network::$cache->delete($key);
- }
- }
-
- function insert()
- {
- $ret = parent::insert();
- $this->decache();
- return $ret;
- }
-
- function delete($useWhere=false)
- {
- $this->decache();
- return parent::delete($useWhere);
- }
-
- static function withTag($tag)
- {
- $snt = new Status_network_tag();
-
- $snt->tag = $tag;
-
- $snt->find();
-
- return $snt;
- }
-}
diff --git a/src/Entity/Subscription.php b/src/Entity/Subscription.php
index 0737d3b486..da017f7d5e 100644
--- a/src/Entity/Subscription.php
+++ b/src/Entity/Subscription.php
@@ -1,31 +1,41 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * @copyright 2008, 2009 StatusNet, Inc.
+ * Entity for subscription
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for subscription
- */
-class Subscription extends Managed_DataObject
+class Subscription
{
+<<<<<<< HEAD
const CACHE_WINDOW = 201;
const FORCE = true;
@@ -446,5 +456,36 @@ class Subscription extends Managed_DataObject
public function getUri()
{
return $this->uri ?: self::newUri($this->getSubscriber(), $this->getSubscribed(), $this->created);
+=======
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'subscription',
+ 'fields' => [
+ 'subscriber' => ['type' => 'int', 'not null' => true, 'description' => 'profile listening'],
+ 'subscribed' => ['type' => 'int', 'not null' => true, 'description' => 'profile being listened to'],
+ 'jabber' => ['type' => 'bool', 'default' => true, 'description' => 'deliver jabber messages'],
+ 'sms' => ['type' => 'bool', 'default' => true, 'description' => 'deliver sms messages'],
+ 'token' => ['type' => 'varchar', 'length' => 191, 'description' => 'authorization token'],
+ 'secret' => ['type' => 'varchar', 'length' => 191, 'description' => 'token secret'],
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['subscriber', 'subscribed'],
+ 'unique keys' => [
+ 'subscription_uri_key' => ['uri'],
+ ],
+ 'indexes' => [
+ 'subscription_subscriber_idx' => ['subscriber', 'created'],
+ 'subscription_subscribed_idx' => ['subscribed', 'created'],
+ 'subscription_token_idx' => ['token'],
+ ],
+ ];
+>>>>>>> e4b74a6aaf ([DATABASE] Extracted schemaDef method from old files and refactored onto new files)
}
}
diff --git a/src/Entity/SubscriptionQueue.php b/src/Entity/SubscriptionQueue.php
new file mode 100644
index 0000000000..6f495033a6
--- /dev/null
+++ b/src/Entity/SubscriptionQueue.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for Subscription queue
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class SubscriptionQueue
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'subscription_queue',
+ 'description' => 'Holder for subscription requests awaiting moderation.',
+ 'fields' => [
+ 'subscriber' => ['type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'],
+ 'subscribed' => ['type' => 'int', 'not null' => true, 'description' => 'remote or local profile being subscribed to'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ ],
+ 'primary key' => ['subscriber', 'subscribed'],
+ 'indexes' => [
+ 'subscription_queue_subscriber_created_idx' => ['subscriber', 'created'],
+ 'subscription_queue_subscribed_created_idx' => ['subscribed', 'created'],
+ ],
+ 'foreign keys' => [
+ 'subscription_queue_subscriber_fkey' => ['profile', ['subscriber' => 'id']],
+ 'subscription_queue_subscribed_fkey' => ['profile', ['subscribed' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Subscription_queue.php b/src/Entity/Subscription_queue.php
deleted file mode 100644
index b09a635676..0000000000
--- a/src/Entity/Subscription_queue.php
+++ /dev/null
@@ -1,124 +0,0 @@
-.
-
-/**
- * Table Definition for subscription_queue
- */
-
-defined('GNUSOCIAL') || die();
-
-class Subscription_queue extends Managed_DataObject
-{
- public $__table = 'subscription_queue'; // table name
- public $subscriber;
- public $subscribed;
- public $created;
-
- public static function schemaDef()
- {
- return array(
- 'description' => 'Holder for subscription requests awaiting moderation.',
- 'fields' => array(
- 'subscriber' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile making the request'),
- 'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'remote or local profile being subscribed to'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- ),
- 'primary key' => array('subscriber', 'subscribed'),
- 'indexes' => array(
- 'subscription_queue_subscriber_created_idx' => array('subscriber', 'created'),
- 'subscription_queue_subscribed_created_idx' => array('subscribed', 'created'),
- ),
- 'foreign keys' => array(
- 'subscription_queue_subscriber_fkey' => array('profile', array('subscriber' => 'id')),
- 'subscription_queue_subscribed_fkey' => array('profile', array('subscribed' => 'id')),
- )
- );
- }
-
- public static function saveNew(Profile $subscriber, Profile $subscribed)
- {
- if (self::exists($subscriber, $subscribed)) {
- throw new AlreadyFulfilledException(_('This subscription request is already in progress.'));
- }
- $rq = new Subscription_queue();
- $rq->subscriber = $subscriber->id;
- $rq->subscribed = $subscribed->id;
- $rq->created = common_sql_now();
- $rq->insert();
- return $rq;
- }
-
- public static function exists(Profile $subscriber, Profile $other)
- {
- $sub = Subscription_queue::pkeyGet(array('subscriber' => $subscriber->getID(),
- 'subscribed' => $other->getID()));
- return ($sub instanceof Subscription_queue);
- }
-
- public static function getSubQueue(Profile $subscriber, Profile $other)
- {
- // This is essentially a pkeyGet but we have an object to return in NoResultException
- $sub = new Subscription_queue();
- $sub->subscriber = $subscriber->id;
- $sub->subscribed = $other->id;
- if (!$sub->find(true)) {
- throw new NoResultException($sub);
- }
- return $sub;
- }
-
- /**
- * Complete a pending subscription, as we've got approval of some sort.
- *
- * @return Subscription
- */
- public function complete()
- {
- $subscriber = Profile::getKV('id', $this->subscriber);
- $subscribed = Profile::getKV('id', $this->subscribed);
- try {
- $sub = Subscription::start($subscriber, $subscribed, Subscription::FORCE);
- $this->delete();
- } catch (AlreadyFulfilledException $e) {
- common_debug('Tried to start a subscription which already existed.');
- }
- return $sub;
- }
-
- /**
- * Cancel an outstanding subscription request to the other profile.
- */
- public function abort()
- {
- $subscriber = Profile::getKV('id', $this->subscriber);
- $subscribed = Profile::getKV('id', $this->subscribed);
- if (Event::handle('StartCancelSubscription', array($subscriber, $subscribed))) {
- $this->delete();
- Event::handle('EndCancelSubscription', array($subscriber, $subscribed));
- }
- }
-
- /**
- * Send notifications via email etc to group administrators about
- * this exciting new pending moderation queue item!
- */
- public function notify()
- {
- $other = Profile::getKV('id', $this->subscriber);
- $listenee = User::getKV('id', $this->subscribed);
- mail_subscribe_pending_notify_profile($listenee, $other);
- }
-}
diff --git a/src/Entity/Token.php b/src/Entity/Token.php
index 196379701c..fc8146309a 100644
--- a/src/Entity/Token.php
+++ b/src/Entity/Token.php
@@ -1,62 +1,64 @@
.
+
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for token
+ * Entity for User token
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-defined('GNUSOCIAL') || die();
-
-class Token extends Managed_DataObject
+class Token
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE BEGIN
- public $__table = 'token'; // table name
- public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
- public $tok; // char(32) primary_key not_null
- public $secret; // char(32) not_null
- public $type; // tinyint(1) not_null
- public $state; // tinyint(1)
- public $verifier; // varchar(191) not 255 because utf8mb4 takes more space
- public $verified_callback; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ // AUTOCODE END
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
- public static function schemaDef()
+ public static function schemaDef(): array
{
- return array(
+ return [
+ 'name' => 'token',
'description' => 'OAuth token record',
- 'fields' => array(
- 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
- 'tok' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'),
- 'secret' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'secret value'),
- 'type' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'request or access'),
- 'state' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'for requests, 0 = initial, 1 = authorized, 2 = used'),
- 'verifier' => array('type' => 'varchar', 'length' => 191, 'description' => 'verifier string for OAuth 1.0a'),
- 'verified_callback' => array('type' => 'varchar', 'length' => 191, 'description' => 'verified callback URL for OAuth 1.0a'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('consumer_key', 'tok'),
- 'foreign keys' => array(
- 'token_consumer_key_fkey' => array('consumer', array('consumer_key' => 'consumer_key')),
- ),
- );
+ 'fields' => [
+ 'consumer_key' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'],
+ 'tok' => ['type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'],
+ 'secret' => ['type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'secret value'],
+ 'type' => ['type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'request or access'],
+ 'state' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'for requests, 0 = initial, 1 = authorized, 2 = used'],
+ 'verifier' => ['type' => 'varchar', 'length' => 191, 'description' => 'verifier string for OAuth 1.0a'],
+ 'verified_callback' => ['type' => 'varchar', 'length' => 191, 'description' => 'verified callback URL for OAuth 1.0a'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['consumer_key', 'tok'],
+ 'foreign keys' => [
+ 'token_consumer_key_fkey' => ['consumer', ['consumer_key' => 'consumer_key']],
+ ],
+ ];
}
}
diff --git a/src/Entity/UnavailableStatusNetwork.php b/src/Entity/UnavailableStatusNetwork.php
new file mode 100644
index 0000000000..9fe4e6e839
--- /dev/null
+++ b/src/Entity/UnavailableStatusNetwork.php
@@ -0,0 +1,57 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity that Keeps a list of unavailable status network names
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UnavailableStatusNetwork
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'unavailable_status_network',
+ 'description' => 'An unavailable status network nickname',
+ 'fields' => [
+ 'nickname' => ['type' => 'varchar',
+ 'length' => 64,
+ 'not null' => true, 'description' => 'nickname not to use', ],
+ 'created' => ['type' => 'datetime',
+ 'not null' => true, 'default' => '0000-00-00 00:00:00', ],
+ ],
+ 'primary key' => ['nickname'],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/Unavailable_status_network.php b/src/Entity/Unavailable_status_network.php
deleted file mode 100644
index 415acaa002..0000000000
--- a/src/Entity/Unavailable_status_network.php
+++ /dev/null
@@ -1,63 +0,0 @@
-.
-
-/**
- * Data class for unavailable status networks
- *
- * @category Data
- * @package GNUsocial
- * @author Evan Prodromou
- * @copyright 2011 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Keeps a list of unavailable status network names
- *
- * @category Data
- * @package GNUsocial
- * @author Evan Prodromou
- * @copyright 2011 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- *
- * @see Managed_DataObject
- */
-class Unavailable_status_network extends Managed_DataObject
-{
- public $__table = 'unavailable_status_network'; // table name
-
- public $nickname; // varchar(64) UUID
- public $created; // datetime()
-
- /**
- * The One True Thingy that must be defined and declared.
- */
- public static function schemaDef()
- {
- return array(
- 'description' => 'An unavailable status network nickname',
- 'fields' => array(
- 'nickname' => array('type' => 'varchar',
- 'length' => 64,
- 'not null' => true, 'description' => 'nickname not to use'),
- 'created' => array('type' => 'datetime'),
- ),
- 'primary key' => array('nickname'),
- );
- }
-}
diff --git a/src/Entity/User.php b/src/Entity/User.php
index 00299ee0ee..5fdd461828 100644
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -1,67 +1,50 @@
.
-defined('GNUSOCIAL') || die();
+/* {{{ License
+ * This file is part of GNU social - https://www.gnu.org/software/social
+ *
+ * GNU social 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.
+ *
+ * GNU social 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 GNU social. If not, see .
+ }}} */
+
+namespace App\Entity;
/**
- * Table Definition for user
+ * Entity for users
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
-
-class User extends Managed_DataObject
+class User
{
- const SUBSCRIBE_POLICY_OPEN = 0;
- const SUBSCRIBE_POLICY_MODERATE = 1;
+ // AUTOCODE BEGIN
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
+ // AUTOCODE END
- public $__table = 'user'; // table name
- public $id; // int(4) primary_key not_null
- public $nickname; // varchar(64) unique_key
- public $password; // text
- public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $emailnotifysub; // bool default_true
- public $emailnotifyfav; // tinyint(1) default_null
- public $emailnotifynudge; // bool default_true
- public $emailnotifymsg; // bool default_true
- public $emailnotifyattn; // bool default_true
- public $language; // varchar(50)
- public $timezone; // varchar(50)
- public $emailpost; // bool default_true
- public $sms; // varchar(64) unique_key
- public $carrier; // int(4)
- public $smsnotify; // bool default_false
- public $smsreplies; // bool default_false
- public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $autosubscribe; // bool default_false
- public $subscribe_policy; // tinyint(1)
- public $urlshorteningservice; // varchar(50) default_ur1.ca
- public $private_stream; // bool default_false
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
+ public static function schemaDef(): array
{
- return array(
+ return [
+ 'name' => 'user',
'description' => 'local users',
+<<<<<<< HEAD
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'),
@@ -1079,5 +1062,51 @@ class User extends Managed_DataObject
public function setPref($namespace, $topic, $data)
{
return $this->getProfile()->setPref($namespace, $topic, $data);
+=======
+ 'fields' => [
+ 'id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'],
+ 'nickname' => ['type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'],
+ 'password' => ['type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'],
+ 'email' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'],
+ 'incomingemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'],
+ 'emailnotifysub' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of subscriptions'],
+ 'emailnotifyfav' => ['type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'],
+ 'emailnotifynudge' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of nudges'],
+ 'emailnotifymsg' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of direct messages'],
+ 'emailnotifyattn' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of @-replies'],
+ 'language' => ['type' => 'varchar', 'length' => 50, 'description' => 'preferred language'],
+ 'timezone' => ['type' => 'varchar', 'length' => 50, 'description' => 'timezone'],
+ 'emailpost' => ['type' => 'bool', 'default' => true, 'description' => 'Post by email'],
+ 'sms' => ['type' => 'varchar', 'length' => 64, 'description' => 'sms phone number'],
+ 'carrier' => ['type' => 'int', 'description' => 'foreign key to sms_carrier'],
+ 'smsnotify' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS'],
+ 'smsreplies' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS on replies'],
+ 'smsemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'],
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'],
+ 'autosubscribe' => ['type' => 'bool', 'default' => false, 'description' => 'automatically subscribe to users who subscribe to us'],
+ 'subscribe_policy' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'],
+ 'urlshorteningservice' => ['type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'],
+ 'private_stream' => ['type' => 'bool', 'default' => false, 'description' => 'whether to limit all notices to followers only'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'user_nickname_key' => ['nickname'],
+ 'user_email_key' => ['email'],
+ 'user_incomingemail_key' => ['incomingemail'],
+ 'user_sms_key' => ['sms'],
+ 'user_uri_key' => ['uri'],
+ ],
+ 'foreign keys' => [
+ 'user_id_fkey' => ['profile', ['id' => 'id']],
+ 'user_carrier_fkey' => ['sms_carrier', ['carrier' => 'id']],
+ ],
+ 'indexes' => [
+ 'user_created_idx' => ['created'],
+ 'user_smsemail_idx' => ['smsemail'],
+ ],
+ ];
+>>>>>>> e4b74a6aaf ([DATABASE] Extracted schemaDef method from old files and refactored onto new files)
}
}
diff --git a/src/Entity/UserGroup.php b/src/Entity/UserGroup.php
new file mode 100644
index 0000000000..03afdcc9e1
--- /dev/null
+++ b/src/Entity/UserGroup.php
@@ -0,0 +1,83 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for groups a user is in
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UserGroup
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'user_group',
+ 'fields' => [
+ 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
+ 'profile_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'],
+
+ 'nickname' => ['type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'],
+ 'fullname' => ['type' => 'varchar', 'length' => 191, 'description' => 'display name'],
+ 'homepage' => ['type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'],
+ 'description' => ['type' => 'text', 'description' => 'group description'],
+ 'location' => ['type' => 'varchar', 'length' => 191, 'description' => 'related physical location, if any'],
+
+ 'original_logo' => ['type' => 'varchar', 'length' => 191, 'description' => 'original size logo'],
+ 'homepage_logo' => ['type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'],
+ 'stream_logo' => ['type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'],
+ 'mini_logo' => ['type' => 'varchar', 'length' => 191, 'description' => 'mini logo'],
+
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+
+ 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'],
+ 'mainpage' => ['type' => 'varchar', 'length' => 191, 'description' => 'page for group info to link to'],
+ 'join_policy' => ['type' => 'int', 'size' => 'tiny', 'description' => '0=open; 1=requires admin approval'],
+ 'force_scope' => ['type' => 'int', 'size' => 'tiny', 'description' => '0=never,1=sometimes,-1=always'],
+ ],
+ 'primary key' => ['id'],
+ 'unique keys' => [
+ 'user_group_uri_key' => ['uri'],
+ // when it's safe and everyone's run upgrade.php 'user_profile_id_key' => array('profile_id'),
+ ],
+ 'foreign keys' => [
+ 'user_group_id_fkey' => ['profile', ['profile_id' => 'id']],
+ ],
+ 'indexes' => [
+ 'user_group_nickname_idx' => ['nickname'],
+ 'user_group_profile_id_idx' => ['profile_id'], //make this unique in future
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/UserImPrefs.php b/src/Entity/UserImPrefs.php
new file mode 100644
index 0000000000..5a9b10a8f9
--- /dev/null
+++ b/src/Entity/UserImPrefs.php
@@ -0,0 +1,63 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user IM preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Craig Andrews
+ * @copyright 2009 StatusNet Inc.
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UserImPrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'user_im_prefs',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user'],
+ 'screenname' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'],
+ 'transport' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'],
+ 'notify' => ['type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Notify when a new notice is sent'],
+ 'replies' => ['type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Send replies from people not subscribed to'],
+ 'updatefrompresence' => ['type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Update from presence.'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id', 'transport'],
+ 'unique keys' => [
+ 'transport_screenname_key' => ['transport', 'screenname'],
+ ],
+ 'foreign keys' => [
+ 'user_im_prefs_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/UserLocationPrefs.php b/src/Entity/UserLocationPrefs.php
new file mode 100644
index 0000000000..0bfac53788
--- /dev/null
+++ b/src/Entity/UserLocationPrefs.php
@@ -0,0 +1,56 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user location preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Evan Prodromou
+ * @copyright 2009 StatusNet Inc.
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UserLocationPrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'user_location_prefs',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user who has the preference'],
+ 'share_location' => ['type' => 'bool', 'default' => true, 'description' => 'Whether to share location data'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id'],
+ 'foreign keys' => [
+ 'user_location_prefs_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/UserUrlshortenerPrefs.php b/src/Entity/UserUrlshortenerPrefs.php
new file mode 100644
index 0000000000..01cb6ae8eb
--- /dev/null
+++ b/src/Entity/UserUrlshortenerPrefs.php
@@ -0,0 +1,60 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for user's url shortener preferences
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UserUrlshortenerPrefs
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'user_urlshortener_prefs',
+ 'fields' => [
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user'],
+ 'urlshorteningservice' => ['type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'],
+ 'maxurllength' => ['type' => 'int', 'not null' => true, 'description' => 'urls greater than this length will be shortened, 0 = always, null = never'],
+ 'maxnoticelength' => ['type' => 'int', 'not null' => true, 'description' => 'notices with content greater than this value will have all urls shortened, 0 = always, -1 = only if notice text is longer than max allowed'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['user_id'],
+ 'foreign keys' => [
+ 'user_urlshortener_prefs_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/UserUsername.php b/src/Entity/UserUsername.php
new file mode 100644
index 0000000000..09b387a283
--- /dev/null
+++ b/src/Entity/UserUsername.php
@@ -0,0 +1,62 @@
+.
+ }}} */
+
+namespace App\Entity;
+
+/**
+ * Entity for association between user and username
+ *
+ * @category DB
+ * @package GNUsocial
+ *
+ * @author Zach Copley
+ * @copyright 2010 StatusNet Inc.
+ * @author Mikael Nordfeldth
+ * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
+ * @author Hugo Sales
+ * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
+ * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
+ */
+class UserUsername
+{
+ // AUTOCODE BEGIN
+
+ // AUTOCODE END
+
+ public static function schemaDef(): array
+ {
+ return [
+ 'name' => 'user_username',
+ 'fields' => [
+ 'provider_name' => ['type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'provider name'],
+ 'username' => ['type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'username'],
+ 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'],
+ 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'],
+ 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
+ ],
+ 'primary key' => ['provider_name', 'username'],
+ 'indexes' => [
+ 'user_id_idx' => ['user_id'],
+ ],
+ 'foreign keys' => [
+ 'user_username_user_id_fkey' => ['user', ['user_id' => 'id']],
+ ],
+ ];
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/User_group.php b/src/Entity/User_group.php
deleted file mode 100644
index 7e86140d77..0000000000
--- a/src/Entity/User_group.php
+++ /dev/null
@@ -1,887 +0,0 @@
-.
-
-defined('GNUSOCIAL') || die();
-
-/**
- * Table Definition for user_group
- */
-class User_group extends Managed_DataObject
-{
- const JOIN_POLICY_OPEN = 0;
- const JOIN_POLICY_MODERATE = 1;
- const CACHE_WINDOW = 201;
-
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'user_group'; // table name
- public $id; // int(4) primary_key not_null
- public $profile_id; // int(4) primary_key not_null
- public $nickname; // varchar(64)
- public $fullname; // varchar(191) not 255 because utf8mb4 takes more space
- public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
- public $description; // text
- public $location; // varchar(191) not 255 because utf8mb4 takes more space
- public $original_logo; // varchar(191) not 255 because utf8mb4 takes more space
- public $homepage_logo; // varchar(191) not 255 because utf8mb4 takes more space
- public $stream_logo; // varchar(191) not 255 because utf8mb4 takes more space
- public $mini_logo; // varchar(191) not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
- public $join_policy; // tinyint
- public $force_scope; // tinyint
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public function getObjectType()
- {
- return ActivityObject::GROUP;
- }
-
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
-
- 'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'),
- 'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name'),
- 'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
- 'description' => array('type' => 'text', 'description' => 'group description'),
- 'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'related physical location, if any'),
-
- 'original_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'original size logo'),
- 'homepage_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'),
- 'stream_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'),
- 'mini_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'mini logo'),
-
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
- 'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page for group info to link to'),
- 'join_policy' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=open; 1=requires admin approval'),
- 'force_scope' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=never,1=sometimes,-1=always'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'user_group_uri_key' => array('uri'),
-// when it's safe and everyone's run upgrade.php 'user_profile_id_key' => array('profile_id'),
- ),
- 'foreign keys' => array(
- 'user_group_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
- ),
- 'indexes' => array(
- 'user_group_nickname_idx' => array('nickname'),
- 'user_group_created_id_idx' => array('created', 'id'),
- 'user_group_profile_id_idx' => array('profile_id'), //make this unique in future
- ),
- );
- }
-
- protected $_profile = array();
-
- /**
- * @return Profile
- *
- * @throws GroupNoProfileException if user has no profile
- */
- public function getProfile()
- {
- if (!isset($this->_profile[$this->profile_id])) {
- $profile = Profile::getKV('id', $this->profile_id);
- if (!$profile instanceof Profile) {
- throw new GroupNoProfileException($this);
- }
- $this->_profile[$this->profile_id] = $profile;
- }
- return $this->_profile[$this->profile_id];
- }
-
- public function getNickname()
- {
- return $this->getProfile()->getNickname();
- }
-
- public function getFullname()
- {
- return $this->getProfile()->getFullname();
- }
-
- public static function defaultLogo($size)
- {
- static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile',
- AVATAR_STREAM_SIZE => 'stream',
- AVATAR_MINI_SIZE => 'mini');
- return Theme::path('default-avatar-'.$sizenames[$size].'.png');
- }
-
- public function homeUrl()
- {
- return $this->getProfile()->getUrl();
- }
-
- public function getUri()
- {
- $uri = null;
- if (Event::handle('StartUserGroupGetUri', array($this, &$uri))) {
- if (!empty($this->uri)) {
- $uri = $this->uri;
- } elseif ($this->isLocal()) {
- $uri = common_local_url('groupbyid', ['id' => $this->id]);
- }
- }
- Event::handle('EndUserGroupGetUri', array($this, &$uri));
- return $uri;
- }
-
- public function permalink()
- {
- $url = null;
- if (Event::handle('StartUserGroupPermalink', array($this, &$url))) {
- if ($this->isLocal()) {
- $url = common_local_url('groupbyid', ['id' => $this->id]);
- }
- }
- Event::handle('EndUserGroupPermalink', array($this, &$url));
- return $url;
- }
-
- public function getNotices($offset, $limit, $since_id = null, $max_id = null)
- {
- // FIXME: Get the Profile::current() some other way, to avoid
- // possible confusion between current session and queue process.
- $stream = new GroupNoticeStream($this, Profile::current());
-
- return $stream->getNotices($offset, $limit, $since_id, $max_id);
- }
-
- public function getMembers($offset = 0, $limit = null)
- {
- $ids = null;
- if (is_null($limit) || $offset + $limit > User_group::CACHE_WINDOW) {
- $ids = $this->getMemberIDs($offset, $limit);
- } else {
- $key = sprintf('group:member_ids:%d', $this->id);
- $window = self::cacheGet($key);
- if ($window === false) {
- $window = $this->getMemberIDs(0, User_group::CACHE_WINDOW);
- self::cacheSet($key, $window);
- }
-
- $ids = array_slice($window, $offset, $limit);
- }
-
- return Profile::multiGet('id', $ids);
- }
-
- public function getMemberIDs($offset = 0, $limit = null)
- {
- $gm = new Group_member();
-
- $gm->selectAdd();
- $gm->selectAdd('profile_id');
-
- $gm->group_id = $this->id;
-
- $gm->orderBy('created DESC, profile_id DESC');
-
- if (!is_null($limit)) {
- $gm->limit($offset, $limit);
- }
-
- $ids = array();
-
- if ($gm->find()) {
- while ($gm->fetch()) {
- $ids[] = $gm->profile_id;
- }
- }
-
- return $ids;
- }
-
- /**
- * Get pending members, who have not yet been approved.
- *
- * @param int $offset
- * @param int $limit
- * @return Profile
- */
- public function getRequests($offset = 0, $limit = null)
- {
- $rq = new Group_join_queue();
- $rq->group_id = $this->id;
-
- $members = new Profile();
-
- $members->joinAdd(['id', $rq, 'profile_id']);
-
- if ($limit != null) {
- $members->limit($offset, $limit);
- }
-
- $members->find();
-
- return $members;
- }
-
- public function getAdminCount()
- {
- $block = new Group_member();
- $block->group_id = $this->id;
- $block->is_admin = true;
-
- return $block->count();
- }
-
- public function getMemberCount()
- {
- $key = sprintf("group:member_count:%d", $this->id);
-
- $cnt = self::cacheGet($key);
-
- if (is_integer($cnt)) {
- return (int) $cnt;
- }
-
- $mem = new Group_member();
- $mem->group_id = $this->id;
-
- // XXX: why 'distinct'?
-
- $cnt = (int) $mem->count('distinct profile_id');
-
- self::cacheSet($key, $cnt);
-
- return $cnt;
- }
-
- public function getBlockedCount()
- {
- // XXX: WORM cache this
-
- $block = new Group_block();
- $block->group_id = $this->id;
-
- return $block->count();
- }
-
- public function getQueueCount()
- {
- // XXX: WORM cache this
-
- $queue = new Group_join_queue();
- $queue->group_id = $this->id;
-
- return $queue->count();
- }
-
- // offset is null because DataObject wants it, 0 would mean no results
- public function getAdmins($offset = null, $limit = null)
- {
- $admins = new Profile();
- $admins->joinAdd(['id', 'group_member:profile_id']);
- $admins->whereAdd(sprintf(
- 'group_member.group_id = %d AND group_member.is_admin IS TRUE',
- $this->getID()
- ));
- $admins->orderBy('group_member.modified, group_member.profile_id');
- $admins->limit($offset, $limit);
- $admins->find();
-
- return $admins;
- }
-
- // offset is null because DataObject wants it, 0 would mean no results
- public function getBlocked($offset = null, $limit = null)
- {
- $blocked = new Profile();
- $blocked->joinAdd(array('id', 'group_block:blocked'));
- $blocked->whereAdd(sprintf('group_block.group_id = %u', $this->id));
- $blocked->orderBy('group_block.modified DESC, group_block.blocked DESC');
- $blocked->limit($offset, $limit);
- $blocked->find();
-
- return $blocked;
- }
-
- public function setOriginal($filename)
- {
- // This should be handled by the Profile->setOriginal function so user and group avatars are handled the same
- $imagefile = new ImageFile(null, Avatar::path($filename));
-
- $sizes = array('homepage_logo' => AVATAR_PROFILE_SIZE,
- 'stream_logo' => AVATAR_STREAM_SIZE,
- 'mini_logo' => AVATAR_MINI_SIZE);
-
- $orig = clone($this);
- $this->original_logo = Avatar::url($filename);
- foreach ($sizes as $name=>$size) {
- $filename = Avatar::filename(
- $this->profile_id,
- image_type_to_extension($imagefile->preferredType()),
- $size,
- common_timestamp()
- );
- $imagefile->resizeTo(Avatar::path($filename), array('width'=>$size, 'height'=>$size));
- $this->$name = Avatar::url($filename);
- }
- common_debug(common_log_objstring($this));
- return $this->update($orig);
- }
-
- public function getBestName()
- {
- return ($this->fullname) ? $this->fullname : $this->nickname;
- }
-
- /**
- * Gets the full name (if filled) with nickname as a parenthetical, or the nickname alone
- * if no fullname is provided.
- *
- * @return string
- */
- public function getFancyName()
- {
- if ($this->fullname) {
- // TRANS: Full name of a profile or group followed by nickname in parens
- return sprintf(_m('FANCYNAME', '%1$s (%2$s)'), $this->fullname, $this->nickname);
- } else {
- return $this->nickname;
- }
- }
-
- public function getAliases()
- {
- $aliases = array();
-
- // XXX: cache this
-
- $alias = new Group_alias();
-
- $alias->group_id = $this->id;
-
- if ($alias->find()) {
- while ($alias->fetch()) {
- $aliases[] = $alias->alias;
- }
- }
-
- $alias->free();
-
- return $aliases;
- }
-
- public function setAliases($newaliases)
- {
- $newaliases = array_unique($newaliases);
-
- $oldaliases = $this->getAliases();
-
- // Delete stuff that's old that not in new
-
- $to_delete = array_diff($oldaliases, $newaliases);
-
- // Insert stuff that's in new and not in old
-
- $to_insert = array_diff($newaliases, $oldaliases);
-
- $alias = new Group_alias();
-
- $alias->group_id = $this->id;
-
- foreach ($to_delete as $delalias) {
- $alias->alias = $delalias;
- $result = $alias->delete();
- if (!$result) {
- common_log_db_error($alias, 'DELETE', __FILE__);
- return false;
- }
- }
-
- foreach ($to_insert as $insalias) {
- if ($insalias === $this->nickname) {
- continue;
- }
- $alias->alias = Nickname::normalize($insalias, true);
- $result = $alias->insert();
- if (!$result) {
- common_log_db_error($alias, 'INSERT', __FILE__);
- return false;
- }
- }
-
- return true;
- }
-
- public static function getForNickname($nickname, Profile $profile = null)
- {
- $nickname = Nickname::normalize($nickname);
-
- // Are there any matching remote groups this profile's in?
- if ($profile instanceof Profile) {
- $group = $profile->getGroups(0, null);
- while ($group instanceof User_group && $group->fetch()) {
- if ($group->nickname == $nickname) {
- // @fixme is this the best way?
- return clone($group);
- }
- }
- }
-
- // If not, check local groups.
- $group = Local_group::getKV('nickname', $nickname);
- if ($group instanceof Local_group) {
- return User_group::getKV('id', $group->group_id);
- }
- $alias = Group_alias::getKV('alias', $nickname);
- if ($alias instanceof Group_alias) {
- return User_group::getKV('id', $alias->group_id);
- }
- return null;
- }
-
- public function getUserMembers()
- {
- // XXX: cache this
-
- $user = new User();
-
- $user->query(sprintf(
- 'SELECT id FROM %1$s INNER JOIN group_member ' .
- 'ON %1$s.id = group_member.profile_id ' .
- 'WHERE group_member.group_id = %2$d ',
- $user->escapedTableName(),
- $this->id
- ));
-
- $ids = [];
-
- while ($user->fetch()) {
- $ids[] = $user->id;
- }
-
- $user->free();
-
- return $ids;
- }
-
- public static function maxDescription()
- {
- $desclimit = common_config('group', 'desclimit');
- // null => use global limit (distinct from 0!)
- if (is_null($desclimit)) {
- $desclimit = common_config('site', 'textlimit');
- }
- return $desclimit;
- }
-
- public static function descriptionTooLong($desc)
- {
- $desclimit = self::maxDescription();
- return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
- }
-
- public function asAtomEntry($namespace = false, $source = false)
- {
- $xs = new XMLStringer(true);
-
- if ($namespace) {
- $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom',
- 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0');
- } else {
- $attrs = array();
- }
-
- $xs->elementStart('entry', $attrs);
-
- if ($source) {
- $xs->elementStart('source');
- $xs->element('id', null, $this->permalink());
- $xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
- $xs->element('link', array('href' => $this->permalink()));
- $xs->element('updated', null, $this->modified);
- $xs->elementEnd('source');
- }
-
- $xs->element('title', null, $this->nickname);
- $xs->element('summary', null, common_xml_safe_str($this->description));
-
- $xs->element('link', array('rel' => 'alternate',
- 'href' => $this->permalink()));
-
- $xs->element('id', null, $this->permalink());
-
- $xs->element('published', null, common_date_w3dtf($this->created));
- $xs->element('updated', null, common_date_w3dtf($this->modified));
-
- $xs->element(
- 'content',
- array('type' => 'html'),
- common_xml_safe_str($this->description)
- );
-
- $xs->elementEnd('entry');
-
- return $xs->getString();
- }
-
- public function asAtomAuthor()
- {
- $xs = new XMLStringer(true);
-
- $xs->elementStart('author');
- $xs->element('name', null, $this->nickname);
- $xs->element('uri', null, $this->permalink());
- $xs->elementEnd('author');
-
- return $xs->getString();
- }
-
- /**
- * Returns an XML string fragment with group information as an
- * Activity Streams noun object with the given element type.
- *
- * Assumes that 'activity', 'georss', and 'poco' namespace has been
- * previously defined.
- *
- * @param string $element one of 'actor', 'subject', 'object', 'target'
- *
- * @return string
- */
- public function asActivityNoun($element)
- {
- $noun = ActivityObject::fromGroup($this);
- return $noun->asString('activity:' . $element);
- }
-
- public function getAvatar()
- {
- return empty($this->homepage_logo)
- ? User_group::defaultLogo(AVATAR_PROFILE_SIZE)
- : $this->homepage_logo;
- }
-
- public static function register($fields)
- {
- if (!empty($fields['userid'])) {
- $profile = Profile::getKV('id', $fields['userid']);
- if ($profile && !$profile->hasRight(Right::CREATEGROUP)) {
- common_log(LOG_WARNING, "Attempted group creation from banned user: " . $profile->nickname);
-
- // TRANS: Client exception thrown when a user tries to create a group while banned.
- throw new ClientException(_('You are not allowed to create groups on this site.'), 403);
- }
- }
-
- $fields['nickname'] = Nickname::normalize($fields['nickname']);
-
- // MAGICALLY put fields into current scope
- // @fixme kill extract(); it makes debugging absurdly hard
-
- $defaults = [
- 'nickname' => null,
- 'fullname' => null,
- 'homepage' => null,
- 'description' => null,
- 'location' => null,
- 'uri' => null,
- 'mainpage' => null,
- 'aliases' => [],
- 'userid' => null,
- ];
-
- $fields = array_merge($defaults, $fields);
-
- extract($fields);
-
- $group = new User_group();
-
- if (empty($uri)) {
- // fill in later...
- $uri = null;
- }
- if (empty($mainpage)) {
- $mainpage = common_local_url('showgroup', array('nickname' => $nickname));
- }
-
- // We must create a new, incrementally assigned profile_id
- $profile = new Profile();
- $profile->nickname = $nickname;
- $profile->fullname = $fullname;
- $profile->profileurl = $mainpage;
- $profile->homepage = $homepage;
- $profile->bio = $description;
- $profile->location = $location;
- $profile->created = common_sql_now();
-
- $group->nickname = $profile->nickname;
- $group->fullname = $profile->fullname;
- $group->homepage = $profile->homepage;
- $group->description = $profile->bio;
- $group->location = $profile->location;
- $group->mainpage = $profile->profileurl;
- $group->created = $profile->created;
-
- $profile->query('START TRANSACTION');
- $id = $profile->insert();
- if ($id === false) {
- $profile->query('ROLLBACK');
- throw new ServerException(_('Profile insertion failed'));
- }
-
- $group->profile_id = $id;
- $group->uri = $uri;
-
- if (isset($fields['join_policy'])) {
- $group->join_policy = intval($fields['join_policy']);
- } else {
- $group->join_policy = 0;
- }
-
- if (isset($fields['force_scope'])) {
- $group->force_scope = intval($fields['force_scope']);
- } else {
- $group->force_scope = 0;
- }
-
- if (Event::handle('StartGroupSave', array(&$group))) {
- $result = $group->insert();
-
- if ($result === false) {
- common_log_db_error($group, 'INSERT', __FILE__);
- // TRANS: Server exception thrown when creating a group failed.
- throw new ServerException(_('Could not create group.'));
- }
-
- if (!isset($uri) || empty($uri)) {
- $orig = clone($group);
- $group->uri = common_local_url('groupbyid', array('id' => $group->id));
- $result = $group->update($orig);
- if (!$result) {
- common_log_db_error($group, 'UPDATE', __FILE__);
- // TRANS: Server exception thrown when updating a group URI failed.
- throw new ServerException(_('Could not set group URI.'));
- }
- }
-
- $result = $group->setAliases($aliases);
-
- if (!$result) {
- // TRANS: Server exception thrown when creating group aliases failed.
- throw new ServerException(_('Could not create aliases.'));
- }
-
- $member = new Group_member();
-
- $member->group_id = $group->id;
- $member->profile_id = $userid;
- $member->is_admin = true;
- $member->created = $group->created;
-
- $result = $member->insert();
-
- if (!$result) {
- common_log_db_error($member, 'INSERT', __FILE__);
- // TRANS: Server exception thrown when setting group membership failed.
- throw new ServerException(_('Could not set group membership.'));
- }
-
- self::blow('profile:groups:%d', $userid);
-
- if ($local) {
- $local_group = new Local_group();
-
- $local_group->group_id = $group->id;
- $local_group->nickname = $nickname;
- $local_group->created = common_sql_now();
-
- $result = $local_group->insert();
-
- if (!$result) {
- common_log_db_error($local_group, 'INSERT', __FILE__);
- // TRANS: Server exception thrown when saving local group information failed.
- throw new ServerException(_('Could not save local group info.'));
- }
- }
-
- Event::handle('EndGroupSave', array($group));
- }
-
- $profile->query('COMMIT');
-
- return $group;
- }
-
- /**
- * Handle cascading deletion, on the model of notice and profile.
- *
- * This should handle freeing up cached entries for the group's
- * id, nickname, URI, and aliases. There may be other areas that
- * are not de-cached in the UI, including the sidebar lists on
- * GroupsAction
- */
- public function delete($useWhere = false)
- {
- if (empty($this->id)) {
- common_log(LOG_WARNING, "Ambiguous User_group->delete(); skipping related tables.");
- return parent::delete($useWhere);
- }
-
- // Safe to delete in bulk for now
-
- $related = array('Group_inbox',
- 'Group_block',
- 'Group_member',
- 'Related_group');
-
- Event::handle('UserGroupDeleteRelated', array($this, &$related));
-
- foreach ($related as $cls) {
- $inst = new $cls();
- $inst->group_id = $this->id;
-
- if ($inst->find()) {
- while ($inst->fetch()) {
- $dup = clone($inst);
- $dup->delete();
- }
- }
- }
-
- // And related groups in the other direction...
- $inst = new Related_group();
- $inst->related_group_id = $this->id;
- $inst->delete();
-
- // Aliases and the local_group entry need to be cleared explicitly
- // or we'll miss clearing some cache keys; that can make it hard
- // to create a new group with one of those names or aliases.
- $this->setAliases(array());
-
- // $this->isLocal() but we're using the resulting object
- $local = Local_group::getKV('group_id', $this->id);
- if ($local instanceof Local_group) {
- $local->delete();
- }
-
- $result = parent::delete($useWhere);
-
- try {
- $profile = $this->getProfile();
- $profile->delete();
- } catch (GroupNoProfileException $unp) {
- common_log(
- LOG_INFO,
- "Group {$this->nickname} has no profile; continuing deletion."
- );
- }
-
- // blow the cached ids
- self::blow('user_group:notice_ids:%d', $this->id);
-
- return $result;
- }
-
- public function update($dataObject=false)
- {
- // Whenever the User_group is updated, find the Local_group
- // and update its nickname too.
- if ($this->nickname != $dataObject->nickname) {
- $local = Local_group::getKV('group_id', $this->id);
- if ($local instanceof Local_group) {
- common_debug("Updating Local_group ({$this->id}) nickname from {$dataObject->nickname} to {$this->nickname}");
- $local->setNickname($this->nickname);
- }
- }
-
- // Also make sure the Profile table is up to date!
- $fields = array(/*group field => profile field*/
- 'nickname' => 'nickname',
- 'fullname' => 'fullname',
- 'mainpage' => 'profileurl',
- 'homepage' => 'homepage',
- 'description' => 'bio',
- 'location' => 'location',
- 'created' => 'created',
- 'modified' => 'modified',
- );
- $profile = $this->getProfile();
- $origpro = clone($profile);
- foreach ($fields as $gf=>$pf) {
- $profile->$pf = $this->$gf;
- }
- if ($profile->update($origpro) === false) {
- throw new ServerException(_('Unable to update profile'));
- }
-
- return parent::update($dataObject);
- }
-
- public function isPrivate()
- {
- return ($this->join_policy == self::JOIN_POLICY_MODERATE &&
- intval($this->force_scope) === 1);
- }
-
- public function isLocal()
- {
- $local = Local_group::getKV('group_id', $this->id);
- return ($local instanceof Local_group);
- }
-
- public static function groupsFromText($text, Profile $profile)
- {
- $groups = array();
-
- /* extract all !group */
- $count = preg_match_all(
- '/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
- strtolower($text),
- $match
- );
-
- if (!$count) {
- return $groups;
- }
-
- foreach (array_unique($match[1]) as $nickname) {
- $group = self::getForNickname($nickname, $profile);
- if ($group instanceof User_group && $profile->isMember($group)) {
- $groups[] = clone($group);
- }
- }
-
- return $groups;
- }
-
- public static function idsFromText($text, Profile $profile)
- {
- $ids = array();
- $groups = self::groupsFromText($text, $profile);
- foreach ($groups as $group) {
- $ids[$group->id] = true;
- }
- return array_keys($ids);
- }
-}
diff --git a/src/Entity/User_im_prefs.php b/src/Entity/User_im_prefs.php
deleted file mode 100644
index 0e465ad2c4..0000000000
--- a/src/Entity/User_im_prefs.php
+++ /dev/null
@@ -1,69 +0,0 @@
-.
-
-/**
- * Data class for user IM preferences
- *
- * @category Data
- * @package GNUsocial
- * @author Craig Andrews
- * @copyright 2009 StatusNet Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class User_im_prefs extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'user_im_prefs'; // table name
- public $user_id; // int(4) primary_key not_null
- public $screenname; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
- public $notify; // bool not_null default_false
- public $replies; // bool not_null default_false
- public $updatefrompresence; // bool not_null default_false
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
- 'screenname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'),
- 'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
- 'notify' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Notify when a new notice is sent'),
- 'replies' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Send replies from people not subscribed to'),
- 'updatefrompresence' => array('type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'Update from presence.'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id', 'transport'),
- 'unique keys' => array(
- 'user_im_prefs_transport_screenname_key' => array('transport', 'screenname'),
- ),
- 'foreign keys' => array(
- 'user_im_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- );
- }
-}
diff --git a/src/Entity/User_location_prefs.php b/src/Entity/User_location_prefs.php
deleted file mode 100644
index 74e09fa0ec..0000000000
--- a/src/Entity/User_location_prefs.php
+++ /dev/null
@@ -1,58 +0,0 @@
-.
-
-/**
- * Data class for user location preferences
- *
- * @category Data
- * @package GNUsocial
- * @author Evan Prodromou
- * @copyright 2009 StatusNet Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class User_location_prefs extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'user_location_prefs'; // table name
- public $user_id; // int(4) primary_key not_null
- public $share_location; // bool default_true
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who has the preference'),
- 'share_location' => array('type' => 'bool', 'default' => true, 'description' => 'Whether to share location data'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id'),
- 'foreign keys' => array(
- 'user_location_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- );
- }
-}
diff --git a/src/Entity/User_urlshortener_prefs.php b/src/Entity/User_urlshortener_prefs.php
deleted file mode 100644
index 2bf2dad76f..0000000000
--- a/src/Entity/User_urlshortener_prefs.php
+++ /dev/null
@@ -1,120 +0,0 @@
-.
-
-/*
- * @copyright 2010 StatusNet, Inc.
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
-
-defined('GNUSOCIAL') || die();
-
-class User_urlshortener_prefs extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'user_urlshortener_prefs'; // table name
- public $user_id; // int(4) primary_key not_null
- public $urlshorteningservice; // varchar(50) default_ur1.ca
- public $maxurllength; // int(4) not_null
- public $maxnoticelength; // int(4) not_null
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
- 'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
- 'maxurllength' => array('type' => 'int', 'not null' => true, 'description' => 'urls greater than this length will be shortened, 0 = always, null = never'),
- 'maxnoticelength' => array('type' => 'int', 'not null' => true, 'description' => 'notices with content greater than this value will have all urls shortened, 0 = always, -1 = only if notice text is longer than max allowed'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('user_id'),
- 'foreign keys' => array(
- 'user_urlshortener_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- );
- }
-
- public static function maxUrlLength($user)
- {
- $def = common_config('url', 'maxurllength');
-
- $prefs = self::getPrefs($user);
-
- if (empty($prefs)) {
- return $def;
- } else {
- return $prefs->maxurllength;
- }
- }
-
- public static function maxNoticeLength($user)
- {
- $def = common_config('url', 'maxnoticelength');
-
- if ($def == -1) {
- /*
- * maxContent==0 means infinite length,
- * but maxNoticeLength==0 means "always shorten"
- * so if maxContent==0 we must set this to -1
- */
- $def = Notice::maxContent() ?: -1;
- }
-
- $prefs = self::getPrefs($user);
-
- if (empty($prefs)) {
- return $def;
- } else {
- return $prefs->maxnoticelength;
- }
- }
-
- public static function urlShorteningService($user)
- {
- $def = common_config('url', 'shortener');
-
- $prefs = self::getPrefs($user);
-
- if (empty($prefs)) {
- if (!empty($user)) {
- return $user->urlshorteningservice;
- } else {
- return $def;
- }
- } else {
- return $prefs->urlshorteningservice;
- }
- }
-
- public static function getPrefs($user)
- {
- if (empty($user)) {
- return null;
- }
-
- $prefs = User_urlshortener_prefs::getKV('user_id', $user->id);
-
- return $prefs;
- }
-}
diff --git a/src/Entity/User_username.php b/src/Entity/User_username.php
deleted file mode 100644
index 8007e8b90a..0000000000
--- a/src/Entity/User_username.php
+++ /dev/null
@@ -1,79 +0,0 @@
-.
-
-/**
- * Table Definition for user_username
- */
-
-defined('GNUSOCIAL') || die();
-
-class User_username extends Managed_DataObject
-{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
- public $__table = 'user_username'; // table name
- public $user_id; // int(4) not_null
- public $provider_name; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
- public $username; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
- public $created; // datetime()
- public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
-
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'provider_name' => array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'provider name'),
- 'username' => array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'username'),
- 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
- 'created' => array('type' => 'datetime', 'description' => 'date this record was created'),
- 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
- ),
- 'primary key' => array('provider_name', 'username'),
- 'foreign keys' => array(
- 'user_username_user_id_fkey' => array('user', array('user_id' => 'id')),
- ),
- 'indexes' => array(
- 'user_username_user_id_idx' => array('user_id'),
- ),
- );
- }
-
- /**
- * Register a user with a username on a given provider
- * @param User User object
- * @param string username on the given provider
- * @param provider_name string name of the provider
- * @return mixed User_username instance if the registration succeeded, false if it did not
- */
- public static function register($user, $username, $provider_name)
- {
- $user_username = new User_username();
- $user_username->user_id = $user->id;
- $user_username->provider_name = $provider_name;
- $user_username->username = $username;
- $user_username->created = common_sql_now();
-
- if ($user_username->insert()) {
- return $user_username;
- } else {
- return false;
- }
- }
-}
diff --git a/src/Entity/status_network.ini b/src/Entity/status_network.ini
deleted file mode 100644
index b298daae46..0000000000
--- a/src/Entity/status_network.ini
+++ /dev/null
@@ -1,30 +0,0 @@
-[status_network]
-site_id = 129
-nickname = 130
-hostname = 2
-pathname = 2
-dbhost = 2
-dbuser = 2
-dbpass = 2
-dbname = 2
-sitename = 2
-theme = 2
-logo = 2
-created = 142
-modified = 384
-
-[status_network__keys]
-site_id = K
-nickname = U
-hostname = U
-pathname = U
-
-[status_network_tag]
-site_id = 129
-tag = 130
-created = 142
-
-[status_network_tag__keys]
-site_id = K
-tag = K
-