Add TemplateDiffPage
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 10 Jun 2014 17:01:00 +0000 (19:01 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 10 Jun 2014 17:17:17 +0000 (19:17 +0200)
wcfsetup/install/files/acp/templates/exceptionLogView.tpl
wcfsetup/install/files/acp/templates/languageExport.tpl
wcfsetup/install/files/acp/templates/templateAdd.tpl
wcfsetup/install/files/acp/templates/templateDiff.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/templateList.tpl
wcfsetup/install/files/lib/acp/page/TemplateDiffPage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/acp/page/TemplateGroupListPage.class.php
wcfsetup/install/files/style/global.less
wcfsetup/install/files/style/layout.less
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index eb3d3fe0a3e5674bacc1640353f20171ad88eb09..c7648779626b6a90d4f33ccb3666a0be4d944707 100644 (file)
                                        {/if}
                                        <dl>
                                                <dt>{lang}wcf.acp.exceptionLog.exception.stacktrace{/lang}</dt>
-                                               <dd style="font-family: monospace; word-wrap: wrap-all; word-break: break-all;">
+                                               <dd class="monospace" style="word-wrap: wrap-all; word-break: break-all;">
                                                        <ul>
                                                                <li>{@"</li><li>"|implode:$exception[stacktrace]}</li>
                                                        </ul>
index 69662337b9c32dfe53540bc6d591cb98a401950a..4940d06e1134a3f553548ce6bdc096681dad50d4 100644 (file)
@@ -31,7 +31,7 @@
                        <dl>
                                <dt><label for="selectedPackages">{lang}wcf.acp.language.export.selectPackages{/lang}</label></dt>
                                <dd>
-                                       <select id="selectedPackages" name="selectedPackages[]" multiple="multiple" size="20" class="long" style="font-family: monospace;">
+                                       <select id="selectedPackages" name="selectedPackages[]" multiple="multiple" size="20" class="long monospace">
                                                <option value="*"{if $selectAllPackages} selected="selected"{/if}>{lang}wcf.acp.language.export.allPackages{/lang}</option>
                                                <option value="-">--------------------</option>
                                                {foreach from=$packages item=package}
index 506b5414052be00262113da2beb43d0cf833a452..e20ab6fdcf012a49b2dc315a4ef2cd7b0b2c8523 100644 (file)
@@ -14,6 +14,7 @@
 <div class="contentNavigation">
        <nav>
                <ul>
+                       {if $action == 'edit'}<li><a href="{link controller='TemplateDiff' id=$template->templateID}{/link}" class="button"><span class="icon icon16 icon-exchange"></span> <span>{lang}wcf.acp.template.diff{/lang}</span></a></li>{/if}
                        <li><a href="{link controller='TemplateList'}{if $action == 'edit'}templateGroupID={@$template->templateGroupID}{/if}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.template.list{/lang}</span></a></li>
                        
                        {event name='contentNavigationButtons'}
diff --git a/wcfsetup/install/files/acp/templates/templateDiff.tpl b/wcfsetup/install/files/acp/templates/templateDiff.tpl
new file mode 100644 (file)
index 0000000..55969a1
--- /dev/null
@@ -0,0 +1,154 @@
+{include file='header' pageTitle="wcf.acp.template.diff"}
+
+<header class="boxHeadline">
+       <h1>{lang}wcf.acp.template.diff{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+<form method="get" action="{link controller='TemplateDiff'}{/link}">
+       <div class="container containerPadding marginTop">
+               <fieldset>
+                       <legend>{lang}wcf.acp.template.group{/lang}</legend>
+                       
+                       <dl>
+                               <dt><label for="parentID">{lang}wcf.acp.template.diff.compareWith{/lang}</label></dt>
+                               <dd>
+                                       <select name="parentID" id="{lang}wcf.acp.template.group.default{/lang}ID">
+                                               <option value="0"></option>
+                                               {assign var=depth value=0}
+                                               {foreach from=$templateGroupHierarchie item='templateGroup' key='templateGroupID'}
+                                                       <option{if $templateGroup[hasTemplate] !== false && $templateGroup[hasTemplate] != $templateID} value="{$templateGroup[hasTemplate]}"{if $parent->templateID == $templateGroup[hasTemplate]} selected="selected"{/if}{else} disabled="disabled"{/if}>{@'&nbsp;'|str_repeat:$depth * 4}{if $templateGroupID}{$templateGroup[group]->templateGroupName}{else}{lang}wcf.acp.template.group.default{/lang}{/if}</option>
+                                                       {assign var=depth value=$depth + 1}
+                                               {/foreach}
+                                       </select>
+                               </dd>
+                       </dl>
+               </fieldset>
+       </div>
+       
+       <div class="formSubmit">
+               {@SID_INPUT_TAG}
+               <input type="hidden" name="id" value="{$templateID}" />
+               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+       </div>
+</form>
+
+{if $diff}
+       <div id="fullscreenContainer">
+               <div class="contentNavigation">
+                       <nav>
+                               <ul>
+                                       <li><a id="requestFullscreen" class="button" style="display: none;"><span class="icon icon16 icon-fullscreen"></span> <span>{lang}wcf.global.button.fullscreen{/lang}</span></a></li>
+                               </ul>
+                       </nav>
+               </div>
+               
+               <div class="container marginTop sideBySide">
+                       <div class="containerPadding">
+                               <header class="boxHeadline boxSubHeadline">
+                                       <h1>
+                                               {if $parent->templateGroupID}{$templateGroupHierarchie[$parent->templateGroupID][group]->templateGroupName}{else}{lang}wcf.acp.template.group.default{/lang}{/if}
+                                       </h1>
+                                       <p>{lang}wcf.acp.template.lastModificationTime{/lang}: {@$parent->lastModificationTime|time}</p>
+                               </header>
+                               
+                               {assign var=removeOffset value=0}
+                               {assign var=lineNo value=0}
+                               <pre id="left" class="marginTop monospace" style="overflow: auto; height: 700px;">{*
+                                       *}<ol class="nativeList">{*
+                                               *}{foreach from=$diff->getRawDiff() item='line'}{*
+                                                       *}{if $line[0] == ' '}{*
+                                                               *}{assign var=removeOffset value=0}{assign var=lineNo value=$lineNo + 1}{*
+                                                               *}<li value="{@$lineNo}" style="margin: 0">{$line[1]}</li>{*
+                                                       *}{elseif $line[0] == '-'}{*
+                                                               *}{assign var=removeOffset value=$removeOffset + 1}{assign var=lineNo value=$lineNo + 1}{*
+                                                               *}<li value="{@$lineNo}" style="color: red;margin: 0">{$line[1]}</li>{*
+                                                       *}{elseif $line[0] == '+'}{*
+                                                               *}{assign var=removeOffset value=$removeOffset - 1}{*
+                                                               *}{if $removeOffset < 0}<li style="list-style-type: none;margin: 0">&nbsp;</li>{/if}{*
+                                                       *}{/if}{*
+                                               *}{/foreach}{*
+                                       *}</ol>{*
+                               *}</pre>
+                               
+                               {if $parent->templateGroupID}
+                                       <div class="contentNavigation">
+                                               <nav>
+                                                       <ul>
+                                                               <li><a href="{link controller='TemplateEdit' id=$parent->templateID}{/link}" class="button"><span class="icon icon16 icon-pencil"></span> <span>{lang}wcf.global.button.edit{/lang}</span></a></li>
+                                                       </ul>
+                                               </nav>
+                                       </div>
+                               {/if}
+                       </div>
+                       <div class="containerPadding">
+                               <header class="boxHeadline boxSubHeadline">
+                                       <h1>
+                                               {if $template->templateGroupID}{$templateGroupHierarchie[$template->templateGroupID][group]->templateGroupName}{else}{lang}wcf.acp.template.group.default{/lang}{/if}
+                                       </h1>
+                                       <p>{lang}wcf.acp.template.lastModificationTime{/lang}: {@$template->lastModificationTime|time}</p>
+                               </header>
+                               {assign var=removeOffset value=0}
+                               {assign var=lineNo value=0}
+                               <pre id="right" class="marginTop monospace" style="overflow: auto; height: 700px;">{*
+                                       *}<ol class="nativeList">{*
+                                               *}{foreach from=$diff->getRawDiff() item='line'}{*
+                                                       *}{if $line[0] == ' '}{*
+                                                               *}{if $removeOffset > 0}{*
+                                                                       *}{@'<li style="list-style-type: none;margin: 0">&nbsp;</li>'|str_repeat:$removeOffset}{*
+                                                               *}{/if}{*
+                                                               *}{assign var=removeOffset value=0}{assign var=lineNo value=$lineNo + 1}{*
+                                                               *}<li value="{@$lineNo}" style="margin: 0">{$line[1]}</li>{*
+                                                       *}{elseif $line[0] == '-'}{*
+                                                               *}{assign var=removeOffset value=$removeOffset + 1}{*
+                                                       *}{elseif $line[0] == '+'}{*
+                                                               *}{assign var=removeOffset value=$removeOffset - 1}{assign var=lineNo value=$lineNo + 1}{*
+                                                               *}<li value="{@$lineNo}" style="color: green; margin: 0">{$line[1]}</li>{*
+                                                       *}{/if}{*
+                                               *}{/foreach}{*
+                                       *}</ol>{*
+                               *}</pre>
+                               
+                               <div class="contentNavigation">
+                                       <nav>
+                                               <ul>
+                                                       <li><a href="{link controller='TemplateEdit' id=$template->templateID}{/link}" class="button"><span class="icon icon16 icon-pencil"></span> <span>{lang}wcf.global.button.edit{/lang}</span></a></li>
+                                               </ul>
+                                       </nav>
+                               </div>
+                       </div>
+               </div>
+       </div>
+       <script data-relocate="true">
+       $(function() {
+               if (WCF.System.Fullscreen.isSupported()) {
+                       $('#requestFullscreen').show();
+               }
+               
+               // force that both containers have got the same width
+               var max = Math.max($('#left > ol').prop('scrollWidth'), $('#right > ol').prop('scrollWidth'));
+               $('#left > ol').width(max);
+               $('#right > ol').width(max);
+               
+               // sync scrolling
+               var sync = $('#left, #right');
+               function syncPosition(event) {
+                       var other = sync.not(this);
+                       other.off('scroll');
+                       other.prop('scrollLeft', $(this).prop('scrollLeft'));
+                       other.prop('scrollTop', $(this).prop('scrollTop'));
+                       setTimeout(function () { other.on('scroll', syncPosition); }, 150);
+               }
+               
+               sync.on('scroll', syncPosition);
+               
+               $('#requestFullscreen').on('click', function() {
+                       var element = $('#fullscreenContainer')[0];
+                       WCF.System.Fullscreen.toggleFullscreen(element);
+               });
+       });
+       </script>
+{/if}
+
+{include file='footer'}
index 17ea03c4e258d0cea1f4672f15f632fdcc2f50da..f3006eca588ee5f5027ec9c1796ac5d015ffa013 100644 (file)
                                                        <a href="{link controller='TemplateAdd'}copy={@$template->templateID}{/link}" title="{lang}wcf.acp.template.copy{/lang}" class="jsTooltip"><span class="icon icon16 icon-copy"></span></a>
                                                        
                                                        {if $template->templateGroupID}
+                                                               <a href="{link controller='TemplateDiff' id=$template->templateID}{/link}" title="{lang}wcf.acp.template.diff{/lang}" class="jsTooltip"><span class="icon icon16 icon-exchange"></span></a>
                                                                <a href="{link controller='TemplateEdit' id=$template->templateID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
                                                                <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$template->templateID}" data-confirm-message="{lang}wcf.acp.template.delete.sure{/lang}"></span>
                                                        {else}
+                                                               <span class="icon icon16 icon-exchange disabled" title="{lang}wcf.acp.template.diff{/lang}"></span>
                                                                <span class="icon icon16 icon-pencil disabled" title="{lang}wcf.global.button.edit{/lang}"></span>
                                                                <span class="icon icon16 icon-remove disabled" title="{lang}wcf.global.button.delete{/lang}"></span>
                                                        {/if}
diff --git a/wcfsetup/install/files/lib/acp/page/TemplateDiffPage.class.php b/wcfsetup/install/files/lib/acp/page/TemplateDiffPage.class.php
new file mode 100644 (file)
index 0000000..ce62984
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+namespace wcf\acp\page;
+use wcf\data\template\group\TemplateGroupList;
+use wcf\data\template\Template;
+use wcf\data\template\TemplateList;
+use wcf\page\AbstractPage;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+use wcf\util\Diff;
+use wcf\util\StringUtil;
+
+/**
+ * Compares two templates.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage acp.form
+ * @category   Community Framework
+ */
+class TemplateDiffPage extends AbstractPage {
+       /**
+        * @see \wcf\page\AbstractPage::$activeMenuItem
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.template';
+       
+       /**
+        * template id
+        * @var integer
+        */
+       public $templateID = 0;
+       
+       /**
+        * template object
+        * @var \wcf\data\template\Template
+        */
+       public $template = null;
+       
+       /**
+        * template id of the template to compare with
+        * @var integer
+        */
+       public $parentID = 0;
+       
+       /**
+        * template to compare with
+        * @var \wcf\data\template\Template
+        */
+       public $parent = null;
+       
+       /**
+        * differences between both templates
+        * @var \wcf\util\Diff
+        */
+       public $diff = null;
+       
+       /**
+        * @see \wcf\page\IPage::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               if (isset($_REQUEST['id'])) $this->templateID = intval($_REQUEST['id']);
+               $this->template = new Template($this->templateID);
+               if (!$this->template->templateID) {
+                       throw new IllegalLinkException();
+               }
+               if (isset($_REQUEST['parentID'])) $this->parentID = intval($_REQUEST['parentID']);
+               $this->parent = new Template($this->parentID);
+               if ($this->parent->templateID) {
+                       if ($this->parent->templateName != $this->template->templateName || $this->parent->application != $this->template->application) {
+                               throw new IllegalLinkException();
+                       }
+               }
+       }
+       
+       /**
+        * @see \wcf\page\IPage::readData()
+        */
+       public function readData() {
+               parent::readData();
+               
+               // read out template groups
+               $templateGroupList = new TemplateGroupList();
+               $templateGroupList->readObjects();
+               
+               // build template group hierarchie (template groups that are parents of the template group of the selected template)
+               $this->templateGroupHierarchie = array();
+               $templateGroup = $templateGroupList->search($this->template->templateGroupID);
+               while($templateGroup !== null) {
+                       $this->templateGroupHierarchie[$templateGroup->templateGroupID] = array('group' => $templateGroup, 'hasTemplate' => false);
+                       $templateGroup = $templateGroupList->search($templateGroup->parentTemplateGroupID);
+               }
+               $this->templateGroupHierarchie[0] = array('group' => array(), 'hasTemplate' => false);
+               
+               // find matching templates in the hierarchie
+               $templateList = new TemplateList();
+               $templateList->getConditionBuilder()->add('templateName = ?', array($this->template->templateName));
+               $templateList->getConditionBuilder()->add('application = ?', array($this->template->application));
+               $templateList->getConditionBuilder()->add('(template.templateGroupID IN(?) OR template.templateGroupID IS NULL)', array(array_keys($this->templateGroupHierarchie)));
+               $templateList->readObjects();
+               foreach ($templateList as $template) {
+                       $this->templateGroupHierarchie[($template->templateGroupID ?: 0)]['hasTemplate'] = $template->templateID;
+               }
+               
+               // a valid parent template was given, calculate diff
+               if ($this->parent->templateID) {
+                       $a = explode("\n", StringUtil::unifyNewlines($this->parent->getSource()));
+                       $b = explode("\n", StringUtil::unifyNewlines($this->template->getSource()));
+                       $this->diff = new Diff($a, $b);
+               }
+       }
+       
+       /**
+        * @see \wcf\page\IPage::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign(array(
+                       'templateID' => $this->templateID,
+                       'template' => $this->template,
+                       'parentID' => $this->parentID,
+                       'parent' => $this->parent,
+                       'diff' => $this->diff,
+                       'templateGroupHierarchie' => array_reverse($this->templateGroupHierarchie, true)
+               ));
+       }
+}
index c1bbca8fb3f151df1ff1eb62f978f7893440c900..dd2b12ad0ebd6ad5c4421ed41f52be0222492213 100644 (file)
@@ -39,7 +39,7 @@ class TemplateGroupListPage extends SortablePage {
        public $validSortFields = array('templateGroupID', 'templateGroupName', 'templateGroupFolderName', 'templates');
        
        /**
-        * @see \wcf\page\MultipleLinkPage::initObjectList
+        * @see \wcf\page\MultipleLinkPage::initObjectList()
         */
        protected function initObjectList() {
                parent::initObjectList();
index c5c9bb409ff7a6b5c3ae8baa14926d054cdd7d28..7293f6ee58ec3053997e2c2b330fb18382caeba8 100644 (file)
@@ -45,6 +45,11 @@ a {
        }
 }
 
+pre {
+       word-wrap: normal;
+       white-space: pre;
+}
+
 .footerContent, .logo {
        color: @wcfPageColor;
        
@@ -167,6 +172,10 @@ body > iframe[src="about:blank"] {
        color: @wcfDimmedColor;
 }
 
+.monospace {
+       font-family: Consolas, 'Courier New', monospace;
+}
+
 .iconFlag {
        background-color: @wcfContentBackgroundColor;
        border: 1px solid @wcfContainerBorderColor;
index ab407cabe6f368cc0e5a1cd3452da3702b70b2a7..6e44193bfc79b27a1c0148f9c9998e56683a7dbb 100644 (file)
@@ -2279,6 +2279,17 @@ ul.inlineDataList {
        }
 }
 
+.sideBySide {
+       display: table;
+       table-layout: fixed;
+       width: 100%;
+       
+       > * {
+               display: table-cell;
+               width: 50%;
+       }
+}
+
 /* print version */
 @media print {
        * {
index ab4139a294d254f41ac84336ac109f1c3b522847..d9dd262e740c81ab84ff0a5015a7098a62cbdc84 100644 (file)
@@ -1360,6 +1360,8 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.template.add"><![CDATA[Template hinzufügen]]></item>
                <item name="wcf.acp.template.edit"><![CDATA[Template bearbeiten]]></item>
                <item name="wcf.acp.template.copy"><![CDATA[Template kopieren]]></item>
+               <item name="wcf.acp.template.diff"><![CDATA[Unterschiede anzeigen]]></item>
+               <item name="wcf.acp.template.diff.compareWith"><![CDATA[Vergleichen mit]]></item>
                <item name="wcf.acp.template.lastModificationTime"><![CDATA[Letzte Änderung]]></item>
                <item name="wcf.acp.template.group.list"><![CDATA[Templategruppen]]></item>
                <item name="wcf.acp.template.group.add"><![CDATA[Templategruppe hinzufügen]]></item>
@@ -1899,6 +1901,7 @@ Fehler sind beispielsweise:
                <item name="wcf.global.button.disabledI18n"><![CDATA[einsprachig]]></item>
                <item name="wcf.global.button.edit"><![CDATA[Bearbeiten]]></item>
                <item name="wcf.global.button.enable"><![CDATA[Aktivieren]]></item>
+               <item name="wcf.global.button.fullscreen"><![CDATA[Vollbildmodus]]></item>
                <item name="wcf.global.button.next"><![CDATA[Weiter »]]></item>
                <item name="wcf.global.button.preview"><![CDATA[Vorschau]]></item>
                <item name="wcf.global.button.reset"><![CDATA[Zurücksetzen]]></item>
index 1d91a6f61eda12bc8a94c0bdc032c1ec171b56e9..9fe709595589548d3753ba565d659a4d77f88903 100644 (file)
@@ -1331,6 +1331,8 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.template.add"><![CDATA[Add Template]]></item>
                <item name="wcf.acp.template.edit"><![CDATA[Edit Template]]></item>
                <item name="wcf.acp.template.copy"><![CDATA[Copy Template]]></item>
+               <item name="wcf.acp.template.diff"><![CDATA[Show differences]]></item>
+               <item name="wcf.acp.template.diff.compareWith"><![CDATA[Compare with]]></item>
                <item name="wcf.acp.template.lastModificationTime"><![CDATA[Last Modification]]></item>
                <item name="wcf.acp.template.group.list"><![CDATA[Template Groups]]></item>
                <item name="wcf.acp.template.group.add"><![CDATA[Add Template Group]]></item>
@@ -1869,6 +1871,7 @@ Errors are:
                <item name="wcf.global.button.disabledI18n"><![CDATA[monolingual]]></item>
                <item name="wcf.global.button.edit"><![CDATA[Edit]]></item>
                <item name="wcf.global.button.enable"><![CDATA[Enable]]></item>
+               <item name="wcf.global.button.fullscreen"><![CDATA[Full Screen Mode]]></item>
                <item name="wcf.global.button.next"><![CDATA[Next »]]></item>
                <item name="wcf.global.button.preview"><![CDATA[Preview]]></item>
                <item name="wcf.global.button.reset"><![CDATA[Reset]]></item>