Add image selector for boxes
authorMatthias Schmidt <gravatronics@live.com>
Thu, 24 Dec 2015 11:51:34 +0000 (12:51 +0100)
committerMatthias Schmidt <gravatronics@live.com>
Thu, 24 Dec 2015 11:51:34 +0000 (12:51 +0100)
37 files changed:
com.woltlab.wcf/templates/mediaJavaScript.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/mediaListItems.tpl
com.woltlab.wcf/templates/mediaManager.tpl
com.woltlab.wcf/templates/wysiwyg.tpl
wcfsetup/install/files/acp/templates/boxAdd.tpl
wcfsetup/install/files/acp/templates/mediaAdd.tpl
wcfsetup/install/files/acp/templates/mediaEditor.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/mediaJavaScript.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/mediaListItems.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/mediaManager.tpl [new file with mode: 0644]
wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabMedia.js
wcfsetup/install/files/js/WoltLab/WCF/Ajax.js
wcfsetup/install/files/js/WoltLab/WCF/Media/Manager.js [deleted file]
wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Select.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/Media/Search.js
wcfsetup/install/files/js/WoltLab/WCF/Media/Upload.js
wcfsetup/install/files/js/WoltLab/WCF/Ui/Alignment.js
wcfsetup/install/files/js/WoltLab/WCF/Upload.js
wcfsetup/install/files/lib/acp/form/BoxAddForm.class.php
wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php
wcfsetup/install/files/lib/acp/form/MediaEditForm.class.php
wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php
wcfsetup/install/files/lib/data/box/Box.class.php
wcfsetup/install/files/lib/data/box/BoxAction.class.php
wcfsetup/install/files/lib/data/media/Media.class.php
wcfsetup/install/files/lib/data/media/MediaAction.class.php
wcfsetup/install/files/lib/data/media/MediaEditor.class.php
wcfsetup/install/files/lib/data/media/MediaList.class.php
wcfsetup/install/files/lib/data/media/ViewableMedia.class.php
wcfsetup/install/files/lib/data/media/ViewableMediaList.class.php
wcfsetup/install/files/lib/data/menu/Menu.class.php
wcfsetup/install/files/lib/system/upload/MediaUploadFileValidationStrategy.class.php [new file with mode: 0644]
wcfsetup/install/files/style/icon/icon.scss
wcfsetup/install/files/style/ui/media.scss
wcfsetup/setup/db/install.sql

