code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * GNU social's logger wrapper around Symfony's,
 * keeping our old static interface, which is more convenient and just as powerful
 *
 * @package GNUsocial
 * @category Log
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core;

use Psr\Log\LoggerInterface;

abstract class Log
{
    private static ?LoggerInterface $logger;

    public static function setLogger($l): void
    {
        self::$logger = $l;
    }

    /**
     * Simple static wrappers around Monolog's functions
     */
    public static function __callStatic(string $name, array $args)
    {
        return self::$logger->{$name}(...$args);
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Core;

use App\Entity\Note;
use App\Util\Common;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;

/**
 * Base class for all GNU social modules (plugins and components)
 */
abstract class Module
{
    /**
     * Serialize the class to store in the cache
     *
     * @param mixed $state
     */
    public static function __set_state($state)
    {
        $class = get_called_class();
        $obj   = new $class();
        foreach ($state as $k => $v) {
            $obj->{$k} = $v;
        }
        return $obj;
    }

    /**
     * Handle the $form submission for the note action for note if
     * $note->getId() == $data['note_id']
     */
    public static function noteActionHandle(Request $request, Form $form, Note $note, string $form_name, callable $handle)
    {
        if ('POST' === $request->getMethod() && $request->request->has($form_name)) {
            $form->handleRequest($request);
            if ($form->isSubmitted()) {
                $data = $form->getData();
                // Loose comparison
                if ($data['note_id'] != $note->getId()) {
                    return Event::next;
                } else {
                    $user = Common::user();
                    if (!$note->isVisibleTo($user)) {
                        // ^ Ensure user isn't trying to trip us up
                        Log::error('Suspicious activity: user ' . $user->getNickname() .
                                   ' tried to repeat note ' . $note->getId() .
                                   ', but they shouldn\'t have access to it');
                        throw new NoSuchNoteException();
                    } else {
                        if ($form->isValid()) {
                            $ret = $handle($note, $data, $user);
                            if ($ret != null) {
                                return $ret;
                            }
                        } else {
                            throw new InvalidFormException();
                        }
                    }
                }
            }
        }
    }
}

repo_name: ksuudilla/gnu

license: mit


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Module and plugin loader code, one of the main features of GNU social
 *
 * Loads plugins from `plugins/enabled`, instances them
 * and hooks its events
 *
 * @package   GNUsocial
 * @category  Modules
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core;

use App\Util\Formatting;
use AppendIterator;
use FilesystemIterator;
use Functional as F;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class ModuleManager
{
    public function __construct()
    {
        if (!defined('CACHE_FILE')) {
            define('CACHE_FILE', INSTALLDIR . '/var/cache/module_manager.php');
        }
    }

    protected static $loader;
    public static function setLoader($l)
    {
        self::$loader = $l;
    }

    protected array $modules = [];
    protected array $events  = [];

    /**
     * Add the $fqcn class from $path as a module
     */
    public function add(string $fqcn, string $path)
    {
        list($type, $module) = preg_split('/\\\\/', $fqcn, 0, PREG_SPLIT_NO_EMPTY);
        self::$loader->addPsr4("\\{$type}\\{$module}\\", dirname($path));
        $id                 = Formatting::camelCaseToSnakeCase($type . '.' . $module);
        $obj                = new $fqcn();
        $this->modules[$id] = $obj;
    }

    /**
     * Container-build-time step that preprocesses the registering of events
     */
    public function preRegisterEvents()
    {
        foreach ($this->modules as $id => $obj) {
            F\map(F\select(get_class_methods($obj),
                           F\ary(F\partial_right('App\Util\Formatting::startsWith', 'on'), 1)),
                  function (string $m) use ($obj) {
                      $ev = substr($m, 2);
                      $this->events[$ev] = $this->events[$ev] ?? [];
                      $this->events[$ev][] = [$obj, $m];
                  }
            );
        }
    }

    /**
     * Compiler pass responsible for registering all modules
     */
    public static function process(?ContainerBuilder $container = null)
    {
        $module_paths   = array_merge(glob(INSTALLDIR . '/components/*/*.php'), glob(INSTALLDIR . '/plugins/*/*.php'));
        $module_manager = new self();
        $entity_paths   = [];
        foreach ($module_paths as $path) {
            $type   = ucfirst(preg_replace('%' . INSTALLDIR . '/(component|plugin)s/.*%', '\1', $path));
            $dir    = dirname($path);
            $module = basename($dir);
            $fqcn   = "\\{$type}\\{$module}\\{$module}";
            $module_manager->add($fqcn, $path);
            if (!is_null($container) && file_exists($dir = $dir . '/Entity') && is_dir($dir)) {
                $entity_paths[] = $dir;
                $container->findDefinition('doctrine.orm.default_metadata_driver')->addMethodCall(
                    'addDriver',
                    [new Reference('app.core.schemadef_driver'), "{$type}\\{$module}\\Entity"]
                );
            }
        }

        if (!is_null($container)) {
            $container->findDefinition('app.core.schemadef_driver')
                      ->addMethodCall('addPaths', ['$paths' => $entity_paths]);
        }

        $module_manager->preRegisterEvents();

        file_put_contents(CACHE_FILE, "<?php\nreturn " . var_export($module_manager, true) . ';');
    }

    /**
     * Serialize this class, for dumping into the cache
     *
     * @param mixed $state
     */
    public static function __set_state($state)
    {
        $obj          = new self();
        $obj->modules = $state['modules'];
        $obj->events  = $state['events'];
        return $obj;
    }

    /**
     * Load the modules at runtime. In production requires the cache
     * file to exist, in dev it rebuilds this cache
     */
    public function loadModules()
    {
        if ($_ENV['APP_ENV'] == 'prod' && !file_exists(CACHE_FILE)) {
            throw new Exception('The application needs to be compiled before using in production');
        } else {
            $rdi = new AppendIterator();
            $rdi->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(INSTALLDIR . '/components', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS)));
            $rdi->append(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(INSTALLDIR . '/plugins',    FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS)));
            $time = file_exists(CACHE_FILE) ? filemtime(CACHE_FILE) : 0;

            if (F\some($rdi, function ($e) use ($time) { return $e->getMTime() > $time; })) {
                Log::info('Rebuilding plugin cache at runtime. This means we can\'t update DB definitions');
                self::process();
            }
        }

