Added update support for styles (WIP)
authorAlexander Ebert <ebert@woltlab.com>
Tue, 14 Jul 2015 14:22:25 +0000 (16:22 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 14 Jul 2015 14:22:25 +0000 (16:22 +0200)
17 files changed:
com.woltlab.wcf/templates/wysiwyg.tpl
wcfsetup/install/files/acp/templates/codemirror.tpl
wcfsetup/install/files/acp/templates/styleAdd.tpl
wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js
wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Style/Editor.js [new file with mode: 0644]
wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php
wcfsetup/install/files/lib/acp/form/StyleEditForm.class.php
wcfsetup/install/files/lib/data/style/Style.class.php
wcfsetup/install/files/lib/data/style/StyleAction.class.php
wcfsetup/install/files/lib/data/style/StyleEditor.class.php
wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php
wcfsetup/install/files/lib/system/package/plugin/StylePackageInstallationPlugin.class.php
wcfsetup/install/files/lib/system/style/StyleHandler.class.php
wcfsetup/install/files/style/dialog.less
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml
wcfsetup/setup/db/install.sql

index ba20944c48201510f8b3f3f55dfd87b4fcc1f5fe..b051d40b4377c5bf43ed718e56cfdb570093f090 100644 (file)
@@ -7,10 +7,10 @@ var __REDACTOR_SOURCE_BBCODES = [ {implode from=$__wcf->getBBCodeHandler()->getS
 var __REDACTOR_CODE_HIGHLIGHTERS = { {implode from=$__wcf->getBBCodeHandler()->getHighlighters() item=__highlighter}'{@$__highlighter}': '{lang}wcf.bbcode.code.{@$__highlighter}.title{/lang}'{/implode} };
 var __REDACTOR_AMD_DEPENDENCIES = { };
 
-require(['Language', 'WoltLab/WCF/BBCode/FromHtml', 'WoltLab/WCF/BBCode/ToHtml'], function(Language, BBCodeFromHTML, BBCodeToHTML) {
+require(['Language', 'WoltLab/WCF/Bbcode/FromHtml', 'WoltLab/WCF/Bbcode/ToHtml'], function(Language, BbcodeFromHTML, BbcodeToHTML) {
        __REDACTOR_AMD_DEPENDENCIES = {
-               BBCodeFromHTML: BBCodeFromHTML,
-               BBCodeToHTML: BBCodeToHTML
+               BbcodeFromHTML: BbcodeFromHTML,
+               BbcodeToHTML: BbcodeToHTML
        };
        
        Language.addObject({
index a188470aa7ddd689c9c8c115bd70d529d50e8d1c..7b18d11ee3fced5f8d4ec107ab9daf67174c8387 100644 (file)
@@ -1,60 +1,63 @@
 {if !$codemirrorLoaded|isset}
+       <script data-relocate="true">window.define.amd = undefined;</script>
        <script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/codemirror.js"></script>
        <script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/addon/dialog/dialog.js"></script>
        <script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/addon/search/searchcursor.js"></script>
        <script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/addon/search/search.js"></script>
+       <script data-relocate="true">window.define.amd = window.__require_define_amd;</script>
+{/if}
+{if $codemirrorMode|isset}
+       <script data-relocate="true">window.define.amd = undefined;</script>
+       <script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/mode/{if $codemirrorMode == 'text/x-less'}css/css{else}{$codemirrorMode}/{$codemirrorMode}{/if}.js"></script>
+       <script data-relocate="true">window.define.amd = window.__require_define_amd;</script>
 {/if}
-{if $codemirrorMode|isset}<script data-relocate="true" src="{@$__wcf->getPath()}js/3rdParty/codemirror/mode/{if $codemirrorMode == 'text/x-less'}css/css{else}{$codemirrorMode}/{$codemirrorMode}{/if}.js"></script>{/if}
 {event name='javascriptIncludes'}
 
 <script data-relocate="true">
-//<![CDATA[
        {if !$codemirrorLoaded|isset}
-               $('<link rel="stylesheet" href="{@$__wcf->getPath()}js/3rdParty/codemirror/codemirror.css" />').appendTo('head');
-               $('<link rel="stylesheet" href="{@$__wcf->getPath()}js/3rdParty/codemirror/addon/dialog/dialog.css" />').appendTo('head');
+               ['{@$__wcf->getPath()}js/3rdParty/codemirror/codemirror.css', '{@$__wcf->getPath()}js/3rdParty/codemirror/addon/dialog/dialog.css'].forEach(function(href) {
+                       var link = document.createElement('link');
+                       link.rel = 'stylesheet';
+                       link.href = href;
+                       document.head.appendChild(link);
+               });
        {/if}
        
-       $(function() {
-               var $elements = $('{@$codemirrorSelector|encodeJS}');
-               var $config = {
-                       {if $codemirrorMode|isset}mode: '{@$codemirrorMode|encodeJS}',{/if}
-                       lineWrapping: true,
-                       indentWithTabs: true,
-                       lineNumbers: true,
-                       indentUnit: 4
-               };
+       var elements = document.querySelectorAll('{@$codemirrorSelector|encodeJS}');
+       var config = {
+               {if $codemirrorMode|isset}mode: '{@$codemirrorMode|encodeJS}',{/if}
+               lineWrapping: true,
+               indentWithTabs: true,
+               lineNumbers: true,
+               indentUnit: 4,
+               readOnly: {if !$editable|isset || $editable}false{else}true{/if}
+       };
+       
+       [].forEach.call(elements, function(element) {
+               {event name='javascriptInit'}
                
-               for (var $i = 0; $i < $elements.length; $i++) {
-                       (function () {
-                               var $element = $elements[$i];
-                               
-                               {event name='javascriptInit'}
-                               
-                               if ($element.codemirror) {
-                                       for (var name in $config) {
-                                               if (!$config.hasOwnProperty($name)) continue;
-                                               
-                                               $element.codemirror.setOption($name, $config[$name]);
-                                       }
-                               }
-                               else {
-                                       $element.codemirror = CodeMirror.fromTextArea($element, $config);
-                                       var oldToTextArea = $element.codemirror.toTextArea;
-                                       $element.codemirror.toTextArea = function() {
-                                               oldToTextArea();
-                                               $element.codemirror = null;
-                                       };
+               if (element.codemirror) {
+                       for (var key in config) {
+                               if (config.hasOwnProperty(key)) {
+                                       element.codemirror.setOption(key, config[key]);
                                }
-                               
-                               setTimeout(function () {
-                                       $element.codemirror.refresh();
-                               }, 250);
-                               setTimeout(function () {
-                                       $element.codemirror.refresh();
-                               }, 1000);
-                       })();
+                       }
                }
+               else {
+                       element.codemirror = CodeMirror.fromTextArea(element, config);
+                       var oldToTextArea = element.codemirror.toTextArea;
+                       element.codemirror.toTextArea = function() {
+                               oldToTextArea();
+                               element.codemirror = null;
+                       };
+               }
+               
+               setTimeout(function () {
+                       element.codemirror.refresh();
+               }, 250);
+               setTimeout(function () {
+                       element.codemirror.refresh();
+               }, 1000);
        });
-//]]>
 </script>
 {assign var='codemirrorLoaded' value=true}
index 23da836782f4f2eb03d2d385ab8a8d85156d7d05..cc85a392a5a2342ec1e27e0056e8fbfa7d6a0d47 100644 (file)
@@ -1,31 +1,17 @@
 {include file='header' pageTitle='wcf.acp.style.'|concat:$action}
 
-<script data-relocate="true" src="{@$__wcf->getPath()}acp/js/WCF.ACP.Style.js?v={@LAST_UPDATE_TIME}"></script>
-<script data-relocate="true" src="{@$__wcf->getPath()}js/WCF.ColorPicker.js?v={@LAST_UPDATE_TIME}"></script>
+{js application='wcf' acp='true' file='WCF.ACP.Style'}
+{js application='wcf' file='WCF.ColorPicker' bundle='WCF.Combined'}
 <script data-relocate="true">
-       //<![CDATA[
+       require(['WoltLab/WCF/Acp/Ui/Style/Editor'], function(AcpUiStyleEditor) {
+               AcpUiStyleEditor.setup({
+                       isTainted: {if $isTainted}true{else}false{/if},
+                       styleId: {if $action === 'edit'}{@$style->styleID}{else}0{/if}
+               });
+       });
+       
        $(function() {
                new WCF.ColorPicker('.jsColorPicker');
-               WCF.TabMenu.init();
-               
-               var $useFluidLayout = $('#useFluidLayout');
-               var $fluidLayoutMinWidth = $('#fluidLayoutMinWidth');
-               var $fluidLayoutMaxWidth = $('#fluidLayoutMaxWidth');
-               var $fixedLayoutVariables = $('#fixedLayoutVariables');
-               function useFluidLayout() {
-                       if ($useFluidLayout.is(':checked')) {
-                               $fluidLayoutMinWidth.show();
-                               $fluidLayoutMaxWidth.show();
-                               $fixedLayoutVariables.hide();
-                       }
-                       else {
-                               $fluidLayoutMinWidth.hide();
-                               $fluidLayoutMaxWidth.hide();
-                               $fixedLayoutVariables.show();
-                       }
-               }
-               $useFluidLayout.change(useFluidLayout);
-               useFluidLayout();
                
                WCF.Language.addObject({
                        'wcf.style.colorPicker': '{lang}wcf.style.colorPicker{/lang}',
                        var $target = $(event.currentTarget);
                        $target.prev().attr('step', ($target.val() == 'em' ? '0.01' : '1'));
                }).trigger('change');
-               
-               $('.tabMenuContainer').on('wcftabsactivate', function (event, ui) {
-                       if (ui.newPanel.selector !== '#advanced') return;
-
-                       setTimeout(function() {
-                               $('#individualLess')[0].codemirror.refresh();
-                               $('#overrideLess')[0].codemirror.refresh();
-                       }, 10);
-               });
        });
-       //]]>
 </script>
 <header class="boxHeadline">
        <h1>{lang}wcf.acp.style.{$action}{/lang}</h1>
@@ -87,7 +63,7 @@
 </div>
 
 <form method="post" action="{if $action == 'add'}{link controller='StyleAdd'}{/link}{else}{link controller='StyleEdit' id=$styleID}{/link}{/if}">
-       <div class="tabMenuContainer" data-active="{$activeTabMenuItem}" data-store="activeTabMenuItem">
+       <div class="tabMenuContainer" data-active="{$activeTabMenuItem}" data-store="activeTabMenuItem" id="styleTabMenuContainer">
                <nav class="tabMenu">
                        <ul>
                                <li><a href="{@$__wcf->getAnchor('general')}">{lang}wcf.acp.style.general{/lang}</a></li>
                                <dl{if $errorField == 'authorName'} class="formError"{/if}>
                                        <dt><label for="authorName">{lang}wcf.acp.style.authorName{/lang}</label></dt>
                                        <dd>
-                                               <input type="text" name="authorName" id="authorName" value="{$authorName}" class="long" />
+                                               <input type="text" name="authorName" id="authorName" value="{$authorName}" class="long"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'authorName'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                <dl{if $errorField == 'copyright'} class="formError"{/if}>
                                        <dt><label for="copyright">{lang}wcf.acp.style.copyright{/lang}</label></dt>
                                        <dd>
-                                               <input type="text" name="copyright" id="copyright" value="{$copyright}" class="long" />
+                                               <input type="text" name="copyright" id="copyright" value="{$copyright}" class="long"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'copyright'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                <dl{if $errorField == 'styleVersion'} class="formError"{/if}>
                                        <dt><label for="styleVersion">{lang}wcf.acp.style.styleVersion{/lang}</label></dt>
                                        <dd>
-                                               <input type="text" name="styleVersion" id="styleVersion" value="{$styleVersion}" class="small" />
+                                               <input type="text" name="styleVersion" id="styleVersion" value="{$styleVersion}" class="small"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'styleVersion'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                <dl{if $errorField == 'styleDate'} class="formError"{/if}>
                                        <dt><label for="styleDate">{lang}wcf.acp.style.styleDate{/lang}</label></dt>
                                        <dd>
-                                               <input type="date" name="styleDate" id="styleDate" value="{$styleDate}" class="small" />
+                                               <input type="date" name="styleDate" id="styleDate" value="{$styleDate}" class="small"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'styleDate'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                <dl{if $errorField == 'license'} class="formError"{/if}>
                                        <dt><label for="license">{lang}wcf.acp.style.license{/lang}</label></dt>
                                        <dd>
-                                               <input type="text" name="license" id="license" value="{$license}" class="long" />
+                                               <input type="text" name="license" id="license" value="{$license}" class="long"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'license'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                <dl{if $errorField == 'authorURL'} class="formError"{/if}>
                                        <dt><label for="authorURL">{lang}wcf.acp.style.authorURL{/lang}</label></dt>
                                        <dd>
-                                               <input type="text" name="authorURL" id="authorURL" value="{$authorURL}" class="long" />
+                                               <input type="text" name="authorURL" id="authorURL" value="{$authorURL}" class="long"{if !$isTainted} readonly{/if} />
                                                {if $errorField == 'authorURL'}
                                                        <small class="innerError">
                                                                {if $errorType == 'empty'}
                                                {/if}
                                        </dd>
                                </dl>
+                               <dl{if $errorField == 'packageName'} class="formError"{/if}>
+                                       <dt><label for="packageName">{lang}wcf.acp.style.packageName{/lang}</label></dt>
+                                       <dd>
+                                               <input type="text" name="packageName" id="packageName" value="{$packageName}" class="long"{if !$isTainted} readonly{/if} />
+                                               {if $errorField == 'packageName'}
+                                                       <small class="innerError">{lang}wcf.acp.style.packageName.error.{$errorType}{/lang}</small>
+                                               {/if}
+                                       </dd>
+                               </dl>
                                <dl{if $errorField == 'styleDescription'} class="formError"{/if}>
                                        <dt><label for="styleDescription">{lang}wcf.acp.style.styleDescription{/lang}</label></dt>
                                        <dd>
                </div>
                
                {* advanced *}
-               <div id="advanced" class="container containerPadding tabMenuContent">
+               <div id="advanced" class="container containerPadding tabMenuContainer tabMenuContent">
+                       {if !$isTainted}
+                               <nav class="menu">
+                                       <ul>
+                                               <li data-name="advanced-custom"><a href="{@$__wcf->getAnchor('advanced-custom')}">{lang}wcf.acp.style.advanced.custom{/lang}</a></li>
+                                               <li data-name="advanced-original"><a href="{@$__wcf->getAnchor('advanced-original')}">{lang}wcf.acp.style.advanced.original{/lang}</a></li>
+                                       </ul>
+                               </nav>
+                               
+                               <p class="info">{lang}wcf.acp.style.protected{/lang}</p>
+                               
+                               {* custom declarations *}
+                               <div id="advanced-custom">
+                                       <fieldset class="marginTop">
+                                               <legend>{lang}wcf.acp.style.advanced.individualLess{/lang}</legend>
+                                               
+                                               <dl class="wide">
+                                                       <dd>
+                                                               <textarea id="individualLessCustom" rows="20" cols="40" name="individualLessCustom">{$variables[individualLessCustom]}</textarea>
+                                                               <small>{lang}wcf.acp.style.advanced.individualLess.description{/lang}</small>
+                                                       </dd>
+                                               </dl>
+                                       </fieldset>
+                                       
+                                       <fieldset{if $errorField == 'overrideLessCustom'} class="formError"{/if}>
+                                               <legend>{lang}wcf.acp.style.advanced.overrideLess{/lang}</legend>
+                                               
+                                               <dl class="wide">
+                                                       <dd>
+                                                               <textarea id="overrideLessCustom" rows="20" cols="40" name="overrideLessCustom">{$variables[overrideLessCustom]}</textarea>
+                                                               {if $errorField == 'overrideLessCustom'}
+                                                                       <small class="innerError">
+                                                                               {lang}wcf.acp.style.advanced.overrideLess.error{/lang}
+                                                                               {implode from=$errorType item=error}{lang}wcf.acp.style.advanced.overrideLess.error.{$error.error}{/lang}{/implode}
+                                                                       </small>
+                                                               {/if}
+                                                               <small>{lang}wcf.acp.style.advanced.overrideLess.description{/lang}</small>
+                                                       </dd>
+                                               </dl>
+                                       </fieldset>
+                                       {include file='codemirror' codemirrorMode='text/x-less' codemirrorSelector='#individualLessCustom, #overrideLessCustom'}
+                                       
+                                       {event name='syntaxFieldsetsCustom'}
+                               </div>
+                               
+                               {* original declarations / tainted style *}
+                               <div id="advanced-original">
+                       {/if}
+                       
                        <fieldset class="marginTop">
-                               <legend>{lang}wcf.acp.style.advanced.individualLess{/lang}</legend>
+                               <legend>{lang}wcf.acp.style.advanced.individualLess{/lang}{if !$isTainted} ({lang}wcf.acp.style.protected.less{/lang}){/if}</legend>
                                
                                <dl class="wide">
                                        <dd>
                        </fieldset>
                        
                        <fieldset{if $errorField == 'overrideLess'} class="formError"{/if}>
-                               <legend>{lang}wcf.acp.style.advanced.overrideLess{/lang}</legend>
+                               <legend>{lang}wcf.acp.style.advanced.overrideLess{/lang}{if !$isTainted} ({lang}wcf.acp.style.protected.less{/lang}){/if}</legend>
                                
                                <dl class="wide">
                                        <dd>
                                        </dd>
                                </dl>
                        </fieldset>
-                       {include file='codemirror' codemirrorMode='text/x-less' codemirrorSelector='#individualLess, #overrideLess'}
+                       {include file='codemirror' codemirrorMode='text/x-less' codemirrorSelector='#individualLess, #overrideLess' editable=$isTainted}
+                       
+                       {event name='syntaxFieldsetsOriginal'}
                        
-                       {event name='syntaxFieldsets'}
+                       {if !$isTainted}
+                               </div>
+                       {/if}
                </div>
                
                {event name='tabMenuContents'}
        </div>
 </form>
 
+<div id="styleDisableProtection" class="jsStaticDialogContent" data-title="{lang}wcf.acp.style.protected.title{/lang}">
+       <p>{lang}wcf.acp.style.protected.description{/lang}</p>
+       
+       <dl class="marginTop">
+               <dt></dt>
+               <dd><label for="styleDisableProtectionConfirm"><input type="checkbox" id="styleDisableProtectionConfirm"> {lang}wcf.acp.style.protected.confirm{/lang}</label></dd>
+       </dl>
+       
+       <div class="formSubmit">
+               <button id="styleDisableProtectionSubmit" disabled>{lang}wcf.global.button.submit{/lang}</button>
+       </div>
+</div>
+
 {include file='footer'}
index 9af01197be02e4b2f2bd8cb5e12705a4656b3c40..3ab34726c1a8ed90a1eabc86067bec26d63e69ec 100644 (file)
@@ -303,7 +303,7 @@ RedactorPlugins.wbbcode = function() {
                        /** @deprecated legacy event */
                        WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'convertFromHtml', obj);
                        
-                       obj.html = __REDACTOR_AMD_DEPENDENCIES.BBCodeFromHTML.convert(obj.html);
+                       obj.html = __REDACTOR_AMD_DEPENDENCIES.BbcodeFromHTML.convert(obj.html);
                        
                        /** @deprecated legacy event */
                        WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertFromHtml', obj);
@@ -322,7 +322,7 @@ RedactorPlugins.wbbcode = function() {
                        /** @deprecated legacy event */
                        WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'beforeConvertToHtml', obj);
                        
-                       obj.data = __REDACTOR_AMD_DEPENDENCIES.BBCodeToHTML.convert(obj.data, {
+                       obj.data = __REDACTOR_AMD_DEPENDENCIES.BbcodeToHTML.convert(obj.data, {
                                attachments: {
                                        images: this.wbbcode._getImageAttachments(),
                                        thumbnailUrl: this.wutil.getOption('woltlab.attachmentThumbnailUrl'),
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Style/Editor.js b/wcfsetup/install/files/js/WoltLab/WCF/Acp/Ui/Style/Editor.js
new file mode 100644 (file)
index 0000000..c979a76
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * Provides the basic core functionality.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Core
+ */
+define(['Ajax', 'EventHandler'], function(Ajax, EventHandler) {
+       "use strict";
+       
+       /**
+        * @module      WoltLab/WCF/Acp/Ui/Style/Editor
+        */
+       var AcpUiStyleEditor = {
+               /**
+                * Sets up dynamic style options.
+                */
+               setup: function(options) {
+                       this._handleLayoutWidth();
+                       this._handleLess(options.isTainted);
+                       
+                       if (!options.isTainted) {
+                               this._handleProtection(options.styleId);
+                       }
+               },
+               
+               /**
+                * Handles the switch between static and fluid layout.
+                */
+               _handleLayoutWidth: function() {
+                       var useFluidLayout = elById('useFluidLayout');
+                       var fluidLayoutMinWidth = elById('fluidLayoutMinWidth');
+                       var fluidLayoutMaxWidth = elById('fluidLayoutMaxWidth');
+                       var fixedLayoutVariables = elById('fixedLayoutVariables');
+                       
+                       function change() {
+                               var checked = useFluidLayout.checked;
+                               
+                               fluidLayoutMinWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
+                               fluidLayoutMaxWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
+                               fixedLayoutVariables.style[(checked ? 'set' : 'remove') + 'Property']('display', 'none');
+                       }
+                       
+                       useFluidLayout.addEventListener('change', change);
+                       
+                       change();
+               },
+               
+               /**
+                * Handles LESS input fields.
+                * 
+                * @param       {boolean}       isTainted       false if style is in protected mode
+                */
+               _handleLess: function(isTainted) {
+                       var individualLess = elById('individualLess');
+                       var overrideLess = elById('overrideLess');
+                       
+                       if (isTainted) {
+                               EventHandler.add('com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer', 'select', function(data) {
+                                       individualLess.codemirror.refresh();
+                                       overrideLess.codemirror.refresh();
+                               });
+                       }
+                       else {
+                               EventHandler.add('com.woltlab.wcf.simpleTabMenu_advanced', 'select', function(data) {
+                                       if (data.activeName === 'advanced-custom') {
+                                               elById('individualLessCustom').codemirror.refresh();
+                                               elById('overrideLessCustom').codemirror.refresh();
+                                       }
+                                       else if (data.activeName === 'advanced-original') {
+                                               individualLess.codemirror.refresh();
+                                               overrideLess.codemirror.refresh();
+                                       }
+                               });
+                       }
+               },
+               
+               _handleProtection: function(styleId) {
+                       var button = elById('styleDisableProtectionSubmit');
+                       var checkbox = elById('styleDisableProtectionConfirm');
+                       
+                       checkbox.addEventListener('change', function() {
+                               button.disabled = !checkbox.checked;
+                       });
+                       
+                       button.addEventListener('click', function() {
+                               Ajax.apiOnce({
+                                       data: {
+                                               actionName: 'markAsTainted',
+                                               className: 'wcf\\data\\style\\StyleAction',
+                                               objectIDs: [styleId]
+                                       },
+                                       success: function() {
+                                               window.location.reload();
+                                       }
+                               });
+                       });
+               }
+       };
+       
+       return AcpUiStyleEditor;
+});
index 52dafe30e7d264a28078a5b291bcf179d013bf2a..6e864901494a9dc776154c5618db4adaed87a2f7 100644 (file)
@@ -99,6 +99,12 @@ class StyleAddForm extends AbstractForm {
         */
        public $imagePath = 'images/';
        
+       /**
+        * tainted style
+        * @var boolean
+        */
+       public $isTainted = true;
+       
        /**
         * license name
         * @var string
@@ -110,6 +116,12 @@ class StyleAddForm extends AbstractForm {
         */
        public $neededPermissions = array('admin.style.canManageStyle');
        
+       /**
+        * style package name
+        * @var string
+        */
+       public $packageName = '';
+       
        /**
         * last change date
         * @var string
@@ -225,6 +237,7 @@ class StyleAddForm extends AbstractForm {
                if (isset($_POST['copyright'])) $this->copyright = StringUtil::trim($_POST['copyright']);
                if (isset($_POST['imagePath'])) $this->imagePath = StringUtil::trim($_POST['imagePath']);
                if (isset($_POST['license'])) $this->license = StringUtil::trim($_POST['license']);
+               if (isset($_POST['packageName'])) $this->packageName = StringUtil::trim($_POST['packageName']);
                if (isset($_POST['styleDate'])) $this->styleDate = StringUtil::trim($_POST['styleDate']);
                if (isset($_POST['styleDescription'])) $this->styleDescription = StringUtil::trim($_POST['styleDescription']);
                if (isset($_POST['styleName'])) $this->styleName = StringUtil::trim($_POST['styleName']);
@@ -267,6 +280,18 @@ class StyleAddForm extends AbstractForm {
                        throw new UserInputException('styleVersion', 'notValid');
                }
                
+               // validate style package name
+               if (!empty($this->packageName)) {
+                       if (!Package::isValidPackageName($this->packageName)) {
+                               throw new UserInputException('packageName', 'notValid');
+                       }
+                       
+                       // 3rd party styles may never have com.woltlab.* as name
+                       if (strpos($this->packageName, 'com.woltlab.') === 0) {
+                               throw new UserInputException('packageName', 'reserved');
+                       }
+               }
+               
                // validate style description
                if (!I18nHandler::getInstance()->validateValue('styleDescription', true, true)) {
                        throw new UserInputException('styleDescription');
@@ -473,7 +498,9 @@ class StyleAddForm extends AbstractForm {
                        'data' => array_merge($this->additionalFields, array(
                                'styleName' => $this->styleName,
                                'templateGroupID' => $this->templateGroupID,
+                               'packageName' => $this->packageName,
                                'isDisabled' => 1, // styles are disabled by default
+                               'isTainted' => 1,
                                'styleDescription' => '',
                                'styleVersion' => $this->styleVersion,
                                'styleDate' => $this->styleDate,
@@ -501,10 +528,11 @@ class StyleAddForm extends AbstractForm {
                $this->saved();
                
                // reset variables
-               $this->authorName = $this->authorURL = $this->copyright = $this->image = '';
+               $this->authorName = $this->authorURL = $this->copyright = $this->packageName = $this->image = '';
                $this->license = $this->styleDate = $this->styleDescription = $this->styleName = $this->styleVersion = '';
                
                $this->imagePath = 'images/';
+               $this->isTainted = true;
                $this->templateGroupID = 0;
                
                I18nHandler::getInstance()->reset();
@@ -532,7 +560,9 @@ class StyleAddForm extends AbstractForm {
                        'availableUnits' => $this->availableUnits,
                        'copyright' => $this->copyright,
                        'imagePath' => $this->imagePath,
+                       'isTainted' => $this->isTainted,
                        'license' => $this->license,
+                       'packageName' => $this->packageName,
                        'styleDate' => $this->styleDate,
                        'styleDescription' => $this->styleDescription,
                        'styleName' => $this->styleName,
index ef6e32bc00a1ed103920324979cc0a26051a960d..8b49c403fe35fdfc2dcfdb497e68e4030cb13323 100644 (file)
@@ -61,6 +61,28 @@ class StyleEditForm extends StyleAddForm {
                        }
                }
                unset($variableValue);
+               
+               if (!$this->style->isTainted) {
+                       $tmp = Style::splitLessVariables($this->variables['individualLess']);
+                       $this->variables['individualLess'] = $tmp['original'];
+                       $this->variables['individualLessCustom'] = $tmp['custom'];
+                       
+                       $tmp = Style::splitLessVariables($this->variables['overrideLess']);
+                       $this->variables['overrideLess'] = $tmp['original'];
+                       $this->variables['overrideLessCustom'] = $tmp['custom'];
+               }
+       }
+       
+       /**
+        * @see \wcf\acp\form\StyleAddForm::setVariables()
+        */
+       protected function setVariables() {
+               parent::setVariables();
+               
+               if (!$this->style->isTainted) {
+                       $this->specialVariables[] = 'individualLessCustom';
+                       $this->specialVariables[] = 'overrideLessCustom';
+               }
        }
        
        /**
@@ -76,7 +98,9 @@ class StyleEditForm extends StyleAddForm {
                        $this->authorURL = $this->style->authorURL;
                        $this->copyright = $this->style->copyright;
                        $this->imagePath = $this->style->imagePath;
+                       $this->isTainted = $this->style->isTainted;
                        $this->license = $this->style->license;
+                       $this->packageName = $this->style->packageName;
                        $this->styleDate = $this->style->styleDate;
                        $this->styleDescription = $this->style->styleDescription;
                        $this->styleName = $this->style->styleName;
@@ -91,6 +115,14 @@ class StyleEditForm extends StyleAddForm {
        public function save() {
                AbstractForm::save();
                
+               if (!$this->style->isTainted) {
+                       $this->variables['individualLess'] = Style::joinLessVariables($this->variables['individualLess'], $this->variables['individualLessCustom']);
+                       $this->variables['overrideLess'] = Style::joinLessVariables($this->variables['overrideLess'], $this->variables['overrideLessCustom']);
+                       
+                       unset($this->variables['individualLessCustom']);
+                       unset($this->variables['overrideLessCustom']);
+               }
+               
                $this->objectAction = new StyleAction(array($this->style), 'update', array(
                        'data' => array_merge($this->additionalFields, array(
                                'styleName' => $this->styleName,
@@ -99,6 +131,7 @@ class StyleEditForm extends StyleAddForm {
                                'styleDate' => $this->styleDate,
                                'imagePath' => $this->imagePath,
                                'copyright' => $this->copyright,
+                               'packageName' => $this->packageName,
                                'license' => $this->license,
                                'authorName' => $this->authorName,
                                'authorURL' => $this->authorURL
index cb6d15180a6e4eb0971ce67f12e3670d07ed8274..81444adf533f9d23a210ee7b3ba844b23b41161c 100644 (file)
@@ -115,4 +115,21 @@ class Style extends DatabaseObject {
                
                return WCF::getPath().'images/stylePreview.png';
        }
+       
+       public static function splitLessVariables($variables) {
+               $tmp = explode("/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n", $variables, 2);
+               
+               return [
+                       'preset' => $tmp[0],
+                       'custom' => (isset($tmp[1])) ? $tmp[1] : ''
+               ];
+       }
+       
+       public static function joinLessVariables($preset, $custom) {
+               if (empty($custom)) {
+                       return $preset;
+               }
+               
+               return $preset . "/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n" . $custom;
+       }
 }
index f16eb2b68c29eb6fd13ab820135b6c78fa4c29d7..1aa36b8ea1fccf963629cf88c59002b61cf279ec 100644 (file)
@@ -14,6 +14,7 @@ use wcf\system\upload\DefaultUploadFileValidationStrategy;
 use wcf\system\Regex;
 use wcf\system\WCF;
 use wcf\util\FileUtil;
+use wcf\util\StringUtil;
 
 /**
  * Executes style-related actions.
@@ -49,7 +50,7 @@ class StyleAction extends AbstractDatabaseObjectAction implements IToggleAction
        /**
         * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
         */
-       protected $requireACP = array('copy', 'delete', 'setAsDefault', 'toggle', 'update', 'upload', 'uploadLogo');
+       protected $requireACP = array('copy', 'delete', 'markAsTainted', 'setAsDefault', 'toggle', 'update', 'upload', 'uploadLogo');
        
        /**
         * style object
@@ -633,4 +634,24 @@ class StyleAction extends AbstractDatabaseObjectAction implements IToggleAction
                        'template' => WCF::getTPL()->fetch('styleChooser')
                );
        }
+       
+       public function validateMarkAsTainted() {
+               if (!WCF::getSession()->getPermission('admin.style.canManageStyle')) {
+                       throw new PermissionDeniedException();
+               }
+               
+               $this->styleEditor = $this->getSingleObject();
+       }
+       
+       public function markAsTainted() {
+               // merge definitions
+               $variables = $this->styleEditor->getVariables();
+               $variables['individualLess'] = str_replace("/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n", '', $variables['individualLess']);
+               $variables['overrideLess'] = str_replace("/* WCF_STYLE_CUSTOM_USER_MODIFICATIONS */\n", '', $variables['overrideLess']);
+               $this->styleEditor->setVariables($variables);
+               
+               $this->styleEditor->update([
+                       'isTainted' => 1
+               ]);
+       }
 }
index fb567af891fa9b0a5fcd11ec6402b0f5bef9635d..aafb03defd919504f67c40201e7f356e4e258f13 100644 (file)
@@ -298,7 +298,7 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                // get style data
                $data = self::readStyleData($tar);
                
-               $styleData = array(
+               $styleData = [
                        'styleName' => $data['name'],
                        'variables' => $data['variables'],
                        'styleVersion' => $data['version'],
@@ -307,54 +307,7 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                        'license' => $data['license'],
                        'authorName' => $data['authorName'],
                        'authorURL' => $data['authorURL']
-               );
-               
-               // create template group
-               if (!empty($data['templates'])) {
-                       $templateGroupName = $originalTemplateGroupName = $data['name'];
-                       $templateGroupFolderName = preg_replace('/[^a-z0-9_-]/i', '', $templateGroupName);
-                       if (empty($templateGroupFolderName)) $templateGroupFolderName = 'generic'.mb_substr(StringUtil::getRandomID(), 0, 8);
-                       $originalTemplateGroupFolderName = $templateGroupFolderName;
-                       
-                       // get unique template group name
-                       $i = 1;
-                       while (true) {
-                               $sql = "SELECT  COUNT(*) AS count
-                                       FROM    wcf".WCF_N."_template_group
-                                       WHERE   templateGroupName = ?";
-                               $statement = WCF::getDB()->prepareStatement($sql);
-                               $statement->execute(array($templateGroupName));
-                               $row = $statement->fetchArray();
-                               if (!$row['count']) break;
-                               $templateGroupName = $originalTemplateGroupName . '_' . $i;
-                               $i++;
-                       }
-                       
-                       // get unique folder name
-                       $i = 1;
-                       while (true) {
-                               $sql = "SELECT  COUNT(*) AS count
-                                       FROM    wcf".WCF_N."_template_group
-                                       WHERE   templateGroupFolderName = ?";
-                               $statement = WCF::getDB()->prepareStatement($sql);
-                               $statement->execute(array(
-                                       FileUtil::addTrailingSlash($templateGroupFolderName)
-                               ));
-                               $row = $statement->fetchArray();
-                               if (!$row['count']) break;
-                               $templateGroupFolderName = $originalTemplateGroupFolderName . '_' . $i;
-                               $i++;
-                       }
-                       
-                       $templateGroupAction = new TemplateGroupAction(array(), 'create', array(
-                               'data' => array(
-                                       'templateGroupName' => $templateGroupName,
-                                       'templateGroupFolderName' => FileUtil::addTrailingSlash($templateGroupFolderName)
-                               )
-                       ));
-                       $returnValues = $templateGroupAction->executeAction();
-                       $styleData['templateGroupID'] = $returnValues['returnValues']->templateGroupID;
-               }
+               ];
                
                // import images
                if (!empty($data['images']) && $data['imagesPath'] != 'images/') {
@@ -384,8 +337,61 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                        }
                }
                
-               // import templates
+               // handle templates
                if (!empty($data['templates'])) {
+                       $templateGroupFolderName = '';
+                       if ($style !== null && $style->templateGroupID) {
+                               $templateGroupFolderName = (new TemplateGroup($style->templateGroupID))->templateGroupFolderName;
+                       }
+                       
+                       if (empty($templateGroupFolderName)) {
+                               // create template group
+                               $templateGroupName = $originalTemplateGroupName = $data['name'];
+                               $templateGroupFolderName = preg_replace('/[^a-z0-9_-]/i', '', $templateGroupName);
+                               if (empty($templateGroupFolderName)) $templateGroupFolderName = 'generic'.mb_substr(StringUtil::getRandomID(), 0, 8);
+                               $originalTemplateGroupFolderName = $templateGroupFolderName;
+                                       
+                               // get unique template group name
+                               $i = 1;
+                               while (true) {
+                                       $sql = "SELECT  COUNT(*) AS count
+                                               FROM    wcf".WCF_N."_template_group
+                                               WHERE   templateGroupName = ?";
+                                       $statement = WCF::getDB()->prepareStatement($sql);
+                                       $statement->execute([$templateGroupName]);
+                                       $row = $statement->fetchArray();
+                                       if (!$row['count']) break;
+                                       $templateGroupName = $originalTemplateGroupName . '_' . $i;
+                                       $i++;
+                               }
+                                       
+                               // get unique folder name
+                               $i = 1;
+                               while (true) {
+                                       $sql = "SELECT  COUNT(*) AS count
+                                               FROM    wcf".WCF_N."_template_group
+                                               WHERE   templateGroupFolderName = ?";
+                                       $statement = WCF::getDB()->prepareStatement($sql);
+                                       $statement->execute([
+                                               FileUtil::addTrailingSlash($templateGroupFolderName)
+                                       ]);
+                                       $row = $statement->fetchArray();
+                                       if (!$row['count']) break;
+                                       $templateGroupFolderName = $originalTemplateGroupFolderName . '_' . $i;
+                                       $i++;
+                               }
+                                       
+                               $templateGroupAction = new TemplateGroupAction([], 'create', [
+                                       'data' => [
+                                               'templateGroupName' => $templateGroupName,
+                                               'templateGroupFolderName' => FileUtil::addTrailingSlash($templateGroupFolderName)
+                                       ]
+                               ]);
+                               $returnValues = $templateGroupAction->executeAction();
+                               $styleData['templateGroupID'] = $returnValues['returnValues']->templateGroupID;
+                       }
+                       
+                       // import templates
                        $index = $tar->getIndexByFilename($data['templates']);
                        if ($index !== false) {
                                // extract templates tar
@@ -395,15 +401,27 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                                // open templates tar and group templates by package
                                $templatesTar = new Tar($destination);
                                $contentList = $templatesTar->getContentList();
-                               $packageToTemplates = array();
+                               $packageToTemplates = [];
                                foreach ($contentList as $val) {
                                        if ($val['type'] == 'file') {
                                                $folders = explode('/', $val['filename']);
                                                $packageName = array_shift($folders);
                                                if (!isset($packageToTemplates[$packageName])) {
-                                                       $packageToTemplates[$packageName] = array();
+                                                       $packageToTemplates[$packageName] = [];
                                                }
-                                               $packageToTemplates[$packageName][] = array('index' => $val['index'], 'filename' => implode('/', $folders));
+                                               $packageToTemplates[$packageName][] = ['index' => $val['index'], 'filename' => implode('/', $folders)];
+                                       }
+                               }
+                               
+                               $knownTemplates = [];
+                               if ($style !== null && $style->templateGroupID) {
+                                       $sql = "SELECT  templateName
+                                               FROM    wcf".WCF_N."_template
+                                               WHERE   templateGroupID = ?";
+                                       $statement = WCF::getDB()->prepareStatement($sql);
+                                       $statement->execute([$style->templateGroupID]);
+                                       while ($row = $statement->fetchArray()) {
+                                               $knownTemplates[] = $row['templateName'];
                                        }
                                }
                                
@@ -415,10 +433,10 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                                                WHERE   package = ?
                                                        AND isApplication = ?";
                                        $statement = WCF::getDB()->prepareStatement($sql);
-                                       $statement->execute(array(
+                                       $statement->execute([
                                                $package,
                                                1
-                                       ));
+                                       ]);
                                        while ($row = $statement->fetchArray()) {
                                                // get template path
                                                $templatesDir = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR.$row['packageDir']).'templates/'.$templateGroupFolderName);
@@ -433,12 +451,15 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                                                foreach ($templates as $template) {
                                                        $templatesTar->extract($template['index'], $templatesDir.$template['filename']);
                                                        
-                                                       TemplateEditor::create(array(
-                                                               'application' => Package::getAbbreviation($package),
-                                                               'packageID' => $row['packageID'],
-                                                               'templateName' => str_replace('.tpl', '', $template['filename']),
-                                                               'templateGroupID' => $styleData['templateGroupID']
-                                                       ));
+                                                       $templateName = str_replace('.tpl', '', $template['filename']);
+                                                       if (!in_array($templateName, $knownTemplates)) {
+                                                               TemplateEditor::create([
+                                                                       'application' => Package::getAbbreviation($package),
+                                                                       'packageID' => $row['packageID'],
+                                                                       'templateName' => $templateName,
+                                                                       'templateGroupID' => $styleData['templateGroupID']
+                                                               ]);
+                                                       }
                                                }
                                        }
                                }
@@ -450,40 +471,52 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject
                }
                
                // save style
-               if ($style !== null) {
-                       $style->update($styleData);
-               }
-               else {
+               if ($style === null) {
                        $styleData['packageID'] = $packageID;
                        $style = new StyleEditor(self::create($styleData));
-               }
-               
-               // import preview image
-               if (!empty($data['image'])) {
-                       $fileExtension = mb_substr($data['image'], mb_strrpos($data['image'], '.'));
-                       $index = $tar->getIndexByFilename($data['image']);
-                       if ($index !== false) {
-                               $filename = WCF_DIR.'images/stylePreview-'.$style->styleID.$fileExtension;
-                               $tar->extract($index, $filename);
-                               FileUtil::makeWritable($filename);
-                               
-                               if (file_exists($filename)) {
-                                       $style->update(array('image' => 'stylePreview-'.$style->styleID.$fileExtension));
+                       
+                       // import preview image
+                       if (!empty($data['image'])) {
+                               $fileExtension = mb_substr($data['image'], mb_strrpos($data['image'], '.'));
+                               $index = $tar->getIndexByFilename($data['image']);
+                               if ($index !== false) {
+                                       $filename = WCF_DIR.'images/stylePreview-'.$style->styleID.$fileExtension;
+                                       $tar->extract($index, $filename);
+                                       FileUtil::makeWritable($filename);
+                       
+                                       if (file_exists($filename)) {
+                                               $style->update(['image' => 'stylePreview-'.$style->styleID.$fileExtension]);
+                                       }
                                }
                        }
+                       
+                       // handle descriptions
+                       if (!empty($data['description'])) {
+                               self::saveLocalizedDescriptions($style, $data['description']);
+                               LanguageFactory::getInstance()->deleteLanguageCache();
+                       }
+                       
+                       if ($data['default']) {
+                               $style->setAsDefault();
+                       }
                }
-               
-               $tar->close();
-               
-               // handle descriptions
-               if (!empty($data['description'])) {
-                       self::saveLocalizedDescriptions($style, $data['description']);
-                       LanguageFactory::getInstance()->deleteLanguageCache();
+               else {
+                       unset($styleData['styleName']);
+                       
+                       $variables = $style->getVariables();
+                       
+                       $individualLess = Style::splitLessVariables($variables['individualLess']);
+                       $variables['individualLess'] = Style::joinLessVariables($styleData['variables']['individualLess'], $individualLess['custom']);
+                       
+                       $overrideLess = Style::splitLessVariables($variables['overrideLess']);
+                       $variables['overrideLess'] = Style::joinLessVariables($styleData['variables']['overrideLess'], $overrideLess['custom']);
+                       
+                       $styleData['variables'] = $variables;
+                       
+                       $style->update($styleData);
                }
                
-               if ($data['default']) {
-                       $style->setAsDefault();
-               }
+               $tar->close();
                
                return $style;
        }
index 497e5b410b72a5d29ff26f899824b3fd4e277026..aff9931c5e42934177dd2c6880172627cb822990 100644 (file)
@@ -801,6 +801,15 @@ class PackageInstallationDispatcher {
                return $this->queue->packageID;
        }
        
+       /**
+        * Returns current package name.
+        * 
+        * @return      string          package name
+        */
+       public function getPackageName() {
+               return $this->queue->packageName;
+       }
+       
        /**
         * Returns current package installation type.
         * 
index d745a679a48e5e2d00c11629cb9a45915c118a8a..4cec3b5a47e18820258f4c669b928900515baa4e 100644 (file)
@@ -3,6 +3,7 @@ namespace wcf\system\package\plugin;
 use wcf\data\style\StyleEditor;
 use wcf\data\style\StyleList;
 use wcf\system\event\EventHandler;
+use wcf\system\style\StyleHandler;
 
 /**
  * Installs, updates and deletes styles.
@@ -29,8 +30,11 @@ class StylePackageInstallationPlugin extends AbstractPackageInstallationPlugin {
                // extract style tar
                $filename = $this->installation->getArchive()->extractTar($this->instruction['value'], 'style_');
                
+               // searches for non-tainted style for updating
+               $styleEditor = StyleHandler::getInstance()->getStyleByName($this->installation->getPackageName(), false);
+               
                // import style
-               $style = StyleEditor::import($filename, $this->installation->getPackageID());
+               $style = StyleEditor::import($filename, $this->installation->getPackageID(), $styleEditor);
                
                // set style as default
                if (isset($this->instruction['attributes']['default'])) {
index 0e2edcfa96a713b06c5b672b33985c953b19d606..9f8c8a8d2ab1e2a6b791a0c2c1d93846f735d441 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\system\style;
 use wcf\data\style\ActiveStyle;
 use wcf\data\style\Style;
+use wcf\data\style\StyleEditor;
 use wcf\system\cache\builder\StyleCacheBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\SingletonFactory;
@@ -180,4 +181,23 @@ class StyleHandler extends SingletonFactory {
                        }
                }
        }
+       
+       /**
+        * Returns a style by package name, optionally filtering tainted styles.
+        * 
+        * @param       string          $packageName    style package name
+        * @param       boolean         $skipTainted    ignore tainted styles
+        * @return      \wcf\data\style\StyleEditor
+        */
+       public function getStyleByName($packageName, $skipTainted = false) {
+               foreach ($this->cache['styles'] as $style) {
+                       if ($style->packageName === $packageName) {
+                               if (!$skipTainted || !$style->isTainted) {
+                                       return new StyleEditor($style);
+                               }
+                       }
+               }
+               
+               return null;
+       }
 }
index 718de712ed524e565996226dca445fad67575fd1..800cd84156b35f59d098fc2113ae506721c341a3 100644 (file)
 @media only screen and (max-width: 800px) {
 }
 
+/* static dialogs */
+.jsStaticDialogContent {
+       display: none;
+}
 
 .dialogContentX {
        
index d1c4a91ca24851144ab023d62b370ad9366de2dc..2f95c19c3ccc4eec5d76f35065b6051fa3929678 100644 (file)
@@ -1420,8 +1420,10 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
        <category name="wcf.acp.style">
                <item name="wcf.acp.style.add"><![CDATA[Stil hinzufügen]]></item>
                <item name="wcf.acp.style.advanced"><![CDATA[Erweiterte Einstellungen]]></item>
+               <item name="wcf.acp.style.advanced.custom"><![CDATA[Eigene Deklarationen]]></item>
                <item name="wcf.acp.style.advanced.individualLess"><![CDATA[Individuelles CSS und LESS]]></item>
                <item name="wcf.acp.style.advanced.individualLess.description"><![CDATA[Die Eingabe wird am Ende des Stils eingefügt und kann vollständig aus CSS bestehen. Sie haben zusätzlich den Zugriff auf LESS und alle von Community Framework zur Verfügung gestellten Mixins.]]></item>
+               <item name="wcf.acp.style.advanced.original"><![CDATA[Vorgegebene Deklarationen]]></item>
                <item name="wcf.acp.style.advanced.overrideLess"><![CDATA[Überschreiben von LESS-Variablen]]></item>
                <item name="wcf.acp.style.advanced.overrideLess.description"><![CDATA[Sie können innerhalb dieser Eingabe beliebige LESS-Variablen überschreiben, die nicht durch den Stil-Editor direkt bearbeitbar sind. Beim Bezug auf andere Variablen muss sichergestellt werden, dass diese in der Reihenfolge vorher definiert wurden. Die Syntax muss wie folgt lauten: „@variableName: variableValue;“]]></item>
                <item name="wcf.acp.style.advanced.overrideLess.error"><![CDATA[Ihre Eingabe war ungültig, bitte überprüfen Sie die folgenden Einträge:]]></item>
@@ -1490,9 +1492,14 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.style.license"><![CDATA[Lizenz]]></item>
                <item name="wcf.acp.style.list"><![CDATA[Stile auflisten]]></item>
                <item name="wcf.acp.style.packageName"><![CDATA[Paketbezeichner]]></item>
-               <item name="wcf.acp.style.packageName.description"><![CDATA[Geben Sie hier den Paketbezeichner in der Form „tld.domain.paketName“ an. Wenn Ihnen beispielsweise die Domain „example.com“ gehört und Ihr Stil „Blue Sunrise“ heißt, so wäre „com.example.style.blueSunrise“ ein passender und gültiger Paketbezeichner.]]></item>
+               <item name="wcf.acp.style.packageName.description"><![CDATA[Geben Sie hier optional den Paketbezeichner in der Form „tld.domain.paketName“ an. Wenn Ihnen beispielsweise die Domain „example.com“ gehört und Ihr Stil „Blue Sunrise“ heißt, so wäre „com.example.style.blueSunrise“ ein passender und gültiger Paketbezeichner.]]></item>
                <item name="wcf.acp.style.packageName.error.notValid"><![CDATA[Der eingegebene Paketbezeichner ist ungültig]]></item>
                <item name="wcf.acp.style.packageName.error.reserved"><![CDATA[Der Paketbezeichner darf nicht mit „com.woltlab.“ beginnen]]></item>
+               <item name="wcf.acp.style.protected"><![CDATA[Dieser Stil ist geschützt und kann nur eingeschränkt verändert werden, Sie können diesen <a class="jsStaticDialog" data-dialog-id="styleDisableProtection">Schutz aufheben</a>.]]></item>
+               <item name="wcf.acp.style.protected.confirm"><![CDATA[Schutz aufheben]]></item>
+               <item name="wcf.acp.style.protected.description"><![CDATA[Importierte bzw. installierte Stile erhalten automatisch einen geschützten Status, dieser unterbindet die Veränderungen der unmittelbar durch den Stil vorgegebenen Deklarationen. Die Aufhebung dieses Schutzes gestattet Ihnen die vollständige Veränderung, verhindert dadurch aber auch eine Aktualisierung mit einer neueren Version des Stils, beispielsweise im Rahmen eines Updates.<br><br>Dieser Schritt wird nicht empfohlen und ist grundsätzlich nicht notwendig, Stile können weiterhin nach belieben angepasst werden, nur die Vorlage selbst kann nicht verändert werden.<br><br>Bitte speichern Sie eventuelle vorgenommene Änderungen bevor Sie diesen Schritt ausführen.]]></item>
+               <item name="wcf.acp.style.protected.less"><![CDATA[Nur lesend]]></item>
+               <item name="wcf.acp.style.protected.title"><![CDATA[Schutz aufheben]]></item>
                <item name="wcf.acp.style.styleDate"><![CDATA[Datum]]></item>
                <item name="wcf.acp.style.styleDescription"><![CDATA[Beschreibung]]></item>
                <item name="wcf.acp.style.styleName"><![CDATA[Name]]></item>
index 4c49e5be401f72941c393d5ce18a441f8f5a8806..748cc3be1bc0c7544b1725b3d01cd45d6ecf1b9f 100644 (file)
@@ -1419,8 +1419,10 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
        <category name="wcf.acp.style">
                <item name="wcf.acp.style.add"><![CDATA[Add Style]]></item>
                <item name="wcf.acp.style.advanced"><![CDATA[Advanced Settings]]></item>
+               <item name="wcf.acp.style.advanced.custom"><![CDATA[Own Declarations]]></item>
                <item name="wcf.acp.style.advanced.individualLess"><![CDATA[Individual CSS and LESS]]></item>
                <item name="wcf.acp.style.advanced.individualLess.description"><![CDATA[Content will be appended to the style sheet and may contain pure CSS. Furthermore you can use LESS including all Mixins provided by Community Framework.]]></item>
+               <item name="wcf.acp.style.advanced.original"><![CDATA[Preset Declarations]]></item>
                <item name="wcf.acp.style.advanced.overrideLess"><![CDATA[LESS Variables Override]]></item>
                <item name="wcf.acp.style.advanced.overrideLess.description"><![CDATA[Allows you to override LESS variables not available through the style editor itself. Syntax: “@variableName: variableValue;”, referred variables must be declared prior any attempt to read their value.]]></item>
                <item name="wcf.acp.style.advanced.overrideLess.error"><![CDATA[Provided value was invalid, please verify these items:]]></item>
@@ -1492,6 +1494,11 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.style.packageName.description"><![CDATA[Enter a package identifier matching the pattern “tld.domain.packageName”. For example if you own the domain “example.com” and created a style called “Blue Sunrise”, you could pick “com.example.style.blueSunrise” as a valid and descriptive identifier.]]></item>
                <item name="wcf.acp.style.packageName.error.notValid"><![CDATA[Package Identifier is invalid.]]></item>
                <item name="wcf.acp.style.packageName.error.reserved"><![CDATA[Package Identifiers cannot begin with “com.woltlab.”]]></item>
+               <item name="wcf.acp.style.protected"><![CDATA[This style is protected and editing is limited; You can <a class="jsStaticDialog" data-dialog-id="styleDisableProtection">disable this protection</a>.]]></item>
+               <item name="wcf.acp.style.protected.confirm"><![CDATA[Disable protection]]></item>
+               <item name="wcf.acp.style.protected.description"><![CDATA[Imported and installed styles are automatically protected to prevent editing the original declaration, preserving the ability to update this style. You may disable this protection and recover full editing permissions, but it can no longer be updated.<br><br>It is neither recommended nor necessary to remove the protection in most cases, styles can still be fully customized while preserving the preset declarations.<br><br>Please save any unsaved changes before proceeding.]]></item>
+               <item name="wcf.acp.style.protected.less"><![CDATA[Read only]]></item>
+               <item name="wcf.acp.style.protected.title"><![CDATA[Disable Protection]]></item>
                <item name="wcf.acp.style.styleDate"><![CDATA[Date]]></item>
                <item name="wcf.acp.style.styleDescription"><![CDATA[Description]]></item>
                <item name="wcf.acp.style.styleName"><![CDATA[Name]]></item>
index ac53487415ca4b3e7c146359ced055b94fadaaf2..b915fb4160b16e1013ab0c21e7458d0c66fbff6a 100644 (file)
@@ -1003,7 +1003,9 @@ CREATE TABLE wcf1_style (
        license VARCHAR(255) NOT NULL DEFAULT '',
        authorName VARCHAR(255) NOT NULL DEFAULT '',
        authorURL VARCHAR(255) NOT NULL DEFAULT '',
-       imagePath VARCHAR(255) NOT NULL DEFAULT ''
+       imagePath VARCHAR(255) NOT NULL DEFAULT '',
+       packageName VARCHAR(255) NOT NULL DEFAULT '',
+       isTainted TINYINT(1) NOT NULL DEFAULT 0
 );
 
 DROP TABLE IF EXISTS wcf1_style_variable;