mirror of
https://github.com/humhub/humhub.git
synced 2025-01-17 14:18:27 +01:00
Merge branch 'next' into develop
This commit is contained in:
commit
9323b6840a
21
CHANGELOG-DEV.md
Normal file
21
CHANGELOG-DEV.md
Normal file
@ -0,0 +1,21 @@
|
||||
HumHub Changelog
|
||||
================
|
||||
|
||||
1.16.0 (Unreleased)
|
||||
-------------------
|
||||
- Fix #6636: Module Manager test
|
||||
- Enh #6530: Small performance improvements
|
||||
- Fix #6511: Only test compatible modules in `onMarketplaceAfterFilterModules()`
|
||||
- Enh #6511: Backup folder path is now return from `removeModule()`
|
||||
- Fix #6511: `canRemoveModule` no longer throws an Exception
|
||||
- Enh #6511: Allow an empty filter list to filter all registered modules
|
||||
- Enh #6511: Allow module paths for `enableModules()`
|
||||
- Enh #6511: Verify module's event definition
|
||||
- Enh #6511: Make module's module.json keywords accessible and searchable
|
||||
- Enh #6511: Add Event tracking capabilities to HumHubDbTestCase
|
||||
- Enh #6511: Add test for ModuleManager
|
||||
- Fix #6519: Ensure e-mails would always have a sender address set
|
||||
- Enh #6512: Show error messages when DB connection configuration is invalid
|
||||
- Enh #5315: Default stream sort by `created_at` instead of `id`
|
||||
- Fix #6337: Update `created_at` after first publishing of content record
|
||||
- Fix #6631: Fix visibility of the method `Controller::getAccessRules()`
|
@ -4,6 +4,19 @@ Module Migration Guide
|
||||
See [humhub/documentation::docs/develop/modules-migrate.md](https://github.com/humhub/documentation/blob/master/docs/develop/modules-migrate.md)
|
||||
for full version.
|
||||
|
||||
Version 1.16 (Unreleased)
|
||||
-------------------------
|
||||
|
||||
### Deprecations
|
||||
- `\humhub\modules\content\components\ContentAddonActiveRecord::canWrite()`
|
||||
|
||||
### Type restrictions
|
||||
- `\humhub\modules\comment\models\Comment` on `canDelete()`
|
||||
- `\humhub\modules\content\components\ContentAddonActiveRecord` on `canDelete()`, `canRead()`, `canWrite()`, `canEdit()`
|
||||
- `\humhub\modules\content\models\Content` on `canEdit()`, `canView()`
|
||||
- `\humhub\modules\file\models\File` on `canRead()`, `canDelete()`
|
||||
|
||||
|
||||
Version 1.15
|
||||
-------------------------
|
||||
|
||||
@ -51,3 +64,4 @@ Version 1.15
|
||||
- `humhub\widgets\MarkdownField`
|
||||
- `humhub\widgets\MarkdownFieldModals`
|
||||
- `humhub\widgets\ModalConfirm`
|
||||
|
||||
|
10
index.php
10
index.php
@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
// comment out the following two lines when deployed to production
|
||||
use humhub\helpers\DatabaseHelper;
|
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||
defined('YII_ENV') or define('YII_ENV', 'dev');
|
||||
|
||||
@ -22,4 +24,10 @@ $config = yii\helpers\ArrayHelper::merge(
|
||||
require(__DIR__ . '/protected/config/web.php')
|
||||
);
|
||||
|
||||
(new humhub\components\Application($config))->run();
|
||||
try {
|
||||
(new humhub\components\Application($config))->run();
|
||||
} catch (\Throwable $ex) {
|
||||
if (null === DatabaseHelper::handleConnectionErrors($ex)) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
namespace humhub\commands;
|
||||
|
||||
use humhub\components\Module;
|
||||
use humhub\helpers\DatabaseHelper;
|
||||
use Yii;
|
||||
use yii\console\Exception;
|
||||
use yii\web\Application;
|
||||
@ -78,9 +79,17 @@ class MigrateController extends \yii\console\controllers\MigrateController
|
||||
*/
|
||||
public function beforeAction($action)
|
||||
{
|
||||
// Make sure to define default table storage engine
|
||||
if (in_array(Yii::$app->db->getDriverName(), ['mysql', 'mysqli'], true)) {
|
||||
Yii::$app->db->pdo->exec('SET default_storage_engine=' . Yii::$app->params['databaseDefaultStorageEngine']);
|
||||
// Make sure to define a default table storage engine
|
||||
$db = Yii::$app->db;
|
||||
|
||||
try {
|
||||
$db->open();
|
||||
} catch (\Throwable $ex) {
|
||||
DatabaseHelper::handleConnectionErrors($ex);
|
||||
}
|
||||
|
||||
if (in_array($db->getDriverName(), ['mysql', 'mysqli'], true)) {
|
||||
$db->pdo->exec('SET default_storage_engine=' . Yii::$app->params['databaseDefaultStorageEngine']);
|
||||
}
|
||||
return parent::beforeAction($action);
|
||||
}
|
||||
|
@ -61,4 +61,9 @@ trait ApplicationTrait
|
||||
{
|
||||
$this->_homeUrl = $value;
|
||||
}
|
||||
|
||||
public function getMailer(): MailerInterface
|
||||
{
|
||||
return parent::getMailer();
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use yii\web\AssetBundle;
|
||||
*
|
||||
* @property-read string $name
|
||||
* @property-read string $description
|
||||
* @property-read array $keywords
|
||||
* @property-read bool $isActivated
|
||||
* @property SettingsManager $settings
|
||||
* @author luke
|
||||
@ -57,7 +58,7 @@ class Module extends \yii\base\Module
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns modules name provided by module.json file
|
||||
* Returns the module's name provided by module.json file
|
||||
*
|
||||
* @return string Name
|
||||
*/
|
||||
@ -73,7 +74,7 @@ class Module extends \yii\base\Module
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns modules description provided by module.json file
|
||||
* Returns the module's description provided by module.json file
|
||||
*
|
||||
* @return string Description
|
||||
*/
|
||||
@ -89,7 +90,7 @@ class Module extends \yii\base\Module
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns modules version number provided by module.json file
|
||||
* Returns the module's version number provided by module.json file
|
||||
*
|
||||
* @return string Version Number
|
||||
*/
|
||||
@ -121,6 +122,22 @@ class Module extends \yii\base\Module
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns module's keywords provided by module.json file
|
||||
*
|
||||
* @return array List of keywords
|
||||
*/
|
||||
public function getKeywords(): array
|
||||
{
|
||||
$info = $this->getModuleInfo();
|
||||
|
||||
if ($info['keywords']) {
|
||||
return (array)$info['keywords'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url of an asset file and publishes all module assets if
|
||||
* the file is not published yet.
|
||||
@ -272,11 +289,16 @@ class Module extends \yii\base\Module
|
||||
* Delete all Migration Table Entries
|
||||
*/
|
||||
$migrations = opendir($migrationPath);
|
||||
$params = [];
|
||||
while (false !== ($migration = readdir($migrations))) {
|
||||
if ($migration == '.' || $migration == '..' || $migration == 'uninstall.php') {
|
||||
continue;
|
||||
}
|
||||
Yii::$app->db->createCommand()->delete('migration', ['version' => str_replace('.php', '', $migration)])->execute();
|
||||
|
||||
$command ??= Yii::$app->db->createCommand()->delete('migration', 'version = :version', $params);
|
||||
|
||||
$version = str_replace('.php', '', $migration);
|
||||
$command->bindValue(':version', $version)->execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
/** @noinspection UnknownInspectionInspection */
|
||||
|
||||
namespace humhub\components;
|
||||
|
||||
use ArrayAccess;
|
||||
use humhub\components\bootstrap\ModuleAutoLoader;
|
||||
use humhub\components\console\Application as ConsoleApplication;
|
||||
use humhub\exceptions\InvalidArgumentTypeException;
|
||||
use humhub\libs\BaseSettingsManager;
|
||||
use humhub\models\ModuleEnabled;
|
||||
use humhub\modules\admin\events\ModulesEvent;
|
||||
@ -146,8 +150,7 @@ class ModuleManager extends Component
|
||||
*/
|
||||
public function register($basePath, $config = null)
|
||||
{
|
||||
$filename = $basePath . '/config.php';
|
||||
if ($config === null && is_file($filename)) {
|
||||
if ($config === null && is_file($filename = $basePath . '/config.php')) {
|
||||
$config = include $filename;
|
||||
}
|
||||
|
||||
@ -182,7 +185,7 @@ class ModuleManager extends Component
|
||||
|
||||
// Not enabled and no core/installer module
|
||||
if (!$isCoreModule && !in_array($config['id'], $this->enabledModules)) {
|
||||
return;
|
||||
return $config['id'];
|
||||
}
|
||||
|
||||
// Handle Submodules
|
||||
@ -213,23 +216,106 @@ class ModuleManager extends Component
|
||||
Yii::$app->setModule($config['id'], $moduleConfig);
|
||||
|
||||
// Register Event Handlers
|
||||
if (isset($config['events'])) {
|
||||
foreach ($config['events'] as $event) {
|
||||
$eventClass = $event['class'] ?? $event[0];
|
||||
$eventName = $event['event'] ?? $event[1];
|
||||
$eventHandler = $event['callback'] ?? $event[2];
|
||||
$eventData = $event['data'] ?? $event[3] ?? null;
|
||||
$eventAppend = filter_var($event['append'] ?? $event[4] ?? true, FILTER_VALIDATE_BOOLEAN);
|
||||
if (method_exists($eventHandler[0], $eventHandler[1])) {
|
||||
Event::on($eventClass, $eventName, $eventHandler, $eventData, $eventAppend);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->registerEventHandlers($basePath, $config);
|
||||
|
||||
// Register Console ControllerMap
|
||||
if (Yii::$app instanceof ConsoleApplication && !(empty($config['consoleControllerMap']))) {
|
||||
Yii::$app->controllerMap = ArrayHelper::merge(Yii::$app->controllerMap, $config['consoleControllerMap']);
|
||||
}
|
||||
|
||||
return $config['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
protected function registerEventHandlers(string $basePath, array &$config): void
|
||||
{
|
||||
$events = $config['events'] ?? null;
|
||||
$strict = $config['strict'] ?? false;
|
||||
|
||||
if (empty($events)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$error = static function (string $message, bool $throw = false) use (&$config, $basePath) {
|
||||
$message = sprintf("Configuration at %s has an invalid event configuration: %s", $basePath, $message);
|
||||
|
||||
if ($throw) {
|
||||
throw new InvalidConfigException($message);
|
||||
}
|
||||
|
||||
Yii::warning($message, $config['id']);
|
||||
};
|
||||
|
||||
if (!ArrayHelper::isTraversable($events)) {
|
||||
$error('events must be traversable', $strict);
|
||||
return;
|
||||
}
|
||||
|
||||
$getProperty = static function ($event, &$var, string $property, int $index, bool $throw = false) use ($error): bool {
|
||||
|
||||
$var = $event[$property] ?? $event[$index] ?? null;
|
||||
|
||||
if (empty($var)) {
|
||||
$error(sprintf("required property '%s' missing!", $property), $throw);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
foreach ($events as $event) {
|
||||
if (empty($event)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($event) && !$event instanceof ArrayAccess) {
|
||||
$error('event configuration must be an array or implement \ArrayAccess', $strict);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$getProperty($event, $eventClass, 'class', 0, $strict)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$getProperty($event, $eventName, 'event', 1, $strict)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$getProperty($event, $eventHandler, 'callback', 2, $strict)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($eventHandler)) {
|
||||
$error("property 'callback' must be a callable defined in the array-notation denoting a method of a class", $strict);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_object($eventHandler[0] ?? null) && !class_exists($eventHandler[0] ?? null)) {
|
||||
$error(sprintf("class '%s' does not exist.", $eventHandler[0] ?? ''), $strict);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method_exists($eventHandler[0], $eventHandler[1])) {
|
||||
$error(
|
||||
sprintf(
|
||||
"class '%s' does not have a method called '%s",
|
||||
is_object($eventHandler[0]) ? get_class($eventHandler[0]) : $eventHandler[0],
|
||||
$eventHandler[1]
|
||||
),
|
||||
$strict
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$eventData = $event['data'] ?? $event[3] ?? null;
|
||||
$eventAppend = filter_var($event['append'] ?? $event[4] ?? true, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
Event::on($eventClass, $eventName, $eventHandler, $eventData, $eventAppend);
|
||||
}
|
||||
|
||||
$events = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,17 +369,18 @@ class ModuleManager extends Component
|
||||
/**
|
||||
* Filter modules by keyword and by additional filters from module event
|
||||
*
|
||||
* @param Module[] $modules
|
||||
* @param array $filters
|
||||
* @param Module[]|null $modules
|
||||
* @param array|ArrayAccess $filters = ['keyword' => 'search term']
|
||||
*
|
||||
* @return Module[]
|
||||
*/
|
||||
public function filterModules(array $modules, $filters = []): array
|
||||
public function filterModules(?array $modules, $filters = []): array
|
||||
{
|
||||
$filters = array_merge([
|
||||
'keyword' => null,
|
||||
], $filters);
|
||||
if (!$filters instanceof ArrayAccess && !is_array($filters)) {
|
||||
throw new InvalidArgumentTypeException('$filters', ['array', ArrayAccess::class], $filters);
|
||||
}
|
||||
|
||||
$modules = $this->filterModulesByKeyword($modules, $filters['keyword']);
|
||||
$modules = $this->filterModulesByKeyword($modules, $filters['keyword'] ?? null);
|
||||
|
||||
$modulesEvent = new ModulesEvent(['modules' => $modules]);
|
||||
$this->trigger(static::EVENT_AFTER_FILTER_MODULES, $modulesEvent);
|
||||
@ -304,12 +391,14 @@ class ModuleManager extends Component
|
||||
/**
|
||||
* Filter modules by keyword
|
||||
*
|
||||
* @param Module[] $modules
|
||||
* @param Module[]|null $modules list of modules, defaulting to installed non-core modules
|
||||
* @param null|string $keyword
|
||||
* @return Module[]
|
||||
*/
|
||||
public function filterModulesByKeyword(array $modules, $keyword = null): array
|
||||
public function filterModulesByKeyword(?array $modules, $keyword = null): array
|
||||
{
|
||||
$modules ??= $this->getModules();
|
||||
|
||||
if ($keyword === null) {
|
||||
$keyword = Yii::$app->request->get('keyword', '');
|
||||
}
|
||||
@ -321,11 +410,16 @@ class ModuleManager extends Component
|
||||
foreach ($modules as $id => $module) {
|
||||
/* @var Module $module */
|
||||
$searchFields = [$id];
|
||||
if (isset($module->name)) {
|
||||
$searchFields[] = $module->name;
|
||||
if ($searchField = $module->getName()) {
|
||||
$searchFields[] = $searchField;
|
||||
}
|
||||
if (isset($module->description)) {
|
||||
$searchFields[] = $module->description;
|
||||
|
||||
if ($searchField = $module->getDescription()) {
|
||||
$searchFields[] = $searchField;
|
||||
}
|
||||
|
||||
if ($searchField = $module->getKeywords()) {
|
||||
array_push($searchFields, ...$searchField);
|
||||
}
|
||||
|
||||
$keywordFound = false;
|
||||
@ -396,6 +490,10 @@ class ModuleManager extends Component
|
||||
*/
|
||||
public function getModule($id, $throwOnMissingModule = true)
|
||||
{
|
||||
if ($id instanceof Module) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
// Enabled Module
|
||||
if (Yii::$app->hasModule($id)) {
|
||||
return Yii::$app->getModule($id, true);
|
||||
@ -407,6 +505,10 @@ class ModuleManager extends Component
|
||||
return Yii::createObject($class, [$id, Yii::$app]);
|
||||
}
|
||||
|
||||
if (is_dir($id) && is_file($id . '/config.php')) {
|
||||
return $this->getModule($this->register($id));
|
||||
}
|
||||
|
||||
if ($throwOnMissingModule) {
|
||||
throw new Exception('Could not find/load requested module: ' . $id);
|
||||
}
|
||||
@ -423,15 +525,16 @@ class ModuleManager extends Component
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the module can removed
|
||||
* Checks if the module can be removed
|
||||
*
|
||||
* @param string $moduleId
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function canRemoveModule($moduleId)
|
||||
*
|
||||
* @noinspection PhpDocMissingThrowsInspection
|
||||
* */
|
||||
public function canRemoveModule($moduleId): bool
|
||||
{
|
||||
$module = $this->getModule($moduleId);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$module = $this->getModule($moduleId, false);
|
||||
|
||||
if ($module === null) {
|
||||
return false;
|
||||
@ -460,11 +563,11 @@ class ModuleManager extends Component
|
||||
* @throws Exception
|
||||
* @throws \yii\base\ErrorException
|
||||
*/
|
||||
public function removeModule($moduleId, $disableBeforeRemove = true)
|
||||
public function removeModule($moduleId, $disableBeforeRemove = true): ?string
|
||||
{
|
||||
$module = $this->getModule($moduleId);
|
||||
|
||||
if ($module == null) {
|
||||
if ($module === null) {
|
||||
throw new Exception('Could not load module to remove!');
|
||||
}
|
||||
|
||||
@ -487,10 +590,13 @@ class ModuleManager extends Component
|
||||
FileHelper::copyDirectory($moduleBasePath, $backupFolderName);
|
||||
FileHelper::removeDirectory($moduleBasePath);
|
||||
} else {
|
||||
$backupFolderName = null;
|
||||
//TODO: Delete directory
|
||||
}
|
||||
|
||||
$this->flushCache();
|
||||
|
||||
return $backupFolderName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -517,7 +623,7 @@ class ModuleManager extends Component
|
||||
public function enableModules($modules = [])
|
||||
{
|
||||
foreach ($modules as $module) {
|
||||
$module = ($module instanceof Module) ? $module : $this->getModule($module);
|
||||
$module = $this->getModule($module);
|
||||
if ($module != null) {
|
||||
$module->enable();
|
||||
}
|
||||
@ -537,11 +643,11 @@ class ModuleManager extends Component
|
||||
$this->trigger(static::EVENT_BEFORE_MODULE_DISABLE, new ModuleEvent(['module' => $module]));
|
||||
|
||||
$moduleEnabled = ModuleEnabled::findOne(['module_id' => $module->id]);
|
||||
if ($moduleEnabled != null) {
|
||||
if ($moduleEnabled !== null) {
|
||||
$moduleEnabled->delete();
|
||||
}
|
||||
|
||||
if (($key = array_search($module->id, $this->enabledModules)) !== false) {
|
||||
if (($key = array_search($module->id, $this->enabledModules, true)) !== false) {
|
||||
unset($this->enabledModules[$key]);
|
||||
}
|
||||
|
||||
@ -557,8 +663,8 @@ class ModuleManager extends Component
|
||||
public function disableModules($modules = [])
|
||||
{
|
||||
foreach ($modules as $module) {
|
||||
$module = ($module instanceof Module) ? $module : $this->getModule($module);
|
||||
if ($module != null) {
|
||||
$module = $this->getModule($module);
|
||||
if ($module !== null) {
|
||||
$module->disable();
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class Application extends \yii\console\Application implements ApplicationInterfa
|
||||
));
|
||||
}
|
||||
|
||||
if (BaseSettingsManager::isDatabaseInstalled()) {
|
||||
if (BaseSettingsManager::isDatabaseInstalled(Yii::$app->params['databaseInstalled'] ?? false)) {
|
||||
$baseUrl = Yii::$app->settings->get('baseUrl');
|
||||
if (!empty($baseUrl)) {
|
||||
if (Yii::getAlias('@web', false) === false) {
|
||||
|
@ -8,8 +8,10 @@
|
||||
|
||||
namespace humhub\components\mail;
|
||||
|
||||
use humhub\interfaces\MailerInterface;
|
||||
use Symfony\Component\Mime\Crypto\SMimeSigner;
|
||||
use Yii;
|
||||
use yii\mail\MessageInterface;
|
||||
|
||||
/**
|
||||
* Mailer implements a mailer based on SymfonyMailer.
|
||||
@ -18,7 +20,7 @@ use Yii;
|
||||
* @since 1.2
|
||||
* @author Luke
|
||||
*/
|
||||
class Mailer extends \yii\symfonymailer\Mailer
|
||||
class Mailer extends \yii\symfonymailer\Mailer implements MailerInterface
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
@ -68,13 +70,7 @@ class Mailer extends \yii\symfonymailer\Mailer
|
||||
{
|
||||
$message = parent::compose($view, $params);
|
||||
|
||||
// Set HumHub default from values
|
||||
if (empty($message->getFrom())) {
|
||||
$message->setFrom([Yii::$app->settings->get('mailer.systemEmailAddress') => Yii::$app->settings->get('mailer.systemEmailName')]);
|
||||
if ($replyTo = Yii::$app->settings->get('mailer.systemEmailReplyTo')) {
|
||||
$message->setReplyTo($replyTo);
|
||||
}
|
||||
}
|
||||
self::ensureHumHubDefaultFromValues($message);
|
||||
|
||||
if ($this->signingCertificatePath !== null && $this->signingPrivateKeyPath !== null) {
|
||||
if ($this->signer === null) {
|
||||
@ -92,6 +88,25 @@ class Mailer extends \yii\symfonymailer\Mailer
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MessageInterface $message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function ensureHumHubDefaultFromValues(MessageInterface $message): MessageInterface
|
||||
{
|
||||
// Set HumHub default from values
|
||||
if ($message->getFrom()) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$message->setFrom([Yii::$app->settings->get('mailer.systemEmailAddress') => Yii::$app->settings->get('mailer.systemEmailName')]);
|
||||
if ($replyTo = Yii::$app->settings->get('mailer.systemEmailReplyTo')) {
|
||||
$message->setReplyTo($replyTo);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
147
protected/humhub/helpers/DatabaseHelper.php
Normal file
147
protected/humhub/helpers/DatabaseHelper.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2017-2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\helpers;
|
||||
|
||||
use Throwable;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* @since 1.15
|
||||
*/
|
||||
class DatabaseHelper
|
||||
{
|
||||
public static function handleConnectionErrors(
|
||||
Throwable $ex,
|
||||
bool $print = true,
|
||||
bool $die = true,
|
||||
bool $forcePlainText = false
|
||||
): ?string {
|
||||
static $last = false;
|
||||
|
||||
if (!$ex instanceof \yii\db\Exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($last) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$last = true;
|
||||
|
||||
$trace = debug_backtrace(0);
|
||||
$trace = end($trace);
|
||||
if ($trace && $trace['function'] === 'handleException' && $trace['args'][0] instanceof \yii\db\Exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($ex->getCode()) {
|
||||
case 2002:
|
||||
$error = 'Hostname not found.';
|
||||
break;
|
||||
|
||||
case 1044:
|
||||
$error = 'Database not found or not accessible.';
|
||||
break;
|
||||
|
||||
case 1049:
|
||||
$error = 'Database not found.';
|
||||
break;
|
||||
|
||||
default:
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.php.net/manual/en/ref.pdo-odbc.connection.php
|
||||
* @see https://www.php.net/manual/en/ref.pdo-ibm.connection.php
|
||||
* @see https://www.php.net/manual/en/ref.pdo-pgsql.connection.php
|
||||
*/
|
||||
$dsn = preg_replace(
|
||||
'@((?<=:|;)(?:user|uid|User ID|pwd|password)=)(.*?)(?=;(?:$|\w+=)|$)@i',
|
||||
'$1****',
|
||||
Yii::$app->db->dsn
|
||||
);
|
||||
|
||||
try {
|
||||
$additionalInfo = json_encode([get_class($ex), ...$ex->errorInfo], JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
$additionalInfo = 'N/A';
|
||||
}
|
||||
|
||||
while ($ex->getPrevious()) {
|
||||
$ex = $ex->getPrevious();
|
||||
}
|
||||
|
||||
$htmlMessage = defined('YII_DEBUG') && YII_DEBUG
|
||||
? sprintf('
|
||||
<h1>Invalid database configuration</h1>
|
||||
<p><strong>%s</strong></p>
|
||||
<p>The following connection string was used:<br><code>%s</code></p>
|
||||
<br>
|
||||
<h2>Technical information</h2>
|
||||
<p><code>%s</code></p>
|
||||
<p><pre>%s</pre></p>
|
||||
', $error, $dsn, $additionalInfo, $ex)
|
||||
: sprintf('
|
||||
<h1>Invalid database configuration</h1>
|
||||
<p><strong>%s</strong></p>
|
||||
', $error);
|
||||
|
||||
$txtMessage = defined('YII_DEBUG') && YII_DEBUG
|
||||
? sprintf('
|
||||
Invalid database configuration
|
||||
==============================
|
||||
|
||||
%s
|
||||
|
||||
The following connection string was used:
|
||||
%s
|
||||
|
||||
|
||||
Technical information
|
||||
---------------------
|
||||
%s
|
||||
|
||||
%s
|
||||
|
||||
', $error, $dsn, $additionalInfo, $ex)
|
||||
: sprintf('
|
||||
Invalid database configuration
|
||||
==============================
|
||||
|
||||
%s
|
||||
|
||||
The following connection string was used:
|
||||
%s
|
||||
|
||||
|
||||
Technical information
|
||||
---------------------
|
||||
%s
|
||||
|
||||
', $error, $dsn, $additionalInfo);
|
||||
|
||||
if ($print) {
|
||||
if ($forcePlainText) {
|
||||
echo $txtMessage;
|
||||
} elseif (Yii::$app instanceof \yii\console\Application && Yii::$app->controller instanceof \yii\console\Controller) {
|
||||
Yii::$app->controller->stderr($txtMessage);
|
||||
} else {
|
||||
header("HTTP/1.1 500 Internal Server Error");
|
||||
echo $htmlMessage;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$die) {
|
||||
return $txtMessage;
|
||||
}
|
||||
|
||||
die(1);
|
||||
}
|
||||
}
|
@ -19,4 +19,6 @@ interface ApplicationInterface
|
||||
* @event ActionEvent an event raised on init of application.
|
||||
*/
|
||||
public const EVENT_ON_INIT = 'onInit';
|
||||
|
||||
public function getMailer(): MailerInterface;
|
||||
}
|
||||
|
28
protected/humhub/interfaces/ArchiveableInterface.php
Normal file
28
protected/humhub/interfaces/ArchiveableInterface.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
use humhub\modules\user\models\User;
|
||||
|
||||
interface ArchiveableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the given user can edit/create this element.
|
||||
*
|
||||
* @param User|integer $user user instance or user id
|
||||
*
|
||||
* @return bool can edit/create this element
|
||||
* @since 1.15
|
||||
*/
|
||||
public function canArchive($user = null): bool;
|
||||
|
||||
public function archive(): bool;
|
||||
|
||||
public function unarchive(): bool;
|
||||
}
|
19
protected/humhub/interfaces/DeletableInterface.php
Normal file
19
protected/humhub/interfaces/DeletableInterface.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
interface DeletableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if given item can be deleted.
|
||||
*/
|
||||
public function canDelete($userId = null);
|
||||
|
||||
public function delete();
|
||||
}
|
27
protected/humhub/interfaces/EditableInterface.php
Normal file
27
protected/humhub/interfaces/EditableInterface.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
use humhub\modules\user\models\User;
|
||||
use Throwable;
|
||||
|
||||
interface EditableInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if the given user can edit/create this element.
|
||||
*
|
||||
* @param User|integer $user user instance or user id
|
||||
*
|
||||
* @return bool can edit/create this element
|
||||
* @since 1.15
|
||||
*/
|
||||
public function canEdit($user = null): bool;
|
||||
|
||||
}
|
13
protected/humhub/interfaces/MailerInterface.php
Normal file
13
protected/humhub/interfaces/MailerInterface.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
interface MailerInterface extends \yii\mail\MailerInterface
|
||||
{
|
||||
}
|
23
protected/humhub/interfaces/ReadableInterface.php
Normal file
23
protected/humhub/interfaces/ReadableInterface.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
use humhub\modules\user\models\User;
|
||||
|
||||
interface ReadableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if given element can be read.
|
||||
*
|
||||
* @param string|User $userId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($userId = ""): bool;
|
||||
}
|
27
protected/humhub/interfaces/ViewableInterface.php
Normal file
27
protected/humhub/interfaces/ViewableInterface.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\interfaces;
|
||||
|
||||
use humhub\modules\user\models\User;
|
||||
use Throwable;
|
||||
|
||||
interface ViewableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if user can view this element.
|
||||
*
|
||||
* @param User|integer $user
|
||||
*
|
||||
* @return boolean can view this element
|
||||
* @throws Throwable
|
||||
* @since 1.15
|
||||
*/
|
||||
public function canView($user = null);
|
||||
|
||||
}
|
@ -10,12 +10,12 @@ namespace humhub\libs;
|
||||
|
||||
use humhub\components\SettingActiveRecord;
|
||||
use humhub\exceptions\InvalidArgumentTypeException;
|
||||
use humhub\helpers\DatabaseHelper;
|
||||
use Stringable;
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\db\conditions\LikeCondition;
|
||||
use yii\db\StaleObjectException;
|
||||
use yii\helpers\Json;
|
||||
|
||||
@ -302,19 +302,20 @@ abstract class BaseSettingsManager extends Component
|
||||
/**
|
||||
* Checks if settings table exists or application is not installed yet
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.3
|
||||
*/
|
||||
public static function isDatabaseInstalled()
|
||||
public static function isDatabaseInstalled(bool $dieOnError = false): bool
|
||||
{
|
||||
try {
|
||||
if (in_array('setting', Yii::$app->db->schema->getTableNames())) {
|
||||
return true;
|
||||
}
|
||||
$db = Yii::$app->db;
|
||||
$db->open();
|
||||
} catch (\Exception $ex) {
|
||||
if ($dieOnError) {
|
||||
DatabaseHelper::handleConnectionErrors($ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return in_array('setting', $db->schema->getTableNames());
|
||||
}
|
||||
}
|
||||
|
@ -65,16 +65,13 @@ class ModuleEnabled extends \yii\db\ActiveRecord
|
||||
|
||||
public static function getEnabledIds()
|
||||
{
|
||||
$enabledModules = Yii::$app->cache->get(self::CACHE_ID_ALL_IDS);
|
||||
$cache = Yii::$app->cache;
|
||||
$enabledModules = $cache->get(self::CACHE_ID_ALL_IDS);
|
||||
if ($enabledModules === false) {
|
||||
$enabledModules = [];
|
||||
foreach (\humhub\models\ModuleEnabled::find()->all() as $em) {
|
||||
$enabledModules[] = $em->module_id;
|
||||
}
|
||||
Yii::$app->cache->set(self::CACHE_ID_ALL_IDS, $enabledModules);
|
||||
$enabledModules = self::find()->select('module_id')->createCommand()->queryColumn();
|
||||
$cache->set(self::CACHE_ID_ALL_IDS, $enabledModules);
|
||||
}
|
||||
|
||||
return $enabledModules;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -47,7 +47,6 @@ class ModuleController extends Controller
|
||||
*/
|
||||
protected function getAccessRules()
|
||||
{
|
||||
|
||||
return [
|
||||
['permissions' => [ManageModules::class]],
|
||||
['permissions' => [ManageSettings::class], 'actions' => ['index', 'list']]
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -343,7 +343,7 @@ class Comment extends ContentAddonActiveRecord
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function canDelete($userId = '')
|
||||
public function canDelete($userId = ''): bool
|
||||
{
|
||||
|
||||
if ($userId == '') {
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -9,6 +9,9 @@
|
||||
namespace humhub\modules\content\components;
|
||||
|
||||
use humhub\components\ActiveRecord;
|
||||
use humhub\interfaces\DeletableInterface;
|
||||
use humhub\interfaces\EditableInterface;
|
||||
use humhub\interfaces\ReadableInterface;
|
||||
use humhub\modules\content\interfaces\ContentOwner;
|
||||
use humhub\modules\content\models\Content;
|
||||
use humhub\modules\content\Module;
|
||||
@ -36,9 +39,8 @@ use yii\base\Exception;
|
||||
* @package humhub.components
|
||||
* @since 0.5
|
||||
*/
|
||||
class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
|
||||
class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner, ReadableInterface, EditableInterface, DeletableInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var boolean also update underlying contents last update stream sorting
|
||||
*/
|
||||
@ -136,9 +138,10 @@ class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
|
||||
* Checks if the given / or current user can delete this content.
|
||||
* Currently only the creator can remove.
|
||||
*
|
||||
* @param null $userId
|
||||
* @return boolean
|
||||
*/
|
||||
public function canDelete()
|
||||
public function canDelete($userId = null): bool
|
||||
{
|
||||
if ($this->created_by == Yii::$app->user->id) {
|
||||
return true;
|
||||
@ -150,11 +153,12 @@ class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
|
||||
/**
|
||||
* Check if current user can read this object
|
||||
*
|
||||
* @param string $userId
|
||||
* @return boolean
|
||||
*/
|
||||
public function canRead()
|
||||
public function canRead($userId = ""): bool
|
||||
{
|
||||
return $this->content->canView();
|
||||
return $this->content->canView($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,24 +166,27 @@ class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
|
||||
*
|
||||
* @return boolean
|
||||
* @deprecated since 1.4
|
||||
* @see static::canEdit()
|
||||
*/
|
||||
public function canWrite()
|
||||
public function canWrite($userId = "")
|
||||
{
|
||||
return $this->canEdit();
|
||||
return $this->canEdit($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this record can be edited
|
||||
*
|
||||
* @param User|null $user the user
|
||||
* @param User|int|null $user the user
|
||||
* @return boolean
|
||||
* @since 1.4
|
||||
*/
|
||||
public function canEdit(User $user = null)
|
||||
public function canEdit($user = null): bool
|
||||
{
|
||||
if ($user === null && Yii::$app->user->isGuest) {
|
||||
return false;
|
||||
} elseif ($user === null) {
|
||||
}
|
||||
|
||||
if ($user === null) {
|
||||
/** @var User $user */
|
||||
try {
|
||||
$user = Yii::$app->user->getIdentity();
|
||||
@ -189,7 +196,11 @@ class ContentAddonActiveRecord extends ActiveRecord implements ContentOwner
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->created_by == $user->id) {
|
||||
if (!$user instanceof User && !($user = User::findOne(['id' => $user]))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->created_by === $user->id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use yii\db\Migration;
|
||||
|
||||
/**
|
||||
* Class m210928_162609_stream_sort_idx
|
||||
*/
|
||||
class m210928_162609_stream_sort_idx extends Migration
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function safeUp()
|
||||
{
|
||||
$this->createIndex('idx_stream_created', 'content', 'created_at', false);
|
||||
$this->createIndex('idx_stream_updated', 'content', 'stream_sort_date', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function safeDown()
|
||||
{
|
||||
echo "m210928_162609_stream_sort_idx cannot be reverted.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
// Use up()/down() to run migration code without a transaction.
|
||||
public function up()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
echo "m210928_162609_stream_sort_idx cannot be reverted.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
@ -12,8 +12,11 @@ use humhub\components\ActiveRecord;
|
||||
use humhub\components\behaviors\GUID;
|
||||
use humhub\components\behaviors\PolymorphicRelation;
|
||||
use humhub\components\Module;
|
||||
use humhub\modules\activity\models\Activity;
|
||||
use humhub\interfaces\ArchiveableInterface;
|
||||
use humhub\interfaces\EditableInterface;
|
||||
use humhub\interfaces\ViewableInterface;
|
||||
use humhub\modules\admin\permissions\ManageUsers;
|
||||
use humhub\modules\content\activities\ContentCreated as ActivitiesContentCreated;
|
||||
use humhub\modules\content\components\ContentActiveRecord;
|
||||
use humhub\modules\content\components\ContentContainerActiveRecord;
|
||||
use humhub\modules\content\components\ContentContainerModule;
|
||||
@ -22,21 +25,22 @@ use humhub\modules\content\events\ContentStateEvent;
|
||||
use humhub\modules\content\interfaces\ContentOwner;
|
||||
use humhub\modules\content\interfaces\SoftDeletable;
|
||||
use humhub\modules\content\live\NewContent;
|
||||
use humhub\modules\content\notifications\ContentCreated as NotificationsContentCreated;
|
||||
use humhub\modules\content\permissions\CreatePrivateContent;
|
||||
use humhub\modules\content\permissions\CreatePublicContent;
|
||||
use humhub\modules\content\permissions\ManageContent;
|
||||
use humhub\modules\content\services\ContentStateService;
|
||||
use humhub\modules\content\services\ContentTagService;
|
||||
use humhub\modules\notification\models\Notification;
|
||||
use humhub\modules\post\models\Post;
|
||||
use humhub\modules\search\libs\SearchHelper;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\user\components\PermissionManager;
|
||||
use humhub\modules\user\helpers\AuthHelper;
|
||||
use humhub\modules\user\models\User;
|
||||
use Throwable;
|
||||
use Yii;
|
||||
use yii\base\Exception;
|
||||
use yii\base\InvalidArgumentException;
|
||||
use yii\db\ActiveQuery;
|
||||
use yii\db\IntegrityException;
|
||||
use yii\helpers\Url;
|
||||
|
||||
@ -79,19 +83,24 @@ use yii\helpers\Url;
|
||||
* @property string $updated_at
|
||||
* @property integer $updated_by
|
||||
* @property ContentContainer $contentContainer
|
||||
* @property-read mixed $contentName
|
||||
* @property-read mixed $content
|
||||
* @property-read ActiveQuery $tagRelations
|
||||
* @property-read ContentActiveRecord $model
|
||||
* @property-read mixed $contentDescription
|
||||
* @property-read StateServiceInterface $stateService
|
||||
* @property ContentContainerActiveRecord $container
|
||||
* @mixin PolymorphicRelation
|
||||
* @mixin GUID
|
||||
* @since 0.5
|
||||
*/
|
||||
class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletable
|
||||
class Content extends ActiveRecord implements Movable, ContentOwner, ArchiveableInterface, EditableInterface, ViewableInterface, SoftDeletable
|
||||
{
|
||||
/**
|
||||
* The default stream channel.
|
||||
* @since 1.6
|
||||
*/
|
||||
const STREAM_CHANNEL_DEFAULT = 'default';
|
||||
|
||||
public const STREAM_CHANNEL_DEFAULT = 'default';
|
||||
|
||||
/**
|
||||
* A array of user objects which should informed about this new content.
|
||||
@ -103,25 +112,25 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
/**
|
||||
* @var int The private visibility mode (e.g. for space member content or user profile posts for friends)
|
||||
*/
|
||||
const VISIBILITY_PRIVATE = 0;
|
||||
public const VISIBILITY_PRIVATE = 0;
|
||||
|
||||
/**
|
||||
* @var int Public visibility mode, e.g. content which are visibile for followers
|
||||
*/
|
||||
const VISIBILITY_PUBLIC = 1;
|
||||
public const VISIBILITY_PUBLIC = 1;
|
||||
|
||||
/**
|
||||
* @var int Owner visibility mode, only visible for contentContainer + content owner
|
||||
*/
|
||||
const VISIBILITY_OWNER = 2;
|
||||
public const VISIBILITY_OWNER = 2;
|
||||
|
||||
/**
|
||||
* Content States - By default, only content with the "Published" state is returned.
|
||||
*/
|
||||
const STATE_PUBLISHED = 1;
|
||||
const STATE_DRAFT = 10;
|
||||
const STATE_SCHEDULED = 20;
|
||||
const STATE_DELETED = 100;
|
||||
public const STATE_PUBLISHED = 1;
|
||||
public const STATE_DRAFT = 10;
|
||||
public const STATE_SCHEDULED = 20;
|
||||
public const STATE_DELETED = 100;
|
||||
|
||||
/**
|
||||
* @var ContentContainerActiveRecord the Container (e.g. Space or User) where this content belongs to.
|
||||
@ -137,7 +146,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
/**
|
||||
* @event Event is used when a Content state is changed.
|
||||
*/
|
||||
const EVENT_STATE_CHANGED = 'changedState';
|
||||
public const EVENT_STATE_CHANGED = 'changedState';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
@ -225,7 +234,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
$this->created_by ??= Yii::$app->user->id;
|
||||
}
|
||||
|
||||
$this->stream_sort_date = date('Y-m-d G:i:s');
|
||||
$this->stream_sort_date = date('Y-m-d H:i:s');
|
||||
|
||||
if ($this->created_by == "") {
|
||||
throw new Exception("Could not save content without created_by!");
|
||||
@ -241,7 +250,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
{
|
||||
if (array_key_exists('state', $changedAttributes)) {
|
||||
// Run process for new content(Send notifications) only after changing state
|
||||
$this->processNewContent();
|
||||
$this->processNewContent($insert);
|
||||
|
||||
$model = $this->getModel();
|
||||
if (!$insert && $model instanceof ContentActiveRecord && $this->getStateService()->isPublished()) {
|
||||
@ -275,7 +284,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
parent::afterSave($insert, $changedAttributes);
|
||||
}
|
||||
|
||||
private function processNewContent()
|
||||
private function processNewContent($insert)
|
||||
{
|
||||
if (!$this->getStateService()->isPublished()) {
|
||||
// Don't notify about not published Content
|
||||
@ -287,6 +296,11 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$insert) {
|
||||
// Update creation datetime after first publishing
|
||||
$this->updateAttributes(['created_at' => date('Y-m-d H:i:s')]);
|
||||
}
|
||||
|
||||
$record = $this->getModel();
|
||||
|
||||
Yii::debug('Process new content: ' . get_class($record) . ' ID: ' . $record->getPrimaryKey(), 'content');
|
||||
@ -345,12 +359,12 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
);
|
||||
}
|
||||
|
||||
\humhub\modules\content\notifications\ContentCreated::instance()
|
||||
NotificationsContentCreated::instance()
|
||||
->from($this->createdBy)
|
||||
->about($contentSource)
|
||||
->sendBulk($userQuery);
|
||||
|
||||
\humhub\modules\content\activities\ContentCreated::instance()
|
||||
ActivitiesContentCreated::instance()
|
||||
->from($this->createdBy)
|
||||
->about($contentSource)->save();
|
||||
}
|
||||
@ -372,7 +386,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
*/
|
||||
public function afterDelete()
|
||||
{
|
||||
// Try delete the underlying object (Post, Question, Task, ...)
|
||||
// Try to delete the underlying object (Post, Question, Task, ...)
|
||||
$this->resetPolymorphicRelation();
|
||||
|
||||
/** @var ContentActiveRecord $record */
|
||||
@ -557,19 +571,31 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
* @throws Exception
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
*/
|
||||
public function canArchive(): bool
|
||||
public function canArchive($user = null): bool
|
||||
{
|
||||
// Currently global content can not be archived
|
||||
if (!$this->getContainer()) {
|
||||
return $this->canEdit();
|
||||
}
|
||||
$appUser = Yii::$app->user;
|
||||
|
||||
// No need to archive content on an archived container, content is marked as archived already
|
||||
if ($this->getContainer()->isArchived()) {
|
||||
if ($appUser->isGuest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getContainer()->permissionManager->can(new ManageContent());
|
||||
if ($user === null) {
|
||||
$user = $appUser->getIdentity();
|
||||
} elseif (!($user instanceof User)) {
|
||||
$user = User::findOne(['id' => $user]);
|
||||
}
|
||||
|
||||
// Currently global content can not be archived
|
||||
if (!$container = $this->getContainer()) {
|
||||
return $this->canEdit($user);
|
||||
}
|
||||
|
||||
// No need to archive content on an archived container, content is marked as archived already
|
||||
if ($container->isArchived()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $container->getPermissionManager($user)->can(new ManageContent());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -589,7 +615,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \Throwable
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function move(ContentContainerActiveRecord $container = null, $force = false)
|
||||
{
|
||||
@ -688,7 +714,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
* @param ContentContainerActiveRecord|null $container
|
||||
* @return bool determines if the current user is generally permitted to move content on the given container (or the related container if no container was provided)
|
||||
* @throws IntegrityException
|
||||
* @throws \Throwable
|
||||
* @throws Throwable
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
*/
|
||||
public function checkMovePermission(ContentContainerActiveRecord $container = null)
|
||||
@ -783,7 +809,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
* Relation to ContentContainer model
|
||||
* Note: this is not a Space or User instance!
|
||||
*
|
||||
* @return \yii\db\ActiveQuery
|
||||
* @return ActiveQuery
|
||||
* @since 1.1
|
||||
*/
|
||||
public function getContentContainer()
|
||||
@ -794,7 +820,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
/**
|
||||
* Returns the ContentTagRelation query.
|
||||
*
|
||||
* @return \yii\db\ActiveQuery
|
||||
* @return ActiveQuery
|
||||
* @since 1.2.2
|
||||
*/
|
||||
public function getTagRelations()
|
||||
@ -805,7 +831,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
/**
|
||||
* Returns all content related tags ContentTags related to this content.
|
||||
*
|
||||
* @return \yii\db\ActiveQuery
|
||||
* @return ActiveQuery
|
||||
* @since 1.2.2
|
||||
*/
|
||||
public function getTags($tagClass = ContentTag::class)
|
||||
@ -852,19 +878,21 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
* @return bool can edit/create this content
|
||||
* @throws Exception
|
||||
* @throws IntegrityException
|
||||
* @throws \Throwable
|
||||
* @throws Throwable
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
* @since 1.1
|
||||
*/
|
||||
public function canEdit($user = null)
|
||||
public function canEdit($user = null): bool
|
||||
{
|
||||
if (Yii::$app->user->isGuest) {
|
||||
$appUser = Yii::$app->user;
|
||||
|
||||
if ($appUser->isGuest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user === null) {
|
||||
$user = Yii::$app->user->getIdentity();
|
||||
} else if (!($user instanceof User)) {
|
||||
$user = $appUser->getIdentity();
|
||||
} elseif (!($user instanceof User)) {
|
||||
$user = User::findOne(['id' => $user]);
|
||||
}
|
||||
|
||||
@ -882,17 +910,17 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
|
||||
// Check additional manage permission for the given container
|
||||
if ($this->container) {
|
||||
if ($model->isNewRecord && $model->hasCreatePermission() && $this->container->getPermissionManager($user)->can($model->getCreatePermission())) {
|
||||
if (($isNewRecord = $model->isNewRecord) && $model->hasCreatePermission() && $this->container->getPermissionManager($user)->can($model->getCreatePermission())) {
|
||||
return true;
|
||||
}
|
||||
if (!$model->isNewRecord && $model->hasManagePermission() && $this->container->getPermissionManager($user)->can($model->getManagePermission())) {
|
||||
if (!$isNewRecord && $model->hasManagePermission() && $this->container->getPermissionManager($user)->can($model->getManagePermission())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if underlying models canEdit implementation
|
||||
// ToDo: Implement this as interface
|
||||
if (method_exists($model, 'canEdit') && $model->canEdit($user)) {
|
||||
// ToDo: Send deprecation waring when not implementing EditableInterface
|
||||
if (($model instanceof EditableInterface || method_exists($model, 'canEdit')) && $model->canEdit($user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -947,14 +975,14 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
* @param User|integer $user
|
||||
* @return boolean can view this content
|
||||
* @throws Exception
|
||||
* @throws \Throwable
|
||||
* @throws Throwable
|
||||
* @since 1.1
|
||||
*/
|
||||
public function canView($user = null)
|
||||
public function canView($user = null): bool
|
||||
{
|
||||
if (!$user && !Yii::$app->user->isGuest) {
|
||||
$user = Yii::$app->user->getIdentity();
|
||||
} else if (!$user instanceof User) {
|
||||
} elseif (!$user instanceof User) {
|
||||
$user = User::findOne(['id' => $user]);
|
||||
}
|
||||
|
||||
@ -1032,7 +1060,7 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
*/
|
||||
public function updateStreamSortTime()
|
||||
{
|
||||
$this->updateAttributes(['stream_sort_date' => date('Y-m-d G:i:s')]);
|
||||
$this->updateAttributes(['stream_sort_date' => date('Y-m-d H:i:s')]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1083,5 +1111,4 @@ class Content extends ActiveRecord implements Movable, ContentOwner, SoftDeletab
|
||||
{
|
||||
$this->getStateService()->set($state, $options);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,10 +8,9 @@
|
||||
|
||||
namespace humhub\modules\content\models;
|
||||
|
||||
use humhub\components\ActiveRecord;
|
||||
use humhub\components\behaviors\PolymorphicRelation;
|
||||
use humhub\modules\content\components\ContentContainerActiveRecord;
|
||||
use humhub\modules\space\models\Space;
|
||||
use yii\db\ActiveRecord;
|
||||
|
||||
/**
|
||||
* This is the model class for table "contentcontainer".
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -145,8 +145,8 @@ class ContentContainerStreamTest extends HumHubDbTestCase
|
||||
$this->becomeUser('User2');
|
||||
$deleteId = $this->createPost('Something to delete',['visibility' => Content::VISIBILITY_PRIVATE]);
|
||||
|
||||
$post = Post::findOne(['id' => $deleteId]);
|
||||
$post->content->softDelete();
|
||||
$content = Content::findOne(['id' => $deleteId]);
|
||||
$content->softDelete();
|
||||
|
||||
$ids = $this->getStreamActionIds($this->space, 3);
|
||||
|
||||
|
@ -20,7 +20,7 @@ use Throwable;
|
||||
|
||||
class RichTextPostProcessTest extends HumHubDbTestCase
|
||||
{
|
||||
public function _before()
|
||||
private function addTestOEmbedRecords()
|
||||
{
|
||||
(new UrlOembed([
|
||||
'url' => 'https://www.youtube.com/watch?v=yt1',
|
||||
@ -31,12 +31,12 @@ class RichTextPostProcessTest extends HumHubDbTestCase
|
||||
'url' => 'https://www.youtube.com/watch?v=yt2',
|
||||
'preview' => 'yt2'
|
||||
]))->save();
|
||||
|
||||
parent::_before();
|
||||
}
|
||||
|
||||
public function testProcessSingleOembed()
|
||||
{
|
||||
$this->addTestOEmbedRecords();
|
||||
|
||||
$post = Post::findOne(['id' => 1]);
|
||||
$text = '[https://www.youtube.com/watch?v=yt1](oembed:https://www.youtube.com/watch?v=yt1)';
|
||||
$result = RichText::postProcess($text, $post);
|
||||
@ -47,6 +47,8 @@ class RichTextPostProcessTest extends HumHubDbTestCase
|
||||
|
||||
public function testProcessNoneOembed()
|
||||
{
|
||||
$this->addTestOEmbedRecords();
|
||||
|
||||
$post = Post::findOne(['id' => 1]);
|
||||
$text = '[Normal link](https://www.youtube.com/watch?v=yt1)';
|
||||
$result = RichText::postProcess($text, $post);
|
||||
@ -55,6 +57,8 @@ class RichTextPostProcessTest extends HumHubDbTestCase
|
||||
|
||||
public function testProcessMultipleOembed()
|
||||
{
|
||||
$this->addTestOEmbedRecords();
|
||||
|
||||
$post = Post::findOne(['id' => 1]);
|
||||
$text = '[https://www.youtube.com/watch?v=yt1](oembed:https://www.youtube.com/watch?v=yt1)\n\n[https://www.youtube.com/watch?v=yt2](oembed:https://www.youtube.com/watch?v=yt2)';
|
||||
$result = RichText::postProcess($text, $post);
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -28,6 +28,7 @@ use yii\db\IntegrityException;
|
||||
use yii\db\StaleObjectException;
|
||||
use yii\helpers\Url;
|
||||
use yii\web\UploadedFile;
|
||||
use humhub\interfaces\ViewableInterface;
|
||||
|
||||
/**
|
||||
* This is the model class for table "file".
|
||||
@ -85,7 +86,7 @@ use yii\web\UploadedFile;
|
||||
* @since 0.5
|
||||
* @noinspection PropertiesInspection
|
||||
*/
|
||||
class File extends FileCompat
|
||||
class File extends FileCompat implements ViewableInterface
|
||||
{
|
||||
/**
|
||||
* @event Event that is triggered after a new file content has been stored.
|
||||
@ -285,10 +286,15 @@ class File extends FileCompat
|
||||
*
|
||||
* If the file is not an instance of HActiveRecordContent or HActiveRecordContentAddon
|
||||
* the file is readable for all.
|
||||
*
|
||||
* @param string|User $userId
|
||||
*
|
||||
* @return bool
|
||||
* @throws IntegrityException
|
||||
* @throws Throwable
|
||||
* @throws \yii\base\Exception
|
||||
*/
|
||||
public function canRead($userId = "")
|
||||
public function canRead($userId = ""): bool
|
||||
{
|
||||
$object = $this->getPolymorphicRelation();
|
||||
if ($object instanceof ContentActiveRecord || $object instanceof ContentAddonActiveRecord) {
|
||||
@ -298,13 +304,26 @@ class File extends FileCompat
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canView($user = null): bool
|
||||
{
|
||||
return $this->canRead($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given file can be deleted.
|
||||
*
|
||||
* If the file is not an instance of ContentActiveRecord or ContentAddonActiveRecord
|
||||
* the file is readable for all unless there is method canEdit or canDelete implemented.
|
||||
*
|
||||
* @param null $userId
|
||||
*
|
||||
* @return bool
|
||||
* @throws IntegrityException
|
||||
* @throws InvalidConfigException
|
||||
* @throws Throwable
|
||||
* @throws \yii\base\Exception
|
||||
*/
|
||||
public function canDelete($userId = null)
|
||||
public function canDelete($userId = null): bool
|
||||
{
|
||||
$object = $this->getPolymorphicRelation();
|
||||
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -8,27 +8,28 @@
|
||||
|
||||
namespace humhub\modules\installer\commands;
|
||||
|
||||
use humhub\helpers\DatabaseHelper;
|
||||
use humhub\libs\DynamicConfig;
|
||||
use humhub\libs\UUID;
|
||||
use humhub\modules\installer\libs\InitialData;
|
||||
use humhub\modules\user\models\Group;
|
||||
use humhub\modules\user\models\Password;
|
||||
use humhub\modules\user\models\User;
|
||||
use Yii;
|
||||
use yii\base\Exception;
|
||||
use yii\console\Controller;
|
||||
use yii\console\ExitCode;
|
||||
use yii\helpers\Console;
|
||||
use yii\base\Exception;
|
||||
use humhub\modules\user\models\User;
|
||||
use humhub\modules\user\models\Password;
|
||||
use humhub\modules\user\models\Group;
|
||||
use humhub\modules\installer\libs\InitialData;
|
||||
use humhub\libs\UUID;
|
||||
use humhub\libs\DynamicConfig;
|
||||
|
||||
/**
|
||||
* Console Install
|
||||
*
|
||||
*
|
||||
* Example usage:
|
||||
* php yii installer/write-db-config "$HUMHUB_DB_HOST" "$HUMHUB_DB_NAME" "$HUMHUB_DB_USER" "$HUMHUB_DB_PASSWORD"
|
||||
* php yii installer/install-db
|
||||
* php yii installer/write-site-config "$HUMHUB_NAME" "$HUMHUB_EMAIL"
|
||||
* php yii installer/create-admin-account
|
||||
*
|
||||
*
|
||||
*/
|
||||
class InstallController extends Controller
|
||||
{
|
||||
@ -42,9 +43,9 @@ class InstallController extends Controller
|
||||
|
||||
return ExitCode::OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to open a connection to given db.
|
||||
* Tries to open a connection to given db.
|
||||
* On success: Writes given settings to config-file and reloads it.
|
||||
* On failure: Throws exception
|
||||
*/
|
||||
@ -80,12 +81,12 @@ class InstallController extends Controller
|
||||
$this->stdout("Install DB:\n\n", Console::FG_YELLOW);
|
||||
|
||||
$this->stdout(" * Checking Database Connection\n", Console::FG_YELLOW);
|
||||
if(!$this->checkDBConnection()){
|
||||
throw new Exception("Could not connect to DB!");
|
||||
if (true !== $message = $this->checkDBConnection()) {
|
||||
throw new Exception($message ?? "Could not connect to DB!");
|
||||
}
|
||||
|
||||
$this->stdout(" * Installing Database\n", Console::FG_YELLOW);
|
||||
|
||||
|
||||
Yii::$app->cache->flush();
|
||||
// Disable max execution time to avoid timeouts during migrations
|
||||
@ini_set('max_execution_time', 0);
|
||||
@ -119,7 +120,7 @@ class InstallController extends Controller
|
||||
$user->profile->firstname = 'Sys';
|
||||
$user->profile->lastname = 'Admin';
|
||||
$user->profile->save();
|
||||
|
||||
|
||||
$password = new Password();
|
||||
$password->user_id = $user->id;
|
||||
$password->setPassword($admin_pass);
|
||||
@ -179,6 +180,8 @@ class InstallController extends Controller
|
||||
|
||||
/**
|
||||
* Tries to open global db connection and checks result.
|
||||
*
|
||||
* @return true|null|string
|
||||
*/
|
||||
private function checkDBConnection()
|
||||
{
|
||||
@ -186,10 +189,9 @@ class InstallController extends Controller
|
||||
// call setActive with true to open connection.
|
||||
Yii::$app->db->open();
|
||||
// return the current connection state.
|
||||
return Yii::$app->db->getIsActive();
|
||||
return Yii::$app->db->getIsActive() ?: null;
|
||||
} catch (Exception $e) {
|
||||
$this->stderr($e->getMessage());
|
||||
return DatabaseHelper::handleConnectionErrors($e, false, false, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ use Yii;
|
||||
class AdminController extends Controller
|
||||
{
|
||||
/**
|
||||
* @inerhitdoc
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getAccessRules()
|
||||
protected function getAccessRules()
|
||||
{
|
||||
return [
|
||||
['permissions' => [ManageSettings::class]],
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -56,8 +56,7 @@ class Events extends BaseObject
|
||||
}
|
||||
|
||||
foreach ($event->modules as $m => $module) {
|
||||
/* @var ModelModule $module */
|
||||
if (!$module->getFilterService()->isFiltered()) {
|
||||
if ($module instanceof ModelModule && !$module->getFilterService()->isFiltered()) {
|
||||
unset($event->modules[$m]);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class LicenceController extends Controller
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getAccessRules()
|
||||
protected function getAccessRules()
|
||||
{
|
||||
return [
|
||||
['permissions' => ManageModules::class]
|
||||
|
@ -68,7 +68,6 @@ class MailTarget extends BaseTarget
|
||||
], $notification->getViewParams());
|
||||
|
||||
$mail = Yii::$app->mailer->compose($this->view, $viewParams)
|
||||
->setFrom([Yii::$app->settings->get('mailer.systemEmailAddress') => Yii::$app->settings->get('mailer.systemEmailName')])
|
||||
->setTo($recipient->email)
|
||||
->setSubject(str_replace("\n", " ", trim($notification->getMailSubject())));
|
||||
if ($replyTo = Yii::$app->settings->get('mailer.systemEmailReplyTo')) {
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -535,6 +535,9 @@ class StreamQuery extends Model
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Sorting
|
||||
*/
|
||||
/**
|
||||
* Setup Sorting
|
||||
*/
|
||||
@ -560,13 +563,28 @@ class StreamQuery extends Model
|
||||
], [':to' => $this->to]);
|
||||
}
|
||||
} else {
|
||||
$this->_query->orderBy('content.id DESC');
|
||||
$this->_query->orderBy('content.created_at DESC,content.id DESC');
|
||||
if (!empty($this->from)) {
|
||||
$this->_query->andWhere("content.id < :from", [':from' => $this->from]);
|
||||
$this->_query->andWhere(
|
||||
['or',
|
||||
"content.created_at < (SELECT created_at FROM content wd WHERE wd.id=:from)",
|
||||
['and',
|
||||
"content.created_at = (SELECT created_at FROM content wd WHERE wd.id=:from)",
|
||||
"content.id < :from"
|
||||
],
|
||||
], [':from' => $this->from]);
|
||||
} elseif (!empty($this->to)) {
|
||||
$this->_query->andWhere("content.id > :to", [':to' => $this->to]);
|
||||
$this->_query->andWhere(
|
||||
['or',
|
||||
"content.created_at > (SELECT created_at FROM content wd WHERE wd.id=:to)",
|
||||
['and',
|
||||
"content.created_at = (SELECT created_at FROM content wd WHERE wd.id=:to)",
|
||||
"content.id > :to"
|
||||
],
|
||||
], [':to' => $this->to]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -11,4 +11,5 @@ modules:
|
||||
config:
|
||||
Yii2:
|
||||
configFile: 'codeception/config/unit.php'
|
||||
transaction: false
|
||||
transaction: true
|
||||
cleanup: false
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
/** @noinspection MissedFieldInspection */
|
||||
|
||||
return [
|
||||
'id' => basename(__DIR__),
|
||||
'class' => \humhub\components\Module::class,
|
||||
'namespace' => "Some\\Name\\Space",
|
||||
'isCoreModule' => true,
|
||||
];
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
/** @noinspection MissedFieldInspection */
|
||||
|
||||
return [
|
||||
'id' => basename(__DIR__),
|
||||
'class' => \humhub\components\Module::class,
|
||||
'namespace' => "Some\\Name\\Space",
|
||||
'isInstallerModule' => true,
|
||||
];
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
return [];
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
$config = require dirname(__DIR__) . "/module1/config.php";
|
||||
$config['id'] = basename(__DIR__);
|
||||
$config['class'] = \yii\base\Module::class;
|
||||
|
||||
return $config;
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace Some\Name\Space\module1;
|
||||
|
||||
class Module extends \humhub\components\Module
|
||||
{
|
||||
public const ID = 'module1';
|
||||
public const NAMESPACE = __NAMESPACE__;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
/** @noinspection MissedFieldInspection */
|
||||
|
||||
require_once __DIR__ . "/Module.php";
|
||||
|
||||
return [
|
||||
'id' => 'module1',
|
||||
'class' => \Some\Name\Space\module1\Module::class,
|
||||
'namespace' => "Some\\Name\\Space\\module1",
|
||||
'events' => [
|
||||
[
|
||||
'class' => \humhub\tests\codeception\unit\components\ModuleManagerTest::class,
|
||||
'event' => 'valid',
|
||||
'callback' => [
|
||||
\humhub\tests\codeception\unit\components\ModuleManagerTest::class,
|
||||
'handleEvent',
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "example",
|
||||
"version": "1.0",
|
||||
"name": "My Example Module 1",
|
||||
"description": "My testing module 1.",
|
||||
"humhub": {
|
||||
"minVersion": "1.2"
|
||||
},
|
||||
"keywords": ["my", "cool", "one"],
|
||||
"homepage": "https://www.example.com",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tom Coder",
|
||||
"email": "tc@example.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sarah Mustermann",
|
||||
"email": "sm@example.com",
|
||||
"homepage": "http://example.com",
|
||||
"role": "Translator"
|
||||
}
|
||||
],
|
||||
"licence": "AGPL-3.0-or-later"
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace Some\Name\Space\module2;
|
||||
|
||||
class Module extends \humhub\components\Module
|
||||
{
|
||||
public const ID = 'module2';
|
||||
public const NAMESPACE = __NAMESPACE__;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
/** @noinspection MissedFieldInspection */
|
||||
|
||||
require_once __DIR__ . "/Module.php";
|
||||
|
||||
return [
|
||||
'id' => 'module2',
|
||||
'class' => \Some\Name\Space\module2\Module::class,
|
||||
'namespace' => "Some\\Name\\Space\\module2",
|
||||
'events' => [
|
||||
[
|
||||
'class' => \humhub\tests\codeception\unit\components\ModuleManagerTest::class,
|
||||
'event' => 'valid',
|
||||
'callback' => [
|
||||
\humhub\tests\codeception\unit\components\ModuleManagerTest::class,
|
||||
'handleEvent',
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "example",
|
||||
"version": "1.0",
|
||||
"name": "My Example Module 2",
|
||||
"description": "My testing module 2.",
|
||||
"humhub": {
|
||||
"minVersion": "1.2"
|
||||
},
|
||||
"keywords": ["my", "cool", "two"],
|
||||
"homepage": "https://www.example.com",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tom Coder",
|
||||
"email": "tc@example.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Sarah Mustermann",
|
||||
"email": "sm@example.com",
|
||||
"homepage": "http://example.com",
|
||||
"role": "Translator"
|
||||
}
|
||||
],
|
||||
"licence": "AGPL-3.0-or-later"
|
||||
}
|
@ -49,6 +49,8 @@ class HumHubDbTestCase extends Unit
|
||||
|
||||
public $time;
|
||||
|
||||
public array $firedEvents = [];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
if (\Yii::$app !== null) {
|
||||
|
@ -34,6 +34,7 @@ use yii\db\ActiveRecord;
|
||||
use yii\db\Command;
|
||||
use yii\db\ExpressionInterface;
|
||||
use yii\db\Query;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\FileHelper;
|
||||
use yii\log\Dispatcher;
|
||||
|
||||
@ -619,15 +620,15 @@ trait HumHubHelperTrait
|
||||
* @since 1.15
|
||||
* @see static::assertEvents()
|
||||
*/
|
||||
public function handleEvent(Event $event)
|
||||
public function handleEvent(Event $event, array $eventData = [])
|
||||
{
|
||||
$eventData = [
|
||||
$eventData = ArrayHelper::merge([
|
||||
'class' => get_class($event),
|
||||
'event' => $event->name,
|
||||
'sender' => $event->sender,
|
||||
'data' => $event->data,
|
||||
'handled' => $event->handled,
|
||||
];
|
||||
], $eventData);
|
||||
|
||||
$this->firedEvents[] = $eventData;
|
||||
}
|
||||
|
25
protected/humhub/tests/codeception/_support/TestMailer.php
Normal file
25
protected/humhub/tests/codeception/_support/TestMailer.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace tests\codeception\_support;
|
||||
|
||||
use humhub\components\mail\Mailer;
|
||||
use humhub\interfaces\MailerInterface;
|
||||
|
||||
/**
|
||||
* @since 1.15
|
||||
*/
|
||||
class TestMailer extends \Codeception\Lib\Connector\Yii2\TestMailer implements MailerInterface
|
||||
{
|
||||
public function compose($view = null, array $params = [])
|
||||
{
|
||||
$message = parent::compose($view, $params);
|
||||
|
||||
return Mailer::ensureHumHubDefaultFromValues($message);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
/**
|
||||
* Application configuration shared by all test types
|
||||
*/
|
||||
|
||||
$default = [
|
||||
'name' => 'HumHub Test',
|
||||
'language' => 'en-US',
|
||||
@ -32,6 +33,13 @@ $default = [
|
||||
'scriptUrl' => '/index-test.php',
|
||||
],
|
||||
],
|
||||
'container' => [
|
||||
'definitions' => [
|
||||
\Codeception\Lib\Connector\Yii2\TestMailer::class => [
|
||||
'class' => \tests\codeception\_support\TestMailer::class,
|
||||
]
|
||||
]
|
||||
],
|
||||
'modules' => [
|
||||
'user' => [
|
||||
'passwordStrength' => [
|
||||
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @link https://www.humhub.org/
|
||||
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
|
||||
* @license https://www.humhub.com/licences
|
||||
*/
|
||||
|
||||
namespace humhub\tests\codeception\unit\components;
|
||||
|
||||
use humhub\components\ModuleManager;
|
||||
|
||||
class ModuleManagerMock extends ModuleManager
|
||||
{
|
||||
public function &myModules(): array
|
||||
{
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
public function &myEnabledModules(): array
|
||||
{
|
||||
return $this->enabledModules;
|
||||
}
|
||||
|
||||
public function &myCoreModules(): array
|
||||
{
|
||||
return $this->coreModules;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -385,7 +385,7 @@ class SettingsManagerTest extends HumHubDbTestCase
|
||||
|
||||
// changing the value behind the scenes
|
||||
$value2 = 'third value';
|
||||
$this->dbUpdate($table, ['value' => $value2], ['name' => $setting, 'module_id' => $module]);
|
||||
self::dbUpdate($table, ['value' => $value2], ['name' => $setting, 'module_id' => $module]);
|
||||
$this->assertRecordValue($value2, 'value', $table, ['name' => $setting, 'module_id' => $module]);
|
||||
|
||||
// getting the value now should still show tho "old" value
|
||||
|
@ -17,30 +17,30 @@ use Yii;
|
||||
class ModuleAutoLoaderTest extends Unit
|
||||
{
|
||||
/** @var array list of expected core modules */
|
||||
const EXPECTED_CORE_MODULES = [
|
||||
'activity',
|
||||
'admin',
|
||||
'comment',
|
||||
'content',
|
||||
'dashboard',
|
||||
'file',
|
||||
'friendship',
|
||||
'installer',
|
||||
'ldap',
|
||||
'like',
|
||||
'live',
|
||||
'marketplace',
|
||||
'notification',
|
||||
'post',
|
||||
'queue',
|
||||
'search',
|
||||
'space',
|
||||
'stream',
|
||||
'topic',
|
||||
'tour',
|
||||
'ui',
|
||||
'user',
|
||||
'web'
|
||||
public const EXPECTED_CORE_MODULES = [
|
||||
'humhub\modules\activity\Module' => 'activity',
|
||||
'humhub\modules\admin\Module' => 'admin',
|
||||
'humhub\modules\comment\Module' => 'comment',
|
||||
'humhub\modules\content\Module' => 'content',
|
||||
'humhub\modules\dashboard\Module' => 'dashboard',
|
||||
'humhub\modules\file\Module' => 'file',
|
||||
'humhub\modules\friendship\Module' => 'friendship',
|
||||
'humhub\modules\installer\Module' => 'installer',
|
||||
'humhub\modules\ldap\Module' => 'ldap',
|
||||
'humhub\modules\like\Module' => 'like',
|
||||
'humhub\modules\live\Module' => 'live',
|
||||
'humhub\modules\marketplace\Module' => 'marketplace',
|
||||
'humhub\modules\notification\Module' => 'notification',
|
||||
'humhub\modules\post\Module' => 'post',
|
||||
'humhub\modules\queue\Module' => 'queue',
|
||||
'humhub\modules\search\Module' => 'search',
|
||||
'humhub\modules\space\Module' => 'space',
|
||||
'humhub\modules\stream\Module' => 'stream',
|
||||
'humhub\modules\topic\Module' => 'topic',
|
||||
'humhub\modules\tour\Module' => 'tour',
|
||||
'humhub\modules\ui\Module' => 'ui',
|
||||
'humhub\modules\user\Module' => 'user',
|
||||
'humhub\modules\web\Module' => 'web',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,8 @@
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
use humhub\helpers\DatabaseHelper;
|
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||
|
||||
// fcgi doesn't have STDIN and STDOUT defined by default
|
||||
@ -25,6 +27,11 @@ $config = yii\helpers\ArrayHelper::merge(
|
||||
require(__DIR__ . '/config/console.php')
|
||||
);
|
||||
|
||||
$application = new humhub\components\console\Application($config);
|
||||
$exitCode = $application->run();
|
||||
exit($exitCode);
|
||||
try {
|
||||
$exitCode = (new humhub\components\console\Application($config))->run();
|
||||
exit($exitCode);
|
||||
} catch (\Throwable $ex) {
|
||||
if (null === DatabaseHelper::handleConnectionErrors($ex)) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user