'wcf.editor.image.float.left': '{lang}wcf.editor.image.float.left{/lang}',
'wcf.editor.image.float.right': '{lang}wcf.editor.image.float.right{/lang}',
'wcf.editor.image.source': '{lang}wcf.editor.image.source{/lang}',
+ 'wcf.editor.image.source.error.blocked': '{lang}wcf.editor.image.source.error.blocked{/lang}',
'wcf.editor.image.source.error.insecure': '{lang}wcf.editor.image.source.error.insecure{/lang}',
'wcf.editor.image.source.error.invalid': '{lang}wcf.editor.image.source.error.invalid{/lang}',
customButtons: customButtons,
forceSecureImages: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
highlighters: highlighters,
+ images: {
+ external: {if IMAGE_ALLOW_EXTERNAL_SOURCE}true{else}false{/if},
+ secureOnly: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
+ whitelist: [
+ {implode from=$__wcf->getBBCodeHandler()->getImageExternalSourceWhitelist() item=$hostname}'{$hostname|encodeJS}'{/implode}
+ ]
+ },
media: {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}true{else}false{/if},
mediaUrl: '{link controller='Media' id=-123456789 thumbnail='void' forceFrontend=true}{/link}'
}
// set code
redactor.code.start(content);
+ redactor.WoltLabImage.validateImages();
// set value
redactor.core.textarea().val(redactor.clean.onSync(redactor.$editor.html()));
else if (this.opts.woltlab.forceSecureImages && source.indexOf('http://') === 0) {
return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.insecure'));
}
+ else if (!this.WoltLabImage.isValidSource(source)) {
+ return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.blocked'));
+ }
// update image source
image.src = source;
else if (this.opts.woltlab.forceSecureImages && source.indexOf('http://') === 0) {
return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.insecure'));
}
+ else if (!this.WoltLabImage.isValidSource(source)) {
+ return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.blocked'));
+ }
// check if link is valid
var linkInput = elById('redactor-image-link');
}
}).bind(this), 1);
}
+ },
+
+ /**
+ * @param {string} src
+ */
+ isValidSource: function (src) {
+ var link = elCreate('a');
+ link.href = src;
+ // Relative links in IE might not have a hostname.
+ if (link.hostname === '') {
+ return true;
+ }
+
+
+ // Check for secure hosts only.
+ if (this.opts.woltlab.images.secureOnly && link.protocol !== 'https:') {
+ return false;
+ }
+
+ if (!this.opts.woltlab.images.external) {
+ var isValid = false;
+ var wildcards = [];
+ this.opts.woltlab.images.whitelist.forEach(function(hostname) {
+ if (hostname.indexOf('*.') === 0) {
+ wildcards.push(hostname.replace(/^\*\./, ''));
+ }
+ else if (link.hostname === hostname) {
+ isValid = true;
+ }
+ });
+
+ if (!isValid) {
+ wildcards.forEach(function(hostname) {
+ if (link.hostname.substr(hostname.length * -1) === hostname) {
+ isValid = true;
+ }
+ });
+ }
+
+ if (!isValid) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ validateImages: function() {
+ elBySelAll('img:not(.smiley):not(.woltlabAttachment)', this.core.editor()[0], (function(img) {
+ if (!this.WoltLabImage.isValidSource(img.src)) {
+ img.classList.add('editorImageBlocked');
+ }
+ }).bind(this));
}
};
};
namespace wcf\system\bbcode;
use wcf\data\bbcode\BBCode;
use wcf\data\bbcode\BBCodeCache;
+use wcf\system\application\ApplicationHandler;
use wcf\system\SingletonFactory;
+use wcf\util\ArrayUtil;
use wcf\util\JSON;
/**
public function getHighlighters() {
return array_keys($this->getHighlighterMeta());
}
+
+ /**
+ * Returns a list of hostnames that are permitted as image sources.
+ *
+ * @return string[]
+ * @since 5.2
+ */
+ public function getImageExternalSourceWhitelist() {
+ $hosts = [];
+ // Hide these hosts unless external sources are actually denied.
+ if (!IMAGE_ALLOW_EXTERNAL_SOURCE) {
+ $hosts = ArrayUtil::trim(explode("\n", IMAGE_EXTERNAL_SOURCE_WHITELIST));
+ }
+
+ foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+ $hosts[] = $application->domainName;
+ }
+
+ return array_unique($hosts);
+ }
}
min-height: 0 !important;
}
}
+
+.editorImageBlocked {
+ filter: brightness(25%);
+ transition: filter .12s linear;
+
+ &:hover {
+ filter: brightness(75%);
+ }
+}
<item name="wcf.editor.image.float.left"><![CDATA[Links]]></item>
<item name="wcf.editor.image.float.right"><![CDATA[Rechts]]></item>
<item name="wcf.editor.image.source"><![CDATA[Quelle]]></item>
+ <item name="wcf.editor.image.source.error.blocked"><![CDATA[Das Einbinden von Grafiken von Drittseiten wurde deaktiviert, bitte verwende{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} die Dateianhangs-Funktion.]]></item>
<item name="wcf.editor.image.source.error.insecure"><![CDATA[Unsichere Adressen („http://“) wurden für Bilder deaktiviert, bitte verwende{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} nur sichere Adressen („https://“).]]></item>
<item name="wcf.editor.image.source.error.invalid"><![CDATA[Der eingegebene Link ist ungültig.]]></item>
<item name="wcf.editor.link.add"><![CDATA[Link einfügen]]></item>
<item name="wcf.editor.image.float.left"><![CDATA[Left]]></item>
<item name="wcf.editor.image.float.right"><![CDATA[Right]]></item>
<item name="wcf.editor.image.source"><![CDATA[Source]]></item>
+ <item name="wcf.editor.image.source.error.blocked"><![CDATA[Images from external sources have been disabled, please use the file attachment function.]]></item>
<item name="wcf.editor.image.source.error.insecure"><![CDATA[Insecure sources (“http://”) for images have been disabled, please use secure sources only (“https://”).]]></item>
<item name="wcf.editor.image.source.error.invalid"><![CDATA[You have entered an invalid link.]]></item>
<item name="wcf.editor.link.add"><![CDATA[Insert Link]]></item>