diff --git a/com.woltlab.wcf/templates/mediaJavaScript.tpl b/com.woltlab.wcf/templates/mediaJavaScript.tpl
new file mode 100644 (file)
index 0000000..960d9f0
--- /dev/null
@@ -0,0 +1,22 @@
+{if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
+       require(['Language', 'Permission'], function(Language, Permission) {
+               Language.addObject({
+                       'wcf.global.button.insert': '{lang}wcf.global.button.insert{/lang}',
+                       
+                       'wcf.media.insert': '{lang}wcf.media.insert{/lang}',
+                       'wcf.media.insert.imageSize': '{lang}wcf.media.insert.imageSize{/lang}',
+                       'wcf.media.insert.imageSize.small': '{lang __literal=true}wcf.media.insert.imageSize.small{/lang}',
+                       'wcf.media.insert.imageSize.medium': '{lang __literal=true}wcf.media.insert.imageSize.medium{/lang}',
+                       'wcf.media.insert.imageSize.large': '{lang __literal=true}wcf.media.insert.imageSize.large{/lang}',
+                       'wcf.media.insert.imageSize.original': '{lang __literal=true}wcf.media.insert.imageSize.original{/lang}',
+                       'wcf.media.manager': '{lang}wcf.media.manager{/lang}',
+                       'wcf.media.edit': '{lang}wcf.media.edit{/lang}',
+                       'wcf.media.imageDimensions.value': '{lang __literal=true}wcf.media.imageDimensions.value{/lang}',
+                       'wcf.media.button.insert': '{lang}wcf.media.button.insert{/lang}',
+                       'wcf.media.search.filetype': '{lang}wcf.media.search.filetype{/lang}',
+                       'wcf.media.search.noResults': '{lang}wcf.media.search.noResults{/lang}'
+               });
+               
+               Permission.add('admin.content.cms.canManageMedia', {if $__wcf->session->getPermission('admin.content.cms.canManageMedia')}true{else}false{/if});
+       });
+{/if}
index 1621bd5ccba65d3ee0e922d089982086fb1f37f4..45007323dd8ac07a36a01b009c94f0f4aed18a67 100644 (file)
@@ -1,7 +1,7 @@
 {foreach from=$mediaList item=media}
        <li class="jsClipboardObject" data-object-id="{@$media->mediaID}">
                <div class="mediaThumbnail">
-                       {@$media->getElementTag(96)}
+                       {@$media->getElementTag(144)}
                </div>
                
                <div class="mediaInformation">
                                                <a><span class="icon icon16 fa-times jsTooltip jsMediaDeleteIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.global.button.delete{/lang}"></span></a>
                                        </li>
                                {/if}
-                               <li>
-                                       <a><span class="icon icon16 fa-plus jsTooltip jsMediaInsertIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.media.button.insert{/lang}"></span></a>
-                               </li>
+                               {if $mode == 'editor'}
+                                       <li>
+                                               <a><span class="icon icon16 fa-plus jsTooltip jsMediaInsertIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.media.button.insert{/lang}"></span></a>
+                                       </li>
+                               {elseif $mode == 'select'}
+                                       <li>
+                                               <a><span class="icon icon16 fa-check jsTooltip jsMediaSelectIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.media.button.select{/lang}"></span></a>
+                                       </li>
+                               {/if}
                        </ul>
                </nav>
        </li>
index 6f53d90ff4f51b25be5327c33f6f400bf01799bc..2c33b79d9d11fe58efa858684cf1b9ab0026c57b 100644 (file)
@@ -1,16 +1,19 @@
-<div class="inputAddon dropdown" id="mediaManagerSearch">
-       <span class="button dropdownToggle inputPrefix">
-               <span class="active">{lang}wcf.media.search.filetype{/lang}</span>
-       </span>
-       <ul class="dropdownMenu">
-               <li data-file-type="image"><span>{lang}wcf.media.search.filetype.image{/lang}</span></li>
-               <li data-file-type="text"><span>{lang}wcf.media.search.filetype.text{/lang}</span></li>
-               <li data-file-type="pdf"><span>{lang}wcf.media.search.filetype.pdf{/lang}</span></li>
-               <li data-file-type="other"><span>{lang}wcf.media.search.filetype.other{/lang}</span></li>
-               {event name='filetype'}
-               <li class="dropdownDivider"></li>
-               <li data-file-type="all"><span>{lang}wcf.media.search.filetype.all{/lang}</span></li>
-       </ul>
+{if !$showFileTypeFilter|isset}{assign var=showFileTypeFilter value=true}{/if}
+<div class="inputAddon{if $showFileTypeFilter} dropdown{/if}" id="mediaManagerSearch">
+       {if $showFileTypeFilter}
+               <span class="button dropdownToggle inputPrefix">
+                       <span class="active">{lang}wcf.media.search.filetype{/lang}</span>
+               </span>
+               <ul class="dropdownMenu">
+                       <li data-file-type="image"><span>{lang}wcf.media.search.filetype.image{/lang}</span></li>
+                       <li data-file-type="text"><span>{lang}wcf.media.search.filetype.text{/lang}</span></li>
+                       <li data-file-type="pdf"><span>{lang}wcf.media.search.filetype.pdf{/lang}</span></li>
+                       <li data-file-type="other"><span>{lang}wcf.media.search.filetype.other{/lang}</span></li>
+                       {event name='filetype'}
+                       <li class="dropdownDivider"></li>
+                       <li data-file-type="all"><span>{lang}wcf.media.search.filetype.all{/lang}</span></li>
+               </ul>
+       {/if}
        <input type="text" id="mediaManagerSearchField" placeholder="{lang}wcf.media.search.placeholder{/lang}" />
        <span class="inputSuffix">
                <span id="mediaManagerSearchCancelButton" class="icon icon16 fa-times pointer jsTooltip" title="{lang}wcf.media.search.cancel{/lang}"></span>
index 976333e51199822c398ced459c7436109383af08..2e08d1b1c63e0c6de737ca681cdf7d5505f08055 100644 (file)
        
        WCF.System.Dependency.Manager.setup(callbackIdentifier, function() {
                // TODO: Should the media stuff be here?
-               {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
-                       require(['Language', 'Permission'], function(Language, Permission) {
-                               Language.addObject({
-                                       'wcf.global.button.insert': '{lang}wcf.global.button.insert{/lang}',
-                                       
-                                       'wcf.media.insert': '{lang}wcf.media.insert{/lang}',
-                                       'wcf.media.insert.imageSize': '{lang}wcf.media.insert.imageSize{/lang}',
-                                       'wcf.media.insert.imageSize.small': '{lang __literal=true}wcf.media.insert.imageSize.small{/lang}',
-                                       'wcf.media.insert.imageSize.medium': '{lang __literal=true}wcf.media.insert.imageSize.medium{/lang}',
-                                       'wcf.media.insert.imageSize.large': '{lang __literal=true}wcf.media.insert.imageSize.large{/lang}',
-                                       'wcf.media.insert.imageSize.original': '{lang __literal=true}wcf.media.insert.imageSize.original{/lang}',
-                                       'wcf.media.manager': '{lang}wcf.media.manager{/lang}',
-                                       'wcf.media.edit': '{lang}wcf.media.edit{/lang}',
-                                       'wcf.media.imageDimensions.value': '{lang __literal=true}wcf.media.imageDimensions.value{/lang}',
-                                       'wcf.media.button.insert': '{lang}wcf.media.button.insert{/lang}',
-                                       'wcf.media.search.filetype': '{lang}wcf.media.search.filetype{/lang}',
-                                       'wcf.media.search.noResults': '{lang}wcf.media.search.noResults{/lang}'
-                               });
-                               
-                               Permission.add('admin.content.cms.canManageMedia', {if $__wcf->session->getPermission('admin.content.cms.canManageMedia')}true{else}false{/if});
-                       });
-               {/if}
+               {include file='mediaJavaScript'}
                
                var element = elById(elementId);
                var autosave = elData(element, 'autosave') || '';
index fa8d5d8636051a0984a507bdcde2c27f2c7a317f..8d1bbbb239803e73af11701bc0001f2fa82aaa78 100644 (file)
@@ -1,5 +1,20 @@
 {include file='header' pageTitle='wcf.acp.box.'|concat:$action}
 
+{if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
+       <script data-relocate="true">
+               {include file='mediaJavaScript'}
+               
+               require(['WoltLab/WCF/Media/Manager/Select'], function(MediaManagerSelect) {
+                       new MediaManagerSelect({
+                               dialogTitle: '{lang}wcf.acp.box.image.dialog.title{/lang}',
+                               fileTypeFilters: {
+                                       isImage: 1
+                               }
+                       });
+               });
+       </script>
+{/if}
+
 <header class="boxHeadline">
        <h1>{if $action == 'add'}{if $isMultilingual}{lang}wcf.acp.box.addMultilingual{/lang}{else}{lang}wcf.acp.box.add{/lang}{/if}{else}{lang}wcf.acp.box.edit{/lang}{/if}</h1>
 </header>
@@ -14,7 +29,7 @@
        <nav>
                <ul>
                        <li><a href="{link controller='BoxList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.cms.box.list{/lang}</span></a></li>
-                               
+                       
                        {event name='contentNavigationButtons'}
                </ul>
        </nav>
                                <input type="number" id="showOrder" name="showOrder" value="{@$showOrder}" class="tiny" min="0" />
                        </dd>
                </dl>
-       
+               
                <dl{if $errorField == 'cssClassName'} class="formError"{/if}>
                        <dt><label for="cssClassName">{lang}wcf.acp.box.cssClassName{/lang}</label></dt>
                        <dd>
                                <label><input type="checkbox" id="showHeader" name="showHeader" value="1" {if $showHeader}checked="checked" {/if}/> {lang}wcf.acp.box.showHeader{/lang}</label>
                        </dd>
                </dl>
-                       
+               
                <dl>
                        <dt></dt>
                        <dd>
                
                {event name='dataFields'}
        </section>
-               
+       
        {if !$isMultilingual}
                <fieldset>
                        <legend>content</legend>
-               
+                       
+                       {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
+                               <dl{if $errorField == 'image'} class="formError"{/if}>
+                                       <dt><label for="image">{lang}wcf.acp.box.image{/lang}</label></dt>
+                                       <dd>
+                                               <div id="imageDisplay">
+                                                       {if $images[0]|isset}
+                                                               {@$images[0]->getThumbnailTag('small')}
+                                                       {/if}
+                                               </div>
+                                               <p class="button jsMediaSelectButton" data-store="imageID0" data-display="imageDisplay">{lang}wcf.acp.box.image.button.chooseImage{/lang}</p>
+                                               <input type="hidden" name="imageID[0]" id="imageID0"{if $imageID[0]|isset} value="{@$imageID[0]}"{/if} />
+                                               {if $errorField == 'image'}
+                                                       <small class="innerError">{lang}wcf.acp.box.image.error.{@$errorType}{/lang}</small>
+                                               {/if}
+                                       </dd>
+                               </dl>
+                       {elseif $action == 'edit' && $images[0]|isset}
+                               <dl>
+                                       <dt>{lang}wcf.acp.box.image{/lang}</dt>
+                                       <dd>
+                                               <div id="imageDisplay">{@$images[0]->getThumbnailTag('small')}</div>
+                                       </dd>
+                               </dl>
+                       {/if}
+                       
                        <dl{if $errorField == 'title'} class="formError"{/if}>
                                <dt><label for="title">{lang}wcf.acp.box.title{/lang}</label></dt>
                                <dd>
-                                       <input type="text" id="title" name="title[0]" value="{if !$title[0]|empty}{$title[0]}{/if}" class="long" />
+                                       <input type="text" id="title0" name="title[0]" value="{if !$title[0]|empty}{$title[0]}{/if}" class="long" />
                                        {if $errorField == 'title'}
                                                <small class="innerError">
                                                        {if $errorType == 'empty'}
                        {foreach from=$availableLanguages item=availableLanguage}
                                <div id="language{@$availableLanguage->languageID}" class="tabMenuContent">
                                        <div>
-                                               <dl{if $errorField == 'title'} class="formError"{/if}>
+                                               {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
+                                                       <dl{if $errorField == 'image'|concat:$availableLanguage->languageID} class="formError"{/if}>
+                                                               <dt><label for="image{@$availableLanguage->languageID}">{lang}wcf.acp.box.image{/lang}</label></dt>
+                                                               <dd>
+                                                                       <div id="imageDisplay{@$availableLanguage->languageID}"></div>
+                                                                       <p class="button jsMediaSelectButton" data-store="imageID{@$availableLanguage->languageID}" data-display="imageDisplay{@$availableLanguage->languageID}">{lang}wcf.acp.box.image.button.chooseImage{/lang}</p>
+                                                                       <input type="hidden" name="imageID[{@$availableLanguage->languageID}]" id="imageID{@$availableLanguage->languageID}"{if $imageID[$availableLanguage->languageID]|isset} value="{@$imageID[$availableLanguage->languageID]}"{/if} />
+                                                                       {if $errorField == 'image'|concat:$availableLanguage->languageID}
+                                                                               <small class="innerError">{lang}wcf.acp.box.image.error.{@$errorType}{/lang}</small>
+                                                                       {/if}
+                                                               </dd>
+                                                       </dl>
+                                               {elseif $action == 'edit' && $images[$availableLanguage->languageID]|isset}
+                                                       <dl>
+                                                               <dt>{lang}wcf.acp.box.image{/lang}</dt>
+                                                               <dd>
+                                                                       <div id="imageDisplay">{@$images[$availableLanguage->languageID]->getThumbnailTag('small')}</div>
+                                                               </dd>
+                                                       </dl>
+                                               {/if}
+                                               
+                                               <dl{if $errorField == 'title'|concat:$availableLanguage->languageID} class="formError"{/if}>
                                                        <dt><label for="title{@$availableLanguage->languageID}">{lang}wcf.acp.box.title{/lang}</label></dt>
                                                        <dd>
                                                                <input type="text" id="title{@$availableLanguage->languageID}" name="title[{@$availableLanguage->languageID}]" value="{if !$title[$availableLanguage->languageID]|empty}{$title[$availableLanguage->languageID]}{/if}" class="long" />
-                                                               {if $errorField == 'title'}
+                                                               {if $errorField == 'title'|concat:$availableLanguage->languageID}
                                                                        <small class="innerError">
                                                                                {if $errorType == 'empty'}
                                                                                        {lang}wcf.global.form.error.empty{/lang}
                                                        </dd>
                                                </dl>
                                                
-                                               <dl{if $errorField == 'content'} class="formError"{/if}>
+                                               <dl{if $errorField == 'content'|concat:$availableLanguage->languageID} class="formError"{/if}>
                                                        <dt><label for="content{@$availableLanguage->languageID}">{lang}wcf.acp.box.content{/lang}</label></dt>
                                                        <dd>
                                                                <textarea name="content[{@$availableLanguage->languageID}]" id="content{@$availableLanguage->languageID}">{if !$content[$availableLanguage->languageID]|empty}{$content[$availableLanguage->languageID]}{/if}</textarea>
-                                                               {if $errorField == 'content'}
+                                                               {if $errorField == 'content'|concat:$availableLanguage->languageID}
                                                                        <small class="innerError">
                                                                                {if $errorType == 'empty'}
                                                                                        {lang}wcf.global.form.error.empty{/lang}
                        {/foreach}
                </div>
        {/if}
-               
+       
        {event name='sections'}
        
        <div class="formSubmit">
index 99571ec0a315512a254644e7233b66dd9f8b7b02..fc28a3c9d289f37688e0791ac14f0b44f1c6c314 100644 (file)
                        
                        <dl>
                                <dt>{lang}wcf.media.file{/lang}</dt>
-                               <dd>{$media->filename} {*TODO: better output *}</dd>
+                               <dd>
+                                       {if $media->isImage}
+                                               {@$media->getThumbnailTag('small')}
+                                       {else}
+                                               {$media->filename}
+                                       {/if}
+                               </dd>
                        </dl>
                        
                        <dl>
diff --git a/wcfsetup/install/files/acp/templates/mediaEditor.tpl b/wcfsetup/install/files/acp/templates/mediaEditor.tpl
new file mode 100644 (file)
index 0000000..65172e5
--- /dev/null
@@ -0,0 +1,65 @@
+<div id="mediaThumbnail" class="framed"></div>
+
+<div class="box48">
+       <span id="mediaFileIcon" class="icon icon48 fa-file-o"></span>
+       
+       <dl class="plain dataList">
+               <dt>{lang}wcf.media.filename{/lang}</dt>
+               <dd id="mediaFilename"></dd>
+               
+               <dt>{lang}wcf.media.filesize{/lang}</dt>
+               <dd id="mediaFilesize"></dd>
+               
+               <dt>{lang}wcf.media.imageDimensions{/lang}</dt>
+               <dd id="mediaImageDimensions"></dd>
+               
+               <dt>{lang}wcf.media.uploader{/lang}</dt>
+               <dd id="mediaUploader"></dd>
+       </dl>
+</div>
+
+<fieldset class="marginTop">
+       <legend>{lang}wcf.global.form.data{/lang}</legend>
+       
+       <dl>
+               <dt></dt>
+               <dd>
+                       <label>
+                               <input type="checkbox" id="isMultilingual" name="isMultilingual" value="1" />
+                               <span>{lang}wcf.media.isMultilingual{/lang}</span>
+                       </label>
+               </dd>
+       </dl>
+       
+       {include file='languageChooser' label='wcf.media.languageID'}
+       
+       <dl>
+               <dt>{lang}wcf.global.title{/lang}</dt>
+               <dd>
+                       <input type="text" id="title" name="title" class="long" />
+               </dd>
+       </dl>
+       {include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=true}
+       
+       <dl>
+               <dt>{lang}wcf.media.caption{/lang}</dt>
+               <dd>
+                       <textarea id="caption" name="caption" cols="40" rows="3"></textarea>
+               </dd>
+       </dl>
+       {include file='multipleLanguageInputJavascript' elementIdentifier='caption' forceSelection=true}
+       
+       <dl>
+               <dt>{lang}wcf.media.altText{/lang}</dt>
+               <dd>
+                       <input type="text" id="altText" name="altText" class="long" />
+               </dd>
+       </dl>
+       {include file='multipleLanguageInputJavascript' elementIdentifier='altText' forceSelection=true}
+       
+       {event name='dataFields'}
+</fieldset>
+
+<div class="formSubmit">
+       <button data-type="submit" class="buttonPrimary">{lang}wcf.global.button.submit{/lang}</button>
+</div>
diff --git a/wcfsetup/install/files/acp/templates/mediaJavaScript.tpl b/wcfsetup/install/files/acp/templates/mediaJavaScript.tpl
new file mode 100644 (file)
index 0000000..960d9f0
--- /dev/null
@@ -0,0 +1,22 @@
+{if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
+       require(['Language', 'Permission'], function(Language, Permission) {
+               Language.addObject({
+                       'wcf.global.button.insert': '{lang}wcf.global.button.insert{/lang}',
+                       
+                       'wcf.media.insert': '{lang}wcf.media.insert{/lang}',
+                       'wcf.media.insert.imageSize': '{lang}wcf.media.insert.imageSize{/lang}',
+                       'wcf.media.insert.imageSize.small': '{lang __literal=true}wcf.media.insert.imageSize.small{/lang}',
+                       'wcf.media.insert.imageSize.medium': '{lang __literal=true}wcf.media.insert.imageSize.medium{/lang}',
+                       'wcf.media.insert.imageSize.large': '{lang __literal=true}wcf.media.insert.imageSize.large{/lang}',
+                       'wcf.media.insert.imageSize.original': '{lang __literal=true}wcf.media.insert.imageSize.original{/lang}',
+                       'wcf.media.manager': '{lang}wcf.media.manager{/lang}',
+                       'wcf.media.edit': '{lang}wcf.media.edit{/lang}',
+                       'wcf.media.imageDimensions.value': '{lang __literal=true}wcf.media.imageDimensions.value{/lang}',
+                       'wcf.media.button.insert': '{lang}wcf.media.button.insert{/lang}',
+                       'wcf.media.search.filetype': '{lang}wcf.media.search.filetype{/lang}',
+                       'wcf.media.search.noResults': '{lang}wcf.media.search.noResults{/lang}'
+               });
+               
+               Permission.add('admin.content.cms.canManageMedia', {if $__wcf->session->getPermission('admin.content.cms.canManageMedia')}true{else}false{/if});
+       });
+{/if}
diff --git a/wcfsetup/install/files/acp/templates/mediaListItems.tpl b/wcfsetup/install/files/acp/templates/mediaListItems.tpl
new file mode 100644 (file)
index 0000000..746b730
--- /dev/null
@@ -0,0 +1,36 @@
+{foreach from=$mediaList item=media}
+       <li class="jsClipboardObject" data-object-id="{@$media->mediaID}">
+               <div class="mediaThumbnail">
+                       {@$media->getElementTag(144)}
+               </div>
+
+               <div class="mediaInformation">
+                       <p class="mediaTitle">{if $media->title}{$media->title}{else}{$media->filename}{/if}</p>
+               </div>
+
+               <nav class="buttonGroupNavigation">
+                       <ul class="smallButtons buttonGroup">
+                               <li>
+                                       <input type="checkbox" class="jsClipboardItem jsMediaCheckbox" data-object-id="{@$media->mediaID}" />
+                               </li>
+                               {if $__wcf->session->getPermission('admin.content.cms.canManageMedia')}
+                                       <li>
+                                               <a><span class="icon icon16 fa-pencil jsTooltip jsMediaEditIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.global.button.edit{/lang}"></span></a>
+                                       </li>
+                                       <li>
+                                               <a><span class="icon icon16 fa-times jsTooltip jsMediaDeleteIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.global.button.delete{/lang}"></span></a>
+                                       </li>
+                               {/if}
+                               {if $mode == 'editor'}
+                                       <li>
+                                               <a><span class="icon icon16 fa-plus jsTooltip jsMediaInsertIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.media.button.insert{/lang}"></span></a>
+                                       </li>
+                               {elseif $mode == 'select'}
+                                       <li>
+                                               <a><span class="icon icon16 fa-check jsTooltip jsMediaSelectIcon" data-object-id="{@$media->mediaID}" title="{lang}wcf.media.button.select{/lang}"></span></a>
+                                       </li>
+                               {/if}
+                       </ul>
+               </nav>
+       </li>
+{/foreach}
diff --git a/wcfsetup/install/files/acp/templates/mediaManager.tpl b/wcfsetup/install/files/acp/templates/mediaManager.tpl
new file mode 100644 (file)
index 0000000..c2cf8c2
--- /dev/null
@@ -0,0 +1,36 @@
+{if !$showFileTypeFilter|isset}{assign var=showFileTypeFilter value=true}{/if}
+<div class="inputAddon{if $showFileTypeFilter} dropdown{/if}" id="mediaManagerSearch">
+       {if $showFileTypeFilter}
+               <span class="button dropdownToggle inputPrefix">
+                       <span class="active">{lang}wcf.media.search.filetype{/lang}</span>
+               </span>
+               <ul class="dropdownMenu">
+                       <li data-file-type="image"><span>{lang}wcf.media.search.filetype.image{/lang}</span></li>
+                       <li data-file-type="text"><span>{lang}wcf.media.search.filetype.text{/lang}</span></li>
+                       <li data-file-type="pdf"><span>{lang}wcf.media.search.filetype.pdf{/lang}</span></li>
+                       <li data-file-type="other"><span>{lang}wcf.media.search.filetype.other{/lang}</span></li>
+                       {event name='filetype'}
+                       <li class="dropdownDivider"></li>
+                       <li data-file-type="all"><span>{lang}wcf.media.search.filetype.all{/lang}</span></li>
+               </ul>
+       {/if}
+       <input type="text" id="mediaManagerSearchField" placeholder="{lang}wcf.media.search.placeholder{/lang}" />
+       <span class="inputSuffix">
+               <span id="mediaManagerSearchCancelButton" class="icon icon16 fa-times pointer jsTooltip" title="{lang}wcf.media.search.cancel{/lang}"></span>
+       </span>
+</div>
+
+{if $__wcf->session->getPermission('admin.content.cms.canManageMedia')}
+       <div id="mediaManagerMediaUploadButton" class="marginTop"></div>
+{/if}
+
+<div class="jsClipboardContainer marginTop" data-type="com.woltlab.wcf.media">
+       <input type="checkbox" class="jsClipboardMarkAll" style="display: none;" />
+       <ul id="mediaManagerMediaList">
+               {include file='mediaListItems'}
+       </ul>
+
+       <div class="contentNavigation">
+               <nav class="jsClipboardEditor" data-types="[ 'com.woltlab.wcf.media' ]"></nav>
+       </div>
+</div>
index 5c645401e7a403235651f11ca7298c5110ceb4ca..7e6f84b4a27db0750df42301ae91952c187a73f0 100644 (file)
@@ -4,10 +4,10 @@ $.Redactor.prototype.WoltLabMedia = function() {
        return {
                init: function() {
                        var button = this.button.add('woltlabMedia', 'Media');
-                       $(button).attr('id', 'mediaManagerButton');
+                       $(button).addClass('jsMediaEditorButton');
                        
-                       require(['WoltLab/WCF/Media/Manager'], function(MediaManager) {
-                               new MediaManager();
+                       require(['WoltLab/WCF/Media/Manager/Editor'], function(MediaManagerEditor) {
+                               new MediaManagerEditor();
                        });
                },
        };
index 1d32756690069bb184e98b466d4fe6b66c09809b..be699f5561cf1675b638f453ff64380cf2270571 100644 (file)
@@ -39,7 +39,7 @@ define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectM
                                options.pinData = true;
                                options.callbackObject = callbackObject;
                                
-                               if (!options.url) options.url = 'index.php/AJAXProxy/?t=' + SECURITY_TOKEN;
+                               if (!options.url) options.url = 'index.php/AJAXProxy/?t=' + SECURITY_TOKEN + SID_ARG_2ND;
                                
                                request = new AjaxRequest(options);
                                
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager.js
deleted file mode 100644 (file)
index 8df686f..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-/**
- * Provides the media manager dialog.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2015 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLab/WCF/Controller/Media/Manager
- */
-define(
-       [
-               'Core',                     'Dictionary',               'Dom/ChangeListener',      'Dom/Traverse',
-               'Dom/Util',                 'EventHandler',             'Language',                'List',
-               'Permission',               'Ui/Dialog',                'Ui/Notification',         'WoltLab/WCF/Controller/Clipboard',
-               'WoltLab/WCF/Media/Editor', 'WoltLab/WCF/Media/Upload', 'WoltLab/WCF/Media/Search'
-               
-
-       ],
-       function(
-               Core,                        Dictionary,                 DomChangeListener,         DomTraverse,
-               DomUtil,                     EventHandler,               Language,                  List,
-               Permission,                  UiDialog,                   UiNotification,            Clipboard,
-               MediaEditor,                 MediaUpload,                MediaSearch
-       )
-{
-       "use strict";
-       
-       /**
-        * @constructor
-        */
-       function MediaManager() {
-               this._media = new Dictionary();
-               this._mediaData = new Dictionary();
-               this._mediaCache = null;
-               this._mediaManagerMediaList = null;
-               this._search = null;
-               
-               if (Permission.get('admin.content.cms.canManageMedia')) {
-                       this._mediaEditor = new MediaEditor(this);
-               }
-               
-               elById('mediaManagerButton').addEventListener('click', this._click.bind(this));
-               
-               DomChangeListener.add('WoltLab/WCF/Controller/Media/Manager', this._addButtonEventListeners.bind(this));
-       };
-       MediaManager.prototype = {
-               /**
-                * Adds click event listeners to media buttons.
-                */
-               _addButtonEventListeners: function() {
-                       if (!this._mediaManagerMediaList) return;
-                       
-                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
-                       for (var i = 0, length = listItems.length; i < length; i++) {
-                               var listItem = listItems[i];
-                               
-                               if (Permission.get('admin.content.cms.canManageMedia')) {
-                                       var editIcon = elByClass('jsMediaEditIcon', listItem)[0];
-                                       if (editIcon) {
-                                               editIcon.classList.remove('jsMediaEditIcon');
-                                               editIcon.addEventListener('click', this._editMedia.bind(this));
-                                       }
-                               }
-                               
-                               var insertIcon = elByClass('jsMediaInsertIcon', listItem)[0];
-                               if (insertIcon) {
-                                       insertIcon.classList.remove('jsMediaInsertIcon');
-                                       insertIcon.addEventListener('click', this._openInsertDialog.bind(this));
-                               }
-                       }
-               },
-               
-               /**
-                * Handles clicks on the media manager button.
-                * 
-                * @param       {object}        event   event object
-                */
-               _click: function(event) {
-                       event.preventDefault();
-                       
-                       UiDialog.open(this);
-               },
-               
-               /**
-                * Reacts to executed clipboard actions.
-                * 
-                * @param       {object<string, *>}     actionData      data of the executed clipboard action
-                */
-               _clipboardAction: function(actionData) {
-                       // only consider events if the action has been executed
-                       if (actionData.responseData === null) {
-                               return;
-                       }
-                       
-                       switch (actionData.data.actionName) {
-                               case 'com.woltlab.wcf.media.delete':
-                                       var mediaIds = actionData.responseData.objectIDs;
-                                       for (var i = 0, length = mediaIds.length; i < length; i++) {
-                                               this.removeMedia(~~mediaIds[i], true);
-                                       }
-                                       
-                                       UiNotification.show();
-                                       
-                                       break;
-                               case 'com.woltlab.wcf.media.insert':
-                                       // TODO
-                                       break;
-                       }
-               },
-               
-               /**
-                * Returns all data to setup the media manager dialog.
-                * 
-                * @return      {object}        dialog setup data
-                */
-               _dialogSetup: function() {
-                       return {
-                               id: 'mediaManager',
-                               options: {
-                                       title: Language.get('wcf.media.manager')
-                               },
-                               source: {
-                                       after: this._initDialog.bind(this),
-                                       data: {
-                                               actionName: 'getManagementDialog',
-                                               className: 'wcf\\data\\media\\MediaAction'
-                                       }
-                               }
-                       };
-               },
-               
-               /**
-                * Opens the media editor for a media file.
-                * 
-                * @param       {Event}         event           event object for clicks on edit icons
-                */
-               _editMedia: function(event) {
-                       if (!Permission.get('admin.content.cms.canManageMedia')) {
-                               throw new Error("You are not allowed to edit media files.");
-                       }
-                       
-                       UiDialog.close('mediaManager');
-                       
-                       this._mediaEditor.edit(this._mediaData.get(~~elData(event.currentTarget, 'object-id')));
-               },
-               
-               /**
-                * Re-opens the manager dialog after closing the editor dialog.
-                */
-               _editorClose: function() {
-                       UiDialog.open(this);
-               },
-               
-               /**
-                * Re-opens the manager dialog and updates the media data after
-                * successfully editing a media file.
-                * 
-                * @param       {object}        media           updated media file data
-                */
-               _editorSuccess: function(media) {
-                       UiDialog.open(this);
-                       
-                       this._mediaData.set(~~media.mediaID, media);
-                       
-                       var listItem = this._media.get(~~media.mediaID);
-                       var p = elByClass('mediaTitle', listItem)[0];
-                       if (media.isMultilingual) {
-                               p.textContent = media.title[LANGUAGE_ID] || media.filename;
-                       }
-                       else {
-                               p.textContent = media.title[media.languageID] || media.filename;
-                       }
-               },
-               
-               /**
-                * Initializes the dialog when first loaded.
-                * 
-                * @param       {string}        content         dialog content
-                * @param       {object}        data            AJAX request's response data
-                */
-               _initDialog: function(content, data) {
-                       // store media data locally
-                       var media = data.returnValues.media || { };
-                       for (var mediaId in media) {
-                               if (media.hasOwnProperty(mediaId)) {
-                                       this._mediaData.set(~~mediaId, media[mediaId]);
-                               }
-                       }
-                       
-                       this._mediaManagerMediaList = elById('mediaManagerMediaList');
-                       
-                       // store list items locally
-                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
-                       for (var i = 0, length = listItems.length; i < length; i++) {
-                               var listItem = listItems[i];
-                               
-                               this._media.set(~~elData(listItem, 'object-id'), listItem);
-                       }
-                       
-                       if (Permission.get('admin.content.cms.canManageMedia')) {
-                               new MediaUpload('mediaManagerMediaUploadButton', 'mediaManagerMediaList', {
-                                       mediaManager: this
-                               });
-                               
-                               Clipboard.setup({
-                                       hasMarkedItems: data.returnValues.hasMarkedItems ? true : false,
-                                       pageClassName: '*'
-                               });
-                               
-                               EventHandler.add('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.media', this._clipboardAction.bind(this));
-                       }
-                       
-                       this._search = new MediaSearch(this);
-                       
-                       if (!listItems.length) {
-                               this._search.hideSearch();
-                               
-                               if (true) {
-                                       elById('mediaManagerMediaUploadButton').classList.remove('marginTop');
-                               }
-                       }
-               },
-               
-               _insertMedia: function() {
-                       // TODO
-               },
-               
-               _openInsertDialog: function(event) {
-                       var media = this._mediaData.get(~~elData(event.currentTarget, 'object-id'));
-                       
-                       // check if media file is image and has at least small thumbnail
-                       // to show insertion options
-                       if (media.isImage && media.smallThumbnailType) {
-                               UiDialog.close(this);
-                               var dialogId = 'mediaInsert' + media.mediaID;
-                               if (UiDialog.getDialog(dialogId)) {
-                                       UiDialog.openStatic(dialogId);
-                               }
-                               else {
-                                       var dialog = elCreate('div');
-                                       
-                                       var fieldset = elCreate('fieldset');
-                                       dialog.appendChild(fieldset);
-                                       
-                                       var dl = elCreate('dl');
-                                       fieldset.appendChild(dl);
-                                       
-                                       var dt = elCreate('dt');
-                                       dt.textContent = Language.get('wcf.media.insert.imageSize');
-                                       dl.appendChild(dt);
-                                       
-                                       var dd = elCreate('dd');
-                                       dl.appendChild(dd);
-                                       
-                                       var select = elCreate('select');
-                                       dd.appendChild(select);
-                                       
-                                       var sizes = ['small', 'medium', 'large'];
-                                       var size, option;
-                                       for (var i = 0, length = sizes.length; i < length; i++) {
-                                               size = sizes[i];
-                                               
-                                               if (media[size + 'ThumbnailType']) {
-                                                       option = elCreate('option');
-                                                       elAttr(option, 'value', size);
-                                                       option.textContent = Language.get('wcf.media.insert.imageSize.' + size, {
-                                                               height: media[size + 'ThumbnailHeight'],
-                                                               width: media[size + 'ThumbnailWidth']
-                                                       });
-                                                       select.appendChild(option);
-                                               }
-                                       }
-                                       
-                                       option = elCreate('option');
-                                       elAttr(option, 'value', 'original');
-                                       option.textContent = Language.get('wcf.media.insert.imageSize.original', {
-                                               height: media.height,
-                                               width: media.width
-                                       });
-                                       select.appendChild(option);
-                                       
-                                       var formSubmit = elCreate('div');
-                                       formSubmit.className = 'formSubmit';
-                                       dialog.appendChild(formSubmit);
-                                       
-                                       var submitButton = elCreate('button');
-                                       submitButton.className = 'buttonPrimary';
-                                       submitButton.textContent = Language.get('wcf.global.button.insert');
-                                       elData(submitButton, 'object-id', media.mediaID);
-                                       submitButton.addEventListener('click', this._insertMedia.bind(this));
-                                       formSubmit.appendChild(submitButton);
-                                       
-                                       UiDialog.open({
-                                               _dialogSetup: (function() {
-                                                       return {
-                                                               id: dialogId,
-                                                               options: {
-                                                                       onClose: this._editorClose.bind(this),
-                                                                       title: Language.get('wcf.media.insert')
-                                                               },
-                                                               source: dialog.outerHTML
-                                                       }
-                                               }).bind(this)
-                                       });
-                               }
-                       }
-                       else {
-                               // insert media
-                               // TODO
-                       }
-               },
-               
-               _setMedia: function(media, listItems) {
-                       if (Core.isPlainObject(media)) {
-                               this._media = Dictionary.fromObject(media);
-                       }
-                       else {
-                               this._media = media;
-                       }
-                       
-                       var info = DomTraverse.nextByClass(this._mediaManagerMediaList, 'info');
-                       
-                       if (this._media.size) {
-                               if (info) {
-                                       elHide(info);
-                               }
-                       }
-                       else {
-                               if (info === null) {
-                                       info = elCreate('p');
-                                       info.className = 'info';
-                                       info.textContent = Language.get('wcf.media.search.noResults');
-                               }
-                               
-                               elShow(info);
-                               DomUtil.insertAfter(info, this._mediaManagerMediaList);
-                       }
-                       
-                       var mediaListItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
-                       for (var i = 0, length = mediaListItems.length; i < length; i++) {
-                               var listItem = mediaListItems[i];
-                               
-                               if (!this._media.has(elData(listItem, 'object-id'))) {
-                                       elHide(listItem);
-                               }
-                               else {
-                                       elShow(listItem);
-                               }
-                       }
-                       
-                       DomChangeListener.trigger();
-                       
-                       Clipboard.reload();
-               },
-               
-               /**
-                * Adds a media file to the manager.
-                * 
-                * @param       {object}        media           data of the media file
-                * @param       {Element}       listItem        list item representing the file
-                */
-               addMedia: function(media, listItem) {
-                       if (!media.languageID) media.isMultilingual = 1;
-                       
-                       this._mediaData.set(~~media.mediaID, media);
-                       this._media.set(~~media.mediaID, listItem);
-                       
-                       if (this._media.size === 1) {
-                               this._search.showSearch();
-                               
-                               if (true) {
-                                       elById('mediaManagerMediaUploadButton').classList.add('marginTop');
-                               }
-                       }
-               },
-
-               /**
-                * Removes a media file.
-                *
-                * @param       {int}                   mediaId         id of the removed media file
-                * @param       {boolean|undefined}     checkCache      media file will also be removed from the local cache if true
-                */
-               removeMedia: function(mediaId, checkCache) {
-                       if (this._media.has(mediaId)) {
-                               // remove list item
-                               elRemove(this._media.get(mediaId));
-
-                               this._media.delete(mediaId);
-                               this._mediaData.delete(mediaId);
-                       }
-
-                       if (checkCache && this._mediaCache && this._mediaCache.has(mediaId)) {
-                               this._mediaCache.delete(mediaId);
-                       }
-               },
-               
-               /**
-                * Changes the displayed media to the previously displayed media.
-                */
-               resetMedia: function() {
-                       if (this._mediaCache !== null) {
-                               this._setMedia(this._mediaCache);
-                               
-                               this._mediaCache = null;
-                               
-                               this._search.resetSearch();
-                       }
-               },
-               
-               /**
-                * Sets the media files currently displayed.
-                * 
-                * @param       {object}        media           media data
-                * @param       {string}        template        
-                */
-               setMedia: function(media, template) {
-                       if (!this._mediaCache) {
-                               this._mediaCache = this._media;
-                       }
-                       
-                       var hasMedia = false;
-                       for (var mediaId in media) {
-                               if (media.hasOwnProperty(mediaId)) {
-                                       hasMedia = true;
-                               }
-                       }
-                       
-                       var newListItems = [];
-                       if (hasMedia) {
-                               var ul = elCreate('ul');
-                               ul.innerHTML = template;
-                               
-                               var listItems = DomTraverse.childrenByTag(ul, 'LI');
-                               for (var i = 0, length = listItems.length; i < length; i++) {
-                                       var listItem = listItems[i];
-                                       if (!this._mediaData.has(~~elData(listItem, 'object-id'))) {
-                                               this._mediaData.set(elData(listItem, 'object-id'), listItem);
-                                               
-                                               this._mediaManagerMediaList.appendChild(listItem);
-                                       }
-                               }
-                       }
-                       
-                       this._setMedia(media);
-               }
-       };
-       
-       return MediaManager;
-});
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Base.js
new file mode 100644 (file)
index 0000000..0847321
--- /dev/null
@@ -0,0 +1,435 @@
+/**
+ * Provides the media manager dialog.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Media/Manager/Base
+ */
+define(
+       [
+               'Core',                     'Dictionary',               'Dom/ChangeListener',      'Dom/Traverse',
+               'Dom/Util',                 'EventHandler',             'Language',                'List',
+               'Permission',               'Ui/Dialog',                'Ui/Notification',         'WoltLab/WCF/Controller/Clipboard',
+               'WoltLab/WCF/Media/Editor', 'WoltLab/WCF/Media/Upload', 'WoltLab/WCF/Media/Search'
+       ],
+       function(
+               Core,                        Dictionary,                 DomChangeListener,         DomTraverse,
+               DomUtil,                     EventHandler,               Language,                  List,
+               Permission,                  UiDialog,                   UiNotification,            Clipboard,
+               MediaEditor,                 MediaUpload,                MediaSearch
+       )
+{
+       "use strict";
+       
+       /**
+        * @constructor
+        */
+       function MediaManagerBase(options) {
+               this._options = Core.extend({
+                       dialogTitle: Language.get('wcf.media.manager'),
+                       fileTypeFilters: {}
+               }, options);
+               
+               this._media = new Dictionary();
+               this._mediaData = new Dictionary();
+               this._mediaCache = null;
+               this._mediaManagerMediaList = null;
+               this._search = null;
+               
+               if (Permission.get('admin.content.cms.canManageMedia')) {
+                       this._mediaEditor = new MediaEditor(this);
+               }
+               
+               DomChangeListener.add('WoltLab/WCF/Media/Manager', this._addButtonEventListeners.bind(this));
+       };
+       MediaManagerBase.prototype = {
+               /**
+                * Adds click event listeners to media buttons.
+                */
+               _addButtonEventListeners: function() {
+                       if (!this._mediaManagerMediaList) return;
+                       
+                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+                       for (var i = 0, length = listItems.length; i < length; i++) {
+                               var listItem = listItems[i];
+                               
+                               if (Permission.get('admin.content.cms.canManageMedia')) {
+                                       var editIcon = elByClass('jsMediaEditIcon', listItem)[0];
+                                       if (editIcon) {
+                                               editIcon.classList.remove('jsMediaEditIcon');
+                                               editIcon.addEventListener('click', this._editMedia.bind(this));
+                                       }
+                               }
+                       }
+               },
+               
+               /**
+                * Handles clicks on the media manager button.
+                * 
+                * @param       {object}        event   event object
+                */
+               _click: function(event) {
+                       event.preventDefault();
+                       
+                       UiDialog.open(this);
+               },
+               
+               /**
+                * Reacts to executed clipboard actions.
+                * 
+                * @param       {object<string, *>}     actionData      data of the executed clipboard action
+                */
+               _clipboardAction: function(actionData) {
+                       // only consider events if the action has been executed
+                       if (actionData.responseData === null) {
+                               return;
+                       }
+                       
+                       switch (actionData.data.actionName) {
+                               case 'com.woltlab.wcf.media.delete':
+                                       var mediaIds = actionData.responseData.objectIDs;
+                                       for (var i = 0, length = mediaIds.length; i < length; i++) {
+                                               this.removeMedia(~~mediaIds[i], true);
+                                       }
+                                       
+                                       UiNotification.show();
+                                       break;
+                       }
+               },
+               
+               /**
+                * Returns all data to setup the media manager dialog.
+                * 
+                * @return      {object}        dialog setup data
+                */
+               _dialogSetup: function() {
+                       return {
+                               id: 'mediaManager',
+                               options: {
+                                       title: this._options.dialogTitle
+                               },
+                               source: {
+                                       after: this._initDialog.bind(this),
+                                       data: {
+                                               actionName: 'getManagementDialog',
+                                               className: 'wcf\\data\\media\\MediaAction',
+                                               parameters: {
+                                                       mode: this.getMode(),
+                                                       fileTypeFilters: this._options.fileTypeFilters
+                                               }
+                                       }
+                               }
+                       };
+               },
+               
+               /**
+                * Opens the media editor for a media file.
+                * 
+                * @param       {Event}         event           event object for clicks on edit icons
+                */
+               _editMedia: function(event) {
+                       if (!Permission.get('admin.content.cms.canManageMedia')) {
+                               throw new Error("You are not allowed to edit media files.");
+                       }
+                       
+                       UiDialog.close('mediaManager');
+                       
+                       this._mediaEditor.edit(this._mediaData.get(~~elData(event.currentTarget, 'object-id')));
+               },
+               
+               /**
+                * Re-opens the manager dialog after closing the editor dialog.
+                */
+               _editorClose: function() {
+                       UiDialog.open(this);
+               },
+               
+               /**
+                * Re-opens the manager dialog and updates the media data after
+                * successfully editing a media file.
+                * 
+                * @param       {object}        media           updated media file data
+                */
+               _editorSuccess: function(media) {
+                       UiDialog.open(this);
+                       
+                       this._mediaData.set(~~media.mediaID, media);
+                       
+                       var listItem = this._media.get(~~media.mediaID);
+                       var p = elByClass('mediaTitle', listItem)[0];
+                       if (media.isMultilingual) {
+                               p.textContent = media.title[LANGUAGE_ID] || media.filename;
+                       }
+                       else {
+                               p.textContent = media.title[media.languageID] || media.filename;
+                       }
+               },
+               
+               /**
+                * Initializes the dialog when first loaded.
+                * 
+                * @param       {string}        content         dialog content
+                * @param       {object}        data            AJAX request's response data
+                */
+               _initDialog: function(content, data) {
+                       // store media data locally
+                       var media = data.returnValues.media || { };
+                       for (var mediaId in media) {
+                               if (media.hasOwnProperty(mediaId)) {
+                                       this._mediaData.set(~~mediaId, media[mediaId]);
+                               }
+                       }
+                       
+                       this._mediaManagerMediaList = elById('mediaManagerMediaList');
+                       
+                       // store list items locally
+                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+                       for (var i = 0, length = listItems.length; i < length; i++) {
+                               var listItem = listItems[i];
+                               
+                               this._media.set(~~elData(listItem, 'object-id'), listItem);
+                       }
+                       
+                       if (Permission.get('admin.content.cms.canManageMedia')) {
+                               new MediaUpload('mediaManagerMediaUploadButton', 'mediaManagerMediaList', {
+                                       mediaManager: this
+                               });
+                               
+                               Clipboard.setup({
+                                       hasMarkedItems: data.returnValues.hasMarkedItems ? true : false,
+                                       pageClassName: '*'
+                               });
+                               
+                               EventHandler.add('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.media', this._clipboardAction.bind(this));
+                       }
+                       
+                       this._search = new MediaSearch(this);
+                       
+                       if (!listItems.length) {
+                               this._search.hideSearch();
+                               
+                               if (true) {
+                                       elById('mediaManagerMediaUploadButton').classList.remove('marginTop');
+                               }
+                       }
+               },
+               
+               /**
+                * Sets the displayed media (after a search).
+                * 
+                * @param       {Dictionary}    media           media to be set as active
+                */
+               _setMedia: function(media) {
+                       if (Core.isPlainObject(media)) {
+                               this._media = Dictionary.fromObject(media);
+                       }
+                       else {
+                               this._media = media;
+                       }
+                       
+                       var info = DomTraverse.nextByClass(this._mediaManagerMediaList, 'info');
+                       
+                       if (this._media.size) {
+                               if (info) {
+                                       elHide(info);
+                               }
+                       }
+                       else {
+                               if (info === null) {
+                                       info = elCreate('p');
+                                       info.className = 'info';
+                                       info.textContent = Language.get('wcf.media.search.noResults');
+                               }
+                               
+                               elShow(info);
+                               DomUtil.insertAfter(info, this._mediaManagerMediaList);
+                       }
+                       
+                       var mediaListItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+                       for (var i = 0, length = mediaListItems.length; i < length; i++) {
+                               var listItem = mediaListItems[i];
+                               
+                               if (!this._media.has(elData(listItem, 'object-id'))) {
+                                       elHide(listItem);
+                               }
+                               else {
+                                       elShow(listItem);
+                               }
+                       }
+                       
+                       DomChangeListener.trigger();
+                       
+                       Clipboard.reload();
+               },
+               
+               /**
+                * Adds a media file to the manager.
+                * 
+                * @param       {object}        media           data of the media file
+                * @param       {Element}       listItem        list item representing the file
+                */
+               addMedia: function(media, listItem) {
+                       if (!media.languageID) media.isMultilingual = 1;
+                       
+                       this._mediaData.set(~~media.mediaID, media);
+                       this._media.set(~~media.mediaID, listItem);
+                       
+                       if (this._media.size === 1) {
+                               this._search.showSearch();
+                               
+                               if (true) {
+                                       elById('mediaManagerMediaUploadButton').classList.add('marginTop');
+                               }
+                       }
+               },
+               
+               /**
+                * Returns the mode of the media manager.
+                *
+                * @return      {string}
+                */
+               getMode: function() {
+                       return '';
+               },
+               
+               /**
+                * Returns the media manager option with the given name.
+                * 
+                * @param       {string}        name            option name
+                * @return      {mixed}         option value or null
+                */
+               getOption: function(name) {
+                       if (this._options[name]) {
+                               return this._options[name];
+                       }
+                       
+                       return null;
+               },
+               
+               /**
+                * Removes a media file.
+                *
+                * @param       {int}                   mediaId         id of the removed media file
+                * @param       {boolean|undefined}     checkCache      media file will also be removed from the local cache if true
+                */
+               removeMedia: function(mediaId, checkCache) {
+                       if (this._media.has(mediaId)) {
+                               // remove list item
+                               elRemove(this._media.get(mediaId));
+                               
+                               this._media.delete(mediaId);
+                               this._mediaData.delete(mediaId);
+                       }
+                       
+                       if (checkCache && this._mediaCache && this._mediaCache.has(mediaId)) {
+                               this._mediaCache.delete(mediaId);
+                       }
+               },
+               
+               /**
+                * Changes the displayed media to the previously displayed media.
+                */
+               resetMedia: function() {
+                       if (this._mediaCache !== null) {
+                               this._setMedia(this._mediaCache);
+                               
+                               this._mediaCache = null;
+                               
+                               this._search.resetSearch();
+                       }
+               },
+               
+               /**
+                * Sets the media files currently displayed.
+                * 
+                * @param       {object}        media           media data
+                * @param       {string}        template        
+                */
+               setMedia: function(media, template) {
+                       if (!this._mediaCache) {
+                               this._mediaCache = this._media;
+                       }
+                       
+                       var hasMedia = false;
+                       for (var mediaId in media) {
+                               if (media.hasOwnProperty(mediaId)) {
+                                       hasMedia = true;
+                               }
+                       }
+                       
+                       var newListItems = [];
+                       if (hasMedia) {
+                               var ul = elCreate('ul');
+                               ul.innerHTML = template;
+                               
+                               var listItems = DomTraverse.childrenByTag(ul, 'LI');
+                               for (var i = 0, length = listItems.length; i < length; i++) {
+                                       var listItem = listItems[i];
+                                       if (!this._mediaData.has(~~elData(listItem, 'object-id'))) {
+                                               this._mediaData.set(elData(listItem, 'object-id'), listItem);
+                                               
+                                               this._mediaManagerMediaList.appendChild(listItem);
+                                       }
+                               }
+                       }
+                       
+                       this._setMedia(media);
+               },
+               
+               /**
+                * Sets up a new media element.
+                * 
+                * @param       {object}        media           data of the media file
+                * @param       {HTMLElement}   mediaElement    element representing the media file
+                */
+               setupMediaElement: function(media, mediaElement) {
+                       var mediaInformation = DomTraverse.childByClass(mediaElement, 'mediaInformation');
+                       
+                       var buttonGroupNavigation = elCreate('nav');
+                       buttonGroupNavigation.className = 'buttonGroupNavigation';
+                       mediaInformation.parentNode.appendChild(buttonGroupNavigation);
+                       
+                       var smallButtons = elCreate('ul');
+                       smallButtons.className = 'smallButtons buttonGroup';
+                       buttonGroupNavigation.appendChild(smallButtons);
+                       
+                       var listItem = elCreate('li');
+                       smallButtons.appendChild(listItem);
+                       
+                       var checkbox = elCreate('input');
+                       checkbox.className = 'jsClipboardItem jsMediaCheckbox';
+                       elAttr(checkbox, 'type', 'checkbox');
+                       elData(checkbox, 'object-id', media.mediaID);
+                       listItem.appendChild(checkbox);
+                       
+                       if (Permission.get('admin.content.cms.canManageMedia')) {
+                               listItem = elCreate('li');
+                               smallButtons.appendChild(listItem);
+                               
+                               var a = elCreate('a');
+                               listItem.appendChild(a);
+                               
+                               var icon = elCreate('span');
+                               icon.className = 'icon icon16 fa-pencil jsTooltip jsMediaEditIcon';
+                               elData(icon, 'object-id', media.mediaID);
+                               elAttr(icon, 'title', Language.get('wcf.global.button.edit'));
+                               a.appendChild(icon);
+                               
+                               listItem = elCreate('li');
+                               smallButtons.appendChild(listItem);
+                               
+                               a = elCreate('a');
+                               listItem.appendChild(a);
+                               
+                               icon = elCreate('span');
+                               icon.className = 'icon icon16 fa-times jsTooltip jsMediaDeleteIcon';
+                               elData(icon, 'object-id', media.mediaID);
+                               elAttr(icon, 'title', Language.get('wcf.global.button.delete'));
+                               a.appendChild(icon);
+                       }
+               }
+       };
+       
+       return MediaManagerBase;
+});
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Editor.js
new file mode 100644 (file)
index 0000000..d6491a3
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * Provides the media manager dialog for selecting media for input elements.
+ *
+ * @author     Matthias Schmidt
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Media/Manager/Editor
+ */
+define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Manager/Base'], function(Core, DomTraverse, Language, UiDialog, MediaManagerBase) {
+       "use strict";
+       
+       /**
+        * @constructor
+        */
+       function MediaManagerEditor(options) {
+               MediaManagerBase.call(this, options);
+               
+               this._activeButton = null;
+               this._buttons = elByClass(this._options.buttonClass || 'jsMediaEditorButton');
+               for (var i = 0, length = this._buttons.length; i < length; i++) {
+                       this._buttons[i].addEventListener('click', this._click.bind(this));
+               }
+               
+               this._activeButton = null;
+       };
+       Core.inherit(MediaManagerEditor, MediaManagerBase, {
+               /**
+                * @see WoltLab/WCF/Media/Manager/base#_addButtonEventListeners
+                */
+               _addButtonEventListeners: function() {
+                       MediaManagerEditor._super.prototype._addButtonEventListeners.call(this);
+                       
+                       if (!this._mediaManagerMediaList) return;
+                       
+                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+                       for (var i = 0, length = listItems.length; i < length; i++) {
+                               var listItem = listItems[i];
+                               
+                               var insertIcon = elByClass('jsMediaInsertIcon', listItem)[0];
+                               if (insertIcon) {
+                                       insertIcon.classList.remove('jsMediaInsertIcon');
+                                       insertIcon.addEventListener('click', this._openInsertDialog.bind(this));
+                               }
+                       }
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/base#_click
+                */
+               _click: function(event) {
+                       this._activeButton = event.currentTarget;
+                       
+                       MediaManagerEditor._super.prototype._click.call(this, event);
+               },
+               
+               _insertMedia: function() {
+                       // TODO
+               },
+               
+               _openInsertDialog: function(event) {
+                       var media = this._mediaData.get(~~elData(event.currentTarget, 'object-id'));
+                       
+                       // check if media file is image and has at least small thumbnail
+                       // to show insertion options
+                       if (media.isImage && media.smallThumbnailType) {
+                               UiDialog.close(this);
+                               var dialogId = 'mediaInsert' + media.mediaID;
+                               if (UiDialog.getDialog(dialogId)) {
+                                       UiDialog.openStatic(dialogId);
+                               }
+                               else {
+                                       var dialog = elCreate('div');
+                                       
+                                       var fieldset = elCreate('fieldset');
+                                       dialog.appendChild(fieldset);
+                                       
+                                       var dl = elCreate('dl');
+                                       fieldset.appendChild(dl);
+                                       
+                                       var dt = elCreate('dt');
+                                       dt.textContent = Language.get('wcf.media.insert.imageSize');
+                                       dl.appendChild(dt);
+                                       
+                                       var dd = elCreate('dd');
+                                       dl.appendChild(dd);
+                                       
+                                       var select = elCreate('select');
+                                       dd.appendChild(select);
+                                       
+                                       var sizes = ['small', 'medium', 'large'];
+                                       var size, option;
+                                       for (var i = 0, length = sizes.length; i < length; i++) {
+                                               size = sizes[i];
+                                               
+                                               if (media[size + 'ThumbnailType']) {
+                                                       option = elCreate('option');
+                                                       elAttr(option, 'value', size);
+                                                       option.textContent = Language.get('wcf.media.insert.imageSize.' + size, {
+                                                               height: media[size + 'ThumbnailHeight'],
+                                                               width: media[size + 'ThumbnailWidth']
+                                                       });
+                                                       select.appendChild(option);
+                                               }
+                                       }
+                                       
+                                       option = elCreate('option');
+                                       elAttr(option, 'value', 'original');
+                                       option.textContent = Language.get('wcf.media.insert.imageSize.original', {
+                                               height: media.height,
+                                               width: media.width
+                                       });
+                                       select.appendChild(option);
+                                       
+                                       var formSubmit = elCreate('div');
+                                       formSubmit.className = 'formSubmit';
+                                       dialog.appendChild(formSubmit);
+                                       
+                                       var submitButton = elCreate('button');
+                                       submitButton.className = 'buttonPrimary';
+                                       submitButton.textContent = Language.get('wcf.global.button.insert');
+                                       elData(submitButton, 'object-id', media.mediaID);
+                                       submitButton.addEventListener('click', this._insertMedia.bind(this));
+                                       formSubmit.appendChild(submitButton);
+                                       
+                                       UiDialog.open({
+                                               _dialogSetup: (function() {
+                                                       return {
+                                                               id: dialogId,
+                                                               options: {
+                                                                       onClose: this._editorClose.bind(this),
+                                                                       title: Language.get('wcf.media.insert')
+                                                               },
+                                                               source: dialog.outerHTML
+                                                       }
+                                               }).bind(this)
+                                       });
+                               }
+                       }
+                       else {
+                               // insert media
+                               // TODO
+                       }
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/Base#getMode
+                */
+               getMode: function() {
+                       return 'editor';
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/Base#setupMediaElement
+                */
+               setupMediaElement: function(media, mediaElement) {
+                       MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement);
+                       
+                       // add media insertion icon
+                       var smallButtons = elBySel('> nav.buttonGroupNavigation > ul.smallButtons', mediaElement);
+                       
+                       var listItem = elCreate('li');
+                       smallButtons.appendChild(listItem);
+                       
+                       var a = elCreate('a');
+                       listItem.appendChild(a);
+                       
+                       var icon = elCreate('span');
+                       icon.className = 'icon icon16 fa-plus jsTooltip jsMediaInsertIcon';
+                       elData(icon, 'object-id', media.mediaID);
+                       elAttr(icon, 'title', Language.get('wcf.media.button.insert'));
+                       a.appendChild(icon);
+               }
+       });
+       
+       return MediaManagerEditor;
+});
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Select.js b/wcfsetup/install/files/js/WoltLab/WCF/Media/Manager/Select.js
new file mode 100644 (file)
index 0000000..ed26729
--- /dev/null
@@ -0,0 +1,127 @@
+/**
+ * Provides the media manager dialog for selecting media for input elements.
+ * 
+ * @author     Matthias Schmidt
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Media/Manager/Select
+ */
+define(['Core', 'Dom/Traverse', 'Language', 'Ui/Dialog', 'WoltLab/WCF/Media/Manager/Base'], function(Core, DomTraverse, Language, UiDialog, MediaManagerBase) {
+       "use strict";
+       
+       /**
+        * @constructor
+        */
+       function MediaManagerSelect(options) {
+               MediaManagerBase.call(this, options);
+               
+               this._activeButton = null;
+               this._buttons = elByClass(this._options.buttonClass || 'jsMediaSelectButton');
+               for (var i = 0, length = this._buttons.length; i < length; i++) {
+                       var button = this._buttons[i];
+                       
+                       // only consider buttons with a proper store specified
+                       var store = elData(button, 'store');
+                       if (store) {
+                               var storeElement = elById(store);
+                               if (storeElement && storeElement.tagName === 'INPUT') {
+                                       this._buttons[i].addEventListener('click', this._click.bind(this));
+                               }
+                       }
+               }
+       };
+       Core.inherit(MediaManagerSelect, MediaManagerBase, {
+               /**
+                * @see WoltLab/WCF/Media/Manager/base#_addButtonEventListeners
+                */
+               _addButtonEventListeners: function() {
+                       MediaManagerSelect._super.prototype._addButtonEventListeners.call(this);
+                       
+                       if (!this._mediaManagerMediaList) return;
+                       
+                       var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI');
+                       for (var i = 0, length = listItems.length; i < length; i++) {
+                               var listItem = listItems[i];
+                               
+                               var chooseIcon = elByClass('jsMediaSelectIcon', listItem)[0];
+                               if (chooseIcon) {
+                                       chooseIcon.classList.remove('jsMediaSelectIcon');
+                                       chooseIcon.addEventListener('click', this._chooseMedia.bind(this));
+                               }
+                       }
+               },
+               
+               /**
+                * Handles clicking on a media choose icon.
+                * 
+                * @param       {Event}         event           click event
+                */
+               _chooseMedia: function(event) {
+                       if (this._activeButton === null) {
+                               throw new Error("Media cannot be chosen if no button is active.");
+                       }
+                       
+                       var media = this._mediaData.get(~~elData(event.currentTarget, 'object-id'));
+                       
+                       // save selected media in store element
+                       elById(elData(this._activeButton, 'store')).value = media.mediaID;
+                       
+                       // display selected media
+                       var display = elData(this._activeButton, 'display');
+                       if (display) {
+                               var displayElement = elById(display);
+                               if (displayElement) {
+                                       // TODO: add visual representation of the media file to display element
+                                       
+                                       if (media.isImage) {
+                                               displayElement.innerHTML = '<img src="' + media.smallThumbnailLink + '" alt="' + media.altText + '" />';
+                                       }
+                               }
+                       }
+                       
+                       UiDialog.close('mediaManager');
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/Base#_click
+                */
+               _click: function(event) {
+                       this._activeButton = event.currentTarget;
+                       
+                       MediaManagerSelect._super.prototype._click.call(this, event);
+                       
+                       // TODO: highlight selected medium?
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/Base#getMode
+                */
+               getMode: function() {
+                       return 'select';
+               },
+               
+               /**
+                * @see WoltLab/WCF/Media/Manager/Base#setupMediaElement
+                */
+               setupMediaElement: function(media, mediaElement) {
+                       MediaManagerSelect._super.prototype.setupMediaElement.call(this, media, mediaElement);
+                       
+                       // add media insertion icon
+                       var smallButtons = elBySel('nav.buttonGroupNavigation > ul.smallButtons', mediaElement);
+                       
+                       var listItem = elCreate('li');
+                       smallButtons.appendChild(listItem);
+                       
+                       var a = elCreate('a');
+                       listItem.appendChild(a);
+                       
+                       var icon = elCreate('span');
+                       icon.className = 'icon icon16 fa-check jsTooltip jsMediaSelectIcon';
+                       elData(icon, 'object-id', media.mediaID);
+                       elAttr(icon, 'title', Language.get('wcf.media.button.choose'));
+                       a.appendChild(icon);
+               }
+       });
+       
+       return MediaManagerSelect;
+});
index c01c4c0a480316a48815eb610924ff062f3f246c..e0e33a3f4d15af951a2f4c0f42cd711f943900aa 100644 (file)
@@ -4,7 +4,7 @@
  * @author     Matthias Schmidt
  * @copyright  2001-2015 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLab/WCF/Controller/Media/Search
+ * @module     WoltLab/WCF/Media/Search
  */
 define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], function(Ajax, DomTraverse, DomUtil, Language, UiSimpleDropdown) {
        "use strict";
@@ -23,13 +23,19 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                this._cancelButton = elById('mediaManagerSearchCancelButton');
                this._cancelButton.addEventListener('click', this._cancelSearch.bind(this));
                
-               this._fileTypes = DomTraverse.childrenBySel(UiSimpleDropdown.getDropdownMenu('mediaManagerSearch'), 'li:not(.dropdownDivider)');
-               var selectFileType = this._selectFileType.bind(this);
-               for (var i = 0, length = this._fileTypes.length; i < length; i++) {
-                       this._fileTypes[i].addEventListener('click', selectFileType);
+               var dropdown = UiSimpleDropdown.getDropdownMenu('mediaManagerSearch');
+               if (dropdown) {
+                       this._fileTypes = DomTraverse.childrenBySel(dropdown, 'li:not(.dropdownDivider)');
+                       var selectFileType = this._selectFileType.bind(this);
+                       for (var i = 0, length = this._fileTypes.length; i < length; i++) {
+                               this._fileTypes[i].addEventListener('click', selectFileType);
+                       }
+                       
+                       UiSimpleDropdown.registerCallback('mediaManagerSearch', this._updateFileTypeDropdown.bind(this));
+               }
+               else {
+                       this._fileType = null;
                }
-               
-               UiSimpleDropdown.registerCallback('mediaManagerSearch', this._updateFileTypeDropdown.bind(this));
        };
        MediaSearch.prototype = {
                /**
@@ -71,21 +77,21 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                /**
                 * Handles the `[ENTER]` key to submit the form.
                 * 
-                * @param       {Event} event           event object
+                * @param       {Event}         event           event object
                 */
                _keyPress: function(event) {
                        // 13 = [ENTER]
                        if (event.charCode === 13) {
                                event.preventDefault();
-
-                               var innerInfo = DomTraverse.childByClass(this._input.parentNode, '.innerInfo');
+                               
+                               var innerInfo = DomTraverse.childByClass(this._input.parentNode.parentNode, 'innerInfo');
                                
                                // TODO: treshold option?
                                if (this._input.value.length >= 3) {
                                        if (innerInfo) {
                                                elHide(innerInfo);
                                        }
-
+                                       
                                        this._search();
                                }
                                else {
@@ -96,8 +102,8 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                                                innerInfo = elCreate('p');
                                                innerInfo.className = 'innerInfo';
                                                innerInfo.textContent = Language.get('wcf.media.search.info.searchStringTreshold');
-
-                                               this._input.parentNode.appendChild(innerInfo);
+                                               
+                                               DomUtil.insertAfter(innerInfo, this._input.parentNode);
                                        }
                                }
                        }
@@ -111,20 +117,19 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                        
                        Ajax.api(this, {
                                parameters: {
-                                       data: {
-                                               fileType: this._fileType,
-                                               // TODO: treshold option?
-                                               searchString: this._input.value.length > 3 ? this._input.value : ''
-                                       }
+                                       fileType: this._fileType,
+                                       fileTypeFilters: this._mediaManager.getOption('fileTypeFilters'),
+                                       mode: this._mediaManager.getMode(),
+                                       searchString: this._input.value
                                }
                        });
                },
-
+               
                /**
                 * Selects a certain file type after clicking on it in the dropdown menu.
                 *
-                * @param       {Event} event
-         */
+                * @param       {Event}         event
+                */
                _selectFileType: function(event) {
                        this._fileType = elData(event.currentTarget, 'file-type');
                        
@@ -132,10 +137,10 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                        
                        this._search();
                },
-
+               
                /**
                 * Updates the label of the dropdown button based on the currently selected file type.
-         */
+                */
                _updateDropdownButtonLabel: function() {
                        var dropdown = UiSimpleDropdown.getDropdown('mediaManagerSearch');
                        var buttonLabel = DomTraverse.childBySel(DomTraverse.childByClass(dropdown, 'dropdownToggle'), 'SPAN');
@@ -147,14 +152,14 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                                buttonLabel.textContent = Language.get('wcf.media.search.filetype');
                        }
                },
-
+               
                /**
                 * Updates the file type dropdown by correctly marking the currently selected file type.
-         */
+                */
                _updateFileTypeDropdown: function() {
                        for (var i = 0, length = this._fileTypes.length; i < length; i++) {
                                var listItem = this._fileTypes[i];
-
+                               
                                listItem.classList[elData(listItem, 'file-type') === this._fileType ? 'add' : 'remove']('active');
                        }
                },
@@ -165,7 +170,7 @@ define(['Ajax', 'Dom/Traverse', 'Dom/Util', 'Language', 'Ui/SimpleDropdown'], fu
                hideSearch: function() {
                        elHide(elById('mediaManagerSearch'));
                },
-
+               
                /**
                 * Resets the media search.
                 */
index 14a8226296477c2e47b7d6b966230f6462120117..d833122fc815b60e69e73dbeb58124f11905a02c 100644 (file)
@@ -4,7 +4,7 @@
  * @author     Matthias Schmidt
  * @copyright  2001-2015 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLab/WCF/Controller/Media/Upload
+ * @module     WoltLab/WCF/Media/Upload
  */
 define(
        [
@@ -56,7 +56,7 @@ define(
                        fileElement.appendChild(thumbnail);
                        
                        var fileIcon = elCreate('span');
-                       fileIcon.className = 'icon icon96 fa-spinner';
+                       fileIcon.className = 'icon icon144 fa-spinner';
                        thumbnail.appendChild(fileIcon);
                        
                        var mediaInformation = elCreate('div');
@@ -79,6 +79,19 @@ define(
                        return fileElement;
                },
                
+               /**
+                * @see WoltLab/WCF/Upload#_getParameters
+                */
+               _getParameters: function() {
+                       if (this._mediaManager) {
+                               return Core.extend(MediaUpload._super.prototype._getParameters.call(this), {
+                                       fileTypeFilters: this._mediaManager.getOption('fileTypeFilters')
+                               });
+                       }
+                       
+                       return MediaUpload._super.prototype._getParameters.call(this);
+               },
+               
                /**
                 * @see WoltLab/WCF/Upload#_success
                 */
@@ -90,6 +103,8 @@ define(
                                var internalFileId = elData(file, 'internal-file-id');
                                var media = data.returnValues.media[internalFileId];
                                
+                               elRemove(DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaInformation'), 'PROGRESS'));
+                               
                                if (media) {
                                        var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
                                        if (media.tinyThumbnailType) {
@@ -99,8 +114,8 @@ define(
                                                var img = elCreate('img');
                                                elAttr(img, 'src', media.tinyThumbnailLink);
                                                elAttr(img, 'alt', '');
-                                               img.style.setProperty('width', '96px');
-                                               img.style.setProperty('height', '96px');
+                                               img.style.setProperty('width', '144px');
+                                               img.style.setProperty('height', '144px');
                                                parentNode.appendChild(img);
                                        }
                                        else {
@@ -111,75 +126,38 @@ define(
                                        file.className = 'jsClipboardObject';
                                        elData(file, 'object-id', media.mediaID);
                                        
-                                       var mediaInformation = DomTraverse.childByClass(file, 'mediaInformation');
-                                       
-                                       elRemove(DomTraverse.childByTag(mediaInformation, 'PROGRESS'));
-                                       
                                        if (this._mediaManager) {
-                                               var buttonGroupNavigation = elCreate('nav');
-                                               buttonGroupNavigation.className = 'buttonGroupNavigation';
-                                               mediaInformation.parentNode.appendChild(buttonGroupNavigation);
-                                               
-                                               var smallButtons = elCreate('ul');
-                                               smallButtons.className = 'smallButtons buttonGroup';
-                                               buttonGroupNavigation.appendChild(smallButtons);
-                                               
-                                               var listItem = elCreate('li');
-                                               smallButtons.appendChild(listItem);
-                                               
-                                               var checkbox = elCreate('input');
-                                               checkbox.className = 'jsClipboardItem jsMediaCheckbox';
-                                               elAttr(checkbox, 'type', 'checkbox');
-                                               elData(checkbox, 'object-id', media.mediaID);
-                                               listItem.appendChild(checkbox);
-                                               
-                                               if (Permission.get('admin.content.cms.canManageMedia')) {
-                                                       listItem = elCreate('li');
-                                                       smallButtons.appendChild(listItem);
-                                                       
-                                                       var a = elCreate('a');
-                                                       listItem.appendChild(a);
-                                                       
-                                                       var icon = elCreate('span');
-                                                       icon.className = 'icon icon16 fa-pencil jsTooltip jsMediaEditIcon';
-                                                       elData(icon, 'object-id', media.mediaID);
-                                                       elAttr(icon, 'title', Language.get('wcf.global.button.edit'));
-                                                       a.appendChild(icon);
-                                                       
-                                                       listItem = elCreate('li');
-                                                       smallButtons.appendChild(listItem);
-                                                       
-                                                       a = elCreate('a');
-                                                       listItem.appendChild(a);
-                                                       
-                                                       icon = elCreate('span');
-                                                       icon.className = 'icon icon16 fa-times jsTooltip jsMediaDeleteIcon';
-                                                       elData(icon, 'object-id', media.mediaID);
-                                                       elAttr(icon, 'title', Language.get('wcf.global.button.delete'));
-                                                       a.appendChild(icon);
-                                               }
-                                               
-                                               listItem = elCreate('li');
-                                               smallButtons.appendChild(listItem);
-                                               
-                                               var a = elCreate('a');
-                                               listItem.appendChild(a);
-                                               
-                                               var icon = elCreate('span');
-                                               icon.className = 'icon icon16 fa-plus jsTooltip jsMediaInsertIcon';
-                                               elData(icon, 'object-id', media.mediaID);
-                                               elAttr(icon, 'title', Language.get('wcf.media.button.insert'));
-                                               a.appendChild(icon);
-                                               
+                                               this._mediaManager.setupMediaElement(media, file);
                                                this._mediaManager.resetMedia();
                                                this._mediaManager.addMedia(media, file);
                                        }
-                                       
-                                       DomChangeListener.trigger();
                                }
                                else {
-                                       // error: TODO
+                                       var error = data.returnValues.errors[internalFileId];
+                                       if (!error) {
+                                               error = {
+                                                       errorType: 'uploadFailed',
+                                                       filename: elData(file, 'filename')
+                                               };
+                                       }
+                                       
+                                       var fileIcon = DomTraverse.childByTag(DomTraverse.childByClass(file, 'mediaThumbnail'), 'SPAN');
+                                       fileIcon.classList.remove('fa-spinner');
+                                       fileIcon.classList.add('fa-remove');
+                                       fileIcon.classList.add('pointer');
+                                       
+                                       file.classList.add('uploadFailed');
+                                       file.addEventListener('click', function() {
+                                               elRemove(this);
+                                       });
+                                       
+                                       var title = DomTraverse.childByClass(DomTraverse.childByClass(file, 'mediaInformation'), 'mediaTitle');
+                                       title.innerText = Language.get('wcf.media.upload.error.' + error.errorType, {
+                                               filename: error.filename
+                                       });
                                }
+                               
+                               DomChangeListener.trigger();
                        }
                        
                        EventHandler.fire('com.woltlab.wcf.media.upload', 'success', {
index e2f5742918f2bee3689233e3018e78b8b15691c2..de947c190b8b59405b10e6e8c824651735e2e6bd 100644 (file)
@@ -104,7 +104,6 @@ define(['Core', 'Language', 'Dom/Traverse', 'Dom/Util'], function(Core, Language
                        var right = horizontal.right;
                        
                        var vertical = this._tryAlignmentVertical(options.vertical, elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
-                       console.debug(vertical);
                        if (!vertical.result && (options.allowFlip === 'both' || options.allowFlip === 'vertical')) {
                                var verticalFlipped = this._tryAlignmentVertical((options.vertical === 'top' ? 'bottom' : 'top'), elDimensions, refDimensions, refOffsets, windowHeight, options.verticalOffset);
                                // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
index d2387557bc8c9785df7fed44f336ba5d5ac34d6b..6322421c47b891848b6b169876656c46a9a92c81 100644 (file)
@@ -6,7 +6,7 @@
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module     WoltLab/WCF/Upload
  */
-define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], function(AjaxRequest, Core, DomChangeListener, Language, DomUtil) {
+define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util', 'Dom/Traverse'], function(AjaxRequest, Core, DomChangeListener, Language, DomUtil, DomTraverse) {
        "use strict";
        
        /**
@@ -102,9 +102,9 @@ define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], fu
                        else {
                                var p = elCreate('p');
                                p.appendChild(progress);
-
+                               
                                this._target.appendChild(p);
-
+                               
                                return p;
                        }
                },
@@ -124,8 +124,8 @@ define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], fu
                                        var fileElement = this._createFileElement(file);
                                        
                                        if (!fileElement.classList.contains('uploadFailed')) {
-                                               elAttr(fileElement, 'data-filename', file.name);
-                                               elAttr(fileElement, 'data-internal-file-id', this._internalFileId++);
+                                               elData(fileElement, 'filename', file.name);
+                                               elData(fileElement, 'internal-file-id', this._internalFileId++);
                                                this._fileElements[uploadId][i] = fileElement;
                                        }
                                }
@@ -217,6 +217,12 @@ define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], fu
                 * @return      {(integer|Array.<integer>|null)}        identifier(s) for the uploaded files
                 */
                _upload: function(event, file, blob) {
+                       // remove failed upload elements first
+                       var failedUploads = DomTraverse.childrenByClass(this._target, 'uploadFailed');
+                       for (var i = 0, length = failedUploads.length; i < length; i++) {
+                               elRemove(failedUploads[i]);
+                       }
+                       
                        var uploadId = null;
                        
                        var files = [];
@@ -299,10 +305,22 @@ define(['AjaxRequest', 'Core', 'Dom/ChangeListener', 'Language', 'Dom/Util'], fu
                        formData.append('actionName', this._options.action);
                        formData.append('className', this._options.className);
                        formData.append('interfaceName', 'wcf\\data\\IUploadAction');
-                       var additionalParameters = this._getParameters();
-                       for (var name in additionalParameters) {
-                               formData.append('parameters[' + name + ']', additionalParameters[name]);
-                       }
+                       
+                       // recursively append additional parameters to form data
+                       var appendFormData = function(parameters, prefix) {
+                               prefix = prefix || '';
+                               
+                               for (var name in parameters) {
+                                       if (typeof parameters[name] === 'object') {
+                                               appendFormData(parameters[name], prefix + '[' + name + ']');
+                                       }
+                                       else {
+                                               formData.append('parameters' + prefix + '[' + name + ']', parameters[name]);
+                                       }
+                               }
+                       };
+                       
+                       appendFormData(this._getParameters());
                        
                        var request = new AjaxRequest({
                                data: formData,
index dfc50927ac729534b5f982dbdc2a814675c33ec6..160628691efda692caa2e53ff56d4a66ce70abb3 100644 (file)
@@ -3,6 +3,8 @@ namespace wcf\acp\form;
 use wcf\data\box\Box;
 use wcf\data\box\BoxAction;
 use wcf\data\box\BoxEditor;
+use wcf\data\media\Media;
+use wcf\data\media\ViewableMediaList;
 use wcf\form\AbstractForm;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\LanguageFactory;
@@ -34,70 +36,82 @@ class BoxAddForm extends AbstractForm {
        
        /**
         * true if created box is multi-lingual
-        * @var boolean
+        * @var boolean
         */
        public $isMultilingual = 0;
        
        /**
         * box type
-        * @var string
+        * @var string
         */
        public $boxType = '';
        
        /**
         * box position
-        * @var string
+        * @var string
         */
        public $position = '';
        
        /**
         * show order
-        * @var integer
+        * @var integer
         */
        public $showOrder = 0;
        
        /**
         * true if created box is visible everywhere 
-        * @var boolean
+        * @var boolean
         */
        public $visibleEverywhere = 1;
        
        /**
         * css class name of created box
-        * @var string
+        * @var string
         */
        public $cssClassName = '';
        
        /**
         * true if box header is visible
-        * @var boolean
+        * @var boolean
         */
        public $showHeader = 1;
        
        /**
         * php class name
-        * @var string
+        * @var string
         */
        public $className = '';
        
        /**
         * box name
-        * @var string
+        * @var string
         */
        public $name = '';
        
        /**
         * page titles
-        * @var array<string>
+        * @var string[]
         */
        public $title = [];
        
        /**
         * page contents
-        * @var array<string>
+        * @var string[]
         */
        public $content = [];
        
+       /**
+        * image ids
+        * @var integer[]
+        */
+       public $imageID = [];
+       
+       /**
+        * images
+        * @var Media[]
+        */
+       public $images = [];
+       
        /**
         * @inheritDoc
         */
@@ -126,6 +140,29 @@ class BoxAddForm extends AbstractForm {
                if (isset($_POST['title']) && is_array($_POST['title'])) $this->title = ArrayUtil::trim($_POST['title']);
                if (isset($_POST['content']) && is_array($_POST['content'])) $this->content = ArrayUtil::trim($_POST['content']);
                
+               if (WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
+                       if (isset($_POST['imageID']) && is_array($_POST['imageID'])) $this->imageID = ArrayUtil::toIntegerArray($_POST['imageID']);
+                       
+                       $this->readBoxImages();
+               }
+       }
+       
+       /**
+        * Reads the box images.
+        */
+       protected function readBoxImages() {
+               if (!empty($this->imageID)) {
+                       $mediaList = new ViewableMediaList();
+                       $mediaList->setObjectIDs($this->imageID);
+                       $mediaList->readObjects();
+                       
+                       foreach ($this->imageID as $languageID => $imageID) {
+                               $image = $mediaList->search($imageID);
+                               if ($image !== null && $image->isImage) {
+                                       $this->images[$languageID] = $image;
+                               }
+                       }
+               }
        }
        
        /**
@@ -154,7 +191,15 @@ class BoxAddForm extends AbstractForm {
                        }
                        
                        // @todo check class
-                       
+               }
+               
+               // validate images
+               if (WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
+                       foreach ($this->imageID as $languageID => $imageID) {
+                               if (!isset($this->imageID[$languageID])) {
+                                       throw new UserInputException('imageID' . $languageID);
+                               }
+                       }
                }
        }
        
@@ -180,15 +225,17 @@ class BoxAddForm extends AbstractForm {
                if ($this->isMultilingual) {
                        foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
                                $content[$language->languageID] = [
-                                       'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
-                                       'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : '')
+                                       'title' => (!empty($this->title[$language->languageID]) ? $this->title[$language->languageID] : ''),
+                                       'content' => (!empty($this->content[$language->languageID]) ? $this->content[$language->languageID] : ''),
+                                       'imageID' => (!empty($this->imageID[$language->languageID]) ? $this->imageID[$language->languageID] : null)
                                ];
                        }
                }
                else {
                        $content[0] = [
-                               'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
-                               'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : '')
+                               'title' => (!empty($this->title[0]) ? $this->title[0] : ''),
+                               'content' => (!empty($this->content[0]) ? $this->content[0] : ''),
+                               'imageID' => (!empty($this->imageID[0]) ? $this->imageID[0] : null)
                        ];
                }
                
@@ -201,11 +248,12 @@ class BoxAddForm extends AbstractForm {
                        'showOrder' => $this->showOrder,
                        'visibleEverywhere' => $this->visibleEverywhere,
                        'cssClassName' => $this->cssClassName,
-                       'showHeader' => $this->showHeader,                      
+                       'showHeader' => $this->showHeader,
                        'className' => $this->className,
                        'identifier' => ''
                ]), 'content' => $content]);
                $returnValues = $this->objectAction->executeAction();
+               
                // set generic box identifier
                $boxEditor = new BoxEditor($returnValues['returnValues']);
                $boxEditor->update([
@@ -222,7 +270,7 @@ class BoxAddForm extends AbstractForm {
                $this->boxType = $this->position = $this->cssClassName = $this->className = $this->name = '';
                $this->showOrder = 0;
                $this->visibleEverywhere = $this->showHeader = 1;
-               $this->title = $this->content = [];
+               $this->title = $this->content = $this->images = $this->imageID = [];
        }
        
        /**
@@ -244,6 +292,8 @@ class BoxAddForm extends AbstractForm {
                        'showHeader' => $this->showHeader,
                        'title' => $this->title,
                        'content' => $this->content,
+                       'imageID' => $this->imageID,
+                       'images' => $this->images,
                        'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
                        'availableBoxTypes' => Box::$availableBoxTypes,
                        'availablePositions' => Box::$availablePositions
index 5fddd3059ddfddd574a0c051b5b64e0e321fcd20..827fbff123eacd2f2cf748127eb748ca52bdd887 100644 (file)
@@ -26,13 +26,13 @@ class BoxEditForm extends BoxAddForm {
        
        /**
         * box id
-        * @var integer
+        * @var integer
         */
        public $boxID = 0;
        
        /**
         * box object
-        * @var Box
+        * @var Box
         */
        public $box = null;
        
@@ -71,6 +71,7 @@ class BoxEditForm extends BoxAddForm {
                                $content[$language->languageID] = [
                                        'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
                                        'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : ''),
+                                       'imageID' => (!empty($this->imageID[$language->languageID]) ? $this->imageID[$language->languageID] : null)
                                ];
                        }
                }
@@ -78,6 +79,7 @@ class BoxEditForm extends BoxAddForm {
                        $content[0] = [
                                'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
                                'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : ''),
+                               'imageID' => (!empty($this->imageID[0]) ? $this->imageID[0] : null)
                        ];
                }
                
@@ -105,8 +107,16 @@ class BoxEditForm extends BoxAddForm {
         * @inheritDoc
         */
        public function readData() {
+               if (!empty($_POST) && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
+                       foreach ($this->box->getBoxContent() as $languageID => $content) {
+                               $this->imageID[$languageID] = $content['imageID'];
+                       }
+                       
+                       $this->readBoxImages();
+               }
+               
                parent::readData();
-       
+               
                if (empty($_POST)) {
                        $this->name = $this->box->name;
                        $this->boxType = $this->box->boxType;
@@ -120,7 +130,10 @@ class BoxEditForm extends BoxAddForm {
                        foreach ($this->box->getBoxContent() as $languageID => $content) {
                                $this->title[$languageID] = $content['title'];
                                $this->content[$languageID] = $content['content'];
+                               $this->imageID[$languageID] = $content['imageID'];
                        }
+                       
+                       $this->readBoxImages();
                }
        }
        
index f610fc5027a8678e65e22d7cbb470ba574b1c810..2e425700c553ffc870fc8120b3e20c344568f2f7 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 namespace wcf\acp\form;
-use wcf\data\media\Media;
 use wcf\data\media\MediaAction;
+use wcf\data\media\ViewableMedia;
 use wcf\form\AbstractForm;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\UserInputException;
@@ -41,7 +41,7 @@ class MediaEditForm extends AbstractForm {
        
        /**
         * edited media
-        * @var Media
+        * @var ViewableMedia
         */
        public $media = null;
        
@@ -126,8 +126,8 @@ class MediaEditForm extends AbstractForm {
                
                if (isset($_REQUEST['id'])) $this->mediaID = intval($_REQUEST['id']);
                
-               $this->media = new Media($this->mediaID);
-               if (!$this->media->mediaID) {
+               $this->media = ViewableMedia::getMedia($this->mediaID);
+               if ($this->media === null) {
                        throw new IllegalLinkException();
                }
                
@@ -142,7 +142,7 @@ class MediaEditForm extends AbstractForm {
        public function save() {
                parent::save();
                
-               $this->objectAction = new MediaAction([$this->media], 'update', [
+               $this->objectAction = new MediaAction([$this->media->getDecoratedObject()], 'update', array_merge($this->additionalFields, [
                        'data' => [
                                'isMultilingual' => $this->isMultilingual,
                                'languageID' => $this->languageID ?: null
@@ -150,7 +150,7 @@ class MediaEditForm extends AbstractForm {
                        'altText' => I18nHandler::getInstance()->getValues('altText'),
                        'caption' => I18nHandler::getInstance()->getValues('caption'),
                        'title' => I18nHandler::getInstance()->getValues('title')
-               ]);
+               ]));
                $this->objectAction->executeAction();
                
                $this->saved();
index 4e647dba813674d4aeec8da5c9de536a9f297065..f5afb7014fede002c54b1bb40ec90a2e2b14a9f7 100644 (file)
@@ -558,7 +558,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
                                                if (!is_array($target[$variableName])) {
                                                        throw new UserInputException($variableName);
                                                }
-                                       
+                                               
                                                for ($i = 0, $length = count($target[$variableName]); $i < $length; $i++) {
                                                        if (empty($target[$variableName][$i])) {
                                                                throw new UserInputException($variableName);
index 58c2f37a0451d7ce44476048f43b2e76df9389c3..d2edd8011acdb1c87dce33ab8168393541492e87 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 namespace wcf\data\box;
+use wcf\data\media\ViewableMedia;
 use wcf\data\DatabaseObject;
 use wcf\data\menu\Menu;
 use wcf\data\menu\MenuCache;
@@ -18,6 +19,18 @@ use wcf\util\StringUtil;
  * @since      2.2
  */
 class Box extends DatabaseObject {
+       /**
+        * box content grouped by language id
+        * @var string[][]
+        */
+       protected $boxContent = null;
+       
+       /**
+        * image media object
+        * @var Media
+        */
+       protected $image = null;
+       
        /**
         * @inheritDoc
         */
@@ -30,13 +43,13 @@ class Box extends DatabaseObject {
        
        /**
         * available box types
-        * @var string[]
+        * @var string[]
         */
        public static $availableBoxTypes = ['text', 'html', 'system', 'menu'];
        
        /**
         * available box positions
-        * @var string[]
+        * @var string[]
         */
        public static $availablePositions = ['hero', 'headerBoxes', 'top', 'sidebarLeft', 'contentTop', 'sidebarRight', 'contentBottom', 'bottom', 'footerBoxes', 'footer'];
        
@@ -49,42 +62,46 @@ class Box extends DatabaseObject {
        /**
         * Returns true if the active user can delete this box.
         * 
-        * @return boolean
+        * @return      boolean
         */
        public function canDelete() {
                if (WCF::getSession()->getPermission('admin.content.cms.canManageBox') && !$this->originIsSystem) {
                        return true;
                }
-                       
+               
                return false;
        }
        
        /**
         * Returns the box content.
-        *
-        * @return array
+        * 
+        * @return      string[][]
         */
        public function getBoxContent() {
-               $content = array();
-               $sql = "SELECT  *
-                       FROM    wcf".WCF_N."_box_content
-                       WHERE   boxID = ?";
-               $statement = WCF::getDB()->prepareStatement($sql);
-               $statement->execute(array($this->boxID));
-               while ($row = $statement->fetchArray()) {
-                       $content[($row['languageID'] ?: 0)] = [
-                               'title' => $row['title'],
-                               'content' => $row['content']
-                       ];
+               if ($this->boxContent === null) {
+                       $this->boxContent = [];
+                       
+                       $sql = "SELECT  *
+                               FROM    wcf" . WCF_N . "_box_content
+                               WHERE   boxID = ?";
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute([$this->boxID]);
+                       while ($row = $statement->fetchArray()) {
+                               $this->boxContent[($row['languageID'] ?: 0)] = [
+                                       'title' => $row['title'],
+                                       'content' => $row['content'],
+                                       'imageID' => $row['imageID']
+                               ];
+                       }
                }
-       
-               return $content;
+               
+               return $this->boxContent;
        }
        
        /**
-        * Gets the title for the rendered version of this box.
-        *
-        * @return      string
+        * Returns the title for the rendered version of this box.
+        * 
+        * @return      string
         */
        public function getTitle() {
                if ($this->boxType == 'system') {
@@ -107,9 +124,9 @@ class Box extends DatabaseObject {
        }
        
        /**
-        * Gets the content for the rendered version of this box.
+        * Returns the content for the rendered version of this box.
         * 
-        * @return      string
+        * @return      string
         */
        public function getContent() {
                if ($this->boxType == 'system') {
@@ -139,7 +156,7 @@ class Box extends DatabaseObject {
        /**
         * Returns the rendered version of this box.
         * 
-        * @return      string
+        * @return      string
         */
        public function __toString() {
                if (!$this->hasContent()) return ''; 
@@ -153,7 +170,7 @@ class Box extends DatabaseObject {
        /**
         * Returns false if this box has no content.
         * 
-        * @return      boolean
+        * @return      boolean
         */
        public function hasContent() {
                if ($this->boxType == 'system') {
@@ -191,7 +208,7 @@ class Box extends DatabaseObject {
        /**
         * Returns the image of this box.
         * 
-        * @return      \wcf\data\media\Media
+        * @return      ViewableMedia
         */
        public function getImage() {
                if ($this->boxType == 'system') {
@@ -200,17 +217,28 @@ class Box extends DatabaseObject {
                else if ($this->boxType == 'menu') {
                        return null;
                }
-               else {
-                       // @todo
+               
+               if ($this->image !== null) {
+                       return $this->image;
                }
                
-               return null;
+               $boxContent = $this->getBoxContent();
+               if ($this->isMultilingual) {
+                       if (isset($boxContent[WCF::getLanguage()->languageID]) && $boxContent[WCF::getLanguage()->languageID]['imageID']) {
+                               $this->image = ViewableMedia::getMedia($boxContent[WCF::getLanguage()->languageID]['imageID']);
+                       }
+               }
+               else if (isset($boxContent[0]) && $boxContent[0]['imageID']) {
+                       $this->image = ViewableMedia::getMedia($boxContent[0]['imageID']);
+               }
+               
+               return $this->image;
        }
        
        /**
         * Returns true if this box has an image.
-        *
-        * @return      boolean
+        * 
+        * @return      boolean
         */
        public function hasImage() {
                if ($this->boxType == 'system') {
@@ -219,11 +247,13 @@ class Box extends DatabaseObject {
                else if ($this->boxType == 'menu') {
                        return false;
                }
-               else {
-                       // @todo
+               
+               $boxContent = $this->getBoxContent();
+               if ($this->isMultilingual) {
+                       return (isset($boxContent[WCF::getLanguage()->languageID]) && $boxContent[WCF::getLanguage()->languageID]['imageID']);
                }
                
-               return false;
+               return (isset($boxContent[0]) && $boxContent[0]['imageID']);
        }
        
        public function getLink() {
index 2f007eb389ef27525fa0555fcfe959a0018285b0..a23b4de7f9aed2c09da6b77c2da43d4365ac7234 100644 (file)
@@ -50,20 +50,21 @@ class BoxAction extends AbstractDatabaseObjectAction {
                // save box content
                if (!empty($this->parameters['content'])) {
                        $sql = "INSERT INTO     wcf".WCF_N."_box_content
-                                               (boxID, languageID, title, content)
-                               VALUES          (?, ?, ?, ?)";
+                                               (boxID, languageID, title, content, imageID)
+                               VALUES          (?, ?, ?, ?, ?)";
                        $statement = WCF::getDB()->prepareStatement($sql);
-                               
+                       
                        foreach ($this->parameters['content'] as $languageID => $content) {
                                $statement->execute([
                                        $box->boxID,
                                        ($languageID ?: null),
                                        $content['title'],
-                                       $content['content']
+                                       $content['content'],
+                                       $content['imageID']
                                ]);
                        }
                }
-       
+               
                return $box;
        }
        
@@ -72,32 +73,33 @@ class BoxAction extends AbstractDatabaseObjectAction {
         */
        public function update() {
                parent::update();
-       
+               
                // update box content
                if (!empty($this->parameters['content'])) {
                        $sql = "DELETE FROM     wcf".WCF_N."_box_content
                                WHERE           boxID = ?";
                        $deleteStatement = WCF::getDB()->prepareStatement($sql);
-                               
+                       
                        $sql = "INSERT INTO     wcf".WCF_N."_box_content
-                                               (boxID, languageID, title, content)
-                               VALUES          (?, ?, ?, ?)";
+                                               (boxID, languageID, title, content, imageID)
+                               VALUES          (?, ?, ?, ?, ?)";
                        $insertStatement = WCF::getDB()->prepareStatement($sql);
-                               
+                       
                        foreach ($this->objects as $box) {
-                               $deleteStatement->execute(array($box->boxID));
-       
+                               $deleteStatement->execute([$box->boxID]);
+                               
                                foreach ($this->parameters['content'] as $languageID => $content) {
                                        $insertStatement->execute([
                                                $box->boxID,
                                                ($languageID ?: null),
                                                $content['title'],
-                                               $content['content']
+                                               $content['content'],
+                                               $content['imageID']
                                        ]);
                                }
                        }
                }
-       
+               
                return $box;
        }
        
index cb6f2d34ce0cf9366bd5a80e30991889f635bbb1..f96973076a7a243d4b7e155f2e9df9db48e8d06c 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\data\media;
 use wcf\data\DatabaseObject;
+use wcf\data\ILinkableObject;
 use wcf\data\IThumbnailFile;
 use wcf\system\request\IRouteController;
 use wcf\system\request\LinkHandler;
@@ -17,7 +18,7 @@ use wcf\system\WCF;
  * @category   Community Framework
  * @since      2.2
  */
-class Media extends DatabaseObject implements IRouteController, IThumbnailFile {
+class Media extends DatabaseObject implements ILinkableObject, IRouteController, IThumbnailFile {
        /**
         * i18n media data grouped by language id for all language
         * @var string[][]
@@ -62,14 +63,24 @@ class Media extends DatabaseObject implements IRouteController, IThumbnailFile {
        ];
        
        /**
-        * @inheritcoc
+        * @inheritDoc
+        */
+       public function getLink() {
+               return LinkHandler::getInstance()->getLink('Media', [
+                       'forceFrontend' => true,
+                       'object' => $this
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
         */
        public function getLocation() {
                return self::getStorage().substr($this->fileHash, 0, 2).'/'.$this->mediaID.'-'.$this->fileHash;
        }
        
        /**
-        * @inheritcoc
+        * @inheritDoc
         */
        public function getThumbnailLink($size) {
                if (!isset(self::$thumbnailSizes[$size])) {
@@ -84,7 +95,7 @@ class Media extends DatabaseObject implements IRouteController, IThumbnailFile {
        }
        
        /**
-        * @inheritcoc
+        * @inheritDoc
         */
        public function getThumbnailLocation($size) {
                if (!isset(self::$thumbnailSizes[$size])) {
@@ -95,7 +106,7 @@ class Media extends DatabaseObject implements IRouteController, IThumbnailFile {
        }
        
        /**
-        * @inheritcoc
+        * @inheritDoc
         */
        public function getTitle() {
                return $this->filename;
@@ -140,7 +151,7 @@ class Media extends DatabaseObject implements IRouteController, IThumbnailFile {
        }
        
        /**
-        * @inheritcoc
+        * @inheritDoc
         */
        public static function getThumbnailSizes() {
                return static::$thumbnailSizes;
index c70430c7a1508d589c5177f019c6eeab3151bc11..7fefe1c2bbaf333546f445a8c0b15027dddc09b6 100644 (file)
@@ -6,10 +6,12 @@ use wcf\data\IUploadAction;
 use wcf\system\clipboard\ClipboardHandler;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
 use wcf\system\language\I18nHandler;
 use wcf\system\language\LanguageFactory;
 use wcf\system\request\Linkhandler;
 use wcf\system\upload\DefaultUploadFileSaveStrategy;
+use wcf\system\upload\MediaUploadFileValidationStrategy;
 use wcf\system\WCF;
 use wcf\util\FileUtil;
 
@@ -37,7 +39,11 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
        public function validateUpload() {
                WCF::getSession()->checkPermissions(['admin.content.cms.canManageMedia']);
                
-               // TODO
+               if (isset($this->parameters['fileTypeFilters']) && !is_array($this->parameters['fileTypeFilters'])) {
+                       throw new UserInputException('fileTypeFilters');
+               }
+               
+               $this->parameters['__files']->validateFiles(new MediaUploadFileValidationStrategy(isset($this->parameters['fileTypeFilters']) ? $this->parameters['fileTypeFilters'] : []));
        }
        
        /**
@@ -115,6 +121,7 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                        'largeThumbnailLink' => $media->largeThumbnailType ? $media->getThumbnailLink('large') : '',
                        'largeThumbnailType' => $media->largeThumbnailType,
                        'largeThumbnailWidth' => $media->largeThumbnailWidth,
+                       'link' => $media->getLink(),
                        'mediaID' => $media->mediaID,
                        'mediumThumbnailHeight' => $media->mediumThumbnailHeight,
                        'mediumThumbnailLink' => $media->mediumThumbnailType ? $media->getThumbnailLink('medium') : '',
@@ -147,6 +154,15 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                if (!WCF::getSession()->getPermission('admin.content.cms.canManageMedia') && !WCF::getSession()->getPermission('admin.content.cms.canUseMedia')) {
                        throw new PermissionDeniedException();
                }
+               
+               if (isset($this->parameters['fileTypeFilters']) && !is_array($this->parameters['fileTypeFilters'])) {
+                       throw new UserInputException('fileTypeFilters');
+               }
+               
+               $this->readString('mode');
+               if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
+                       throw new UserInputException('mode');
+               }
        }
        
        /**
@@ -156,13 +172,18 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
         */
        public function getManagementDialog() {
                $mediaList = new ViewableMediaList();
+               if (!empty($this->parameters['fileTypeFilters'])) {
+                       $mediaList->addFileTypeFilters($this->parameters['fileTypeFilters']);
+               }
                $mediaList->readObjects();
                
                return [
                        'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')),
                        'media' => $this->getI18nMediaData($mediaList),
                        'template' => WCF::getTPL()->fetch('mediaManager', 'wcf', [
-                               'mediaList' => $mediaList
+                               'mediaList' => $mediaList,
+                               'mode' => $this->parameters['mode'],
+                               'showFileTypeFilter' => empty($this->parameters['fileTypeFilters'])
                        ])
                ];
        }
@@ -331,15 +352,15 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                        throw new PermissionDeniedException();
                }
                
-               $this->readString('searchString', true, 'data');
-               $this->readString('fileType', true, 'data');
+               $this->readString('searchString', true);
+               $this->readString('fileType', true);
                
-               if (!$this->parameters['data']['searchString'] && !$this->parameters['data']['fileType']) {
+               if (!$this->parameters['searchString'] && !$this->parameters['fileType']) {
                        throw new UserInputException('searchString');
                }
                
                $this->fileTypeConditionBuilder = new PreparedStatementConditionBuilder(false);
-               switch ($this->parameters['data']['fileType']) {
+               switch ($this->parameters['fileType']) {
                        case 'other':
                                $this->fileTypeConditionBuilder->add('media.fileType NOT LIKE ?', ['image/%']);
                                $this->fileTypeConditionBuilder->add('media.fileType <> ?', ['application/pdf']);
@@ -358,52 +379,57 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                                $this->fileTypeConditionBuilder->add('media.fileType LIKE ?', ['text/%']);
                        break;
                }
+               
+               if (isset($this->parameters['fileTypeFilters']) && !is_array($this->parameters['fileTypeFilters'])) {
+                       throw new UserInputException('fileTypeFilters');
+               }
+               
+               $this->readString('mode');
+               if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
+                       throw new UserInputException('mode');
+               }
        }
        
        /**
         * @inheritdoc
         */
        public function getSearchResultList() {
-               $searchString = '%'.addcslashes($this->parameters['data']['searchString'], '_%').'%';
-               
-               $sql = "SELECT          media.mediaID
-                       FROM            wcf".WCF_N."_media media
-                       LEFT JOIN       wcf".WCF_N."_media_content media_content
-                       ON              (media_content.mediaID = media.mediaID)
-                       WHERE           (media_content.title LIKE ?
-                                       OR media_content.caption LIKE ?
-                                       OR media_content.altText LIKE ?
-                                       OR media.filename LIKE ?)";
+               $searchString = '%'.addcslashes($this->parameters['searchString'], '_%').'%';
+               
+               $mediaList = new MediaList();
+               $mediaList->sqlConditionJoins = 'LEFT JOIN wcf'.WCF_N.'_media_content media_content ON (media_content.mediaID = media.mediaID)';
+               
+               $searchConditionBuilder = new PreparedStatementConditionBuilder(false, 'OR');
+               $searchConditionBuilder->add('media_content.title LIKE ?', [$searchString]);
+               $searchConditionBuilder->add('media_content.caption LIKE ?', [$searchString]);
+               $searchConditionBuilder->add('media_content.altText LIKE ?', [$searchString]);
+               $searchConditionBuilder->add('media.filename LIKE ?', [$searchString]);
+               $mediaList->getConditionBuilder()->add($searchConditionBuilder->__toString(), $searchConditionBuilder->getParameters());
+               
                if (!empty($this->fileTypeConditionBuilder->__toString())) {
-                       $sql .= " AND ".$this->fileTypeConditionBuilder;
+                       $mediaList->getConditionBuilder()->add($this->fileTypeConditionBuilder->__toString(), $this->fileTypeConditionBuilder->getParameters());
                }
-               $statement = WCF::getDB()->prepareStatement($sql, 0, 10);
-               $statement->execute(array_merge([
-                       $searchString,
-                       $searchString,
-                       $searchString,
-                       $searchString
-               ], $this->fileTypeConditionBuilder->getParameters()));
-               
-               $mediaIDs = [];
-               while ($mediaID = $statement->fetchColumn()) {
-                       $mediaIDs[] = $mediaID;
+               if (!empty($this->parameters['fileTypeFilters'])) {
+                       $mediaList->addFileTypeFilters($this->parameters['fileTypeFilters']);
                }
                
-               if (empty($mediaIDs)) {
+               $mediaList->readObjectIDs();
+               
+               if (empty($mediaList->getObjectIDs())) {
                        return [
                                'template' => WCF::getLanguage()->getDynamicVariable('wcf.media.search.noResults')
                        ];
                }
                
-               $mediaList = new ViewableMediaList();
-               $mediaList->setObjectIDs($mediaIDs);
-               $mediaList->readObjects();
+               $viewableMediaList = new ViewableMediaList();
+               $viewableMediaList->setObjectIDs($mediaList->getObjectIDs());
+               $viewableMediaList->readObjects();
                
                return [
-                       'media' => $this->getI18nMediaData($mediaList),
+                       'media' => $this->getI18nMediaData($viewableMediaList),
                        'template' => WCF::getTPL()->fetch('mediaListItems', 'wcf', [
-                               'mediaList' => $mediaList
+                               'mediaList' => $viewableMediaList,
+                               'mode' => $this->parameters['mode']
                        ])
                ];
        }
index e7073641e9f91362d6f613999bbd5fa24a69427c..24b4af28a9a7831a8cedd6eb075bb6680e6db37a 100644 (file)
@@ -24,7 +24,7 @@ class MediaEditor extends DatabaseObjectEditor {
         */
        public function deleteFiles() {
                @unlink($this->getLocation());
-
+               
                // delete thumbnails
                if ($this->isImage) {
                        foreach (Media::getThumbnailSizes() as $size => $data) {
index ab8ca3fbf1e150aca52968cb5610f69e2773b871..4355635a9b7f1d832e618134ef1ec358fec8bf7b 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\data\media;
 use wcf\data\DatabaseObjectList;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
 
 /**
  * Represents a list of madia files.
@@ -18,4 +19,29 @@ class MediaList extends DatabaseObjectList {
         * @inheritdoc
         */
        public $className = Media::class;
+       
+       /**
+        * Adds filters for the media files based on their file type.
+        * 
+        * @param       array           $filters
+        */
+       public function addFileTypeFilters(array $filters) {
+               if (isset($filters['isImage'])) {
+                       $this->getConditionBuilder()->add('isImage = ?', [$filters['isImage'] ? 1 : 0]);
+               }
+               
+               if (isset($filters['fileTypes'])) {
+                       $conditionBuilder = new PreparedStatementConditionBuilder(false, 'OR');
+                       foreach ($filters['fileTypes'] as $fileType) {
+                               if (substr($fileType, -1) == '*') {
+                                       $conditionBuilder->add('fileType LIKE ?', [substr($fileType, 0, -1).'%']);
+                               }
+                               else {
+                                       $conditionBuilder->add('fileType = ?', [$fileType]);
+                               }
+                       }
+                       
+                       $this->getConditionBuilder()->add($conditionBuilder->__toString(), $conditionBuilder->getParameters());
+               }
+       }
 }
index b0155930508db5a2ac069ffff89b6ff703e5db73..f1c053a75b5c89272d0b934ee7c08e1cf0839850 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace wcf\data\media;
 use wcf\data\DatabaseObjectDecorator;
+use wcf\system\exception\SystemException;
 use wcf\util\StringUtil;
 use wcf\util\FileUtil;
 
@@ -21,6 +22,19 @@ class ViewableMedia extends DatabaseObjectDecorator {
         */
        protected static $baseClass = Media::class;
        
+       /**
+        * Returns a textual representation of the media file to be used in templates.
+        * 
+        * @return      string
+        */
+       public function __toString() {
+               if ($this->isImage) {
+                       return '<img src="'.$this->getLink().'" alt="'.StringUtil::encodeHTML($this->altText).'" />';
+               }
+               
+               return '<a href="'.$this->getLink().'>'.$this->getTitle().'</a>';
+       }
+       
        /**
         * Returns a tag to display the media element.
         * 
@@ -30,9 +44,37 @@ class ViewableMedia extends DatabaseObjectDecorator {
        public function getElementTag($size) {
                // todo: validate $size
                if ($this->isImage && $this->tinyThumbnailType) {
-                       return '<img src="'.$this->getThumbnailLink('tiny').'" alt="" style="width: '.$size.'px; height: '.$size.'px;" />';
+                       return '<img src="'.$this->getThumbnailLink('tiny').'" alt="'.StringUtil::encodeHTML($this->altText).'" style="width: '.$size.'px; height: '.$size.'px;" />';
                }
                
                return '<span class="icon icon'.$size.' '.FileUtil::getIconClassByMimeType($this->fileType).'"></span>';
        }
+       
+       /**
+        * Returns a tag to display a certain thumbnail.
+        * 
+        * @param       string          $size           thumbnail size
+        * @return      string
+        */
+       public function getThumbnailTag($size = '') {
+               if (!isset(Media::getThumbnailSizes()[$size])) {
+                       throw new SystemException("Unknown thumbnail size '".$size."'");
+               }
+               
+               return '<img src="'.$this->getThumbnailLink($size).'" alt="'.StringUtil::encodeHTML($this->altText).'" />';
+       }
+       
+       /**
+        * Returns the viewable media file with the given id.
+        * 
+        * @param       integer         $mediaID
+        * @return      Media|null
+        */
+       public static function getMedia($mediaID) {
+               $mediaList = new ViewableMediaList();
+               $mediaList->setObjectIDs([$mediaID]);
+               $mediaList->readObjects();
+               
+               return $mediaList->search($mediaID);
+       }
 }
index d444be8c2e051ec2a9edcd9f3c571f794c40c722..2bf24296a769d4cd80ede25188776299ebeb37db 100644 (file)
@@ -27,6 +27,6 @@ class ViewableMediaList extends MediaList {
                
                // fetch content data
                $this->sqlSelects .= "media_content.*";
-               $this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_media_content media_content ON (media_content.mediaID = media.mediaID AND media_content.languageID = COALESCE(media.languageID, ".WCF::getUser()->languageID."))";
+               $this->sqlJoins .= " LEFT JOIN wcf".WCF_N."_media_content media_content ON (media_content.mediaID = media.mediaID AND media_content.languageID = COALESCE(media.languageID, ".WCF::getLanguage()->languageID."))";
        }
 }
index ca0c153b14878f726fc3a99a48012387df43c9c5..088d39ccd7e6d33b52fd5ab957518afff298e587 100644 (file)
@@ -28,13 +28,13 @@ class Menu extends DatabaseObject {
        /**
         * Returns true if the active user can delete this menu.
         * 
-        * @return boolean
+        * @return      boolean
         */
        public function canDelete() {
                if (WCF::getSession()->getPermission('admin.content.cms.canManageMenu') && !$this->originIsSystem) {
                        return true;
                }
-                       
+               
                return false;
        }
 }
diff --git a/wcfsetup/install/files/lib/system/upload/MediaUploadFileValidationStrategy.class.php b/wcfsetup/install/files/lib/system/upload/MediaUploadFileValidationStrategy.class.php
new file mode 100644 (file)
index 0000000..86612ad
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+namespace wcf\system\upload;
+
+/**
+ * Upload file validation strategy implementation for media files.
+ *
+ * @author     Matthias Schmidt
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.upload
+ * @category   Community Framework
+ * @since      2.2
+ */
+class MediaUploadFileValidationStrategy implements IUploadFileValidationStrategy {
+       /**
+        * file type filters
+        * @var array
+        */
+       protected $fileTypeFilters = [];
+       
+       /**
+        * Creates a new instance of MediaUploadFileValidationStrategy.
+        * 
+        * @param       array   $fileTypeFilters
+        */
+       public function __construct(array $fileTypeFilters) {
+               $this->fileTypeFilters = $fileTypeFilters;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function validate(UploadFile $uploadFile) {
+               if ($uploadFile->getErrorCode()) {
+                       $uploadFile->setValidationErrorType('uploadFailed');
+                       return false;
+               }
+               
+               if (!empty($this->fileTypeFilters['isImage']) && ($uploadFile->getImageData() === null || !preg_match('~^image/(gif|jpe?g|png)$~i', $uploadFile->getMimeType()))) {
+                       $uploadFile->setValidationErrorType('noImage');
+                       return false;
+               }
+               
+               if (isset($this->fileTypeFilters['fileTypes'])) {
+                       foreach ($this->fileTypeFilters['fileTypes'] as $fileType) {
+                               if (substr($fileType, -1) == '*') {
+                                       if (!preg_match('~^'.preg_quote(substr($fileType, 0, -1), '~').'~', $uploadFile->getMimeType())) {
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       if ($uploadFile->getMimeType() != $fileType) {
+                                               $uploadFile->setValidationErrorType('noImage');
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+               
+               return true;
+       }
+}
index a9e43c9568f0ea6d2132c2828df0b0707b6891e4..6309bd1ed4a8b4c0b27910d26a56568cbb2069bb 100644 (file)
@@ -114,6 +114,12 @@ a > span.fa:not(.pointer) {
        width: 96px;
 }
 
+.icon144 {
+       font-size: 130px; // TODO
+       height: 144px;
+       width: 144px;
+}
+
 // spinner animation
 .fa-spinner {
        animation: wcfSpinner .6s linear infinite;
index 68a4dd80893e15379105c54d9162a1b94ac7946f..58a72dbc141be41f122d7bef98a757b35865e5c4 100644 (file)
@@ -16,8 +16,8 @@
        
        > li {
                float: left;
-               height: 120px;
-               width: 120px;
+               height: 160px;
+               width: 160px;
                position: relative;
                border: 1px solid #aaa;
                overflow: hidden;
                        }
                }
                
+               &.uploadFailed {
+                       cursor: pointer;
+                       
+                       > .mediaInformation {
+                               background-color: rgba(242, 222, 222, 0.6); // todo
+                               color: rgb(169, 68, 66); // todo
+                       }
+               }
+               
                > .mediaThumbnail {
-                       height: 96px;
-                       width: 96px;
-                       padding: 12px;
+                       height: 144px;
+                       width: 144px;
+                       padding: 8px;
                }
                
                > .mediaInformation {
                        position: absolute;
                        bottom: 0;
-                       background: rgba(0,0,0,0.6);
+                       background: rgba(0,0,0,0.6); // TOOD
                        color: #fff;
                        width: 100%;
                        padding: $wcfGapSmall;
@@ -70,7 +79,7 @@
                        position: absolute;
                        top: 0;
                        right: 0;
-                       background: rgba(0,0,0,0.6);
+                       background: rgba(0,0,0,0.6); // TOOD
                        height: 0;
                        overflow: hidden;
                        
index 062e3c719a1e31345714c62f0d670b9e39e48528..5c2286ce66290ec5e63a3ea0044b4851d798510d 100644 (file)
@@ -238,6 +238,7 @@ CREATE TABLE wcf1_box_content (
        languageID INT(10),
        title VARCHAR(255) NOT NULL,
        content MEDIUMTEXT,
+       imageID INT(10),
        
        KEY (boxID, languageID)
 );
@@ -1654,7 +1655,8 @@ ALTER TABLE wcf1_box ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packag
 ALTER TABLE wcf1_box ADD FOREIGN KEY (menuID) REFERENCES wcf1_menu (menuID) ON DELETE CASCADE;
 
 ALTER TABLE wcf1_box_content ADD FOREIGN KEY (boxID) REFERENCES wcf1_box (boxID) ON DELETE CASCADE;
-ALTER TABLE wcf1_box_content ADD FOREIGN KEY (languageID) REFERENCES wcf1_language (languageID) ON DELETE CASCADE; 
+ALTER TABLE wcf1_box_content ADD FOREIGN KEY (languageID) REFERENCES wcf1_language (languageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_box_content ADD FOREIGN KEY (imageID) REFERENCES wcf1_media (mediaID) ON DELETE SET NULL;
 
 ALTER TABLE wcf1_category ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;