Display confirmation message before display embedded content (#5490)

* Display confirmation message before display embedded content

* Fix confirmation of embedded content

* Optimize loading of embedded content

* New setting "Show Oembed content only after consent"

* Small fix

* Change wording

* Fix wording
This commit is contained in:
Yuriy Bakhtin 2022-01-19 21:05:09 +03:00 committed by GitHub
parent 154266f188
commit 3894b250a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 316 additions and 23 deletions

View File

@ -6,3 +6,4 @@
- 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

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

@ -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;
@ -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.
*
@ -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

@ -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;
@ -301,9 +302,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,
]);
}

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

@ -1,7 +1,13 @@
<?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;
/* @var array $providers */
/* @var OEmbedSettingsForm $settings */
?>
<?php $this->beginContent('@admin/views/setting/_advancedLayout.php') ?>
@ -22,4 +28,12 @@ use yii\helpers\Url;
<p><strong><?= Yii::t('AdminModule.settings', 'Currently no provider active!'); ?></strong></p>
<?php endif; ?>
<?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

@ -162,7 +162,8 @@ class CoreJsConfig extends Widget
]
],
'oembed' => [
'loadUrl' => Url::to(['/oembed'])
'loadUrl' => Url::to(['/oembed']),
'displayUrl' => Url::to(['/oembed/display']),
],
'ui.markdown', [
'text' => [

View File

@ -28,28 +28,58 @@ humhub.module('oembed', function(module, require, $) {
});
};
var get = function(url) {
var $result = null;
const get = function(url) {
const $result = cache[url] ? $(cache[url]) : findSnippetByUrl(url);
if (cache[url]) {
$result = $(cache[url]);
} else {
var $dom = $('[data-oembed="' + $.escapeSelector(util.string.escapeHtml(url, true)) + '"]:first');
if ($dom.length && $dom.is('[data-oembed]')) {
$result = $dom.find('.oembed_snippet').clone().show();
}
}
if($result && $result.is('.oembed_snippet')) {
if ($result && $result.is('.oembed_snippet,.oembed_confirmation')) {
return $result;
}
return null;
};
const findSnippetByUrl = function(url) {
const $dom = $('[data-oembed="' + $.escapeSelector(util.string.escapeHtml(url, true)) + '"]:first')
if (!$dom.length || !$dom.is('[data-oembed]')) {
return null;
}
const confirmation = $dom.find('.oembed_confirmation');
if (confirmation.length) {
return confirmation.clone().show();
}
return $dom.find('.oembed_snippet').clone().show();
}
const display = function(evt) {
const confirmation = evt.$trigger.closest('.oembed_confirmation');
if (!confirmation.length) {
return;
}
const data = {
url: confirmation.data('url'),
alwaysShow: confirmation.find('input[type=checkbox]:checked').length ? 1 : 0,
}
client.post(module.config.displayUrl, {data}).then(function(response) {
if (response.success) {
confirmation.after(response.content).remove();
} else {
module.log.error(response, true);
evt.finish();
}
}).catch(function(e) {
module.log.error(e, true);
evt.finish();
});
};
module.export({
load: load,
get: get
load,
get,
display,
});
});

View File

@ -20,3 +20,33 @@
width: 100%;
height: 100%;
}
.oembed_confirmation {
background: #feebb4;
border-radius: 4px;
padding: 15px;
line-height: 30px;
i.fa {
float: left;
color: #02a0b0;
background: #FFF;
border-radius: 50%;
font-size: 30px;
line-height: 25px;
margin-right: 15px;
}
> div:not(.clearfix) {
float: left;
}
.regular-checkbox-box {
top: 6px;
}
.regular-checkbox {
&:not(:checked) + .regular-checkbox-box {
background: #FFF;
}
&:checked + .regular-checkbox-box:after {
top: -8px;
}
}
}

File diff suppressed because one or more lines are too long