From: Marcel Werk Date: Mon, 20 May 2013 20:37:46 +0000 (+0200) Subject: Merged com.woltlab.wcf.bbcode into WCF X-Git-Tag: 2.0.0_Beta_1~131 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=dcc2332da42408c65ed4e83c4173b56a23dc4a27;p=GitHub%2FWoltLab%2FWCF.git Merged com.woltlab.wcf.bbcode into WCF --- diff --git a/com.woltlab.wcf/acpMenu.xml b/com.woltlab.wcf/acpMenu.xml index 616cbf171b..8825023387 100644 --- a/com.woltlab.wcf/acpMenu.xml +++ b/com.woltlab.wcf/acpMenu.xml @@ -387,6 +387,70 @@ wcf.acp.menu.link.content + + wcf.acp.menu.link.content + + + + + wcf.acp.menu.link.bbcode + admin.content.bbcode.canManageBBCode + 1 + + + + + wcf.acp.menu.link.bbcode + admin.content.bbcode.canManageBBCode + 2 + + + + + wcf.acp.menu.link.bbcode + admin.content.bbcode.canManageBBCode + 3 + + + + + wcf.acp.menu.link.bbcode + admin.content.bbcode.canManageBBCode + 4 + + + + wcf.acp.menu.link.content + + + + + wcf.acp.menu.link.smiley + admin.content.smiley.canManageSmiley + 1 + + + + + wcf.acp.menu.link.smiley + admin.content.smiley.canManageSmiley + 2 + + + + + wcf.acp.menu.link.smiley + admin.content.smiley.canManageSmiley + 3 + + + + + wcf.acp.menu.link.smiley + admin.content.smiley.canManageSmiley + 4 + + wcf.acp.menu.link.attachment diff --git a/com.woltlab.wcf/bbcode.xml b/com.woltlab.wcf/bbcode.xml new file mode 100644 index 0000000000..503a402330 --- /dev/null +++ b/com.woltlab.wcf/bbcode.xml @@ -0,0 +1,163 @@ + + + + + b + b + all^b + + + i + i + all^i + + + span style="text-decoration: underline" + span + all^u + + + s + s + all^s + + + sub + sub + all^sub + + + sup + sup + all^sup + + + a + a + none^img,b,i,u,s,sub,sup,color,size,font + + + href="mailto:%s" + 1 + 1 + ^[^\s]+@[^\s]+$ + + + + + span + span + + + style="color: %s" + ^(#([0-9a-f]{3}|[0-9a-f]{6})|[a-z]+)$ + 1 + + + + + span + span + + + style="font-size: %dpt" + ^([89]{1}|[1-3]{1}[0-9]{1})$ + 1 + + + 1 + + + span + span + + + style="font-family: %s" + ^[^"';}\(\)]*$ + 1 + + + + + div + div + + + style="text-align: %s" + ^(left|right|center|justify)$ + 1 + + + + + wcf\system\bbcode\QuoteBBCode + + + + + + + wcf\system\bbcode\CodeBBCode + none + + + ^\d+$ + + + 1 + + + wcf\system\bbcode\ImageBBCode + none + + + 1 + 1 + ^.+$ + + + ^(left|right)$ + + + + + wcf\system\bbcode\URLBBCode + none^img,b,i,u,s,sub,sup,color,size,font + + + 1 + 1 + ^.+$ + + + + + wcf\system\bbcode\ListBBCode + + + ^(1|a|none|circle|square|disc|decimal|lower-roman|upper-roman|decimal-leading-zero|lower-greek|lower-latin|upper-latin|armenian|georgian)$ + + + + + wcf\system\bbcode\TableBBCode + all^table + + + wcf\system\bbcode\MediaBBCode + 1 + + + wcf\system\bbcode\SpoilerBBCode + + + + + + code class="inlineCode" + code + none + 1 + + + \ No newline at end of file diff --git a/com.woltlab.wcf/coreObject.xml b/com.woltlab.wcf/coreObject.xml index 9d10bf1928..ed880434dc 100644 --- a/com.woltlab.wcf/coreObject.xml +++ b/com.woltlab.wcf/coreObject.xml @@ -22,5 +22,8 @@ + + + diff --git a/com.woltlab.wcf/objectType.xml b/com.woltlab.wcf/objectType.xml index f3f26309ee..8efe83b66f 100644 --- a/com.woltlab.wcf/objectType.xml +++ b/com.woltlab.wcf/objectType.xml @@ -11,5 +11,11 @@ com.woltlab.wcf.collapsibleSidebar com.woltlab.wcf.collapsibleContent + + + com.woltlab.wcf.bbcode.smiley + com.woltlab.wcf.category + wcf\system\category\SmileyCategoryType + \ No newline at end of file diff --git a/com.woltlab.wcf/option.xml b/com.woltlab.wcf/option.xml index 927f8e04c4..e69fab03f1 100644 --- a/com.woltlab.wcf/option.xml +++ b/com.woltlab.wcf/option.xml @@ -180,6 +180,12 @@ 1 + + + + + + diff --git a/wcfsetup/install/files/acp/templates/bbCodeSelectOptionType.tpl b/wcfsetup/install/files/acp/templates/bbCodeSelectOptionType.tpl new file mode 100644 index 0000000000..73c9395640 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/bbCodeSelectOptionType.tpl @@ -0,0 +1,3 @@ +{foreach from=$bbCodes item='bbCode'} + +{/foreach} \ No newline at end of file diff --git a/wcfsetup/install/files/acp/templates/bbcodeAdd.tpl b/wcfsetup/install/files/acp/templates/bbcodeAdd.tpl new file mode 100644 index 0000000000..9f1f8cd4d8 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/bbcodeAdd.tpl @@ -0,0 +1,270 @@ +{include file='header' pageTitle='wcf.acp.bbcode.'|concat:$action} + +{capture assign='attributeTemplate'} +
+ {lang}wcf.acp.bbcode.attribute{/lang} {ldelim}#$attributeNo} +
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {lang}wcf.acp.bbcode.attribute.useText.description{/lang} +
+
+ + {event name='attributeFields'} +
+{/capture} + + +
+

{lang}wcf.acp.bbcode.{$action}{/lang}

+
+ +{if $action == 'add'} +

{lang}wcf.acp.bbcode.add.userGroupOptionInfo{/lang}

+{/if} + +{if $errorField} +

{lang}wcf.global.form.error{/lang}

+{/if} + +{if $success|isset} +

{lang}wcf.global.success.{$action}{/lang}

+{/if} + +
+ +
+ +
+
+
+ {lang}wcf.global.form.data{/lang} + + +
+
+ + {if $errorField == 'bbcodeTag'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.bbcodeTag.error.{$errorType}{/lang} + {/if} + + {/if} +
+ + + +
+
+ + {if $errorField == 'htmlOpen'} + {lang}wcf.acp.bbcode.htmlOpen.error.{$errorType}{/lang} + {/if} +
+ + + +
+
+ + {if $errorField == 'htmlClose'} + {lang}wcf.acp.bbcode.htmlClose.error.{$errorType}{/lang} + {/if} +
+ + + +
+
+ + {if $errorField == 'allowedChildren'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.allowedChildren.error.{$errorType}{/lang} + {/if} + + {/if} +
+ + +
+
+ + {lang}wcf.acp.bbcode.isSourceCode.description{/lang} +
+
+ + +
+
+ + {if $errorField == 'className'} + {lang}wcf.acp.bbcode.className.error.{$errorType}{/lang} + {/if} +
+ + + {if $nativeBBCode|empty} +
+
+ +
+
+ +
+
+
+ + {if $errorField == 'buttonLabel'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.buttonLabel.error.{@$errorType}{/lang} + {/if} + + {/if} + + {include file='multipleLanguageInputJavascript' elementIdentifier='buttonLabel' forceSelection=false} +
+
+ +
+
+
+ + {if $errorField == 'wysiwygIcon'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.wysiwygIcon.error.{@$errorType}{/lang} + {/if} + + {/if} +
+
+ {/if} + + {event name='dataFields'} +
+ +
+ {lang}wcf.acp.bbcode.attributes{/lang} + + {foreach from=$attributes item='attribute'} +
+ {lang}wcf.acp.bbcode.attribute{/lang} {#$attribute->attributeNo} + + attributeNo} class="formError"{/if}> +
+
+ +
+ + + attributeNo} class="formError"{/if}> +
+
+ + {if $errorField == 'attributeValidationPattern'|concat:$attribute->attributeNo} + + {if $errorType == 'notValid'} + {lang}wcf.acp.bbcode.attribute.error.validationPattern.notValid{/lang} + {/if} + + {/if} +
+ + + attributeNo} class="formError"{/if}> +
+ +
+ + + attributeNo} class="formError"{/if}> +
+ + {lang}wcf.acp.bbcode.attribute.useText.description{/lang} +
+ + + {event name='attributeFields'} +
+ {/foreach} +
+ + {event name='fieldsets'} +
+ +
+ +
+
+ +{include file='footer'} \ No newline at end of file diff --git a/wcfsetup/install/files/acp/templates/bbcodeList.tpl b/wcfsetup/install/files/acp/templates/bbcodeList.tpl new file mode 100644 index 0000000000..1a75dcca12 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/bbcodeList.tpl @@ -0,0 +1,82 @@ +{include file='header' pageTitle='wcf.acp.bbcode.list'} + +
+

{lang}wcf.acp.bbcode.list{/lang}

+ + +
+ +
+ {pages print=true assign=pagesLinks controller="BBCodeList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"} + + +
+ +{if $objects|count} +
+
+

{lang}wcf.acp.bbcode.list{/lang} {#$items}

+
+ + + + + + + + + {event name='columnHeads'} + + + + + {foreach from=$objects item=bbcode} + + + + + + + {event name='columns'} + + {/foreach} + +
{lang}wcf.global.objectID{/lang}{lang}wcf.acp.bbcode.bbcodeTag{/lang}{lang}wcf.acp.bbcode.className{/lang}
+ + + + + {event name='rowButtons'} + {@$bbcode->bbcodeID}[{$bbcode->bbcodeTag}]{$bbcode->className}
+ +
+ +
+ {@$pagesLinks} + + +
+{else} +

{lang}wcf.acp.bbcode.noneAvailable{/lang}

+{/if} + +{include file='footer'} diff --git a/wcfsetup/install/files/acp/templates/bbcodeMediaProviderAdd.tpl b/wcfsetup/install/files/acp/templates/bbcodeMediaProviderAdd.tpl new file mode 100644 index 0000000000..12bb93e2ea --- /dev/null +++ b/wcfsetup/install/files/acp/templates/bbcodeMediaProviderAdd.tpl @@ -0,0 +1,91 @@ +{include file='header' pageTitle='wcf.acp.bbcode.mediaProvider.'|concat:$action} + +
+

{lang}wcf.acp.bbcode.mediaProvider.{$action}{/lang}

+
+ +{if $errorField} +

{lang}wcf.global.form.error{/lang}

+{/if} + +{if $success|isset} +

{lang}wcf.global.success.{$action}{/lang}

+{/if} + +
+ +
+ +
+
+
+ {lang}wcf.global.form.data{/lang} + + +
+
+ + {if $errorField == 'title'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.mediaProvider.title.error.{$errorType}{/lang} + {/if} + + {/if} +
+ + + +
+
+ + {if $errorField == 'regex'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.mediaProvider.regex.error.{$errorType}{/lang} + {/if} + + {/if} + {lang}wcf.acp.bbcode.mediaProvider.regex.description{/lang} +
+ + + +
+
+ + {if $errorField == 'html'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.bbcode.mediaProvider.html.error.{$errorType}{/lang} + {/if} + + {/if} + {lang}wcf.acp.bbcode.mediaProvider.html.description{/lang} +
+ + + {event name='dataFields'} +
+ + {event name='fieldsets'} +
+ +
+ +
+
+ +{include file='footer'} \ No newline at end of file diff --git a/wcfsetup/install/files/acp/templates/bbcodeMediaProviderList.tpl b/wcfsetup/install/files/acp/templates/bbcodeMediaProviderList.tpl new file mode 100644 index 0000000000..fbfb184100 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/bbcodeMediaProviderList.tpl @@ -0,0 +1,81 @@ +{include file='header' pageTitle='wcf.acp.bbcode.mediaProvider.list'} + +
+

{lang}wcf.acp.bbcode.mediaProvider.list{/lang}

+ + +
+ +
+ {pages print=true assign=pagesLinks controller="BBCodeMediaProviderList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"} + + +
+ +{if $objects|count} +
+
+

{lang}wcf.acp.bbcode.mediaProvider.list{/lang} {#$items}

+
+ + + + + + + + {event name='columnHeads'} + + + + + {foreach from=$objects item='mediaProvider'} + + + + + + {event name='columns'} + + {/foreach} + +
{lang}wcf.global.objectID{/lang}{lang}wcf.acp.bbcode.mediaProvider.title{/lang}
+ + + + {event name='rowButtons'} + {@$mediaProvider->providerID}{$mediaProvider->title}
+
+ +
+ {@$pagesLinks} + + +
+{else} +
+
+

{lang}wcf.acp.bbcode.mediaProvider.noneAvailable{/lang}

+
+
+{/if} + +{include file='footer'} diff --git a/wcfsetup/install/files/acp/templates/smileyAdd.tpl b/wcfsetup/install/files/acp/templates/smileyAdd.tpl new file mode 100644 index 0000000000..fd173559e8 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/smileyAdd.tpl @@ -0,0 +1,120 @@ +{include file='header' pageTitle='wcf.acp.smiley.'|concat:$action} + +
+

{lang}wcf.acp.smiley.{$action}{/lang}

+
+ +{if $errorField} +

{lang}wcf.global.form.error{/lang}

+{/if} + +{if $success|isset} +

{lang}wcf.global.form.{$action}.success{/lang}

+{/if} + +
+ {hascontent} + + {/hascontent} +
+ +
+
+
+ {lang}wcf.global.form.data{/lang} + + +
+
+ + {if $errorField == 'smileyTitle'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.smiley.smileyTitle.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ + + {include file='multipleLanguageInputJavascript' elementIdentifier='smileyTitle' forceSelection=false} + + +
+
+ + + {if $errorField == 'categoryID'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.smiley.categoryID.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ + + +
+
+ + {if $errorField == 'smileyCode'} + + {if $errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang} + {/if} + + {/if} +
+ + + +
+
+ + + {if $errorField == 'aliases'} + + {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang} + + {/if} +
+ + + +
+
+ + + {if $errorField == 'showOrder'} + + {lang}wcf.acp.smiley.smileyCode.error.{@$errorType}{/lang} + + {/if} +
+ +
+
+ +
+ +
+
+ +{include file='footer'} \ No newline at end of file diff --git a/wcfsetup/install/files/acp/templates/smileyList.tpl b/wcfsetup/install/files/acp/templates/smileyList.tpl new file mode 100644 index 0000000000..a9a543ae67 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/smileyList.tpl @@ -0,0 +1,69 @@ +{include file='header' pageTitle='wcf.acp.smiley.list'} + +{if $objects|count} + +{/if} + +
+

{lang}wcf.acp.smiley.list{/lang}

+
+ +
+ {pages print=true assign=pagesLinks controller="SmileyList" object=$category link="pageNo=%d"} + + +
+{if $smileyCount} +
+ +
+ {if $objects|count} +
    + {foreach from=$objects item=smiley} +
  1. + + {lang}{$smiley->smileyTitle}{/lang} {$smiley->smileyCode}{foreach from=$smiley->getAliases() item='alias'} {$alias}{/foreach} + + + + + + {event name='itemButtons'} + + +
    1. + + {/foreach} +
    +
    + +
    + {else} +

    {lang}wcf.acp.smiley.category.noItems{/lang}

    + {/if} +
    +
    +{else} +

    {lang}wcf.acp.smiley.noItems{/lang}

    +{/if} + +{include file='footer'} diff --git a/wcfsetup/install/files/images/smilies/angry.png b/wcfsetup/install/files/images/smilies/angry.png new file mode 100644 index 0000000000..e16d826e3b Binary files /dev/null and b/wcfsetup/install/files/images/smilies/angry.png differ diff --git a/wcfsetup/install/files/images/smilies/attention.png b/wcfsetup/install/files/images/smilies/attention.png new file mode 100644 index 0000000000..e75f040d8c Binary files /dev/null and b/wcfsetup/install/files/images/smilies/attention.png differ diff --git a/wcfsetup/install/files/images/smilies/biggrin.png b/wcfsetup/install/files/images/smilies/biggrin.png new file mode 100644 index 0000000000..3772ea7c98 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/biggrin.png differ diff --git a/wcfsetup/install/files/images/smilies/blink.png b/wcfsetup/install/files/images/smilies/blink.png new file mode 100644 index 0000000000..d5661376bf Binary files /dev/null and b/wcfsetup/install/files/images/smilies/blink.png differ diff --git a/wcfsetup/install/files/images/smilies/confused.png b/wcfsetup/install/files/images/smilies/confused.png new file mode 100644 index 0000000000..1a42f191e9 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/confused.png differ diff --git a/wcfsetup/install/files/images/smilies/cool.png b/wcfsetup/install/files/images/smilies/cool.png new file mode 100644 index 0000000000..727de8150d Binary files /dev/null and b/wcfsetup/install/files/images/smilies/cool.png differ diff --git a/wcfsetup/install/files/images/smilies/crying.png b/wcfsetup/install/files/images/smilies/crying.png new file mode 100644 index 0000000000..05aea3d451 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/crying.png differ diff --git a/wcfsetup/install/files/images/smilies/cursing.png b/wcfsetup/install/files/images/smilies/cursing.png new file mode 100644 index 0000000000..591bb65da5 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/cursing.png differ diff --git a/wcfsetup/install/files/images/smilies/evil.png b/wcfsetup/install/files/images/smilies/evil.png new file mode 100644 index 0000000000..bf810603c7 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/evil.png differ diff --git a/wcfsetup/install/files/images/smilies/grin.png b/wcfsetup/install/files/images/smilies/grin.png new file mode 100644 index 0000000000..8950626671 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/grin.png differ diff --git a/wcfsetup/install/files/images/smilies/heart.png b/wcfsetup/install/files/images/smilies/heart.png new file mode 100644 index 0000000000..4aa3120bbb Binary files /dev/null and b/wcfsetup/install/files/images/smilies/heart.png differ diff --git a/wcfsetup/install/files/images/smilies/huh.png b/wcfsetup/install/files/images/smilies/huh.png new file mode 100644 index 0000000000..ef3f6d95f0 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/huh.png differ diff --git a/wcfsetup/install/files/images/smilies/kiss.png b/wcfsetup/install/files/images/smilies/kiss.png new file mode 100644 index 0000000000..e7bf28255e Binary files /dev/null and b/wcfsetup/install/files/images/smilies/kiss.png differ diff --git a/wcfsetup/install/files/images/smilies/love.png b/wcfsetup/install/files/images/smilies/love.png new file mode 100644 index 0000000000..aa9dd52211 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/love.png differ diff --git a/wcfsetup/install/files/images/smilies/mellow.png b/wcfsetup/install/files/images/smilies/mellow.png new file mode 100644 index 0000000000..78c4c9fa42 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/mellow.png differ diff --git a/wcfsetup/install/files/images/smilies/pinch.png b/wcfsetup/install/files/images/smilies/pinch.png new file mode 100644 index 0000000000..171f28a5ae Binary files /dev/null and b/wcfsetup/install/files/images/smilies/pinch.png differ diff --git a/wcfsetup/install/files/images/smilies/question.png b/wcfsetup/install/files/images/smilies/question.png new file mode 100644 index 0000000000..95bd6cbaa7 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/question.png differ diff --git a/wcfsetup/install/files/images/smilies/rolleyes.png b/wcfsetup/install/files/images/smilies/rolleyes.png new file mode 100644 index 0000000000..2e68be7b94 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/rolleyes.png differ diff --git a/wcfsetup/install/files/images/smilies/sad.png b/wcfsetup/install/files/images/smilies/sad.png new file mode 100644 index 0000000000..17f9febdc2 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/sad.png differ diff --git a/wcfsetup/install/files/images/smilies/saint.png b/wcfsetup/install/files/images/smilies/saint.png new file mode 100644 index 0000000000..f5fc8cc919 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/saint.png differ diff --git a/wcfsetup/install/files/images/smilies/scared.png b/wcfsetup/install/files/images/smilies/scared.png new file mode 100644 index 0000000000..62a6caed85 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/scared.png differ diff --git a/wcfsetup/install/files/images/smilies/sick.png b/wcfsetup/install/files/images/smilies/sick.png new file mode 100644 index 0000000000..2ef69f5f2a Binary files /dev/null and b/wcfsetup/install/files/images/smilies/sick.png differ diff --git a/wcfsetup/install/files/images/smilies/sleeping.png b/wcfsetup/install/files/images/smilies/sleeping.png new file mode 100644 index 0000000000..59897d13f7 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/sleeping.png differ diff --git a/wcfsetup/install/files/images/smilies/smile.png b/wcfsetup/install/files/images/smilies/smile.png new file mode 100644 index 0000000000..34e0a9ccbc Binary files /dev/null and b/wcfsetup/install/files/images/smilies/smile.png differ diff --git a/wcfsetup/install/files/images/smilies/thumbdown.png b/wcfsetup/install/files/images/smilies/thumbdown.png new file mode 100644 index 0000000000..b46bc4a9bb Binary files /dev/null and b/wcfsetup/install/files/images/smilies/thumbdown.png differ diff --git a/wcfsetup/install/files/images/smilies/thumbsup.png b/wcfsetup/install/files/images/smilies/thumbsup.png new file mode 100644 index 0000000000..6f648625e0 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/thumbsup.png differ diff --git a/wcfsetup/install/files/images/smilies/thumbup.png b/wcfsetup/install/files/images/smilies/thumbup.png new file mode 100644 index 0000000000..1ac0eb1882 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/thumbup.png differ diff --git a/wcfsetup/install/files/images/smilies/tired.png b/wcfsetup/install/files/images/smilies/tired.png new file mode 100644 index 0000000000..cf463eccf0 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/tired.png differ diff --git a/wcfsetup/install/files/images/smilies/tongue.png b/wcfsetup/install/files/images/smilies/tongue.png new file mode 100644 index 0000000000..68f91ab0de Binary files /dev/null and b/wcfsetup/install/files/images/smilies/tongue.png differ diff --git a/wcfsetup/install/files/images/smilies/unsure.png b/wcfsetup/install/files/images/smilies/unsure.png new file mode 100644 index 0000000000..2abb2befc2 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/unsure.png differ diff --git a/wcfsetup/install/files/images/smilies/w00t.png b/wcfsetup/install/files/images/smilies/w00t.png new file mode 100644 index 0000000000..4a129caf36 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/w00t.png differ diff --git a/wcfsetup/install/files/images/smilies/wacko.png b/wcfsetup/install/files/images/smilies/wacko.png new file mode 100644 index 0000000000..a30bdf4893 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/wacko.png differ diff --git a/wcfsetup/install/files/images/smilies/whistling.png b/wcfsetup/install/files/images/smilies/whistling.png new file mode 100644 index 0000000000..f70f532cce Binary files /dev/null and b/wcfsetup/install/files/images/smilies/whistling.png differ diff --git a/wcfsetup/install/files/images/smilies/wink.png b/wcfsetup/install/files/images/smilies/wink.png new file mode 100644 index 0000000000..87ebba3242 Binary files /dev/null and b/wcfsetup/install/files/images/smilies/wink.png differ diff --git a/wcfsetup/install/files/lib/acp/form/BBCodeAddForm.class.php b/wcfsetup/install/files/lib/acp/form/BBCodeAddForm.class.php new file mode 100644 index 0000000000..fdee91d406 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/BBCodeAddForm.class.php @@ -0,0 +1,287 @@ + + * @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 + */ + 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 + )); + } +} diff --git a/wcfsetup/install/files/lib/acp/form/BBCodeEditForm.class.php b/wcfsetup/install/files/lib/acp/form/BBCodeEditForm.class.php new file mode 100644 index 0000000000..66c9a52129 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/BBCodeEditForm.class.php @@ -0,0 +1,158 @@ + + * @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 + */ + 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)) + )); + } +} diff --git a/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderAddForm.class.php b/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderAddForm.class.php new file mode 100644 index 0000000000..d2c6964e37 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderAddForm.class.php @@ -0,0 +1,126 @@ + + * @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 + )); + } +} diff --git a/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderEditForm.class.php b/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderEditForm.class.php new file mode 100644 index 0000000000..d718419753 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/BBCodeMediaProviderEditForm.class.php @@ -0,0 +1,101 @@ + + * @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' + )); + } +} diff --git a/wcfsetup/install/files/lib/acp/form/SmileyAddForm.class.php b/wcfsetup/install/files/lib/acp/form/SmileyAddForm.class.php new file mode 100644 index 0000000000..a3b07ff3d9 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/SmileyAddForm.class.php @@ -0,0 +1,205 @@ + + * @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 + } +} diff --git a/wcfsetup/install/files/lib/acp/form/SmileyCategoryAddForm.class.php b/wcfsetup/install/files/lib/acp/form/SmileyCategoryAddForm.class.php new file mode 100644 index 0000000000..3bb4948b80 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/SmileyCategoryAddForm.class.php @@ -0,0 +1,29 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/acp/form/SmileyCategoryEditForm.class.php b/wcfsetup/install/files/lib/acp/form/SmileyCategoryEditForm.class.php new file mode 100644 index 0000000000..fc4c70ff88 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/SmileyCategoryEditForm.class.php @@ -0,0 +1,29 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/acp/form/SmileyEditForm.class.php b/wcfsetup/install/files/lib/acp/form/SmileyEditForm.class.php new file mode 100644 index 0000000000..4ecaa9c805 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/form/SmileyEditForm.class.php @@ -0,0 +1,120 @@ + + * @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' + )); + } +} diff --git a/wcfsetup/install/files/lib/acp/page/BBCodeListPage.class.php b/wcfsetup/install/files/lib/acp/page/BBCodeListPage.class.php new file mode 100644 index 0000000000..cfe049b47f --- /dev/null +++ b/wcfsetup/install/files/lib/acp/page/BBCodeListPage.class.php @@ -0,0 +1,45 @@ + + * @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'); +} diff --git a/wcfsetup/install/files/lib/acp/page/BBCodeMediaProviderListPage.class.php b/wcfsetup/install/files/lib/acp/page/BBCodeMediaProviderListPage.class.php new file mode 100644 index 0000000000..3f927ac42e --- /dev/null +++ b/wcfsetup/install/files/lib/acp/page/BBCodeMediaProviderListPage.class.php @@ -0,0 +1,45 @@ + + * @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'); +} diff --git a/wcfsetup/install/files/lib/acp/page/SmileyCategoryListPage.class.php b/wcfsetup/install/files/lib/acp/page/SmileyCategoryListPage.class.php new file mode 100644 index 0000000000..b9a7f03583 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/page/SmileyCategoryListPage.class.php @@ -0,0 +1,29 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/acp/page/SmileyListPage.class.php b/wcfsetup/install/files/lib/acp/page/SmileyListPage.class.php new file mode 100644 index 0000000000..c33427e7f9 --- /dev/null +++ b/wcfsetup/install/files/lib/acp/page/SmileyListPage.class.php @@ -0,0 +1,113 @@ + + * @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 + */ + 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(); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/BBCode.class.php b/wcfsetup/install/files/lib/data/bbcode/BBCode.class.php new file mode 100644 index 0000000000..7dd1f906f7 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/BBCode.class.php @@ -0,0 +1,98 @@ + + * @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 + */ + 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 $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); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/BBCodeAction.class.php b/wcfsetup/install/files/lib/data/bbcode/BBCodeAction.class.php new file mode 100644 index 0000000000..2aeaa581f9 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/BBCodeAction.class.php @@ -0,0 +1,113 @@ + + * @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 + )); + } + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/BBCodeCache.class.php b/wcfsetup/install/files/lib/data/bbcode/BBCodeCache.class.php new file mode 100644 index 0000000000..987ccffefc --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/BBCodeCache.class.php @@ -0,0 +1,63 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage data.bbcode + * @category Community Framework + */ +class BBCodeCache extends SingletonFactory { + /** + * cached bbcodes + * @var array + */ + protected $cachedBBCodes = array(); + + /** + * @see wcf\system\SingletonFactory::init() + */ + protected function init() { + // get bbcode cache + $this->cachedBBCodes = BBCodeCacheBuilder::getInstance()->getData(); + } + + /** + * Returns all bbcodes. + * + * @return array + */ + 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 + */ + public function getBBCodeAttributes($tag) { + return $this->cachedBBCodes[$tag]->getAttributes(); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/BBCodeEditor.class.php b/wcfsetup/install/files/lib/data/bbcode/BBCodeEditor.class.php new file mode 100644 index 0000000000..fcb8951239 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/BBCodeEditor.class.php @@ -0,0 +1,29 @@ + + * @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(); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/BBCodeList.class.php b/wcfsetup/install/files/lib/data/bbcode/BBCodeList.class.php new file mode 100644 index 0000000000..153e0e05b9 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/BBCodeList.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttribute.class.php b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttribute.class.php new file mode 100644 index 0000000000..7a0e1ea557 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttribute.class.php @@ -0,0 +1,40 @@ + + * @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 + */ + 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(); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeAction.class.php b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeAction.class.php new file mode 100644 index 0000000000..9eac9628a2 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeAction.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeEditor.class.php b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeEditor.class.php new file mode 100644 index 0000000000..9d3dcd7614 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeEditor.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeList.class.php b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeList.class.php new file mode 100644 index 0000000000..0277aaa262 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/attribute/BBCodeAttributeList.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProvider.class.php b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProvider.class.php new file mode 100644 index 0000000000..58ccda0826 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProvider.class.php @@ -0,0 +1,110 @@ + + * @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; + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderAction.class.php b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderAction.class.php new file mode 100644 index 0000000000..9cdb76af5f --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderAction.class.php @@ -0,0 +1,33 @@ + + * @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'); +} diff --git a/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderEditor.class.php b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderEditor.class.php new file mode 100644 index 0000000000..dd4c239247 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderEditor.class.php @@ -0,0 +1,29 @@ + + * @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(); + } +} diff --git a/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderList.class.php b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderList.class.php new file mode 100644 index 0000000000..f794d4fa31 --- /dev/null +++ b/wcfsetup/install/files/lib/data/bbcode/media/provider/BBCodeMediaProviderList.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/smiley/Smiley.class.php b/wcfsetup/install/files/lib/data/smiley/Smiley.class.php new file mode 100644 index 0000000000..2c14db062b --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/Smiley.class.php @@ -0,0 +1,47 @@ + + * @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 + */ + public function getAliases() { + if (!$this->aliases) return array(); + + return explode("\n", StringUtil::unifyNewlines($this->aliases)); + } +} diff --git a/wcfsetup/install/files/lib/data/smiley/SmileyAction.class.php b/wcfsetup/install/files/lib/data/smiley/SmileyAction.class.php new file mode 100644 index 0000000000..5ea2dae8ca --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/SmileyAction.class.php @@ -0,0 +1,72 @@ + + * @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(); + } +} diff --git a/wcfsetup/install/files/lib/data/smiley/SmileyCache.class.php b/wcfsetup/install/files/lib/data/smiley/SmileyCache.class.php new file mode 100644 index 0000000000..269ab6e2c5 --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/SmileyCache.class.php @@ -0,0 +1,83 @@ + + * @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 + */ + 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 + */ + 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(); + } +} diff --git a/wcfsetup/install/files/lib/data/smiley/SmileyEditor.class.php b/wcfsetup/install/files/lib/data/smiley/SmileyEditor.class.php new file mode 100644 index 0000000000..f4618a2a4b --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/SmileyEditor.class.php @@ -0,0 +1,29 @@ + + * @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(); + } +} diff --git a/wcfsetup/install/files/lib/data/smiley/SmileyList.class.php b/wcfsetup/install/files/lib/data/smiley/SmileyList.class.php new file mode 100644 index 0000000000..3abf397e39 --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/SmileyList.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/data/smiley/category/SmileyCategory.class.php b/wcfsetup/install/files/lib/data/smiley/category/SmileyCategory.class.php new file mode 100644 index 0000000000..71321d7d97 --- /dev/null +++ b/wcfsetup/install/files/lib/data/smiley/category/SmileyCategory.class.php @@ -0,0 +1,127 @@ + + * @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 + */ + protected $indexToObject = null; + + /** + * list of assigned smilies + * @var array + */ + 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; + } + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/AbstractBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/AbstractBBCode.class.php new file mode 100644 index 0000000000..b1f9ecd335 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/AbstractBBCode.class.php @@ -0,0 +1,20 @@ + + * @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'; +} diff --git a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php new file mode 100644 index 0000000000..1277dcec2a --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php @@ -0,0 +1,42 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage system.bbcode + * @category Community Framework + */ +class BBCodeHandler extends SingletonFactory { + /** + * list of BBCodes displayed as buttons + * @var array + */ + 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 + */ + public function getButtonBBCodes() { + return $this->buttonBBCodes; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/BBCodeParser.class.php b/wcfsetup/install/files/lib/system/bbcode/BBCodeParser.class.php new file mode 100644 index 0000000000..f7a60aca85 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/BBCodeParser.class.php @@ -0,0 +1,534 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage system.bbcode + * @category Community Framework + */ +class BBCodeParser extends SingletonFactory { + /** + * list of bbcodes + * @var array + */ + 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 '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 $allowedBBCodes + * @return array + */ + 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; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/CodeBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/CodeBBCode.class.php new file mode 100644 index 0000000000..9aaab318ba --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/CodeBBCode.class.php @@ -0,0 +1,279 @@ + + * @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 + */ + 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, '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 $lines + */ + public static function fixMarkup(array $lines) { + static $spanRegex = null; + static $emptyTagRegex = null; + if ($spanRegex === null) { + $spanRegex = new Regex('(?:|)'); + $emptyTagRegex = new Regex(''); + } + + $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 === '') array_pop($openTags); + else { + array_push($openTags, $match); + } + } + + // close all tags + $line .= str_repeat('', count($openTags)); + + // remove empty tags to avoid cluttering the output + $line = $emptyTagRegex->replace($line, ''); + } + unset($line); + return $lines; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/IBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/IBBCode.class.php new file mode 100644 index 0000000000..b78bc9fbd7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/IBBCode.class.php @@ -0,0 +1,26 @@ + + * @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); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/ImageBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/ImageBBCode.class.php new file mode 100644 index 0000000000..5d2c3719bd --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/ImageBBCode.class.php @@ -0,0 +1,38 @@ + + * @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 ''; + } + else if ($parser->getOutputType() == 'text/simplified-html') { + $src = StringUtil::decodeHTML($src); + return StringUtil::getAnchorTag($src); + } + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php new file mode 100644 index 0000000000..f30bce7005 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/KeywordHighlighter.class.php @@ -0,0 +1,140 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage system.bbcode + * @category Community Framework + */ +class KeywordHighlighter extends SingletonFactory { + /** + * search keywords + * @var array + */ + protected $keywords = array(); + + /** + * search query parameters + * @var array + */ + 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('/(?<()~]+/', '', $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('+(?)+i', '\\1', $text); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/ListBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/ListBBCode.class.php new file mode 100644 index 0000000000..a7c21354de --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/ListBBCode.class.php @@ -0,0 +1,77 @@ + + * @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] == '
    ') { + 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">
  2. '.implode('
  3. ', $listElements).'
  4. '; + } + 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']; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/MediaBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/MediaBBCode.class.php new file mode 100644 index 0000000000..8a9d5cb603 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/MediaBBCode.class.php @@ -0,0 +1,40 @@ + + * @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; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/MessageParser.class.php b/wcfsetup/install/files/lib/system/bbcode/MessageParser.class.php new file mode 100644 index 0000000000..0a04658408 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/MessageParser.class.php @@ -0,0 +1,227 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage system.bbcode + * @category Community Framework + */ +class MessageParser extends BBCodeParser { + /** + * list of smilies + * @var array + */ + 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] = ''.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
    '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:', '$1:', '$1:'); + $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('~(?)~', $html, $text); + $text = preg_replace('~(?<=^|\s)'.preg_quote((!$enableHtml ? StringUtil::encodeHTML($code) : $code), '~').'(?=$|\s|'.(!$enableHtml ? '|
    ' : '').')~', $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; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/PreParser.class.php b/wcfsetup/install/files/lib/system/bbcode/PreParser.class.php new file mode 100644 index 0000000000..0b4b90fa05 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/PreParser.class.php @@ -0,0 +1,193 @@ + + * @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 + */ + 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 $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(' + (?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(' + (?()\[\]{}\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'); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php new file mode 100644 index 0000000000..0cbb717c48 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/QuoteBBCode.class.php @@ -0,0 +1,32 @@ + + * @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] : ''))); + } + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/SimpleMessageParser.class.php b/wcfsetup/install/files/lib/system/bbcode/SimpleMessageParser.class.php new file mode 100644 index 0000000000..1b8fed0cbf --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/SimpleMessageParser.class.php @@ -0,0 +1,170 @@ + + * @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 + */ + 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] = ''.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
    '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:', '$1:', '$1:'); + $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 = '~(?()\[\]{}\s]* + (?: + [!.,?;(){}]+ [^!.,?;"\'<>()\[\]{}\s]+ + )* + )? + ~ix'; + $emailPattern = '~(?\\0', $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('~(?)~', $html, $text); + $text = preg_replace('~(?<=^|\s)'.preg_quote(StringUtil::encodeHTML($code), '~').'(?=$|\s|
    )~', $html, $text); + } + + return $text; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/SpoilerBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/SpoilerBBCode.class.php new file mode 100644 index 0000000000..d50018a866 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/SpoilerBBCode.class.php @@ -0,0 +1,31 @@ + + * @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'); + } + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/TableBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/TableBBCode.class.php new file mode 100644 index 0000000000..956ac6a1e5 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/TableBBCode.class.php @@ -0,0 +1,90 @@ + + * @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|
    )*(\[tr\].*\[/tr\])(?:\s|
    )*', 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|
    )*', Regex::CASE_INSENSITIVE)->replace($parsedContent, ''); + // td + $parsedContent = StringUtil::replaceIgnoreCase('[td]', '', $parsedContent); + // /td + $parsedContent = Regex::compile('\[/td\](?:\s|
    )*', Regex::CASE_INSENSITIVE)->replace($parsedContent, ''); + // /tr + $parsedContent = Regex::compile('\[/tr\](?:\s|
    )*', Regex::CASE_INSENSITIVE)->replace($parsedContent, ''); + + return '
    '.$parsedContent.'
    '; + } + 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; + } + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/URLBBCode.class.php b/wcfsetup/install/files/lib/system/bbcode/URLBBCode.class.php new file mode 100644 index 0000000000..30dd613557 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/URLBBCode.class.php @@ -0,0 +1,33 @@ + + * @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); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/BashHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/BashHighlighter.class.php new file mode 100644 index 0000000000..32a888e5a4 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/BashHighlighter.class.php @@ -0,0 +1,94 @@ + + * @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( + '$?' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/BrainfuckHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/BrainfuckHighlighter.class.php new file mode 100644 index 0000000000..d98e5cf81f --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/BrainfuckHighlighter.class.php @@ -0,0 +1,28 @@ + + * @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('/[\\<\\>]+/', '\\0', $string); + $string = preg_replace('/[-\\+]+/', '\\0', $string); + $string = preg_replace('/[\\.,]+/', '\\0', $string); + $string = preg_replace('/[\\[\\]]+/', '\\0', $string); + + $string = str_replace(array('||span class="hlComments"||', '||/span||'), array('', ''), $string); + return $string; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/CHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/CHighlighter.class.php new file mode 100644 index 0000000000..1e9f8dbb34 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/CHighlighter.class.php @@ -0,0 +1,123 @@ + + * @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' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/CssHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/CssHighlighter.class.php new file mode 100644 index 0000000000..277d02dafe --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/CssHighlighter.class.php @@ -0,0 +1,667 @@ + + * @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 + */ + 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', '\\0', $string); + + // highlight colors (hexadecimal numbers) + $string = preg_replace('!(?<='.$this->separatorsRegEx.')(#([0-9a-f]{3}|[0-9a-f]{6}))(?='.$this->separatorsRegEx.')!i', '\\0', $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', '\\0', $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 + '&' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/DiffHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/DiffHighlighter.class.php new file mode 100644 index 0000000000..7a9d41b7a0 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/DiffHighlighter.class.php @@ -0,0 +1,66 @@ + + * @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 + */ + protected $add = array("+", ">"); + + /** + * keywords for an deleted line, the - is used in unified diff, the < in + * normal diffs + * @var array + */ + protected $delete = array("-", "<"); + + /** + * splitter in changes for normal diff + * @var array + */ + protected $splitter = array("---"); + + /** + * keywords for the line info, the @ is used in unified diffs, the numbers + * in normal diffs + * @var array + */ + 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] = ''.StringUtil::encodeHTML($val).''; + } + else if (in_array(StringUtil::substring($val, 0, 1), $this->add)) { + $lines[$key] = ''.StringUtil::encodeHTML($val).''; + } + else if (in_array(StringUtil::substring($val, 0, 1), $this->delete)) { + $lines[$key] = ''.StringUtil::encodeHTML($val).''; + } + else { + $lines[$key] = StringUtil::encodeHTML($val); + } + } + + $data = implode("\n", $lines); + return $data; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/Highlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/Highlighter.class.php new file mode 100644 index 0000000000..f0126722a5 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/Highlighter.class.php @@ -0,0 +1,319 @@ + + * @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 + */ + protected $commentEnd = array("*/"); + + /** + * comment start delimiter + * @var array + */ + protected $commentStart = array("/*"); + + /** + * escape sequence + * @var array + */ + protected $escapeSequence = array("\\"); + + /** + * categorized keywords + * @var array + */ + protected $keywords1 = array(); + + /** + * categorized keywords + * @var array + */ + protected $keywords2 = array(); + + /** + * categorized keywords + * @var array + */ + protected $keywords3 = array(); + + /** + * categorized keywords + * @var array + */ + protected $keywords4 = array(); + + /** + * categorized keywords + * @var array + */ + protected $keywords5 = array(); + + /** + * list of arithmetic operators + * @var array + */ + protected $operators = array(); + + /** + * list of quote marks + * @var array + */ + protected $quotes = array("'", '"'); + + /** + * list of separator sequences + * @var array + */ + protected $separators = array(); + + /** + * inline comment sequence + * @var array + */ + 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(''.StringUtil::encodeHTML($comment).'', '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(''.StringUtil::encodeHTML($matches[0]).'', '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', '\\0', $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), '\\0', $string); + } + if (!empty($this->keywords2)) { + $string = preg_replace($buildKeywordRegex($this->keywords2), '\\0', $string); + } + if (!empty($this->keywords3)) { + $string = preg_replace($buildKeywordRegex($this->keywords3), '\\0', $string); + } + if (!empty($this->keywords4)) { + $string = preg_replace($buildKeywordRegex($this->keywords4), '\\0', $string); + } + if (!empty($this->keywords5)) { + $string = preg_replace($buildKeywordRegex($this->keywords5), '\\0', $string); + } + + return $string; + } + + /** + * Highlights numbers. + */ + protected function highlightNumbers($string) { + $string = preg_replace('!(?<='.$this->separatorsRegEx.')(-?\d+)(?='.$this->separatorsRegEx.')!i', '\\0', $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'); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/HtmlHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/HtmlHighlighter.class.php new file mode 100644 index 0000000000..a890f361ae --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/HtmlHighlighter.class.php @@ -0,0 +1,66 @@ + + * @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)[^>]*>)(.*?)()', 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(''.$class::getInstance()->highlight($content).'', '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; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/JavaHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/JavaHighlighter.class.php new file mode 100644 index 0000000000..bb09fb28f7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/JavaHighlighter.class.php @@ -0,0 +1,104 @@ + + * @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' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/JsHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/JsHighlighter.class.php new file mode 100644 index 0000000000..ab759eb6a7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/JsHighlighter.class.php @@ -0,0 +1,132 @@ + + * @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" + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/PerlHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/PerlHighlighter.class.php new file mode 100644 index 0000000000..a1393e59c1 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/PerlHighlighter.class.php @@ -0,0 +1,129 @@ + + * @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' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/PhpHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/PhpHighlighter.class.php new file mode 100644 index 0000000000..1d0059ecc6 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/PhpHighlighter.class.php @@ -0,0 +1,68 @@ + + * @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[''] = ''; + foreach ($types as $type => $class) { + self::$colorToClass[''] = ''; + } + } + + /** + * @see wcf\system\bbcode\highlighter\Highlighter::highlight() + */ + public function highlight($code) { + // add starting php tag + $phpTagsAdded = false; + if (StringUtil::indexOf($code, ''; + } + + // do highlight + $highlightedCode = highlight_string($code, true); + + // clear code + $highlightedCode = str_replace('', '', $highlightedCode); + $highlightedCode = str_replace('', '', $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('
    ', "\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('(?)')->replace($highlightedCode, '"'); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/PlainHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/PlainHighlighter.class.php new file mode 100644 index 0000000000..9316fc3042 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/PlainHighlighter.class.php @@ -0,0 +1,22 @@ + + * @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); + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/PythonHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/PythonHighlighter.class.php new file mode 100644 index 0000000000..983422d669 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/PythonHighlighter.class.php @@ -0,0 +1,107 @@ + + * @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' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/SqlHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/SqlHighlighter.class.php new file mode 100644 index 0000000000..009d75fb6c --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/SqlHighlighter.class.php @@ -0,0 +1,434 @@ + + * @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(''.StringUtil::encodeHTML($comment).'', '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(''.StringUtil::encodeHTML($matches[0]).'', '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' + ); +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/TexHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/TexHighlighter.class.php new file mode 100644 index 0000000000..1e477e2fae --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/TexHighlighter.class.php @@ -0,0 +1,44 @@ + + * @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, '\\0'); + $string = Regex::compile('(\\\\(?:[a-z]+))(\\[[^\\]\\\\]+\\])?(\\{[^\\}]*\\})?', Regex::CASE_INSENSITIVE)->replace($string, '\\1\\2\\3'); + $string = str_replace('\\\\', '\\\\', $string); + + return $string; + } + + /** + * @see wcf\system\bbcode\highlighter\Highlighter::highlightNumbers() + */ + protected function highlightNumbers($string) { + // do not highlight numbers + return $string; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/TplHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/TplHighlighter.class.php new file mode 100644 index 0000000000..00ae85a8a2 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/TplHighlighter.class.php @@ -0,0 +1,27 @@ + + * @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, '\\0'); + + return $string; + } +} diff --git a/wcfsetup/install/files/lib/system/bbcode/highlighter/XmlHighlighter.class.php b/wcfsetup/install/files/lib/system/bbcode/highlighter/XmlHighlighter.class.php new file mode 100644 index 0000000000..86985af215 --- /dev/null +++ b/wcfsetup/install/files/lib/system/bbcode/highlighter/XmlHighlighter.class.php @@ -0,0 +1,95 @@ + + * @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::$separators + */ + protected $separators = array("<", ">"); + + /** + * @see wcf\system\bbcode\highlighter\Highlighter::$operators + */ + protected $operators = array(); + + const XML_ATTRIBUTE_NAME = '[a-z0-9](?:(?:(?replace($string, new Callback(function ($matches) { + // highlight attributes + $tag = Regex::compile(XmlHighlighter::XML_ATTRIBUTE_NAME.'(?:=[^\s/\?&]+)?(?=\s|&)', Regex::CASE_INSENSITIVE)->replace($matches[0], '\\0'); + + // highlight tag + return ''.$tag.''; + })); + + return $string; + } + + /** + * @see wcf\system\bbcode\highlighter\Highlighter::cacheQuotes() + */ + protected function cacheQuotes($string) { + // highlight CDATA-Tags as quotes + $string = Regex::compile('', Regex::DOT_ALL)->replace($string, new Callback(function (array $matches) { + return StringStack::pushToStringStack(''.StringUtil::encodeHTML($matches[0]).'', 'highlighterQuotes'); + })); + + $string = parent::cacheQuotes($string); + + return $string; + } + + /** + * @see wcf\system\bbcode\highlighter\Highlighter::highlightNumbers() + */ + protected function highlightNumbers($string) { + // do not highlight numbers + return $string; + } +} diff --git a/wcfsetup/install/files/lib/system/cache/builder/BBCodeCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/BBCodeCacheBuilder.class.php new file mode 100644 index 0000000000..b9053ac030 --- /dev/null +++ b/wcfsetup/install/files/lib/system/cache/builder/BBCodeCacheBuilder.class.php @@ -0,0 +1,52 @@ + + * @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; + } +} diff --git a/wcfsetup/install/files/lib/system/cache/builder/BBCodeMediaProviderCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/BBCodeMediaProviderCacheBuilder.class.php new file mode 100644 index 0000000000..e56d693330 --- /dev/null +++ b/wcfsetup/install/files/lib/system/cache/builder/BBCodeMediaProviderCacheBuilder.class.php @@ -0,0 +1,25 @@ + + * @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(); + } +} diff --git a/wcfsetup/install/files/lib/system/cache/builder/SmileyCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/SmileyCacheBuilder.class.php new file mode 100644 index 0000000000..64e351ef79 --- /dev/null +++ b/wcfsetup/install/files/lib/system/cache/builder/SmileyCacheBuilder.class.php @@ -0,0 +1,37 @@ + + * @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; + } +} diff --git a/wcfsetup/install/files/lib/system/category/SmileyCategoryType.class.php b/wcfsetup/install/files/lib/system/category/SmileyCategoryType.class.php new file mode 100644 index 0000000000..252ebc8454 --- /dev/null +++ b/wcfsetup/install/files/lib/system/category/SmileyCategoryType.class.php @@ -0,0 +1,59 @@ + + * @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'); + } +} diff --git a/wcfsetup/install/files/lib/system/option/user/group/BBCodeSelectUserGroupOptionType.class.php b/wcfsetup/install/files/lib/system/option/user/group/BBCodeSelectUserGroupOptionType.class.php new file mode 100644 index 0000000000..6573f192de --- /dev/null +++ b/wcfsetup/install/files/lib/system/option/user/group/BBCodeSelectUserGroupOptionType.class.php @@ -0,0 +1,153 @@ + + * @package com.woltlab.wcf.bbcode + * @subpackage system.option + * @category Community Framework + */ +class BBCodeSelectUserGroupOptionType extends AbstractOptionType implements IUserGroupOptionType { + /** + * available BBCodes + * @var array + */ + 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 + */ + 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'); + } + } + } +} diff --git a/wcfsetup/install/files/lib/system/package/plugin/BBCodePackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/BBCodePackageInstallationPlugin.class.php new file mode 100644 index 0000000000..d3b73db25a --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/plugin/BBCodePackageInstallationPlugin.class.php @@ -0,0 +1,168 @@ + + * @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 + */ + 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), + )); + } + } + } + } +} diff --git a/wcfsetup/install/files/lib/system/package/plugin/SmileyPackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/SmileyPackageInstallationPlugin.class.php new file mode 100644 index 0000000000..792171cbdd --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/plugin/SmileyPackageInstallationPlugin.class.php @@ -0,0 +1,77 @@ + + * @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 + ); + } +} diff --git a/wcfsetup/install/files/style/bbcode.less b/wcfsetup/install/files/style/bbcode.less new file mode 100644 index 0000000000..632bb748d6 --- /dev/null +++ b/wcfsetup/install/files/style/bbcode.less @@ -0,0 +1,284 @@ +/* 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; +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index becf44e3c3..9e0bc0b60f 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -38,6 +38,55 @@ + + + + + + + + + + + + + + + + + + + bbcodeTag}]“ wirklich löschen?]]> + + + + + + + title}“ wirklich löschen?]]> + + + {$variable} werden durch die entsprechend benannte Untergruppe des regulären Ausdrucks ersetzt.
    + Beispiel:
    + {$ID} wird durch die beispielhaften Medium-IDs der Erklärung oberhalb ersetzt.{/literal}]]>
    + + + getPath('wcf')}acp/dereferrer.php?url=http%3A%2F%2Fde.wikipedia.org%2Fwiki%2FRegul%25C3%25A4rer_Ausdruck">regulären Ausdruck an.
    + Mehrere Ausdrücke können zeilenweise angegeben werden.
    + Beispiele für die Medium-ID Erkennungen: +
      +
    • (?<ID>[0-9]+) erkennt eine Medium-ID aus Zahlen.
    • +
    • (?<ID>[0-9a-zA-Z]+) erkennt eine alphanumerische Medium-ID.
    • +
    ]]>
    + + + + + + + +
    + @@ -193,6 +242,10 @@ + + + + @@ -325,6 +378,16 @@ + + + + + + + + + + @@ -478,6 +541,7 @@ + @@ -633,6 +697,22 @@ + + + + smileyTitle|language}“ wirklich löschen?]]> + + + + + + + + + + + + @@ -886,6 +966,30 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getAllowedExtensions() + + + + + + + + + + + + + + + + + + + + + + + + @@ -1227,6 +1331,7 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getAllowedExtensions() user->banReason}: {@$__wcf->user->banReason|htmlspecialchars|nl2br}{else}.{/if}]]> - + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 68f5119803..d3fd5ca184 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -38,6 +38,54 @@ + + + + + + + + + + + + + + + + + + + bbcodeTag}]”?]]> + + + + + + + title}”?]]> + + + +Example:
    +{$ID} will be replaced with the medium ID as shown above.{/literal}]]>
    + + + getPath()}acp/dereferrer.php?url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FRegular_expression">regular expression. One expression per line.
    +Examples for medium ID detection: +
      +
    • (?<ID>[0-9]+) detects a numeric ID.
    • +
    • (?<ID>[0-9a-zA-Z]+) detects an alphanumeric ID
    • +
    ]]>
    + + + + + + + +
    + @@ -193,6 +241,10 @@ + + + + @@ -325,6 +377,16 @@ + + + + + + + + + + @@ -478,6 +540,7 @@ + @@ -633,6 +696,22 @@ + + + + smileyTitle|language}”?]]> + + + + + + + + + + + + @@ -885,6 +964,29 @@ Allowed extensions: {', '|implode:$attachmentHandler->getAllowedExtensions()}]]> + + + + + + + + + + + + + + + + + + + + + + + @@ -1226,6 +1328,7 @@ Allowed extensions: {', '|implode:$attachmentHandler->getAllowedExtensions()}]]> user->banReason}: {@$__wcf->user->banReason|htmlspecialchars|nl2br}{else}.{/if}]]> - + + diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 6f6fa7dd9e..61c8ea7c08 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -156,6 +156,43 @@ CREATE TABLE wcf1_attachment ( 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, @@ -572,6 +609,19 @@ CREATE TABLE wcf1_sitemap ( 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, @@ -814,6 +864,10 @@ ALTER TABLE wcf1_application ADD FOREIGN KEY (packageID) REFERENCES wcf1_package 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; @@ -886,6 +940,9 @@ ALTER TABLE wcf1_session ADD FOREIGN KEY (spiderID) REFERENCES wcf1_spider (spid 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; @@ -1040,3 +1097,26 @@ INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('useFluidLa 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=)(?[a-zA-Z0-9_-]+)(?#t=(?:\\d+|(?:\\d+h(?:\\d+m)?(?:\\d+s)?)|(?:\\d+m(?:\\d+s)?)|(?:\\d+s))$)?', ''); + -- Vimeo + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Vimeo', 'http://vimeo\\.com/(?\\d+)', ''); + -- MyVideo + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('MyVideo', 'http://(?:www\\.)?myvideo\\.de/watch/(?\\d+)', ''); + -- Clipfish + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Clipfish', 'http://(?:www\\.)?clipfish\\.de/video/(?\\d+)/', '
    '); + -- Veoh + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Veoh', 'http://(?:www\\.)?veoh\\.com/watch/v(?\\d+[a-zA-Z0-9]+)', ''); + -- DailyMotion + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('DailyMotion', 'https?://(?:www\\.)?dailymotion\\.com/video/(?[a-zA-Z0-9]+)', ''); + -- YouKu + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('YouKu', 'https?://(?:.+?\\.)?youku\\.com/v_show/id_(?[a-zA-Z0-9_-]+)(?:\\.html)?', ''); +-- Misc + -- github gist + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('github gist', 'https://gist.github.com/(?[^/]+/[0-9a-zA-Z]+)', ''); + -- soundcloud + INSERT INTO wcf1_bbcode_media_provider (title, regex, html) VALUES ('Soundcloud', 'https?://soundcloud.com/(?[a-zA-Z0-9_-]+)/(?[a-zA-Z0-9_-]+)', ''); +