        $obj = require CACHE_FILE;

        foreach ($obj->events as $event => $callables) {
            foreach ($callables as $callable) {
                Event::addHandler($event, $callable);
            }
        }
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Core;

use App\Util\Bitmap;

class NoteScope extends Bitmap
{
    public const PUBLIC    = 1;
    public const SITE      = 2;
    public const ADDRESSEE = 4;
    public const GROUP     = 8;
    public const FOLLOWER  = 16;
    public const MESSAGE   = 32;
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Generic message wrapper
 */

namespace App\Core\Queue;

class Message
{
    public $content;
    public string $queue;

    public function __construct($content, string $queue)
    {
        $this->queue   = $queue;
        $this->content = $content;
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Generic handler, distributes work based o
 */

namespace App\Core\Queue;

use App\Core\Event;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class MessageHandler implements MessageHandlerInterface
{
    public function __invoke(Message $message)
    {
        Event::handle($message->queue, [$message->content]);
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Generic high priority message wrapper
 */

namespace App\Core\Queue;

class MessageHigh extends Message
{
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Generic low priority message wrapper
 */

namespace App\Core\Queue;

class MessageLow extends Message
{
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Queue wrapper
 *
 * @package  GNUsocial
 * @category Wrapper
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core\Queue;

use Symfony\Component\Messenger\MessageBusInterface;

abstract class Queue
{
    private static ?MessageBusInterface $message_bus;

    public static function setMessageBus($mb): void
    {
        self::$message_bus = $mb;
    }

    /**
     * Enqueue a $message in a configured trasnport, to be handled by the $queue handler
     *
     * @param object|string
     * @param mixed $message
     */
    public static function enqueue($message, string $queue, bool $high = false, array $stamps = [])
    {
        if ($high) {
            self::$message_bus->dispatch(new MessageHigh($message, $queue), $stamps);
        } else {
            self::$message_bus->dispatch(new MessageLow($message, $queue), $stamps);
        }
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.

// }}}

/**
 * Dynamic router loader and URLMapper interface atop Symfony's router
 *
 * Converts a path into a set of parameters, and vice versa
 *
 * @package   GNUsocial
 * @category  URL
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core\Router;

use App\Core\Event;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class RouteLoader extends Loader
{
    private RouteCollection $rc;

    /**
     * Route loading entry point, called from `config/routes.php`
     *
     * Must conform to symfony's interface, but the $resource is unused
     * and $type must not be null
     *
     * @param mixed $resource
     */
    public function load($resource, ?string $type = null): RouteCollection
    {
        $this->rc = new RouteCollection();

        $route_files = glob(INSTALLDIR . '/src/Routes/*.php');
        foreach ($route_files as $file) {
            require_once $file;
            $ns = '\\App\\Routes\\' . basename($file, '.php');
            $ns::load($this);
        }

        Event::handle('AddRoute', [&$this]);

        return $this->rc;
    }

    /**
     * Connect a route to a controller
     *
     * @param string     $id         Route unique id, used to generate urls, for instance
     * @param string     $uri_path   Path, possibly with {param}s
     * @param mixed      $target     Some kind of callable, typically class with `__invoke` or [object, method]
     * @param null|array $param_reqs Array of {param} => regex
     * @param null|array $options    Possible keys are ['condition', 'defaults', 'format',
     *                               'fragment', 'http-methods', 'locale', 'methods', 'schemes']
     *                               'http-methods' and 'methods' are aliases
     */
    public function connect(string $id, string $uri_path, $target, ?array $options = [], ?array $param_reqs = [])
    {
        $this->rc->add($id,
            new Route(
                // path -- URI path
                $uri_path,
                // defaults = []     -- param default values,
                // and special configuration options
                array_merge(
                    [
                        '_controller' => is_array($target) ? $target : [$target, '__invoke'],
                        '_format'     => $options['format'] ?? 'html',
                        '_fragment'   => $options['fragment'] ?? '',
                        '_locale'     => $options['locale'] ?? 'en',
                        'template'    => $options['template'] ?? '',
                    ],
                    $options['defaults'] ?? []
                ),
                // requirements = [] -- param => regex
                $param_reqs,
                // options = []      -- possible keys: compiler_class:, utf8
                // No need for a special compiler class for now,
                // Enforce UTF8
                ['utf8' => true],
                // host = ''         -- hostname (subdomain, for instance) to match,
                // we don't want this
                '',
                // schemes = []      -- URI schemes (https, ftp and such)
                $options['schemes'] ?? [],
                // methods = []      -- HTTP methods
                $options['http-methods'] ?? $options['methods'] ?? [],
                // condition = ''    -- Symfony condition expression,
                // see https://symfony.com/doc/current/routing.html#matching-expressions
                $options['condition'] ?? ''
            )
        );
    }

    /**
     * Whether this loader supports loading this route type
     * Passed the arguments from the `RoutingConfigurator::import` call from
     * `config/routes.php`
     *
     * @param mixed $resource
     */
    public function supports($resource, ?string $type = null): bool
    {
        return 'GNUsocial' === $type;
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Static wrapper for Symfony's router
 *
 * @package   GNUsocial
 * @category  URL
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core\Router;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\Router as SRouter;

abstract class Router
{
    /**
     * Generates an absolute URL, e.g. "http://example.com/dir/file".
     */
    const ABSOLUTE_URL = UrlGeneratorInterface::ABSOLUTE_URL;

    /**
     * Generates an absolute path, e.g. "/dir/file".
     */
    const ABSOLUTE_PATH = UrlGeneratorInterface::ABSOLUTE_PATH;

    /**
     * Generates a relative path based on the current request path, e.g. "../parent-file".
     *
     * @see UrlGenerator::getRelativePath()
     */
    const RELATIVE_PATH = UrlGeneratorInterface::RELATIVE_PATH;

    /**
     * Generates a network path, e.g. "//example.com/dir/file".
     * Such reference reuses the current scheme but specifies the host.
     */
    const NETWORK_PATH = UrlGeneratorInterface::NETWORK_PATH;

    public static ?SRouter $router                = null;
    public static ?UrlGeneratorInterface $url_gen = null;

    public static function setRouter($rtr, $gen): void
    {
        self::$router  = $rtr;
        self::$url_gen = $gen;
    }

    public static function url(string $id, array $args, int $type = self::ABSOLUTE_PATH): string
    {
        return self::$url_gen->generate($id, $args, $type);
    }

    public static function __callStatic(string $name, array $args)
    {
        return self::$router->{$name}(...$args);
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Wrapper around Symfony's Security service, for static access
 *
 * @package GNUsocial
 * @category Security
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core;

use HtmlSanitizer\SanitizerInterface;
use Symfony\Component\Security\Core\Security as SSecurity;

abstract class Security
{
    private static ?SSecurity $security;
    private static ?SanitizerInterface $sanitizer;

    public static function setHelper($sec, $san): void
    {
        self::$security  = $sec;
        self::$sanitizer = $san;
    }

    public static function __callStatic(string $name, array $args)
    {
        if (method_exists(self::$security, $name)) {
            return self::$security->{$name}(...$args);
        } else {
            return self::$sanitizer->{$name}(...$args);
        }
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * User role enum
 *
 * @category  User
 * @package   GNUsocial
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\Core;

use App\Util\Bitmap;

class UserRoles extends Bitmap
{
    public const ADMIN     = 1;
    public const MODERATOR = 2;
    public const USER      = 4;
    public const BOT       = 8;

    public const PREFIX = 'ROLE_';
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Module and plugin loader code, one of the main features of GNU social
 *
 * Loads plugins from `plugins/enabled`, instances them
 * and hooks its events
 *
 * @package   GNUsocial
 * @category  Modules
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\DependencyInjection\Compiler;

use App\Core\ModuleManager;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ModuleManagerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        ModuleManager::process($container);
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

/**
 * Compiler pass which triggers Symfony to tell Doctrine to
 * use out `SchemaDef` metadata driver
 *
 * @package  GNUsocial
 * @category DB
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

namespace App\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Register a new ORM driver to allow use to use the old (and better) schemaDef format
 */
class SchemaDefPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->findDefinition('doctrine.orm.default_metadata_driver')
                  ->addMethodCall('addDriver',
                                  [new Reference('app.core.schemadef_driver'), 'App\\Entity']
                  );
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.

// }}}

namespace App\Entity;

use App\Core\DB\DB;
use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for all activities we know about
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class Activity extends Entity
{
    // {{{ Autocode

    private int $id;
    private int $gsactor_id;
    private string $verb;
    private string $object_type;
    private int $object_id;
    private bool $is_local;
    private string $source;
    private DateTimeInterface $created;

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setGsactorId(int $gsactor_id): self
    {
        $this->gsactor_id = $gsactor_id;
        return $this;
    }

    public function getGsactorId(): int
    {
        return $this->gsactor_id;
    }

    public function setVerb(string $verb): self
    {
        $this->verb = $verb;
        return $this;
    }

    public function getVerb(): string
    {
        return $this->verb;
    }

    public function setObjectType(string $object_type): self
    {
        $this->object_type = $object_type;
        return $this;
    }

    public function getObjectType(): string
    {
        return $this->object_type;
    }

    public function setObjectId(int $object_id): self
    {
        $this->object_id = $object_id;
        return $this;
    }

    public function getObjectId(): int
    {
        return $this->object_id;
    }

    public function setIsLocal(bool $is_local): self
    {
        $this->is_local = $is_local;
        return $this;
    }

    public function getIsLocal(): bool
    {
        return $this->is_local;
    }

    public function setSource(string $source): self
    {
        $this->source = $source;
        return $this;
    }

    public function getSource(): string
    {
        return $this->source;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'activity',
            'fields' => [
                'id'          => ['type' => 'serial',   'not null' => true],
                'gsactor_id'  => ['type' => 'int',      'not null' => true, 'description' => 'foreign key to gsactor table'],
                'verb'        => ['type' => 'varchar',  'length' => 32,     'not null' => true, 'description' => 'foreign key to file table'],
                'object_type' => ['type' => 'varchar',  'length' => 32,     'not null' => true, 'description' => 'foreign key to file table'],
                'object_id'   => ['type' => 'int',      'not null' => true, 'description' => 'foreign key to file table'],
                'is_local'    => ['type' => 'bool',     'not null' => true, 'description' => 'foreign key to file table'],
                'source'      => ['type' => 'varchar',  'length' => 32,     'not null' => true, 'description' => 'foreign key to file table'],
                'created'     => ['type' => 'datetime', 'not null' => true, 'description' => 'date this record was created',  'default' => 'CURRENT_TIMESTAMP'],
            ],
            'primary key' => ['id'],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.

// }}}

namespace App\Entity;

use App\Core\DB\DB;
use App\Core\Entity;
use App\Core\Router\Router;
use App\Util\Common;
use DateTimeInterface;

/**
 * Entity for user's avatar
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @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 Entity
{
    // {{{ Autocode

    private int $gsactor_id;
    private int $file_id;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setGsactorId(int $gsactor_id): self
    {
        $this->gsactor_id = $gsactor_id;
        return $this;
    }

    public function getGsactorId(): int
    {
        return $this->gsactor_id;
    }

    public function setFileId(int $file_id): self
    {
        $this->file_id = $file_id;
        return $this;
    }

    public function getFileId(): int
    {
        return $this->file_id;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    private ?File $file = null;

    public function getUrl(): string
    {
        return Router::url('avatar', ['nickname' => GSActor::getNicknameFromId($this->gsactor_id)]);
    }

    public function getFile(): File
    {
        $this->file = $this->file ?: DB::find('file', ['id' => $this->file_id]);
        return $this->file;
    }

    public static function getFilePathStatic(string $filename): string
    {
        return Common::config('avatar', 'dir') . $filename;
    }

    public function getFilePath(): string
    {
        return Common::config('avatar', 'dir') . $this->getFile()->getFileName();
    }

    /**
     * Delete this avatar and the corresponding file and thumbnails, which this owns
     */
    public function delete(bool $flush = false, bool $delete_files_now = false, bool $cascading = false): array
    {
        // Don't go into a loop if we're deleting from File
        if (!$cascading) {
            $files = $this->getFile()->delete($cascade = true, $file_flush = false, $delete_files_now);
        } else {
            DB::remove(DB::getReference('avatar', ['gsactor_id' => $this->gsactor_id]));
            $file_path = $this->getFilePath();
            $files[]   = $file_path;
            if ($flush) {
                DB::flush();
            }
            return $delete_files_now ? [] : $files;
        }
        return [];
    }

    public static function schemaDef(): array
    {
        return [
            'name'   => 'avatar',
            'fields' => [
                'gsactor_id' => ['type' => 'int',       'not null' => true, 'description' => 'foreign key to gsactor table'],
                'file_id'    => ['type' => 'int',       'not null' => true, 'description' => 'foreign key to file table'],
                'created'    => ['type' => 'datetime',  'not null' => true, 'description' => 'date this record was created',  'default' => 'CURRENT_TIMESTAMP'],
                'modified'   => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified', 'default' => 'CURRENT_TIMESTAMP'],
            ],
            'primary key'  => ['gsactor_id'],
            'foreign keys' => [
                'avatar_gsactor_id_fkey' => ['gsactor', ['gsactor_id' => 'id']],
                'avatar_file_id_fkey'    => ['file', ['file_id' => 'id']],
            ],
            'indexes' => [
                'avatar_file_id_idx' => ['file_id'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for user's email confimation
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @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 extends Entity
{
    // {{{ Autocode

    private string $code;
    private ?int $user_id;
    private string $address;
    private ?string $address_extra;
    private string $address_type;
    private ?DateTimeInterface $claimed;
    private DateTimeInterface $sent;
    private DateTimeInterface $modified;

    public function setCode(string $code): self
    {
        $this->code = $code;
        return $this;
    }

    public function getCode(): string
    {
        return $this->code;
    }

    public function setUserId(?int $user_id): self
    {
        $this->user_id = $user_id;
        return $this;
    }

    public function getUserId(): ?int
    {
        return $this->user_id;
    }

    public function setAddress(string $address): self
    {
        $this->address = $address;
        return $this;
    }

    public function getAddress(): string
    {
        return $this->address;
    }

    public function setAddressExtra(?string $address_extra): self
    {
        $this->address_extra = $address_extra;
        return $this;
    }

    public function getAddressExtra(): ?string
    {
        return $this->address_extra;
    }

    public function setAddressType(string $address_type): self
    {
        $this->address_type = $address_type;
        return $this;
    }

    public function getAddressType(): string
    {
        return $this->address_type;
    }

    public function setClaimed(?DateTimeInterface $claimed): self
    {
        $this->claimed = $claimed;
        return $this;
    }

    public function getClaimed(): ?DateTimeInterface
    {
        return $this->claimed;
    }

    public function setSent(DateTimeInterface $sent): self
    {
        $this->sent = $sent;
        return $this;
    }

    public function getSent(): DateTimeInterface
    {
        return $this->sent;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    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',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this was sent for queueing'],
                'modified'      => ['type' => 'timestamp', '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']],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Data class for Conversations
 *
 * @category  Data
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @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
 */
class Conversation extends Entity
{
    // {{{ Autocode

    private int $id;
    private int $note_id;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setNoteId(int $note_id): self
    {
        $this->note_id = $note_id;
        return $this;
    }

    public function getNoteId(): int
    {
        return $this->note_id;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'conversation',
            'fields' => [
                'id'       => ['type' => 'serial',    'not null' => true, 'description' => 'Unique identifier'],
                'note_id'  => ['type' => 'int',       'not null' => true, 'description' => 'Root of note for this conversation'],
                'created'  => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['id'],
            'foreign keys' => ['conversation_note_id_fkey' => ['note', ['note_id' => 'id']]],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

class Favourite extends Entity
{
    // {{{ Autocode

    private int $note_id;
    private int $gsactor_id;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setNoteId(int $note_id): self
    {
        $this->note_id = $note_id;
        return $this;
    }

    public function getNoteId(): int
    {
        return $this->note_id;
    }

    public function setGsactorId(int $gsactor_id): self
    {
        $this->gsactor_id = $gsactor_id;
        return $this;
    }

    public function getGsactorId(): int
    {
        return $this->gsactor_id;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }
    // }}} Autocode

    public static function schemaDef()
    {
        return [
            'name'   => 'favourite',
            'fields' => [
                'note_id'    => ['type' => 'int', 'not null' => true, 'description' => 'note that is the favorite of'],
                'gsactor_id' => ['type' => 'int', 'not null' => true, 'description' => 'actor who favourited this note'],
                'created'    => ['type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'],
                'modified'   => ['type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['note_id', 'gsactor_id'],
            'foreign keys' => [
                'fave_note_id_fkey'  => ['note', ['note_id' => 'id']],
                'fave_actor_id_fkey' => ['gsactor', ['gsactor_id' => 'id']], // note: formerly referenced notice.id, but we can now record remote users' favorites
            ],
            'indexes' => [
                'fave_note_id_idx'  => ['note_id'],
                'fave_actor_id_idx' => ['gsactor_id', 'modified'],
                'fave_modified_idx' => ['modified'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\DB\DB;
use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for uploaded files
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class File extends Entity
{
    // {{{ Autocode

    private int $id;
    private ?string $url;
    private ?string $url_hash;
    private ?string $file_hash;
    private ?int $actor_id;
    private ?string $mimetype;
    private ?string $title;
    private ?bool $is_local;
    private ?bool $is_nsfw;
    private ?bool $is_url_protected;
    private DateTimeInterface $modified;

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setUrl(?string $url): self
    {
        $this->url = $url;
        return $this;
    }

    public function getUrl(): ?string
    {
        return $this->url;
    }

    public function setUrlHash(?string $url_hash): self
    {
        $this->url_hash = $url_hash;
        return $this;
    }

    public function getUrlHash(): ?string
    {
        return $this->url_hash;
    }

    public function setFileHash(?string $file_hash): self
    {
        $this->file_hash = $file_hash;
        return $this;
    }

    public function getFileHash(): ?string
    {
        return $this->file_hash;
    }

    public function setActorId(?int $actor_id): self
    {
        $this->actor_id = $actor_id;
        return $this;
    }

    public function getActorId(): ?int
    {
        return $this->actor_id;
    }

    public function setMimetype(?string $mimetype): self
    {
        $this->mimetype = $mimetype;
        return $this;
    }

    public function getMimetype(): ?string
    {
        return $this->mimetype;
    }

    public function setTitle(?string $title): self
    {
        $this->title = $title;
        return $this;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setIsLocal(?bool $is_local): self
    {
        $this->is_local = $is_local;
        return $this;
    }

    public function getIsLocal(): ?bool
    {
        return $this->is_local;
    }

    public function setIsNsfw(?bool $is_nsfw): self
    {
        $this->is_nsfw = $is_nsfw;
        return $this;
    }

    public function getIsNsfw(): ?bool
    {
        return $this->is_nsfw;
    }

    public function setIsUrlProtected(?bool $is_url_protected): self
    {
        $this->is_url_protected = $is_url_protected;
        return $this;
    }

    public function getIsUrlProtected(): ?bool
    {
        return $this->is_url_protected;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    const URLHASH_ALGO  = 'sha256';
    const FILEHASH_ALGO = 'sha256';

    public function getFileName(): string
    {
        return $this->file_hash;
    }

    /**
     * Delete this file and by default all the associated entities (avatar and/or thumbnails, which this owns)
     */
    public function delete(bool $cascade = true, bool $flush = false, bool $delete_files_now = false): array
    {
        $files = [];
        if ($cascade) {
            // An avatar can own a file, and it becomes invalid if the file is deleted
            $avatar = DB::findBy('avatar', ['file_id' => $this->id]);
            foreach ($avatar as $a) {
                $files[] = $a->getFilePath();
                $a->delete($flush, $delete_files_now, $cascading = true);
            }
            foreach (DB::findBy('file_thumbnail', ['file_id' => $this->id]) as $ft) {
                $files[] = $ft->delete($flush, $delete_files_now, $cascading);
            }
        }
        DB::remove($this);
        if ($flush) {
            DB::flush();
        }
        if ($delete_files_now) {
            self::deleteFiles($files);
            return [];
        }
        return $files;
    }

    public static function deleteFiles(array $files)
    {
        foreach ($files as $f) {
            @unlink($f);
        }
    }

    public static function schemaDef(): array
    {
        // TODO add scope
        return [
            'name'   => 'file',
            'fields' => [
                'id'               => ['type' => 'serial',   'not null' => true],
                'url'              => ['type' => 'text',     'description' => 'URL after following possible redirections'],
                'url_hash'         => ['type' => 'varchar',  'length' => 64,  'description' => 'sha256 of destination URL (url field)'],
                'file_hash'        => ['type' => 'varchar',  'length' => 64,  'description' => 'sha256 of the file contents, if the file is stored locally'],
                'actor_id'         => ['type' => 'int',      'description' => 'If set, used so each actor can have a version of this file (for avatars, for instance)'],
                'mimetype'         => ['type' => 'varchar',  'length' => 50,  'description' => 'mime type of resource'],
                'title'            => ['type' => 'text',     'description' => 'title of resource when available'],
                'is_local'         => ['type' => 'bool',     'description' => 'whether the file is stored locally'],
                'is_nsfw'          => ['type' => 'bool',     'default' => false, 'description' => 'whether the file is NSFW'],
                'is_url_protected' => ['type' => 'bool',     'default' => false, 'description' => 'true when URL is private (needs login)'],
                'modified'         => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key' => ['id'],
            'unique keys' => [
                // 'file_file_key' => ['file_hash', 'actor_id'],
            ],
            'indexes' => [
                'file_filehash_idx' => ['file_hash'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for File thumbnails
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @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 extends Entity
{
    // {{{ Autocode

    private int $file_id;
    private int $width;
    private int $height;
    private DateTimeInterface $modified;

    public function setFileId(int $file_id): self
    {
        $this->file_id = $file_id;
        return $this;
    }

    public function getFileId(): int
    {
        return $this->file_id;
    }

    public function setWidth(int $width): self
    {
        $this->width = $width;
        return $this;
    }

    public function getWidth(): int
    {
        return $this->width;
    }

    public function setHeight(int $height): self
    {
        $this->height = $height;
        return $this;
    }

    public function getHeight(): int
    {
        return $this->height;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    /**
     * Delete a file thumbnail. This table doesn't own all the files, only itself
     */
    public function delete(bool $flush = false, bool $delete_files_now = false, bool $cascading = false): string
    {
        // TODO Implement deleting file thumbnails
        return '';
    }

    public static function schemaDef(): array
    {
        return [
            'name'   => 'file_thumbnail',
            'fields' => [
                'file_id'  => ['type' => 'int', 'not null' => true, 'description' => 'thumbnail for what file'],
                'width'    => ['type' => 'int', 'not null' => true, 'description' => 'width of thumbnail'],
                'height'   => ['type' => 'int', 'not null' => true, 'description' => 'height of thumbnail'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['file_id', 'width', 'height'],
            'foreign keys' => [
                'file_thumbnail_file_id_fkey' => ['file', ['file_id' => 'id']],
            ],
            'indexes' => [
                'file_thumbnail_file_id_idx' => ['file_id'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for relating a file to a post
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class FileToNote extends Entity
{
    // {{{ Autocode

    private int $file_id;
    private int $note_id;
    private DateTimeInterface $modified;

    public function setFileId(int $file_id): self
    {
        $this->file_id = $file_id;
        return $this;
    }

    public function getFileId(): int
    {
        return $this->file_id;
    }

    public function setNoteId(int $note_id): self
    {
        $this->note_id = $note_id;
        return $this;
    }

    public function getNoteId(): int
    {
        return $this->note_id;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'file_to_note',
            'fields' => [
                'file_id'  => ['type' => 'int', 'not null' => true,       'description' => 'id of file'],
                'note_id'  => ['type' => 'int', 'not null' => true,       'description' => 'id of the note it belongs to'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['file_id', 'note_id'],
            'foreign keys' => [
                'file_to_note_file_id_fkey' => ['file', ['file_id' => 'id']],
                'file_to_note_note_id_fkey' => ['note', ['note_id' => 'id']],
            ],
            'indexes' => [
                'file_id_idx' => ['file_id'],
                'note_id_idx' => ['note_id'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for subscription
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class Follow extends Entity
{
    // {{{ Autocode

    private int $follower;
    private int $followed;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setFollower(int $follower): self
    {
        $this->follower = $follower;
        return $this;
    }

    public function getFollower(): int
    {
        return $this->follower;
    }

    public function setFollowed(int $followed): self
    {
        $this->followed = $followed;
        return $this;
    }

    public function getFollowed(): int
    {
        return $this->followed;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'follow',
            'fields' => [
                'follower' => ['type' => 'int', 'not null' => true,  'description' => 'gsactor listening'],
                'followed' => ['type' => 'int', 'not null' => true,  'description' => 'gsactor being listened to'],
                'created'  => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key' => ['follower', 'followed'],
            'indexes'     => [
                'follow_follower_idx' => ['follower', 'created'],
                'follow_followed_idx' => ['followed', 'created'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for Subscription queue
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class FollowQueue extends Entity
{
    // {{{ Autocode

    private int $follower;
    private int $followed;
    private DateTimeInterface $created;

    public function setFollower(int $follower): self
    {
        $this->follower = $follower;
        return $this;
    }

    public function getFollower(): int
    {
        return $this->follower;
    }

    public function setFollowed(int $followed): self
    {
        $this->followed = $followed;
        return $this;
    }

    public function getFollowed(): int
    {
        return $this->followed;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'        => 'follow_queue',
            'description' => 'Holder for Follow requests awaiting moderation.',
            'fields'      => [
                'follower' => ['type' => 'int', 'not null' => true, 'description' => 'gsactor making the request'],
                'followed' => ['type' => 'int', 'not null' => true, 'description' => 'gsactor being followed'],
                'created'  => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
            ],
            'primary key' => ['follower', 'followed'],
            'indexes'     => [
                'Follow_queue_follower_created_idx' => ['follower', 'created'],
                'Follow_queue_followed_created_idx' => ['followed', 'created'],
            ],
            'foreign keys' => [
                'Follow_queue_follower_fkey' => ['gsactor', ['follower' => 'id']],
                'Follow_queue_followed_fkey' => ['gsactor', ['followed' => 'id']],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.

// }}}

namespace App\Entity;

use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\Entity;
use App\Core\UserRoles;
use DateTimeInterface;
use Functional as F;

/**
 * Entity for actors
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GSActor extends Entity
{
    // {{{ Autocode

    private int $id;
    private string $nickname;
    private ?string $fullname;
    private int $roles = 4;
    private ?string $homepage;
    private ?string $bio;
    private ?string $location;
    private ?float $lat;
    private ?float $lon;
    private ?int $location_id;
    private ?int $location_service;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setNickname(string $nickname): self
    {
        $this->nickname = $nickname;
        return $this;
    }

    public function getNickname(): string
    {
        return $this->nickname;
    }

    public function setFullname(?string $fullname): self
    {
        $this->fullname = $fullname;
        return $this;
    }

    public function getFullname(): ?string
    {
        return $this->fullname;
    }

    public function setRoles(int $roles): self
    {
        $this->roles = $roles;
        return $this;
    }

    public function getRoles(): int
    {
        return $this->roles;
    }

    public function setHomepage(?string $homepage): self
    {
        $this->homepage = $homepage;
        return $this;
    }

    public function getHomepage(): ?string
    {
        return $this->homepage;
    }

    public function setBio(?string $bio): self
    {
        $this->bio = $bio;
        return $this;
    }

    public function getBio(): ?string
    {
        return $this->bio;
    }

    public function setLocation(?string $location): self
    {
        $this->location = $location;
        return $this;
    }

    public function getLocation(): ?string
    {
        return $this->location;
    }

    public function setLat(?float $lat): self
    {
        $this->lat = $lat;
        return $this;
    }

    public function getLat(): ?float
    {
        return $this->lat;
    }

    public function setLon(?float $lon): self
    {
        $this->lon = $lon;
        return $this;
    }

    public function getLon(): ?float
    {
        return $this->lon;
    }

    public function setLocationId(?int $location_id): self
    {
        $this->location_id = $location_id;
        return $this;
    }

    public function getLocationId(): ?int
    {
        return $this->location_id;
    }

    public function setLocationService(?int $location_service): self
    {
        $this->location_service = $location_service;
        return $this;
    }

    public function getLocationService(): ?int
    {
        return $this->location_service;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function getFromId(int $id): ?self
    {
        return Cache::get('gsactor-id-' . $id, function () use ($id) {
            return DB::find('gsactor', ['id' => $id]);
        });
    }

    public static function getFromNickname(string $nickname): ?self
    {
        return Cache::get('gsactor-nick-' . $nickname, function () use ($nickname) {
            return DB::findOneBy('gsactor', ['nickname' => $nickname]);
        });
    }

    public static function getNicknameFromId(int $id): string
    {
        return Cache::get('gsactor-nick-id-' . $id, function () use ($id) {
            return self::getFromId($id)->getNickname();
        });
    }

    public function getSelfTags(): array
    {
        return Cache::get('selftags-' . $this->id,
                          function () {
                              return DB::findBy('gsactor_tag', ['tagger' => $this->id, 'tagged' => $this->id]);
                          });
    }

    public function setSelfTags(array $tags, array $existing): void
    {
        $tag_existing  = F\map($existing, function ($pt) { return $pt->getTag(); });
        $tag_to_add    = array_diff($tags, $tag_existing);
        $tag_to_remove = array_diff($tag_existing, $tags);
        $pt_to_remove  = F\filter($existing, function ($pt) use ($tag_to_remove) { return in_array($pt->getTag(), $tag_to_remove); });
        foreach ($tag_to_add as $tag) {
            $pt = GSActorTag::create(['tagger' => $this->id, 'tagged' => $this->id, 'tag' => $tag]);
            DB::persist($pt);
        }
        foreach ($pt_to_remove as $pt) {
            DB::remove($pt);
        }
        Cache::delete('selftags-' . $this->id);
    }

    public function getFollowersCount()
    {
        return Cache::get('followers-' . $this->id,
                          function () {
                              return DB::dql('select count(f) from App\Entity\Follow f where f.followed = :followed',
                                             ['followed' => $this->id])[0][1] - 1; // Remove self follow
                          });
    }

    public function getFollowedCount()
    {
        return Cache::get('followed-' . $this->id,
                          function () {
                              return DB::dql('select count(f) from App\Entity\Follow f where f.follower = :follower',
                                             ['follower' => $this->id])[0][1] - 1; // Remove self follow
                          });
    }

    public static function schemaDef(): array
    {
        $def = [
            'name'        => 'gsactor',
            'description' => 'local and remote users, groups and bots are gsactors, for instance',
            'fields'      => [
                'id'               => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
                'nickname'         => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'],
                'fullname'         => ['type' => 'text', 'description' => 'display name'],
                'roles'            => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this gsactor has'],
                'homepage'         => ['type' => 'text', 'description' => 'identifying URL'],
                'bio'              => ['type' => 'text', 'description' => 'descriptive biography'],
                'location'         => ['type' => 'text', 'description' => 'physical location'],
                '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_service' => ['type' => 'int', 'description' => 'service used to obtain location id'],
                'created'          => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
                'modified'         => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key' => ['id'],
            'indexes'     => [
                'gsactor_nickname_idx' => ['nickname'],
            ],
            'fulltext indexes' => [
                'gsactor_fulltext_idx' => ['nickname', 'fullname', 'location', 'bio', 'homepage'],
            ],
        ];

        return $def;
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for User's GSActor Block
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GSActorBlock extends Entity
{
    // {{{ Autocode

    private int $blocker;
    private int $blocked;
    private DateTimeInterface $modified;

    public function setBlocker(int $blocker): self
    {
        $this->blocker = $blocker;
        return $this;
    }

    public function getBlocker(): int
    {
        return $this->blocker;
    }

    public function setBlocked(int $blocked): self
    {
        $this->blocked = $blocked;
        return $this;
    }

    public function getBlocked(): int
    {
        return $this->blocked;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'gsactor_block',
            'fields' => [
                'blocker'  => ['type' => 'int', 'not null' => true, 'description' => 'user making the block'],
                'blocked'  => ['type' => 'int', 'not null' => true, 'description' => 'gsactor that is blocked'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['blocker', 'blocked'],
            'foreign keys' => [
                'gsactor_block_blocker_fkey' => ['local_user', ['blocker' => 'id']],
                'gsactor_block_blocked_fkey' => ['gsactor', ['blocked' => 'id']],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for List of gsactors
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GSActorCircle extends Entity
{
    // {{{ Autocode

    private int $id;
    private int $tagger;
    private string $tag;
    private ?string $description;
    private ?bool $private;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setId(int $id): self
    {
        $this->id = $id;
        return $this;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function setTagger(int $tagger): self
    {
        $this->tagger = $tagger;
        return $this;
    }

    public function getTagger(): int
    {
        return $this->tagger;
    }

    public function setTag(string $tag): self
    {
        $this->tag = $tag;
        return $this;
    }

    public function getTag(): string
    {
        return $this->tag;
    }

    public function setDescription(?string $description): self
    {
        $this->description = $description;
        return $this;
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setPrivate(?bool $private): self
    {
        $this->private = $private;
        return $this;
    }

    public function getPrivate(): ?bool
    {
        return $this->private;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'        => 'gsactor_list',
            'description' => 'a gsactor can have lists of gsactors, to separate their timeline',
            'fields'      => [
                'id'          => ['type' => 'int', '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' => 'gsactor tag'], // Join with GSActorTag
                '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' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
                'modified'    => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key' => ['tagger', 'tag'],
            'unique keys' => [
                'gsactor_list_id_key' => ['id'],
            ],
            'foreign keys' => [
                'gsactor_list_tagger_fkey' => ['gsactor', ['tagger' => 'id']],
            ],
            'indexes' => [
                'gsactor_list_modified_idx'   => ['modified'],
                'gsactor_list_tag_idx'        => ['tag'],
                'gsactor_list_tagger_tag_idx' => ['tagger', 'tag'],
            ],
        ];
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for GSActor Tag
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GSActorTag extends Entity
{
    // {{{ Autocode

    private int $tagger;
    private int $tagged;
    private string $tag;
    private DateTimeInterface $modified;

    public function setTagger(int $tagger): self
    {
        $this->tagger = $tagger;
        return $this;
    }

    public function getTagger(): int
    {
        return $this->tagger;
    }

    public function setTagged(int $tagged): self
    {
        $this->tagged = $tagged;
        return $this;
    }

    public function getTagged(): int
    {
        return $this->tagged;
    }

    public function setTag(string $tag): self
    {
        $this->tag = $tag;
        return $this;
    }

    public function getTag(): string
    {
        return $this->tag;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'gsactor_tag',
            'fields' => [
                'tagger'   => ['type' => 'int', 'not null' => true, 'description' => 'user making the tag'],
                'tagged'   => ['type' => 'int', 'not null' => true, 'description' => 'gsactor tagged'],
                'tag'      => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'],
                'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['tagger', 'tagged', 'tag'],
            'foreign keys' => [
                'gsactor_tag_tagger_fkey' => ['gsactor', ['tagger' => 'id']],
                'gsactor_tag_tagged_fkey' => ['gsactor', ['tagged' => 'id']],
            ],
            'indexes' => [
                'gsactor_tag_modified_idx'   => ['modified'],
                'gsactor_tag_tagger_tag_idx' => ['tagger', 'tag'], // For Circles
                'gsactor_tag_tagged_idx'     => ['tagged'],
            ],
        ];
    }

    public function __toString()
    {
        return $this->tag;
    }
}


code:
<?php

// {{{ 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 <http://www.gnu.org/licenses/>.
// }}}

namespace App\Entity;

use App\Core\Entity;
use DateTimeInterface;

/**
 * Entity for Gsactor Tag Subscription
 *
 * @category  DB
 * @package   GNUsocial
 *
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010 StatusNet Inc.
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
 * @author    Hugo Sales <hugo@fc.up.pt>
 * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class GSActorTagFollow extends Entity
{
    // {{{ Autocode

    private int $gsactor_tag_id;
    private int $gsactor_id;
    private DateTimeInterface $created;
    private DateTimeInterface $modified;

    public function setGsactorTagId(int $gsactor_tag_id): self
    {
        $this->gsactor_tag_id = $gsactor_tag_id;
        return $this;
    }

    public function getGsactorTagId(): int
    {
        return $this->gsactor_tag_id;
    }

    public function setGsactorId(int $gsactor_id): self
    {
        $this->gsactor_id = $gsactor_id;
        return $this;
    }

    public function getGsactorId(): int
    {
        return $this->gsactor_id;
    }

    public function setCreated(DateTimeInterface $created): self
    {
        $this->created = $created;
        return $this;
    }

    public function getCreated(): DateTimeInterface
    {
        return $this->created;
    }

    public function setModified(DateTimeInterface $modified): self
    {
        $this->modified = $modified;
        return $this;
    }

    public function getModified(): DateTimeInterface
    {
        return $this->modified;
    }

    // }}} Autocode

    public static function schemaDef(): array
    {
        return [
            'name'   => 'gsactor_tag_follow',
            'fields' => [
                'gsactor_tag_id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to gsactor_tag'],
                'gsactor_id'     => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to gsactor table'],
                'created'        => ['type' => 'datetime',  'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
                'modified'       => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
            ],
            'primary key'  => ['gsactor_tag_id', 'gsactor_id'],
            'foreign keys' => [
                'gsactor_tag_follow_gsactor_list_id_fkey' => ['gsactor_list', ['gsactor_tag_id' => 'id']],
                'gsactor_tag_follow_gsactor_id_fkey'      => ['gsactor', ['gsactor_id' => 'id']],
            ],
            'indexes' => [
                'gsactor_tag_follow_gsactor_id_idx' => ['gsactor_id'],
                'gsactor_tag_follow_created_idx'    => ['created'],
            ],
        ];
    }
}