Enh: Updated Translations (translate.humhub.org)

This commit is contained in:
HumHub Translations 2022-03-15 23:29:08 +01:00
commit f7b396d300
303 changed files with 6383 additions and 5876 deletions

View File

@ -25,10 +25,11 @@ jobs:
- ubuntu-latest
php-version:
- "7.2"
# - "7.2"
- "7.3"
- "7.4"
- "8.0"
# - "8.1"
mysql-version:
- "5.7"

3
.gitignore vendored
View File

@ -24,6 +24,7 @@ uploads/profile_image/banner/*
nbproject
.idea/*
.gitmodules
.vscode
themes/*
!themes/HumHub
@ -38,3 +39,5 @@ favicon.ico
**/tests/codeception/_output/**
/.php_cs.cache
.DS_Store

View File

@ -5,6 +5,9 @@ HumHub Changelog
-------------------
- Fix #5480: `el` language file in Admin section broken
- Fix #5479: Fix ContentContainerPermissionAccess without provided container
- Fix #5513: Fix PermaLink controller error for content without container
- Fix #5517: Don't send "Follows" notification on request friendship
- Fix #5563: Limit backup duration of RichText editor for 2 hours
1.10.3 (December 20, 2021)

View File

@ -1,7 +1,47 @@
1.11.0-beta.1 (March 1, 2022 - UNRELEASED)
------------------------------------------
1.11.0 (Unreleased)
--------------------------
- Fix #5434: Hide disabled next/prev buttons on guide first/last steps
- Fix #5456: `canImpersonate` only possible for SystemAdmins
- Enh #5462: Added Single Page Post View
- Enh #5476: Rework modules administration section
- Enh #5472: New interface `TabbedFormModel` for activate first tab with error input
- Enh #5224: Add reply-to email in the settings
- Enh #5471: On the pending approval page, add grouped actions and custom columns
- Enh #5490: Display confirmation message before display embedded content
- Enh #5258: Display who invited the user on the Approval page
- Enh #5475: Option for forbidden usernames
- Enh #4890: Allow to define actions in a controller which should not be intercepted by other actions
- Enh #5503: Allow profile fields link prefixes like "tel://"
- Enh #5510: oEmbed support for other social networks, redesign of oEmbed settings pages
- Fix #5534: Statistic input loading problem
- Enh #5523: Option to include E-Mail address to search
- Enh #5352: Remove "NewMembers" widget from Core
- Fix #5478: Avoid permalinks on comments related to a global content (not in a container) to crash.
- Fix #5547: Remove Google Fonts from E-mails
- Enh #5536: Optional notification w/ reason when admin deletes content/comment
- Fix #5549: Topic icon is missing in TopicPicker search
- Fix #5556: Formatted output for date fields in "About me" profile area.
- Fix #5553: Cannot have two or more users without email when emailRequired is disabled.
- Fix #5506: Modal: Close Icon optimization
- Fix #5564: Close modal button doesn't work after form validation
- Fix #5533: Users can't live in UTC
- Fix #5460: Untranslatable string New Updates in Stream
- Fix #5573: Allow replying for sub comments
- Fix #5518: Number of activities reported in the mail summary is always limited to 20
- Enh #5358: Remove deprecated "Directory" module
- Fix #5524: Mentioning Permission
- Fix #5529: Tooltip: improving readability
- Enh #5298: Added Followers to Space About Page
- Enh #4558: Deprecate CompatModuleManager
- Enh #5323: Remove deprecated "Setting" classes
- Enh #5381: Optimize module states query
- Enh #4823: Removed CHTML and CActiveForm classes as well as usages (plus refactoring)
- Fix #5449: File - Update info after `setStoredFileContent` and `setStoredFile`
- Enh #5127: LDAP: Reset mapping for single user only
- Fix #5581: Fix long words in comment form
- Fix #5578: Improved `rememberMe` parameter handling for thirdparty auth provider
- Fix #5304: In a button, data-action-cancel-text doesn't work
- Fix #5340: Mobile navigation: width detection problem
- Enh #5432: Possibility to add buttons in the People page with a module
- Fix #5585: Don't force password change on user simulating
- Fix #5591: Fix people/space cards banner

View File

@ -62,7 +62,7 @@
"web-token/jwt-signature-algorithm-hmac": ">=1.0 <3.0",
"web-token/jwt-signature-algorithm-rsa": ">=1.0 <3.0",
"xj/yii2-jplayer-widget": "*",
"yiisoft/yii2": "2.0.40",
"yiisoft/yii2": "2.0.45",
"yiisoft/yii2-authclient": "~2.2.0",
"yiisoft/yii2-bootstrap": "~2.0.0",
"yiisoft/yii2-httpclient": "~2.0.0",
@ -105,7 +105,10 @@
"platform-check": false,
"platform": {
"php": "7.3"
}
},
"allow-plugins": {
"yiisoft/yii2-composer": true
}
},
"scripts": {
"post-create-project-cmd": [

992
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,13 +10,13 @@ namespace humhub\assets;
use humhub\components\assets\WebStaticAssetBundle;
use yii\web\View;
class DirectoryAsset extends WebStaticAssetBundle
class CardsAsset extends WebStaticAssetBundle
{
/**
* @inheritdoc
*/
public $js = [
'js/humhub/humhub.directory.js',
'js/humhub/humhub.cards.js',
];
/**

View File

@ -13,8 +13,7 @@ namespace Zend\Stdlib;
use ArrayAccess;
use Countable;
use IteratorAggregate;
use Serializable;
use Zend\Stdlib\Exception\InvalidArgumentException;
use yii\base\InvalidArgumentException;
/**
* ***** PHP 7.3 modified variant of ZF2 Array Object *****
@ -23,7 +22,7 @@ use Zend\Stdlib\Exception\InvalidArgumentException;
*
* Extends version-specific "abstract" implementation.
*/
class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable
class ArrayObject implements IteratorAggregate, ArrayAccess, Countable
{
/**
* Properties of the object have their normal functionality
@ -83,7 +82,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
return $this->offsetExists($key);
}
if (in_array($key, $this->protectedProperties)) {
throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
throw new InvalidArgumentException('$key is a protected property, use a different key');
}
return isset($this->$key);
@ -102,7 +101,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
return $this->offsetSet($key, $value);
}
if (in_array($key, $this->protectedProperties)) {
throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
throw new InvalidArgumentException('$key is a protected property, use a different key');
}
$this->$key = $value;
}
@ -119,7 +118,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
return $this->offsetUnset($key);
}
if (in_array($key, $this->protectedProperties)) {
throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
throw new InvalidArgumentException('$key is a protected property, use a different key');
}
unset($this->$key);
}
@ -139,7 +138,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
return $ret;
}
if (in_array($key, $this->protectedProperties)) {
throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
throw new InvalidArgumentException('$key is a protected property, use a different key');
}
return $this->$key;
@ -185,7 +184,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
public function exchangeArray($data)
{
if (!is_array($data) && !is_object($data)) {
throw new Exception\InvalidArgumentException('Passed variable is not an array or object, using empty array instead');
throw new InvalidArgumentException('Passed variable is not an array or object, using empty array instead');
}
if (is_object($data) && ($data instanceof self || $data instanceof \ArrayObject)) {
@ -330,11 +329,11 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
/**
* Serialize an ArrayObject
*
* @return string
* @return array
*/
public function serialize()
public function __serialize(): array
{
return serialize(get_object_vars($this));
return get_object_vars($this);
}
/**
@ -371,7 +370,7 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
}
}
throw new Exception\InvalidArgumentException('The iterator class does not exist');
throw new InvalidArgumentException('The iterator class does not exist');
}
/**
@ -403,12 +402,11 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
/**
* Unserialize an ArrayObject
*
* @param string $data
* @param array $ar
* @return void
*/
public function unserialize($data)
public function __unserialize($ar)
{
$ar = unserialize($data);
$this->protectedProperties = array_keys(get_object_vars($this));
$this->setFlags($ar['flag']);

View File

@ -1,95 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\compat;
/**
* CActiveForm is a Yii 1 compatible active form
*
* @author luke
* @deprecated since 1.4
*/
class CActiveForm extends \yii\widgets\ActiveForm
{
public function label($model, $attribute, $htmlOptions = [])
{
return CHtml::activeLabel($model, $attribute, $htmlOptions);
}
public function labelEx($model, $attribute, $htmlOptions = [])
{
return CHtml::activeLabelEx($model, $attribute, $htmlOptions);
}
public function error($model, $attribute, $htmlOptions = [], $enableAjaxValidation = true, $enableClientValidation = true)
{
return CHtml::error($model, $attribute, $htmlOptions);
}
public function passwordField($model, $attribute, $htmlOptions = [])
{
return CHtml::activePasswordInput($model, $attribute, $htmlOptions);
}
public function textArea($model, $attribute, $htmlOptions = [])
{
return CHtml::activeTextarea($model, $attribute, $htmlOptions);
}
public function checkBox($model, $attribute, $htmlOptions = [])
{
return CHtml::activeCheckboxNoLabel($model, $attribute, $htmlOptions);
}
public function dropDownList($model, $attribute, $data, $htmlOptions = [])
{
return CHtml::activeDropDownList($model, $attribute, $data, $htmlOptions);
}
public function radioButtonList($model, $attribute, $data, $htmlOptions = [])
{
return CHtml::activeRadioList($model, $attribute, $data, $htmlOptions);
}
public function radioButton($model, $attribute, $options = [])
{
$name = isset($options['name']) ? $options['name'] : CHtml::getInputName($model, $attribute);
$value = CHtml::getAttributeValue($model, $attribute);
if (!array_key_exists('value', $options)) {
$options['value'] = '1';
}
$checked = "$value" === "{$options['value']}";
if (!array_key_exists('id', $options)) {
$options['id'] = CHtml::getInputId($model, $attribute);
}
return CHtml::radio($name, $checked, $options);
}
public function textField($model, $attribute, $htmlOptions = [])
{
return CHtml::activeTextField($model, $attribute, $htmlOptions);
}
public function fileField($model, $attribute, $htmlOptions = [])
{
return CHtml::activeFileInput($model, $attribute, $htmlOptions);
}
public function hiddenField($model, $attribute, $htmlOptions = [], $value=null)
{
if ($value !== null) {
$model->$attribute = $value;
}
return CHtml::activeHiddenInput($model, $attribute, $htmlOptions);
}
}

View File

@ -1,175 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\compat;
/**
* CHtml - Yii1 compatiblity
*
* @author luke
* @deprecated since 1.4
*/
class CHtml extends \yii\helpers\Html
{
public static function form($action, $method = "POST")
{
return self::beginForm($action, $method);
}
public static function hiddenField($name, $value)
{
return self::hiddenInput($name, $value);
}
public function ajaxButton()
{
return "";
}
/**
* Generates a label tag for a model attribute.
* This is an enhanced version of {@link activeLabel}. It will render additional
* CSS class and mark when the attribute is required.
* In particular, it calls {@link CModel::isAttributeRequired} to determine
* if the attribute is required.
* If so, it will add a CSS class {@link CHtml::requiredCss} to the label,
* and decorate the label with {@link CHtml::beforeRequiredLabel} and
* {@link CHtml::afterRequiredLabel}.
* @param CModel $model the data model
* @param string $attribute the attribute
* @param array $htmlOptions additional HTML attributes.
* @return string the generated label tag
*/
public static function activeLabelEx($model, $attribute, $htmlOptions = [])
{
$realAttribute = $attribute;
self::resolveName($model, $attribute); // strip off square brackets if any
$htmlOptions['required'] = $model->isAttributeRequired($attribute);
return self::activeLabel($model, $realAttribute, $htmlOptions);
}
/**
* Generates a text field input for a model attribute.
* If the attribute has input error, the input field's CSS class will
* be appended with {@link errorCss}.
* @param CModel $model the data model
* @param string $attribute the attribute
* @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
* attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
* @return string the generated input field
* @see clientChange
* @see activeInputField
*/
public static function activeTextField($model, $attribute, $htmlOptions = [])
{
self::resolveNameID($model, $attribute, $htmlOptions);
#self::clientChange('change', $htmlOptions);
return self::activeInput('text', $model, $attribute, $htmlOptions);
}
/**
* Generates input name for a model attribute.
* Note, the attribute name may be modified after calling this method if the name
* contains square brackets (mainly used in tabular input) before the real attribute name.
* @param CModel $model the data model
* @param string $attribute the attribute
* @return string the input name
*/
public static function resolveName($model, &$attribute)
{
$modelName = self::modelName($model);
if (($pos = strpos($attribute, '[')) !== false) {
if ($pos !== 0) // e.g. name[a][b]
return $modelName . '[' . substr($attribute, 0, $pos) . ']' . substr($attribute, $pos);
if (($pos = strrpos($attribute, ']')) !== false && $pos !== strlen($attribute) - 1) { // e.g. [a][b]name
$sub = substr($attribute, 0, $pos + 1);
$attribute = substr($attribute, $pos + 1);
return $modelName . $sub . '[' . $attribute . ']';
}
if (preg_match('/\](\w+\[.*)$/', $attribute, $matches)) {
$name = $modelName . '[' . str_replace(']', '][', trim(strtr($attribute, ['][' => ']', '[' => ']']), ']')) . ']';
$attribute = $matches[1];
return $name;
}
}
return $modelName . '[' . $attribute . ']';
}
/**
* Generates input name and ID for a model attribute.
* This method will update the HTML options by setting appropriate 'name' and 'id' attributes.
* This method may also modify the attribute name if the name
* contains square brackets (mainly used in tabular input).
* @param CModel $model the data model
* @param string $attribute the attribute
* @param array $htmlOptions the HTML options
*/
public static function resolveNameID($model, &$attribute, &$htmlOptions)
{
if (!isset($htmlOptions['name']))
$htmlOptions['name'] = self::resolveName($model, $attribute);
if (!isset($htmlOptions['id']))
$htmlOptions['id'] = self::getIdByName($htmlOptions['name']);
elseif ($htmlOptions['id'] === false)
unset($htmlOptions['id']);
}
/**
* Generates a valid HTML ID based on name.
* @param string $name name from which to generate HTML ID
* @return string the ID generated based on name.
*/
public static function getIdByName($name)
{
return str_replace(['[]', '][', '[', ']', ' '], ['', '_', '_', '', '_'], $name);
}
/**
* Generates HTML name for given model.
* @see CHtml::setModelNameConverter()
* @param CModel|string $model the data model or the model class name
* @return string the generated HTML name value
* @since 1.1.14
*/
public static function modelName($model)
{
return $model->formName();
}
/**
* Active Checkbox without Label
*
* @param type $model
* @param type $attribute
* @param type $options
* @return type
*/
public static function activeCheckboxNoLabel($model, $attribute, $options = [])
{
$name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
$value = static::getAttributeValue($model, $attribute);
if (!array_key_exists('value', $options)) {
$options['value'] = '1';
}
if (!array_key_exists('uncheck', $options)) {
$options['uncheck'] = '0';
}
$checked = "$value" === "{$options['value']}";
if (!array_key_exists('id', $options)) {
$options['id'] = static::getInputId($model, $attribute);
}
return static::checkbox($name, $checked, $options);
}
}

View File

@ -21,7 +21,7 @@ use yii\db\Expression;
* @property User $updatedBy
* @author luke
*/
class ActiveRecord extends \yii\db\ActiveRecord implements \Serializable
class ActiveRecord extends \yii\db\ActiveRecord
{
/**
@ -151,14 +151,14 @@ class ActiveRecord extends \yii\db\ActiveRecord implements \Serializable
*
* @link http://php.net/manual/en/function.serialize.php
* @since 1.2
* @return string
* @return array
*/
public function serialize()
public function __serialize(): array
{
return serialize([
return [
'attributes' => $this->getAttributes(),
'oldAttributes' => $this->getOldAttributes()
]);
];
}
/**
@ -167,13 +167,20 @@ class ActiveRecord extends \yii\db\ActiveRecord implements \Serializable
* Note: Subclasses have to call $this->init() if overwriting this function.
*
* @link http://php.net/manual/en/function.unserialize.php
* @param string $serialized
* @param array $unserializedArr
*/
public function unserialize($serialized)
public function __unserialize($unserializedArr)
{
$this->init();
$unserializedArr = unserialize($serialized);
$this->setAttributes($unserializedArr['attributes'],false);
$this->setOldAttributes($unserializedArr['oldAttributes'],false);
$this->setOldAttributes($unserializedArr['oldAttributes']);
}
/**
* @inheritdoc
*/
public function getAttributeLabel($attribute)
{
return $attribute === null ? '' : parent::getAttributeLabel($attribute);
}
}

View File

@ -59,6 +59,12 @@ class Controller extends \yii\web\Controller
*/
protected $access = StrictAccess::class;
/**
* @var string[] List of action ids which should not be intercepted by another actions. Use '*' for all action ids.
* @since 1.9
*/
protected $doNotInterceptActionIds = [];
/**
* Returns access rules for the standard access control behavior.
*
@ -284,4 +290,30 @@ class Controller extends \yii\web\Controller
\humhub\widgets\TopMenu::setViewState();
}
}
/**
* Check if action cannot be intercepted
*
* @since 1.9
* @param string|null $actionId, NULL - to use current action
* @return bool
*/
public function isNotInterceptedAction(string $actionId = null) : bool
{
if ($actionId === null) {
if (isset($this->action->id)) {
$actionId = $this->action->id;
} else {
return false;
}
}
foreach ($this->doNotInterceptActionIds as $doNotInterceptActionId) {
if ($doNotInterceptActionId === '*' || $doNotInterceptActionId === $actionId) {
return true;
}
}
return false;
}
}

View File

@ -8,21 +8,23 @@
namespace humhub\components;
use humhub\modules\activity\components\BaseActivity;
use humhub\modules\activity\models\Activity;
use Yii;
use yii\helpers\Json;
use humhub\models\Setting;
use humhub\modules\activity\components\BaseActivity;
use humhub\modules\content\models\ContentContainerSetting;
use humhub\modules\file\libs\FileHelper;
use humhub\modules\notification\components\BaseNotification;
use humhub\modules\content\models\ContentContainerSetting;
use Yii;
use yii\helpers\Json;
use yii\web\AssetBundle;
use yii\web\HttpException;
/**
* Base Class for Modules / Extensions
*
* @property-read string $name
* @property-read string $description
* @property-read bool $isActivated
* @property SettingsManager $settings
* @mixin OnlineModule
* @author luke
*/
class Module extends \yii\base\Module
@ -204,6 +206,16 @@ class Module extends \yii\base\Module
return $this->getBasePath() . '/' . $this->resourcesPath;
}
/**
* Check this module is activated
*
* @return bool
*/
public function getIsActivated(): bool
{
return (bool) Yii::$app->hasModule($this->id);
}
/**
* Enables this module
*

View File

@ -12,6 +12,8 @@ use humhub\components\bootstrap\ModuleAutoLoader;
use humhub\components\console\Application as ConsoleApplication;
use humhub\libs\BaseSettingsManager;
use humhub\models\ModuleEnabled;
use humhub\modules\admin\events\ModulesEvent;
use humhub\modules\marketplace\Module as ModuleMarketplace;
use Yii;
use yii\base\Component;
use yii\base\Event;
@ -51,6 +53,12 @@ class ModuleManager extends Component
*/
const EVENT_AFTER_MODULE_DISABLE = 'afterModuleDisabled';
/**
* @event triggered after filter modules
* @since 1.11
*/
const EVENT_AFTER_FILTER_MODULES = 'afterFilterModules';
/**
* Create a backup on module folder deletion
*
@ -239,25 +247,25 @@ class ModuleManager extends Component
*/
public function getModules($options = [])
{
$options = array_merge([
'includeCoreModules' => false,
'enabled' => false,
'returnClass' => false,
], $options);
$modules = [];
foreach ($this->modules as $id => $class) {
// Skip core modules
if (!isset($options['includeCoreModules']) || $options['includeCoreModules'] === false) {
if (in_array($class, $this->coreModules)) {
continue;
}
if (!$options['includeCoreModules'] && in_array($class, $this->coreModules)) {
// Skip core modules
continue;
}
if (isset($options['enabled']) && $options['enabled'] === true) {
if (!in_array($class, $this->coreModules) && !in_array($id, $this->enabledModules)) {
continue;
}
if ($options['enabled'] && !in_array($class, $this->coreModules) && !in_array($id, $this->enabledModules)) {
// Skip disabled modules
continue;
}
if (isset($options['returnClass']) && $options['returnClass']) {
if ($options['returnClass']) {
$modules[$id] = $class;
} else {
$module = $this->getModule($id);
@ -270,6 +278,70 @@ class ModuleManager extends Component
return $modules;
}
/**
* Filter modules by keyword and by additional filters from module event
*
* @param Module[] $modules
* @param array $filters
* @return Module[]
*/
public function filterModules(array $modules, $filters = []): array
{
$filters = array_merge([
'keyword' => null,
], $filters);
$modules = $this->filterModulesByKeyword($modules, $filters['keyword']);
$modulesEvent = new ModulesEvent(['modules' => $modules]);
$this->trigger(static::EVENT_AFTER_FILTER_MODULES, $modulesEvent);
return $modulesEvent->modules;
}
/**
* Filter modules by keyword
*
* @param Module[] $modules
* @param null|string $keyword
* @return Module[]
*/
public function filterModulesByKeyword(array $modules, $keyword = null): array
{
if ($keyword === null) {
$keyword = Yii::$app->request->get('keyword', '');
}
if (!is_scalar($keyword) || $keyword === '') {
return $modules;
}
foreach ($modules as $id => $module) {
/* @var Module $module */
$searchFields = [$id];
if (isset($module->name)) {
$searchFields[] = $module->name;
}
if (isset($module->description)) {
$searchFields[] = $module->description;
}
$keywordFound = false;
foreach ($searchFields as $searchField) {
if (stripos($searchField, $keyword) !== false) {
$keywordFound = true;
continue;
}
}
if (!$keywordFound) {
unset($modules[$id]);
}
}
return $modules;
}
/**
* Returns all enabled modules and supportes further options as [[getModules()]].
*
@ -359,8 +431,12 @@ class ModuleManager extends Component
}
// Check is in dynamic/marketplace module folder
if (strpos($module->getBasePath(), Yii::getAlias(Yii::$app->getModule('marketplace')->modulesPath)) !== false) {
return true;
/** @var ModuleMarketplace $marketplaceModule */
$marketplaceModule = Yii::$app->getModule('marketplace');
if ($marketplaceModule !== null) {
if (strpos($module->getBasePath(), Yii::getAlias($marketplaceModule->modulesPath)) !== false) {
return true;
}
}
return false;

View File

@ -0,0 +1,136 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\components;
use humhub\modules\marketplace\models\Licence;
use humhub\modules\marketplace\models\Module as ModelModule;
use humhub\modules\marketplace\Module as MarketplaceModule;
use Yii;
use yii\base\Component;
/**
* Online Module class for load module date from marketplace
*
* @property-read bool $isInstalled
* @property-read bool $isProOnly
* @property-read bool $isFeatured
* @property-read bool $isThirdParty
* @property-read bool $isPartner
* @property-read bool $isDeprecated
* @property-read array $categories
*
* @author Lucas Bartholemy <lucas@bartholemy.com>
* @since 1.11
*/
class OnlineModule extends Component
{
/**
* @var Module
*/
public $module;
/**
* @var array the cached info loaded from online
*/
private $_onlineInfo = null;
/**
* Get online info of the Module
*
* @param string|null $field Null - to return all fields, String - to return a value of the requested field:
* - id
* - name
* - description
* - useCases
* - featured
* - showDisclaimer
* - isThirdParty
* - isCommunity
* - isPartner
* - isDeprecated
* - latestVersion
* - moduleImageUrl
* - marketplaceUrl
* - latestCompatibleVersion
* - purchased
* - price_eur
* - price_request_quote
* - checkoutUrl
* - professional_only
* - categories
* @return array|null|string
*/
public function info(?string $field = null)
{
if ($this->_onlineInfo === null) {
/* @var MarketplaceModule $marketplaceModule */
$marketplaceModule = Yii::$app->getModule('marketplace');
if (!($marketplaceModule instanceof MarketplaceModule && $marketplaceModule->enabled)) {
return null;
}
if ($this->module instanceof ModelModule) {
$this->_onlineInfo = (array)$this->module;
} else {
$onlineModules = $marketplaceModule->onlineModuleManager->getModules();
$this->_onlineInfo = isset($onlineModules[$this->module->id]) ? $onlineModules[$this->module->id] : [];
}
}
if ($field === null) {
return $this->_onlineInfo;
}
return $this->_onlineInfo[$field] ?? null;
}
public function getIsInstalled(): bool
{
return Yii::$app->moduleManager->hasModule($this->module->id);
}
public function isProOnly(): bool
{
if (empty($this->info('professional_only'))) {
return false;
}
return true;
}
public function getIsProOnly(): bool
{
return $this->isProOnly();
}
public function getCategories(): array
{
$onlineInfo = $this->info();
return $onlineInfo['categories'] ?? [];
}
public function getIsFeatured(): bool
{
return (bool) $this->info('featured');
}
public function getIsThirdParty(): bool
{
return (bool) $this->info('isThirdParty');
}
public function getIsPartner(): bool
{
return (bool) $this->info('isPartner');
}
public function getIsDeprecated(): bool
{
return (bool) $this->info('isDeprecated');
}
}

View File

@ -36,7 +36,7 @@ use humhub\modules\content\interfaces\ContentOwner;
* @since 1.1
* @author buddha
*/
abstract class SocialActivity extends BaseObject implements rendering\Viewable, \Serializable
abstract class SocialActivity extends BaseObject implements rendering\Viewable
{
/**
@ -480,12 +480,12 @@ abstract class SocialActivity extends BaseObject implements rendering\Viewable,
/**
* Serializes the $source and $originator fields.
*
* @return string
* @return array
* @link http://php.net/manual/en/function.serialize.php
* @since 1.2
* @see ActiveRecord::serialize() for the serialization of your $source
*/
public function serialize()
public function __serialize(): array
{
$sourceClass = null;
$sourcePk = null;
@ -497,24 +497,23 @@ abstract class SocialActivity extends BaseObject implements rendering\Viewable,
$originatorId = ($this->originator != null) ? $this->originator->id : null;
return serialize([
return [
'sourceClass' => $sourceClass,
'sourcePk' => $sourcePk,
'originator_id' => $originatorId
]);
];
}
/**
* Unserializes the given string, calls the init() function and sets the $source and $originator fields (and $record indirectyl).
*
* @param string $serialized
* @param array $serialized
* @link http://php.net/manual/en/function.unserialize.php
* @see ActiveRecord::unserialize() for the serialization of your $source
*/
public function unserialize($serialized)
public function __unserialize($unserializedArr)
{
$this->init();
$unserializedArr = unserialize($serialized);
if (isset($unserializedArr['originator_id'])) {
$user = User::findOne(['id' => $unserializedArr['originator_id']]);

View File

@ -12,6 +12,7 @@ use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\data\ActiveDataProvider;
use yii\data\BaseDataProvider;
use yii\di\Instance;
use yii\i18n\Formatter;
@ -136,7 +137,7 @@ class SpreadsheetExport extends Component
]);
}
if ($this->dataProvider instanceof ActiveDataProvider) {
if ($this->dataProvider instanceof BaseDataProvider) {
$this->dataProvider->setPagination(false);
}
}

View File

@ -51,7 +51,7 @@ class PhpMessageSource extends PhpMessageSourceYii
$messages = $this->loadMessagesFromFile($messageFile);
$fallbackLanguage = substr($language, 0, 2);
$fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
$fallbackSourceLanguage = is_string($this->sourceLanguage) ? substr($this->sourceLanguage, 0, 2) : null;
if ($language !== $fallbackLanguage) {
$messages = $this->loadFallbackMessages($category, $fallbackLanguage, $messages, $messageFile);
@ -76,11 +76,12 @@ class PhpMessageSource extends PhpMessageSourceYii
{
$fallbackMessageFile = $this->getMessageFilePath($category, $fallbackLanguage);
$fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile);
$fallbackSourceLanguage = is_string($this->sourceLanguage) ? substr($this->sourceLanguage, 0, 2) : null;
if (
$messages === null && $fallbackMessages === null
&& $fallbackLanguage !== $this->sourceLanguage
&& $fallbackLanguage !== substr($this->sourceLanguage, 0, 2)
&& $fallbackLanguage !== $fallbackSourceLanguage
) {
// modification warning --> debug
Yii::debug("The message file for category '$category' does not exist: $originalMessageFile "

View File

@ -2,7 +2,7 @@
/**
* This file is generated by the "yii asset" command.
* DO NOT MODIFY THIS FILE DIRECTLY.
* @version 2021-09-16 07:53:30
* @version 2021-12-22 09:17:29
*/
return [
'app' => [
@ -393,6 +393,14 @@ return [
'defer',
],
],
'humhub\\assets\\JqueryCookieAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'defer',
],
],
'humhub\\modules\\user\\assets\\UserAsset' => [
'sourcePath' => null,
'js' => [],

View File

@ -12,6 +12,7 @@ namespace humhub\controllers;
use humhub\components\Controller;
use humhub\models\UrlOembed;
use Yii;
use yii\web\HttpException;
/**
* @since 1.3
@ -44,4 +45,33 @@ class OembedController extends Controller
return $this->asJson($result);
}
/**
* Display the hidden embedded content
*/
public function actionDisplay()
{
$this->forcePostRequest();
$url = Yii::$app->request->post('url');
if (empty($url)) {
throw new HttpException(400, 'URL is not provided!');
}
$urlData = parse_url($url);
if (!isset($urlData['host'])) {
throw new HttpException(400, 'Wrong URL!');
}
if (Yii::$app->request->post('alwaysShow', false)) {
UrlOembed::saveAllowedDomain($urlData['host']);
}
$urlOembed = UrlOembed::findExistingOembed($url);
return $this->asJson([
'success' => true,
'content' => $urlOembed ? $urlOembed->preview : UrlOembed::loadUrl($url)
]);
}
}

View File

@ -40,9 +40,9 @@ class OembedFetchEvent extends Event
private function getProviderUrl()
{
foreach ($this->providers as $url => $endpoint) {
if (strpos($this->url, $url) !== false) {
return str_replace("%url%", urlencode($this->url), $endpoint);
foreach ($this->providers as $providerName => $provider) {
if (preg_match($provider['pattern'], $this->url)) {
return str_replace("%url%", urlencode($this->url), $provider['endpoint']);
}
}
return '';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,97 @@
<?php
use yii\db\Migration;
use yii\helpers\Json;
/**
* Class m220121_193617_oembed_setting_update
*/
class m220121_193617_oembed_setting_update extends Migration
{
/**
* {@inheritdoc}
*/
public function up()
{
$oembedProviders = [
'Facebook Video' => [
'pattern' => '/facebook\.com\/(.*)(video)/',
'endpoint' => 'https://graph.facebook.com/v12.0/oembed_video?url=%url%&access_token='
],
'Facebook Post' => [
'pattern' => '/facebook\.com\/(.*)(post|activity|photo|permalink|media|question|note)/',
'endpoint' => 'https://graph.facebook.com/v12.0/oembed_post?url=%url%&access_token='
],
'Facebook Page' => [
'pattern' => '/^(https\:\/\/)*(www\.)*facebook\.com\/((?!video|post|activity|photo|permalink|media|question|note).)*$/',
'endpoint' => 'https://graph.facebook.com/v12.0/oembed_post?url=%url%&access_token='
],
'Instagram' => [
'pattern' => '/instagram\.com/',
'endpoint' => 'https://graph.facebook.com/v12.0/instagram_oembed?url=%url%&access_token='
],
'Twitter' => [
'pattern' => '/twitter\.com/',
'endpoint' => 'https://publish.twitter.com/oembed?url=%url%&maxwidth=450'
],
'YouTube' => [
'pattern' => '/youtube\.com|youtu.be/',
'endpoint' => 'https://www.youtube.com/oembed?scheme=https&url=%url%&format=json&maxwidth=450'
],
'Soundcloud' => [
'pattern' => '/soundcloud\.com/',
'endpoint' => 'https://soundcloud.com/oembed?url=%url%&format=json&maxwidth=450'
],
'Vimeo' => [
'pattern' => '/vimeo\.com/',
'endpoint' => 'https://vimeo.com/api/oembed.json?scheme=https&url=%url%&format=json&maxwidth=450'
],
'SlideShare' => [
'pattern' => '/slideshare\.net/',
'endpoint' => 'https://www.slideshare.net/api/oembed/2?url=%url%&format=json&maxwidth=450'
]
];
foreach (\humhub\models\UrlOembed::getProviders() as $providerUrl => $providerEndpoint)
{
$providerExists = false;
foreach ($oembedProviders as $provider) {
if(preg_match($provider['pattern'], $providerUrl)) {
$providerExists = true;
}
}
if(!$providerExists) {
$oembedProviders[$providerUrl] = [
'pattern' => '/' . str_replace('.', '\.', $providerUrl) . '/',
'endpoint' => $providerEndpoint
];
}
}
$this->update('setting', ['value' => Json::encode($oembedProviders)], ['name' => 'oembedProviders', 'module_id' => 'base']);
Yii::$app->settings->set('oembedProviders', Json::encode($oembedProviders));
}
/**
* {@inheritdoc}
*/
public function down()
{
$oembedProvidersJson = Json::encode([
'twitter.com' => 'https://publish.twitter.com/oembed?url=%url%&maxwidth=450',
'instagram.com' => 'https://graph.facebook.com/v12.0/instagram_oembed?url=%url%&access_token=',
'vimeo.com' => 'https://vimeo.com/api/oembed.json?scheme=https&url=%url%&format=json&maxwidth=450',
'youtube.com' => 'https://www.youtube.com/oembed?scheme=https&url=%url%&format=json&maxwidth=450',
'youtu.be' => 'https://www.youtube.com/oembed?scheme=https&url=%url%&format=json&maxwidth=450',
'soundcloud.com' => 'https://soundcloud.com/oembed?url=%url%&format=json&maxwidth=450',
'slideshare.net' => 'https://www.slideshare.net/api/oembed/2?url=%url%&format=json&maxwidth=450',
]);
$this->update('setting', ['value' => $oembedProvidersJson], ['name' => 'oembedProviders', 'module_id' => 'base']);
Yii::$app->settings->set('oembedProviders', $oembedProvidersJson);
}
}

View File

@ -8,7 +8,10 @@
namespace humhub\models;
use yii\base\InvalidArgumentException;
use humhub\modules\admin\models\forms\OEmbedSettingsForm;
use humhub\modules\ui\icon\widgets\Icon;
use humhub\modules\user\models\User;
use humhub\widgets\Button;
use humhub\events\OembedFetchEvent;
use humhub\libs\RestrictedCallException;
use humhub\libs\UrlOembedClient;
@ -114,9 +117,9 @@ class UrlOembed extends ActiveRecord
*/
public function getProviderUrl()
{
foreach (static::getProviders() as $providerBaseUrl => $providerAPI) {
if (strpos($this->url, $providerBaseUrl) !== false) {
return str_replace("%url%", urlencode($this->url), $providerAPI);
foreach (static::getProviders() as $provider) {
if (preg_match($provider['pattern'], $this->url)) {
return str_replace("%url%", urlencode($this->url), $provider['endpoint']);
}
}
return null;
@ -154,10 +157,15 @@ class UrlOembed extends ActiveRecord
$url = trim($url);
if (static::hasOEmbedSupport($url)) {
$urlOembed = static::findExistingOembed($url);
$result = $urlOembed ? $urlOembed->preview : self::loadUrl($url);
if (!self::isAllowedDomain($url)) {
$result = self::confirmationContent($url);
} else {
$urlOembed = static::findExistingOembed($url);
$result = $urlOembed ? $urlOembed->preview : self::loadUrl($url);
}
if (!empty($result)) {
return trim(preg_replace('/\s+/', ' ', $result));
}
}
@ -208,7 +216,7 @@ class UrlOembed extends ActiveRecord
* @return UrlOembed|null
* @throws RestrictedCallException
*/
protected static function findExistingOembed($url)
public static function findExistingOembed($url)
{
if (array_key_exists($url, static::$cache)) {
return static::$cache[$url];
@ -292,6 +300,35 @@ class UrlOembed extends ActiveRecord
return null;
}
/**
* Replace the embedded content with confirmation before display it
*
* @param string $url
* @return string
*/
protected static function confirmationContent(string $url): string
{
$urlData = parse_url($url);
$urlPrefix = $urlData['host'] ?? $url;
$html = Html::tag('strong', Yii::t('base', 'Allow content from external source')) .
Html::tag('br') .
Yii::t('base', 'Do you want to enable content from \'{urlPrefix}\'?', ['urlPrefix' => Html::tag('strong', $urlPrefix)]) .
Html::tag('br') .
Html::tag('label', '<input type="checkbox">' . Yii::t('base', 'Always allow content from this provider!')) .
Html::tag('br') .
Button::info(Yii::t('base', 'Confirm'))->action('oembed.display')->sm();
$html = Icon::get('info-circle') .
Html::tag('div', $html) .
Html::tag('div', '', ['class' => 'clearfix']);
return Html::tag('div', $html, [
'data-url' => $url,
'class' => 'oembed_confirmation',
]);
}
/**
* Validates the given $data array.
*
@ -323,9 +360,9 @@ class UrlOembed extends ActiveRecord
*/
public static function getProviderByUrl($url)
{
foreach (static::getProviders() as $providerBaseUrl => $providerAPI) {
if (strpos($url, $providerBaseUrl) !== false) {
return $providerBaseUrl;
foreach (static::getProviders() as $provider) {
if (preg_match($provider['pattern'], $url)) {
return $provider['endpoint'];
}
}
@ -399,4 +436,77 @@ class UrlOembed extends ActiveRecord
Yii::$app->settings->set('oembedProviders', Json::encode($providers));
}
/**
* Check the domain is always allowed to display for current User
*
* @param string $url Domain or full URL
* @return array
*/
public static function isAllowedDomain(string $url): bool
{
$oembedSettings = new OEmbedSettingsForm();
if (!$oembedSettings->requestConfirmation) {
return true;
}
if (Yii::$app->user->isGuest) {
return true;
}
if (preg_match('#^(https?:)?//#i',$url)) {
$url = parse_url($url);
if (!isset($url['host'])) {
return false;
}
$url = $url['host'];
}
return array_search($url, self::getAllowedDomains()) !== false;
}
/**
* Get domains always allowed to be displayed for current User
*
* @return array
*/
public static function getAllowedDomains(): array
{
if (Yii::$app->user->isGuest) {
return [];
}
/* @var User $user */
$user = Yii::$app->user->getIdentity();
$allowedUrls = $user->settings->get('allowedOembedUrls');
return empty($allowedUrls) ? [] : explode(',', $allowedUrls);
}
/**
* Add a new allowed domain for oembed URLs to the current User settings
*
* @param string $domain
* @return bool
*/
public static function saveAllowedDomain(string $domain): bool
{
if (Yii::$app->user->isGuest) {
return false;
}
if (self::isAllowedDomain($domain)) {
return true;
}
$allowedUrls = self::getAllowedDomains();
$allowedUrls[] = $domain;
/* @var User $user */
$user = Yii::$app->user->getIdentity();
$user->settings->set('allowedOembedUrls', implode(',', $allowedUrls));
return true;
}
}

View File

@ -38,7 +38,7 @@ class Module extends \humhub\components\Module
/**
* Returns all configurable Activitiess
* Returns all configurable Activities
*
* @since 1.2
* @return ConfigurableActivityInterface[] a list of configurable activities

View File

@ -46,7 +46,6 @@ class ActivityStreamCest
$I->see('Peter Tester created a new post "Activity test post!"', '#activityStream');
$I->click('.activity-entry');
$I->waitForText('Back to stream', null, '#wallStream');
$I->waitForText('Activity test post!', null,'.wall-entry');
$I->wantToTest('deleting my post will remove the activity');

View File

@ -0,0 +1,26 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\assets;
use humhub\components\assets\AssetBundle;
class ModuleAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@admin/resources';
/**
* @inheritdoc
*/
public $css = [
'css/modules.css'
];
}

View File

@ -11,10 +11,8 @@ namespace humhub\modules\admin\controllers;
use humhub\components\Module;
use humhub\modules\admin\components\Controller;
use humhub\modules\admin\models\forms\ModuleSetAsDefaultForm;
use humhub\modules\admin\permissions\ManageModules;
use humhub\modules\content\components\ContentContainerModule;
use humhub\modules\content\components\ContentContainerModuleManager;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use Yii;
use yii\base\Exception;
use yii\web\HttpException;
@ -27,6 +25,11 @@ use yii\web\HttpException;
class ModuleController extends Controller
{
/**
* @inheritdoc
*/
public $subLayout = '@admin/views/layouts/module';
/**
* @inheritdoc
*/
@ -38,7 +41,6 @@ class ModuleController extends Controller
public function init()
{
$this->appendPageTitle(Yii::t('AdminModule.base', 'Modules'));
$this->subLayout = '@admin/views/layouts/module';
return parent::init();
}
@ -49,7 +51,7 @@ class ModuleController extends Controller
public function getAccessRules()
{
return [
['permissions' => \humhub\modules\admin\permissions\ManageModules::class]
['permissions' => ManageModules::class]
];
}
@ -59,53 +61,9 @@ class ModuleController extends Controller
return $this->redirect(['/admin/module/list']);
}
public function actionList()
{
$installedModules = Yii::$app->moduleManager->getModules();
return $this->render('list', [
'installedModules' => $installedModules,
'deprecatedModuleIds' => $this->getDeprecatedModules(),
'marketplaceUrls' => $this->getMarketplaceUrls()
]);
}
private function getMarketplaceUrls()
{
$marketplaceUrls = [];
if (Yii::$app->hasModule('marketplace')) {
try {
foreach (Yii::$app->getModule('marketplace')->onlineModuleManager->getModules() as $id => $module) {
if (!empty($module['marketplaceUrl'])) {
$marketplaceUrls[$id] = $module['marketplaceUrl'];
}
}
} catch (\Exception $ex) {
}
}
return $marketplaceUrls;
}
private function getDeprecatedModules()
{
$deprecatedModuleIds = [];
if (Yii::$app->hasModule('marketplace')) {
try {
foreach (Yii::$app->getModule('marketplace')->onlineModuleManager->getModules() as $id => $module) {
if (!empty($module['isDeprecated'])) {
$deprecatedModuleIds[] = $id;
}
}
} catch (\Exception $ex) {
}
}
return $deprecatedModuleIds;
return $this->render('list');
}
/**
@ -244,13 +202,8 @@ class ModuleController extends Controller
throw new HttpException(500, 'Invalid module type!');
}
$model = new ModuleSetAsDefaultForm();
$model->spaceDefaultState = ContentContainerModuleManager::getDefaultState(Space::class, $moduleId);
$model->userDefaultState = ContentContainerModuleManager::getDefaultState(User::class, $moduleId);
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
ContentContainerModuleManager::setDefaultState(User::class, $moduleId, $model->userDefaultState);
ContentContainerModuleManager::setDefaultState(Space::class, $moduleId, $model->spaceDefaultState);
$model = (new ModuleSetAsDefaultForm())->setModule($moduleId);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->renderModalClose();
}

View File

@ -16,6 +16,7 @@ use humhub\modules\admin\models\forms\FileSettingsForm;
use humhub\modules\admin\models\forms\LogsSettingsForm;
use humhub\modules\admin\models\forms\MailingSettingsForm;
use humhub\modules\admin\models\forms\OEmbedProviderForm;
use humhub\modules\admin\models\forms\OEmbedSettingsForm;
use humhub\modules\admin\models\forms\ProxySettingsForm;
use humhub\modules\admin\models\forms\StatisticSettingsForm;
use humhub\modules\admin\permissions\ManageSettings;
@ -27,6 +28,7 @@ use humhub\models\UrlOembed;
use humhub\modules\admin\components\Controller;
use humhub\modules\admin\models\Log;
use humhub\modules\notification\models\forms\NotificationSettings;
use yii\base\BaseObject;
/**
* SettingController
@ -301,9 +303,17 @@ class SettingController extends Controller
public function actionOembed()
{
$providers = UrlOembed::getProviders();
return $this->render('oembed',
[
'providers' => $providers
$settings = new OEmbedSettingsForm();
if ($settings->load(Yii::$app->request->post()) && $settings->save()) {
$this->view->saved();
return $this->redirect(['/admin/setting/oembed']);
}
return $this->render('oembed', [
'providers' => $providers,
'settings' => $settings,
]);
}
@ -349,19 +359,23 @@ class SettingController extends Controller
{
$form = new OEmbedProviderForm;
$prefix = Yii::$app->request->get('prefix');
$name = Yii::$app->request->get('name');
$providers = UrlOembed::getProviders();
if (isset($providers[$prefix])) {
$form->prefix = $prefix;
$form->endpoint = $providers[$prefix];
if (isset($providers[$name])) {
$form->name = $name;
$form->endpoint = $providers[$name]['endpoint'];
$form->pattern = $providers[$name]['pattern'];
}
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
if ($prefix && isset($providers[$prefix])) {
unset($providers[$prefix]);
if ($name && isset($providers[$name])) {
unset($providers[$name]);
}
$providers[$form->prefix] = $form->endpoint;
$providers[$form->name] = [
'endpoint' => $form->endpoint,
'pattern' => $form->pattern
];
UrlOembed::setProviders($providers);
return $this->redirect(
@ -373,7 +387,7 @@ class SettingController extends Controller
return $this->render('oembed_edit',
[
'model' => $form,
'prefix' => $prefix
'name' => $name
]);
}
@ -383,11 +397,11 @@ class SettingController extends Controller
public function actionOembedDelete()
{
$this->forcePostRequest();
$prefix = Yii::$app->request->get('prefix');
$name = Yii::$app->request->get('name');
$providers = UrlOembed::getProviders();
if (isset($providers[$prefix])) {
unset($providers[$prefix]);
if (isset($providers[$name])) {
unset($providers[$name]);
UrlOembed::setProviders($providers);
}
return $this->redirect([

View File

@ -0,0 +1,25 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\events;
use humhub\components\Module;
use yii\base\Event;
/**
* This event is used when modules is listed and filtered
*
* @author luke
* @since 1.11
*/
class ModulesEvent extends Event
{
/**
* @var Module[]|array
*/
public $modules;
}

View File

@ -2,16 +2,28 @@
namespace humhub\modules\admin\models\forms;
use humhub\modules\content\components\ContentContainerModuleManager;
use humhub\modules\content\models\ContentContainerModuleState;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use Yii;
use yii\base\Model;
/**
* GroupForm is used to modify group settings
*
* @package humhub.modules_core.admin.forms
* @since 0.5
*/
class ModuleSetAsDefaultForm extends \yii\base\Model
class ModuleSetAsDefaultForm extends Model
{
/** @var string */
protected $moduleId;
/** @var int | string */
public $spaceDefaultState;
/** @var int | string */
public $userDefaultState;
/**
@ -22,8 +34,58 @@ class ModuleSetAsDefaultForm extends \yii\base\Model
public function rules()
{
return [
[['userDefaultState', 'spaceDefaultState'], 'integer'],
[['userDefaultState', 'spaceDefaultState'], 'in', 'range' => ContentContainerModuleState::getStates()],
];
}
/**
* @inheritDoc
*/
public function attributeLabels()
{
return [
'spaceDefaultState' => Yii::t('AdminModule.modules', 'Space default state'),
'userDefaultState' => Yii::t('AdminModule.modules', 'User default state')
];
}
/**
* @return array
*/
public function getStatesList()
{
return ContentContainerModuleState::getStates(true);
}
/**
* @param $id
* @return $this
*/
public function setModule($id): self
{
if ($this->moduleId == $id) {
return $this;
}
$this->moduleId = $id;
$this->spaceDefaultState = ContentContainerModuleManager::getDefaultState(Space::class, $id) ?? ContentContainerModuleState::STATE_DISABLED;
$this->userDefaultState = ContentContainerModuleManager::getDefaultState(User::class, $id) ?? ContentContainerModuleState::STATE_DISABLED;
return $this;
}
/**
* @return bool
*/
public function save($validate = true)
{
if ($validate && !$this->validate()) {
return false;
}
ContentContainerModuleManager::setDefaultState(User::class, $this->moduleId, $this->userDefaultState);
ContentContainerModuleManager::setDefaultState(Space::class, $this->moduleId, $this->spaceDefaultState);
return true;
}
}

View File

@ -11,8 +11,10 @@ use Yii;
class OEmbedProviderForm extends \yii\base\Model
{
public $prefix;
public $name;
public $endpoint;
public $pattern;
public $access_token;
/**
* Declares the validation rules.
@ -20,9 +22,13 @@ class OEmbedProviderForm extends \yii\base\Model
public function rules()
{
return [
['prefix', 'safe'],
[['prefix', 'endpoint'], 'required'],
[['name', 'pattern', 'endpoint'], 'string'],
[['name', 'pattern', 'endpoint'], 'required'],
['endpoint', 'url'],
['access_token', 'required', 'when' => function($model) {
parse_str($model->endpoint, $query);
return isset($query['access_token']);
}]
];
}
@ -34,8 +40,10 @@ class OEmbedProviderForm extends \yii\base\Model
public function attributeLabels()
{
return [
'prefix' => Yii::t('AdminModule.settings', 'Url Prefix'),
'name' => Yii::t('AdminModule.settings', 'Provider Name'),
'endpoint' => Yii::t('AdminModule.settings', 'Endpoint Url'),
'pattern' => Yii::t('AdminModule.settings', 'Url Pattern'),
'access_token' => Yii::t('AdminModule.settings', 'Access Token'),
];
}

View File

@ -0,0 +1,68 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\models\forms;
use Yii;
use yii\base\Model;
/**
* OEmbed settings form
*
* @package humhub.modules_core.admin.forms
* @since 1.11
*/
class OEmbedSettingsForm extends Model
{
/**
* @var bool
*/
public $requestConfirmation;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
$this->requestConfirmation = (bool) Yii::$app->settings->get('oembed.requestConfirmation', true);
}
/**
* @inheritdoc
*/
public function rules()
{
return [
['requestConfirmation', 'boolean'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'requestConfirmation' => Yii::t('AdminModule.settings', 'Embedded content requires the user\'s consent to be loaded'),
];
}
public function save(): bool
{
if (!$this->validate()) {
return false;
}
Yii::$app->settings->set('oembed.requestConfirmation', $this->requestConfirmation);
return true;
}
}

View File

@ -0,0 +1 @@
.container-modules .modules-type{font-size:16px;font-weight:bold;color:#000;margin:70px 0 40px}.container-modules .row.cards{margin-top:40px}.container-modules .card-module .card-panel{background:none;overflow:hidden}.container-modules .card-module .card-panel>div:not(.card-status){background-color:#ffffff}.container-modules .card-module .card-header{position:relative}.container-modules .card-module .card-body{padding-top:8px;font-size:13px;color:#6C787E}.container-modules .card-module .card-body>div{padding-bottom:8px}.container-modules .card-module .card-body>div:last-child{padding-bottom:0}.container-modules .card-module .card-title{color:#000}.container-modules .card-module .card-footer{padding-bottom:14px}.container-modules .card-module .card-footer a.btn{float:none}.container-modules .card-module .card-footer.text-right a.btn{margin-left:8px;margin-right:0}.container-modules .card-module .card-footer.text-right a.btn:first-child{margin-left:0}.container-modules .module-settings-link{float:right;color:#02A1B1;font-size:22px;line-height:20px;margin:2px}.container-content-modules{width:100%;padding:0 18px 5px 5px}.container-content-modules h4{font-size:16px;color:#000}.container-content-modules .card{width:100%;padding-right:3px}.container-content-modules .card .card-panel{margin-top:3px}@media (min-width:460px){.container-content-modules .card{width:50%}}@media (min-width:656px){.container-content-modules .card{width:33.33333333%}}@media (min-width:768px){.container-content-modules{padding:0 12px 5px 0}}@media (min-width:1200px){.container-content-modules .card{width:25%}}@media (min-width:460px){.container-content-modules.container-content-modules-col-3 .card{width:50%}}@media (min-width:656px){.container-content-modules.container-content-modules-col-3 .card{width:33.33333333%}}.container-create-space-modules.container-cards{width:100%;padding:0}.container-create-space-modules.container-cards .row.cards{margin-top:0}.container-create-space-modules.container-cards .card .card-panel>div{background:#F5F5F5}/*# sourceMappingURL=modules.css.map */

View File

@ -0,0 +1 @@
{"version":3,"sources":["modules.less"],"names":[],"mappings":"AAAA,kBACI,eACI,cAAA,CACA,gBAAA,CACA,UAAA,CACA,mBALR,kBAOI,KAAI,OACA,gBARR,kBAUI,aACI,aACI,eAAA,CACA,gBAbZ,kBAUI,aACI,YAGI,IAAK,IAAI,eACL,yBAfhB,kBAUI,aAQI,cACI,kBAnBZ,kBAUI,aAWI,YACI,eAAA,CACA,cAAA,CACA,cAxBZ,kBAUI,aAWI,WAII,KACI,mBACA,kBAjBZ,aAWI,WAII,IAEK,YACG,iBA5BpB,kBAUI,aAsBI,aACI,WAjCZ,kBAUI,aAyBI,cACI,oBApCZ,kBAUI,aAyBI,aAEI,EAAC,KACG,WAEJ,kBA9BR,aAyBI,aAKK,WACG,EAAC,KACG,eAAA,CACA,eACA,kBAlChB,aAyBI,aAKK,WACG,EAAC,IAGI,aACG,cA7CxB,kBAmDI,uBACI,WAAA,CACA,aAAA,CACA,cAAA,CACA,gBAAA,CACA,WAIR,2BACI,UAAA,CACA,uBAFJ,0BAGI,IACI,cAAA,CACA,WALR,0BAOI,OACI,UAAA,CACA,kBATR,0BAOI,MAGI,aACI,eAGR,QAA0B,iBAA1B,0BACI,OACI,WAGR,QAA0B,iBAA1B,0BACI,OACI,oBAGR,QAA0B,iBAA1B,2BACI,sBAEJ,QAA2B,kBAA3B,0BACI,OACI,WAIJ,QAA0B,iBAA1B,0BADH,gCAEO,OACI,WAGR,QAA0B,iBAA1B,0BANH,gCAOO,OACI,oBAMhB,+BAA+B,iBAC3B,UAAA,CACA,UAFJ,+BAA+B,gBAG3B,KAAI,OACA,aAJR,+BAA+B,gBAM3B,MACI,YAAY,KACR","file":"modules.css"}

View File

@ -0,0 +1,118 @@
.container-modules {
.modules-type {
font-size: 16px;
font-weight: bold;
color: #000;
margin: 70px 0 40px;
}
.row.cards {
margin-top: 40px;
}
.card-module {
.card-panel {
background: none;
overflow: hidden;
> div:not(.card-status) {
background-color: #ffffff;
}
}
.card-header {
position: relative;
}
.card-body {
padding-top: 8px;
font-size: 13px;
color: #6C787E;
> div {
padding-bottom: 8px;
&:last-child {
padding-bottom: 0;
}
}
}
.card-title {
color: #000;
}
.card-footer {
padding-bottom: 14px;
a.btn {
float: none;
}
&.text-right {
a.btn {
margin-left: 8px;
margin-right: 0;
&:first-child {
margin-left: 0;
}
}
}
}
}
.module-settings-link {
float: right;
color: #02A1B1;
font-size: 22px;
line-height: 20px;
margin: 2px;
}
}
.container-content-modules {
width: 100%;
padding: 0 18px 5px 5px;
h4 {
font-size: 16px;
color: #000;
}
.card {
width: 100%;
padding-right: 3px;
.card-panel {
margin-top: 3px;
}
}
@media (min-width: 460px) {
.card {
width: 50%;
}
}
@media (min-width: 656px) {
.card {
width: 33.33333333%;
}
}
@media (min-width: 768px) {
padding: 0 12px 5px 0;
}
@media (min-width: 1200px) {
.card {
width: 25%;
}
}
&.container-content-modules-col-3 {
@media (min-width: 460px) {
.card {
width: 50%;
}
}
@media (min-width: 656px) {
.card {
width: 33.33333333%;
}
}
}
}
.container-create-space-modules.container-cards {
width: 100%;
padding: 0;
.row.cards {
margin-top: 0;
}
.card {
.card-panel > div {
background: #F5F5F5;
}
}
}

View File

@ -147,7 +147,7 @@ class PermissionCest
public function testCanManageSettings(FunctionalTester $I)
{
$I->wantTo('ensure that see manage groups permission works');
$I->wantTo('ensure that see manage settings permission works');
$I->amUser2();
@ -215,7 +215,7 @@ class PermissionCest
public function testCanManageModules(FunctionalTester $I)
{
$I->wantTo('ensure that see manage groups permission works');
$I->wantTo('ensure that see manage modules permission works');
$I->amUser2();
@ -229,11 +229,7 @@ class PermissionCest
$I->expectTo('not to see permission denied message');
$I->see('Module administration');
$I->dontSee('Users', '#admin-menu');
$I->dontSee('Spaces', '#admin-menu');
$I->see('Modules', '#admin-menu');
$I->dontSee('Settings', '#admin-menu');
$I->dontSee('Information', '#admin-menu');
$I->dontSeeElement('#admin-menu');
$I->amOnPage(['/admin/user']);
$I->see('You are not permitted to access this section.');
@ -262,7 +258,7 @@ class PermissionCest
public function testCanManageSpaces(FunctionalTester $I)
{
$I->wantTo('ensure that see manage groups permission works');
$I->wantTo('ensure that see manage spaces permission works');
$I->amUser2();

View File

@ -28,6 +28,7 @@ $columns = [
['class' => ImageColumn::class],
['class' => DisplayNameColumn::class],
'email',
'originator.username',
];
foreach ($profileFieldsColumns as $profileField) {
$columns[] = [

View File

@ -1,15 +1,18 @@
<?php $this->beginContent('@admin/views/layouts/main.php') ?>
<div class="panel panel-default">
<div class="panel-heading">
<?= Yii::t('AdminModule.base', '<strong>Module </strong> administration'); ?>
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
<div class="help-block">
<?= Yii::t('AdminModule.modules', 'Modules extend the functionality of HumHub. Here you can install and manage modules from the HumHub Marketplace.') ?>
use humhub\modules\ui\view\helpers\ThemeHelper;
/* @var $content string */
?>
<div class="<?php if (ThemeHelper::isFluid()): ?>container-fluid<?php else: ?>container<?php endif; ?> container-cards container-modules">
<div class="row">
<div class="col-lg-12">
<?= $content; ?>
</div>
</div>
<?= \humhub\modules\admin\widgets\ModuleMenu::widget(); ?>
<?= $content; ?>
</div>
<?php $this->endContent(); ?>

View File

@ -50,7 +50,7 @@ use yii\log\Logger;
<h4 class="media-heading">
<span class="label <?= $labelClass; ?>"><?= Html::encode($levelName) ?></span>&nbsp;
<?= date('r', $entry->log_time) ?>&nbsp;
<?= date('r', (int) $entry->log_time) ?>&nbsp;
<span class="pull-right"><?= Html::encode($entry->category) ?></span>
</h4>
<div data-ui-show-more data-collapse-at="150">

View File

@ -1,78 +1,30 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use yii\helpers\Html;
use yii\helpers\Url;
use humhub\assets\CardsAsset;
use humhub\libs\Html;
use humhub\modules\admin\assets\ModuleAsset;
use humhub\modules\admin\widgets\ModuleFilters;
use humhub\modules\admin\widgets\Modules;
use humhub\modules\ui\icon\widgets\Icon;
use humhub\modules\ui\view\components\View;
/* @var $this \humhub\modules\ui\view\components\View */
/* @var $installedModules array */
/* @var $deprecatedModuleIds array */
/* @var $marketplaceUrls array */
/* @var $this View */
ModuleAsset::register($this);
CardsAsset::register($this);
?>
<div class="panel-body">
<?php if (count($installedModules) == 0): ?>
<br>
<div><?= Yii::t('AdminModule.modules', 'No modules installed yet. Install some to enhance the functionality!'); ?></div>
<?php endif; ?>
<?php foreach ($installedModules
as $moduleId => $module) : ?>
<div class="media">
<img class="media-object img-rounded pull-left" data-src="holder.js/64x64" alt="64x64"
style="width: 64px; height: 64px;" src="<?= $module->getImage(); ?>">
<div class="media-body">
<h4 class="media-heading"><?= $module->getName(); ?>
<small>
<?php if (Yii::$app->hasModule($module->id)) : ?>
<span class="label label-info"><?= Yii::t('AdminModule.modules', 'Activated'); ?></span>
<?php endif; ?>
<?php if (in_array($module->id, $deprecatedModuleIds)): ?>
<span class="label label-default" data-toggle="tooltip" data-placement="bottom"
title="<?= Yii::t('AdminModule.modules', 'Not maintained or maintenance is about to be discontinued.'); ?>"><?= Yii::t('AdminModule.modules', 'Legacy'); ?></span>
<?php endif; ?>
</small>
</h4>
<p><?= $module->getDescription(); ?></p>
<div class="module-controls">
<?= Yii::t('AdminModule.modules', 'Version:'); ?> <?= $module->getVersion(); ?>
<?php if (Yii::$app->hasModule($module->id)) : ?>
<?php if ($module->getConfigUrl() != "") : ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'Configure'), $module->getConfigUrl(), ['style' => 'font-weight:bold']); ?>
<?php endif; ?>
<?php if ($module instanceof \humhub\modules\content\components\ContentContainerModule): ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'Set as default'), Url::to(['/admin/module/set-as-default', 'moduleId' => $moduleId]), ['data-target' => '#globalModal']); ?>
<?php endif; ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'Disable'), Url::to(['/admin/module/disable', 'moduleId' => $moduleId]), ['data-method' => 'POST', 'data-confirm' => Yii::t('AdminModule.modules', 'Are you sure? *ALL* module data will be lost!')]); ?>
<?php else: ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'Enable'), Url::to(['/admin/module/enable', 'moduleId' => $moduleId]), ['data-method' => 'POST', 'style' => 'font-weight:bold', 'data-loader' => "modal", 'data-message' => Yii::t('AdminModule.modules', 'Enable module...')]); ?>
<?php endif; ?>
<?php if (Yii::$app->moduleManager->canRemoveModule($moduleId)): ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'Uninstall'), Url::to(['/admin/module/remove', 'moduleId' => $moduleId]), ['data-method' => 'POST', 'data-confirm' => Yii::t('AdminModule.modules', 'Are you sure? *ALL* module related data and files will be lost!')]); ?>
<?php endif; ?>
<?php if (isset($marketplaceUrls[$moduleId])): ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'More info') . '&nbsp;<i class="fa fa-external-link" aria-hidden="true"></i>'
, $marketplaceUrls[$moduleId],
['rel' => 'noopener', 'target' => '_blank']
); ?>
<?php else: ?>
&middot; <?= Html::a(Yii::t('AdminModule.modules', 'More info'), Url::to(['/admin/module/info', 'moduleId' => $moduleId]), ['data-target' => '#globalModal']); ?>
<?php endif; ?>
</div>
</div>
</div>
<hr/>
<?php endforeach; ?>
<div class="panel panel-default">
<div class="panel-heading">
<?= Yii::t('AdminModule.base', '<strong>Module </strong> Administration'); ?>
</div>
<div class="panel-body">
<?= ModuleFilters::widget(); ?>
</div>
</div>
<?= Modules::widget() ?>

View File

@ -1,125 +1,53 @@
<?php
use humhub\modules\space\models\Space;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\modules\user\models\User;
use humhub\widgets\AjaxButton;
use humhub\widgets\LoaderWidget;
/**
* @var $module \humhub\components\Module
* @var $model \humhub\modules\admin\models\forms\ModuleSetAsDefaultForm
*/
?>
<div class="modal-dialog modal-dialog-normal animated fadeIn">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">
<?= Yii::t('AdminModule.modules', '%moduleName% - Set as default module', ['%moduleName%' => "<strong>" . $module->getName() . "</strong>"]); ?>
</h4>
</div>
<?php $form = humhub\compat\CActiveForm::begin(); ?>
<?php $form = ActiveForm::begin(); ?>
<div class="modal-body">
<p>
<?= Yii::t('AdminModule.modules', 'Here you can choose whether or not a module should be automatically activated on a space or user profile. If the module should be activated, choose "always activated".'); ?>
</p>
<p><?= Yii::t('AdminModule.modules', 'Here you can choose whether or not a module should be automatically activated on a space or user profile. If the module should be activated, choose "always activated".'); ?></p>
<br>
<div class="row">
<?php if ($module->hasContentContainerType(Space::class)) : ?>
<div class="col-md-6">
<label for=""><?= Yii::t('AdminModule.modules', 'Spaces'); ?></label>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'spaceDefaultState', [
'value' => 0,
'uncheckValue' => null,
'id' => 'radioSpaceDeactivated',
'checked' => ($model->spaceDefaultState == 0)]);
?>
<?= Yii::t('AdminModule.modules', 'Deactivated'); ?>
</label>
</div>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'spaceDefaultState', [
'value' => 1,
'uncheckValue' => null,
'id' => 'radioSpaceActivated',
'checked' => ($model->spaceDefaultState == 1)
]);
?>
<?= Yii::t('AdminModule.modules', 'Activated'); ?>
</label>
</div>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'spaceDefaultState', [
'value' => 2,
'uncheckValue' => null,
'id' => 'radioSpaceAlwaysActivated',
'checked' => ($model->spaceDefaultState == 2)
]);
?>
<?= Yii::t('AdminModule.modules', 'Always activated'); ?>
</label>
</div>
<br>
<?= $form->field($model, 'spaceDefaultState')->radioList($model->getStatesList())
->label(Yii::t('AdminModule.modules', 'Spaces')); ?>
</div>
<?php endif; ?>
<?php if ($module->hasContentContainerType(User::class)) : ?>
<div class="col-md-6">
<label for=""><?= Yii::t('AdminModule.modules', 'User Profiles'); ?></label>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'userDefaultState', [
'value' => 0,
'uncheckValue' => null,
'id' => 'radioUserDeactivated',
'checked' => ($model->userDefaultState == 0)
]);
?>
<?= Yii::t('AdminModule.modules', 'Deactivated'); ?>
</label>
</div>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'userDefaultState', [
'value' => 1,
'uncheckValue' => null,
'id' => 'radioUserActivated',
'checked' => ($model->userDefaultState == 1)
]);
?>
<?= Yii::t('AdminModule.modules', 'Activated'); ?>
</label>
</div>
<div class="radio">
<label>
<?=
$form->radioButton($model, 'userDefaultState', [
'value' => 2,
'uncheckValue' => null,
'id' => 'radioUserAlwaysActivated',
'checked' => ($model->userDefaultState == 2)
]);
?>
<?= Yii::t('AdminModule.modules', 'Always activated'); ?>
</label>
</div>
<br>
<?= $form->field($model, 'userDefaultState')->radioList($model->getStatesList())
->label(Yii::t('AdminModule.modules', 'Users')); ?>
</div>
<?php endif; ?>
<br>
</div>
</div>
<div class="modal-footer">
<?=
\humhub\widgets\AjaxButton::widget([
<div class="modal-footer">
<?= AjaxButton::widget([
'label' => Yii::t('AdminModule.modules', 'Save'),
'ajaxOptions' => [
'type' => 'POST',
@ -127,26 +55,17 @@ use humhub\modules\user\models\User;
'success' => new yii\web\JsExpression('function(html){ $("#globalModal").html(html); }'),
'url' => \yii\helpers\Url::to(['/admin/module/set-as-default', 'moduleId' => $module->id]),
],
'htmlOptions' => [
'class' => 'btn btn-primary'
]
]);
?>
'htmlOptions' => ['class' => 'btn btn-primary']
]); ?>
<button type="button" class="btn btn-primary" data-dismiss="modal">
<?= Yii::t('AdminModule.modules', 'Close'); ?>
</button>
<?=
\humhub\widgets\LoaderWidget::widget([
'id' => 'default-loader',
'cssClass' => 'loader-modal hidden'
]);
?>
<?= LoaderWidget::widget(['id' => 'default-loader', 'cssClass' => 'loader-modal hidden']); ?>
</div>
<?php humhub\compat\CActiveForm::end(); ?>
<?php $form::end(); ?>
</div>
</div>

View File

@ -1,7 +1,19 @@
<?php
use humhub\modules\admin\models\forms\OEmbedSettingsForm;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\widgets\Button;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\web\View;
/* @var array $providers */
/* @var OEmbedSettingsForm $settings */
$this->registerJs(<<<JS
$('[data-toggle="tooltip"]').tooltip();
JS, View::POS_READY);
?>
<?php $this->beginContent('@admin/views/setting/_advancedLayout.php') ?>
@ -13,13 +25,43 @@ use yii\helpers\Url;
<h4><?= Yii::t('AdminModule.settings', 'Enabled OEmbed providers'); ?></h4>
<?php if (count($providers) != 0): ?>
<ul>
<?php foreach ($providers as $providerUrl => $providerOEmbedAPI) : ?>
<li><?= Html::a($providerUrl, Url::to(['oembed-edit', 'prefix' => $providerUrl]), ['data-method' => 'POST']); ?></li>
<div id="oembed-providers">
<?php foreach ($providers as $providerName => $provider) : ?>
<div class="oembed-provider-container col-xs-6 col-md-3">
<div class="oembed-provider">
<div class="oembed-provider-name">
<span>
<?= Html::encode($providerName) ?>
</span>
<?php parse_str($provider['endpoint'], $query); ?>
<?php if (isset($query['access_token']) && empty($query['access_token'])): ?>
<span class="label label-danger label-error"
data-toggle="tooltip" data-placement="right"
title="<?= Yii::t('AdminModule.settings', 'Access token is not provided yet.') ?>">
<i class="fa fa-exclamation-circle" aria-hidden="true"></i>
</span>
<?php endif; ?>
</div>
<?= Html::a(Yii::t('base', 'Edit'), Url::to(['oembed-edit', 'name' => $providerName]), ['data-method' => 'POST', 'class' => 'btn btn-xs btn-link']); ?>
</div>
</div>
<?php endforeach; ?>
</ul>
</div>
<?php else: ?>
<p><strong><?= Yii::t('AdminModule.settings', 'Currently no provider active!'); ?></strong></p>
<?php endif; ?>
<hr>
<?php $form = ActiveForm::begin() ?>
<?= $form->field($settings, 'requestConfirmation')->checkbox() ?>
<?= Button::primary(Yii::t('AdminModule.settings', 'Save'))->submit() ?>
<?php ActiveForm::end(); ?>
<?php $this->endContent(); ?>

View File

@ -1,11 +1,72 @@
<?php
use humhub\modules\admin\models\forms\OEmbedProviderForm;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\widgets\Button;
use yii\helpers\Html;
use yii\helpers\Url;
use humhub\modules\ui\form\widgets\ActiveForm;
use yii\web\View;
/* @var $name string */
/** @var OEmbedProviderForm $model */
parse_str($model->endpoint, $query);
$this->registerJs(<<<JS
function initEndpointInputs() {
var url = new URL($('#oembedproviderform-endpoint').val());
var formGroup;
$('#endpoint-parameters').html('');
for (var key of url.searchParams.keys()) {
if (key !== 'url' && key !== 'access_token') {
var value = url.searchParams.get(key);
var label = key[0].toUpperCase() + key.substring(1)
.replace(/_([a-z])/, function (m, w) {
return w.toUpperCase();
}).replace(/-([a-z])/, function (m, w) {
return w.toUpperCase();
}).replace(/([A-Z])/, " $1");
var inputId = 'oembedproviderform-' + key;
formGroup = '<div class="form-group col-xs-12 col-sm-6">' +
'<label for="' + inputId + '" class="control-label" type="text">' + label + '</label>' +
'<input id="' + inputId + '" value="' + (!value.match(/\%\w+\%/) ? value : "") + '" type="text" class="form-control endpoint-param" data-param-name="' + key + '">' +
'</div>';
$('#endpoint-parameters').append(formGroup);
}
}
$('input[data-param-name]').on('input change', composeEndpoint);
}
function composeEndpoint() {
var endpointInput = $('#oembedproviderform-endpoint');
var url = new URL(endpointInput.val());
$('.endpoint-param').each(function (index) {
url.searchParams.set($(this).attr('data-param-name'), $(this).val());
});
endpointInput.val(url.toString());
}
$('#oembedproviderform-endpoint').on('input change', function () {
initEndpointInputs();
});
$('#oembed-provider-form').on('submit', function () {
composeEndpoint();
});
JS, View::POS_END);
$this->registerJs(<<<JS
initEndpointInputs();
JS, View::POS_LOAD);
/* @var $prefix string */
?>
<?php $this->beginContent('@admin/views/setting/_advancedLayout.php') ?>
@ -14,7 +75,7 @@ use humhub\modules\ui\form\widgets\ActiveForm;
<?= Button::back(Url::to(['setting/oembed']), Yii::t('AdminModule.settings', 'Back to overview')) ?>
<h4 class="pull-left">
<?php
if ($prefix == "") {
if (empty($name)) {
echo Yii::t('AdminModule.settings', 'Add OEmbed provider');
} else {
echo Yii::t('AdminModule.settings', 'Edit OEmbed provider');
@ -25,21 +86,30 @@ use humhub\modules\ui\form\widgets\ActiveForm;
<br>
<?php $form = ActiveForm::begin(['id' => 'authentication-settings-form', 'acknowledge' => true]); ?>
<?php $form = ActiveForm::begin(['id' => 'oembed-provider-form', 'acknowledge' => true]); ?>
<?= $form->errorSummary($model); ?>
<?= $form->field($model, 'prefix')->textInput(['class' => 'form-control']); ?>
<p class="help-block"><?= Yii::t('AdminModule.settings', 'Url Prefix without http:// or https:// (e.g. youtube.com)'); ?></p>
<?= $form->field($model, 'name')->textInput(['class' => 'form-control']); ?>
<?= $form->field($model, 'pattern')->textInput(['class' => 'form-control']); ?>
<p class="help-block"><?= Yii::t('AdminModule.settings', 'Regular expression by which the link match will be checked.'); ?></p>
<?= $form->field($model, 'endpoint')->textInput(['class' => 'form-control']); ?>
<p class="help-block"><?= Yii::t('AdminModule.settings', 'Use %url% as placeholder for URL. Format needs to be JSON. (e.g. http://www.youtube.com/oembed?url=%url%&format=json)'); ?></p>
<?php if(isset($query['access_token'])): ?>
<?= $form->field($model, 'access_token')->textInput(['class' => 'form-control endpoint-param', 'data-param-name' => 'access_token', 'value' => $query['access_token']]) ?>
<?php endif; ?>
<div id="endpoint-parameters"></div>
<?= Html::submitButton(Yii::t('AdminModule.settings', 'Save'), ['class' => 'btn btn-primary', 'data-ui-loader' => ""]); ?>
<?php ActiveForm::end(); ?>
<?php if ($prefix != ""): ?>
<?= Html::a(Yii::t('AdminModule.settings', 'Delete'), Url::to(['oembed-delete', 'prefix' => $prefix]), ['class' => 'btn btn-danger pull-right', 'data-method' => 'POST']); ?>
<?php if (!empty($name)): ?>
<?= Html::a(Yii::t('AdminModule.settings', 'Delete'), Url::to(['oembed-delete', 'name' => $name]), ['class' => 'btn btn-danger pull-right', 'data-method' => 'POST']); ?>
<?php endif; ?>
<?php $this->endContent(); ?>

View File

@ -4,8 +4,10 @@ use humhub\modules\ui\form\widgets\ActiveForm;
use yii\helpers\Html;
use humhub\modules\admin\models\forms\StatisticSettingsForm;
use humhub\modules\ui\form\widgets\CodeMirrorInputWidget;
use yii\web\View;
/* @var $model StatisticSettingsForm */
?>
<?php $this->beginContent('@admin/views/setting/_advancedLayout.php') ?>
@ -19,11 +21,13 @@ use humhub\modules\ui\form\widgets\CodeMirrorInputWidget;
<div class="form-group">
<?= $form->field($model, 'trackingHtmlCode')->widget(CodeMirrorInputWidget::class); ?>
</div>
<hr>
<?= Html::submitButton(Yii::t('AdminModule.settings', 'Save'), ['class' => 'btn btn-primary', 'data-ui-loader' => ""]); ?>
<?= \humhub\widgets\DataSaved::widget(); ?>
<?php ActiveForm::end(); ?>
<?php $this->endContent(); ?>

View File

@ -178,8 +178,7 @@ class AdminMenu extends LeftNavigation
{
/** @var Module $module */
$module = Yii::$app->getModule('marketplace');
if (!$module->enabled) {
if ($module === null || !$module->enabled) {
return '';
}

View File

@ -0,0 +1,70 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\components\Module;
use humhub\components\Widget;
use humhub\widgets\Button;
use Yii;
use yii\helpers\Url;
/**
* ModuleActionsButton shows actions for module
*
* @since 1.11
* @author Luke
*/
class ModuleActionButtons extends Widget
{
/**
* @var Module
*/
public $module;
/**
* @var string Template for buttons
*/
public $template = '<div class="card-footer text-right">{buttons}</div>';
/**
* @inheritdoc
*/
public function run()
{
$html = '';
if ($this->module->isActivated) {
if ($this->module->getConfigUrl() != '') {
$html .= Button::asLink(Yii::t('AdminModule.modules', 'Configure'), $this->module->getConfigUrl())
->cssClass('btn btn-sm btn-info');
}
$html .= Button::asLink('<span class="glyphicon glyphicon-ok"></span>&nbsp;&nbsp;' . Yii::t('AdminModule.modules', 'Activated'), Url::to(['/admin/module/disable', 'moduleId' => $this->module->id]))
->cssClass('btn btn-sm btn-info active')
->options([
'data-method' => 'POST',
'data-confirm' => Yii::t('AdminModule.modules', 'Are you sure? *ALL* module data will be lost!')
]);
} else {
$html .= Button::asLink(Yii::t('AdminModule.modules', 'Activate'), Url::to(['/admin/module/enable', 'moduleId' => $this->module->id]))
->cssClass('btn btn-sm btn-info')
->options([
'data-method' => 'POST',
'data-loader' => 'modal',
'data-message' => Yii::t('AdminModule.modules', 'Enable module...')
]);
}
if (trim($html) === '') {
return '';
}
return str_replace('{buttons}', $html, $this->template);
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\components\Module;
use humhub\components\OnlineModule;
use humhub\components\Widget;
/**
* ModuleCard shows a card with module data
*
* @since 1.11
* @author Luke
*/
class ModuleCard extends Widget
{
/**
* @var Module|array
*/
public $module;
/**
* @var string HTML wrapper around card
*/
public $template;
/**
* @var string
*/
public $view;
public function init()
{
parent::init();
if (empty($this->template)) {
$this->template = '<div class="card card-module col-lg-3 col-md-4 col-sm-6 col-xs-12">{card}</div>';
}
if (empty($this->view)) {
$this->view = 'moduleCard';
}
}
/**
* @inheritdoc
*/
public function run()
{
$onlineModule = new OnlineModule(['module' => $this->module]);
$card = $this->render($this->view, [
'module' => $this->module,
'isFeaturedModule' => $onlineModule->isFeatured,
]);
return str_replace('{card}', $card, $this->template);
}
}

View File

@ -0,0 +1,164 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\components\Module;
use humhub\modules\content\components\ContentContainerModule;
use humhub\modules\ui\menu\MenuLink;
use humhub\modules\ui\menu\widgets\Menu;
use Yii;
/**
* Widget for rendering the context menu for module.
*/
class ModuleControls extends Menu
{
/**
* @var Module
*/
public $module;
/**
* @inheritdoc
*/
public $template = '@admin/widgets/views/moduleControls';
/**
* @inheritdoc
*/
public function init()
{
$this->initControls();
return parent::init();
}
public function initControls()
{
$this->initInstalledModuleControls();
if ($marketplaceUrl = $this->getMarketplaceUrl($this->module)) {
$this->addEntry(new MenuLink([
'id' => 'marketplace-info',
'label' => Yii::t('AdminModule.base', 'Information'),
'url' => $marketplaceUrl,
'htmlOptions' => ['rel' => 'noopener', 'target' => '_blank'],
'icon' => 'external-link',
'sortOrder' => 500,
]));
} else {
$this->addEntry(new MenuLink([
'id' => 'info',
'label' => Yii::t('AdminModule.base', 'Information'),
'url' => ['/admin/module/info', 'moduleId' => $this->module->id],
'htmlOptions' => ['data-target' => '#globalModal'],
'icon' => 'info-circle',
'sortOrder' => 600,
]));
}
}
private function initInstalledModuleControls()
{
if (!($this->module instanceof Module)) {
return;
}
if ($this->module->isActivated) {
if ($this->module->getConfigUrl() != '') {
$this->addEntry(new MenuLink([
'id' => 'configure',
'label' => Yii::t('AdminModule.base', 'Configure'),
'url' => $this->module->getConfigUrl(),
'icon' => 'wrench',
'sortOrder' => 100,
]));
}
if ($this->module instanceof ContentContainerModule) {
$this->addEntry(new MenuLink([
'id' => 'default',
'label' => Yii::t('AdminModule.base', 'Set as default'),
'url' => ['/admin/module/set-as-default', 'moduleId' => $this->module->id],
'htmlOptions' => ['data-target' => '#globalModal'],
'icon' => 'check-square',
'sortOrder' => 200,
]));
}
$this->addEntry(new MenuLink([
'id' => 'deactivate',
'label' => Yii::t('AdminModule.base', 'Deactivate'),
'url' => ['/admin/module/disable', 'moduleId' => $this->module->id],
'htmlOptions' => [
'data-method' => 'POST',
'data-confirm' => Yii::t('AdminModule.modules', 'Are you sure? *ALL* module data will be lost!'),
],
'icon' => 'minus-circle',
'sortOrder' => 300,
]));
} else {
$this->addEntry(new MenuLink([
'id' => 'deactivate',
'label' => Yii::t('AdminModule.base', 'Activate'),
'url' => ['/admin/module/enable', 'moduleId' => $this->module->id],
'htmlOptions' => [
'data-method' => 'POST',
'data-loader' => 'modal',
'data-message' => Yii::t('AdminModule.modules', 'Enable module...'),
],
'icon' => 'check-circle',
'sortOrder' => 300,
]));
}
if (Yii::$app->moduleManager->canRemoveModule($this->module->id)) {
$this->addEntry(new MenuLink([
'id' => 'uninstall',
'label' => Yii::t('AdminModule.base', 'Uninstall'),
'url' => ['/admin/module/remove', 'moduleId' => $this->module->id],
'htmlOptions' => [
'data-method' => 'POST',
'data-confirm' => Yii::t('AdminModule.modules', 'Are you sure? *ALL* module related data and files will be lost!'),
],
'icon' => 'trash',
'sortOrder' => 400,
]));
}
}
private function getMarketplaceUrl($module): ?string
{
if (!Yii::$app->hasModule('marketplace')) {
return false;
}
static $onlineModules;
if (!isset($modules)) {
/* @var \humhub\modules\marketplace\Module $marketplaceModule */
$marketplaceModule = Yii::$app->getModule('marketplace');
$onlineModules = $marketplaceModule->onlineModuleManager->getModules();
}
return empty($onlineModules[$module->id]['marketplaceUrl'])
? null
: $onlineModules[$module->id]['marketplaceUrl'];
}
/**
* @inheritdoc
*/
public function getAttributes()
{
return [
'class' => 'nav nav-pills preferences'
];
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\libs\Html;
use humhub\modules\ui\icon\widgets\Icon;
use humhub\modules\ui\widgets\DirectoryFilters;
use Yii;
/**
* ModuleFilters displays the filters on the modules list
*
* @since 1.11
* @author Luke
*/
class ModuleFilters extends DirectoryFilters
{
/**
* @inheritdoc
*/
public $pageUrl = '/admin/module/list';
/**
* @inheritdoc
*/
public $paginationUsed = false;
protected function initDefaultFilters()
{
$this->addFilter('keyword', [
'title' => Yii::t('AdminModule.base', 'Search'),
'placeholder' => Yii::t('AdminModule.base', 'Search...'),
'type' => 'input',
'wrapperClass' => 'col-md-7 form-search-filter-keyword',
'afterInput' => Html::submitButton(Icon::get('search'), ['class' => 'form-button-search']),
'sortOrder' => 100,
]);
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\components\Module;
use humhub\components\OnlineModule;
use humhub\components\Widget;
use Yii;
/**
* ModuleStatus shows a status of the module
*
* @property-read string|null $status
* @property-read string|null $statusTitle
* @property-read string $class
*
* @since 1.11
* @author Luke
*/
class ModuleStatus extends Widget
{
/**
* @var Module
*/
public $module;
/**
* @var string HTML wrapper around the status
*/
public $template = '<div class="card-status {class}">{status}</div>';
/**
* @var string|null Cached status of the module
*/
private $_status;
/**
* @inheritdoc
*/
public function run()
{
return str_replace(['{status}', '{class}'], [$this->statusTitle, $this->class], $this->template);
}
/**
* @return false|string|null
*/
public function getStatus()
{
if ($this->_status !== null) {
return $this->_status;
}
$onlineModule = new OnlineModule(['module' => $this->module]);
if ($onlineModule->isProOnly) {
$this->_status = 'professional';
} else if ($onlineModule->isFeatured) {
$this->_status = 'featured';
} else if (!$onlineModule->isThirdParty) {
$this->_status = 'official';
} else if ($onlineModule->isPartner) {
$this->_status = 'partner';
} else if ($onlineModule->isDeprecated) {
$this->_status = 'deprecated';
} else {
$this->_status = 'none';
}
// TODO: Implement new status detection
return $this->_status;
}
public function getStatusTitle(): string
{
switch ($this->status) {
case 'professional':
return Yii::t('AdminModule.modules', 'Professional Edition');
case 'featured':
return Yii::t('AdminModule.modules', 'Featured');
case 'official':
return Yii::t('AdminModule.modules', 'Official');
case 'partner':
return Yii::t('AdminModule.modules', 'Partner');
case 'deprecated':
return Yii::t('AdminModule.modules', 'Deprecated');
case 'new':
return Yii::t('AdminModule.modules', 'New');
}
return '';
}
public function getClass(): string
{
return 'card-status-' . $this->status;
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\admin\widgets;
use humhub\components\Widget;
use humhub\modules\marketplace\Module;
use Yii;
use yii\helpers\ArrayHelper;
/**
* Modules displays the modules list
*
* @since 1.11
* @author Luke
*/
class Modules extends Widget
{
/**
* @var array
*/
public $groups;
/**
* @inheritdoc
*/
public function init()
{
$this->initDefaultGroups();
parent::init();
ArrayHelper::multisort($this->groups, 'sortOrder');
}
private function initDefaultGroups()
{
/* @var Module $marketplaceModule */
$marketplaceModule = Yii::$app->getModule('marketplace');
if ($marketplaceModule->isFilteredBySingleTag('not_installed')) {
return;
}
$installedModules = Yii::$app->moduleManager->getModules();
ArrayHelper::multisort($installedModules, 'isActivated', SORT_DESC);
$this->addGroup('installed', [
'title' => Yii::t('AdminModule.modules', 'Installed'),
'modules' => Yii::$app->moduleManager->filterModules($installedModules),
'count' => count($installedModules),
'noModulesMessage' => Yii::t('AdminModule.base', 'No modules installed yet. Install some to enhance the functionality!'),
'sortOrder' => 100,
]);
}
public function addGroup(string $groupType, array $group)
{
$this->groups[$groupType] = $group;
}
/**
* @inheritdoc
*/
public function run()
{
$modules = '';
$alwaysVisibleGroup = 'availableUpdates';
$displaySingleGroup = false;
$emptyGroupCount = 0;
foreach ($this->groups as $groupType => $group) {
if ($groupType !== $alwaysVisibleGroup && empty($group['modules'])) {
$displaySingleGroup = true;
$emptyGroupCount++;
}
}
$singleGroupPrinted = false;
foreach ($this->groups as $groupType => $group) {
if ($singleGroupPrinted) {
continue;
}
if (empty($group['count']) || ($emptyGroupCount === 1 && empty($group['modules']))) {
continue;
}
if ($displaySingleGroup && $groupType !== $alwaysVisibleGroup) {
$singleGroupPrinted = true;
if (empty($group['modules'])) {
$group['title'] = false;
}
}
$group['type'] = $groupType;
$renderedGroup = $this->render('moduleGroup', $group);
if (isset($group['groupTemplate'])) {
$renderedGroup = str_replace('{group}', $renderedGroup, $group['groupTemplate']);
}
$modules .= $renderedGroup;
}
return $modules;
}
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use humhub\components\Module;
use humhub\libs\Html;
use humhub\modules\admin\widgets\ModuleActionButtons;
use humhub\modules\admin\widgets\ModuleControls;
use humhub\modules\admin\widgets\ModuleStatus;
use humhub\modules\ui\icon\widgets\Icon;
/* @var $module Module */
/* @var $isFeaturedModule bool */
?>
<div class="card-panel">
<?= ModuleStatus::widget(['module' => $module]) ?>
<div class="card-header">
<?= Html::img($module->getImage(), [
'class' => 'media-object img-rounded',
'data-src' => 'holder.js/94x94',
'alt' => '94x94',
'style' => 'width:94px;height:94px',
]) ?>
<?= ModuleControls::widget(['module' => $module]) ?>
</div>
<div class="card-body">
<div class="card-title"><?= $module->getName() . ($isFeaturedModule ? ' ' . Icon::get('star')->color('info') : '') ?></div>
<div><?= $module->getVersion() ?></div>
<div><?= $module->getDescription() ?></div>
</div>
<?= ModuleActionButtons::widget(['module' => $module]) ?>
</div>

View File

@ -0,0 +1,31 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use humhub\libs\Html;
use humhub\modules\ui\icon\widgets\Icon;
use humhub\modules\ui\menu\MenuEntry;
/* @var MenuEntry[] $entries */
/* @var array $options */
?>
<?= Html::beginTag('ul', $options)?>
<li class="dropdown ">
<a class="dropdown-toggle" data-toggle="dropdown" href="#"
aria-label="<?= Yii::t('base', 'Toggle stream entry menu'); ?>" aria-haspopup="true">
<?= Icon::get('dropdownToggle') ?>
</a>
<ul class="dropdown-menu pull-right">
<?php foreach ($entries as $entry) : ?>
<li>
<?= $entry->render() ?>
</li>
<?php endforeach; ?>
</ul>
</li>
<?= Html::endTag('ul')?>

View File

@ -0,0 +1,42 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use humhub\components\Module;
use humhub\modules\admin\widgets\ModuleCard;
/* @var string $type */
/* @var string|bool $title */
/* @var int $count */
/* @var string $noModulesMessage */
/* @var string $view */
/* @var string $moduleTemplate */
/* @var Module[] $modules */
?>
<?php if ($title !== false) : ?>
<h4 class="modules-type"><?= $title ?> (<span class="group-modules-count-<?= $type ?>"><?= $count ?></span>)</h4>
<?php endif; ?>
<div class="row cards">
<?php if (empty($modules)) : ?>
<div class="col-md-12 cards-no-results">
<?php if ($count) : ?>
<strong><?= Yii::t('AdminModule.modules', 'No modules found.') ?></strong><br/>
<?= Yii::t('AdminModule.modules', 'Try other keywords or remove filters.') ?>
<?php elseif (isset($noModulesMessage)) : ?>
<strong><?= $noModulesMessage ?></strong>
<?php endif; ?>
</div>
<?php endif; ?>
<?php foreach ($modules as $module) : ?>
<?= ModuleCard::widget([
'module' => $module,
'view' => $view ?? null,
'template' => $moduleTemplate ?? null,
]); ?>
<?php endforeach; ?>
</div>

View File

@ -9,10 +9,11 @@
namespace humhub\modules\comment;
use humhub\modules\content\components\ContentActiveRecord;
use Yii;
use humhub\modules\content\widgets\WallEntryAddons;
use humhub\modules\comment\models\Comment;
use humhub\modules\search\events\SearchAttributesEvent;
use humhub\modules\search\engine\Search;
use Yii;
use yii\base\Component;
use yii\base\Event;
@ -108,7 +109,13 @@ class Events extends Component
*/
public static function onWallEntryAddonInit($event)
{
$event->sender->addWidget(widgets\Comments::class, ['object' => $event->sender->object], ['sortOrder' => 30]);
/* @var WallEntryAddons $wallEntryAddons */
$wallEntryAddons = $event->sender;
$wallEntryAddons->addWidget(widgets\Comments::class, [
'object' => $wallEntryAddons->object,
'renderOptions' => $wallEntryAddons->renderOptions,
], ['sortOrder' => 30]);
}
/**

View File

@ -29,6 +29,16 @@ class Module extends \humhub\components\Module
*/
public $commentsPreviewMax = 2;
/**
* @var int Maximum comments to load at once on VIEW mode
*/
public $commentsBlockLoadSizeViewMode = 25;
/**
* @var int Maximum comments to show initially on VIEW mode
*/
public $commentsPreviewMaxViewMode = 25;
/**
* @inheritdoc
*/
@ -75,14 +85,9 @@ class Module extends \humhub\components\Module
return false;
}
// Only allow one level of subcomments
if (Comment::isSubComment($object)) {
return false;
}
$content = $object->content;
if($content->container) {
if ($content->container) {
if (!$content->container->permissionManager->can(CreateComment::class)) {
return false;
}

View File

@ -11,19 +11,24 @@ namespace humhub\modules\comment\controllers;
use humhub\components\access\ControllerAccess;
use humhub\components\Controller;
use humhub\libs\Helpers;
use humhub\modules\comment\models\Comment;
use humhub\modules\comment\models\forms\AdminDeleteCommentForm;
use humhub\modules\comment\models\forms\CommentForm;
use humhub\modules\comment\Module;
use humhub\modules\comment\widgets\Form;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\models\Content;
use Yii;
use yii\data\Pagination;
use yii\web\HttpException;
use yii\helpers\Url;
use humhub\modules\comment\models\Comment;
use humhub\modules\comment\notifications\CommentDeleted;
use humhub\modules\comment\widgets\AdminDeleteModal;
use humhub\modules\comment\widgets\Comment as CommentWidget;
use humhub\modules\comment\widgets\Form;
use humhub\modules\comment\widgets\ShowMore;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\file\handler\FileHandlerCollection;
use Yii;
use yii\base\BaseObject;
use yii\data\Pagination;
use yii\helpers\Url;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\HttpException;
use yii\web\NotFoundHttpException;
/**
@ -58,7 +63,7 @@ class CommentController extends Controller
public function beforeAction($action)
{
$modelClass = Yii::$app->request->get('objectModel', Yii::$app->request->post('objectModel'));
$modelPk = (int) Yii::$app->request->get('objectId', Yii::$app->request->post('objectId'));
$modelPk = (int)Yii::$app->request->get('objectId', Yii::$app->request->post('objectId'));
Helpers::CheckClassType($modelClass, [Comment::class, ContentActiveRecord::class]);
$this->target = $modelClass::findOne(['id' => $modelPk]);
@ -88,12 +93,19 @@ class CommentController extends Controller
$pagination = new Pagination([
'totalCount' => Comment::GetCommentCount(get_class($this->target), $this->target->getPrimaryKey()),
'pageSize' => $this->module->commentsBlockLoadSize
'pageSize' => Yii::$app->request->get('pageSize', $this->module->commentsBlockLoadSize)
]);
$query->offset($pagination->offset)->limit($pagination->limit);
// If need to load more than 1 page per request
$pageNum = Yii::$app->request->get('pageNum', 1);
$query->offset($pagination->offset)->limit($pagination->limit * $pageNum);
$comments = array_reverse($query->all());
if ($pageNum > 1) {
$pagination->setPage($pagination->page + $pageNum - 1);
}
$output = ShowMore::widget(['pagination' => $pagination, 'object' => $this->target]);
foreach ($comments as $comment) {
$output .= CommentWidget::widget(['comment' => $comment]);
@ -127,7 +139,8 @@ class CommentController extends Controller
return $this->renderAjaxContent(Form::widget([
'object' => $this->target,
'model' => $form->comment
'model' => $form->comment,
'isHidden' => false,
]));
});
}
@ -164,7 +177,8 @@ class CommentController extends Controller
'comment' => $comment,
'objectModel' => $comment->object_model,
'objectId' => $comment->object_id,
'submitUrl' => $submitUrl
'submitUrl' => $submitUrl,
'fileHandlers' => FileHandlerCollection::getByType([FileHandlerCollection::TYPE_IMPORT, FileHandlerCollection::TYPE_CREATE]),
]);
}
@ -203,13 +217,57 @@ class CommentController extends Controller
$comment = $this->getComment($id);
if(!$comment->canDelete()) {
if (!$comment->canDelete()) {
throw new ForbiddenHttpException();
}
$comment->delete();
$form = new AdminDeleteCommentForm();
return $this->asJson(['success' => true]);
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
if (!$form->validate()) {
throw new BadRequestHttpException();
}
if ($form->notify) {
$commentDeleted = CommentDeleted::instance()
->from(Yii::$app->user->getIdentity())
->about($comment->getCommentedRecord())
->payload(['commentText' => (new CommentDeleted())->getContentPreview($comment, 30), 'reason' => $form->message]);
$commentDeleted->saveRecord($comment->createdBy);
$commentDeleted->record->updateAttributes([
'send_web_notifications' => 1
]);
}
}
return $this->asJson(['success' => $comment->delete()]);
}
/**
* Returns modal content for admin to delete comment
*
* @throws NotFoundHttpException
* @throws ForbiddenHttpException
*/
public function actionGetAdminDeleteModal($id)
{
Yii::$app->response->format = 'json';
$comment = $this->getComment($id);
if (!$comment->canDelete()) {
throw new ForbiddenHttpException();
}
return [
'header' => Yii::t('CommentModule.base', '<strong>Delete</strong> comment?'),
'body' => AdminDeleteModal::widget([
'model' => new AdminDeleteCommentForm()
]),
'confirmText' => Yii::t('CommentModule.base', 'Confirm'),
'cancelText' => Yii::t('CommentModule.base', 'Cancel'),
];
}
/**
@ -221,7 +279,7 @@ class CommentController extends Controller
{
$comment = Comment::findOne(['id' => $id]);
if(!$comment) {
if (!$comment) {
throw new NotFoundHttpException();
}

View File

@ -31,14 +31,20 @@ class PermaController extends Controller
{
$comment = Comment::findOne(['id' => $id]);
if (!$comment || !$comment->canRead()) {
if (!$comment || !$comment->content || !$comment->canRead() || !$comment->content->container) {
throw new NotFoundHttpException();
}
return $this->redirect($comment->content->container->createUrl(null, [
'contentId' => $comment->content->id,
'commentId' => $comment->id,
]));
$content = $comment->content;
if ($content->container !== null) {
return $this->redirect($content->container->createUrl(null, [
'contentId' => $comment->content->id,
'commentId' => $comment->id,
]));
}
if (method_exists($content->getPolymorphicRelation(), 'getUrl')) {
return $this->redirect($content->getPolymorphicRelation()->getUrl());
}
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace humhub\modules\comment\models\forms;
use humhub\modules\comment\models\Comment;
use Yii;
/**
* AdminCommentDeleteForm is shown when admin deletes someone's comment
*/
class AdminDeleteCommentForm extends yii\base\Model
{
/**
* @var string
*/
public $message;
/**
* @var boolean
*/
public $notify;
/**
* @inheritdoc
*/
public function rules()
{
return [
[['message'], 'required', 'when' => function ($model) {
return $model->notify;
}],
[['message'], 'string'],
[['notify'], 'boolean']
];
}
public function attributeLabels()
{
return [
'message' => Yii::t('CommentModule.base', 'Reason'),
'notify' => Yii::t('CommentModule.base', 'Send a notification to author')
];
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\comment\notifications;
use humhub\modules\comment\models\Comment;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\widgets\richtext\converter\RichTextToShortTextConverter;
use humhub\modules\notification\components\BaseNotification;
use humhub\modules\notification\models\Notification;
use humhub\modules\user\models\User;
use humhub\modules\user\notifications\Mentioned;
use Yii;
use yii\bootstrap\Html;
use yii\helpers\Json;
/**
* CommentDeletedNotification is fired when admin deletes a comment
*/
class CommentDeleted extends BaseNotification
{
/**
* @inheritdoc
*/
public $requireSource = false;
/**
* @inheritdoc
*/
public $moduleId = 'comment';
/**
* @inheritdoc
*/
public function category()
{
return new CommentNotificationCategory();
}
/**
* @inheritdoc
*/
public function html()
{
return Yii::t('CommentModule.notifications', 'Your comment \'{commentText}\' has been deleted by {displayName} for \'{reason}\'', [
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),
'commentText' => $this->payload['commentText'],
'reason' => $this->payload['reason'],
]);
}
}

View File

@ -139,6 +139,25 @@ humhub.module('comment', function (module, require, $) {
});
};
Comment.prototype.adminDelete = function (evt) {
var $form = this.$.parent().siblings('.comment_create');
var hideHr = !this.isNestedComment() && $form.length && !this.$.siblings('.media').length;
this.$.data('content-delete-url', evt.$trigger.data('content-delete-url'));
this.$.data('admin-delete-modal-url', evt.$trigger.data('admin-delete-modal-url'));
this.super('adminDelete').then(function ($confirm) {
if ($confirm) {
module.log.success('success.delete');
if (hideHr) {
$form.find('hr').hide();
}
}
}).catch(function (err) {
module.log.error(err, true);
});
}
Comment.prototype.isNestedComment = function () {
return this.$.closest('.nested-comments-root').length !== 0;
};

View File

@ -1,5 +1,7 @@
<?php
use humhub\modules\file\handler\BaseFileHandler;
use humhub\modules\file\widgets\FileHandlerButtonDropdown;
use humhub\modules\file\widgets\FilePreview;
use humhub\modules\file\widgets\UploadButton;
use humhub\modules\ui\form\widgets\ActiveForm;
@ -12,6 +14,7 @@ use humhub\modules\content\widgets\richtext\RichTextField;
/* @var $objectId integer */
/* @var $comment \humhub\modules\comment\models\Comment */
/* @var $submitUrl string */
/* @var $fileHandlers BaseFileHandler[] */
/** @var \humhub\modules\content\Module $contentModule */
$contentModule = Yii::$app->getModule('content');
@ -35,19 +38,28 @@ $contentModule = Yii::$app->getModule('content');
]
])->label(false) ?>
<div class="comment-buttons">
<?= UploadButton::widget([
<div class="comment-buttons"><?php
$uploadButton = UploadButton::widget([
'id' => 'comment_upload_' . $comment->id,
'model' => $comment,
'tooltip' => Yii::t('ContentModule.base', 'Attach Files'),
'dropZone' => '#comment_' . $comment->id,
'preview' => '#comment_upload_preview_' . $comment->id,
'progress' => '#comment_upload_progress_' . $comment->id,
'max' => $contentModule->maxAttachedFiles
]); ?>
<?= Button::defaultType(Yii::t('base', 'Save'))->cssClass('btn-comment-submit')->action('editSubmit', $submitUrl)->submit()->sm() ?>
</div>
'max' => $contentModule->maxAttachedFiles,
'cssButtonClass' => 'btn-sm btn-info',
]);
echo FileHandlerButtonDropdown::widget([
'primaryButton' => $uploadButton,
'handlers' => $fileHandlers,
'cssButtonClass' => 'btn-info btn-sm',
'pullRight' => true,
]);
echo Button::info()
->icon('send')
->cssClass('btn-comment-submit')->sm()
->action('editSubmit', $submitUrl)->submit();
?></div>
</div>
<div id="comment_upload_progress_<?= $comment->id ?>" style="display:none; margin:10px 0;"></div>

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2016 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\comment\widgets;
use humhub\modules\comment\models\forms\AdminDeleteCommentForm;
/**
* Admin Delete Modal for Comments
*
* This widget will be shown when admin deletes someone's comment
*
*/
class AdminDeleteModal extends \yii\base\Widget
{
/**
* @var AdminDeleteCommentForm
*/
public $model = null;
/**
* Executes the widget.
*/
public function run()
{
return $this->render('adminDeleteModal', [
'model' => $this->model,
]);
}
}

View File

@ -60,19 +60,36 @@ class CommentControls extends Menu
}
if ($this->comment->canDelete()) {
$deleteUrl = Url::to(['/comment/comment/delete', 'objectModel' => $this->comment->object_model,
$isAdmin = $this->comment->created_by !== Yii::$app->user->id;
$deleteUrl = Url::to(['/comment/comment/delete',
'objectModel' => $this->comment->object_model,
'objectId' => $this->comment->object_id,
'id' => $this->comment->id,
]);
if($isAdmin) {
$adminDeleteModalUrl = Url::to(['/comment/comment/get-admin-delete-modal',
'objectModel' => $this->comment->object_model,
'objectId' => $this->comment->object_id,
'id' => $this->comment->id,
]);
}
$htmlOptions = [
'data-action-click' => $isAdmin ? 'adminDelete' : 'delete',
'data-content-delete-url' => $deleteUrl
];
if($isAdmin) {
$htmlOptions['data-admin-delete-modal-url'] = $adminDeleteModalUrl;
}
$this->addEntry(new MenuLink([
'label' => Yii::t('CommentModule.base', 'Delete'),
'icon' => 'delete',
'url' => '#',
'htmlOptions' => [
'data-action-click' => 'delete',
'data-content-delete-url' => $deleteUrl,
],
'htmlOptions' => $htmlOptions,
'sortOrder' => 300,
]));
}

View File

@ -6,7 +6,10 @@ use humhub\modules\comment\models\Comment as CommentModel;
use humhub\modules\comment\Module;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\components\Widget;
use humhub\modules\content\widgets\stream\StreamEntryOptions;
use humhub\modules\content\widgets\stream\WallStreamEntryOptions;
use Yii;
use yii\helpers\Url;
/**
* This widget is used include the comments functionality to a wall entry.
@ -14,25 +17,52 @@ use Yii;
* Normally it shows a excerpt of all comments, but provides the functionality
* to show all comments.
*
* @property-read int $limit
* @property-read int $pageSize
*
* @package humhub.modules_core.comment
* @since 0.5
*/
class Comments extends Widget
{
const VIEW_MODE_COMPACT = 'compact';
const VIEW_MODE_FULL = 'full';
/**
* @var Comment|ContentActiveRecord
*/
public $object;
/**
* @var StreamEntryOptions|null
*/
public $renderOptions;
/**
* @var Module
*/
public $module;
/**
* @var string
*/
public $viewMode = self::VIEW_MODE_COMPACT;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
$this->module = Yii::$app->getModule('comment');
}
/**
* Executes the widget.
*/
public function run()
{
/** @var Module $module */
$module = Yii::$app->getModule('comment');
$objectModel = get_class($this->object);
$objectId = $this->object->getPrimaryKey();
@ -43,20 +73,49 @@ class Comments extends Widget
$commentCount = CommentModel::GetCommentCount($objectModel, $objectId);
$comments = [];
if ($commentCount !== 0) {
$comments = CommentModel::GetCommentsLimited($objectModel, $objectId, $module->commentsPreviewMax, $currentCommentId);
$comments = CommentModel::GetCommentsLimited($objectModel, $objectId, $this->limit, $currentCommentId);
}
$isLimited = ($commentCount > $module->commentsPreviewMax);
return $this->render('comments', [
'object' => $this->object,
'comments' => $comments,
'currentCommentId' => $currentCommentId,
'objectModel' => $objectModel,
'objectId' => $objectId,
'id' => $this->object->getUniqueId(),
'isLimited' => $isLimited,
'total' => $commentCount
'isLimited' => $commentCount > $this->limit,
'total' => $commentCount,
'showMoreUrl' => $this->getShowMoreUrl(),
]);
}
private function isFullViewMode(): bool
{
return $this->viewMode === self::VIEW_MODE_FULL ||
(($this->renderOptions instanceof StreamEntryOptions) && $this->renderOptions->isViewContext(WallStreamEntryOptions::VIEW_CONTEXT_DETAIL));
}
public function getLimit(): int
{
return $this->isFullViewMode() ? $this->module->commentsPreviewMaxViewMode : $this->module->commentsPreviewMax;
}
public function getPageSize(): int
{
return $this->isFullViewMode() ? $this->module->commentsBlockLoadSizeViewMode : $this->module->commentsBlockLoadSize;
}
private function getShowMoreUrl(): string
{
$urlParams = ['/comment/comment/show',
'objectModel' => get_class($this->object),
'objectId' => $this->object->getPrimaryKey(),
'pageSize' => $this->pageSize,
];
if ($this->pageSize <= $this->limit) {
// We should load second page together with first because on init page we already see the first page
$urlParams['pageNum'] = 2;
}
return Url::to($urlParams);
}
}

View File

@ -12,6 +12,7 @@ use humhub\components\Widget;
use humhub\modules\comment\Module;
use humhub\modules\comment\models\Comment as CommentModel;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\file\handler\FileHandlerCollection;
use Yii;
use yii\helpers\Url;
@ -40,6 +41,24 @@ class Form extends Widget
*/
public $mentioningUrl = '/search/mentioning/content';
/**
* @var bool
*/
public $isHidden;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->isHidden === null) {
// Hide the comment form for sub comments until the button is clicked
$this->isHidden = ($this->object instanceof Comment);
}
}
/**
* Executes the widget.
*/
@ -67,6 +86,8 @@ class Form extends Widget
'model' => $this->model,
'isNestedComment' => ($this->object instanceof CommentModel),
'mentioningUrl' => Url::to([$this->mentioningUrl, 'id' => $this->object->content->id]),
'isHidden' => $this->isHidden,
'fileHandlers' => FileHandlerCollection::getByType([FileHandlerCollection::TYPE_IMPORT, FileHandlerCollection::TYPE_CREATE]),
]);
}

View File

@ -37,6 +37,7 @@ class ShowMore extends \yii\base\Widget
'/comment/comment/show',
'objectModel' => get_class($this->object),
'objectId' => $this->object->getPrimaryKey(),
'pageSize' => $this->pagination->pageSize,
'page' => $this->pagination->page + 2
]);

View File

@ -0,0 +1,31 @@
<?php
use humhub\libs\Html;
use humhub\modules\comment\models\forms\AdminDeleteCommentForm;
use humhub\modules\ui\form\widgets\ActiveForm;
/* @var $model AdminDeleteCommentForm */
?>
<?php $form = ActiveForm::begin(['acknowledge' => true]); ?>
<?= $form->field($model, 'message')->textarea(['rows' => 3]) ?>
<?= $form->field($model, 'notify')->checkbox(['value' => '1', 'checked ' => true]) ?>
<?php ActiveForm::end(); ?>
<script <?= Html::nonce() ?>>
var $messageTextarea = $('#admindeletecommentform-message');
var $notifyCheckbox = $('#admindeletecommentform-notify');
humhub.modules.ui.form.elements.initCheckbox($notifyCheckbox);
$notifyCheckbox.on('change', function () {
if($notifyCheckbox.is(':checked'))
$messageTextarea.removeAttr('disabled');
else
$messageTextarea.attr('disabled', 'disabled');
});
</script>

View File

@ -2,18 +2,16 @@
use humhub\modules\comment\widgets\Form;
use humhub\modules\comment\widgets\Comment;
use yii\helpers\Url;
use humhub\libs\Html;
/* @var $this \humhub\modules\ui\view\components\View */
/* @var $object \humhub\modules\content\components\ContentActiveRecord */
/* @var $comments \humhub\modules\comment\models\Comment[] */
/* @var $currentCommentId int */
/* @var $objectModel string */
/* @var $objectId int */
/* @var $id string unqiue object id */
/* @var $isLimited boolean */
/* @var $total int */
/* @var $showMoreUrl string */
?>
<div class="well well-small comment-container" style="display:none;" id="comment_<?= $id; ?>">
@ -22,7 +20,7 @@ use humhub\libs\Html;
<?php if ($isLimited): ?>
<a href="#" class="show show-all-link" data-ui-loader data-action-click="comment.showAll"
data-action-url="<?= Url::to(['/comment/comment/show', 'objectModel' => $objectModel, 'objectId' => $objectId]) ?>">
data-action-url="<?= $showMoreUrl ?>">
<?= Yii::t('CommentModule.base', 'Show all {total} comments', ['{total}' => $total]) ?>
</a>
<hr class="comments-start-separator">

View File

@ -1,6 +1,8 @@
<?php
use humhub\modules\content\Module;
use humhub\modules\file\handler\BaseFileHandler;
use humhub\modules\file\widgets\FileHandlerButtonDropdown;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\modules\ui\view\components\View;
use humhub\widgets\Button;
@ -19,6 +21,8 @@ use humhub\modules\comment\models\Comment;
/* @var $isNestedComment boolean */
/* @var $contentModule Module */
/* @var $mentioningUrl string */
/* @var $isHidden bool */
/* @var $fileHandlers BaseFileHandler[] */
$contentModule = Yii::$app->getModule('content');
$submitUrl = Url::to(['/comment/comment/post']);
@ -26,9 +30,6 @@ $submitUrl = Url::to(['/comment/comment/post']);
$placeholder = ($isNestedComment)
? Yii::t('CommentModule.base', 'Write a new reply...')
: Yii::t('CommentModule.base', 'Write a new comment...');
// Hide the comment form for sub comments until the button is clicked
$isHidden = ($objectModel === Comment::class);
?>
<div id="comment_create_form_<?= $id ?>" class="comment_create" data-ui-widget="comment.Form"
@ -57,21 +58,28 @@ $isHidden = ($objectModel === Comment::class);
]
])->label(false) ?>
<div class="comment-buttons">
<?= UploadButton::widget([
<div class="comment-buttons"><?php
$uploadButton = UploadButton::widget([
'id' => 'comment_create_upload_' . $id,
'tooltip' => Yii::t('ContentModule.base', 'Attach Files'),
'options' => ['class' => 'main_comment_upload'],
'progress' => '#comment_create_upload_progress_' . $id,
'preview' => '#comment_create_upload_preview_' . $id,
'dropZone' => '#comment_create_form_' . $id,
'max' => $contentModule->maxAttachedFiles
]); ?>
<?= Button::defaultType(Yii::t('CommentModule.base', 'Send'))
->cssClass('btn-comment-submit')
->action('submit', $submitUrl)->submit()->sm() ?>
</div>
'max' => $contentModule->maxAttachedFiles,
'cssButtonClass' => 'btn-sm btn-info',
]);
echo FileHandlerButtonDropdown::widget([
'primaryButton' => $uploadButton,
'handlers' => $fileHandlers,
'cssButtonClass' => 'btn-info btn-sm',
'pullRight' => true,
]);
echo Button::info()
->icon('send')
->cssClass('btn-comment-submit')->sm()
->action('submit', $submitUrl)->submit();
?></div>
</div>
<div id="comment_create_upload_progress_<?= $id ?>" style="display:none;margin:10px 0px;"></div>

View File

@ -306,13 +306,11 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
/**
* Returns a ModuleManager
*
* @param User $user
* @return ModuleManager
* @return ContentContainerModuleManager
* @since 1.3
*/
public function getModuleManager()
public function getModuleManager(): ?ContentContainerModuleManager
{
if ($this->moduleManager !== null) {
return $this->moduleManager;
}
@ -386,7 +384,7 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
*/
public function getTags(): array
{
$tags = trim($this->contentContainerRecord->tags_cached);
$tags = is_string($this->contentContainerRecord->tags_cached) ? trim($this->contentContainerRecord->tags_cached) : '';
return $tags === '' ? [] : preg_split('/\s*,\s*/', $tags);
}

View File

@ -156,7 +156,7 @@ class ContentContainerModuleManager extends \yii\base\Component
$this->_available = [];
foreach (Yii::$app->moduleManager->getModules() as $id => $module) {
if ($module instanceof ContentContainerModule && Yii::$app->hasModule($module->id) &&
if ($module instanceof ContentContainerModule && $module->isActivated &&
$module->hasContentContainerType($this->contentContainer->className())) {
$this->_available[$module->id] = $module;
}
@ -205,21 +205,27 @@ class ContentContainerModuleManager extends \yii\base\Component
*/
protected function getStates()
{
$states = [];
static $states = [];
if (isset($states[$this->contentContainer->contentcontainer_id])) {
return $states[$this->contentContainer->contentcontainer_id];
}
$states[$this->contentContainer->contentcontainer_id] = [];
// Get states for this contentcontainer from database
foreach (ContentContainerModuleState::findAll(['contentcontainer_id' => $this->contentContainer->contentcontainer_id]) as $module) {
$states[$module->module_id] = $module->module_state;
$states[$this->contentContainer->contentcontainer_id][$module->module_id] = $module->module_state;
}
// Get default states, when no state is stored
foreach ($this->getAvailable() as $module) {
if (!isset($states[$module->id])) {
$states[$module->id] = self::getDefaultState($this->contentContainer->className(), $module->id);
if (!isset($states[$this->contentContainer->contentcontainer_id][$module->id])) {
$states[$this->contentContainer->contentcontainer_id][$module->id] = self::getDefaultState($this->contentContainer->className(), $module->id);
}
}
return $states;
return $states[$this->contentContainer->contentcontainer_id];
}
/**

View File

@ -18,6 +18,8 @@ use yii\base\Behavior;
* @author luke
*
* @property \humhub\modules\content\components\ContentContainerActiveRecord $owner
*
* @deprecated since 1.10.0 use methods of the ContentContainerActiveRecord->moduleManager
*/
class CompatModuleManager extends Behavior
{

View File

@ -8,17 +8,23 @@
namespace humhub\modules\content\controllers;
use humhub\modules\content\Module;
use humhub\modules\stream\actions\StreamEntryResponse;
use Yii;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\web\ForbiddenHttpException;
use yii\web\HttpException;
use humhub\components\behaviors\AccessControl;
use humhub\components\Controller;
use humhub\modules\content\models\Content;
use humhub\modules\content\models\forms\AdminDeleteContentForm;
use humhub\modules\content\Module;
use humhub\modules\content\notifications\ContentDeleted;
use humhub\modules\content\permissions\CreatePublicContent;
use humhub\components\behaviors\AccessControl;
use humhub\modules\content\widgets\AdminDeleteModal;
use humhub\modules\stream\actions\StreamEntryResponse;
use Yii;
use yii\base\BaseObject;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\HttpException;
use yii\web\NotAcceptableHttpException;
use yii\web\NotFoundHttpException;
use yii\web\Response;
@ -58,22 +64,41 @@ class ContentController extends Controller
$model = Yii::$app->request->get('model');
// Due to backward compatibility we use the old delete mechanism in case a model parameter is provided
$id = (int) ($model != null) ? Yii::$app->request->get('id') : Yii::$app->request->post('id');
$id = (int)($model != null) ? Yii::$app->request->get('id') : Yii::$app->request->post('id');
/* @var $contentObjs Content */
$contentObj = ($model != null) ? Content::Get($model, $id) : Content::findOne(['id' => $id]);
if (!$contentObj) {
throw new HttpException(404);
throw new NotFoundHttpException();
}
if (!$contentObj->canEdit()) {
throw new HttpException(400, Yii::t('ContentModule.base', 'Could not delete content: Access denied!'));
throw new ForbiddenHttpException();
}
if ($contentObj !== null && $contentObj->delete()) {
if ($contentObj !== null) {
$form = new AdminDeleteContentForm();
if ($form->load(Yii::$app->request->post())) {
if (!$form->validate()) {
throw new BadRequestHttpException();
}
if ($form->notify) {
$contentDeleted = ContentDeleted::instance()
->from(Yii::$app->user->getIdentity())
->payload(['contentTitle' => (new ContentDeleted)->getContentPlainTextInfo($contentObj), 'reason' => $form->message]);
$contentDeleted->saveRecord($contentObj->createdBy);
$contentDeleted->record->updateAttributes([
'send_web_notifications' => 1
]);
}
}
$json = [
'success' => true,
'success' => $contentObj->delete(),
'uniqueId' => $contentObj->getUniqueId(),
'model' => $model,
'pk' => $id
@ -85,6 +110,35 @@ class ContentController extends Controller
return $json;
}
/**
* Returns modal content for admin to delete content
*/
public function actionGetAdminDeleteModal()
{
Yii::$app->response->format = 'json';
$id = Yii::$app->request->post('id');
$contentObj = Content::findOne(['id' => $id]);
if (!$contentObj) {
throw new NotFoundHttpException();
}
if (!$contentObj->canEdit()) {
throw new HttpException(400);
}
return [
'header' => Yii::t('ContentModule.base', '<strong>Delete</strong> content?'),
'body' => AdminDeleteModal::widget([
'model' => new AdminDeleteContentForm()
]),
'confirmText' => Yii::t('ContentModule.base', 'Confirm'),
'cancelText' => Yii::t('ContentModule.base', 'Cancel'),
];
}
/**
* Archives an wall entry & corresponding content object.
*
@ -98,7 +152,7 @@ class ContentController extends Controller
$json = [];
$json['success'] = false;
$id = (int) Yii::$app->request->get('id', '');
$id = (int)Yii::$app->request->get('id', '');
$content = Content::findOne(['id' => $id]);
if ($content !== null && $content->canArchive()) {
@ -122,7 +176,7 @@ class ContentController extends Controller
$json = [];
$json['success'] = false; // default
$id = (int) Yii::$app->request->get('id', '');
$id = (int)Yii::$app->request->get('id', '');
$content = Content::findOne(['id' => $id]);
if ($content !== null && $content->canArchive()) {
@ -137,11 +191,36 @@ class ContentController extends Controller
public function actionDeleteId()
{
$this->forcePostRequest();
$content = Content::findOne(['id' => Yii::$app->request->post('id')]);
$post = Yii::$app->request->post();
$content = Content::findOne(['id' => $post['id']]);
if (!$content) {
throw new HttpException(400, Yii::t('ContentModule.base', 'Invalid content id given!'));
} elseif (!$content->canEdit()) {
throw new HttpException(403);
throw new NotFoundHttpException();
}
if (!$content->canEdit()) {
throw new ForbiddenHttpException();
}
$form = new AdminDeleteContentForm();
if ($form->load($post)) {
if (!$form->validate()) {
throw new BadRequestHttpException();
}
if($form->notify) {
$contentDeleted = ContentDeleted::instance()
->from(Yii::$app->user->getIdentity())
->payload(['contentTitle' => (new ContentDeleted)->getContentPlainTextInfo($content), 'reason' => $form->message]);
$contentDeleted->saveRecord($content->createdBy);
$contentDeleted->record->updateAttributes([
'send_web_notifications' => 1
]);
}
}
return $this->asJson(['success' => $content->delete()]);
@ -193,8 +272,8 @@ class ContentController extends Controller
}
return $this->asJson([
'success' => $content->save(),
'state' => $content->visibility
'success' => $content->save(),
'state' => $content->visibility
]);
}
@ -277,11 +356,11 @@ class ContentController extends Controller
$content = Content::findOne(['id' => Yii::$app->request->get('id', '')]);
if(!$content) {
if (!$content) {
throw new NotFoundHttpException();
}
if(!$content->canPin()) {
if (!$content->canPin()) {
throw new ForbiddenHttpException();
}

View File

@ -8,6 +8,7 @@
namespace humhub\modules\content\models;
use Yii;
use yii\db\ActiveRecord;
/**
@ -19,9 +20,13 @@ use yii\db\ActiveRecord;
*/
class ContentContainerModuleState extends ActiveRecord
{
/** @var int */
const STATE_DISABLED = 0;
/** @var int */
const STATE_ENABLED = 1;
/** @var int */
const STATE_FORCE_ENABLED = 2;
/**
@ -32,4 +37,18 @@ class ContentContainerModuleState extends ActiveRecord
return 'contentcontainer_module';
}
/**
* @param false $labels
* @return array|int[]|string[]
*/
public static function getStates($labels = false)
{
$states = [
self::STATE_DISABLED => Yii::t('AdminModule.modules', 'Deactivated'),
self::STATE_ENABLED => Yii::t('AdminModule.modules', 'Activated'),
self::STATE_FORCE_ENABLED => Yii::t('AdminModule.modules', 'Always activated')
];
return $labels ? $states : array_keys($states);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace humhub\modules\content\models\forms;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\models\Content;
use Yii;
use yii\base\Model;
/**
* AdminDeleteContentForm is shown when admin deletes someone's content (e.g. post)
*/
class AdminDeleteContentForm extends Model
{
/**
* @var string
*/
public $message;
/**
* @var boolean
*/
public $notify;
/**
* @inheritdoc
*/
public function rules()
{
return [
[['message'], 'required', 'when' => function ($model) {
return $model->notify;
}],
[['message'], 'string'],
[['notify'], 'boolean']
];
}
public function attributeLabels()
{
return [
'message' => Yii::t('CommentModule.base', 'Reason'),
'notify' => Yii::t('CommentModule.base', 'Send a notification to author')
];
}
}

View File

@ -0,0 +1,54 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2017 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\notifications;
use humhub\modules\notification\components\BaseNotification;
use Yii;
use yii\bootstrap\Html;
use humhub\modules\user\models\User;
use humhub\libs\Helpers;
use yii\helpers\Json;
/**
* ContentDeletedNotification is fired when admin deletes a content (e.g. post)
*/
class ContentDeleted extends BaseNotification
{
/**
* @inheritdoc
*/
public $requireSource = false;
/**
* @inheritdoc
*/
public $moduleId = 'content';
/**
* @inheritdoc
*/
public function category()
{
return new \humhub\modules\content\notifications\ContentCreatedNotificationCategory();
}
/**
* @inheritdoc
*/
public function html()
{
return Yii::t('ContentModule.notifications', 'Your {contentTitle} has been deleted by {displayName} for \'{reason}\'', [
'displayName' => Html::tag('strong', Html::encode($this->originator->displayName)),
'contentTitle' => $this->payload['contentTitle'],
'reason' => $this->payload['reason']
]);
}
}
?>

View File

@ -16,6 +16,7 @@ humhub.module('content', function (module, require, $) {
var DATA_CONTENT_KEY = "content-key";
var DATA_CONTENT_DELETE_URL = "content-delete-url";
var DATA_ADMIN_DELETE_MODAL_URL = "admin-delete-modal-url";
Component.addSelector('content-component');
@ -75,13 +76,59 @@ humhub.module('content', function (module, require, $) {
});
};
Content.prototype.deleteContent = function(resolve, reject) {
Content.prototype.adminDelete = function (options) {
var that = this;
var loadModalUrl = that.data(DATA_ADMIN_DELETE_MODAL_URL) || module.config.adminDeleteModalUrl;
if(!loadModalUrl) {
that.delete(options);
return;
}
return new Promise(function (resolve, reject) {
client.post(loadModalUrl, {
data: {
id: that.getKey()
}
}).then(function (response) {
modal.confirm(response).then(function ($confirmed) {
if (!$confirmed) {
resolve(false);
return;
}
var form = modal.globalConfirm.$.find('form')[0];
that.deleteContent(resolve, reject, form);
});
}).catch(function (err) {
reject(err);
});
});
};
Content.prototype.deleteContent = function(resolve, reject, form) {
var that = this;
that.loader();
var deleteUrl = that.data(DATA_CONTENT_DELETE_URL) || module.config.deleteUrl;
var postData = {
id: that.getKey()
};
if(typeof form !== 'undefined') {
Object.assign(postData, {
...$(form).serializeArray().reduce(function(a, e) {
a[e.name] = e.value;
return a;
})
});
}
if (deleteUrl) {
client.post(deleteUrl, {
data: {id: that.getKey()}
data: postData
}).then(function (response) {
that.remove().then(function () {
resolve(true);

View File

@ -125,7 +125,7 @@ humhub.module('ui.richtext.prosemirror', function(module, require, $) {
}
if (Object.keys(backup).length) {
$.cookie(this.options.backupCookieKey, JSON.stringify(backup));
$.cookie(this.options.backupCookieKey, JSON.stringify(backup), {expires: 120 / 1440/*2 hours*/});
} else {
$.cookie(this.options.backupCookieKey, null, {expires: -1});
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2016 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\widgets;
use humhub\modules\content\models\forms\AdminDeleteContentForm;
/**
* Admin Delete Modal for Wall Entries
*
* This widget will be shown when admin deletes someone's content
*
*/
class AdminDeleteModal extends \yii\base\Widget
{
/**
* @var AdminDeleteContentForm
*/
public $model = null;
/**
* Executes the widget.
*/
public function run()
{
return $this->render('adminDeleteModal', [
'model' => $this->model,
]);
}
}

View File

@ -88,8 +88,8 @@ class ContentObjectLinks extends BaseStack
if (!($this->object instanceof ContentActiveRecord)) {
return;
}
$this->addWidget(LikeLink::class, ['object' => $this->object], ['sortOrder' => 100]);
$this->addWidget(CommentLink::class, ['object' => $this->object], ['sortOrder' => 200]);
$this->addWidget(CommentLink::class, ['object' => $this->object], ['sortOrder' => 100]);
$this->addWidget(LikeLink::class, ['object' => $this->object], ['sortOrder' => 200]);
}
/**

View File

@ -31,9 +31,13 @@ class DeleteLink extends \yii\base\Widget
public function run()
{
if ($this->content->content->canEdit()) {
$isAdmin = $this->content->content->created_by !== \Yii::$app->user->id;
return $this->render('deleteLink', [
'model' => $this->content->content->object_model,
'id' => $this->content->content->object_id
'model' => $this->content->content->object_model,
'id' => $this->content->content->object_id,
'isAdmin' => $isAdmin
]);
}

View File

@ -0,0 +1,108 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\widgets;
use humhub\components\Module;
use humhub\components\Widget;
use humhub\modules\content\components\behaviors\CompatModuleManager;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use Yii;
/**
* ModuleActionsButton shows actions for module of Content Container
*
* @since 1.11
* @author Luke
*/
class ModuleActionButtons extends Widget
{
/**
* @var Module
*/
public $module;
/**
* @var ContentContainerActiveRecord|CompatModuleManager
*/
public $contentContainer;
/**
* @var string Template for buttons
*/
public $template = '<div class="card-footer text-right">{buttons}</div>';
/**
* @inheritdoc
*/
public function run()
{
$html = '';
if ($this->module->getContentContainerConfigUrl($this->contentContainer) && $this->contentContainer->isModuleEnabled($this->module->id)) {
$html .= Button::asLink(Yii::t('ContentModule.modules', 'Configure'), $this->module->getContentContainerConfigUrl($this->contentContainer))
->cssClass('btn btn-sm btn-info configure-module-' . $this->module->id);
}
if ($this->contentContainer->canDisableModule($this->module->id)) {
$html .= Button::asLink('<span class="glyphicon glyphicon-ok"></span>&nbsp;&nbsp;' . Yii::t('ContentModule.modules', 'Activated'), '#')
->cssClass('btn btn-sm btn-info active disable disable-module-' . $this->module->id)
->style($this->contentContainer->isModuleEnabled($this->module->id) ? '' : 'display:none')
->options([
'data-action-click' => 'content.container.disableModule',
'data-action-url' => $this->getDisableUrl(),
'data-reload' => '1',
'data-action-confirm' => $this->getDisableConfirmationText(),
'data-ui-loader' => 1,
]);
}
$html .= Button::asLink(Yii::t('ContentModule.modules', 'Enable'), '#')
->cssClass('btn btn-sm btn-info enable enable-module-' . $this->module->id)
->style($this->contentContainer->isModuleEnabled($this->module->id) ? 'display:none' : '')
->options([
'data-action-click' => 'content.container.enableModule',
'data-action-url' => $this->getEnableUrl(),
'data-reload' => '1',
'data-ui-loader' => 1,
]);
if (trim($html) === '') {
return '';
}
return str_replace('{buttons}', $html, $this->template);
}
private function isSpace(): bool
{
return $this->contentContainer instanceof Space;
}
private function getDisableUrl(): string
{
$route = $this->isSpace() ? '/space/manage/module/disable' : '/user/account/disable-module';
return $this->contentContainer->createUrl($route, ['moduleId' => $this->module->id]);
}
private function getDisableConfirmationText(): string
{
return $this->isSpace()
? Yii::t('ContentModule.manage', 'Are you sure? *ALL* module data for this space will be deleted!')
: Yii::t('ContentModule.manage', 'Are you really sure? *ALL* module data for your profile will be deleted!');
}
private function getEnableUrl(): string
{
$route = $this->isSpace() ? '/space/manage/module/enable' : '/user/account/enable-module';
return $this->contentContainer->createUrl($route, ['moduleId' => $this->module->id]);
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\widgets;
use humhub\modules\admin\widgets\ModuleCard as AdminModuleCard;
use humhub\modules\content\components\ContentContainerActiveRecord;
/**
* ModuleCard shows a card with module data of Content Container
*
* @since 1.11
* @author Luke
*/
class ModuleCard extends AdminModuleCard
{
/**
* @var ContentContainerActiveRecord
*/
public $contentContainer;
/**
* @inheritdoc
*/
public function run()
{
$card = $this->render($this->view, [
'module' => $this->module,
'contentContainer' => $this->contentContainer,
]);
return str_replace('{card}', $card, $this->template);
}
}

View File

@ -9,7 +9,6 @@
namespace humhub\modules\content\widgets;
use humhub\modules\content\permissions\CreatePublicContent;
use humhub\modules\stream\actions\Stream;
use humhub\modules\stream\actions\StreamEntryResponse;
use humhub\modules\topic\models\Topic;
use Yii;
@ -88,9 +87,6 @@ class WallCreateContentForm extends Widget
$canSwitchVisibility = false;
}
$fileHandlerImport = FileHandlerCollection::getByType(FileHandlerCollection::TYPE_IMPORT);
$fileHandlerCreate = FileHandlerCollection::getByType(FileHandlerCollection::TYPE_CREATE);
return $this->render('@humhub/modules/content/widgets/views/wallCreateContentForm', [
'form' => $this->renderForm(),
'contentContainer' => $this->contentContainer,
@ -98,7 +94,7 @@ class WallCreateContentForm extends Widget
'submitButtonText' => $this->submitButtonText,
'defaultVisibility' => $defaultVisibility,
'canSwitchVisibility' => $canSwitchVisibility,
'fileHandlers' => array_merge($fileHandlerCreate, $fileHandlerImport),
'fileHandlers' => FileHandlerCollection::getByType([FileHandlerCollection::TYPE_IMPORT, FileHandlerCollection::TYPE_CREATE]),
]);
}

View File

@ -0,0 +1,31 @@
<?php
use humhub\libs\Html;
use humhub\modules\content\models\forms\AdminDeleteContentForm;
use humhub\modules\ui\form\widgets\ActiveForm;
/* @var $model AdminDeleteContentForm */
?>
<?php $form = ActiveForm::begin(['acknowledge' => true]); ?>
<?= $form->field($model, 'message')->textarea(['rows' => 3]) ?>
<?= $form->field($model, 'notify')->checkbox(['value' => '1', 'checked ' => true]) ?>
<?php ActiveForm::end(); ?>
<script <?= Html::nonce() ?>>
var $messageTextarea = $('#admindeletecontentform-message');
var $notifyCheckbox = $('#admindeletecontentform-notify');
humhub.modules.ui.form.elements.initCheckbox($notifyCheckbox);
$notifyCheckbox.on('change', function () {
if($notifyCheckbox.is(':checked'))
$messageTextarea.removeAttr('disabled');
else
$messageTextarea.attr('disabled', 'disabled');
});
</script>

View File

@ -1,12 +1,14 @@
<?php
use humhub\modules\ui\icon\widgets\Icon;
use yii\helpers\Url;
/* @var $this humhub\modules\ui\view\components\View */
/* @var $isAdmin boolean */
?>
<li>
<!-- load modal confirm widget -->
<a href="#" data-action-click="delete">
<a href="#" data-action-click="<?= $isAdmin ? 'adminDelete' : 'delete' ?>" data-content-delete-url="<?= $isAdmin ? Url::to(['/content/content/admin-delete']) : Url::to(['/content/content/delete']) ?>">
<?= Icon::get('delete') ?> <?= Yii::t('ContentModule.base', 'Delete') ?>
</a>
</li>

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use humhub\components\Module;
use humhub\libs\Html;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\widgets\ModuleActionButtons;
/* @var Module $module */
/* @var ContentContainerActiveRecord $contentContainer */
?>
<div class="card-panel">
<div class="card-header">
<?= Html::img($module->getImage(), [
'class' => 'media-object img-rounded',
'data-src' => 'holder.js/94x94',
'alt' => '94x94',
'style' => 'width:94px;height:94px',
]) ?>
</div>
<div class="card-body">
<div class="card-title"><?= $module->getName() ?></div>
</div>
<?= ModuleActionButtons::widget([
'module' => $module,
'contentContainer' => $contentContainer,
]) ?>
</div>

View File

@ -4,7 +4,6 @@ use humhub\libs\Html;
use humhub\modules\dashboard\widgets\DashboardContent;
use humhub\modules\dashboard\widgets\Sidebar;
use humhub\modules\space\widgets\NewSpaces;
use humhub\modules\user\widgets\NewMembers;
use humhub\widgets\FooterMenu;
?>
@ -17,11 +16,6 @@ use humhub\widgets\FooterMenu;
<div class="col-md-4 layout-sidebar-container">
<?= Sidebar::widget([
'widgets' => [
[
NewMembers::class,
['showMoreButton' => true],
['sortOrder' => 300]
],
[
NewSpaces::class,
['showMoreButton' => true],

View File

@ -8,125 +8,21 @@
namespace humhub\modules\directory;
use humhub\modules\ui\menu\MenuLink;
use Yii;
use humhub\modules\user\models\Group;
use humhub\modules\directory\permissions\AccessDirectory;
/**
* Directory Base Module
*
* The directory module adds a menu item "Directory" to the top navigation
* with some lists about spaces, users or group inside the application.
*
* @package humhub.modules_core.directory
* @since 0.5
* @deprecated since 1.9 but it can be activated temporary by console command `php yii directory/activate`
* Deprecated Directory Base Module
* @deprecated since 1.9
*/
class Module extends \humhub\components\Module
{
/**
* @inheritdoc
*/
public $isCoreModule = true;
/**
* @var string sort field (e.g. lastname) of member list
* @deprecated since 1.11 will be removed with v1.12
*/
public $isCoreModule = false;
public $memberListSortField = 'profile.lastname';
/**
* @var int default page size for directory pages
*/
public $pageSize = 25;
/**
* @var bool defines if the directory is active, if not the directory is not visible and can't be accessed
*/
public $active = false;
/**
* @var bool defines if the directory is available for guest users, this flag will only have effect if guest access is allowed and the module is active
*/
public $guestAccess = true;
/**
* @var bool show menu entry for user profile posts on directory
*/
public $showUserProfilePosts = true;
/**
* @inerhitdoc
*/
public function init()
{
parent::init();
$this->active = $this->settings->get('isActive', false);
}
/**
* @return bool checks if the current user can access the directory
*/
public function canAccess()
{
if(!$this->active) {
return false;
}
if(Yii::$app->user->isGuest) {
return $this->guestAccess;
}
return Yii::$app->user->can(AccessDirectory::class);
}
/**
* On build of the TopMenu, check if module is enabled
* When enabled add a menu item
*
* @param type $event
*/
public static function onTopMenuInit($event)
{
/** @var static $module */
$module = Yii::$app->getModule('directory');
if($module->canAccess()) {
$event->sender->addEntry(new MenuLink([
'id' => 'directory',
'icon' => 'directory',
'label' => Yii::t('DirectoryModule.base', 'Directory'),
'url' => ['/directory/directory'],
'sortOrder' => 400,
'isActive' => MenuLink::isActiveState('directory'),
]));
}
}
/**
* @inheritdoc
*/
public function getPermissions($contentContainer = null)
{
if ($this->active && !$contentContainer) {
return [
new AccessDirectory(),
];
}
return [];
}
/**
* Show groups in directory
*
* @return boolean
*/
public function isGroupListingEnabled()
{
return (Group::find()->where(['show_at_directory' => 1])->count() != 0);
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\directory\assets;
use yii\web\AssetBundle;
use yii\web\View;
/**
* Directory related assets.
*
* @author buddha
*/
class DirectoryAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@directory/resources';
/**
* @inheritdoc
*/
public $js = [
'js/humhub.directory.js'
];
/**
* @inheritdoc
*/
public $jsOptions = ['position' => View::POS_END];
/**
* @inheritdoc
*/
public $depends = [
'humhub\assets\JqueryKnobAsset'
];
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\directory\commands;
use humhub\components\SettingsManager;
use humhub\modules\directory\Module;
use Yii;
/**
* Management of activity of the deprecated module "Directory"
*
* @since 1.9
*/
class DirectoryController extends \yii\console\Controller
{
/**
* Activate the deprecated module "Directory"
*/
public function actionActivate()
{
$this->getModuleSettings()->set('isActive', true);
$this->stdout('Module "Directory" is activated.' . "\n\n");
}
/**
* Deactivate the deprecated module "Directory"
*/
public function actionDeactivate()
{
$this->getModuleSettings()->delete('isActive');
$this->stdout('Module "Directory" is deactivated.' . "\n\n");
}
protected function getModuleSettings(): SettingsManager
{
/* @var Module $module */
$module = Yii::$app->getModule('directory');
return $module->settings;
}
}

View File

@ -1,69 +0,0 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\directory\components;
use humhub\components\access\StrictAccess;
use humhub\modules\directory\Module;
use Yii;
/**
* Directory Base Controller
*
* @author luke
*/
class Controller extends \humhub\components\Controller
{
/**
* @inheritdoc
*/
public $access = StrictAccess::class;
/**
* @inheritdoc
*/
public function getAccessRules()
{
return [
['checkModuleActive']
];
}
/**
* Global access rule for the current user.
*
* @param $rule
* @param $access
* @return bool
*/
public function checkModuleActive($rule, $access)
{
/** @var Module $module */
$module = Yii::$app->getModule('directory');
if(!$module->canAccess()) {
$access->code = 403;
return false;
}
return true;
}
public function init() {
$this->appendPageTitle(\Yii::t('DirectoryModule.base', 'Directory'));
return parent::init();
}
/**
* @inheritdoc
*/
public $subLayout = "@humhub/modules/directory/views/directory/_layout";
}

Some files were not shown because too many files have changed in this diff Show More