{/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>
<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}
<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'}
--- /dev/null
+{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}>{@' '|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"> </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"> </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'}
<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}
--- /dev/null
+<?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)
+ ));
+ }
+}
public $validSortFields = array('templateGroupID', 'templateGroupName', 'templateGroupFolderName', 'templates');
/**
- * @see \wcf\page\MultipleLinkPage::initObjectList
+ * @see \wcf\page\MultipleLinkPage::initObjectList()
*/
protected function initObjectList() {
parent::initObjectList();
}
}
+pre {
+ word-wrap: normal;
+ white-space: pre;
+}
+
.footerContent, .logo {
color: @wcfPageColor;
color: @wcfDimmedColor;
}
+.monospace {
+ font-family: Consolas, 'Courier New', monospace;
+}
+
.iconFlag {
background-color: @wcfContentBackgroundColor;
border: 1px solid @wcfContainerBorderColor;
}
}
+.sideBySide {
+ display: table;
+ table-layout: fixed;
+ width: 100%;
+
+ > * {
+ display: table-cell;
+ width: 50%;
+ }
+}
+
/* print version */
@media print {
* {
<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>
<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>
<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>
<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>