<parent>wcf.acp.menu.link.content</parent>
</acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.bbcode">
+ <parent>wcf.acp.menu.link.content</parent>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.bbcode.list">
+ <controller><![CDATA[wcf\acp\page\BBCodeListPage]]></controller>
+ <parent>wcf.acp.menu.link.bbcode</parent>
+ <permissions>admin.content.bbcode.canManageBBCode</permissions>
+ <showorder>1</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.bbcode.add">
+ <controller><![CDATA[wcf\acp\form\BBCodeAddForm]]></controller>
+ <parent>wcf.acp.menu.link.bbcode</parent>
+ <permissions>admin.content.bbcode.canManageBBCode</permissions>
+ <showorder>2</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.bbcode.mediaProvider.list">
+ <controller><![CDATA[wcf\acp\page\BBCodeMediaProviderListPage]]></controller>
+ <parent>wcf.acp.menu.link.bbcode</parent>
+ <permissions>admin.content.bbcode.canManageBBCode</permissions>
+ <showorder>3</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.bbcode.mediaProvider.add">
+ <controller><![CDATA[wcf\acp\form\BBCodeMediaProviderAddForm]]></controller>
+ <parent>wcf.acp.menu.link.bbcode</parent>
+ <permissions>admin.content.bbcode.canManageBBCode</permissions>
+ <showorder>4</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.smiley">
+ <parent>wcf.acp.menu.link.content</parent>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.smiley.list">
+ <controller><![CDATA[wcf\acp\page\SmileyListPage]]></controller>
+ <parent>wcf.acp.menu.link.smiley</parent>
+ <permissions>admin.content.smiley.canManageSmiley</permissions>
+ <showorder>1</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.smiley.add">
+ <controller><![CDATA[wcf\acp\form\SmileyAddForm]]></controller>
+ <parent>wcf.acp.menu.link.smiley</parent>
+ <permissions>admin.content.smiley.canManageSmiley</permissions>
+ <showorder>2</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.smiley.category.list">
+ <controller><![CDATA[wcf\acp\page\SmileyCategoryListPage]]></controller>
+ <parent>wcf.acp.menu.link.smiley</parent>
+ <permissions>admin.content.smiley.canManageSmiley</permissions>
+ <showorder>3</showorder>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.smiley.category.add">
+ <controller><![CDATA[wcf\acp\form\SmileyCategoryAddForm]]></controller>
+ <parent>wcf.acp.menu.link.smiley</parent>
+ <permissions>admin.content.smiley.canManageSmiley</permissions>
+ <showorder>4</showorder>
+ </acpmenuitem>
+
<acpmenuitem name="wcf.acp.menu.link.attachment.list">
<controller><![CDATA[wcf\acp\page\AttachmentListPage]]></controller>
<parent>wcf.acp.menu.link.attachment</parent>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/bbcode.xsd">
+ <import>
+ <bbcode name="b">
+ <htmlopen>b</htmlopen>
+ <htmlclose>b</htmlclose>
+ <allowedchildren>all^b</allowedchildren>
+ </bbcode>
+ <bbcode name="i">
+ <htmlopen>i</htmlopen>
+ <htmlclose>i</htmlclose>
+ <allowedchildren>all^i</allowedchildren>
+ </bbcode>
+ <bbcode name="u">
+ <htmlopen>span style="text-decoration: underline"</htmlopen>
+ <htmlclose>span</htmlclose>
+ <allowedchildren>all^u</allowedchildren>
+ </bbcode>
+ <bbcode name="s">
+ <htmlopen>s</htmlopen>
+ <htmlclose>s</htmlclose>
+ <allowedchildren>all^s</allowedchildren>
+ </bbcode>
+ <bbcode name="sub">
+ <htmlopen>sub</htmlopen>
+ <htmlclose>sub</htmlclose>
+ <allowedchildren>all^sub</allowedchildren>
+ </bbcode>
+ <bbcode name="sup">
+ <htmlopen>sup</htmlopen>
+ <htmlclose>sup</htmlclose>
+ <allowedchildren>all^sup</allowedchildren>
+ </bbcode>
+ <bbcode name="email">
+ <htmlopen>a</htmlopen>
+ <htmlclose>a</htmlclose>
+ <allowedchildren>none^img,b,i,u,s,sub,sup,color,size,font</allowedchildren>
+ <attributes>
+ <attribute name="0">
+ <html>href="mailto:%s"</html>
+ <required>1</required>
+ <usetext>1</usetext>
+ <validationpattern>^[^\s]+@[^\s]+$</validationpattern>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="color">
+ <htmlopen>span</htmlopen>
+ <htmlclose>span</htmlclose>
+ <attributes>
+ <attribute name="0">
+ <html>style="color: %s"</html>
+ <validationpattern>^(#([0-9a-f]{3}|[0-9a-f]{6})|[a-z]+)$</validationpattern>
+ <required>1</required>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="size">
+ <htmlopen>span</htmlopen>
+ <htmlclose>span</htmlclose>
+ <attributes>
+ <attribute name="0">
+ <html>style="font-size: %dpt"</html>
+ <validationpattern>^([89]{1}|[1-3]{1}[0-9]{1})$</validationpattern>
+ <required>1</required>
+ </attribute>
+ </attributes>
+ <wysiwyg>1</wysiwyg>
+ </bbcode>
+ <bbcode name="font">
+ <htmlopen>span</htmlopen>
+ <htmlclose>span</htmlclose>
+ <attributes>
+ <attribute name="0">
+ <html>style="font-family: %s"</html>
+ <validationpattern>^[^"';}\(\)]*$</validationpattern>
+ <required>1</required>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="align">
+ <htmlopen>div</htmlopen>
+ <htmlclose>div</htmlclose>
+ <attributes>
+ <attribute name="0">
+ <html>style="text-align: %s"</html>
+ <validationpattern>^(left|right|center|justify)$</validationpattern>
+ <required>1</required>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="quote">
+ <classname>wcf\system\bbcode\QuoteBBCode</classname>
+ <attributes>
+ <attribute name="0"></attribute>
+ <attribute name="1"></attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="code">
+ <classname>wcf\system\bbcode\CodeBBCode</classname>
+ <allowedchildren>none</allowedchildren>
+ <attributes>
+ <attribute name="0">
+ <validationpattern>^\d+$</validationpattern>
+ </attribute>
+ </attributes>
+ <sourcecode>1</sourcecode>
+ </bbcode>
+ <bbcode name="img">
+ <classname>wcf\system\bbcode\ImageBBCode</classname>
+ <allowedchildren>none</allowedchildren>
+ <attributes>
+ <attribute name="0">
+ <required>1</required>
+ <usetext>1</usetext>
+ <validationpattern>^.+$</validationpattern>
+ </attribute>
+ <attribute name="1">
+ <validationpattern>^(left|right)$</validationpattern>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="url">
+ <classname>wcf\system\bbcode\URLBBCode</classname>
+ <allowedchildren>none^img,b,i,u,s,sub,sup,color,size,font</allowedchildren>
+ <attributes>
+ <attribute name="0">
+ <required>1</required>
+ <usetext>1</usetext>
+ <validationpattern>^.+$</validationpattern>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="list">
+ <classname>wcf\system\bbcode\ListBBCode</classname>
+ <attributes>
+ <attribute name="0">
+ <validationpattern>^(1|a|none|circle|square|disc|decimal|lower-roman|upper-roman|decimal-leading-zero|lower-greek|lower-latin|upper-latin|armenian|georgian)$</validationpattern>
+ </attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="table">
+ <classname>wcf\system\bbcode\TableBBCode</classname>
+ <allowedchildren>all^table</allowedchildren>
+ </bbcode>
+ <bbcode name="media">
+ <classname>wcf\system\bbcode\MediaBBCode</classname>
+ <sourcecode>1</sourcecode>
+ </bbcode>
+ <bbcode name="spoiler">
+ <classname>wcf\system\bbcode\SpoilerBBCode</classname>
+ <attributes>
+ <attribute name="0"></attribute>
+ </attributes>
+ </bbcode>
+ <bbcode name="tt">
+ <htmlopen>code class="inlineCode"</htmlopen>
+ <htmlclose>code</htmlclose>
+ <allowedchildren>none</allowedchildren>
+ <sourcecode>1</sourcecode>
+ </bbcode>
+ </import>
+</data>
\ No newline at end of file
<coreobject>
<objectname><![CDATA[wcf\system\MetaTagHandler]]></objectname>
</coreobject>
+ <coreobject>
+ <objectname><![CDATA[wcf\system\bbcode\BBCodeHandler]]></objectname>
+ </coreobject>
</import>
</data>
<name>com.woltlab.wcf.collapsibleSidebar</name>
<definitionname>com.woltlab.wcf.collapsibleContent</definitionname>
</type>
+
+ <type>
+ <name>com.woltlab.wcf.bbcode.smiley</name>
+ <definitionname>com.woltlab.wcf.category</definitionname>
+ <classname>wcf\system\category\SmileyCategoryType</classname>
+ </type>
</import>
</data>
\ No newline at end of file
<defaultvalue>1</defaultvalue>
</option>
+ <option name="module_smiley">
+ <categoryname>module.content</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+
<!-- general.page -->
<option name="page_title">
<categoryname>general.page</categoryname>
<instruction type="objectType">objectType.xml</instruction>
<instruction type="acpSearchProvider">acpSearchProvider.xml</instruction>
<instruction type="style">woltlab-basic-style.tgz</instruction>
+ <instruction type="bbcode">bbcode.xml</instruction>
+ <instruction type="smiley">smiley.xml</instruction>
</instructions>
<instructions type="update" fromversion="2.0.0 Alpha 1">
<pip name="sitemap">wcf\system\package\plugin\SitemapPackageInstallationPlugin</pip>
<pip name="acpSearchProvider">wcf\system\package\plugin\ACPSearchProviderPackageInstallationPlugin</pip>
<pip name="style">wcf\system\package\plugin\StylePackageInstallationPlugin</pip>
+ <pip name="bbcode">wcf\system\package\plugin\BBCodePackageInstallationPlugin</pip>
+ <pip name="smiley">wcf\system\package\plugin\SmileyPackageInstallationPlugin</pip>
</import>
</data>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/smiley.xsd">
+ <import>
+ <smiley name=":)">
+ <title>smile</title>
+ <path>images/smilies/smile.png</path>
+ <aliases><![CDATA[:-)
+(:]]></aliases>
+ </smiley>
+ <smiley name=":(">
+ <title>sad</title>
+ <path>images/smilies/sad.png</path>
+ <aliases><![CDATA[:-(
+):]]></aliases>
+ </smiley>
+ <smiley name=";)">
+ <title>wink</title>
+ <path>images/smilies/wink.png</path>
+ <aliases><![CDATA[;-)
+(;]]></aliases>
+ </smiley>
+ <smiley name=":P">
+ <title>tongue</title>
+ <path>images/smilies/tongue.png</path>
+ <aliases><![CDATA[:-P]]></aliases>
+ </smiley>
+ <smiley name="^^">
+ <title>grin</title>
+ <path>images/smilies/grin.png</path>
+ <aliases><![CDATA[^.^
+^_^]]></aliases>
+ </smiley>
+ <smiley name=":D">
+ <title>biggrin</title>
+ <path>images/smilies/biggrin.png</path>
+ <aliases><![CDATA[:-D]]></aliases>
+ </smiley>
+ <smiley name=";(">
+ <title>crying</title>
+ <path>images/smilies/crying.png</path>
+ <aliases><![CDATA[;-(]]></aliases>
+ </smiley>
+ <smiley name="X(">
+ <title>angry</title>
+ <path>images/smilies/angry.png</path>
+ </smiley>
+ <smiley name=":*">
+ <title>kiss</title>
+ <path>images/smilies/kiss.png</path>
+ <aliases><![CDATA[:-*
+*:]]></aliases>
+ </smiley>
+ <smiley name=":|">
+ <title>mellow</title>
+ <path>images/smilies/mellow.png</path>
+ </smiley>
+ <smiley name="8o">
+ <title>w00t</title>
+ <path>images/smilies/w00t.png</path>
+ </smiley>
+ <smiley name="=O">
+ <title>scared</title>
+ <path>images/smilies/scared.png</path>
+ </smiley>
+ <smiley name="<X">
+ <title>sick</title>
+ <path>images/smilies/sick.png</path>
+ </smiley>
+ <smiley name="||">
+ <title>tired</title>
+ <path>images/smilies/tired.png</path>
+ </smiley>
+ <smiley name=":/">
+ <title>unsure</title>
+ <path>images/smilies/unsure.png</path>
+ </smiley>
+ <smiley name=":S">
+ <title>wacko</title>
+ <path>images/smilies/wacko.png</path>
+ </smiley>
+ <smiley name="X/">
+ <title>pinch</title>
+ <path>images/smilies/pinch.png</path>
+ </smiley>
+ <smiley name="8)">
+ <title>cool</title>
+ <path>images/smilies/cool.png</path>
+ </smiley>
+ <smiley name="?(">
+ <title>confused</title>
+ <path>images/smilies/confused.png</path>
+ </smiley>
+ <smiley name=":huh:">
+ <title>Huh</title>
+ <path>images/smilies/huh.png</path>
+ </smiley>
+ <smiley name=":rolleyes:">
+ <title>rolleyes</title>
+ <path>images/smilies/rolleyes.png</path>
+ </smiley>
+ <smiley name=":love:">
+ <title>love</title>
+ <path>images/smilies/love.png</path>
+ </smiley>
+ <smiley name="8|">
+ <title>blink</title>
+ <path>images/smilies/blink.png</path>
+ </smiley>
+ <smiley name=":cursing:">
+ <title>cursing</title>
+ <path>images/smilies/cursing.png</path>
+ </smiley>
+ <smiley name=":thumbdown:">
+ <title>thumbdown</title>
+ <path>images/smilies/thumbdown.png</path>
+ </smiley>
+ <smiley name=":thumbsup:">
+ <title>thumbsup</title>
+ <path>images/smilies/thumbsup.png</path>
+ </smiley>
+ <smiley name=":thumbup:">
+ <title>thumbup</title>
+ <path>images/smilies/thumbup.png</path>
+ </smiley>
+ <smiley name=":sleeping:">
+ <title>sleeping</title>
+ <path>images/smilies/sleeping.png</path>
+ </smiley>
+ <smiley name=":whistling:">
+ <title>whistling</title>
+ <path>images/smilies/whistling.png</path>
+ </smiley>
+ <smiley name=":evil:">
+ <title>evil</title>
+ <path>images/smilies/evil.png</path>
+ </smiley>
+ <smiley name=":saint:">
+ <title>saint</title>
+ <path>images/smilies/saint.png</path>
+ </smiley>
+ <smiley name="<3">
+ <title>heart</title>
+ <path>images/smilies/heart.png</path>
+ </smiley>
+ <smiley name=":!:">
+ <title>attention</title>
+ <path>images/smilies/attention.png</path>
+ </smiley>
+ <smiley name=":?:">
+ <title>question</title>
+ <path>images/smilies/question.png</path>
+ </smiley>
+ </import>
+</data>
\ No newline at end of file
--- /dev/null
+<div class="container codeBox {$highlighter|get_class|substr:30|lcfirst}">
+ <div>
+ <div>
+ <h3>{@$highlighter->getTitle()}{if $filename}: {@$filename}{/if}</h3>
+ </div>
+
+ <ol start="{$startLineNumber}">
+ {assign var='lineNumber' value=$startLineNumber}
+ {foreach from=$content item=line}
+ {if $lineNumbers[$lineNumber]|isset}
+ <li id="{@$lineNumbers[$lineNumber]}"><a href="{@$__wcf->getAnchor($lineNumbers[$lineNumber])}" class="lineAnchor"></a>{@$line}</li>
+ {else}
+ <li>{@$line}</li>
+ {/if}
+
+ {assign var='lineNumber' value=$lineNumber+1}
+ {/foreach}
+ </ol>
+ </div>
+</div>
--- /dev/null
+<blockquote class="container containerPadding quoteBox"{if $quoteLink} cite="{$quoteLink}"{/if}>
+ {if $quoteAuthor}
+ <header>
+ <h3>
+ {if $quoteLink}
+ <a href="{@$quoteLink}">{lang}wcf.bbcode.quote.title{/lang}</a>
+ {else}
+ {lang}wcf.bbcode.quote.title{/lang}
+ {/if}
+ </h3>
+ </header>
+ {else}
+ <header class="invisible">
+ <h3>
+ {lang}wcf.bbcode.quote{/lang}
+ </h3>
+ </header>
+ {/if}
+
+ <div>
+ {@$content}
+ </div>
+</blockquote>
\ No newline at end of file
--- /dev/null
+<div class="container containerPadding spoilerBox jsSpoilerBox">
+ <!-- begin:parser_nonessential -->
+ <header class="jsOnly">
+ <a class="button jsSpoilerToggle">{if $buttonTitle}{$buttonTitle}{else}{lang}wcf.bbcode.spoiler.show{/lang}{/if}</a>
+ </header>
+ <!-- end:parser_nonessential -->
+
+ <div>
+ {@$content}
+ </div>
+</div>
+
+<!-- begin:parser_nonessential -->
+<script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ var $spoilerBox = $('.jsSpoilerBox').removeClass('jsSpoilerBox');
+ $spoilerBox.children('div').hide();
+ $spoilerBox.find('> header > .jsSpoilerToggle').click(function() {
+ $(this).toggleClass('active').parent().next().slideToggle();
+ });
+ });
+ //]]>
+</script>
+<!-- end:parser_nonessential -->
\ No newline at end of file
<category name="admin.attachment">
<parent>admin.content</parent>
</category>
+ <category name="admin.content.bbcode">
+ <parent>admin.content</parent>
+ </category>
+ <category name="admin.content.smiley">
+ <parent>admin.content</parent>
+ </category>
+
<category name="admin.community">
<parent>admin</parent>
</category>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
+
+ <option name="admin.content.bbcode.canManageBBCode">
+ <categoryname>admin.content.bbcode</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
+
+ <option name="admin.content.smiley.canManageSmiley">
+ <categoryname>admin.content.smiley</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
</options>
</import>
</data>
--- /dev/null
+{foreach from=$bbCodes item='bbCode'}
+ <label><input type="checkbox" name="values[{$option->optionName}][]" value="{$bbCode}" {if $bbCode|in_array:$selectedBBCodes}checked="checked" {/if} /> {$bbCode}</label>
+{/foreach}
\ No newline at end of file
--- /dev/null
+{include file='header' pageTitle='wcf.acp.bbcode.'|concat:$action}
+
+{capture assign='attributeTemplate'}
+ <fieldset>
+ <legend><span class="icon icon16 icon-remove pointer jsDeleteButton jsTooltip" title="{lang}wcf.global.button.delete{/lang}"></span> <span>{lang}wcf.acp.bbcode.attribute{/lang} {ldelim}#$attributeNo}</span></legend>
+ <dl>
+ <dt><label for="attributes[{ldelim}@$attributeNo}][attributeHtml]">{lang}wcf.acp.bbcode.attribute.attributeHtml{/lang}</label></dt>
+ <dd>
+ <input type="text" id="attributes[{ldelim}@$attributeNo}][attributeHtml]" name="attributes[{ldelim}@$attributeNo}][attributeHtml]" value="" class="long" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="attributes[{ldelim}@$attributeNo}][validationPattern]">{lang}wcf.acp.bbcode.attribute.validationPattern{/lang}</label></dt>
+ <dd>
+ <input type="text" id="attributes[{ldelim}@$attributeNo}][validationPattern]" name="attributes[{ldelim}@$attributeNo}][validationPattern]" value="" class="long" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dd>
+ <label for="attributes[{ldelim}@$attributeNo}][required]"><input type="checkbox" id="attributes[{ldelim}@$attributeNo}][required]" name="attributes[{ldelim}@$attributeNo}][required]" value="1" /> {lang}{lang}wcf.acp.bbcode.attribute.required{/lang}{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl>
+ <dd>
+ <label for="attributes[{ldelim}@$attributeNo}][useText]"><input type="checkbox" id="attributes[{ldelim}@$attributeNo}][useText]" name="attributes[{ldelim}@$attributeNo}][useText]" value="1" /> {lang}{lang}wcf.acp.bbcode.attribute.useText{/lang}{/lang}</label>
+ <small>{lang}wcf.acp.bbcode.attribute.useText.description{/lang}</small>
+ </dd>
+ </dl>
+
+ {event name='attributeFields'}
+ </fieldset>
+{/capture}
+<script type="text/javascript">
+//<![CDATA[
+ $(function() {
+ $('.jsDeleteButton').click(function (event) {
+ $(event.target).parent().parent().remove();
+ });
+
+ var attributeNo = {if !$attributes|count}0{else}{assign var='lastAttribute' value=$attributes|end}{$lastAttribute->attributeNo+1}{/if};
+ var attributeTemplate = new WCF.Template('{@$attributeTemplate|encodeJS}');
+
+ $('.jsAddButton').click(function (event) {
+ var $html = $($.parseHTML(attributeTemplate.fetch({ attributeNo: attributeNo++ })));
+ $html.find('.jsDeleteButton').click(function (event) {
+ $(event.target).parent().parent().remove();
+ });
+ $('#attributeFieldset').append($html);
+ });
+
+ var $buttonSettings = $('.jsButtonSetting');
+ var $showButton = $('#showButton');
+ function toggleButtonSettings() {
+ if ($showButton.is(':checked')) {
+ $buttonSettings.show();
+ }
+ else {
+ $buttonSettings.hide();
+ }
+ }
+
+ $showButton.change(toggleButtonSettings);
+ toggleButtonSettings();
+ });
+//]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.bbcode.{$action}{/lang}</h1>
+</header>
+
+{if $action == 'add'}
+ <p class="info">{lang}wcf.acp.bbcode.add.userGroupOptionInfo{/lang}</p>
+{/if}
+
+{if $errorField}
+ <p class="error">{lang}wcf.global.form.error{/lang}</p>
+{/if}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.bbcode.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='BBCodeAdd'}{/link}{else}{link controller='BBCodeEdit' object=$bbcode}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'bbcodeTag'} class="formError"{/if}>
+ <dt><label for="bbcodeTag">{lang}wcf.acp.bbcode.bbcodeTag{/lang}</label></dt>
+ <dd>
+ <input type="text" id="bbcodeTag" name="bbcodeTag" value="{$bbcodeTag}" required="required" autofocus="autofocus" pattern="^[a-zA-Z0-9]+$" class="medium" />
+ {if $errorField == 'bbcodeTag'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.bbcodeTag.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'htmlOpen'} class="formError"{/if}>
+ <dt><label for="htmlOpen">{lang}wcf.acp.bbcode.htmlOpen{/lang}</label></dt>
+ <dd>
+ <input type="text" id="htmlOpen" name="htmlOpen" value="{$htmlOpen}" class="long" />
+ {if $errorField == 'htmlOpen'}
+ <small class="innerError">{lang}wcf.acp.bbcode.htmlOpen.error.{$errorType}{/lang}</small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'htmlClose'} class="formError"{/if}>
+ <dt><label for="htmlClose">{lang}wcf.acp.bbcode.htmlClose{/lang}</label></dt>
+ <dd>
+ <input type="text" id="htmlClose" name="htmlClose" value="{$htmlClose}" class="long" />
+ {if $errorField == 'htmlClose'}
+ <small class="innerError">{lang}wcf.acp.bbcode.htmlClose.error.{$errorType}{/lang}</small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'allowedChildren'} class="formError"{/if}>
+ <dt><label for="allowedChildren">{lang}wcf.acp.bbcode.allowedChildren{/lang}</label></dt>
+ <dd>
+ <input type="text" id="allowedChildren" name="allowedChildren" value="{$allowedChildren}" class="long" required="required" pattern="^((all|none)\^)?([a-zA-Z0-9]+,)*[a-zA-Z0-9]+$" />
+ {if $errorField == 'allowedChildren'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.allowedChildren.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dd>
+ <label for="isSourceCode"><input type="checkbox" id="isSourceCode" name="isSourceCode" value="1"{if $isSourceCode} checked="checked"{/if} /> {lang}wcf.acp.bbcode.isSourceCode{/lang}</label>
+ <small>{lang}wcf.acp.bbcode.isSourceCode.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'className'} class="formError"{/if}>
+ <dt><label for="className">{lang}wcf.acp.bbcode.className{/lang}</label></dt>
+ <dd>
+ <input type="text" id="className" name="className" value="{$className}" class="long" pattern="^\\?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\)*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$" />
+ {if $errorField == 'className'}
+ <small class="innerError">{lang}wcf.acp.bbcode.className.error.{$errorType}{/lang}</small>
+ {/if}
+ </dd>
+ </dl>
+
+ {if $nativeBBCode|empty}
+ <dl>
+ <dd>
+ <label><input type="checkbox" id="showButton" name="showButton" value="1"{if $showButton} checked="checked"{/if} /> {lang}wcf.acp.bbcode.showButton{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl class="jsButtonSetting{if $errorField == 'buttonLabel'} formError{/if}">
+ <dt><label for="buttonLabel">{lang}wcf.acp.bbcode.buttonLabel{/lang}</label></dt>
+ <dd>
+ <input type="text" id="buttonLabel" name="buttonLabel" value="{$i18nPlainValues['buttonLabel']}" class="long" />
+ {if $errorField == 'buttonLabel'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.buttonLabel.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+
+ {include file='multipleLanguageInputJavascript' elementIdentifier='buttonLabel' forceSelection=false}
+ </dd>
+ </dl>
+
+ <dl class="jsButtonSetting{if $errorField == 'wysiwygIcon'} formError{/if}">
+ <dt><label for="wysiwygIcon">{lang}wcf.acp.bbcode.wysiwygIcon{/lang}</label></dt>
+ <dd>
+ <input type="text" id="wysiwygIcon" name="wysiwygIcon" value="{$wysiwygIcon}" class="long" />
+ {if $errorField == 'wysiwygIcon'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.wysiwygIcon.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ {/if}
+
+ {event name='dataFields'}
+ </fieldset>
+
+ <fieldset id="attributeFieldset">
+ <legend><span class="icon icon16 icon-plus pointer jsAddButton jsTooltip" title="{lang}wcf.global.button.add{/lang}"></span> {lang}wcf.acp.bbcode.attributes{/lang}</legend>
+
+ {foreach from=$attributes item='attribute'}
+ <fieldset>
+ <legend><span class="icon icon16 icon-remove pointer jsDeleteButton jsTooltip" title="{lang}wcf.global.button.delete{/lang}"></span> <span>{lang}wcf.acp.bbcode.attribute{/lang} {#$attribute->attributeNo}</span></legend>
+
+ <dl{if $errorField == 'attributeHtml'|concat:$attribute->attributeNo} class="formError"{/if}>
+ <dt><label for="attributes[{@$attribute->attributeNo}][attributeHtml]">{lang}wcf.acp.bbcode.attribute.attributeHtml{/lang}</label></dt>
+ <dd>
+ <input type="text" id="attributes[{@$attribute->attributeNo}][attributeHtml]" name="attributes[{@$attribute->attributeNo}][attributeHtml]" value="{$attribute->attributeHtml}" class="long" />
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'attributeValidationPattern'|concat:$attribute->attributeNo} class="formError"{/if}>
+ <dt><label for="attributes[{@$attribute->attributeNo}][validationPattern]">{lang}wcf.acp.bbcode.attribute.validationPattern{/lang}</label></dt>
+ <dd>
+ <input type="text" id="attributes[{@$attribute->attributeNo}][validationPattern]" name="attributes[{@$attribute->attributeNo}][validationPattern]" value="{$attribute->validationPattern}" class="long" />
+ {if $errorField == 'attributeValidationPattern'|concat:$attribute->attributeNo}
+ <small class="innerError">
+ {if $errorType == 'notValid'}
+ {lang}wcf.acp.bbcode.attribute.error.validationPattern.notValid{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'attributeRequired'|concat:$attribute->attributeNo} class="formError"{/if}>
+ <dd>
+ <label for="attributes[{@$attribute->attributeNo}][required]"><input type="checkbox" id="attributes[{@$attribute->attributeNo}][required]" name="attributes[{@$attribute->attributeNo}][required]" value="1"{if $attribute->required} checked="checked"{/if} /> {lang}wcf.acp.bbcode.attribute.required{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'attributeUseText'|concat:$attribute->attributeNo} class="formError"{/if}>
+ <dd>
+ <label for="attributes[{@$attribute->attributeNo}][useText]"><input type="checkbox" id="attributes[{@$attribute->attributeNo}][useText]" name="attributes[{@$attribute->attributeNo}][useText]" value="1"{if $attribute->useText} checked="checked"{/if} /> {lang}wcf.acp.bbcode.attribute.useText{/lang}</label>
+ <small>{lang}wcf.acp.bbcode.attribute.useText.description{/lang}</small>
+ </dd>
+ </dl>
+
+ {event name='attributeFields'}
+ </fieldset>
+ {/foreach}
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ </div>
+</form>
+
+{include file='footer'}
\ No newline at end of file
--- /dev/null
+{include file='header' pageTitle='wcf.acp.bbcode.list'}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.bbcode.list{/lang}</h1>
+
+ <script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\bbcode\\BBCodeAction', '.jsBBCodeRow');
+ new WCF.Action.Toggle('wcf\\data\\bbcode\\BBCodeAction', $('.jsBBCodeRow'));
+ });
+ //]]>
+ </script>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="BBCodeList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.bbcode.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.bbcode.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnBBCodeID{if $sortField == 'bbcodeID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='BBCodeList'}pageNo={@$pageNo}&sortField=bbcodeID&sortOrder={if $sortField == 'bbcodeID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle columnBBCode{if $sortField == 'bbcodeTag'} active {@$sortOrder}{/if}"><a href="{link controller='BBCodeList'}pageNo={@$pageNo}&sortField=bbcodeTag&sortOrder={if $sortField == 'bbcodeTag' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.bbcode.bbcodeTag{/lang}</a></th>
+ <th class="columnText columnClassName{if $sortField == 'className'} active {@$sortOrder}{/if}"><a href="{link controller='BBCodeList'}pageNo={@$pageNo}&sortField=className&sortOrder={if $sortField == 'className' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.bbcode.className{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=bbcode}
+ <tr class="jsBBCodeRow">
+ <td class="columnIcon">
+ <span class="icon icon16 icon-{if $bbcode->isDisabled}off{else}circle-blank{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if $bbcode->isDisabled}enable{else}disable{/if}{/lang}" data-object-id="{@$bbcode->bbcodeID}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}"></span>
+ <a href="{link controller='BBCodeEdit' object=$bbcode}{/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="{@$bbcode->bbcodeID}" data-confirm-message="{lang}wcf.acp.bbcode.delete.sure{/lang}"></span>
+
+ {event name='rowButtons'}
+ </td>
+ <td class="columnID">{@$bbcode->bbcodeID}</td>
+ <td class="columnTitle columnBBCode"><a href="{link controller='BBCodeEdit' object=$bbcode}{/link}">[{$bbcode->bbcodeTag}]</a></td>
+ <td class="columnText columnClassName">{$bbcode->className}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.bbcode.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+ </div>
+{else}
+ <p class="info">{lang}wcf.acp.bbcode.noneAvailable{/lang}</p>
+{/if}
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.bbcode.mediaProvider.'|concat:$action}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.bbcode.mediaProvider.{$action}{/lang}</h1>
+</header>
+
+{if $errorField}
+ <p class="error">{lang}wcf.global.form.error{/lang}</p>
+{/if}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeMediaProviderList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.bbcode.mediaProvider.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='BBCodeMediaProviderAdd'}{/link}{else}{link controller='BBCodeMediaProviderEdit' object=$mediaProvider}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'title'} class="formError"{/if}>
+ <dt><label for="title">{lang}wcf.acp.bbcode.mediaProvider.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="title" name="title" value="{$title}" required="required" autofocus="autofocus" class="long" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.mediaProvider.title.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'regex'} class="formError"{/if}>
+ <dt><label for="regex">{lang}wcf.acp.bbcode.mediaProvider.regex{/lang}</label></dt>
+ <dd>
+ <textarea id="regex" name="regex" cols="40" rows="10" required="required">{$regex}</textarea>
+ {if $errorField == 'regex'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.mediaProvider.regex.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.bbcode.mediaProvider.regex.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'html'} class="formError"{/if}>
+ <dt><label for="html">{lang}wcf.acp.bbcode.mediaProvider.html{/lang}</label></dt>
+ <dd>
+ <textarea id="html" name="html" cols="40" rows="10" required="required">{$html}</textarea>
+ {if $errorField == 'html'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.bbcode.mediaProvider.html.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.bbcode.mediaProvider.html.description{/lang}</small>
+ </dd>
+ </dl>
+
+ {event name='dataFields'}
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ </div>
+</form>
+
+{include file='footer'}
\ No newline at end of file
--- /dev/null
+{include file='header' pageTitle='wcf.acp.bbcode.mediaProvider.list'}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.bbcode.mediaProvider.list{/lang}</h1>
+
+ <script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\bbcode\\media\\provider\\BBCodeMediaProviderAction', '.jsMediaProviderRow');
+ });
+ //]]>
+ </script>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="BBCodeMediaProviderList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeMediaProviderAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.bbcode.mediaProvider.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.bbcode.mediaProvider.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnMediaProviderID{if $sortField == 'providerID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='BBCodeMediaProviderList'}pageNo={@$pageNo}&sortField=providerID&sortOrder={if $sortField == 'providerID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle columnMediaProviderTitle{if $sortField == 'title'} active {@$sortOrder}{/if}"><a href="{link controller='BBCodeMediaProviderList'}pageNo={@$pageNo}&sortField=title&sortOrder={if $sortField == 'title' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.bbcode.mediaProvider.title{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item='mediaProvider'}
+ <tr class="jsMediaProviderRow">
+ <td class="columnIcon">
+ <a href="{link controller='BBCodeMediaProviderEdit' object=$mediaProvider}{/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="{@$mediaProvider->providerID}" data-confirm-message="{lang}wcf.acp.bbcode.mediaProvider.delete.sure{/lang}"></span>
+
+ {event name='rowButtons'}
+ </td>
+ <td class="columnID">{@$mediaProvider->providerID}</td>
+ <td class="columnTitle columnMediaProviderTitle"><a href="{link controller='BBCodeMediaProviderEdit' object=$mediaProvider}{/link}">{$mediaProvider->title}</a></td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BBCodeMediaProviderAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.bbcode.mediaProvider.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+ </div>
+{else}
+ <div class="container containerPadding">
+ <div>
+ <p class="warning">{lang}wcf.acp.bbcode.mediaProvider.noneAvailable{/lang}</p>
+ </div>
+ </div>
+{/if}
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.smiley.'|concat:$action}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.smiley.{$action}{/lang}</h1>
+</header>
+
+{if $errorField}
+ <p class="error">{lang}wcf.global.form.error{/lang}</p>
+{/if}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.form.{$action}.success{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ <li><a href="{link controller='SmileyList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.smiley.list{/lang}</span></a></li>
+ {event name='contentNavigationButtons'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='SmileyAdd'}{/link}{else}{link controller='SmileyEdit' id=$smiley->smileyID}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'smileyTitle'} class="formError"{/if}>
+ <dt><label for="smileyTitle">{lang}wcf.acp.smiley.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="smileyTitle" name="smileyTitle" value="{$smileyTitle}" autofocus="autofocus" class="long" />
+ {if $errorField == 'smileyTitle'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.smiley.smileyTitle.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ {include file='multipleLanguageInputJavascript' elementIdentifier='smileyTitle' forceSelection=false}
+
+ <dl{if $errorField == 'categoryID'} class="formError"{/if}>
+ <dt><label for="categoryID">{lang}wcf.acp.smiley.categoryID{/lang}</label></dt>
+ <dd>
+ <select id="categoryID" name="categoryID">
+ <option value="0"{if $categoryID === null} selected="selected"{/if}>{lang}wcf.acp.smiley.categoryID.default{/lang}</option>
+ {include file='categoryOptionList' maximumNestingLevel=0}
+ </select>
+
+ {if $errorField == 'categoryID'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.smiley.categoryID.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'smileyCode'} class="formError"{/if}>
+ <dt><label for="smileyCode">{lang}wcf.acp.smiley.smileyCode{/lang}</label></dt>
+ <dd>
+ <input type="text" id="smileyCode" name="smileyCode" value="{$smileyCode}" required="required" class="medium" />
+ {if $errorField == 'smileyCode'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'aliases'} class="formError"{/if}>
+ <dt><label for="aliases">{lang}wcf.acp.smiley.aliases{/lang}</label></dt>
+ <dd>
+ <textarea id="aliases" name="aliases" cols="40" rows="10">{$aliases}</textarea>
+
+ {if $errorField == 'aliases'}
+ <small class="innerError">
+ {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'showOrder'} class="formError"{/if}>
+ <dt><label for="showOrder">{lang}wcf.acp.smiley.showOrder{/lang}</label></dt>
+ <dd>
+ <input type="number" id="showOrder" name="showOrder" value="{$showOrder}" min="0" class="short" />
+
+ {if $errorField == 'showOrder'}
+ <small class="innerError">
+ {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ </fieldset>
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ </div>
+</form>
+
+{include file='footer'}
\ No newline at end of file
--- /dev/null
+{include file='header' pageTitle='wcf.acp.smiley.list'}
+
+{if $objects|count}
+ <script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\smiley\\SmileyAction', $('.smileyRow'));
+ new WCF.Sortable.List('smileyList', 'wcf\\data\\smiley\\SmileyAction', {@$startIndex});
+ });
+ //]]>
+ </script>
+{/if}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.smiley.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="SmileyList" object=$category link="pageNo=%d"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='SmileyAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.smiley.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+{if $smileyCount}
+ <div class="tabMenuContainer container containerPadding marginTop">
+ <nav class="menu">
+ <ul>
+ {foreach from=$categories item=categoryLoop}
+ <li{if (!$category && !$categoryLoop->categoryID) || ($category && $category->categoryID == $categoryLoop->categoryID)} class="ui-state-active"{/if}><a href="{if $categoryLoop->categoryID}{link controller='SmileyList' object=$categoryLoop}{/link}{else}{link controller='SmileyList'}{/link}{/if}">{$categoryLoop->title|language}</a></li>
+ {/foreach}
+ </ul>
+ </nav>
+ <section id="smileyList" class="sortableListContainer">
+ {if $objects|count}
+ <ol class="sortableList" data-object-id="0">
+ {foreach from=$objects item=smiley}
+ <li class="sortableNode sortableNoNesting smileyRow" data-object-id="{@$smiley->smileyID}">
+ <span class="sortableNodeLabel">
+ <a href="{link controller='SmileyEdit' id=$smiley->smileyID}{/link}"><img src="{$smiley->getURL()}" alt=""/ > {lang}{$smiley->smileyTitle}{/lang}</a> <span class="badge">{$smiley->smileyCode}</span>{foreach from=$smiley->getAliases() item='alias'} <span class="badge">{$alias}</span>{/foreach}
+
+ <span class="statusDisplay sortableButtonContainer">
+ <a href="{link controller='SmileyEdit' id=$smiley->smileyID}{/link}"><span title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip icon icon16 icon-pencil" /></a>
+ <span title="{lang}wcf.global.button.delete{/lang}" class="jsDeleteButton jsTooltip icon icon16 icon-remove" data-object-id="{@$smiley->smileyID}" data-confirm-message="{lang}wcf.acp.smiley.delete.sure{/lang}" />
+
+ {event name='itemButtons'}
+ </span>
+ </span>
+ <ol class="sortableList" data-object-id="{@$smiley->smileyID}"></ol></li>
+ </li>
+ {/foreach}
+ </ol>
+ <div class="formSubmit">
+ <button class="button" data-type="submit">{lang}wcf.global.button.submit{/lang}</button>
+ </div>
+ {else}
+ <p class="info">{lang}wcf.acp.smiley.category.noItems{/lang}</p>
+ {/if}
+ </section>
+ </div>
+{else}
+ <p class="warning">{lang}wcf.acp.smiley.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\bbcode\attribute\BBCodeAttributeAction;
+use wcf\data\bbcode\BBCode;
+use wcf\data\bbcode\BBCodeAction;
+use wcf\data\bbcode\BBCodeEditor;
+use wcf\data\package\PackageCache;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the bbcode add form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class BBCodeAddForm extends AbstractForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode.add';
+
+ /**
+ * allowed child bbcodes
+ * @var string
+ */
+ public $allowedChildren = 'all';
+
+ /**
+ * list of attributes
+ * @var array<object>
+ */
+ public $attributes = array();
+
+ /**
+ * tag name
+ * @var string
+ */
+ public $bbcodeTag = '';
+
+ /**
+ * editor button label
+ * @var string
+ */
+ public $buttonLabel = '';
+
+ /**
+ * class name
+ * @var string
+ */
+ public $className = '';
+
+ /**
+ * closing html tag
+ * @var string
+ */
+ public $htmlClose = '';
+
+ /**
+ * opening html tag
+ * @var string
+ */
+ public $htmlOpen = '';
+
+ /**
+ * true, if bbcode contains source code
+ * @var boolean
+ */
+ public $isSourceCode = false;
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'bbcodeAdd';
+
+ /**
+ * show editor button
+ * @var boolean
+ */
+ public $showButton = false;
+
+ /**
+ * wysiwyg editor icon
+ * @var string
+ */
+ public $wysiwygIcon = '';
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('buttonLabel');
+ }
+
+ /**
+ * @see wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (isset($_POST['allowedChildren'])) $this->allowedChildren = StringUtil::trim($_POST['allowedChildren']);
+ if (isset($_POST['attributes'])) $this->attributes = $_POST['attributes'];
+ if (isset($_POST['bbcodeTag'])) $this->bbcodeTag = StringUtil::trim($_POST['bbcodeTag']);
+ if (isset($_POST['className'])) $this->className = StringUtil::trim($_POST['className']);
+ if (isset($_POST['htmlClose'])) $this->htmlClose = StringUtil::trim($_POST['htmlClose']);
+ if (isset($_POST['htmlOpen'])) $this->htmlOpen = StringUtil::trim($_POST['htmlOpen']);
+ if (isset($_POST['isSourceCode'])) $this->isSourceCode = true;
+ if (isset($_POST['showButton'])) $this->showButton = true;
+ if (isset($_POST['wysiwygIcon'])) $this->wysiwygIcon = StringUtil::trim($_POST['wysiwygIcon']);
+
+ // TODO: The code below violates every implicit convention of value reading and type casting
+ $attributeNo = 0;
+ foreach ($this->attributes as $key => $val) {
+ $val['attributeNo'] = $attributeNo++;
+ $val['required'] = (int) isset($val['required']);
+ $val['useText'] = (int) isset($val['useText']);
+ $this->attributes[$key] = (object) $val;
+ }
+
+ I18nHandler::getInstance()->readValues();
+ if (I18nHandler::getInstance()->isPlainValue('buttonLabel')) $this->buttonLabel = I18nHandler::getInstance()->getValue('buttonLabel');
+ }
+
+ /**
+ * @see wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // tag name must not be empty
+ if (empty($this->bbcodeTag)) {
+ throw new UserInputException('bbcodeTag');
+ }
+
+ // tag may only contain alphanumeric chars
+ if (!Regex::compile('^[a-z0-9]+$', Regex::CASE_INSENSITIVE)->match($this->bbcodeTag)) {
+ throw new UserInputException('bbcodeTag', 'notValid');
+ }
+
+ // disallow the Pseudo-BBCodes all and none
+ if ($this->bbcodeTag == 'all' || $this->bbcodeTag == 'none') {
+ throw new UserInputException('bbcodeTag', 'notValid');
+ }
+
+ // check whether the tag is in use
+ $bbcode = BBCode::getBBCodeByTag($this->bbcodeTag);
+ if ((!isset($this->bbcode) && $bbcode->bbcodeID) || (isset($this->bbcode) && $bbcode->bbcodeID != $this->bbcode->bbcodeID)) {
+ throw new UserInputException('bbcodeTag', 'inUse');
+ }
+
+ // handle empty case first
+ if (empty($this->allowedChildren)) {
+ throw new UserInputException('allowedChildren');
+ }
+
+ // validate syntax of allowedChildren: Optional all|none^ followed by a comma-separated list of bbcodes
+ if (!empty($this->allowedChildren) && !Regex::compile('^(?:(?:all|none)\^)?(?:[a-zA-Z0-9]+,)*[a-zA-Z0-9]+$')->match($this->allowedChildren)) {
+ throw new UserInputException('allowedChildren', 'notValid');
+ }
+
+ // validate class
+ if (!empty($this->className) && !class_exists($this->className)) {
+ throw new UserInputException('className', 'notFound');
+ }
+
+ // validate attributes
+ foreach ($this->attributes as $attribute) {
+ // Check whether the pattern is a valid regex
+ if (!Regex::compile($attribute->validationPattern)->isValid()) {
+ throw new UserInputException('attributeValidationPattern'.$attribute->attributeNo, 'notValid');
+ }
+ }
+
+ // button
+ if ($this->showButton) {
+ // validate label
+ if (!I18nHandler::getInstance()->validateValue('buttonLabel')) {
+ throw new UserInputException('buttonLabel');
+ }
+
+ // validate image path
+ if (empty($this->wysiwygIcon)) {
+ throw new UserInputException('wysiwygIcon');
+ }
+ }
+ else {
+ $this->buttonLabel = '';
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ // save bbcode
+ $this->objectAction = new BBCodeAction(array(), 'create', array('data' => array(
+ 'allowedChildren' => $this->allowedChildren,
+ 'bbcodeTag' => $this->bbcodeTag,
+ 'buttonLabel' => $this->buttonLabel,
+ 'className' => $this->className,
+ 'htmlOpen' => $this->htmlOpen,
+ 'htmlClose' => $this->htmlClose,
+ 'isSourceCode' => ($this->isSourceCode ? 1 : 0),
+ 'packageID' => PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'),
+ 'showButton' => ($this->showButton ? 1 : 0),
+ 'wysiwygIcon' => $this->wysiwygIcon
+ )));
+ $returnValues = $this->objectAction->executeAction();
+ foreach ($this->attributes as $attribute) {
+ $attributeAction = new BBCodeAttributeAction(array(), 'create', array('data' => array(
+ 'bbcodeID' => $returnValues['returnValues']->bbcodeID,
+ 'attributeNo' => $attribute->attributeNo,
+ 'attributeHtml' => $attribute->attributeHtml,
+ 'validationPattern' => $attribute->validationPattern,
+ 'required' => $attribute->required,
+ 'useText' => $attribute->useText,
+ )));
+ $attributeAction->executeAction();
+ }
+
+ if ($this->showButton && !I18nHandler::getInstance()->isPlainValue('buttonLabel')) {
+ $bbcodeID = $returnValues['returnValues']->bbcodeID;
+ I18nHandler::getInstance()->save('buttonLabel', 'wcf.bbcode.buttonLabel'.$bbcodeID, 'wcf.bbcode', PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'));
+
+ // update button label
+ $bbcodeEditor = new BBCodeEditor($returnValues['returnValues']);
+ $bbcodeEditor->update(array(
+ 'buttonLabel' => 'wcf.bbcode.buttonLabel'.$bbcodeID
+ ));
+ }
+
+ $this->saved();
+
+ // reset values
+ $this->bbcodeTag = $this->htmlOpen = $this->htmlClose = $this->className = $this->buttonLabel = $this->wysiwygIcon = '';
+ $this->allowedChildren = 'all';
+ $this->attributes = array();
+ $this->isSourceCode = $this->showButton = false;
+
+ I18nHandler::getInstance()->reset();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'allowedChildren' => $this->allowedChildren,
+ 'attributes' => $this->attributes,
+ 'bbcodeTag' => $this->bbcodeTag,
+ 'buttonLabel' => $this->buttonLabel,
+ 'className' => $this->className,
+ 'htmlOpen' => $this->htmlOpen,
+ 'htmlClose' => $this->htmlClose,
+ 'isSourceCode' => $this->isSourceCode,
+ 'showButton' => $this->showButton,
+ 'wysiwygIcon' => $this->wysiwygIcon
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\bbcode\attribute\BBCodeAttribute;
+use wcf\data\bbcode\attribute\BBCodeAttributeAction;
+use wcf\data\bbcode\BBCode;
+use wcf\data\bbcode\BBCodeAction;
+use wcf\data\package\PackageCache;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the bbcode edit form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class BBCodeEditForm extends BBCodeAddForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * bbcode id
+ * @var integer
+ */
+ public $bbcodeID = 0;
+
+ /**
+ * bbcode object
+ * @var wcf\data\bbcode\BBCode
+ */
+ public $bbcode = null;
+
+ /**
+ * list of native bbcodes
+ * @var array<string>
+ */
+ public static $nativeBBCodes = array('b', 'i', 'u', 's', 'sub', 'sup', 'list', 'align', 'color', 'size', 'font', 'url', 'img', 'email', 'table');
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->bbcodeID = intval($_REQUEST['id']);
+ $this->bbcode = new BBCode($this->bbcodeID);
+ if (!$this->bbcode->bbcodeID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ if ($this->showButton) {
+ $this->buttonLabel = 'wcf.bbcode.buttonLabel'.$this->bbcode->bbcodeID;
+ if (I18nHandler::getInstance()->isPlainValue('buttonLabel')) {
+ I18nHandler::getInstance()->remove($this->buttonLabel);
+ $this->buttonLabel = I18nHandler::getInstance()->getValue('buttonLabel');
+ }
+ else {
+ I18nHandler::getInstance()->save('buttonLabel', $this->buttonLabel, 'wcf.bbcode', PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'));
+ }
+ }
+
+ // update bbcode
+ $this->objectAction = new BBCodeAction(array($this->bbcodeID), 'update', array('data' => array(
+ 'allowedChildren' => $this->allowedChildren,
+ 'bbcodeTag' => $this->bbcodeTag,
+ 'buttonLabel' => $this->buttonLabel,
+ 'className' => $this->className,
+ 'htmlClose' => $this->htmlClose,
+ 'htmlOpen' => $this->htmlOpen,
+ 'isSourceCode' => ($this->isSourceCode ? 1 : 0),
+ 'showButton' => ($this->showButton ? 1 : 0),
+ 'wysiwygIcon' => $this->wysiwygIcon
+ )));
+ $this->objectAction->executeAction();
+
+ // clear existing attributes
+ $sql = "DELETE FROM wcf".WCF_N."_bbcode_attribute
+ WHERE bbcodeID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($this->bbcodeID));
+
+ foreach ($this->attributes as $attribute) {
+ $attributeAction = new BBCodeAttributeAction(array(), 'create', array('data' => array(
+ 'bbcodeID' => $this->bbcodeID,
+ 'attributeNo' => $attribute->attributeNo,
+ 'attributeHtml' => $attribute->attributeHtml,
+ 'validationPattern' => $attribute->validationPattern,
+ 'required' => $attribute->required,
+ 'useText' => $attribute->useText,
+ )));
+ $attributeAction->executeAction();
+ }
+
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('buttonLabel', 1, $this->bbcode->buttonLabel, 'wcf.bbcode.buttonLabel\d+');
+ $this->buttonLabel = $this->bbcode->buttonLabel;
+
+ $this->attributes = BBCodeAttribute::getAttributesByBBCode($this->bbcode);
+ $this->bbcodeTag = $this->bbcode->bbcodeTag;
+ $this->htmlOpen = $this->bbcode->htmlOpen;
+ $this->htmlClose = $this->bbcode->htmlClose;
+ $this->allowedChildren = $this->bbcode->allowedChildren;
+ $this->isSourceCode = $this->bbcode->isSourceCode;
+ $this->className = $this->bbcode->className;
+ $this->showButton = $this->bbcode->showButton;
+ $this->wysiwygIcon = $this->bbcode->wysiwygIcon;
+ }
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables(!empty($_POST));
+
+ WCF::getTPL()->assign(array(
+ 'bbcode' => $this->bbcode,
+ 'action' => 'edit',
+ 'nativeBBCode' => (in_array($this->bbcode->bbcodeTag, self::$nativeBBCodes))
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\bbcode\media\provider\BBCodeMediaProviderAction;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the BBCode media provider add form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class BBCodeMediaProviderAddForm extends AbstractForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode.mediaProvider.add';
+
+ /**
+ * html value
+ * @var string
+ */
+ public $html = '';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'bbcodeMediaProviderAdd';
+
+ /**
+ * title value
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * regex value
+ * @var string
+ */
+ public $regex = '';
+
+ /**
+ * @see wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (isset($_POST['title'])) $this->title = StringUtil::trim($_POST['title']);
+ if (isset($_POST['regex'])) $this->regex = StringUtil::trim($_POST['regex']);
+ if (isset($_POST['html'])) $this->html = StringUtil::trim($_POST['html']);
+ }
+
+ /**
+ * @see wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate fields
+ if (empty($this->title)) {
+ throw new UserInputException('title');
+ }
+ if (empty($this->regex)) {
+ throw new UserInputException('regex');
+ }
+ if (empty($this->html)) {
+ throw new UserInputException('html');
+ }
+
+ $lines = explode("\n", StringUtil::unifyNewlines($this->regex));
+
+ foreach ($lines as $line) {
+ if (!Regex::compile($line)->isValid()) throw new UserInputException('regex', 'notValid');
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ // save media provider
+ $this->objectAction = new BBCodeMediaProviderAction(array(), 'create', array('data' => array(
+ 'title' => $this->title,
+ 'regex' => $this->regex,
+ 'html' => $this->html
+ )));
+ $this->objectAction->executeAction();
+ $this->saved();
+
+ // reset values
+ $this->title = $this->regex = $this->html = '';
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'title' => $this->title,
+ 'regex' => $this->regex,
+ 'html' => $this->html
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\bbcode\media\provider\BBCodeMediaProvider;
+use wcf\data\bbcode\media\provider\BBCodeMediaProviderAction;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+
+/**
+ * Shows the BBCode media provider edit form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class BBCodeMediaProviderEditForm extends BBCodeMediaProviderAddForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * id of the edited media provider
+ * @var integer
+ */
+ public $providerID = 0;
+
+ /**
+ * edited media provider object
+ * @var wcf\data\bbcode\media\provider\BBCodeMediaProvider
+ */
+ public $mediaProvider = null;
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->providerID = intval($_REQUEST['id']);
+ $this->mediaProvider = new BBCodeMediaProvider($this->providerID);
+ if (!$this->mediaProvider->providerID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ // update media-provider
+ $this->objectAction = new BBCodeMediaProviderAction(array($this->providerID), 'update', array('data' => array(
+ 'title' => $this->title,
+ 'regex' => $this->regex,
+ 'html' => $this->html
+ )));
+ $this->objectAction->executeAction();
+
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ $this->title = $this->mediaProvider->title;
+ $this->regex = $this->mediaProvider->regex;
+ $this->html = $this->mediaProvider->html;
+ }
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'mediaProvider' => $this->mediaProvider,
+ 'action' => 'edit'
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\category\Category;
+use wcf\data\category\CategoryNodeTree;
+use wcf\data\package\PackageCache;
+use wcf\data\smiley\SmileyAction;
+use wcf\data\smiley\SmileyEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the smiley add form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class SmileyAddForm extends AbstractForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley.add';
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'smileyAdd';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.smiley.canManageSmiley');
+
+ /**
+ * primary smiley code
+ * @var string
+ */
+ public $smileyCode = '';
+
+ /**
+ * showorder value
+ * @var integer
+ */
+ public $showOrder = 0;
+
+ /**
+ * categoryID value
+ * @var integer
+ */
+ public $categoryID = 0;
+
+ /**
+ * smileyTitle
+ * @var string
+ */
+ public $smileyTitle = '';
+
+ /**
+ * aliases value
+ * @var string
+ */
+ public $aliases = '';
+
+ /**
+ * node tree with available smiley categories
+ * @var wcf\data\category\CategoryNodeTree
+ */
+ public $categoryNodeTree = null;
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'smileyTitle' => $this->smileyTitle,
+ 'showOrder' => $this->showOrder,
+ 'categoryID' => $this->categoryID,
+ 'smileyCode' => $this->smileyCode,
+ 'aliases' => $this->aliases,
+ 'categoryNodeList' => $this->categoryNodeTree->getIterator()
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $this->categoryNodeTree = new CategoryNodeTree('com.woltlab.wcf.bbcode.smiley', 0, true);
+ }
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('smileyTitle');
+ }
+
+
+ /**
+ * @see wcf\page\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ I18nHandler::getInstance()->readValues();
+
+ if (I18nHandler::getInstance()->isPlainValue('smileyTitle')) $this->smileyTitle = I18nHandler::getInstance()->getValue('smileyTitle');
+
+ if (isset($_POST['showOrder'])) {
+ $this->showOrder = intval($_POST['showOrder']);
+ }
+ if (isset($_POST['smileyCode'])) {
+ $this->smileyCode = StringUtil::trim($_POST['smileyCode']);
+ }
+ if (isset($_POST['categoryID'])) {
+ $this->categoryID = intval($_POST['categoryID']);
+ }
+ if (isset($_POST['aliases'])) {
+ $this->aliases = StringUtil::unifyNewlines(StringUtil::trim($_POST['aliases']));
+ }
+ }
+
+ /**
+ * @see wcf\page\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ $this->objectAction = new SmileyAction(array(), 'create', array(
+ 'data' => array(
+ 'smileyTitle' => $this->smileyTitle,
+ 'smileyCode' => $this->smileyCode,
+ 'showOrder' => $this->showOrder,
+ 'categoryID' => $this->categoryID ?: null,
+ 'packageID' => PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'),
+ 'aliases' => $this->aliases
+ )
+ ));
+ $this->objectAction->executeAction();
+ $returnValues = $this->objectAction->getReturnValues();
+ $smileyEditor = new SmileyEditor($returnValues['returnValues']);
+ $smileyID = $returnValues['returnValues']->smileyID;
+
+ if (!I18nHandler::getInstance()->isPlainValue('smileyTitle')) {
+ I18nHandler::getInstance()->save('smileyTitle', 'wcf.smiley.title'.$smileyID, 'wcf.smiley', PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'));
+
+ // update title
+ $smileyEditor->update(array(
+ 'title' => 'wcf.smiley.title'.$smileyID
+ ));
+ }
+
+ // reset values
+ $this->smileyCode = '';
+ $this->categoryID = 0;
+ $this->showOrder = 0;
+
+ I18nHandler::getInstance()->reset();
+
+ $this->saved();
+
+ // show success message
+ WCF::getTPL()->assign('success', true);
+ }
+
+ /**
+ * @see wcf\page\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate title
+ if (!I18nHandler::getInstance()->validateValue('smileyTitle')) {
+ throw new UserInputException('smileyTitle');
+ }
+
+ if ($this->categoryID !== 0) {
+ $category = new Category($this->categoryID);
+ if (!$category->categoryID) {
+ throw new UserInputException('categoryID', 'notValid');
+ }
+ }
+
+ if ($this->smileyCode === '') {
+ throw new UserInputException('smileyCode');
+ }
+
+ // TODO: Validate uniqueness of smileyCode and aliases
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+
+/**
+ * Shows the smiley category add form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.category
+ * @category Community Framework
+ */
+class SmileyCategoryAddForm extends AbstractCategoryAddForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley.category.add';
+
+ /**
+ * @see wcf\acp\form\AbstractCategoryAddForm::$objectTypeName
+ */
+ public $objectTypeName = 'com.woltlab.wcf.bbcode.smiley';
+
+ /**
+ * @see wcf\acp\form\AbstractCategoryAddForm::$pageTitle
+ */
+ public $pageTitle = 'wcf.acp.smiley.category.add';
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+
+/**
+ * Shows the category edit form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.category
+ * @category Community Framework
+ */
+class SmileyCategoryEditForm extends AbstractCategoryEditForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley.category.list';
+
+ /**
+ * @see wcf\acp\form\AbstractCategoryAddForm::$objectTypeName
+ */
+ public $objectTypeName = 'com.woltlab.wcf.bbcode.smiley';
+
+ /**
+ * @see wcf\acp\form\AbstractCategoryAddForm::$pageTitle
+ */
+ public $pageTitle = 'wcf.acp.smiley.category.edit';
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\package\PackageCache;
+use wcf\data\smiley\Smiley;
+use wcf\data\smiley\SmileyAction;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the smiley edit form.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class SmileyEditForm extends SmileyAddForm {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.smiley.canManageSmiley');
+
+ /**
+ * smiley id
+ * @var integer
+ */
+ public $smileyID = 0;
+
+ /**
+ * smiley object
+ * @var wcf\data\smiley\Smiley
+ */
+ public $smiley = null;
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->smileyID = intval($_REQUEST['id']);
+ $this->smiley = new Smiley($this->smileyID);
+ if (!$this->smiley->smileyID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ $this->smileyTitle = 'wcf.smiley.title'.$this->smiley->smileyID;
+ if (I18nHandler::getInstance()->isPlainValue('smileyTitle')) {
+ I18nHandler::getInstance()->remove($this->smileyTitle);
+ $this->smileyTitle = I18nHandler::getInstance()->getValue('smileyTitle');
+ }
+ else {
+ I18nHandler::getInstance()->save('smileyTitle', $this->smileyTitle, 'wcf.smiley', PackageCache::getInstance()->getPackageID('com.woltlab.wcf.bbcode'));
+ }
+
+ // update bbcode
+ $this->objectAction = new SmileyAction(array($this->smileyID), 'update', array('data' => array(
+ 'smileyTitle' => $this->smileyTitle,
+ 'smileyCode' => $this->smileyCode,
+ 'showOrder' => $this->showOrder,
+ 'categoryID' => $this->categoryID ?: null,
+ 'aliases' => $this->aliases
+ )));
+ $this->objectAction->executeAction();
+
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('smileyTitle', 1, $this->smiley->smileyTitle, 'wcf.smiley.title\d+');
+ $this->smileyTitle = $this->smiley->smileyTitle;
+
+ $this->smileyCode = $this->smiley->smileyCode;
+ $this->aliases = $this->smiley->aliases;
+ $this->showOrder = $this->smiley->showOrder;
+ $this->categoryID = $this->smiley->categoryID;
+ }
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables(!empty($_POST));
+
+ WCF::getTPL()->assign(array(
+ 'smiley' => $this->smiley,
+ 'action' => 'edit'
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Lists the available BBCodes.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class BBCodeListPage extends SortablePage {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode.list';
+
+ /**
+ * @see wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'bbcodeTag';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\bbcode\BBCodeList';
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'bbcodeList';
+
+ /**
+ * @see wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('bbcodeID', 'bbcodeTag', 'className');
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Lists the available media providers.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class BBCodeMediaProviderListPage extends SortablePage {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.bbcode.mediaProvider.list';
+
+ /**
+ * @see wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'title';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\bbcode\media\provider\BBCodeMediaProviderList';
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'bbcodeMediaProviderList';
+
+ /**
+ * @see wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('providerID', 'title');
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+
+/**
+ * Shows the smiley category list.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.category
+ * @category Community Framework
+ */
+class SmileyCategoryListPage extends AbstractCategoryListPage {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley.category.list';
+
+ /**
+ * @see wcf\acp\page\AbstractCategoryListPage::$activeMenuItem
+ */
+ public $objectTypeName = 'com.woltlab.wcf.bbcode.smiley';
+
+ /**
+ * @see wcf\acp\page\AbstractCategoryListPage::$pageTitle
+ */
+ public $pageTitle = 'wcf.acp.smiley.category.list';
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\data\category\Category;
+use wcf\data\smiley\SmileyCache;
+use wcf\page\MultipleLinkPage;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+
+/**
+ * Lists the available smilies.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class SmileyListPage extends MultipleLinkPage {
+ /**
+ * @see wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.smiley.list';
+
+ /**
+ * @see wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.smiley.canManageSmiley');
+
+ /**
+ * @see wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\smiley\SmileyList';
+
+ /**
+ * @see wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'smileyList';
+
+ /**
+ * @see wcf\page\MultipleLinkPage::$sqlOrderBy
+ */
+ public $sqlOrderBy = 'showOrder ASC, smileyID ASC';
+
+ /**
+ * category id
+ * @var integer
+ */
+ public $categoryID = 0;
+
+ /**
+ * active category
+ * @var wcf\data\category\Category
+ */
+ public $category = null;
+
+ /**
+ * available categories
+ * @var array<wcf\data\category\Category>
+ */
+ public $categories = array();
+
+ /**
+ * @see wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) {
+ $this->categoryID = intval($_REQUEST['id']);
+ $this->category = new Category($this->categoryID);
+ if (!$this->category->categoryID) {
+ throw new IllegalLinkException();
+ }
+ }
+ }
+
+ /**
+ * @see wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'category' => $this->category,
+ 'categories' => $this->categories,
+ 'smileyCount' => count(SmileyCache::getInstance()->getSmilies())
+ ));
+ }
+
+ /**
+ * @see wcf\page\MultipleLinkPage::initObjectList()
+ */
+ protected function initObjectList() {
+ parent::initObjectList();
+
+ if ($this->categoryID) {
+ $this->objectList->getConditionBuilder()->add('categoryID = ?', array($this->categoryID));
+ }
+ else {
+ $this->objectList->getConditionBuilder()->add('categoryID IS NULL', array());
+ }
+ }
+
+ /**
+ * @see wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $this->categories = SmileyCache::getInstance()->getCategories();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode;
+use wcf\data\ProcessibleDatabaseObject;
+use wcf\system\request\IRouteController;
+use wcf\system\WCF;
+
+/**
+ * Represents a bbcode.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode
+ * @category Community Framework
+ */
+class BBCode extends ProcessibleDatabaseObject implements IRouteController {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'bbcode';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'bbcodeID';
+
+ /**
+ * @see wcf\data\ProcessibleDatabaseObject::$processorInterface
+ */
+ protected static $processorInterface = 'wcf\system\bbcode\IBBCode';
+
+ /**
+ * Returns the attributes of this bbcode.
+ *
+ * @return array<wcf\data\bbcode\attribute\BBCodeAttribute>
+ */
+ public function getAttributes() {
+ if ($this->attributes === null) {
+ $this->data['attributes'] = BBCodeCache::getInstance()->getBBCodeAttributes($this->bbcodeTag);
+ }
+
+ return $this->attributes;
+ }
+
+ /**
+ * @see wcf\data\ITitledObject::getTitle()
+ */
+ public function getTitle() {
+ return $this->bbcodeTag;
+ }
+
+ /**
+ * Returns BBCode object with the given tag.
+ *
+ * @param string $tag
+ * @return wcf\data\bbcode\BBCode
+ */
+ public static function getBBCodeByTag($tag) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_bbcode
+ WHERE bbcodeTag = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($tag));
+ $row = $statement->fetchArray();
+ if (!$row) $row = array();
+
+ return new self(null, $row);
+ }
+
+ /**
+ * Returns true if the given BBCode tag is allowed by the given list of
+ * BBCode tags. If the relevant BBCode should be globally disabled or non-
+ * existent, false is returned.
+ *
+ * @param string $bbcodeTag
+ * @param array<string> $allowedBBCodeTags
+ * @return boolean
+ */
+ public static function isAllowedBBCode($bbcodeTag, array $allowedBBCodeTags) {
+ // check if bbcode is unknown or disabled
+ if (BBCodeCache::getInstance()->getBBCodeByTag($bbcodeTag) === null) {
+ return false;
+ }
+
+ // all BBCodes are allowed
+ if (in_array('all', $allowedBBCodeTags)) {
+ return true;
+ }
+
+ // no BBCode are allowed
+ if (in_array('none', $allowedBBCodeTags)) {
+ return false;
+ }
+
+ return in_array($bbcodeTag, $allowedBBCodeTags);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode;
+use wcf\data\user\group\UserGroup;
+use wcf\data\user\group\UserGroupEditor;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\WCF;
+
+/**
+ * Executes bbcode-related actions.
+ *
+ * @author Tim Duesterhus, Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode
+ * @category Community Framework
+ */
+class BBCodeAction extends AbstractDatabaseObjectAction implements IToggleAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\bbcode\BBCodeEditor';
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::create()
+ */
+ public function create() {
+ $bbCode = parent::create();
+
+ // add bbcode to BBCodeSelect user group options
+ $sql = "SELECT optionID
+ FROM wcf".WCF_N."_user_group_option
+ WHERE optionType = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array('BBCodeSelect'));
+
+ $optionIDs = array();
+ while ($optionID = $statement->fetchColumn()) {
+ $optionIDs[] = $optionID;
+ }
+
+ if (!empty($optionIDs)) {
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add("optionID IN (?)", array($optionIDs));
+ $conditionBuilder->add("groupID IN (?)", array(UserGroup::getGroupIDsByType(array(UserGroup::EVERYONE))));
+ $conditionBuilder->add("optionValue <> ?", array('all'));
+
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_user_group_option_value
+ ".$conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ $sql = "UPDATE wcf".WCF_N."_user_group_option_value
+ SET optionValue = ?
+ WHERE optionID = ?
+ AND groupID = ?";
+ $updateStatement = WCF::getDB()->prepareStatement($sql);
+
+ WCF::getDB()->beginTransaction();
+ while ($row = $statement->fetchArray()) {
+ if (!empty($row['optionValue'])) {
+ $row['optionValue'] .= ','.$bbCode->bbcodeTag;
+ }
+ else {
+ $row['optionValue'] = $bbCode->bbcodeTag;
+ }
+
+ $updateStatement->execute(array(
+ $row['optionValue'],
+ $row['optionID'],
+ $row['groupID']
+ ));
+ }
+ WCF::getDB()->commitTransaction();
+
+ // clear user group option cache
+ UserGroupEditor::resetCache();
+ }
+
+ return $bbCode;
+ }
+
+ /**
+ * @see wcf\data\IToggleAction::validateToggle()
+ */
+ public function validateToggle() {
+ parent::validateUpdate();
+ }
+
+ /**
+ * @see wcf\data\IToggleAction::toggle()
+ */
+ public function toggle() {
+ foreach ($this->objects as $bbcode) {
+ $bbcode->update(array(
+ 'isDisabled' => $bbcode->isDisabled ? 0 : 1
+ ));
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode;
+use wcf\system\cache\builder\BBCodeCacheBuilder;
+use wcf\system\SingletonFactory;
+
+/**
+ * Manages the bbcode cache.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode
+ * @category Community Framework
+ */
+class BBCodeCache extends SingletonFactory {
+ /**
+ * cached bbcodes
+ * @var array<wcf\data\bbcode\BBCode>
+ */
+ protected $cachedBBCodes = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ // get bbcode cache
+ $this->cachedBBCodes = BBCodeCacheBuilder::getInstance()->getData();
+ }
+
+ /**
+ * Returns all bbcodes.
+ *
+ * @return array<wcf\data\bbcode\BBCode>
+ */
+ public function getBBCodes() {
+ return $this->cachedBBCodes;
+ }
+
+ /**
+ * Returns the BBCode with the given tag or null if no such BBCode exists.
+ *
+ * @param string $tag
+ * @return wcf\data\bbcode\BBCode
+ */
+ public function getBBCodeByTag($tag) {
+ if (isset($this->cachedBBCodes[$tag])) {
+ return $this->cachedBBCodes[$tag];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns all attributes of a bbcode.
+ *
+ * @param string $tag
+ * @return array<wcf\data\bbcode\attribute\BBCodeAttribute>
+ */
+ public function getBBCodeAttributes($tag) {
+ return $this->cachedBBCodes[$tag]->getAttributes();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\BBCodeCacheBuilder;
+
+/**
+ * Provides functions to edit bbcodes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode
+ * @category Community Framework
+ */
+class BBCodeEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\bbcode\BBCode';
+
+ /**
+ * @see wcf\data\IEditableCachedObject::resetCache()
+ */
+ public static function resetCache() {
+ BBCodeCacheBuilder::getInstance()->reset();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of bbcodes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode
+ * @category Community Framework
+ */
+class BBCodeList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\bbcode\BBCode';
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\attribute;
+use wcf\data\bbcode\BBCode;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a bbcode attribute.
+ *
+ * @author Tim Duesterhus, Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.attribute
+ * @category Community Framework
+ */
+class BBCodeAttribute extends DatabaseObject {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'bbcode_attribute';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'attributeID';
+
+ /**
+ * Reads attributes by assigned bbcode.
+ *
+ * @param wcf\data\bbcode\BBCode $bbcode
+ * @return array<wcf\data\bbcode\attribute\BBCodeAttribute>
+ */
+ public static function getAttributesByBBCode(BBCode $bbcode) {
+ $attributeList = new BBCodeAttributeList();
+ $attributeList->sqlOrderBy = "bbcode_attribute.attributeNo ASC";
+ $attributeList->getConditionBuilder()->add('bbcode_attribute.bbcodeID = ?', array($bbcode->bbcodeID));
+ $attributeList->readObjects();
+ return $attributeList->getObjects();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\attribute;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes bbcode attribute-related actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.attribute
+ * @category Community Framework
+ */
+class BBCodeAttributeAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\bbcode\attribute\BBCodeAttributeEditor';
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\attribute;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit bbcode attributes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.attribute
+ * @category Community Framework
+ */
+class BBCodeAttributeEditor extends DatabaseObjectEditor {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\bbcode\attribute\BBCodeAttribute';
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\attribute;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of bbcode attribute.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.attribute
+ * @category Community Framework
+ */
+class BBCodeAttributeList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\bbcode\attribute\BBCodeAttribute';
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\media\provider;
+use wcf\data\DatabaseObject;
+use wcf\system\cache\builder\BBCodeMediaProviderCacheBuilder;
+use wcf\system\request\IRouteController;
+use wcf\system\Regex;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a BBCode media provider.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.media.provider
+ * @category Community Framework
+ */
+class BBCodeMediaProvider extends DatabaseObject implements IRouteController {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'bbcode_media_provider';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'providerID';
+
+ /**
+ * cached providers
+ * @var array<\wcf\data\bbcode\media\MediaProvider>
+ */
+ protected static $cache = null;
+
+ /**
+ * Loads the provider cache.
+ *
+ * @return array<\wcf\data\bbcode\media\MediaProvider>
+ */
+ public static function getCache() {
+ if (self::$cache === null) {
+ self::$cache = BBCodeMediaProviderCacheBuilder::getInstance()->getData();
+ }
+
+ return self::$cache;
+ }
+
+ /**
+ * Returns true if given URL is a media URL.
+ *
+ * @param string $url
+ * @return boolean
+ */
+ public static function isMediaURL($url) {
+ foreach (static::getCache() as $provider) {
+ if ($provider->matches($url)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether this provider matches the given URL.
+ *
+ * @param string $url
+ * @return boolean
+ */
+ public function matches($url) {
+ $lines = explode("\n", StringUtil::unifyNewlines($this->regex));
+
+ foreach ($lines as $line) {
+ if (Regex::compile($line)->match($url)) return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the html for this provider.
+ *
+ * @param string $url
+ * @return string
+ */
+ public function getOutput($url) {
+ $lines = explode("\n", StringUtil::unifyNewlines($this->regex));
+
+ foreach ($lines as $line) {
+ $regex = new Regex($line);
+ if (!$regex->match($url)) continue;
+
+ $output = $this->html;
+ foreach ($regex->getMatches() as $name => $value) {
+ $output = StringUtil::replace('{$'.$name.'}', $value, $output);
+ }
+ return $output;
+ }
+
+ return '';
+ }
+
+ /**
+ * @see wcf\data\ITitledObject::getTitle()
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\media\provider;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\SystemException;
+use wcf\system\Regex;
+use wcf\util\StringUtil;
+
+/**
+ * Executes BBCode media provider-related actions.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2012 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.media.provider
+ * @category Community Framework
+ */
+class BBCodeMediaProviderAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\bbcode\media\provider\BBCodeMediaProviderEditor';
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.content.bbcode.canManageBBCode');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.content.bbcode.canManageBBCode');
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\media\provider;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\BBCodeMediaProviderCacheBuilder;
+
+/**
+ * Provides functions to edit BBCode media providers.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.media.provider
+ * @category Community Framework
+ */
+class BBCodeMediaProviderEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\bbcode\media\provider\BBCodeMediaProvider';
+
+ /**
+ * @see wcf\data\IEditableCachedObject::resetCache()
+ */
+ public static function resetCache() {
+ BBCodeMediaProviderCacheBuilder::getInstance()->reset();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\bbcode\media\provider;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of BBCode media providers.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2012 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.bbcode.media.provider
+ * @category Community Framework
+ */
+class BBCodeMediaProviderList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\bbcode\media\provider\BBCodeMediaProvider';
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a smiley.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley
+ * @category Community Framework
+ */
+class Smiley extends DatabaseObject {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'smiley';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'smileyID';
+
+ /**
+ * Returns the url to this smiley.
+ *
+ * @return string
+ */
+ public function getURL() {
+ return WCF::getPath().$this->smileyPath;
+ }
+
+ /**
+ * Returns all aliases for this smiley.
+ *
+ * @return array<string>
+ */
+ public function getAliases() {
+ if (!$this->aliases) return array();
+
+ return explode("\n", StringUtil::unifyNewlines($this->aliases));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\ISortableAction;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+
+/**
+ * Executes smiley-related actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley
+ * @category Community Framework
+ */
+class SmileyAction extends AbstractDatabaseObjectAction implements ISortableAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\smiley\SmileyEditor';
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.content.smiley.canManageSmiley');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.content.smiley.canManageSmiley');
+
+ /**
+ * @see wcf\data\ISortableAction::validateUpdatePosition()
+ */
+ public function validateUpdatePosition() {
+ // validate permissions
+ if (is_array($this->permissionsUpdate) && count($this->permissionsUpdate)) {
+ WCF::getSession()->checkPermissions($this->permissionsUpdate);
+ }
+ else {
+ throw new PermissionDeniedException();
+ }
+
+ if (!isset($this->parameters['data']['structure'])) {
+ throw new UserInputException('structure');
+ }
+
+ $this->readInteger('offset', true, 'data');
+ }
+
+ /**
+ * @see wcf\data\ISortableAction::updatePosition()
+ */
+ public function updatePosition() {
+ $smileyList = new SmileyList();
+ $smileyList->readObjects();
+
+ $i = $this->parameters['data']['offset'];
+ WCF::getDB()->beginTransaction();
+ foreach ($this->parameters['data']['structure'][0] as $smileyID) {
+ $smiley = $smileyList->search($smileyID);
+ if ($smiley === null) continue;
+
+ $editor = new SmileyEditor($smiley);
+ $editor->update(array('showOrder' => $i++));
+ }
+ WCF::getDB()->commitTransaction();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley;
+use wcf\data\category\Category;
+use wcf\data\smiley\category\SmileyCategory;
+use wcf\system\cache\builder\SmileyCacheBuilder;
+use wcf\system\category\CategoryHandler;
+use wcf\system\SingletonFactory;
+
+/**
+ * Manages the smiley cache.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley
+ * @category Community Framework
+ */
+class SmileyCache extends SingletonFactory {
+ /**
+ * cached smilies
+ * @var array
+ */
+ protected $cachedSmilies = array();
+
+ /**
+ * cached smiley categories
+ * @var array<wcf\data\smiley\category\SmileyCategory>
+ */
+ protected $cachedCategories = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ // get smiley cache
+ $this->cachedSmilies = SmileyCacheBuilder::getInstance()->getData(array(), 'smilies');
+ $smileyCategories = CategoryHandler::getInstance()->getCategories('com.woltlab.wcf.bbcode.smiley');
+
+ $this->cachedCategories[null] = new SmileyCategory(new Category(null, array(
+ 'categoryID' => null,
+ 'parentCategoryID' => 0,
+ 'title' => 'wcf.acp.smiley.categoryID.default',
+ 'description' => '',
+ 'showOrder' => -1,
+ 'isDisabled' => 0
+ )));
+
+ foreach ($smileyCategories as $key => $smileyCategory) {
+ $this->cachedCategories[$key] = new SmileyCategory($smileyCategory);
+ }
+ }
+
+ /**
+ * Returns all smilies.
+ *
+ * @return array
+ */
+ public function getSmilies() {
+ return $this->cachedSmilies;
+ }
+
+ /**
+ * Returns all smiley categories.
+ *
+ * @return array<wcf\data\smiley\category\SmileyCategory>
+ */
+ public function getCategories() {
+ return $this->cachedCategories;
+ }
+
+ /**
+ * Returns all the smilies of a category.
+ *
+ * @param integer $categoryID
+ * @return array
+ */
+ public function getCategorySmilies($categoryID = null) {
+ if (isset($this->cachedSmilies[$categoryID])) return $this->cachedSmilies[$categoryID];
+
+ return array();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\SmileyCacheBuilder;
+
+/**
+ * Provides functions to edit smilies.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley
+ * @category Community Framework
+ */
+class SmileyEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\smiley\Smiley';
+
+ /**
+ * @see wcf\data\IEditableCachedObject::resetCache()
+ */
+ public static function resetCache() {
+ SmileyCacheBuilder::getInstance()->reset();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of smilies.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley
+ * @category Community Framework
+ */
+class SmileyList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\smiley\Smiley';
+}
--- /dev/null
+<?php
+namespace wcf\data\smiley\category;
+use wcf\data\category\AbstractDecoratedCategory;
+use wcf\data\smiley\SmileyCache;
+use wcf\data\ITraversableObject;
+use wcf\system\exception\SystemException;
+
+/**
+ * Represents a smiley category.
+ *
+ * @author Tim Duesterhus, Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage data.smiley.category
+ * @category Community Framework
+ */
+class SmileyCategory extends AbstractDecoratedCategory implements \Countable, ITraversableObject {
+ /**
+ * current iterator index
+ * @var integer
+ */
+ protected $index = 0;
+
+ /**
+ * list of index to object relation
+ * @var array<integer>
+ */
+ protected $indexToObject = null;
+
+ /**
+ * list of assigned smilies
+ * @var array<wcf\data\smiley\Smiley>
+ */
+ public $smilies = null;
+
+ /**
+ * Loads associated smilies from cache.
+ */
+ public function loadSmilies() {
+ if ($this->smilies === null) {
+ $this->smilies = SmileyCache::getInstance()->getCategorySmilies($this->categoryID ?: null);
+ $this->indexToObject = array_keys($this->smilies);
+ }
+ }
+
+ /**
+ * @see \Countable::count()
+ */
+ public function count() {
+ return count($this->smilies);
+ }
+
+ /**
+ * @see \Iterator::current()
+ */
+ public function current() {
+ $objectID = $this->indexToObject[$this->index];
+ return $this->smilies[$objectID];
+ }
+
+ /**
+ * CAUTION: This methods does not return the current iterator index,
+ * rather than the object key which maps to that index.
+ *
+ * @see \Iterator::key()
+ */
+ public function key() {
+ return $this->indexToObject[$this->index];
+ }
+
+ /**
+ * @see \Iterator::next()
+ */
+ public function next() {
+ ++$this->index;
+ }
+
+ /**
+ * @see \Iterator::rewind()
+ */
+ public function rewind() {
+ $this->index = 0;
+ }
+
+ /**
+ * @see \Iterator::valid()
+ */
+ public function valid() {
+ return isset($this->indexToObject[$this->index]);
+ }
+
+ /**
+ * @see \SeekableIterator::seek()
+ */
+ public function seek($index) {
+ $this->index = $index;
+
+ if (!$this->valid()) {
+ throw new \OutOfBoundsException();
+ }
+ }
+
+ /**
+ * @see wcf\data\ITraversableObject::seekTo()
+ */
+ public function seekTo($objectID) {
+ $this->index = array_search($objectID, $this->indexToObject);
+
+ if ($this->index === false) {
+ throw new SystemException("object id '".$objectID."' is invalid");
+ }
+ }
+
+ /**
+ * @see wcf\data\ITraversableObject::search()
+ */
+ public function search($objectID) {
+ try {
+ $this->seekTo($objectID);
+ return $this->current();
+ }
+ catch (SystemException $e) {
+ return null;
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Provides an abstract implementation for bbcodes.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+abstract class AbstractBBCode extends DatabaseObjectDecorator implements IBBCode {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\bbcode\BBCode';
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\bbcode\BBCodeCache;
+use wcf\system\SingletonFactory;
+
+/**
+ * Handles BBCodes displayed as buttons within the WYSIWYG editor.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class BBCodeHandler extends SingletonFactory {
+ /**
+ * list of BBCodes displayed as buttons
+ * @var array<wcf\data\bbcode\BBCode>
+ */
+ protected $buttonBBCodes = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ foreach (BBCodeCache::getInstance()->getBBCodes() as $bbcode) {
+ if ($bbcode->showButton) {
+ $this->buttonBBCodes[] = $bbcode;
+ }
+ }
+ }
+
+ /**
+ * Returns a list of BBCodes displayed as buttons.
+ *
+ * @return array<wcf\data\bbcode\BBCode>
+ */
+ public function getButtonBBCodes() {
+ return $this->buttonBBCodes;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\bbcode\attribute\BBCodeAttribute;
+use wcf\data\bbcode\BBCodeCache;
+use wcf\system\SingletonFactory;
+use wcf\util\StringUtil;
+
+/**
+ * Parses bbcode tags in text.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class BBCodeParser extends SingletonFactory {
+ /**
+ * list of bbcodes
+ * @var array<wcf\data\bbcode\BBCode>
+ */
+ protected $bbcodes = array();
+
+ /**
+ * output type
+ * @var string
+ */
+ protected $outputType = 'text/html';
+
+ /**
+ * source text
+ * @var string
+ */
+ protected $text = '';
+
+ /**
+ * parsed text
+ * @var string
+ */
+ protected $parsedText = '';
+
+ /**
+ * tag array
+ * @var array
+ */
+ protected $tagArray = array();
+
+ /**
+ * text array
+ * @var array
+ */
+ protected $textArray = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ // get bbcodes
+ $this->bbcodes = BBCodeCache::getInstance()->getBBCodes();
+ }
+
+ /**
+ * Sets the output type of the parser.
+ *
+ * @param string $outputType
+ */
+ public function setOutputType($outputType) {
+ $this->outputType = $outputType;
+ }
+
+ /**
+ * Returns the current output type.
+ *
+ * @return string
+ */
+ public function getOutputType() {
+ return $this->outputType;
+ }
+
+ /**
+ * Sets the text to be parsed.
+ *
+ * @param string $text
+ */
+ public function setText($text) {
+ $this->text = $text;
+ }
+
+ /**
+ * Parses the given text.
+ *
+ * @param string $text
+ * @return string parsed text
+ */
+ public function parse($text) {
+ $this->setText($text);
+ $this->buildTagArray();
+ $this->buildXMLStructure();
+ $this->buildParsedString();
+
+ return $this->parsedText;
+ }
+
+ /**
+ * Builds a valid xml structure of bbcode tags.
+ * Inserts unclosed tags automatically.
+ */
+ public function buildXMLStructure() {
+ // stack for open tags
+ $openTagStack = $openTagDataStack = array();
+ $newTagArray = array();
+ $newTextArray = array();
+
+ $i = -1;
+ foreach ($this->tagArray as $i => $tag) {
+ if ($tag['closing']) {
+ // closing tag
+ if (in_array($tag['name'], $openTagStack) && $this->isAllowed($openTagStack, $tag['name'], true)) {
+ // close unclosed tags
+ $tmpOpenTags = array();
+ while (($previousTag = end($openTagStack)) != $tag['name']) {
+ $nextIndex = count($newTagArray);
+ $newTagArray[$nextIndex] = $this->buildTag('[/'.$previousTag.']');
+ if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = '';
+ $newTextArray[$nextIndex] .= $this->textArray[$i];
+ $this->textArray[$i] = '';
+ $tmpOpenTags[] = end($openTagDataStack);
+ array_pop($openTagStack);
+ array_pop($openTagDataStack);
+ }
+
+ $nextIndex = count($newTagArray);
+ $newTagArray[$nextIndex] = $tag;
+ array_pop($openTagStack);
+ array_pop($openTagDataStack);
+ if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = '';
+ $newTextArray[$nextIndex] .= $this->textArray[$i];
+
+ // open closed unclosed tags
+ while ($tmpTag = end($tmpOpenTags)) {
+ $nextIndex = count($newTagArray);
+ $newTagArray[$nextIndex] = $tmpTag;
+ if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = '';
+ $openTagStack[] = $tmpTag['name'];
+ $openTagDataStack[] = $tmpTag;
+ array_pop($tmpOpenTags);
+ }
+ }
+ else {
+ // no such tag open
+ // handle as plain text
+ $this->textArray[$i] .= $tag['source'];
+ $last = count($newTagArray);
+ if (!isset($newTextArray[$last])) $newTextArray[$last] = '';
+ $newTextArray[$last] .= $this->textArray[$i];
+ }
+ }
+ else {
+ // opening tag
+ if ($this->isAllowed($openTagStack, $tag['name']) && $this->isValidTag($tag)) {
+ $openTagStack[] = $tag['name'];
+ $openTagDataStack[] = $tag;
+ $nextIndex = count($newTagArray);
+ $newTagArray[$nextIndex] = $tag;
+ if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = '';
+ $newTextArray[$nextIndex] .= $this->textArray[$i];
+ }
+ else {
+ // tag not allowed
+ $this->textArray[$i] .= $tag['source'];
+ $last = count($newTagArray);
+ if (!isset($newTextArray[$last])) $newTextArray[$last] = '';
+ $newTextArray[$last] .= $this->textArray[$i];
+ }
+ }
+ }
+
+ $last = count($newTagArray);
+ if (!isset($newTextArray[$last])) $newTextArray[$last] = '';
+ $newTextArray[$last] .= $this->textArray[$i + 1];
+
+ // close unclosed open tags
+ while (end($openTagStack)) {
+ $nextIndex = count($newTagArray);
+ $newTagArray[$nextIndex] = $this->buildTag('[/'.end($openTagStack).']');
+ if (!isset($newTextArray[$nextIndex])) $newTextArray[$nextIndex] = '';
+ array_pop($openTagStack);
+ array_pop($openTagDataStack);
+ }
+
+ $this->tagArray = $newTagArray;
+ $this->textArray = $newTextArray;
+ }
+
+ /**
+ * Validates the attributes of a tag.
+ *
+ * @param array $tag
+ * @return boolean
+ */
+ protected function isValidTag(array $tag) {
+ if (isset($tag['attributes']) && count($tag['attributes']) > count($this->bbcodes[$tag['name']]->getAttributes())) {
+ return false;
+ }
+
+ foreach ($this->bbcodes[$tag['name']]->getAttributes() as $attribute) {
+ if (!$this->isValidTagAttribute((isset($tag['attributes']) ? $tag['attributes'] : array()), $attribute)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates an attributes of a tag.
+ *
+ * @param array $tagAttributes
+ * @param wcf\data\bbcode\BBCodeAttribute $definedTagAttribute
+ * @return boolean
+ */
+ protected function isValidTagAttribute(array $tagAttributes, BBCodeAttribute $definedTagAttribute) {
+ if ($definedTagAttribute->validationPattern && isset($tagAttributes[$definedTagAttribute->attributeNo])) {
+ // validate attribute
+ if (!preg_match('~'.$definedTagAttribute->validationPattern.'~i', $tagAttributes[$definedTagAttribute->attributeNo])) {
+ return false;
+ }
+ }
+
+ if ($definedTagAttribute->required && !$definedTagAttribute->useText && !isset($tagAttributes[$definedTagAttribute->attributeNo])) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if the text inside the given text needs to be buffered.
+ *
+ * @param array $tag
+ * @return boolean
+ */
+ protected function needBuffering(array $tag) {
+ // check for special bbcode class
+ if (!empty($this->bbcodes[$tag['name']]->className)) {
+ return true;
+ }
+
+ // search 'useText' attributes
+ foreach ($this->bbcodes[$tag['name']]->getAttributes() as $attribute) {
+ if ($attribute->useText && !isset($tag['attributes'][$attribute->attributeNo])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Builds the opening tag.
+ *
+ * @param array $tag
+ * @return string
+ */
+ protected function buildOpeningTag(array $tag) {
+ // build attributes
+ $attributesString = '';
+ foreach ($this->bbcodes[$tag['name']]->getAttributes() as $attribute) {
+ if (isset($tag['attributes'][$attribute->attributeNo])) {
+ $atrributeString = '';
+ if (!empty($attribute->attributeHtml)) {
+ $atrributeString = ' '.$attribute->attributeHtml;
+ }
+
+ if (!empty($atrributeString)) {
+ $attributesString .= sprintf($atrributeString, $tag['attributes'][$attribute->attributeNo]);
+ }
+ }
+ }
+
+ // build tag
+ if (!empty($this->bbcodes[$tag['name']]->htmlOpen)) {
+ return '<'.$this->bbcodes[$tag['name']]->htmlOpen.$attributesString.(empty($this->bbcodes[$tag['name']]->htmlClose) ? ' /' : '').'>';
+ }
+
+ return '';
+ }
+
+ /**
+ * Builds the closing tag.
+ *
+ * @param array $tag
+ * @return string
+ */
+ protected function buildClosingTag(array $tag) {
+ if (!empty($this->bbcodes[$tag['name']]->htmlClose)) {
+ return '</'.$this->bbcodes[$tag['name']]->htmlClose.'>';
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns true if the given tag is allowed in the given list of open tags.
+ *
+ * @param array $openTags
+ * @param string $tag
+ * @param boolean $closing
+ * @return boolean
+ */
+ protected function isAllowed(array $openTags, $tag, $closing = false) {
+ foreach ($openTags as $openTag) {
+ if ($closing && $openTag == $tag) continue;
+ if ($this->bbcodes[$openTag]->allowedChildren == 'all') continue;
+ if ($this->bbcodes[$openTag]->allowedChildren == 'none') return false;
+
+ $arguments = explode('^', $this->bbcodes[$openTag]->allowedChildren);
+ if (!empty($arguments[1])) $tags = explode(',', $arguments[1]);
+ else $tags = array();
+
+ if ($arguments[0] == 'none' && !in_array($tag, $tags)) return false;
+ if ($arguments[0] == 'all' && in_array($tag, $tags)) return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Builds the parsed string.
+ */
+ public function buildParsedString() {
+ // reset parsed text
+ $this->parsedText = '';
+
+ // create text buffer
+ $buffer =& $this->parsedText;
+
+ // stack of buffered tags
+ $bufferedTagStack = array();
+
+ // loop through the tags
+ $i = -1;
+ foreach ($this->tagArray as $i => $tag) {
+ // append text to buffer
+ $buffer .= $this->textArray[$i];
+
+ if ($tag['closing']) {
+ // get buffered opening tag
+ $openingTag = end($bufferedTagStack);
+
+ // closing tag
+ if ($openingTag && $openingTag['name'] == $tag['name']) {
+ $hideBuffer = false;
+ // insert buffered content as attribute value
+ foreach ($this->bbcodes[$tag['name']]->getAttributes() as $attribute) {
+ if ($attribute->useText && !isset($openingTag['attributes'][$attribute->attributeNo])) {
+ $openingTag['attributes'][$attribute->attributeNo] = $buffer;
+ $hideBuffer = true;
+ break;
+ }
+ }
+
+ // validate tag attributes again
+ if ($this->isValidTag($openingTag)) {
+ if ($this->bbcodes[$tag['name']]->getProcessor()) {
+ // build tag
+ $parsedTag = $this->bbcodes[$tag['name']]->getProcessor()->getParsedTag($openingTag, $buffer, $tag, $this);
+ }
+ else {
+ // build tag
+ $parsedTag = $this->buildOpeningTag($openingTag);
+ $closingTag = $this->buildClosingTag($tag);
+ if (!empty($closingTag) && $hideBuffer) $parsedTag .= $buffer.$closingTag;
+ }
+ }
+ else {
+ $parsedTag = $openingTag['source'].$buffer.$tag['source'];
+ }
+
+ // close current buffer
+ array_pop($bufferedTagStack);
+
+ // open previous buffer
+ if (count($bufferedTagStack) > 0) {
+ $bufferedTag =& $bufferedTagStack[count($bufferedTagStack) - 1];
+ $buffer =& $bufferedTag['buffer'];
+ }
+ else {
+ $buffer =& $this->parsedText;
+ }
+
+ // append parsed tag
+ $buffer .= $parsedTag;
+ }
+ else {
+ $buffer .= $this->buildClosingTag($tag);
+ }
+ }
+ else {
+ // opening tag
+ if ($this->needBuffering($tag)) {
+ // start buffering
+ $tag['buffer'] = '';
+ $bufferedTagStack[] = $tag;
+ $buffer =& $bufferedTagStack[(count($bufferedTagStack) - 1)]['buffer'];
+ }
+ else {
+ $buffer .= $this->buildOpeningTag($tag);
+ }
+ }
+ }
+
+ if (isset($this->textArray[$i + 1])) $this->parsedText .= $this->textArray[$i + 1];
+ }
+
+ /**
+ * Builds the tag array from the given text.
+ *
+ * @param boolean $ignoreSoureCodes
+ */
+ public function buildTagArray($ignoreSoureCodes = true) {
+ // build tag pattern
+ $validTags = '';
+ if (!$ignoreSoureCodes) {
+ $validTags = implode('|', array_keys($this->bbcodes));
+ }
+ else {
+ foreach ($this->bbcodes as $tag => $bbcode) {
+ if (!$bbcode->isSourceCode) {
+ // remove source codes
+ if (!empty($validTags)) $validTags .= '|';
+ $validTags .= $tag;
+ }
+ }
+ }
+ $pattern = '~\[(?:/(?:'.$validTags.')|(?:'.$validTags.')
+ (?:=
+ (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*)
+ (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))*
+ )?)\]~ix';
+
+ // get bbcode tags
+ preg_match_all($pattern, $this->text, $matches);
+ $this->tagArray = $matches[0];
+ unset($matches);
+
+ // build tags
+ for ($i = 0, $j = count($this->tagArray); $i < $j; $i++) {
+ $this->tagArray[$i] = $this->buildTag($this->tagArray[$i]);
+ }
+
+ // get text
+ $this->textArray = preg_split($pattern, $this->text);
+ }
+
+ /**
+ * Builds a bbcode tag.
+ *
+ * @param string $string
+ * @return array bbcode tag data
+ */
+ protected function buildTag($string) {
+ $tag = array('name' => '', 'closing' => false, 'source' => $string);
+
+ if (StringUtil::substring($string, 1, 1) == '/') {
+ // closing tag
+ $tag['name'] = StringUtil::toLowerCase(StringUtil::substring($string, 2, StringUtil::length($string) - 3));
+ $tag['closing'] = true;
+ }
+ else {
+ // opening tag
+ // split tag and attributes
+ preg_match("!^\[([a-z0-9]+)=?(.*)]$!si", $string, $match);
+ $tag['name'] = StringUtil::toLowerCase($match[1]);
+
+ // build attributes
+ if (!empty($match[2])) {
+ $tag['attributes'] = $this->buildTagAttributes($match[2]);
+ }
+ }
+
+ return $tag;
+ }
+
+ /**
+ * Builds the attributes of a bbcode tag.
+ *
+ * @param string $string
+ * @return array bbcode attributes
+ */
+ protected function buildTagAttributes($string) {
+ preg_match_all("~(?:^|,)('[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|[^,]*)~", $string, $matches);
+
+ // remove quotes
+ for ($i = 0, $j = count($matches[1]); $i < $j; $i++) {
+ if (StringUtil::substring($matches[1][$i], 0, 1) == "'" && StringUtil::substring($matches[1][$i], -1) == "'") {
+ $matches[1][$i] = StringUtil::replace("\'", "'", $matches[1][$i]);
+ $matches[1][$i] = StringUtil::replace("\\\\", "\\", $matches[1][$i]);
+
+ $matches[1][$i] = StringUtil::substring($matches[1][$i], 1, -1);
+ }
+ }
+
+ return $matches[1];
+ }
+
+ /**
+ * Validates the used BBCodes in the given text by the given allowed
+ * BBCodes and returns a list of used disallowed BBCodes.
+ *
+ * @param string $text
+ * @param array<string> $allowedBBCodes
+ * @return array<string>
+ */
+ public function validateBBCodes($text, array $allowedBBCodes) {
+ // if all BBCodes are allowed, return directly
+ if (in_array('all', $allowedBBCodes)) {
+ return array();
+ }
+
+ $this->setText($text);
+ $this->buildTagArray(false);
+
+ $usedDisallowedBBCodes = array();
+ foreach ($this->tagArray as $tag) {
+ if (!in_array($tag['name'], $allowedBBCodes) && !isset($usedDisallowedBBCodes[$tag['name']])) {
+ $usedDisallowedBBCodes[$tag['name']] = $tag['name'];
+ }
+ }
+
+ return $usedDisallowedBBCodes;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [code] bbcode tag.
+ *
+ * @author Tim Duesterhus, Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class CodeBBCode extends AbstractBBCode {
+ /**
+ * code type attribute value
+ * @var string
+ */
+ protected $codeType = '';
+
+ /**
+ * file name attribute value
+ * @var string
+ */
+ protected $filename = '';
+
+ /**
+ * start line numer attribute value
+ * @var string
+ */
+ protected $startLineNumber = 1;
+
+ /**
+ * already used ids for line numbers to prevent duplicate ids in the output
+ * @var array<string>
+ */
+ private static $codeIDs = array();
+
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ // encode html
+ $content = self::trim($content);
+
+ // get attributes
+ $this->mapAttributes($openingTag);
+
+ // fetch highlighter-classname
+ $className = '\wcf\system\bbcode\highlighter\PlainHighlighter';
+ if ($this->codeType) {
+ $className = '\wcf\system\bbcode\highlighter\\'.StringUtil::firstCharToUpperCase(StringUtil::toLowerCase($this->codeType)).'Highlighter';
+
+ switch (StringUtil::substring($className, strlen('\wcf\system\bbcode\highlighter\\'))) {
+ case 'ShellHighlighter':
+ $className = '\wcf\system\bbcode\highlighter\BashHighlighter';
+ break;
+
+ case 'C++Highlighter':
+ $className = '\wcf\system\bbcode\highlighter\CHighlighter';
+ break;
+
+ case 'JavascriptHighlighter':
+ $className = '\wcf\system\bbcode\highlighter\JsHighlighter';
+ break;
+
+ case 'LatexHighlighter':
+ $className = '\wcf\system\bbcode\highlighter\TexHighlighter';
+ break;
+ }
+ }
+ else {
+ // try to guess highlighter
+ if (StringUtil::indexOf($content, '<?php') !== false) {
+ $className = '\wcf\system\bbcode\highlighter\PhpHighlighter';
+ }
+ else if (StringUtil::indexOf($content, '<html') !== false) {
+ $className = '\wcf\system\bbcode\highlighter\HtmlHighlighter';
+ }
+ else if (StringUtil::indexOf($content, '<?xml') === 0) {
+ $className = '\wcf\system\bbcode\highlighter\XmlHighlighter';
+ }
+ else if ( StringUtil::indexOf($content, 'SELECT') === 0
+ || StringUtil::indexOf($content, 'UPDATE') === 0
+ || StringUtil::indexOf($content, 'INSERT') === 0
+ || StringUtil::indexOf($content, 'DELETE') === 0) {
+ $className = '\wcf\system\bbcode\highlighter\SqlHighlighter';
+ }
+ else if (StringUtil::indexOf($content, 'import java.') !== false) {
+ $className = '\wcf\system\bbcode\highlighter\JavaHighlighter';
+ }
+ else if ( StringUtil::indexOf($content, "---") !== false
+ && StringUtil::indexOf($content, "\n+++") !== false) {
+ $className = '\wcf\system\bbcode\highlighter\DiffHighlighter';
+ }
+ else if (StringUtil::indexOf($content, "\n#include ") !== false) {
+ $className = '\wcf\system\bbcode\highlighter\CHighlighter';
+ }
+ else if (StringUtil::indexOf($content, '#!/usr/bin/perl') === 0) {
+ $className = '\wcf\system\bbcode\highlighter\PerlHighlighter';
+ }
+ else if (StringUtil::indexOf($content, 'def __init__(self') !== false) {
+ $className = '\wcf\system\bbcode\highlighter\PythonHighlighter';
+ }
+ else if (Regex::compile('^#!/bin/(ba|z)?sh')->match($content)) {
+ $className = '\wcf\system\bbcode\highlighter\BashHighlighter';
+ }
+ else if (StringUtil::indexOf($content, '\\documentclass') !== false) {
+ $className = '\wcf\system\bbcode\highlighter\TexHighlighter';
+ }
+ else if (Regex::compile('[-\\+\\.,\\[\\]\\>\\<]{9}')->match($content)) {
+ // 9 times a brainfuck char in a row -> seems to be brainfuck
+ $className = '\wcf\system\bbcode\highlighter\BrainfuckHighlighter';
+ }
+ }
+
+ if (!class_exists($className)) {
+ $className = '\wcf\system\bbcode\highlighter\PlainHighlighter';
+ }
+
+ if ($parser->getOutputType() == 'text/html') {
+ $highlightedContent = self::fixMarkup(explode("\n", $className::getInstance()->highlight($content)));
+
+ // show template
+ WCF::getTPL()->assign(array(
+ 'lineNumbers' => self::makeLineNumbers($content, $this->startLineNumber),
+ 'startLineNumber' => $this->startLineNumber,
+ 'content' => $highlightedContent,
+ 'highlighter' => $className::getInstance(),
+ 'filename' => $this->filename
+ ));
+ return WCF::getTPL()->fetch('codeBBCodeTag');
+ }
+ else if ($parser->getOutputType() == 'text/simplified-html') {
+ return WCF::getLanguage()->getDynamicVariable('wcf.bbcode.code.text', array(
+ 'highlighterTitle' => $className::getInstance()->getTitle(),
+ 'lines' => substr_count($content, "\n") + 1
+ ));
+ }
+ }
+
+ /**
+ * Maps the arguments to the property they represent.
+ *
+ * @param array $openingTag
+ */
+ protected function mapAttributes(array $openingTag) {
+ // reset default values
+ $this->codeType = '';
+ $this->startLineNumber = 1;
+ $this->filename = '';
+
+ if (!isset($openingTag['attributes'])) {
+ return;
+ }
+
+ $attributes = $openingTag['attributes'];
+ switch (count($attributes)) {
+ case 1:
+ if (is_numeric($attributes[0])) {
+ $this->startLineNumber = intval($attributes[0]);
+ }
+ else if (StringUtil::indexOf($attributes[0], '.') === false) {
+ $this->codeType = $attributes[0];
+ }
+ else {
+ $this->filename = $attributes[0];
+ }
+ break;
+
+ case 2:
+ if (is_numeric($attributes[0])) {
+ $this->startLineNumber = intval($attributes[0]);
+ if (StringUtil::indexOf($attributes[1], '.') === false) {
+ $this->codeType = $attributes[1];
+ }
+ else {
+ $this->filename = $attributes[1];
+ }
+ }
+ else {
+ $this->codeType = $attributes[0];
+ $this->filename = $attributes[1];
+ }
+ break;
+
+ default:
+ $this->codeType = $attributes[0];
+ $this->startLineNumber = intval($attributes[1]);
+ $this->filename = $attributes[2];
+ break;
+ }
+
+ // correct illegal line number
+ if ($this->startLineNumber < 1) {
+ $this->startLineNumber = 1;
+ }
+ }
+
+ /**
+ * Returns a string with all line numbers
+ *
+ * @param string $code
+ * @param integer $start
+ * @return string
+ */
+ protected static function makeLineNumbers($code, $start, $split = "\n") {
+ $lines = explode($split, $code);
+
+ $lineNumbers = array();
+ $i = -1;
+ // find an unused codeID
+ do {
+ $codeID = StringUtil::substring(StringUtil::getHash($code), 0, 6).(++$i ? '_'.$i : '');
+ }
+ while (isset(self::$codeIDs[$codeID]));
+
+ // mark codeID as used
+ self::$codeIDs[$codeID] = true;
+
+ for ($i = $start, $j = count($lines) + $start; $i < $j; $i++) {
+ $lineNumbers[$i] = 'codeLine_'.$i.'_'.$codeID;
+ }
+ return $lineNumbers;
+ }
+
+ /**
+ * Removes empty lines from the beginning and end of a string.
+ *
+ * @param string $string
+ * @return string
+ */
+ protected static function trim($string) {
+ $string = preg_replace('/^\s*\n/', '', $string);
+ $string = preg_replace('/\n\s*$/', '', $string);
+ return $string;
+ }
+
+ /**
+ * Fixes markup that every line has proper number of opening and closing tags
+ *
+ * @param array<string> $lines
+ */
+ public static function fixMarkup(array $lines) {
+ static $spanRegex = null;
+ static $emptyTagRegex = null;
+ if ($spanRegex === null) {
+ $spanRegex = new Regex('(?:<span(?: class="(?:[^"])*")?>|</span>)');
+ $emptyTagRegex = new Regex('<span(?: class="(?:[^"])*")?></span>');
+ }
+
+ $openTags = array();
+ foreach ($lines as &$line) {
+ $spanRegex->match($line, true);
+ // open all tags again
+ $line = implode('', $openTags).$line;
+ $matches = $spanRegex->getMatches();
+
+ // parse opening and closing spans
+ foreach ($matches[0] as $match) {
+ if ($match === '</span>') array_pop($openTags);
+ else {
+ array_push($openTags, $match);
+ }
+ }
+
+ // close all tags
+ $line .= str_repeat('</span>', count($openTags));
+
+ // remove empty tags to avoid cluttering the output
+ $line = $emptyTagRegex->replace($line, '');
+ }
+ unset($line);
+ return $lines;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\IDatabaseObjectProcessor;
+
+/**
+ * Any special bbcode class should implement this interface.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf..bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+interface IBBCode extends IDatabaseObjectProcessor {
+ /**
+ * Returns the parsed bbcode tag.
+ *
+ * @param array $openingTag
+ * @param string $content
+ * @param array $closingTag
+ * @param wcf\system\bbcode\BBCodeParser $parser
+ * @return string
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser);
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [img] bbcode tag.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class ImageBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ $src = '';
+ if (isset($openingTag['attributes'][0])) {
+ $src = $openingTag['attributes'][0];
+ }
+
+ if ($parser->getOutputType() == 'text/html') {
+ $float = '';
+ if (isset($openingTag['attributes'][1])) {
+ $float = $openingTag['attributes'][1];
+ }
+
+ return '<img src="'.$src.'" class="jsResizeImage" alt=""'.($float ? ('style="float: '.$float.'; margin: ' . ($float == 'left' ? '0 15px 7px 0' : '0 0 7px 15px') . '"') : '').' />';
+ }
+ else if ($parser->getOutputType() == 'text/simplified-html') {
+ $src = StringUtil::decodeHTML($src);
+ return StringUtil::getAnchorTag($src);
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\system\SingletonFactory;
+use wcf\util\ArrayUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights keywords in text messages.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class KeywordHighlighter extends SingletonFactory {
+ /**
+ * search keywords
+ * @var array<string>
+ */
+ protected $keywords = array();
+
+ /**
+ * search query parameters
+ * @var array<string>
+ */
+ protected static $searchQueryKeys = array(
+ 'q', // google, msn, altavista
+ 'p', // yahoo
+ 'query', // lycos, fireball
+ 'eingabe', // metager
+ 'begriff', // acoon.de
+ 'keyword', // fixx.de
+ 'search', // excite.co.jp
+ // 'highlight', // burning board and other bulletin board systems ;)
+
+ // ???:
+ 'ask',
+ 'searchfor',
+ 'key',
+ 'keywords',
+ 'qry',
+ 'searchitem',
+ 'kwd',
+ 'recherche',
+ 'search_text',
+ 'search_term',
+ 'term',
+ 'terms',
+ 'qq',
+ 'qry_str',
+ 'qu',
+ //'s',
+ //'k',
+ //'t',
+ 'va'
+ );
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ // take keywords from request
+ if (isset($_GET['highlight'])) {
+ $this->parseKeywords($_GET['highlight']);
+ }
+ // take keywords from referer
+ else if (!empty($_SERVER['HTTP_REFERER'])) {
+ $url = parse_url($_SERVER['HTTP_REFERER']);
+ if (!empty($url['query'])) {
+ $query = explode('&', $url['query']);
+ foreach ($query as $element) {
+ if (strpos($element, '=') === false) continue;
+ list($varname, $value) = explode('=', $element, 2);
+
+ if (in_array($varname, static::$searchQueryKeys)) {
+ $this->parseKeywords(urldecode($value));
+ break;
+ }
+ }
+ }
+ }
+
+ if (!empty($this->keywords)) {
+ $this->keywords = array_unique($this->keywords);
+ $this->keywords = array_map('preg_quote', $this->keywords);
+ }
+ }
+
+ /**
+ * Parses search keywords.
+ *
+ * @param string $keywordString
+ */
+ protected function parseKeywords($keywordString) {
+ // convert encoding if necessary
+ if (!StringUtil::isASCII($keywordString) && !StringUtil::isUTF8($keywordString)) {
+ $keywordString = StringUtil::convertEncoding('ISO-8859-1', 'UTF-8', $keywordString);
+ }
+
+ // remove bad wildcards
+ $keywordString = preg_replace('/(?<!\w)\*/', '', $keywordString);
+
+ // remove search operators
+ $keywordString = preg_replace('/[\+\-><()~]+/', '', $keywordString);
+
+ if (StringUtil::substring($keywordString, 0, 1) == '"' && StringUtil::substring($keywordString, -1) == '"') {
+ // phrases search
+ $keywordString = StringUtil::trim(StringUtil::substring($keywordString, 1, -1));
+
+ if (!empty($keywordString)) {
+ $this->keywords = array_merge($this->keywords, array(StringUtil::encodeHTML($keywordString)));
+ }
+ }
+ else {
+ // replace word delimiters by space
+ $keywordString = str_replace(array('.', ','), ' ', $keywordString);
+
+ $keywords = ArrayUtil::encodeHTML(ArrayUtil::trim(explode(' ', $keywordString)));
+ if (!empty($keywords)) {
+ $this->keywords = array_merge($this->keywords, $keywords);
+ }
+ }
+ }
+
+ /**
+ * Highlights search keywords.
+ *
+ * @param string $text
+ * @return string highlighted text
+ */
+ public function doHighlight($text) {
+ if (empty($this->keywords)) return $text;
+
+ $keywordPattern = '('.implode('|', $this->keywords).')';
+ $keywordPattern = StringUtil::replace('\*', '\w*', $keywordPattern);
+ return preg_replace('+(?<!&|&\w{1}|&\w{2}|&\w{3}|&\w{4}|&\w{5}|&\w{6})'.$keywordPattern.'(?![^<]*>)+i', '<span class="highlight">\\1</span>', $text);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [list] bbcode tag.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class ListBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ if (StringUtil::indexOf($content, '[*]') !== false) {
+ // get list elements
+ $listElements = preg_split('/\[\*\]/', StringUtil::trim($content), -1, PREG_SPLIT_NO_EMPTY);
+
+ // remove empty elements
+ foreach ($listElements as $key => $val) {
+ $listElements[$key] = StringUtil::trim($val);
+ if (empty($listElements[$key]) || $listElements[$key] == '<br />') {
+ unset($listElements[$key]);
+ }
+ }
+
+ if (!empty($listElements)) {
+ // get list style type
+ $listType = 'disc';
+ if (isset($openingTag['attributes'][0])) $listType = $openingTag['attributes'][0];
+ $listType = strtolower($listType);
+
+ // replace old types
+ if ($listType == '1') $listType = 'decimal';
+ if ($listType == 'a') $listType = 'lower-latin';
+
+ if ($parser->getOutputType() == 'text/html') {
+ // build list html
+ $listHTML = 'ol';
+ if ($listType == 'none' || $listType == 'circle' || $listType == 'square' || $listType == 'disc') {
+ $listHTML = 'ul';
+ }
+
+ return '<'.$listHTML.' style="list-style-type: '.$listType.'" class="nativeList"><li>'.implode('</li><li>', $listElements).'</li></'.$listHTML.'>';
+ }
+ else if ($parser->getOutputType() == 'text/simplified-html') {
+ $result = '';
+
+ $i = 1;
+ foreach ($listElements as $listElement) {
+ switch ($listType) {
+ case 'decimal':
+ $result .= $i.'. ';
+ break;
+ default:
+ $result .= '- ';
+ }
+
+ $result .= $listElement."\n";
+ $i++;
+ }
+
+ return $result;
+ }
+ }
+ }
+
+ // no valid list
+ // return bbcode as text
+ return $openingTag['source'].$content.$closingTag['source'];
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\bbcode\media\provider\BBCodeMediaProvider;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [media] bbcode tag.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class MediaBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ $content = StringUtil::trim($content);
+
+ if ($parser->getOutputType() == 'text/html') {
+ foreach (BBCodeMediaProvider::getCache() as $provider) {
+ if ($provider->matches($content)) {
+ return $provider->getOutput($content);
+ }
+ }
+ }
+ if ($parser->getOutputType() == 'text/simplified-html') {
+ foreach (BBCodeMediaProvider::getCache() as $provider) {
+ if ($provider->matches($content)) {
+ return StringUtil::getAnchorTag(StringUtil::decodeHTML($content));
+ }
+ }
+ }
+
+ return $content;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\bbcode\attribute\BBCodeAttribute;
+use wcf\data\smiley\SmileyCache;
+use wcf\system\event\EventHandler;
+use wcf\util\StringUtil;
+
+/**
+ * Parses bbcode tags, smilies etc. in messages.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class MessageParser extends BBCodeParser {
+ /**
+ * list of smilies
+ * @var array<wcf\data\smiley\Smiley>
+ */
+ protected $smilies = array();
+
+ /**
+ * cached bbcodes
+ * @var array
+ */
+ protected $cachedCodes = array();
+
+ /**
+ * regular expression for source code tags
+ * @var string
+ */
+ protected $sourceCodeRegEx = '';
+
+ /**
+ * currently parsed message
+ * @var string
+ */
+ public $message = '';
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ parent::init();
+
+ // handle source codes
+ $sourceCodeTags = array();
+ foreach ($this->bbcodes as $bbcode) {
+ if ($bbcode->isSourceCode) $sourceCodeTags[] = $bbcode->bbcodeTag;
+ }
+ if (!empty($sourceCodeTags)) $this->sourceCodeRegEx = implode('|', $sourceCodeTags);
+
+ if (MODULE_SMILEY == 1) {
+ // get smilies
+ $smilies = SmileyCache::getInstance()->getSmilies();
+ $categories = SmileyCache::getInstance()->getCategories();
+ foreach ($smilies as $categoryID => $categorySmilies) {
+ if ($categories[$categoryID ?: null]->isDisabled) continue;
+
+ foreach ($categorySmilies as $smiley) {
+ foreach ($smiley->smileyCodes as $smileyCode) {
+ $this->smilies[$smileyCode] = '<img src="'.$smiley->getURL().'" alt="'.StringUtil::encodeHTML($smiley->smileyCode).'" />';
+ }
+ }
+ }
+ krsort($this->smilies);
+ }
+ }
+
+ /**
+ * Parses a message.
+ *
+ * @param string $message
+ * @param boolean $enableSmilies
+ * @param boolean $enableHtml
+ * @param boolean $enableBBCodes
+ * @param boolean $doKeywordHighlighting
+ * @return string parsed message
+ */
+ public function parse($message, $enableSmilies = true, $enableHtml = false, $enableBBCodes = true, $doKeywordHighlighting = true) {
+ $this->cachedCodes = array();
+ $this->message = $message;
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'beforeParsing');
+
+ if ($enableBBCodes) {
+ // cache codes
+ $this->message = $this->cacheCodes($this->message);
+ }
+
+ if (!$enableHtml) {
+ // encode html
+ $this->message = StringUtil::encodeHTML($this->message);
+
+ // converts newlines to <br />'s
+ if ($this->getOutputType() == 'text/html') {
+ $this->message = nl2br($this->message);
+ }
+ }
+
+ // parse bbcodes
+ if ($enableBBCodes) {
+ $this->message = parent::parse($this->message);
+ }
+
+ // parse smilies
+ if ($enableSmilies) {
+ $this->message = $this->parseSmilies($this->message, $enableHtml);
+ }
+
+ if ($enableBBCodes && !empty($this->cachedCodes)) {
+ // insert cached codes
+ $this->message = $this->insertCachedCodes($this->message);
+ }
+
+ // highlight search query
+ if ($doKeywordHighlighting) {
+ $this->message = KeywordHighlighter::getInstance()->doHighlight($this->message);
+ }
+
+ // replace bad html tags (script etc.)
+ $badSearch = array('/(javascript):/i', '/(about):/i', '/(vbscript):/i');
+ $badReplace = array('$1<b></b>:', '$1<b></b>:', '$1<b></b>:');
+ $this->message = preg_replace($badSearch, $badReplace, $this->message);
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'afterParsing');
+
+ return $this->message;
+ }
+
+ /**
+ * Parses smiley codes.
+ *
+ * @param string $text
+ * @return string text
+ */
+ protected function parseSmilies($text, $enableHtml = false) {
+ foreach ($this->smilies as $code => $html) {
+ //$text = preg_replace('~(?<!&\w{2}|&\w{3}|&\w{4}|&\w{5}|&\w{6}|&#\d{2}|&#\d{3}|&#\d{4}|&#\d{5})'.preg_quote((!$enableHtml ? StringUtil::encodeHTML($code) : $code), '~').'(?![^<]*>)~', $html, $text);
+ $text = preg_replace('~(?<=^|\s)'.preg_quote((!$enableHtml ? StringUtil::encodeHTML($code) : $code), '~').'(?=$|\s|</li>'.(!$enableHtml ? '|<br />' : '').')~', $html, $text);
+ }
+
+ return $text;
+ }
+
+ /**
+ * Caches code bbcodes to avoid parsing of smileys and other bbcodes inside them.
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function cacheCodes($text) {
+ if (!empty($this->sourceCodeRegEx)) {
+ $text = preg_replace_callback("~(\[(".$this->sourceCodeRegEx.")
+ (?:=
+ (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*)
+ (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))*
+ )?\])
+ (.*?)
+ (?:\[/\\2\])~six", array($this, 'cacheCodesCallback'), $text);
+ }
+ return $text;
+ }
+
+ /**
+ * Returns the hash for an matched code bbcode in the message.
+ *
+ * @param array $matches
+ * @return string
+ */
+ protected function cacheCodesCallback($matches) {
+ // create hash
+ $hash = '@@'.StringUtil::getHash(uniqid(microtime()).$matches[3]).'@@';
+
+ // build tag
+ $tag = $this->buildTag($matches[1]);
+ $tag['content'] = $matches[3];
+
+ // save tag
+ $this->cachedCodes[$hash] = $tag;
+
+ return $hash;
+ }
+
+ /**
+ * Reinserts cached code bbcodes.
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function insertCachedCodes($text) {
+ foreach ($this->cachedCodes as $hash => $tag) {
+ // build code and insert
+ if ($this->bbcodes[$tag['name']]->className) {
+ $replacement = $this->bbcodes[$tag['name']]->getProcessor()->getParsedTag($tag, $tag['content'], $tag, $this);
+ }
+ else {
+ $replacement = $this->buildOpeningTag($tag) . StringUtil::encodeHTML($tag['content']) . $this->buildClosingTag($tag);
+ }
+
+ $text = str_replace($hash, $replacement, $text);
+ }
+
+ return $text;
+ }
+
+ /**
+ * @see wcf\system\bbcode\BBCodeParser::isValidTagAttribute()
+ */
+ protected function isValidTagAttribute(array $tagAttributes, BBCodeAttribute $definedTagAttribute) {
+ if (!parent::isValidTagAttribute($tagAttributes, $definedTagAttribute)) {
+ return false;
+ }
+
+ // check for cached codes
+ if (isset($tagAttributes[$definedTagAttribute->attributeNo]) && preg_match('/@@[a-f0-9]{40}@@/', $tagAttributes[$definedTagAttribute->attributeNo])) {
+ return false;
+ }
+
+ return true;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\bbcode\media\provider\BBCodeMediaProvider;
+use wcf\data\bbcode\BBCode;
+use wcf\data\bbcode\BBCodeCache;
+use wcf\system\event\EventHandler;
+use wcf\system\Callback;
+use wcf\system\Regex;
+use wcf\system\SingletonFactory;
+use wcf\util\StringStack;
+use wcf\util\StringUtil;
+
+/**
+ * Parses message before inserting them into the database.
+ *
+ * @author Tim Duesterhus, Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class PreParser extends SingletonFactory {
+ /**
+ * forbidden characters
+ * @var string
+ */
+ protected static $illegalChars = '[^\x0-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+';
+
+ /**
+ * list of allowed bbcode tags
+ * @var array<string>
+ */
+ public $allowedBBCodes = null;
+
+ /**
+ * regular expression for source codes
+ * @var string
+ */
+ protected $sourceCodeRegEx = '';
+
+ /**
+ * text
+ * @var string
+ */
+ public $text = '';
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $sourceCodeTags = array();
+ foreach (BBCodeCache::getInstance()->getBBCodes() as $bbcode) {
+ if ($bbcode->isSourceCode) $sourceCodeTags[] = $bbcode->bbcodeTag;
+ }
+ if (!empty($sourceCodeTags)) $this->sourceCodeRegEx = implode('|', $sourceCodeTags);
+ }
+
+ /**
+ * Preparses the given text.
+ *
+ * @param string $text
+ * @param array<string> $allowedBBCodes
+ * @return string
+ */
+ public function parse($text, array $allowedBBCodes = null) {
+ $this->text = $text;
+ $this->allowedBBCodes = $allowedBBCodes;
+
+ // cache codes
+ $this->cacheCodes();
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'beforeParsing');
+
+ // parse urls
+ if ($this->allowedBBCodes === null || BBCode::isAllowedBBCode('media', $this->allowedBBCodes) || BBCode::isAllowedBBCode('url', $this->allowedBBCodes)) {
+ $this->parseURLs();
+ }
+
+ // parse email addresses
+ if ($this->allowedBBCodes === null || BBCode::isAllowedBBCode('email', $this->allowedBBCodes)) {
+ $this->parseEmails();
+ }
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'afterParsing');
+
+ // insert cached codes
+ $this->insertCachedCodes();
+
+ return $this->text;
+ }
+
+ /**
+ * Handles pre-parsing of email addresses.
+ */
+ protected function parseEmails() {
+ if (StringUtil::indexOf($this->text, '@') === false) return;
+
+ static $emailPattern = null;
+ if ($emailPattern === null) {
+ $emailPattern = new Regex('
+ (?<!\B|"|\'|=|/|\]|,|:)
+ (?:)
+ \w+(?:[\.\-]\w+)*
+ @
+ (?:'.self::$illegalChars.'\.)+ # hostname
+ (?:[a-z]{2,4}(?=\b))
+ (?!"|\'|\[|\-|\.[a-z])', Regex::IGNORE_WHITESPACE | Regex::CASE_INSENSITIVE);
+ }
+
+ $this->text = $emailPattern->replace($this->text, '[email]\\0[/email]');
+ }
+
+ /**
+ * Handles pre-parsing of URLs.
+ */
+ protected function parseURLs() {
+ static $urlPattern = null;
+ static $callback = null;
+ if ($urlPattern === null) {
+ $urlPattern = new Regex('
+ (?<!\B|"|\'|=|/|\]|,|\?)
+ (?: # hostname
+ (?:ftp|https?)://'.static::$illegalChars.'(?:\.'.static::$illegalChars.')*
+ |
+ www\.(?:'.static::$illegalChars.'\.)+
+ (?:[a-z]{2,4}(?=\b))
+ )
+
+ (?::\d+)? # port
+
+ (?:
+ /
+ [^!.,?;"\'<>()\[\]{}\s]*
+ (?:
+ [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+
+ )*
+ )?', Regex::IGNORE_WHITESPACE | Regex::CASE_INSENSITIVE);
+ }
+ if ($callback === null) {
+ $callback = new Callback(function ($matches) {
+ if ((PreParser::getInstance()->allowedBBCodes === null || BBCode::isAllowedBBCode('media', PreParser::getInstance()->allowedBBCodes)) && BBCodeMediaProvider::isMediaURL($matches[0])) {
+ return '[media]'.$matches[0].'[/media]';
+ }
+
+ if (PreParser::getInstance()->allowedBBCodes === null || BBCode::isAllowedBBCode('url', PreParser::getInstance()->allowedBBCodes)) {
+ return '[url]'.$matches[0].'[/url]';
+ }
+
+ return $matches[0];
+ });
+ }
+
+ $this->text = $urlPattern->replace($this->text, $callback);
+ }
+
+ /**
+ * Caches code bbcodes to avoid parsing inside them.
+ */
+ protected function cacheCodes() {
+ if (!empty($this->sourceCodeRegEx)) {
+ static $bbcodeRegex = null;
+ static $callback = null;
+
+ if ($bbcodeRegex === null) {
+ $bbcodeRegex = new Regex("
+ (\[(".$this->sourceCodeRegEx.")
+ (?:=
+ (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*)
+ (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\]]*))*
+ )?\])
+ (.*?)
+ (?:\[/\\2\])", Regex::DOT_ALL | Regex::IGNORE_WHITESPACE | Regex::CASE_INSENSITIVE);
+
+ $callback = new Callback(function ($matches) {
+ return StringStack::pushToStringStack($matches[0], 'preParserCode');
+ });
+ }
+
+ $this->cachedCodes = array();
+ $this->text = $bbcodeRegex->replace($this->text, $callback);
+ }
+ }
+
+ /**
+ * Reinserts cached code bbcodes.
+ */
+ protected function insertCachedCodes() {
+ $this->text = StringStack::reinsertStrings($this->text, 'preParserCode');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\system\WCF;
+
+/**
+ * Parses the [quote] bbcode tag.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class QuoteBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ if ($parser->getOutputType() == 'text/html') {
+ WCF::getTPL()->assign(array(
+ 'content' => $content,
+ 'quoteLink' => (!empty($openingTag['attributes'][1]) ? $openingTag['attributes'][1] : ''),
+ 'quoteAuthor' => (!empty($openingTag['attributes'][0]) ? $openingTag['attributes'][0] : '')
+ ));
+ return WCF::getTPL()->fetch('quoteBBCodeTag');
+ }
+ else if ($parser->getOutputType() == 'text/simplified-html') {
+ return WCF::getLanguage()->getDynamicVariable('wcf.bbcode.quote.text', array('content' => $content, 'cite' => (!empty($openingTag['attributes'][0]) ? $openingTag['attributes'][0] : '')));
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\smiley\SmileyCache;
+use wcf\system\event\EventHandler;
+use wcf\system\SingletonFactory;
+use wcf\util\StringUtil;
+
+/**
+ * Parses urls and smilies in simple messages.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class SimpleMessageParser extends SingletonFactory {
+ /**
+ * forbidden characters
+ * @var string
+ */
+ protected static $illegalChars = '[^\x0-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+';
+
+ /**
+ * list of smilies
+ * @var array<wcf\data\smiley\Smiley>
+ */
+ protected $smilies = array();
+
+ /**
+ * currently parsed message
+ * @var string
+ */
+ public $message = '';
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ parent::init();
+
+ if (MODULE_SMILEY == 1) {
+ // get smilies
+ $smilies = SmileyCache::getInstance()->getSmilies();
+ $categories = SmileyCache::getInstance()->getCategories();
+ foreach ($smilies as $categoryID => $categorySmilies) {
+ if ($categories[$categoryID ?: null]->isDisabled) continue;
+
+ foreach ($categorySmilies as $smiley) {
+ foreach ($smiley->smileyCodes as $smileyCode) {
+ $this->smilies[$smileyCode] = '<img src="'.$smiley->getURL().'" alt="'.StringUtil::encodeHTML($smiley->smileyCode).'" />';
+ }
+ }
+ }
+ krsort($this->smilies);
+ }
+ }
+
+ /**
+ * Parses the given message and returns the parsed message.
+ *
+ * @param string $message
+ * @param boolean $parseURLs
+ * @param boolean $parseSmilies
+ * @return string
+ */
+ public function parse($message, $parseURLs = true, $parseSmilies = true) {
+ $this->message = $message;
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'beforeParsing');
+
+ // encode html
+ $this->message = StringUtil::encodeHTML($this->message);
+
+ // converts newlines to <br />'s
+ $this->message = nl2br($this->message);
+
+ // parse urls
+ if ($parseURLs) {
+ $this->message = $this->parseURLs($this->message);
+ }
+
+ // parse smilies
+ if ($parseSmilies) {
+ $this->message = $this->parseSmilies($this->message);
+ }
+
+ // replace bad html tags (script etc.)
+ $badSearch = array('/(javascript):/i', '/(about):/i', '/(vbscript):/i');
+ $badReplace = array('$1<b></b>:', '$1<b></b>:', '$1<b></b>:');
+ $this->message = preg_replace($badSearch, $badReplace, $this->message);
+
+ // call event
+ EventHandler::getInstance()->fireAction($this, 'afterParsing');
+
+ return $this->message;
+ }
+
+ /**
+ * Parses urls.
+ *
+ * @param string $text
+ * @return string text
+ */
+ public function parseURLs($text) {
+ // define pattern
+ $urlPattern = '~(?<!\B|"|\'|=|/|\]|,|\?)
+ (?: # hostname
+ (?:ftp|https?)://'.static::$illegalChars.'(?:\.'.static::$illegalChars.')*
+ |
+ www\.(?:'.static::$illegalChars.'\.)+
+ (?:[a-z]{2,4}(?=\b))
+ )
+
+ (?::\d+)? # port
+
+ (?:
+ /
+ [^!.,?;"\'<>()\[\]{}\s]*
+ (?:
+ [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+
+ )*
+ )?
+ ~ix';
+ $emailPattern = '~(?<!\B|"|\'|=|/|\]|,|:)
+ (?:)
+ \w+(?:[\.\-]\w+)*
+ @
+ (?:'.static::$illegalChars.'\.)+ # hostname
+ (?:[a-z]{2,4}(?=\b))
+ (?!"|\'|\[|\-)
+ ~ix';
+
+ // parse urls
+ $text = preg_replace_callback($urlPattern, array($this, 'parseURLsCallback'), $text);
+
+ // parse emails
+ if (StringUtil::indexOf($text, '@') !== false) {
+ $text = preg_replace($emailPattern, '<a href="mailto:\\0">\\0</a>', $text);
+ }
+
+ return $text;
+ }
+
+ /**
+ * Callback for preg_replace.
+ *
+ * @see wcf\system\bbcode\SimpleMessageParser::parseURLs()
+ */
+ protected function parseURLsCallback($matches) {
+ return StringUtil::getAnchorTag(StringUtil::decodeHTML($matches[0]));
+ }
+
+ /**
+ * Parses smiley codes.
+ *
+ * @param string $text
+ * @return string text
+ */
+ public function parseSmilies($text) {
+ foreach ($this->smilies as $code => $html) {
+ //$text = preg_replace('~(?<!&\w{2}|&\w{3}|&\w{4}|&\w{5}|&\w{6}|&#\d{2}|&#\d{3}|&#\d{4}|&#\d{5})'.preg_quote(StringUtil::encodeHTML($code), '~').'(?![^<]*>)~', $html, $text);
+ $text = preg_replace('~(?<=^|\s)'.preg_quote(StringUtil::encodeHTML($code), '~').'(?=$|\s|<br />)~', $html, $text);
+ }
+
+ return $text;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\system\WCF;
+
+/**
+ * Parses the [spoiler] bbcode tag.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class SpoilerBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ if ($parser->getOutputType() == 'text/html') {
+ WCF::getTPL()->assign(array(
+ 'content' => $content,
+ 'buttonTitle' => (!empty($openingTag['attributes'][0]) ? $openingTag['attributes'][0] : '')
+ ));
+ return WCF::getTPL()->fetch('spoilerBBCodeTag');
+ }
+ if ($parser->getOutputType() == 'text/simplified-html') {
+ return WCF::getLanguage()->get('wcf.bbcode.spoiler.text');
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\system\Regex;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [table] bbcode tag.
+ *
+ * @author Tim Duesterhus, Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class TableBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ if ($parser->getOutputType() == 'text/html') {
+ $parsedContent = Regex::compile('(?:\s|<br />)*(\[tr\].*\[/tr\])(?:\s|<br />)*', Regex::CASE_INSENSITIVE | Regex::DOT_ALL)->replace($content, '\\1');
+
+ // check syntax
+ $regex = new Regex('\[/?t[rd]\]', Regex::CASE_INSENSITIVE);
+ if ($regex->match($parsedContent, true)) {
+ $matches = $regex->getMatches();
+
+ $openTags = array();
+ $openTDs = 0;
+ $firstRowTDs = 0;
+
+ // parse tags
+ foreach ($matches[0] as $match) {
+ switch ($match) {
+ case '[td]':
+ if (end($openTags) !== '[tr]') return;
+ $openTags[] = $match;
+ $openTDs++;
+ break;
+ case '[/td]':
+ if (end($openTags) !== '[td]') return;
+ array_pop($openTags);
+ break;
+ case '[tr]':
+ if (!empty($openTags)) return;
+ $openTags[] = $match;
+ break;
+ case '[/tr]':
+ if (end($openTags) !== '[tr]') return;
+
+ array_pop($openTags);
+
+ // check that every row has got the same number of tds
+ if ($firstRowTDs === 0) $firstRowTDs = $openTDs;
+ if ($openTDs !== $firstRowTDs) return;
+
+ $openTDs = 0;
+ break;
+ }
+ }
+
+ if (!empty($openTags)) return;
+ }
+ else {
+ return '';
+ }
+
+ // tr
+ $parsedContent = Regex::compile('\[tr\](?:\s|<br />)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '<tr>');
+ // td
+ $parsedContent = StringUtil::replaceIgnoreCase('[td]', '<td>', $parsedContent);
+ // /td
+ $parsedContent = Regex::compile('\[/td\](?:\s|<br />)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '</td>');
+ // /tr
+ $parsedContent = Regex::compile('\[/tr\](?:\s|<br />)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '</tr>');
+
+ return '<div class="container bbcodeTable"><table class="table"><tbody>'.$parsedContent.'</tbody></table></div>';
+ }
+ else if ($parser->getOutputType() == 'text/simplified-html') {
+ // remove table tags
+ $content = StringUtil::replaceIgnoreCase('[td]', '* ', $content);
+ $content = StringUtil::replaceIgnoreCase('[/td]', ' ', $content);
+ $content = StringUtil::replaceIgnoreCase('[tr]', '', $content);
+ $content = StringUtil::replaceIgnoreCase('[/tr]', '', $content);
+
+ return $content;
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [url] bbcode tag.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode
+ * @category Community Framework
+ */
+class URLBBCode extends AbstractBBCode {
+ /**
+ * @see wcf\system\bbcode\IBBCode::getParsedTag()
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ $url = '';
+ if (isset($openingTag['attributes'][0])) {
+ $url = $openingTag['attributes'][0];
+ }
+
+ $noTitle = ($content == $url);
+ $url = StringUtil::decodeHTML($url);
+
+ // add protocol if necessary
+ if (!preg_match("/[a-z]:\/\//si", $url)) $url = 'http://'.$url;
+
+ return StringUtil::getAnchorTag($url, (!$noTitle ? $content : ''), false);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of bash scripts.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2012 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class BashHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array(';', '=');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$quotes
+ */
+ protected $quotes = array('"', "'", '`');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('#');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentStart
+ */
+ protected $commentStart = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentEnd
+ */
+ protected $commentEnd = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array('||', '&&', '&', '|', '<<=', '>>=', '<<', '+=', '-=', '*=', '/=', '%=',
+ '-gt', '-lt', '-n', '-a', '-o',
+ '+', '-', '*', '/', '%', '<', '?', ':', '==', '!=', '=', '!', '>', '2>', '>>');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'true',
+ 'false'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'if',
+ 'then',
+ 'else',
+ 'fi',
+ 'for',
+ 'until',
+ 'while',
+ 'do',
+ 'done',
+ 'case',
+ 'in',
+ 'esac'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ 'echo',
+ 'exit',
+ 'unset',
+ 'read',
+ '[', ']', 'test',
+ 'let',
+ 'sed',
+ 'grep',
+ 'awk'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords4
+ */
+ protected $keywords4 = array(
+ '$?'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of brainfuck.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class BrainfuckHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlight()
+ */
+ public function highlight($string) {
+ $string = preg_replace('/[^-\\+\\.,\\[\\]\\>\\<]+/', '||span class="hlComments"||\\0||/span||', $string);
+ $string = preg_replace('/[\\<\\>]+/', '<span class="hlKeywords4">\\0</span>', $string);
+ $string = preg_replace('/[-\\+]+/', '<span class="hlKeywords1">\\0</span>', $string);
+ $string = preg_replace('/[\\.,]+/', '<span class="hlKeywords2">\\0</span>', $string);
+ $string = preg_replace('/[\\[\\]]+/', '<span class="hlKeywords3">\\0</span>', $string);
+
+ $string = str_replace(array('||span class="hlComments"||', '||/span||'), array('<span class="hlComments">', '</span>'), $string);
+ return $string;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of c / c++ source code.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class CHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array('(', ')', '{', '}', '[', ']', ';', '.', ',');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array('=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=',
+ '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%', '<<', '>>', '>>>', '+=', '-=', '*=',
+ '/=', '&=', '|=', '^=', '%=', '<<=', '>>=', '>>>=');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'and',
+ 'and_eq',
+ 'asm',
+ 'bitand',
+ 'bitor',
+ 'break',
+ 'case',
+ 'catch',
+ 'compl',
+ 'const_cast',
+ 'continue',
+ 'default',
+ 'delete',
+ 'do',
+ 'dynamic_cast',
+ 'else',
+ 'for',
+ 'fortran',
+ 'friend',
+ 'goto',
+ 'if',
+ 'new',
+ 'not',
+ 'not_eq',
+ 'operator',
+ 'or',
+ 'or_eq',
+ 'private',
+ 'protected',
+ 'public',
+ 'reinterpret_cast',
+ 'return',
+ 'sizeof',
+ 'static_cast',
+ 'switch',
+ 'this',
+ 'throw',
+ 'try',
+ 'typeid',
+ 'using',
+ 'while',
+ 'xor',
+ 'xor_eq'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'auto',
+ 'bool',
+ 'char',
+ 'class',
+ 'const',
+ 'double',
+ 'enum',
+ 'explicit',
+ 'export',
+ 'extern',
+ 'float',
+ 'inline',
+ 'int',
+ 'long',
+ 'mutable',
+ 'namespace',
+ 'register',
+ 'short',
+ 'signed',
+ 'static',
+ 'struct',
+ 'template',
+ 'typedef',
+ 'typename',
+ 'union',
+ 'unsigned',
+ 'virtual',
+ 'void',
+ 'volatile',
+ 'wchar_t'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ '#include',
+ '#define',
+ '#if',
+ '#else',
+ '#ifdef',
+ '#endif'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of cascading style sheets.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class CssHighlighter extends Highlighter {
+ /**
+ * temporary string replacement map for properties that can also be tags
+ * @var array<string>
+ */
+ public static $duplicates = array(
+ 'table:' => 't@@able:',
+ 'caption:' => 'c@@aption:',
+ 'menu:' => 'm@@enu:',
+ 'code:' => 'c@@ode:',
+ 'sub:' => 's@@ub:',
+ 'pre:' => 'p@@re:',
+ 'small:' => 's@@mall:'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightNumbers()
+ */
+ protected function highlightNumbers($string) {
+ $string = preg_replace('!(?<='.$this->separatorsRegEx.')(-?\d*\.?\d+(?:px|pt|em|%|ex|in|cm|mm|pc)?)(?='.$this->separatorsRegEx.')!i', '<span class="hlNumbers">\\0</span>', $string);
+
+ // highlight colors (hexadecimal numbers)
+ $string = preg_replace('!(?<='.$this->separatorsRegEx.')(#([0-9a-f]{3}|[0-9a-f]{6}))(?='.$this->separatorsRegEx.')!i', '<span class="hlColors">\\0</span>', $string);
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightKeywords()
+ */
+ protected function highlightKeywords($string) {
+ $string = parent::highlightKeywords($string);
+ $string = preg_replace('!(?<='.$this->separatorsRegEx.')(@[a-z0-9-]+)(?='.$this->separatorsRegEx.')!i', '<span class="hlKeywords5">\\0</span>', $string);
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlight()
+ */
+ public function highlight($string) {
+ $string = str_replace('span', '053a0024219422ca9215c0a3ed0578ee76cff477', $string); // fix to not highlight the spans of the highlighter
+ $string = str_replace(':link', ':li@@nk', $string); // fix to highlight pseudo-class different than tag
+ $string = str_replace(array('right:', 'left:'), array('r@@ight:', 'l@@eft:'), $string); // fix to highlight properties different than values
+ $string = strtr($string, self::$duplicates); // fix to highlight properties different than tags
+
+ $string = parent::highlight($string);
+
+ $string = strtr($string, array_flip(self::$duplicates)); // fix to highlight properties different than tags
+ $string = str_replace(array('r@@ight', 'l@@eft'), array('right', 'left'), $string); // fix to highlight properties different than values
+ $string = str_replace('li@@nk', 'link', $string); // fix to highlight pseudo-class different than tag
+ return str_replace('053a0024219422ca9215c0a3ed0578ee76cff477', 'span', $string); // fix to not highlight the spans of the highlighter
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('//');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array('(', ')', '{', '}', ';', '[', ']', ':', ',', '.');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'azimuth',
+ 'background',
+ 'background-attachment',
+ 'background-clip',
+ 'background-color',
+ 'background-image',
+ 'background-origin',
+ 'background-position',
+ 'background-repeat',
+ 'background-size',
+ 'border',
+ 'border-bottom',
+ 'border-bottom-color',
+ 'border-bottom-radius',
+ 'border-bottom-left-radius',
+ 'border-bottom-right-radius',
+ 'border-bottom-style',
+ 'border-bottom-width',
+ 'border-collapse',
+ 'border-color',
+ 'border-l@@eft',
+ 'border-left-color',
+ 'border-left-radius',
+ 'border-left-style',
+ 'border-left-width',
+ 'border-radius',
+ 'border-r@@ight',
+ 'border-right-color',
+ 'border-right-radius',
+ 'border-right-style',
+ 'border-right-width',
+ 'border-spacing',
+ 'border-style',
+ 'border-top',
+ 'border-top-color',
+ 'border-top-radius',
+ 'border-top-left-radius',
+ 'border-top-right-radius',
+ 'border-top-style',
+ 'border-top-width',
+ 'border-width',
+ 'bottom',
+ 'box-shadow',
+ 'caption-side',
+ 'clear',
+ 'clip',
+ 'color',
+ 'content',
+ 'counter-increment',
+ 'counter-reset',
+ 'cue',
+ 'cue-after',
+ 'cue-before',
+ 'cursor',
+ 'direction',
+ 'display',
+ 'elevation',
+ 'empty-cells',
+ 'float',
+ 'font',
+ 'font-family',
+ 'font-size',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'height',
+ 'l@@eft',
+ 'letter-spacing',
+ 'line-height',
+ 'list-style',
+ 'list-style-image',
+ 'list-style-position',
+ 'list-style-type',
+ 'margin',
+ 'margin-bottom',
+ 'margin-l@@eft',
+ 'margin-r@@ight',
+ 'margin-top',
+ 'max-height',
+ 'max-width',
+ 'min-height',
+ 'min-width',
+ 'opacity',
+ 'orphans',
+ 'outline',
+ 'outline-color',
+ 'outline-style',
+ 'outline-width',
+ 'overflow',
+ 'overflow-x',
+ 'overflow-y',
+ 'padding',
+ 'padding-bottom',
+ 'padding-l@@eft',
+ 'padding-r@@ight',
+ 'padding-top',
+ 'page-break-after',
+ 'page-break-before',
+ 'page-break-inside',
+ 'pause',
+ 'pause-after',
+ 'pause-before',
+ 'pitch',
+ 'pitch-range',
+ 'play-during',
+ 'position',
+ 'quotes',
+ 'richness',
+ 'r@@ight',
+ 'scrollbar-3dlight-color',
+ 'scrollbar-arrow-color',
+ 'scrollbar-base-color',
+ 'scrollbar-darkshadow-color',
+ 'scrollbar-face-color',
+ 'scrollbar-highlight-color',
+ 'scrollbar-shadow-color',
+ 'scrollbar-track-color',
+ 'speak',
+ 'speak-header',
+ 'speak-numeral',
+ 'speak-punctuation',
+ 'speech-rate',
+ 'stress',
+ 'table-layout',
+ 'text-align',
+ 'text-decoration',
+ 'text-indent',
+ 'text-overflow',
+ 'text-shadow',
+ 'text-transform',
+ 'top',
+ 'unicode-bidi',
+ 'vertical-align',
+ 'visibility',
+ 'voice-family',
+ 'volume',
+ 'white-space',
+ 'widows',
+ 'width',
+ 'word-spacing',
+ 'word-wrap',
+ 'z-index',
+ '!important',
+ '@import',
+ '@media'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'left-side',
+ 'far-left',
+ 'left',
+ 'center-left',
+ 'center-right',
+ 'center',
+ 'far-right',
+ 'right-side',
+ 'right',
+ 'behind',
+ 'leftwards',
+ 'rightwards',
+ 'inherit',
+ 'scroll',
+ 'fixed',
+ 'transparent',
+ 'none',
+ 'repeat-x',
+ 'repeat-y',
+ 'repeat',
+ 'no-repeat',
+ 'collapse',
+ 'separate',
+ 'auto',
+ 'top',
+ 'bottom',
+ 'both',
+ 'open-quote',
+ 'close-quote',
+ 'no-open-quote',
+ 'no-close-quote',
+ 'crosshair',
+ 'default',
+ 'pointer',
+ 'move',
+ 'e-resize',
+ 'ne-resize',
+ 'nw-resize',
+ 'n-resize',
+ 'se-resize',
+ 'sw-resize',
+ 's-resize',
+ 'text',
+ 'wait',
+ 'help',
+ 'ltr',
+ 'rtl',
+ 'inline',
+ 'block',
+ 'list-item',
+ 'run-in',
+ 'compact',
+ 'marker',
+ 't@@able', // table
+ 'inline-table',
+ 'table-row-group',
+ 'table-header-group',
+ 'table-footer-group',
+ 'table-row',
+ 'table-column-group',
+ 'table-column',
+ 'table-cell',
+ 'table-caption',
+ 'below',
+ 'level',
+ 'above',
+ 'higher',
+ 'lower',
+ 'show',
+ 'hide',
+ 'c@@aption', // caption
+ 'icon',
+ 'm@@enu', // menu
+ 'message-box',
+ 'small-caption',
+ 'status-bar',
+ 'normal',
+ 'wider',
+ 'narrower',
+ 'ultra-condensed',
+ 'extra-condensed',
+ 'condensed',
+ 'semi-condensed',
+ 'semi-expanded',
+ 'expanded',
+ 'extra-expanded',
+ 'ultra-expanded',
+ 'italic',
+ 'oblique',
+ 'small-caps',
+ 'bold',
+ 'bolder',
+ 'lighter',
+ 'inside',
+ 'outside',
+ 'disc',
+ 'circle',
+ 'square',
+ 'decimal',
+ 'decimal-leading-zero',
+ 'lower-roman',
+ 'upper-roman',
+ 'lower-greek',
+ 'lower-alpha',
+ 'lower-latin',
+ 'upper-alpha',
+ 'upper-latin',
+ 'hebrew',
+ 'armenian',
+ 'georgian',
+ 'cjk-ideographic',
+ 'hiragana',
+ 'katakana',
+ 'hiragana-iroha',
+ 'katakana-iroha',
+ 'crop',
+ 'cross',
+ 'invert',
+ 'visible',
+ 'hidden',
+ 'always',
+ 'avoid',
+ 'x-low',
+ 'low',
+ 'medium',
+ 'high',
+ 'x-high',
+ 'mix?',
+ 'repeat?',
+ 'static',
+ 'relative',
+ 'absolute',
+ 'portrait',
+ 'landscape',
+ 'spell-out',
+ 'once',
+ 'digits',
+ 'continuous',
+ 'c@@ode', // code
+ 'x-slow',
+ 'slow',
+ 'fast',
+ 'x-fast',
+ 'faster',
+ 'slower',
+ 'justify',
+ 'underline',
+ 'overline',
+ 'line-through',
+ 'blink',
+ 'capitalize',
+ 'uppercase',
+ 'lowercase',
+ 'e@@mbed', // embed
+ 'bidi-override',
+ 'baseline',
+ 's@@ub', // sub
+ 'super',
+ 'text-top',
+ 'middle',
+ 'text-bottom',
+ 'silent',
+ 'x-soft',
+ 'soft',
+ 'loud',
+ 'x-loud',
+ 'p@@re', // pre
+ 'nowrap',
+ 'serif',
+ 'sans-serif',
+ 'cursive',
+ 'fantasy',
+ 'monospace',
+ 'empty',
+ 'string',
+ 'strict',
+ 'loose',
+ 'char',
+ 'true',
+ 'false',
+ 'dotted',
+ 'dashed',
+ 'solid',
+ 'double',
+ 'groove',
+ 'ridge',
+ 'inset',
+ 'outset',
+ 'larger',
+ 'smaller',
+ 'xx-small',
+ 'x-small',
+ 's@@mall', // small
+ 'large',
+ 'x-large',
+ 'xx-large',
+ 'all',
+ 'newspaper',
+ 'distribute',
+ 'distribute-all-lines',
+ 'distribute-center-last',
+ 'inter-word',
+ 'inter-ideograph',
+ 'inter-cluster',
+ 'kashida',
+ 'ideograph-alpha',
+ 'ideograph-numeric',
+ 'ideograph-parenthesis',
+ 'ideograph-space',
+ 'keep-all',
+ 'break-all',
+ 'break-word',
+ 'lr-tb',
+ 'tb-rl',
+ 'thin',
+ 'thick',
+ 'inline-block',
+ 'w-resize',
+ 'hand',
+ 'distribute-letter',
+ 'distribute-space',
+ 'whitespace',
+ 'male',
+ 'female',
+ 'child',
+ 'print',
+ 'screen',
+ 'tty',
+ 'aural',
+ 'all',
+ 'braille',
+ 'embossed',
+ 'handheld',
+ 'projection',
+ 'tv',
+ 'hsl',
+ 'hsla',
+ 'rgb',
+ 'rgba'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ 'active',
+ 'after',
+ 'before',
+ 'checked',
+ 'disabled',
+ 'empty',
+ 'enabled',
+ 'first-child',
+ 'first-letter',
+ 'first-line',
+ 'first-of-type',
+ 'focus',
+ 'lang',
+ 'last-child',
+ 'last-of-type',
+ 'li@@nk', // link
+ 'hover',
+ 'not',
+ 'nth-child',
+ 'nth-last-child',
+ 'nth-of-type',
+ 'nth-last-of-type',
+ 'only-child',
+ 'only-of-type',
+ 'root',
+ 'target',
+ 'visited'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords4
+ */
+ protected $keywords4 = array(
+ 'abbr',
+ 'acronym',
+ 'address',
+ 'area',
+ 'article',
+ 'aside',
+ 'audio',
+ 'a',
+ 'base',
+ 'bdi',
+ 'bdo',
+ 'big',
+ 'blockquote',
+ 'body',
+ 'br',
+ 'button',
+ 'b',
+ 'canvas',
+ 'caption',
+ 'cite',
+ 'code',
+ 'col',
+ 'colgroup',
+ 'command',
+ 'datalist',
+ 'dd',
+ 'del',
+ 'details',
+ 'dfn',
+ 'div',
+ 'dl',
+ 'dt',
+ 'embed',
+ 'em',
+ 'fieldset',
+ 'figcaption',
+ 'figure',
+ 'footer',
+ 'form',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'head',
+ 'header',
+ 'hgroup',
+ 'hr',
+ 'html',
+ 'iframe',
+ 'img',
+ 'input',
+ 'ins',
+ 'i',
+ 'kbd',
+ 'keygen',
+ 'label',
+ 'legend',
+ 'li',
+ 'link',
+ 'map',
+ 'mark',
+ 'menu',
+ 'meta',
+ 'meter',
+ 'nav',
+ 'noscript',
+ 'object',
+ 'ol',
+ 'optgroup',
+ 'option',
+ 'output',
+ 'param',
+ 'pre',
+ 'progress',
+ 'p',
+ 'q',
+ 'rbc',
+ 'rb',
+ 'rp',
+ 'rtc',
+ 'rt',
+ 'ruby',
+ 'samp',
+ 'script',
+ 'section',
+ 'select',
+ 'small',
+ 'source',
+ '053a0024219422ca9215c0a3ed0578ee76cff477', // span
+ 'strong',
+ 'style',
+ 'sub',
+ 'summary',
+ 'sup',
+ 'table',
+ 'tbody',
+ 'td',
+ 'textarea',
+ 'tfoot',
+ 'thead',
+ 'th',
+ 'time',
+ 'title',
+ 'track',
+ 'tr',
+ 'tt',
+ 'ul',
+ 'u',
+ 'var',
+ 'video',
+ 'wbr'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords5
+ */
+ public $keywords5 = array(
+ // modifying
+ 'darken',
+ 'lighten',
+ 'saturate',
+ 'desaturate',
+ 'fadein',
+ 'fadeout',
+ 'fade',
+ 'spin',
+ 'mix',
+ // reading
+ 'lightness',
+ 'hue',
+ 'saturation',
+ 'alpha',
+ 'percentage',
+ // typechecking
+ 'isnumber',
+ 'iscolor',
+ 'isstring',
+ 'iskeyword',
+ 'isurl',
+ 'ispixel',
+ 'ispercentage',
+ 'isem',
+ // math
+ 'round',
+ 'ceil',
+ 'floor',
+ // if
+ 'when',
+ 'not',
+ 'and',
+ 'true',
+ // others
+ '&'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights difference files.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class DiffHighlighter extends Highlighter {
+ /**
+ * keywords for an added line, the + is used in unified diffs, the > in
+ * normal diffs
+ * @var array<string>
+ */
+ protected $add = array("+", ">");
+
+ /**
+ * keywords for an deleted line, the - is used in unified diff, the < in
+ * normal diffs
+ * @var array<string>
+ */
+ protected $delete = array("-", "<");
+
+ /**
+ * splitter in changes for normal diff
+ * @var array<string>
+ */
+ protected $splitter = array("---");
+
+ /**
+ * keywords for the line info, the @ is used in unified diffs, the numbers
+ * in normal diffs
+ * @var array<string>
+ */
+ protected $info = array("@", '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlight()
+ */
+ public function highlight($data) {
+ $lines = explode("\n", $data);
+ foreach ($lines as $key => $val) {
+ if (in_array(StringUtil::substring($val, 0, 1), $this->info) || in_array($val, $this->splitter)) {
+ $lines[$key] = '<span class="hlComments">'.StringUtil::encodeHTML($val).'</span>';
+ }
+ else if (in_array(StringUtil::substring($val, 0, 1), $this->add)) {
+ $lines[$key] = '<span class="hlAdded">'.StringUtil::encodeHTML($val).'</span>';
+ }
+ else if (in_array(StringUtil::substring($val, 0, 1), $this->delete)) {
+ $lines[$key] = '<span class="hlRemoved">'.StringUtil::encodeHTML($val).'</span>';
+ }
+ else {
+ $lines[$key] = StringUtil::encodeHTML($val);
+ }
+ }
+
+ $data = implode("\n", $lines);
+ return $data;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Callback;
+use wcf\system\Regex;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+use wcf\util\StringStack;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights syntax of source code.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+abstract class Highlighter extends SingletonFactory {
+ /**
+ * allow multiline quotes
+ * @var boolean
+ */
+ protected $allowsNewslinesInQuotes = false;
+
+ /**
+ * comment end delimiter
+ * @var array<string>
+ */
+ protected $commentEnd = array("*/");
+
+ /**
+ * comment start delimiter
+ * @var array<string>
+ */
+ protected $commentStart = array("/*");
+
+ /**
+ * escape sequence
+ * @var array<string>
+ */
+ protected $escapeSequence = array("\\");
+
+ /**
+ * categorized keywords
+ * @var array<string>
+ */
+ protected $keywords1 = array();
+
+ /**
+ * categorized keywords
+ * @var array<string>
+ */
+ protected $keywords2 = array();
+
+ /**
+ * categorized keywords
+ * @var array<string>
+ */
+ protected $keywords3 = array();
+
+ /**
+ * categorized keywords
+ * @var array<string>
+ */
+ protected $keywords4 = array();
+
+ /**
+ * categorized keywords
+ * @var array<string>
+ */
+ protected $keywords5 = array();
+
+ /**
+ * list of arithmetic operators
+ * @var array<string>
+ */
+ protected $operators = array();
+
+ /**
+ * list of quote marks
+ * @var array<string>
+ */
+ protected $quotes = array("'", '"');
+
+ /**
+ * list of separator sequences
+ * @var array<string>
+ */
+ protected $separators = array();
+
+ /**
+ * inline comment sequence
+ * @var array<string>
+ */
+ protected $singleLineComment = array("//");
+
+ /**
+ * regular expression to extract comments
+ * @var wcf\system\Regex
+ */
+ public $cacheCommentsRegEx = null;
+
+ /**
+ * regular expression to find quote marks
+ * @var wcf\system\Regex
+ */
+ public $quotesRegEx = null;
+
+ /**
+ * regular expression to find string separators
+ * @var wcf\system\Regex
+ */
+ public $separatorsRegEx = '';
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $this->buildRegularExpressions();
+ }
+
+ /**
+ * Returns the title of this highlighter.
+ *
+ * @return string
+ */
+ public function getTitle() {
+ // regex to extract the Highlighter out of the namespaced classname
+ $reType = new Regex('\\\\?wcf\\\\system\\\\bbcode\\\\highlighter\\\\(.*)Highlighter', Regex::CASE_INSENSITIVE);
+
+ return WCF::getLanguage()->get('wcf.bbcode.code.'.$reType->replace(strtolower(get_class($this)), '\1').'.title');
+ }
+
+ /**
+ * Highlights syntax of source code.
+ *
+ * @param string $string
+ * @return string
+ */
+ public function highlight($string) {
+ // cache comments
+ $string = $this->cacheComments($string);
+
+ // cache quotes
+ $string = $this->cacheQuotes($string);
+
+ // encode html
+ $string = StringUtil::encodeHTML($string);
+
+ // do highlight
+ $string = $this->highlightOperators($string);
+ $string = $this->highlightKeywords($string);
+ $string = $this->highlightNumbers($string);
+
+ // insert and highlight quotes
+ $string = $this->highlightQuotes($string);
+
+ // insert and highlight comments
+ $string = $this->highlightComments($string);
+
+ return $string;
+ }
+
+ /**
+ * Builds regular expressions.
+ */
+ protected function buildRegularExpressions() {
+ // quotes regex
+ $quotedEscapeSequence = preg_quote(implode('', $this->escapeSequence));
+ $quotesRegEx = '';
+ foreach ($this->quotes as $quote) {
+ if ($quotesRegEx !== '') $quotesRegEx .= '|';
+ if (!is_array($quote)) $quote = array($quote, $quote);
+ list($opening, $closing) = $quote;
+
+ $opening = preg_quote($opening);
+ $closing = preg_quote($closing);
+ $quotesRegEx .= $opening.'(?:[^'.$closing.$quotedEscapeSequence.']|'.$quotedEscapeSequence.'.)*'.$closing;
+ }
+
+ if ($quotesRegEx !== '') {
+ $quotesRegEx = '(?:'.$quotesRegEx.')';
+ $this->quotesRegEx = new Regex($quotesRegEx, ($this->allowsNewslinesInQuotes) ? Regex::DOT_ALL : Regex::MODIFIER_NONE);
+ }
+
+ // cache comment regex
+ if (!empty($this->singleLineComment) || !empty($this->commentStart)) {
+ $cacheCommentsRegEx = '';
+
+ if ($quotesRegEx !== '') {
+ $cacheCommentsRegEx .= "(".$quotesRegEx.")|";
+ }
+ else {
+ $cacheCommentsRegEx .= "()";
+ }
+
+ $cacheCommentsRegEx .= "(";
+ if (!empty($this->singleLineComment)) {
+ $cacheCommentsRegEx .= "(?:".implode('|', array_map('preg_quote', $this->singleLineComment)).")[^\n]*";
+ if (!empty($this->commentStart)) {
+ $cacheCommentsRegEx .= '|';
+ }
+ }
+
+ if (!empty($this->commentStart)) {
+ $cacheCommentsRegEx .= '(?:'.implode('|', array_map('preg_quote', $this->commentStart)).').*?(?:'.implode('|', array_map('preg_quote', $this->commentEnd)).')';
+ }
+ $cacheCommentsRegEx .= ")";
+
+ $this->cacheCommentsRegEx = new Regex($cacheCommentsRegEx, Regex::DOT_ALL);
+ }
+
+ $this->separatorsRegEx = StringUtil::encodeHTML(implode('|', array_map('preg_quote', $this->separators))).'|\s| |^|$|>|<';
+ }
+
+ /**
+ * Caches comments.
+ */
+ protected function cacheComments($string) {
+ if ($this->cacheCommentsRegEx !== null) {
+ $string = $this->cacheCommentsRegEx->replace($string, new Callback(function (array $matches) {
+ $string = $matches[1];
+ if (isset($matches[2])) $comment = $matches[2];
+ else $comment = '';
+
+ // strip slashes
+ $string = str_replace("\\\"", "\"", $string);
+ $hash = '';
+ if (!empty($comment)) {
+ $comment = str_replace("\\\"", "\"", $comment);
+
+ // create hash
+ $hash = StringStack::pushToStringStack('<span class="hlComments">'.StringUtil::encodeHTML($comment).'</span>', 'highlighterComments');
+ }
+
+ return $string.$hash;
+ }));
+ }
+
+ return $string;
+ }
+
+ /**
+ * Caches quotes.
+ */
+ protected function cacheQuotes($string) {
+ if ($this->quotesRegEx !== null) {
+ $string = $this->quotesRegEx->replace($string, new Callback(function (array $matches) {
+ return StringStack::pushToStringStack('<span class="hlQuotes">'.StringUtil::encodeHTML($matches[0]).'</span>', 'highlighterQuotes');
+ }));
+ }
+
+ return $string;
+ }
+
+ /**
+ * Highlights operators.
+ */
+ protected function highlightOperators($string) {
+ if (!empty($this->operators)) {
+ $string = preg_replace('!('.StringUtil::encodeHTML(implode('|', array_map('preg_quote', $this->operators))).')!i', '<span class="hlOperators">\\0</span>', $string);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Highlights keywords.
+ */
+ protected function highlightKeywords($string) {
+ $_this = $this;
+ $buildKeywordRegex = function (array $keywords) use ($_this) {
+ return '!(?<='.$_this->separatorsRegEx.')('.StringUtil::encodeHTML(implode('|', array_map('preg_quote', $keywords))).')(?='.$_this->separatorsRegEx.')!i';
+ };
+
+ if (!empty($this->keywords1)) {
+ $string = preg_replace($buildKeywordRegex($this->keywords1), '<span class="hlKeywords1">\\0</span>', $string);
+ }
+ if (!empty($this->keywords2)) {
+ $string = preg_replace($buildKeywordRegex($this->keywords2), '<span class="hlKeywords2">\\0</span>', $string);
+ }
+ if (!empty($this->keywords3)) {
+ $string = preg_replace($buildKeywordRegex($this->keywords3), '<span class="hlKeywords3">\\0</span>', $string);
+ }
+ if (!empty($this->keywords4)) {
+ $string = preg_replace($buildKeywordRegex($this->keywords4), '<span class="hlKeywords4">\\0</span>', $string);
+ }
+ if (!empty($this->keywords5)) {
+ $string = preg_replace($buildKeywordRegex($this->keywords5), '<span class="hlKeywords5">\\0</span>', $string);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Highlights numbers.
+ */
+ protected function highlightNumbers($string) {
+ $string = preg_replace('!(?<='.$this->separatorsRegEx.')(-?\d+)(?='.$this->separatorsRegEx.')!i', '<span class="hlNumbers">\\0</span>', $string);
+
+ return $string;
+ }
+
+ /**
+ * Highlights quotes.
+ */
+ protected function highlightQuotes($string) {
+ return StringStack::reinsertStrings($string, 'highlighterQuotes');
+ }
+
+ /**
+ * Highlights comments.
+ */
+ protected function highlightComments($string) {
+ return StringStack::reinsertStrings($string, 'highlighterComments');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Callback;
+use wcf\system\Regex;
+use wcf\util\StringStack;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights syntax of (x)html documents including style and script blocks.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class HtmlHighlighter extends XmlHighlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::cacheComments()
+ */
+ protected function cacheComments($string) {
+ // cache inline scripts and inline css
+ $string = $this->cacheScriptsAndStyles($string);
+
+ return parent::cacheComments($string);
+ }
+
+ /**
+ * Caches scripts and styles in the given string.
+ *
+ * @param string $string
+ * @return string
+ */
+ protected function cacheScriptsAndStyles($string) {
+ $regex = new Regex('(<(style|script)[^>]*>)(.*?)(</\\2>)', Regex::CASE_INSENSITIVE | Regex::DOT_ALL);
+
+ return $regex->replace($string, new Callback(function ($matches) {
+ $type = ($matches[2] === 'script') ? 'js' : 'css';
+
+ // strip slashes
+ $content = str_replace('\\"', '"', $matches[3]);
+ $openingTag = str_replace('\\"', '"', $matches[1]);
+ $closingTag = str_replace('\\"', '"', $matches[4]);
+
+ if (StringUtil::trim($content) == '') return $matches[0];
+
+ $class = '\wcf\system\bbcode\highlighter\\'.ucfirst($type).'Highlighter';
+
+ return $openingTag.StringStack::pushToStringStack('<span class="'.$type.'Highlighter">'.$class::getInstance()->highlight($content).'</span>', 'htmlHighlighter'.ucfirst($type)).$closingTag;
+ }));
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightComments()
+ */
+ protected function highlightComments($string) {
+ $string = parent::highlightComments($string);
+
+ // highlight script and style blocks
+ $string = StringStack::reinsertStrings($string, 'htmlHighlighterJs');
+ $string = StringStack::reinsertStrings($string, 'htmlHighlighterCss');
+
+ return $string;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of java source code.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class JavaHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array("(", ")", "{", "}", "[", "]", ";", ".", ",", "<", ">");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'package',
+ 'abstract',
+ 'break',
+ 'case',
+ 'catch',
+ 'class',
+ 'continue',
+ 'default',
+ 'do',
+ 'else',
+ 'extends',
+ 'false',
+ 'finally',
+ 'for',
+ 'goto',
+ 'if',
+ 'implements',
+ 'instanceof',
+ 'interface',
+ 'native',
+ 'new',
+ 'null',
+ 'private',
+ 'protected',
+ 'public',
+ 'return',
+ 'super',
+ 'strictfp',
+ 'switch',
+ 'synchronized',
+ 'this',
+ 'throws',
+ 'throw',
+ 'transient',
+ 'true',
+ 'try',
+ 'volatile',
+ 'while',
+ 'boolean',
+ 'byte',
+ 'char',
+ 'const',
+ 'double',
+ 'final',
+ 'float',
+ 'int',
+ 'long',
+ 'short',
+ 'static',
+ 'void',
+ 'import'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ 'Boolean',
+ 'Float',
+ 'Character',
+ 'Double',
+ 'Enum',
+ 'Long',
+ 'Short',
+ 'Integer',
+ 'Math',
+ 'Object',
+ 'String',
+ 'StringBuffer',
+ 'System',
+ 'Thread',
+ 'Exception',
+ 'Throwable',
+ 'Runnable',
+ 'Appendable',
+ 'Cloneable',
+ 'HashMap',
+ 'List',
+ 'ArrayList'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of JavaScript code.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class JsHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array("(", ")", "{", "}", "[", "]", ";", ".", ",");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array("=", ">", "<", "!", "~", "?", ":", "==", "<=", ">=", "!=",
+ "&&", "||", "++", "--", "+", "-", "*", "/", "&", "|", "^", "%", "<<", ">>", ">>>", "+=", "-=", "*=",
+ "/=", "&=", "|=", "^=", "%=", "<<=", ">>=", ">>>=");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ "String",
+ "Array",
+ "RegExp",
+ "Function",
+ "Math",
+ "Number",
+ "Date",
+ "Image",
+ "window",
+ "document",
+ "navigator",
+ "onAbort",
+ "onBlur",
+ "onChange",
+ "onClick",
+ "onDblClick",
+ "onDragDrop",
+ "onError",
+ "onFocus",
+ "onKeyDown",
+ "onKeyPress",
+ "onKeyUp",
+ "onLoad",
+ "onMouseDown",
+ "onMouseOver",
+ "onMouseOut",
+ "onMouseMove",
+ "onMouseUp",
+ "onMove",
+ "onReset",
+ "onResize",
+ "onSelect",
+ "onSubmit",
+ "onUnload"
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ "break",
+ "continue",
+ "do",
+ "while",
+ "export",
+ "for",
+ "in",
+ "if",
+ "else",
+ "import",
+ "return",
+ "label",
+ "switch",
+ "case",
+ "var",
+ "with",
+ "delete",
+ "new",
+ "this",
+ "typeof",
+ "void",
+ "abstract",
+ "boolean",
+ "byte",
+ "catch",
+ "char",
+ "class",
+ "const",
+ "debugger",
+ "default",
+ "double",
+ "enum",
+ "extends",
+ "false",
+ "final",
+ "finally",
+ "float",
+ "function",
+ "implements",
+ "goto",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "null",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "short",
+ "static",
+ "super",
+ "synchronized",
+ "throw",
+ "throws",
+ "transient",
+ "true",
+ "try",
+ "volatile"
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of Perl sourcecode.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class PerlHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array('(', ')', '{', '}', '[', ']', ';', '.', ',');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('#');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentStart
+ */
+ protected $commentStart = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentEnd
+ */
+ protected $commentEnd = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array('.=', '=', '>', '<', '!', '~', '?', ':', '==', '<=', '>=', '!=',
+ '&&', '||', '++', '--', '+', '-', '*', '/', '&', '|', '^', '%', '<<', '>>', '>>>', '+=', '-=', '*=',
+ '/=', '&=', '|=', '^=', '%=', '<<=', '>>=', '>>>=', '->', '::');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'print',
+ 'sprintf',
+ 'length',
+ 'substr',
+ 'eval',
+ 'die',
+ 'opendir',
+ 'closedir',
+ 'open',
+ 'close',
+ 'chmod',
+ 'unlink',
+ 'flock',
+ 'read',
+ 'seek',
+ 'stat',
+ 'truncate',
+ 'chomp',
+ 'localtime',
+ 'defined',
+ 'undef',
+ 'uc',
+ 'lc',
+ 'trim',
+ 'split',
+ 'sort',
+ 'keys',
+ 'push',
+ 'pop',
+ 'join',
+ 'local',
+ 'select',
+ 'index',
+ 'sqrt',
+ 'system',
+ 'crypt',
+ 'pack',
+ 'unpack'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'case',
+ 'do',
+ 'while',
+ 'for',
+ 'if',
+ 'foreach',
+ 'my',
+ 'else',
+ 'elsif',
+ 'eq',
+ 'ne',
+ 'or',
+ 'xor',
+ 'and',
+ 'lt',
+ 'gt',
+ 'ge',
+ 'le',
+ 'return',
+ 'last',
+ 'goto',
+ 'unless',
+ 'given',
+ 'when',
+ 'default',
+ 'until',
+ 'break',
+ 'exit'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ 'use',
+ 'import',
+ 'require',
+ 'sub'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Regex;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights syntax of PHP sourcecode.
+ *
+ * @author Tim Düsterhus, Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class PhpHighlighter extends Highlighter {
+ public static $colorToClass = array();
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ parent::init();
+
+ $types = array('default' => 'hlKeywords1', 'keyword' => 'hlKeywords2', 'comment' => 'hlComments', 'string' => 'hlQuotes');
+
+ self::$colorToClass['<span style="color: '.ini_get('highlight.html').'">'] = '<span>';
+ foreach ($types as $type => $class) {
+ self::$colorToClass['<span style="color: '.ini_get('highlight.'.$type).'">'] = '<span class="'.$class.'">';
+ }
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlight()
+ */
+ public function highlight($code) {
+ // add starting php tag
+ $phpTagsAdded = false;
+ if (StringUtil::indexOf($code, '<?') === false) {
+ $phpTagsAdded = true;
+ $code = '<?php '.$code.' ?>';
+ }
+
+ // do highlight
+ $highlightedCode = highlight_string($code, true);
+
+ // clear code
+ $highlightedCode = str_replace('<code>', '', $highlightedCode);
+ $highlightedCode = str_replace('</code>', '', $highlightedCode);
+
+ // remove added php tags
+ if ($phpTagsAdded) {
+ $regex = new Regex('([^\\2]*)(<\?php )(.*)( .*\?>)([^\\4]*)', Regex::CASE_INSENSITIVE | Regex::DOT_ALL);
+ $highlightedCode = $regex->replace($highlightedCode, '\\1\\3\\5');
+ }
+
+ // remove breaks
+ $highlightedCode = str_replace("\n", "", $highlightedCode);
+ $highlightedCode = str_replace('<br />', "\n", $highlightedCode);
+ // get tabs back
+ $highlightedCode = str_replace(' ', "\t", $highlightedCode);
+ // convert colors to classes
+ $highlightedCode = strtr($highlightedCode, self::$colorToClass);
+
+ // replace double quotes by entity
+ return Regex::compile('(?<!\<span class=)"(?!\>)')->replace($highlightedCode, '"');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\util\StringUtil;
+
+/**
+ * Does no highlighting.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class PlainHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlight()
+ */
+ public function highlight($code) {
+ return StringUtil::encodeHTML($code);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+
+/**
+ * Highlights syntax of Python sourcecode.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2012 Tim Duesterhus
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class PythonHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array('(', ')',/* from __future__ import braces '{', '}', */'[', ']', ';', '.', ',', ':');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('#');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentStart
+ */
+ protected $commentStart = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentEnd
+ */
+ protected $commentEnd = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array('+=', '-=', '**=', '*=', '//=', '/=', '%=', '~=', '+', '-', '**', '*', '//', '/', '%',
+ '&=', '<<=', '>>=', '^=', '~', '&', '^', '|', '<<', '>>', '=', '!=', '<', '>', '<=', '>=');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$quotes
+ */
+ protected $quotes = array(array("r'", "'"), array("u'", "'"), array('r"', '"'), array('u"', '"'), "'", '"');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'print',
+ 'del',
+ 'str',
+ 'int',
+ 'len',
+ 'repr',
+ 'range',
+ 'raise',
+ 'pass',
+ 'continue',
+ 'break',
+ 'return'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'if',
+ 'elif',
+ 'else',
+ 'try',
+ 'except',
+ 'finally',
+ 'for',
+ 'in',
+ 'while'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords3
+ */
+ protected $keywords3 = array(
+ 'from',
+ 'import',
+ 'as',
+ 'class',
+ 'def'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords4
+ */
+ protected $keywords4 = array(
+ '__name__',
+ '__init__',
+ '__str__',
+ '__del__',
+ 'self',
+ 'True',
+ 'False',
+ 'None',
+ 'and',
+ 'or',
+ 'not',
+ 'is'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Callback;
+use wcf\util\StringStack;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights syntax of sql queries.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class SqlHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$allowsNewslinesInQuotes
+ */
+ protected $allowsNewslinesInQuotes = true;
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$quotes
+ */
+ protected $quotes = array("'", '"');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('#', '--');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array('(', ')', ',', ';');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array('<>', '~=', '!=', '^=', '=', '<', '<=', '>', '>=', '*', '/', '+', '-', '||', '@', '%', '&', '?', '\$');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::cacheComments()
+ */
+ protected function cacheComments($string) {
+ if ($this->cacheCommentsRegEx !== null) {
+ $string = $this->cacheCommentsRegEx->replace($string, new Callback(function (array $matches) {
+ $string = $matches[1];
+ if (isset($matches[2])) $comment = $matches[2];
+ else $comment = '';
+
+ // strip slashes
+ $string = str_replace("\\\"", "\"", $string);
+ $hash = '';
+ if (!empty($comment)) {
+ $comment = str_replace("\\\"", "\"", $comment);
+
+ // create hash
+ $hash = StringStack::pushToStringStack('<span class="hlComments">'.StringUtil::encodeHTML($comment).'</span>', 'highlighterComments', '!!!');
+ }
+
+ return $string.$hash;
+ }));
+ }
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::cacheQuotes()
+ */
+ protected function cacheQuotes($string) {
+ if ($this->quotesRegEx !== null) {
+ $string = $this->quotesRegEx->replace($string, new Callback(function (array $matches) {
+ return StringStack::pushToStringStack('<span class="hlQuotes">'.StringUtil::encodeHTML($matches[0]).'</span>', 'highlighterQuotes', '!!!');
+ }));
+ }
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords1
+ */
+ protected $keywords1 = array(
+ 'action',
+ 'add',
+ 'aggregate',
+ 'all',
+ 'alter',
+ 'after',
+ 'and',
+ 'as',
+ 'asc',
+ 'avg',
+ 'avg_row_length',
+ 'auto_increment',
+ 'between',
+ 'bigint',
+ 'bit',
+ 'binary',
+ 'blob',
+ 'bool',
+ 'both',
+ 'by',
+ 'cascade',
+ 'case',
+ 'char',
+ 'character',
+ 'change',
+ 'check',
+ 'checksum',
+ 'column',
+ 'columns',
+ 'comment',
+ 'constraint',
+ 'create',
+ 'cross',
+ 'current_date',
+ 'current_time',
+ 'current_timestamp',
+ 'data',
+ 'database',
+ 'databases',
+ 'date',
+ 'datetime',
+ 'day',
+ 'day_hour',
+ 'day_minute',
+ 'day_second',
+ 'dayofmonth',
+ 'dayofweek',
+ 'dayofyear',
+ 'dec',
+ 'decimal',
+ 'default',
+ 'delayed',
+ 'delay_key_write',
+ 'delete',
+ 'desc',
+ 'describe',
+ 'distinct',
+ 'distinctrow',
+ 'double',
+ 'drop',
+ 'end',
+ 'else',
+ 'escape',
+ 'escaped',
+ 'enclosed',
+ 'enum',
+ 'explain',
+ 'exists',
+ 'fields',
+ 'file',
+ 'first',
+ 'float',
+ 'float4',
+ 'float8',
+ 'flush',
+ 'foreign',
+ 'from',
+ 'for',
+ 'full',
+ 'function',
+ 'global',
+ 'grant',
+ 'grants',
+ 'group',
+ 'having',
+ 'heap',
+ 'high_priority',
+ 'hour',
+ 'hour_minute',
+ 'hour_second',
+ 'hosts',
+ 'identified',
+ 'ignore',
+ 'in',
+ 'index',
+ 'infile',
+ 'inner',
+ 'insert',
+ 'insert_id',
+ 'int',
+ 'integer',
+ 'interval',
+ 'int1',
+ 'int2',
+ 'int3',
+ 'int4',
+ 'int8',
+ 'into',
+ 'if',
+ 'is',
+ 'isam',
+ 'join',
+ 'key',
+ 'keys',
+ 'kill',
+ 'last_insert_id',
+ 'leading',
+ 'left',
+ 'length',
+ 'like',
+ 'lines',
+ 'limit',
+ 'load',
+ 'local',
+ 'lock',
+ 'logs',
+ 'long',
+ 'longblob',
+ 'longtext',
+ 'low_priority',
+ 'max',
+ 'max_rows',
+ 'match',
+ 'mediumblob',
+ 'mediumtext',
+ 'mediumint',
+ 'middleint',
+ 'min_rows',
+ 'minute',
+ 'minute_second',
+ 'modify',
+ 'month',
+ 'monthname',
+ 'myisam',
+ 'natural',
+ 'numeric',
+ 'no',
+ 'not',
+ 'null',
+ 'on',
+ 'optimize',
+ 'option',
+ 'optionally',
+ 'or',
+ 'order',
+ 'outer',
+ 'outfile',
+ 'pack_keys',
+ 'partial',
+ 'password',
+ 'precision',
+ 'primary',
+ 'procedure',
+ 'process',
+ 'processlist',
+ 'privileges',
+ 'read',
+ 'real',
+ 'references',
+ 'reload',
+ 'regexp',
+ 'rename',
+ 'replace',
+ 'restrict',
+ 'returns',
+ 'revoke',
+ 'rlike',
+ 'row',
+ 'rows',
+ 'second',
+ 'select',
+ 'set',
+ 'show',
+ 'shutdown',
+ 'smallint',
+ 'soname',
+ 'sql_big_tables',
+ 'sql_big_selects',
+ 'sql_low_priority_updates',
+ 'sql_log_off',
+ 'sql_log_update',
+ 'sql_select_limit',
+ 'sql_small_result',
+ 'sql_big_result',
+ 'sql_warnings',
+ 'straight_join',
+ 'starting',
+ 'status',
+ 'string',
+ 'table',
+ 'tables',
+ 'temporary',
+ 'terminated',
+ 'text',
+ 'then',
+ 'time',
+ 'timestamp',
+ 'tinyblob',
+ 'tinytext',
+ 'tinyint',
+ 'trailing',
+ 'to',
+ 'type',
+ 'use',
+ 'using',
+ 'unique',
+ 'unlock',
+ 'unsigned',
+ 'update',
+ 'usage',
+ 'values',
+ 'varchar',
+ 'variables',
+ 'varying',
+ 'varbinary',
+ 'with',
+ 'write',
+ 'when',
+ 'where',
+ 'year',
+ 'year_month',
+ 'zerofill'
+ );
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$keywords2
+ */
+ protected $keywords2 = array(
+ 'ABS',
+ 'ACOS',
+ 'ADDDATE',
+ 'ASCII',
+ 'ASIN',
+ 'ATAN',
+ 'ATAN2',
+ 'AVG',
+ 'BENCHMARK',
+ 'BIN',
+ 'CEILING',
+ 'CHAR',
+ 'COALESCE',
+ 'CONCAT',
+ 'CONV',
+ 'COS',
+ 'COT',
+ 'COUNT',
+ 'CURDATE',
+ 'CURTIME',
+ 'DATABASE',
+ 'DAYNAME',
+ 'DAYOFMONTH',
+ 'DAYOFWEEK',
+ 'DAYOFYEAR',
+ 'DECODE',
+ 'DEGREES',
+ 'ELT',
+ 'ENCODE',
+ 'ENCRYPT',
+ 'EXP',
+ 'EXTRACT',
+ 'FIELD',
+ 'FLOOR',
+ 'FORMAT',
+ 'GREATEST',
+ 'HEX',
+ 'HOUR',
+ 'IF',
+ 'IFNULL',
+ 'INSERT',
+ 'INSTR',
+ 'INTERVAL',
+ 'ISNULL',
+ 'LCASE',
+ 'LEAST',
+ 'LEFT',
+ 'LENGTH',
+ 'LOCATE',
+ 'LOCATE',
+ 'LOG',
+ 'LOG10',
+ 'LOWER',
+ 'LPAD',
+ 'LTRIM',
+ 'MAX',
+ 'MD5',
+ 'MID',
+ 'MIN',
+ 'MINUTE',
+ 'MOD',
+ 'MONTH',
+ 'MONTHNAME',
+ 'NOW',
+ 'NULLIF',
+ 'OCT',
+ 'ORD',
+ 'PASSWORD',
+ 'PI',
+ 'POSITION',
+ 'POW',
+ 'POWER',
+ 'prepare',
+ 'QUARTER',
+ 'RADIANS',
+ 'RAND',
+ 'REPEAT',
+ 'REPLACE',
+ 'REVERSE',
+ 'RIGHT',
+ 'ROUND',
+ 'ROUND',
+ 'RPAD',
+ 'RTRIM',
+ 'SECOND',
+ 'SIGN',
+ 'SIN',
+ 'SOUNDEX',
+ 'SPACE',
+ 'SQRT',
+ 'STD',
+ 'STDDEV',
+ 'STRCMP',
+ 'SUBDATE',
+ 'SUBSTRING',
+ 'SUBSTRING',
+ 'SUM',
+ 'SYSDATE',
+ 'TAN',
+ 'TRIM',
+ 'TRUNCATE',
+ 'UCASE',
+ 'UPPER',
+ 'USER',
+ 'VERSION',
+ 'WEEK',
+ 'WEEKDAY',
+ 'YEAR'
+ );
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Regex;
+
+/**
+ * Highlights syntax of TeX source code.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class TexHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$quotes
+ */
+ protected $quotes = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array('%');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightKeywords()
+ */
+ protected function highlightKeywords($string) {
+ $string = Regex::compile('\\$([^\\$]*)\\$', Regex::DOT_ALL)->replace($string, '<span class="hlKeywords2">\\0</span>');
+ $string = Regex::compile('(\\\\(?:[a-z]+))(\\[[^\\]\\\\]+\\])?(\\{[^\\}]*\\})?', Regex::CASE_INSENSITIVE)->replace($string, '<span class="hlKeywords3">\\1</span><span class="hlKeywords4">\\2</span><span class="hlKeywords1">\\3</span>');
+ $string = str_replace('\\\\', '<span class="hlKeywords3">\\\\</span>', $string);
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightNumbers()
+ */
+ protected function highlightNumbers($string) {
+ // do not highlight numbers
+ return $string;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Regex;
+
+/**
+ * Highlights syntax of template documents with smarty-syntax.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class TplHighlighter extends HtmlHighlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightComments()
+ */
+ protected function highlightComments($string) {
+ $string = parent::highlightComments($string);
+
+ // highlight template tags
+ $string = Regex::compile('\{(?=\S).+?(?<=\S)\}', Regex::DOT_ALL)->replace($string, '<span class="hlKeywords3">\\0</span>');
+
+ return $string;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\bbcode\highlighter;
+use wcf\system\Callback;
+use wcf\system\Regex;
+use wcf\util\StringStack;
+use wcf\util\StringUtil;
+
+/**
+ * Highlights syntax of xml sourcecode.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.bbcode.highlighter
+ * @category Community Framework
+ */
+class XmlHighlighter extends Highlighter {
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$allowsNewslinesInQuotes
+ */
+ protected $allowsNewslinesInQuotes = true;
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$quotes
+ */
+ protected $quotes = array('"');
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$singleLineComment
+ */
+ protected $singleLineComment = array();
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentStart
+ */
+ protected $commentStart = array("<!--");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$commentEnd
+ */
+ protected $commentEnd = array("-->");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$separators
+ */
+ protected $separators = array("<", ">");
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::$operators
+ */
+ protected $operators = array();
+
+ const XML_ATTRIBUTE_NAME = '[a-z0-9](?:(?:(?<!-)-)?[a-z0-9])*';
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightKeywords()
+ */
+ protected function highlightKeywords($string) {
+ $string = parent::highlightKeywords($string);
+ // find tags
+ $regex = new Regex('<(?:/|\!|\?)?[a-z0-9]+(?:\s+'.self::XML_ATTRIBUTE_NAME.'(?:=[^\s/\?&]+)?)*(?:\s+/|\?)?>', Regex::CASE_INSENSITIVE);
+ $string = $regex->replace($string, new Callback(function ($matches) {
+ // highlight attributes
+ $tag = Regex::compile(XmlHighlighter::XML_ATTRIBUTE_NAME.'(?:=[^\s/\?&]+)?(?=\s|&)', Regex::CASE_INSENSITIVE)->replace($matches[0], '<span class="hlKeywords2">\\0</span>');
+
+ // highlight tag
+ return '<span class="hlKeywords1">'.$tag.'</span>';
+ }));
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::cacheQuotes()
+ */
+ protected function cacheQuotes($string) {
+ // highlight CDATA-Tags as quotes
+ $string = Regex::compile('<!\[CDATA\[.*?\]\]>', Regex::DOT_ALL)->replace($string, new Callback(function (array $matches) {
+ return StringStack::pushToStringStack('<span class="hlQuotes">'.StringUtil::encodeHTML($matches[0]).'</span>', 'highlighterQuotes');
+ }));
+
+ $string = parent::cacheQuotes($string);
+
+ return $string;
+ }
+
+ /**
+ * @see wcf\system\bbcode\highlighter\Highlighter::highlightNumbers()
+ */
+ protected function highlightNumbers($string) {
+ // do not highlight numbers
+ return $string;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\bbcode\attribute\BBCodeAttribute;
+use wcf\data\bbcode\BBCode;
+use wcf\system\WCF;
+
+/**
+ * Caches the bbcodes.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class BBCodeCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ protected function rebuild(array $parameters) {
+ $data = $attributes = array();
+
+ // get attributes
+ $sql = "SELECT attribute.*, bbcode.bbcodeTag
+ FROM wcf".WCF_N."_bbcode_attribute attribute
+ LEFT JOIN wcf".WCF_N."_bbcode bbcode
+ ON (bbcode.bbcodeID = attribute.bbcodeID)
+ WHERE bbcode.isDisabled = 0
+ ORDER BY attribute.attributeNo";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute();
+ while ($row = $statement->fetchArray()) {
+ if (!isset($attributes[$row['bbcodeTag']])) $attributes[$row['bbcodeTag']] = array();
+
+ $attributes[$row['bbcodeTag']][$row['attributeNo']] = new BBCodeAttribute(null, $row);
+ }
+
+ // get bbcodes
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_bbcode
+ WHERE isDisabled = 0";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute();
+ while ($row = $statement->fetchArray()) {
+ $row['attributes'] = (isset($attributes[$row['bbcodeTag']]) ? $attributes[$row['bbcodeTag']] : array());
+ $data[$row['bbcodeTag']] = new BBCode(null, $row);
+ }
+
+ return $data;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\bbcode\media\provider\BBCodeMediaProviderList;
+
+/**
+ * Caches the BBCode media providers.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2011-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class BBCodeMediaProviderCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ protected function rebuild(array $parameters) {
+ $providerList = new BBCodeMediaProviderList();
+ $providerList->readObjects();
+
+ return $providerList->getObjects();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\system\WCF;
+
+/**
+ * Caches the smilies.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class SmileyCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ protected function rebuild(array $parameters) {
+ $data = array('smilies' => array());
+
+ // get smilies
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_smiley
+ ORDER BY showOrder";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute();
+ while ($object = $statement->fetchObject('wcf\data\smiley\Smiley')) {
+ $object->smileyCodes = $object->getAliases();
+ $object->smileyCodes[] = $object->smileyCode;
+
+ $data['smilies'][$object->categoryID][$object->smileyID] = $object;
+ }
+
+ return $data;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\category;
+use wcf\system\category\AbstractCategoryType;
+use wcf\system\WCF;
+
+/**
+ * Category implementation for smilies.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.category
+ * @category Community Framework
+ */
+class SmileyCategoryType extends AbstractCategoryType {
+ /**
+ * @see wcf\system\category\AbstractCategoryType::$langVarPrefix
+ */
+ protected $langVarPrefix = 'wcf.smiley.category';
+
+ /**
+ * @see wcf\system\category\AbstractCategoryType::$forceDescription
+ */
+ protected $hasDescription = false;
+
+ /**
+ * @see wcf\system\category\AbstractCategoryType::$maximumNestingLevel
+ */
+ protected $maximumNestingLevel = 0;
+
+ /**
+ * @see wcf\system\category\ICategoryType::getApplication()
+ */
+ public function getApplication() {
+ return 'wcf';
+ }
+
+ /**
+ * @see wcf\system\category\ICategoryType::canAddCategory()
+ */
+ public function canAddCategory() {
+ return $this->canEditCategory();
+ }
+
+ /**
+ * @see wcf\system\category\ICategoryType::canDeleteCategory()
+ */
+ public function canDeleteCategory() {
+ return $this->canEditCategory();
+ }
+
+ /**
+ * @see wcf\system\category\ICategoryType::canEditCategory()
+ */
+ public function canEditCategory() {
+ return WCF::getSession()->getPermission('admin.content.smiley.canManageSmiley');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\option\user\group;
+use wcf\data\bbcode\BBCodeCache;
+use wcf\data\option\Option;
+use wcf\system\exception\UserInputException;
+use wcf\system\option\AbstractOptionType;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * User group option type implementation for BBCode select lists.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage system.option
+ * @category Community Framework
+ */
+class BBCodeSelectUserGroupOptionType extends AbstractOptionType implements IUserGroupOptionType {
+ /**
+ * available BBCodes
+ * @var array<string>
+ */
+ protected $bbCodes = null;
+
+ /**
+ * @see wcf\system\option\IOptionType::getData()
+ */
+ public function getData(Option $option, $newValue) {
+ if (!is_array($newValue)) {
+ $newValue = array();
+ }
+
+ return implode(',', $newValue);
+ }
+
+ /**
+ * @see wcf\system\option\IOptionType::getFormElement()
+ */
+ public function getFormElement(Option $option, $value) {
+ if ($this->bbCodes === null) {
+ $this->loadBBCodeSelection();
+ }
+
+ $selectedBBCodes = array();
+ if ($value == 'all') {
+ $selectedBBCodes = $this->bbCodes;
+ }
+ else {
+ $selectedBBCodes = explode(',', $value);
+ }
+
+ WCF::getTPL()->assign(array(
+ 'bbCodes' => $this->bbCodes,
+ 'option' => $option,
+ 'selectedBBCodes' => $selectedBBCodes
+ ));
+
+ return WCF::getTPL()->fetch('bbCodeSelectOptionType');
+ }
+
+ /**
+ * Loads the list of BBCodes for the HTML select element.
+ *
+ * @return array<string>
+ */
+ protected function loadBBCodeSelection() {
+ $this->bbCodes = array_keys(BBCodeCache::getInstance()->getBBCodes());
+ asort($this->bbCodes);
+ }
+
+ /**
+ * @see wcf\system\option\user\group\IUserGroupOptionType::merge()
+ */
+ public function diff($defaultValue, $groupValue) {
+ if ($this->bbCodes === null) {
+ $this->loadBBCodeSelection();
+ }
+
+ if ($defaultValue == 'all') {
+ $defaultValue = $this->bbCodes;
+ }
+ else {
+ $defaultValue = explode(',', StringUtil::unifyNewlines($defaultValue));
+ }
+ if ($groupValue == 'all') {
+ $groupValue = $this->bbCodes;
+ }
+ else {
+ $groupValue = explode(',', StringUtil::unifyNewlines($groupValue));
+ }
+
+ $result = array_diff($groupValue, $defaultValue);
+ if (empty($result)) {
+ return null;
+ }
+
+ return implode(',', $result);
+ }
+
+ /**
+ * @see wcf\system\option\user\group\IUserGroupOptionType::merge()
+ */
+ public function merge($defaultValue, $groupValue) {
+ if ($this->bbCodes === null) {
+ $this->loadBBCodeSelection();
+ }
+
+ if ($defaultValue == 'all') {
+ $defaultValue = $this->bbCodes;
+ }
+ else if (empty($defaultValue) || $defaultValue == 'none') {
+ $defaultValue = array();
+ }
+ else {
+ $defaultValue = explode(',', StringUtil::unifyNewlines($defaultValue));
+ }
+ if ($groupValue == 'all') {
+ $groupValue = $this->bbCodes;
+ }
+ else if (empty($groupValue) || $groupValue == 'none') {
+ $groupValue = array();
+ }
+ else {
+ $groupValue = explode(',', StringUtil::unifyNewlines($groupValue));
+ }
+
+ $newValue = array_unique(array_merge($defaultValue, $groupValue));
+ sort($newValue);
+
+ return implode(',', $newValue);
+ }
+
+ /**
+ * @see wcf\system\option\IOptionType::validate()
+ */
+ public function validate(Option $option, $newValue) {
+ if (!is_array($newValue)) {
+ $newValue = array();
+ }
+
+ if ($this->bbCodes === null) {
+ $this->loadBBCodeSelection();
+ }
+
+ foreach ($newValue as $tag) {
+ if (!in_array($tag, $this->bbCodes)) {
+ throw new UserInputException($option->optionName, 'validationFailed');
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\package\plugin;
+use wcf\data\bbcode\attribute\BBCodeAttributeEditor;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+
+/**
+ * Installs, updates and deletes bbcodes.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.package.plugin
+ * @category Community Framework
+ */
+class BBCodePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::$className
+ */
+ public $className = 'wcf\data\bbcode\BBCodeEditor';
+
+ /**
+ * @see wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName
+ */
+ public $tableName = 'bbcode';
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::$tagName
+ */
+ public $tagName = 'bbcode';
+
+ /**
+ * list of attributes per bbcode id
+ * @var array<array>
+ */
+ protected $attributes = array();
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::handleDelete()
+ */
+ protected function handleDelete(array $items) {
+ $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
+ WHERE bbcodeTag = ?
+ AND packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ foreach ($items as $item) {
+ $statement->execute(array(
+ $item['attributes']['name'],
+ $this->installation->getPackageID()
+ ));
+ }
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::getElement()
+ */
+ protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) {
+ $nodeValue = $element->nodeValue;
+
+ // read pages
+ if ($element->tagName == 'attributes') {
+ $nodeValue = array();
+
+ $attributes = $xpath->query('child::*', $element);
+ foreach ($attributes as $attribute) {
+ $attributeNo = $attribute->getAttribute('name');
+ $nodeValue[$attributeNo] = array();
+
+ $attributeValues = $xpath->query('child::*', $attribute);
+ foreach ($attributeValues as $attributeValue) {
+ $nodeValue[$attributeNo][$attributeValue->tagName] = $attributeValue->nodeValue;
+ }
+ }
+ }
+
+ $elements[$element->tagName] = $nodeValue;
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::prepareImport()
+ */
+ protected function prepareImport(array $data) {
+ return array(
+ 'bbcodeTag' => $data['attributes']['name'],
+ 'htmlOpen' => (!empty($data['elements']['htmlopen']) ? $data['elements']['htmlopen'] : ''),
+ 'htmlClose' => (!empty($data['elements']['htmlclose']) ? $data['elements']['htmlclose'] : ''),
+ 'allowedChildren' => (!empty($data['elements']['allowedchildren']) ? $data['elements']['allowedchildren'] : 'all'),
+ 'wysiwygIcon' => (!empty($data['elements']['wysiwygicon']) ? $data['elements']['wysiwygicon'] : ''),
+ 'attributes' => (isset($data['elements']['attributes']) ? $data['elements']['attributes'] : array()),
+ 'className' => (!empty($data['elements']['classname']) ? $data['elements']['classname'] : ''),
+ 'isSourceCode' => (!empty($data['elements']['sourcecode']) ? 1 : 0),
+ );
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::validateImport()
+ */
+ protected function validateImport(array $data) {
+ if ($data['bbcodeTag'] == 'all' || $data['bbcodeTag'] == 'none') {
+ throw new SystemException("BBCodes can't be called 'all' or 'none'");
+ }
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem()
+ */
+ protected function findExistingItem(array $data) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_".$this->tableName."
+ WHERE bbcodeTag = ?
+ AND packageID = ?";
+ $parameters = array(
+ $data['bbcodeTag'],
+ $this->installation->getPackageID()
+ );
+
+ return array(
+ 'sql' => $sql,
+ 'parameters' => $parameters
+ );
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::import()
+ */
+ protected function import(array $row, array $data) {
+ // extract attributes
+ $attributes = $data['attributes'];
+ unset($data['attributes']);
+
+ // import or update action
+ $object = parent::import($row, $data);
+
+ // store attributes for later import
+ $this->attributes[$object->bbcodeID] = $attributes;
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::postImport()
+ */
+ protected function postImport() {
+ // clear attributes
+ $sql = "DELETE FROM wcf".WCF_N."_bbcode_attribute
+ WHERE bbcodeID IN (
+ SELECT bbcodeID
+ FROM wcf".WCF_N."_bbcode
+ WHERE packageID = ?
+ )";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($this->installation->getPackageID()));
+
+ if (!empty($this->attributes)) {
+ foreach ($this->attributes as $bbcodeID => $bbcodeAttributes) {
+ foreach ($bbcodeAttributes as $attributeNo => $attribute) {
+ BBCodeAttributeEditor::create(array(
+ 'bbcodeID' => $bbcodeID,
+ 'attributeNo' => $attributeNo,
+ 'attributeHtml' => (!empty($attribute['html']) ? $attribute['html'] : ''),
+ 'validationPattern' => (!empty($attribute['validationpattern']) ? $attribute['validationpattern'] : ''),
+ 'required' => (!empty($attribute['required']) ? $attribute['required'] : 0),
+ 'useText' => (!empty($attribute['usetext']) ? $attribute['usetext'] : 0),
+ ));
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\package\plugin;
+use wcf\system\WCF;
+
+/**
+ * Installs, updates and deletes smilies.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.bbcode
+ * @subpackage acp.package.plugin
+ * @category Community Framework
+ */
+class SmileyPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::$className
+ */
+ public $className = 'wcf\data\smiley\SmileyEditor';
+
+ /**
+ * @see wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName
+ */
+ public $tableName = 'smiley';
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::$tagName
+ */
+ public $tagName = 'smiley';
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::handleDelete()
+ */
+ protected function handleDelete(array $items) {
+ $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
+ WHERE smileyCode = ?
+ AND packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ foreach ($items as $item) {
+ $statement->execute(array(
+ $item['attributes']['name'],
+ $this->installation->getPackageID()
+ ));
+ }
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::prepareImport()
+ */
+ protected function prepareImport(array $data) {
+ return array(
+ 'smileyCode' => $data['attributes']['name'],
+ 'smileyTitle' => $data['elements']['title'],
+ 'smileyPath' => $data['elements']['path'],
+ 'aliases' => (isset($data['elements']['aliases']) ? $data['elements']['aliases'] : '')
+ );
+ }
+
+ /**
+ * @see wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem()
+ */
+ protected function findExistingItem(array $data) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_".$this->tableName."
+ WHERE smileyCode = ?
+ AND packageID = ?";
+ $parameters = array(
+ $data['smileyCode'],
+ $this->installation->getPackageID()
+ );
+
+ return array(
+ 'sql' => $sql,
+ 'parameters' => $parameters
+ );
+ }
+}
--- /dev/null
+/* Code Box */
+.codeBox {
+ background-color: @wcfContentBackgroundColor;
+
+ > div {
+ padding: @wcfGapMedium @wcfGapLarge @wcfGapMedium @wcfGapMedium;
+
+ .boxShadowNative(~"inset 4em 0 0 " @wcfContainerBackgroundColor ~", inset 4.1em 0 0" lighten(@wcfContainerBorderColor, 10%));
+
+ > div {
+ margin: 0 0 @wcfGapSmall 3.4em;
+
+ > h3 {
+ font-size: 110%;
+ font-weight: bold;
+ padding-left: @wcfGapSmall;
+ position: relative;
+
+ > span.icon {
+ position: absolute;
+ right: 0;
+ top: 3px;
+ }
+ }
+ }
+
+ > ol {
+ list-style-type: decimal;
+ margin-left: 3.4em;
+
+ > li {
+ font-family: Consolas, 'Courier New', monospace;
+ padding-left: @wcfGapSmall;
+ position: relative;
+ white-space: pre-wrap;
+ word-break: break-all;
+ word-wrap: break-word;
+
+ > .lineAnchor {
+ display: block;
+ height: 1.4em;
+ left: -3.6em;
+ position: absolute;
+ top: 0;
+ width: 3.0em;
+ }
+
+ &:target {
+ background-color: @wcfHighlightBackgroundColor;
+ }
+ }
+ }
+ }
+}
+
+@media only screen and (max-width: 800px) {
+ .codeBox {
+ > div {
+ padding: @wcfGapSmall;
+
+ .boxShadow(0, 0, transparent);
+
+ > div {
+ margin-left: 0;
+
+ > h3 {
+ padding-left: 0;
+ }
+ }
+
+ > ol {
+ list-style-type: none;
+ margin-left: 0;
+
+ > li {
+ padding-left: 0;
+ }
+ }
+ }
+ }
+}
+
+/* ############## Code Styles ############## */
+
+/* -- -- -- Code Box -- -- -- */
+
+.codeBox .hlQuotes {
+ color: red;
+}
+
+.codeBox .hlComments,
+.codeBox .hlOperators {
+ color: green;
+}
+
+.codeBox .hlKeywords1 {
+ color: blue;
+}
+
+.codeBox .hlKeywords2 {
+ color: darkred;
+}
+
+.codeBox .hlKeywords3 {
+ color: darkviolet;
+}
+
+.codeBox .hlKeywords4 {
+ color: darkgoldenrod;
+}
+
+.codeBox .hlKeywords5 {
+ color: crimson;
+}
+
+.codeBox .hlNumbers {
+ color: darkorange;
+}
+
+/* -- -- -- Code Highlighters -- -- -- */
+
+/* DIFF */
+
+.diffHighlighter .hlComments {
+ color: darkviolet;
+}
+
+.diffHighlighter .hlRemoved {
+ color: red;
+}
+
+.diffHighlighter .hlAdded {
+ color: green;
+}
+
+/* PHP */
+
+.phpHighlighter .hlKeywords2 {
+ color: green;
+}
+
+.phpHighlighter .hlComments {
+ color: darkgoldenrod;
+}
+
+/* CSS */
+
+.cssHighlighter .hlComments {
+ color: #236e26;
+}
+
+.cssHighlighter .hlColors {
+ color: #751116;
+}
+
+.cssHighlighter .hlNumbers,
+.sqlHighlighter .hlNumbers {
+ color: #1906fd;
+}
+
+.cssHighlighter .hlKeywords1 {
+ color: #87154f;
+}
+
+.cssHighlighter .hlKeywords2 {
+ color: #994509;
+}
+
+.cssHighlighter .hlKeywords3,
+.cssHighlighter .hlKeywords4 {
+ color: inherit;
+}
+
+/* SQL */
+
+.sqlHighlighter .hlKeywords1 {
+ color: #663821;
+}
+
+.sqlHighlighter .hlKeywords2 {
+ color: #871550;
+}
+
+/* Quote Box */
+.quoteBox {
+ background-color: @wcfContentBackgroundColor;
+ min-height: 28px;
+ margin-bottom: @wcfGapTiny;
+ position: relative;
+
+ &.containerPadding {
+ padding-left: 54px;
+ }
+
+ &:before {
+ content: "\f10d";
+ color: @wcfDimmedColor;
+ font-family: FontAwesome;
+ font-size: 28px;
+ position: absolute;
+ left: @wcfGapMedium;
+ top: @wcfGapSmall;
+ }
+
+ > header {
+ padding-bottom: @wcfGapTiny;
+ border-bottom: 1px dotted @wcfContainerBorderColor;
+ margin-bottom: @wcfGapSmall;
+
+ > h3 {
+ font-weight: bold;
+ }
+ }
+
+ /* nested quotes */
+ .quoteBox {
+ background-image: none;
+ padding-left: @wcfGapLarge;
+ min-height: 0;
+
+ &:before {
+ display: none;
+ }
+ }
+}
+
+@media only screen and (max-width: 800px) {
+ .quoteBox {
+ &:before {
+ font-size: 14px;
+ left: @wcfGapSmall;
+ }
+
+ &.containerPadding {
+ padding-left: 28px;
+ }
+
+ .quoteBox {
+ padding-left: @wcfGapSmall;
+ }
+ }
+}
+
+/* spoiler box */
+.spoilerBox {
+ background-color: @wcfContentBackgroundColor;
+ padding-bottom: @wcfGapSmall;
+
+ > header {
+ margin-bottom: @wcfGapSmall;
+ }
+
+ > div {
+ padding-bottom: @wcfGapSmall;
+ }
+}
+
+/* inline code tag */
+.inlineCode {
+ background-color: @wcfContentBackgroundColor;
+ border: 1px solid @wcfContainerBorderColor;
+ display: inline-block;
+ font-family: Consolas, 'Courier New', monospace;
+ margin: 0 2px;
+ padding: 0 5px;
+
+ .borderRadius(3px);
+}
+
+/* smiley list */
+ul.smileyList > li {
+ display: inline;
+}
+
+/* bbcode table */
+.bbcodeTable {
+ display: inline-block;
+}
+
+/* media provider */
+/* thanks for this generic class name usage, github */
+.gist .highlight {
+ background-color: inherit;
+}
<item name="wcf.acp.attachment.noItems"><![CDATA[Es wurden keine Dateianhänge gefunden.]]></item>
</category>
+ <category name="wcf.acp.bbcode">
+ <item name="wcf.acp.bbcode.add"><![CDATA[BBCode hinzufügen]]></item>
+ <item name="wcf.acp.bbcode.add.userGroupOptionInfo"><![CDATA[Beachten Sie, dass der neu erstellte BBCode standardmäßig überall verwenden werden kann. Soll das nicht der Fall sein, bearbeiten Sie nach der Erstellung des BBCodes die entsprechenden Benutzergruppenrechte.]]></item>
+ <item name="wcf.acp.bbcode.allowedChildren"><![CDATA[Erlaubte Kindelemente]]></item>
+ <item name="wcf.acp.bbcode.allowedChildren.error.notValid"><![CDATA[Die Form für die erlaubten Kindelemente ist ungültig.]]></item>
+ <item name="wcf.acp.bbcode.attribute"><![CDATA[Attribut]]></item>
+ <item name="wcf.acp.bbcode.attribute.attributeHtml"><![CDATA[HTML-Code des Attributs]]></item>
+ <item name="wcf.acp.bbcode.attribute.validationPattern.error.notValid"><![CDATA[Dies ist kein gültiger regulärer Ausdruck.]]></item>
+ <item name="wcf.acp.bbcode.attribute.validationPattern"><![CDATA[Regulärer Ausdruck zur Validierung]]></item>
+ <item name="wcf.acp.bbcode.attribute.required"><![CDATA[Attribut muss ausgefüllt sein]]></item>
+ <item name="wcf.acp.bbcode.attribute.useText"><![CDATA[Inhalt übernehmen]]></item>
+ <item name="wcf.acp.bbcode.attribute.useText.description"><![CDATA[Soll der Inhalt des BBCodes als Wert für dieses Attribut übernommen werden, wenn es nicht gesetzt ist?]]></item>
+ <item name="wcf.acp.bbcode.attributes"><![CDATA[Attribute]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag"><![CDATA[BBCode]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag.error.inUse"><![CDATA[Dieser BBCode ist bereits vorhanden.]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag.error.notValid"><![CDATA[Dieser BBCode ist ungültig.]]></item>
+ <item name="wcf.acp.bbcode.className"><![CDATA[Klassen-Name]]></item>
+ <item name="wcf.acp.bbcode.className.error.notFound"><![CDATA[Diese Klasse wurde nicht gefunden.]]></item>
+ <item name="wcf.acp.bbcode.delete.sure"><![CDATA[Wollen Sie den BBCode „[{$bbcode->bbcodeTag}]“ wirklich löschen?]]></item>
+ <item name="wcf.acp.bbcode.edit"><![CDATA[BBCode bearbeiten]]></item>
+ <item name="wcf.acp.bbcode.htmlClose"><![CDATA[Schließendes HTML-Tag]]></item>
+ <item name="wcf.acp.bbcode.htmlOpen"><![CDATA[Öffnendes HTML-Tag]]></item>
+ <item name="wcf.acp.bbcode.list"><![CDATA[BBCodes]]></item>
+
+ <item name="wcf.acp.bbcode.mediaProvider.add"><![CDATA[Medienanbieter hinzufügen]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.delete.sure"><![CDATA[Wollen Sie den Medienanbieter „{$mediaProvider->title}“ wirklich löschen?]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.edit"><![CDATA[Medienanbieter bearbeiten]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.html"><![CDATA[HTML-Code]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.html.description"><![CDATA[{literal}Geben Sie hier den HTML-Code für das Medium an. Variablen im Format <code>{$variable}</code> werden durch die entsprechend benannte Untergruppe des regulären Ausdrucks ersetzt.<br />
+ Beispiel:<br />
+ <code>{$ID}</code> wird durch die beispielhaften Medium-IDs der Erklärung oberhalb ersetzt.{/literal}]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.list"><![CDATA[Medienanbieter]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex"><![CDATA[Regulärer Ausdruck]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex.description"><![CDATA[Geben Sie hier das Linkformat als <a href="{@$__wcf->getPath('wcf')}acp/dereferrer.php?url=http%3A%2F%2Fde.wikipedia.org%2Fwiki%2FRegul%25C3%25A4rer_Ausdruck">regulären Ausdruck</a> an.<br />
+ Mehrere Ausdrücke können zeilenweise angegeben werden.<br />
+ Beispiele für die Medium-ID Erkennungen:
+ <ul>
+ <li><code>(?<ID>[0-9]+)</code> erkennt eine Medium-ID aus Zahlen.</li>
+ <li><code>(?<ID>[0-9a-zA-Z]+)</code> erkennt eine alphanumerische Medium-ID.</li>
+ </ul>]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex.error.notValid"><![CDATA[Dies ist kein gültiger regulärer Ausdruck.]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.title"><![CDATA[Medienanbieter]]></item>
+ <item name="wcf.acp.bbcode.isSourceCode"><![CDATA[Inhalt als Quelltext behandeln]]></item>
+ <item name="wcf.acp.bbcode.isSourceCode.description"><![CDATA[Der Inhalt dieses BBCodes wird als Quelltext interpretiert, BBCodes und Smileys werden nicht umgewandelt.]]></item>
+ <item name="wcf.acp.bbcode.showButton"><![CDATA[Button im WYSIWYG-Editor anzeigen]]></item>
+ <item name="wcf.acp.bbcode.buttonLabel"><![CDATA[Button-Beschriftung]]></item>
+ <item name="wcf.acp.bbcode.wysiwygIcon"><![CDATA[Button-Grafik]]></item>
+ </category>
+
<category name="wcf.acp.cache">
<item name="wcf.acp.cache.button.clear"><![CDATA[Cache leeren]]></item>
<item name="wcf.acp.cache.clear.sure"><![CDATA[Wollen Sie den Cache wirklich komplett leeren?]]></item>
<item name="wcf.acp.group.option.user.attachment.quota.description"><![CDATA[Dem Nutzer zur Verfügung stehenden Speicherplatz für alle Dateianhänge insgesamt.]]></item>
<item name="wcf.acp.group.option.category.admin.attachment"><![CDATA[Dateianhänge]]></item>
<item name="wcf.acp.group.option.admin.attachment.canManageAttachment"><![CDATA[Kann Dateianhänge verwalten]]></item>
+ <item name="wcf.acp.group.option.category.admin.content.bbcode"><![CDATA[BBCodes]]></item>
+ <item name="wcf.acp.group.option.admin.content.bbcode.canManageBBCode"><![CDATA[Kann BBCodes verwalten]]></item>
+ <item name="wcf.acp.group.option.category.admin.content.smiley"><![CDATA[Smileys]]></item>
+ <item name="wcf.acp.group.option.admin.content.smiley.canManageSmiley"><![CDATA[Kann Smileys verwalten]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.template.group.add"><![CDATA[Templategruppe hinzufügen]]></item>
<item name="wcf.acp.menu.link.attachment"><![CDATA[Dateianhänge]]></item>
<item name="wcf.acp.menu.link.attachment.list"><![CDATA[Dateianhänge verwalten]]></item>
+ <item name="wcf.acp.menu.link.bbcode"><![CDATA[BBCodes]]></item>
+ <item name="wcf.acp.menu.link.bbcode.add"><![CDATA[BBCode hinzufügen]]></item>
+ <item name="wcf.acp.menu.link.bbcode.list"><![CDATA[BBCodes auflisten]]></item>
+ <item name="wcf.acp.menu.link.bbcode.mediaProvider.add"><![CDATA[Medienanbieter hinzufügen]]></item>
+ <item name="wcf.acp.menu.link.bbcode.mediaProvider.list"><![CDATA[Medienanbieter auflisten]]></item>
+ <item name="wcf.acp.menu.link.smiley"><![CDATA[Smileys]]></item>
+ <item name="wcf.acp.menu.link.smiley.add"><![CDATA[Smiley hinzufügen]]></item>
+ <item name="wcf.acp.menu.link.smiley.list"><![CDATA[Smileys auflisten]]></item>
+ <item name="wcf.acp.menu.link.smiley.category.add"><![CDATA[Smiley-Kategorie hinzufügen]]></item>
+ <item name="wcf.acp.menu.link.smiley.category.list"><![CDATA[Smiley-Kategorien auflisten]]></item>
</category>
<category name="wcf.acp.option">
<item name="wcf.acp.option.attachment_storage"><![CDATA[Speicherort der Dateianhänge]]></item>
<item name="wcf.acp.option.attachment_storage.description"><![CDATA[Sie können optional einen vom Standard abweichenden Speicherort für die Dateianhänge definieren. Bereits existierende Dateianhänge müssen bei einer Änderung des Speicherortes manuell in den neuen Ort übertragen werden.]]></item>
<item name="wcf.acp.option.module_attachment"><![CDATA[Dateianhänge]]></item>
+ <item name="wcf.acp.option.module_smiley"><![CDATA[Smileys]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.acp.sessionLog.time"><![CDATA[Datum]]></item>
</category>
+ <category name="wcf.acp.smiley">
+ <item name="wcf.acp.smiley.list"><![CDATA[Smileys]]></item>
+ <item name="wcf.acp.smiley.noItems"><![CDATA[Es sind keine Smileys vorhanden.]]></item>
+ <item name="wcf.acp.smiley.delete.sure"><![CDATA[Wollen Sie das Smiley „{$smiley->smileyTitle|language}“ wirklich löschen?]]></item>
+ <item name="wcf.acp.smiley.add"><![CDATA[Smiley hinzufügen]]></item>
+ <item name="wcf.acp.smiley.edit"><![CDATA[Smiley bearbeiten]]></item>
+ <item name="wcf.acp.smiley.title"><![CDATA[Titel]]></item>
+ <item name="wcf.acp.smiley.category.noItems"><![CDATA[Diese Kategorie enthält keine Smileys.]]></item>
+ <item name="wcf.acp.smiley.categoryID"><![CDATA[Kategorie]]></item>
+ <item name="wcf.acp.smiley.categoryID.default"><![CDATA[Standard-Kategorie]]></item>
+ <item name="wcf.acp.smiley.categoryID.error.notValid"><![CDATA[Die gewählte Kategorie ist nicht gültig.]]></item>
+ <item name="wcf.acp.smiley.smileyCode"><![CDATA[Primärer Smiley-Code]]></item>
+ <item name="wcf.acp.smiley.aliases"><![CDATA[Alternative Smiley-Codes]]></item>
+ <item name="wcf.acp.smiley.showOrder"><![CDATA[Position]]></item>
+ </category>
+
<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.attachment.fileType"><![CDATA[Dateityp]]></item>
</category>
+ <category name="wcf.bbcode">
+ <item name="wcf.bbcode.code.text"><![CDATA[{@$highlighterTitle} ({#$lines} Zeile{if $lines != 1}n{/if})]]></item>
+ <item name="wcf.bbcode.code.bash.title"><![CDATA[Shell-Script]]></item>
+ <item name="wcf.bbcode.code.brainfuck.title"><![CDATA[Brainfuck-Quellcode]]></item>
+ <item name="wcf.bbcode.code.c.title"><![CDATA[C-Quellcode]]></item>
+ <item name="wcf.bbcode.code.css.title"><![CDATA[CSS-Quellcode]]></item>
+ <item name="wcf.bbcode.code.diff.title"><![CDATA[Unterschiede-Datei]]></item>
+ <item name="wcf.bbcode.code.html.title"><![CDATA[HTML-Quellcode]]></item>
+ <item name="wcf.bbcode.code.java.title"><![CDATA[Java-Quellcode]]></item>
+ <item name="wcf.bbcode.code.js.title"><![CDATA[JavaScript-Quellcode]]></item>
+ <item name="wcf.bbcode.code.perl.title"><![CDATA[Perl-Quellcode]]></item>
+ <item name="wcf.bbcode.code.php.title"><![CDATA[PHP-Quellcode]]></item>
+ <item name="wcf.bbcode.code.plain.title"><![CDATA[Quellcode]]></item>
+ <item name="wcf.bbcode.code.sql.title"><![CDATA[SQL-Abfrage]]></item>
+ <item name="wcf.bbcode.code.tex.title"><![CDATA[TeX-Quellcode]]></item>
+ <item name="wcf.bbcode.code.tpl.title"><![CDATA[Smarty-Template]]></item>
+ <item name="wcf.bbcode.code.xml.title"><![CDATA[XML-Quellcode]]></item>
+ <item name="wcf.bbcode.quote.title"><![CDATA[{@$quoteAuthor} schrieb:]]></item>
+ <item name="wcf.bbcode.quote.text"><![CDATA[Zitat{if $cite} von {@$cite}{/if}: „{@$content}“]]></item>
+ <item name="wcf.bbcode.spoiler.show"><![CDATA[Spoiler anzeigen]]></item>
+ <item name="wcf.bbcode.spoiler.text"><![CDATA[(Versteckter Text)]]></item>
+ </category>
+
+
<category name="wcf.category">
<item name="wcf.category.add"><![CDATA[Kategorie hinzufügen]]></item>
<item name="wcf.category.button.list"><![CDATA[Kategorien auflisten]]></item>
<item name="wcf.user.error.isBanned"><![CDATA[Ihr Benutzeraccount wurde gesperrt{if $__wcf->user->banReason}: {@$__wcf->user->banReason|htmlspecialchars|nl2br}{else}.{/if}]]></item>
</category>
- <!-- i18n category -->
+ <!-- i18n categories -->
<category name="wcf.user.option" />
+ <category name="wcf.smiley" />
</language>
<item name="wcf.acp.attachment.noItems"><![CDATA[There are no attachments.]]></item>
</category>
+ <category name="wcf.acp.bbcode">
+ <item name="wcf.acp.bbcode.add"><![CDATA[Add BBCode]]></item>
+ <item name="wcf.acp.bbcode.add.userGroupOptionInfo"><![CDATA[Newly created BBCodes will be accessible for everyone. You can restrict usage by editing the specific user group permissions.]]></item>
+ <item name="wcf.acp.bbcode.allowedChildren"><![CDATA[Allowed Children]]></item>
+ <item name="wcf.acp.bbcode.allowedChildren.error.notValid"><![CDATA[List of allowed children is invalid.]]></item>
+ <item name="wcf.acp.bbcode.attribute"><![CDATA[Attribute]]></item>
+ <item name="wcf.acp.bbcode.attribute.attributeHtml"><![CDATA[Attribute HTML code]]></item>
+ <item name="wcf.acp.bbcode.attribute.validationPattern.error.notValid"><![CDATA[Regular expression failed to validate.]]></item>
+ <item name="wcf.acp.bbcode.attribute.validationPattern"><![CDATA[Regular Expression for Validation]]></item>
+ <item name="wcf.acp.bbcode.attribute.required"><![CDATA[Required]]></item>
+ <item name="wcf.acp.bbcode.attribute.useText"><![CDATA[Use content]]></item>
+ <item name="wcf.acp.bbcode.attribute.useText.description"><![CDATA[Content between BBCode tags will be used as attribute value unless it was explicitly specified]]></item>
+ <item name="wcf.acp.bbcode.attributes"><![CDATA[Attribute]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag"><![CDATA[BBCode]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag.error.inUse"><![CDATA[BBCode already exists.]]></item>
+ <item name="wcf.acp.bbcode.bbcodeTag.error.notValid"><![CDATA[BBCode is invalid.]]></item>
+ <item name="wcf.acp.bbcode.className"><![CDATA[PHP Class Name]]></item>
+ <item name="wcf.acp.bbcode.className.error.notFound"><![CDATA[Unable to find specified class.]]></item>
+ <item name="wcf.acp.bbcode.delete.sure"><![CDATA[Do you really want to delete the BBCode “[{$bbcode->bbcodeTag}]”?]]></item>
+ <item name="wcf.acp.bbcode.edit"><![CDATA[Edit BBCode]]></item>
+ <item name="wcf.acp.bbcode.htmlClose"><![CDATA[Closing HTML tag]]></item>
+ <item name="wcf.acp.bbcode.htmlOpen"><![CDATA[Opening HTML tag]]></item>
+ <item name="wcf.acp.bbcode.list"><![CDATA[BBCodes]]></item>
+
+ <item name="wcf.acp.bbcode.mediaProvider.add"><![CDATA[Add Media Provider]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.delete.sure"><![CDATA[Do you really want to delete the media provider “{$mediaProvider->title}”?]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.edit"><![CDATA[Edit Media Provider]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.html"><![CDATA[HTML Code]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.html.description"><![CDATA[{literal}Enter the media’s HTML code. Variables are recognized as {$variable} and will be replaced with the regular expression’s matching named group.<br />
+Example:<br />
+<code>{$ID}</code> will be replaced with the medium ID as shown above.{/literal}]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.list"><![CDATA[Media Providers]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex"><![CDATA[Regular Expression]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex.description"><![CDATA[Provide the link format as a <a href="{@$__wcf->getPath()}acp/dereferrer.php?url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FRegular_expression">regular expression</a>. One expression per line.<br />
+Examples for medium ID detection:
+<ul>
+ <li><code>(?<ID>[0-9]+)</code> detects a numeric ID.</li>
+ <li><code>(?<ID>[0-9a-zA-Z]+)</code> detects an alphanumeric ID</li>
+</ul>]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.regex.error.notValid"><![CDATA[Regular expression failed to validate.]]></item>
+ <item name="wcf.acp.bbcode.mediaProvider.title"><![CDATA[Media Providers]]></item>
+ <item name="wcf.acp.bbcode.isSourceCode"><![CDATA[Content represents source code]]></item>
+ <item name="wcf.acp.bbcode.isSourceCode.description"><![CDATA[Disables conversion of bbcodes and smilies.]]></item>
+ <item name="wcf.acp.bbcode.showButton"><![CDATA[Show button in WYSIWYG editor]]></item>
+ <item name="wcf.acp.bbcode.buttonLabel"><![CDATA[Button Label]]></item>
+ <item name="wcf.acp.bbcode.wysiwygIcon"><![CDATA[Button Image]]></item>
+ </category>
+
<category name="wcf.acp.cache">
<item name="wcf.acp.cache.button.clear"><![CDATA[Clear Cache]]></item>
<item name="wcf.acp.cache.clear.sure"><![CDATA[Do you really want to clear the cache?]]></item>
<item name="wcf.acp.group.option.user.attachment.quota.description"><![CDATA[Total quota for all attachments uploaded by a user]]></item>
<item name="wcf.acp.group.option.category.admin.attachment"><![CDATA[Attachments]]></item>
<item name="wcf.acp.group.option.admin.attachment.canManageAttachment"><![CDATA[Can manage attachments]]></item>
+ <item name="wcf.acp.group.option.category.admin.content.bbcode"><![CDATA[BBCodes]]></item>
+ <item name="wcf.acp.group.option.admin.content.bbcode.canManageBBCode"><![CDATA[Can manage BBCodes]]></item>
+ <item name="wcf.acp.group.option.category.admin.content.smiley"><![CDATA[Smilies]]></item>
+ <item name="wcf.acp.group.option.admin.content.smiley.canManageSmiley"><![CDATA[Can manage smilies]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.template.group.add"><![CDATA[Add Template Group]]></item>
<item name="wcf.acp.menu.link.attachment"><![CDATA[Attachments]]></item>
<item name="wcf.acp.menu.link.attachment.list"><![CDATA[Browse Attachments]]></item>
+ <item name="wcf.acp.menu.link.bbcode"><![CDATA[BBCodes]]></item>
+ <item name="wcf.acp.menu.link.bbcode.add"><![CDATA[Add BBCode]]></item>
+ <item name="wcf.acp.menu.link.bbcode.list"><![CDATA[List BBCodes]]></item>
+ <item name="wcf.acp.menu.link.bbcode.mediaProvider.add"><![CDATA[Add Media Provider]]></item>
+ <item name="wcf.acp.menu.link.bbcode.mediaProvider.list"><![CDATA[List Media Provider]]></item>
+ <item name="wcf.acp.menu.link.smiley"><![CDATA[Smilies]]></item>
+ <item name="wcf.acp.menu.link.smiley.add"><![CDATA[Add Smiley]]></item>
+ <item name="wcf.acp.menu.link.smiley.list"><![CDATA[List Smilies]]></item>
+ <item name="wcf.acp.menu.link.smiley.category.add"><![CDATA[Add Smiley Category]]></item>
+ <item name="wcf.acp.menu.link.smiley.category.list"><![CDATA[List Smiley Categories]]></item>
</category>
<category name="wcf.acp.option">
<item name="wcf.acp.option.attachment_storage"><![CDATA[Storage Location]]></item>
<item name="wcf.acp.option.attachment_storage.description"><![CDATA[Changes storage location for attachments. Heads up! You are responsible to move already existing attachments to the new location.]]></item>
<item name="wcf.acp.option.module_attachment"><![CDATA[Attachments]]></item>
+ <item name="wcf.acp.option.module_smiley"><![CDATA[Smilies]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.acp.sessionLog.time"><![CDATA[Time]]></item>
</category>
+ <category name="wcf.acp.smiley">
+ <item name="wcf.acp.smiley.list"><![CDATA[Smilies]]></item>
+ <item name="wcf.acp.smiley.noItems"><![CDATA[There are no smilies.]]></item>
+ <item name="wcf.acp.smiley.delete.sure"><![CDATA[Do you really want to delete the smiley “{$smiley->smileyTitle|language}”?]]></item>
+ <item name="wcf.acp.smiley.add"><![CDATA[Add Smiley]]></item>
+ <item name="wcf.acp.smiley.edit"><![CDATA[Edit Smiley]]></item>
+ <item name="wcf.acp.smiley.title"><![CDATA[Title]]></item>
+ <item name="wcf.acp.smiley.category.noItems"><![CDATA[This category contains no smilies.]]></item>
+ <item name="wcf.acp.smiley.categoryID"><![CDATA[Category]]></item>
+ <item name="wcf.acp.smiley.categoryID.default"><![CDATA[Default Category]]></item>
+ <item name="wcf.acp.smiley.categoryID.error.notValid"><![CDATA[Chosen category is invalid.]]></item>
+ <item name="wcf.acp.smiley.smileyCode"><![CDATA[Primary Smiley Code]]></item>
+ <item name="wcf.acp.smiley.aliases"><![CDATA[Alternative Smiley Codes]]></item>
+ <item name="wcf.acp.smiley.showOrder"><![CDATA[Show Order]]></item>
+ </category>
+
<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.attachment.fileType"><![CDATA[Type]]></item>
</category>
+ <category name="wcf.bbcode">
+ <item name="wcf.bbcode.code.text"><![CDATA[{@$highlighterTitle} ({#$lines} line{if $lines != 1}s{/if})]]></item>
+ <item name="wcf.bbcode.code.bash.title"><![CDATA[Shell-Script]]></item>
+ <item name="wcf.bbcode.code.brainfuck.title"><![CDATA[Brainfuck Source Code]]></item>
+ <item name="wcf.bbcode.code.c.title"><![CDATA[C Source Code]]></item>
+ <item name="wcf.bbcode.code.css.title"><![CDATA[CSS Source Code]]></item>
+ <item name="wcf.bbcode.code.diff.title"><![CDATA[Difference-File]]></item>
+ <item name="wcf.bbcode.code.html.title"><![CDATA[HTML Source Code]]></item>
+ <item name="wcf.bbcode.code.java.title"><![CDATA[Java Source Code]]></item>
+ <item name="wcf.bbcode.code.js.title"><![CDATA[JavaScript Source Code]]></item>
+ <item name="wcf.bbcode.code.perl.title"><![CDATA[Perl Source Code]]></item>
+ <item name="wcf.bbcode.code.php.title"><![CDATA[PHP Source Code]]></item>
+ <item name="wcf.bbcode.code.plain.title"><![CDATA[Source Code]]></item>
+ <item name="wcf.bbcode.code.sql.title"><![CDATA[SQL-Query]]></item>
+ <item name="wcf.bbcode.code.tex.title"><![CDATA[TeX Source Code]]></item>
+ <item name="wcf.bbcode.code.tpl.title"><![CDATA[Smarty-Template]]></item>
+ <item name="wcf.bbcode.code.xml.title"><![CDATA[XML Source Code]]></item>
+ <item name="wcf.bbcode.quote.title"><![CDATA[{@$quoteAuthor} wrote:]]></item>
+ <item name="wcf.bbcode.quote.text"><![CDATA[Quote{if $cite} from {@$cite}{/if}: “{@$content}”]]></item>
+ <item name="wcf.bbcode.spoiler.show"><![CDATA[Show Spoiler]]></item>
+ <item name="wcf.bbcode.spoiler.text"><![CDATA[(Hidden Content)]]></item>
+ </category>
+
<category name="wcf.category">
<item name="wcf.category.add"><![CDATA[Add Category]]></item>
<item name="wcf.category.button.list"><![CDATA[List Categories]]></item>
<item name="wcf.user.error.isBanned"><![CDATA[Your user account has been banned{if $__wcf->user->banReason}: {@$__wcf->user->banReason|htmlspecialchars|nl2br}{else}.{/if}]]></item>
</category>
- <!-- i18n category -->
+ <!-- i18n categories -->
<category name="wcf.user.option" />
+ <category name="wcf.smiley" />
</language>
KEY (objectID, uploadTime)
);
+DROP TABLE IF EXISTS wcf1_bbcode;
+CREATE TABLE wcf1_bbcode (
+ bbcodeID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ bbcodeTag VARCHAR(255) NOT NULL,
+ packageID INT(10) NOT NULL,
+ htmlOpen VARCHAR(255) NOT NULL DEFAULT '',
+ htmlClose VARCHAR(255) NOT NULL DEFAULT '',
+ allowedChildren VARCHAR(255) NOT NULL DEFAULT 'all',
+ className VARCHAR(255) NOT NULL DEFAULT '',
+ wysiwygIcon varchar(255) NOT NULL DEFAULT '',
+ buttonLabel VARCHAR(255) NOT NULL DEFAULT '',
+ isSourceCode TINYINT(1) NOT NULL DEFAULT 0,
+ isDisabled TINYINT(1) NOT NULL DEFAULT 0,
+ showButton TINYINT(1) NOT NULL DEFAULT 0,
+ UNIQUE KEY bbcodeTag (bbcodeTag)
+);
+
+DROP TABLE IF EXISTS wcf1_bbcode_attribute;
+CREATE TABLE wcf1_bbcode_attribute (
+ attributeID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ bbcodeID INT(10) NOT NULL,
+ attributeNo TINYINT(3) NOT NULL DEFAULT 0,
+ attributeHtml VARCHAR(255) NOT NULL DEFAULT '',
+ validationPattern VARCHAR(255) NOT NULL DEFAULT '',
+ required TINYINT(1) NOT NULL DEFAULT 0,
+ useText TINYINT(1) NOT NULL DEFAULT 0,
+ UNIQUE KEY attributeNo (bbcodeID, attributeNo)
+);
+
+DROP TABLE IF EXISTS wcf1_bbcode_media_provider;
+CREATE TABLE wcf1_bbcode_media_provider (
+ providerID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ title VARCHAR(255) NOT NULL,
+ regex TEXT NOT NULL,
+ html TEXT NOT NULL
+);
+
DROP TABLE IF EXISTS wcf1_category;
CREATE TABLE wcf1_category (
categoryID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
UNIQUE KEY sitemapName (packageID, sitemapName)
);
+DROP TABLE IF EXISTS wcf1_smiley;
+CREATE TABLE wcf1_smiley (
+ smileyID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ packageID INT(10) NOT NULL,
+ categoryID INT(10),
+ smileyPath VARCHAR(255) NOT NULL DEFAULT '',
+ smileyTitle VARCHAR(255) NOT NULL DEFAULT '',
+ smileyCode VARCHAR(255) NOT NULL DEFAULT '',
+ aliases TEXT NOT NULL,
+ showOrder INT(10) NOT NULL DEFAULT 0,
+ UNIQUE KEY smileyCode (smileyCode)
+);
+
DROP TABLE IF EXISTS wcf1_spider;
CREATE TABLE wcf1_spider (
spiderID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_attachment ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_attachment ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+ALTER TABLE wcf1_bbcode ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_bbcode_attribute ADD FOREIGN KEY (bbcodeID) REFERENCES wcf1_bbcode (bbcodeID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_category ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_clipboard_action ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
ALTER TABLE wcf1_sitemap ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_smiley ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_smiley ADD FOREIGN KEY (categoryID) REFERENCES wcf1_category (categoryID) ON DELETE SET NULL;
+
ALTER TABLE wcf1_user_storage ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
ALTER TABLE wcf1_style ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('pageLogo', '');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('individualLess', '');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('overrideLess', '');
+
+-- media providers
+-- Videos
+ -- Youtube
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('YouTube', 'https?://(?:.+?\\.)?youtu(?:\\.be/|be\\.com/watch\\?(?:.*?&)?v=)(?<ID>[a-zA-Z0-9_-]+)(?<start>#t=(?:\\d+|(?:\\d+h(?:\\d+m)?(?:\\d+s)?)|(?:\\d+m(?:\\d+s)?)|(?:\\d+s))$)?', '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/{$ID}?wmode=transparent{$start}" frameborder="0" allowfullscreen></iframe>');
+ -- Vimeo
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Vimeo', 'http://vimeo\\.com/(?<ID>\\d+)', '<iframe src="http://player.vimeo.com/video/{$ID}" width="400" height="225" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>');
+ -- MyVideo
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('MyVideo', 'http://(?:www\\.)?myvideo\\.de/watch/(?<ID>\\d+)', '<object style="width:611px;height:383px;" width="611" height="383"><embed src="http://www.myvideo.de/movie/{$ID}" width="611" height="383" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true"></embed><param name="movie" value="http://www.myvideo.de/movie/{$ID}"></param><param name="AllowFullscreen" value="true"></param><param name="AllowScriptAccess" value="always"></param></object>');
+ -- Clipfish
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Clipfish', 'http://(?:www\\.)?clipfish\\.de/video/(?<ID>\\d+)/', '<div style="width:464px; height:404px;"><div style="width:464px; height:384px;"><iframe src="http://www.clipfish.de/embed_video/?vid={$ID}&as=0&col=990000" name="Clipfish Embedded Video" width="464" height="384" align="left" marginheight="0" marginwidth="0" frameborder="0" scrolling="no"></iframe></div></div>');
+ -- Veoh
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Veoh', 'http://(?:www\\.)?veoh\\.com/watch/v(?<ID>\\d+[a-zA-Z0-9]+)', '<object width="410" height="341" id="veohFlashPlayer" name="veohFlashPlayer"><param name="movie" value="http://www.veoh.com/swf/webplayer/WebPlayer.swf?version=AFrontend.5.7.0.1308&permalinkId=v{$ID}&player=videodetailsembedded&videoAutoPlay=0&id=anonymous"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.veoh.com/swf/webplayer/WebPlayer.swf?version=AFrontend.5.7.0.1308&permalinkId=v{$ID}&player=videodetailsembedded&videoAutoPlay=0&id=anonymous" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="410" height="341" id="veohFlashPlayerEmbed" name="veohFlashPlayerEmbed"></embed></object>');
+ -- DailyMotion
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('DailyMotion', 'https?://(?:www\\.)?dailymotion\\.com/video/(?<ID>[a-zA-Z0-9]+)', '<iframe frameborder="0" width="480" height="208" src="http://www.dailymotion.com/embed/video/{$ID}"></iframe>');
+ -- YouKu
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('YouKu', 'https?://(?:.+?\\.)?youku\\.com/v_show/id_(?<ID>[a-zA-Z0-9_-]+)(?:\\.html)?', '<iframe height=498 width=510 src="http://player.youku.com/embed/{$ID}" frameborder="0" allowfullscreen></iframe>');
+-- Misc
+ -- github gist
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('github gist', 'https://gist.github.com/(?<ID>[^/]+/[0-9a-zA-Z]+)', '<script src="https://gist.github.com/{$ID}.js"> </script>');
+ -- soundcloud
+ INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Soundcloud', 'https?://soundcloud.com/(?<artist>[a-zA-Z0-9_-]+)/(?<song>[a-zA-Z0-9_-]+)', '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=http%3A%2F%2Fsoundcloud.com%2F{$artist}%2F{$song}"></iframe>');
+