From d1f9bb4a28e0344a4c3154a6afa0f2765546a0ad Mon Sep 17 00:00:00 2001 From: lincolnwebs Date: Thu, 2 Jul 2015 17:25:13 -0400 Subject: [PATCH] Cleanup Editor to not be a trainwreck because I like to be able to actually read code. --- plugins/editor/class.editor.plugin.php | 981 +++++++++++-------------- 1 file changed, 431 insertions(+), 550 deletions(-) diff --git a/plugins/editor/class.editor.plugin.php b/plugins/editor/class.editor.plugin.php index 7ddd1022c35..b5cf297ba84 100644 --- a/plugins/editor/class.editor.plugin.php +++ b/plugins/editor/class.editor.plugin.php @@ -15,9 +15,6 @@ 'Author' => "Dane MacMillan", 'AuthorUrl' => 'http://www.vanillaforums.org/profile/dane', 'RequiredApplications' => array('Vanilla' => '>=2.2'), - 'RequiredTheme' => false, - 'RequiredPlugins' => false, - 'HasLocale' => false, 'MobileFriendly' => true, 'RegisterPermissions' => array( 'Plugins.Attachments.Upload.Allow' => 'Garden.Profiles.Edit' @@ -31,42 +28,42 @@ */ class EditorPlugin extends Gdn_Plugin { - /** Base string to be used for generating a memcached key. */ + /** Base string to be used for generating a memcached key. */ const DISCUSSION_MEDIA_CACHE_KEY = 'media.discussion.%d'; - /** @var bool */ + /** @var bool */ protected $canUpload = false; - /** @var array Give class access to PluginInfo */ + /** @var array Give class access to PluginInfo */ protected $pluginInfo = array(); - /** @var array List of possible formats the editor supports. */ + /** @var array List of possible formats the editor supports. */ protected $Formats = array('Wysiwyg', 'Html', 'Markdown', 'BBCode', 'Text', 'TextEx'); - /** @var string Default format being used for current rendering. Can be one of the formats listed in $Formats. */ + /** @var string Default format being used for current rendering. Can be one of the formats listed in $Formats. */ protected $Format; - /** @var string Asset path for this plugin, set in Gdn_Form_BeforeBodyBox_Handler. */ + /** @var string Asset path for this plugin, set in Gdn_Form_BeforeBodyBox_Handler. */ protected $AssetPath; - /** - * @var string This is used as the input name for file uploads. It will be - * passed to JS as well. Note that it can be defined as an array, by adding square brackets, e.g., `editorupload[]`, - * but that will make all the Vanilla upload classes incompatible because they are hardcoded to handle only - * single files at a time, not an array of files. Perhaps in future make core upload classes more flexible. - */ + /** + * @var string This is used as the input name for file uploads. It will be + * passed to JS as well. Note that it can be defined as an array, by adding square brackets, e.g., `editorupload[]`, + * but that will make all the Vanilla upload classes incompatible because they are hardcoded to handle only + * single files at a time, not an array of files. Perhaps in future make core upload classes more flexible. + */ protected $editorFileInputName = 'editorupload'; - /** @var string */ + /** @var string */ protected $editorBaseUploadDestinationDir = ''; - /** @var int|mixed */ + /** @var int|mixed */ public $ForceWysiwyg = 0; - /** @var array This will cache the discussion media results for the page request. Populated from either the db or memcached. */ + /** @var array This will cache the discussion media results for the page request. Populated from either the db or memcached. */ protected $mediaCache; - /** @var int How long memcached holds data until it expires. */ + /** @var int How long memcached holds data until it expires. */ protected $mediaCacheExpire; /** @@ -80,7 +77,7 @@ public function __construct() { $this->pluginInfo = Gdn::pluginManager()->getPluginInfo('editor', Gdn_PluginManager::ACCESS_PLUGINNAME); $this->ForceWysiwyg = c('Plugins.editor.ForceWysiwyg', false); - // Check upload permissions + // Check upload permissions $this->canUpload = Gdn::session()->checkPermission('Plugins.Attachments.Upload.Allow', false); if ($this->canUpload) { @@ -90,86 +87,86 @@ public function __construct() { } } - // Check against config, too + // Check against config, too if (!c('Garden.AllowFileUploads', false)) { $this->canUpload = false; } } - /** - * Set the editor actions to true or false to enable or disable the action - * from displaying in the editor toolbar. This will also let you toggle - * the separators from appearing between the loosely grouped actions. - * - * @return array List of allowed editor actions - */ + /** + * Set the editor actions to true or false to enable or disable the action + * from displaying in the editor toolbar. + * + * This will also let you toggle the separators from appearing between the loosely grouped actions. + * + * @return array List of allowed editor actions + */ public function getAllowedEditorActions() { static $allowedEditorActions = array( - 'bold' => true, - 'italic' => true, - 'strike' => true, - 'orderedlist' => true, - 'unorderedlist' => true, - 'indent' => false, - 'outdent' => false, - - 'sep-format' => true, // separator - 'color' => false, - 'highlightcolor' => false, // Dependent on color. TODO add multidim support. - 'format' => true, - 'fontfamily' => false, - - - 'sep-media' => true, // separator - 'emoji' => true, - 'links' => true, - 'images' => true, - 'uploads' => false, - - 'sep-align' => true, // separator - 'alignleft' => true, - 'aligncenter' => true, - 'alignright' => true, - - 'sep-switches' => true, // separator - 'togglehtml' => true, - 'fullpage' => true, - 'lights' => true + 'bold' => true, + 'italic' => true, + 'strike' => true, + 'orderedlist' => true, + 'unorderedlist' => true, + 'indent' => false, + 'outdent' => false, + + 'sep-format' => true, // separator + 'color' => false, + 'highlightcolor' => false, // Dependent on color. TODO add multidim support. + 'format' => true, + 'fontfamily' => false, + + + 'sep-media' => true, // separator + 'emoji' => true, + 'links' => true, + 'images' => true, + 'uploads' => false, + + 'sep-align' => true, // separator + 'alignleft' => true, + 'aligncenter' => true, + 'alignright' => true, + + 'sep-switches' => true, // separator + 'togglehtml' => true, + 'fullpage' => true, + 'lights' => true ); return $allowedEditorActions; } - /** - * To enable more colors in the dropdown, simply expand the array to - * include more human-readable font color names. - * - * Note: in building the dropdown, each color is styled inline, but it will - * still be required to add the appropriate post-color-* CSS class selectors - * in the external stylesheet, so that when viewing a posted comment, the - * color will appear. In addition, the class names must be whitelisted in - * advanced.js. Not all colors in the CSS stylesheet are included here. - * - * Note: use these http://clrs.cc/ and purple: #7b11d0 - * - * @return array Returns array of font colors to use in dropdown - */ + /** + * To enable more colors in the dropdown, simply expand the array to include more human-readable font color names. + * + * Note: in building the dropdown, each color is styled inline, but it will + * still be required to add the appropriate post-color-* CSS class selectors + * in the external stylesheet, so that when viewing a posted comment, the + * color will appear. In addition, the class names must be whitelisted in + * advanced.js. Not all colors in the CSS stylesheet are included here. + * + * Note: use these http://clrs.cc/ and purple: #7b11d0 + * + * @return array Returns array of font colors to use in dropdown + */ protected function getFontColorList() { $fontColorList = array( - 'black', - //'white', - 'gray', - 'red', - 'green', - 'purple', - 'yellow', - 'blue', - 'orange' - //'olive', - //'navy', - //'lime', - //'silver', - //'maroon' + 'black', + //'white', + 'gray', + 'red', + 'green', + 'purple', + 'yellow', + 'blue', + 'orange' + //'olive', + //'navy', + //'lime', + //'silver', + //'maroon' ); return $fontColorList; @@ -182,79 +179,76 @@ protected function getFontColorList() { */ public function getFontFamilyOptions() { $fontFamilyOptions = array( - - 'separator' => array( - 'text' => '', - 'command' => '', - 'value' => '', - 'class' => 'dd-separator', - 'html_tag' => 'div' - ), - - 'default' => array( - 'text' => 'Default font', - 'font-family' => "", - 'command' => 'fontfamily', - 'value' => 'default', - 'class' => 'post-fontfamily-default' - ), - - 'arial' => array( - 'text' => 'Arial', - 'font-family' => "Arial, 'Helvetica Neue', Helvetica, sans-serif", - 'command' => 'fontfamily', - 'value' => 'arial', - 'class' => 'post-fontfamily-arial' - ), - 'comicsansms' => array( - 'text' => 'Comic Sans MS', - 'font-family' => "'Comic Sans MS', cursive", - 'command' => 'fontfamily', - 'value' => 'comicsansms', - 'class' => 'post-fontfamily-comicsansms' - ), - 'couriernew' => array( - 'text' => 'Courier New', - 'font-family' => "'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace", - 'command' => 'fontfamily', - 'value' => 'couriernew', - 'class' => 'post-fontfamily-couriernew' - ), - 'georgia' => array( - 'text' => 'Georgia', - 'font-family' => "Georgia, Times, 'Times New Roman', serif", - 'command' => 'fontfamily', - 'value' => 'georgia', - 'class' => 'post-fontfamily-georgia' - ), - 'impact' => array( - 'text' => 'Impact', - 'font-family' => "Impact, Haettenschweiler, 'Franklin Gothic Bold', Charcoal, 'Helvetica Inserat', 'Bitstream Vera Sans Bold', 'Arial Black', sans-serif", - 'command' => 'fontfamily', - 'value' => 'impact', - 'class' => 'post-fontfamily-impact' - ), - 'timesnewroman' => array( - 'text' => 'Times New Roman', - 'font-family' => "'Times New Roman', Times, Baskerville, Georgia, serif", - 'command' => 'fontfamily', - 'value' => 'timesnewroman', - 'class' => 'post-fontfamily-timesnewroman' - ), - 'trebuchetms' => array( - 'text' => 'Trebuchet MS', - 'font-family' => "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif", - 'command' => 'fontfamily', - 'value' => 'trebuchetms', - 'class' => 'post-fontfamily-trebuchetms' - ), - 'verdana' => array( - 'text' => 'Verdana', - 'font-family' => "Verdana, Geneva, sans-serif", - 'command' => 'fontfamily', - 'value' => 'verdana', - 'class' => 'post-fontfamily-verdana' - ) + 'separator' => array( + 'text' => '', + 'command' => '', + 'value' => '', + 'class' => 'dd-separator', + 'html_tag' => 'div' + ), + 'default' => array( + 'text' => 'Default font', + 'font-family' => "", + 'command' => 'fontfamily', + 'value' => 'default', + 'class' => 'post-fontfamily-default' + ), + 'arial' => array( + 'text' => 'Arial', + 'font-family' => "Arial, 'Helvetica Neue', Helvetica, sans-serif", + 'command' => 'fontfamily', + 'value' => 'arial', + 'class' => 'post-fontfamily-arial' + ), + 'comicsansms' => array( + 'text' => 'Comic Sans MS', + 'font-family' => "'Comic Sans MS', cursive", + 'command' => 'fontfamily', + 'value' => 'comicsansms', + 'class' => 'post-fontfamily-comicsansms' + ), + 'couriernew' => array( + 'text' => 'Courier New', + 'font-family' => "'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace", + 'command' => 'fontfamily', + 'value' => 'couriernew', + 'class' => 'post-fontfamily-couriernew' + ), + 'georgia' => array( + 'text' => 'Georgia', + 'font-family' => "Georgia, Times, 'Times New Roman', serif", + 'command' => 'fontfamily', + 'value' => 'georgia', + 'class' => 'post-fontfamily-georgia' + ), + 'impact' => array( + 'text' => 'Impact', + 'font-family' => "Impact, Haettenschweiler, 'Franklin Gothic Bold', Charcoal, 'Helvetica Inserat', 'Bitstream Vera Sans Bold', 'Arial Black', sans-serif", + 'command' => 'fontfamily', + 'value' => 'impact', + 'class' => 'post-fontfamily-impact' + ), + 'timesnewroman' => array( + 'text' => 'Times New Roman', + 'font-family' => "'Times New Roman', Times, Baskerville, Georgia, serif", + 'command' => 'fontfamily', + 'value' => 'timesnewroman', + 'class' => 'post-fontfamily-timesnewroman' + ), + 'trebuchetms' => array( + 'text' => 'Trebuchet MS', + 'font-family' => "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif", + 'command' => 'fontfamily', + 'value' => 'trebuchetms', + 'class' => 'post-fontfamily-trebuchetms' + ), + 'verdana' => array( + 'text' => 'Verdana', + 'font-family' => "Verdana, Geneva, sans-serif", + 'command' => 'fontfamily', + 'value' => 'verdana', + 'class' => 'post-fontfamily-verdana' + ) ); return $fontFamilyOptions; @@ -276,64 +270,63 @@ public function getFontFamilyOptions() { * @return array */ protected function getFontFormatOptions() { - - // Stuff like 'heading1' is the editor-action. + // Stuff like 'heading1' is the editor-action. $fontFormatOptions = array( - 'heading1' => array( - 'text' => sprintf(t('Heading %s'), 1), - 'command' => 'formatBlock', - 'value' => 'h1', - 'class' => 'post-font-size-h1', - 'sort' => 100 - ), - 'heading2' => array( - 'text' => sprintf(t('Heading %s'), 2), - 'command' => 'formatBlock', - 'value' => 'h2', - 'class' => 'post-font-size-h2', - 'sort' => 99 - ), - 'separator' => array( - 'text' => '', - 'command' => '', - 'value' => '', - 'class' => 'dd-separator', - 'html_tag' => 'div', - 'sort' => 98 - ), - 'blockquote' => array( - 'text' => t('Quote'), - 'command' => 'blockquote', - 'value' => 'blockquote', - 'class' => '', - 'sort' => 10 - ), - 'code' => array( - 'text' => t('Source Code', 'Code'), - 'command' => 'code', - 'value' => 'code', - 'class' => '', - 'sort' => 9 - ), - 'spoiler' => array( - 'text' => t('Spoiler'), - 'command' => 'spoiler', - 'value' => 'spoiler', - 'class' => '', - 'sort' => 8 - ) + 'heading1' => array( + 'text' => sprintf(t('Heading %s'), 1), + 'command' => 'formatBlock', + 'value' => 'h1', + 'class' => 'post-font-size-h1', + 'sort' => 100 + ), + 'heading2' => array( + 'text' => sprintf(t('Heading %s'), 2), + 'command' => 'formatBlock', + 'value' => 'h2', + 'class' => 'post-font-size-h2', + 'sort' => 99 + ), + 'separator' => array( + 'text' => '', + 'command' => '', + 'value' => '', + 'class' => 'dd-separator', + 'html_tag' => 'div', + 'sort' => 98 + ), + 'blockquote' => array( + 'text' => t('Quote'), + 'command' => 'blockquote', + 'value' => 'blockquote', + 'class' => '', + 'sort' => 10 + ), + 'code' => array( + 'text' => t('Source Code', 'Code'), + 'command' => 'code', + 'value' => 'code', + 'class' => '', + 'sort' => 9 + ), + 'spoiler' => array( + 'text' => t('Spoiler'), + 'command' => 'spoiler', + 'value' => 'spoiler', + 'class' => '', + 'sort' => 8 + ) ); return $fontFormatOptions; } - /** - * Sort dropdown options by given weight. - * - * Currently this is only in use for the formatting options. - * - * @param array &$options Options to sort. - */ + /** + * Sort dropdown options by given weight. + * + * Currently this is only in use for the formatting options. + * + * @param array &$options Options to sort. + */ public function sortWeightedOptions(&$options) { if (is_array($options)) { uasort($options, function ($a, $b) { @@ -344,15 +337,14 @@ public function sortWeightedOptions(&$options) { } } - /** - * This method will grab the permissions array from getAllowedEditorActions, - * build the "kitchen sink" editor toolbar, then filter out the allowed - * ones and return it. - * - * @param array $editorToolbar Holds the final copy of allowed editor actions - * @param array $editorToolbarAll Holds the "kitchen sink" of editor actions - * @return array Returns the array of allowed editor toolbar actions - */ + /** + * This method will grab the permissions array from getAllowedEditorActions, + * build the "kitchen sink" editor toolbar, then filter out the allowed ones and return it. + * + * @param array $editorToolbar Holds the final copy of allowed editor actions + * @param array $editorToolbarAll Holds the "kitchen sink" of editor actions + * @return array Returns the array of allowed editor toolbar actions + */ protected function getEditorToolbar($attributes = array()) { $editorToolbar = array(); $editorToolbarAll = array(); @@ -366,29 +358,27 @@ protected function getEditorToolbar($attributes = array()) { $fontFormatOptions = $this->getFontFormatOptions(); $fontFamilyOptions = $this->getFontFamilyOptions(); - // Let plugins and themes override the defaults. + // Let plugins and themes override the defaults. $this->EventArguments['actions'] =& $allowedEditorActions; $this->EventArguments['colors'] =& $fontColorList; $this->EventArguments['format'] =& $fontFormatOptions; $this->EventArguments['font'] =& $fontFamilyOptions; $this->fireEvent('toolbarConfig'); - // Order the specified dropdowns. + // Order the specified dropdowns. $this->sortWeightedOptions($fontFormatOptions); - /** - * Build color dropdown from array - */ + // Build color dropdown from array $toolbarColorGroups = array(); $toolbarDropdownFontColor = array(); $toolbarDropdownFontColorHighlight = array(); foreach ($fontColorList as $fontColor) { - // Fore color + // Fore color $editorDataAttr = '{"action":"color","value":"'.$fontColor.'"}'; $toolbarDropdownFontColor[] = array('edit' => 'basic', 'action' => 'color', 'type' => 'button', 'html_tag' => 'span', 'attr' => array('class' => 'color cell-color-'.$fontColor.' editor-dialog-fire-close', 'data-wysihtml5-command' => 'foreColor', 'data-wysihtml5-command-value' => $fontColor, /*'title' => t($fontColor),*/ 'data-editor' => $editorDataAttr)); - // Highlight color + // Highlight color if ($fontColor == 'black') { $fontColor = 'white'; } @@ -402,13 +392,10 @@ protected function getEditorToolbar($attributes = array()) { $toolbarColorGroups['highlight'] = $toolbarDropdownFontColorHighlight; } - // Build formatting options + // Build formatting options $toolbarFormatOptions = array(); foreach ($fontFormatOptions as $editorAction => $actionValues) { - $htmlTag = (!empty($actionValues['html_tag'])) - ? $actionValues['html_tag'] - : 'a'; - + $htmlTag = (!empty($actionValues['html_tag'])) ? $actionValues['html_tag'] : 'a'; $toolbarFormatOptions[] = array( 'edit' => 'format', 'action' => $editorAction, @@ -425,20 +412,15 @@ protected function getEditorToolbar($attributes = array()) { ); } - /** - * Build emoji dropdown from array - * - * Using CSS background images instead of img tag, because CSS images - * do not download until actually displayed on page. display:none - * prevents browsers from loading the resources. - */ + // Build emoji dropdown from array. + // Using CSS background images instead of img tag, because CSS images + // do not download until actually displayed on page. display:none prevents browsers from loading the resources. $toolbarDropdownEmoji = array(); $emoji = Emoji::instance(); $emojiAliasList = $emoji->getEditorList(); foreach ($emojiAliasList as $emojiAlias => $emojiCanonical) { $emojiFilePath = $emoji->getEmojiPath($emojiCanonical); $editorDataAttr = '{"action":"emoji","value":'.json_encode($emojiAlias).'}'; - $toolbarDropdownEmoji[] = array( 'edit' => 'media', 'action' => 'emoji', @@ -453,50 +435,40 @@ protected function getEditorToolbar($attributes = array()) { 'data-editor' => $editorDataAttr)); } - // Font family options. + // Font family options. $toolbarFontFamilyOptions = array(); foreach ($fontFamilyOptions as $editorAction => $actionValues) { - $htmlTag = (!empty($actionValues['html_tag'])) - ? $actionValues['html_tag'] - : 'a'; - + $htmlTag = (!empty($actionValues['html_tag'])) ? $actionValues['html_tag'] : 'a'; $toolbarFontFamilyOptions[] = array( - 'edit' => 'fontfamily', - 'action' => $editorAction, - 'type' => 'button', - 'text' => $actionValues['text'], - 'html_tag' => $htmlTag, - 'attr' => array( - 'class' => "editor-action editor-action-{$editorAction} editor-dialog-fire-close {$actionValues['class']}", - 'data-wysihtml5-command' => $actionValues['command'], - 'data-wysihtml5-command-value' => $actionValues['value'], - 'title' => $actionValues['text'], - 'data-editor' => '{"action":"'.$actionValues['command'].'","value":"'.$actionValues['value'].'"}' - ) + 'edit' => 'fontfamily', + 'action' => $editorAction, + 'type' => 'button', + 'text' => $actionValues['text'], + 'html_tag' => $htmlTag, + 'attr' => array( + 'class' => "editor-action editor-action-{$editorAction} editor-dialog-fire-close {$actionValues['class']}", + 'data-wysihtml5-command' => $actionValues['command'], + 'data-wysihtml5-command-value' => $actionValues['value'], + 'title' => $actionValues['text'], + 'data-editor' => '{"action":"'.$actionValues['command'].'","value":"'.$actionValues['value'].'"}' + ) ); } - // If enabled, just merge with current formatting dropdown. + // If enabled, just merge with current formatting dropdown. if ($allowedEditorActions['fontfamily']) { $toolbarFormatOptions = array_merge($toolbarFormatOptions, $toolbarFontFamilyOptions); } - - /** - * Compile whole list of editor actions into single $editorToolbarAll - * array. Once complete, loop through allowedEditorActions and filter - * out the actions that will not be allowed. - * - * TODO this is ugly. Pop everything into array, and build this in a loop. - */ - + // Compile whole list of editor actions into single $editorToolbarAll array. + // Once complete, loop through allowedEditorActions and filter out the actions that will not be allowed. $editorToolbarAll['bold'] = array('edit' => 'basic', 'action' => 'bold', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-bold editor-dialog-fire-close', 'data-wysihtml5-command' => 'bold', 'title' => t('Bold'), 'data-editor' => '{"action":"bold","value":""}')); $editorToolbarAll['italic'] = array('edit' => 'basic', 'action' => 'italic', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-italic editor-dialog-fire-close', 'data-wysihtml5-command' => 'italic', 'title' => t('Italic'), 'data-editor' => '{"action":"italic","value":""}')); $editorToolbarAll['strike'] = array('edit' => 'basic', 'action' => 'strike', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-strikethrough editor-dialog-fire-close editor-optional-button', 'data-wysihtml5-command' => 'strikethrough', 'title' => t('Strikethrough'), 'data-editor' => '{"action":"strike","value":""}')); $editorToolbarAll['color'] = array('edit' => 'basic', 'action' => 'color', 'type' => - $toolbarColorGroups, - 'attr' => array('class' => 'editor-action icon icon-font editor-dd-color editor-optional-button', 'data-wysihtml5-command-group' => 'foreColor', 'title' => t('Color'), 'data-editor' => '{"action":"color","value":""}')); + $toolbarColorGroups, + 'attr' => array('class' => 'editor-action icon icon-font editor-dd-color editor-optional-button', 'data-wysihtml5-command-group' => 'foreColor', 'title' => t('Color'), 'data-editor' => '{"action":"color","value":""}')); $editorToolbarAll['orderedlist'] = array('edit' => 'format', 'action' => 'orderedlist', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-list-ol editor-dialog-fire-close editor-optional-button', 'data-wysihtml5-command' => 'insertOrderedList', 'title' => t('Ordered list'), 'data-editor' => '{"action":"orderedlist","value":""}')); $editorToolbarAll['unorderedlist'] = array('edit' => 'format', 'action' => 'unorderedlist', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-list-ul editor-dialog-fire-close editor-optional-button', 'data-wysihtml5-command' => 'insertUnorderedList', 'title' => t('Unordered list'), 'data-editor' => '{"action":"unorderedlist","value":""}')); @@ -505,8 +477,8 @@ protected function getEditorToolbar($attributes = array()) { $editorToolbarAll['sep-format'] = array('type' => 'separator', 'attr' => array('class' => 'editor-sep sep-headers editor-optional-button')); $editorToolbarAll['format'] = array('edit' => 'format', 'action' => 'headers', 'type' => - $toolbarFormatOptions, - 'attr' => array('class' => 'editor-action icon icon-paragraph editor-dd-format', 'title' => t('Format'), 'data-editor' => '{"action":"format","value":""}')); + $toolbarFormatOptions, + 'attr' => array('class' => 'editor-action icon icon-paragraph editor-dd-format', 'title' => t('Format'), 'data-editor' => '{"action":"format","value":""}')); $editorToolbarAll['sep-media'] = array('type' => 'separator', 'attr' => array('class' => 'editor-sep sep-media editor-optional-button')); $editorToolbarAll['emoji'] = array('edit' => 'media', 'action' => 'emoji', 'type' => $toolbarDropdownEmoji, 'attr' => array('class' => 'editor-action icon icon-smile editor-dd-emoji', 'data-wysihtml5-command' => '', 'title' => t('Emoji'), 'data-editor' => '{"action":"emoji","value":""}')); @@ -525,7 +497,7 @@ protected function getEditorToolbar($attributes = array()) { $editorToolbarAll['fullpage'] = array('edit' => 'switches', 'action' => 'fullpage', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-resize-full editor-toggle-fullpage-button editor-dialog-fire-close editor-optional-button', 'title' => t('Toggle full page'), 'data-editor' => '{"action":"fullpage","value":""}')); $editorToolbarAll['lights'] = array('edit' => 'switches', 'action' => 'lights', 'type' => 'button', 'attr' => array('class' => 'editor-action icon icon-adjust editor-toggle-lights-button editor-dialog-fire-close editor-optional-button', 'title' => t('Toggle lights'), 'data-editor' => '{"action":"lights","value":""}')); - // Filter out disallowed editor actions + // Filter out disallowed editor actions foreach ($allowedEditorActions as $editorAction => $allowed) { if ($allowed && isset($editorToolbarAll[$editorAction])) { $editorToolbar[$editorAction] = $editorToolbarAll[$editorAction]; @@ -535,16 +507,9 @@ protected function getEditorToolbar($attributes = array()) { return $editorToolbar; } - - /** - * - * Vanilla event handlers - * - */ - - /** - * Load CSS into head for editor - */ + /** + * Load CSS into head for editor + */ public function assetModel_styleCss_handler($Sender) { $Sender->addCssFile('vanillicon.css', 'static'); $Sender->addCssFile('editor.css', 'plugins/editor'); @@ -589,33 +554,31 @@ public function isEmbeddedComment($Sender) { * editor in some areas where the values were not yet injected into HTML. */ public function base_render_before(&$Sender) { - - // Don't render any assets for editor if it's embedded. This effectively - // disables the editor from embedded comments. Some HTML is still - // inserted, because of the BeforeBodyBox handler, which does not contain - // any data relating to embedded content. + // Don't render any assets for editor if it's embedded. This effectively + // disables the editor from embedded comments. Some HTML is still + // inserted, because of the BeforeBodyBox handler, which does not contain any data relating to embedded content. if ($this->isEmbeddedComment($Sender)) { return false; } $c = Gdn::controller(); - // If user wants to modify styling of Wysiwyg content in editor, - // they can override the styles with this file. + // If user wants to modify styling of Wysiwyg content in editor, + // they can override the styles with this file. $CssInfo = AssetModel::cssPath('wysiwyg.css', 'plugins/editor'); if ($CssInfo) { $CssPath = Asset($CssInfo[1]); } - // Load JavaScript used by every editor view. + // Load JavaScript used by every editor view. $c->addJsFile('editor.js', 'plugins/editor'); - // Fileuploads + // Fileuploads $c->addJsFile('jquery.ui.widget.js', 'plugins/editor'); $c->addJsFile('jquery.iframe-transport.js', 'plugins/editor'); $c->addJsFile('jquery.fileupload.js', 'plugins/editor'); - // Set definitions for JavaScript to read + // Set definitions for JavaScript to read $c->addDefinition('editorVersion', $this->pluginInfo['Version']); $c->addDefinition('editorInputFormat', $this->Format); $c->addDefinition('editorPluginAssets', $this->AssetPath); @@ -626,53 +589,50 @@ public function base_render_before(&$Sender) { $c->addDefinition('textHelpText', t('editor.TextHelpText', 'You are using plain text in your post.')); $c->addDefinition('editorWysiwygCSS', $CssPath); - // Set variables for file uploads + // Set variables for file uploads $PostMaxSize = Gdn_Upload::unformatFileSize(ini_get('post_max_size')); $FileMaxSize = Gdn_Upload::unformatFileSize(ini_get('upload_max_filesize')); $ConfigMaxSize = Gdn_Upload::unformatFileSize(c('Garden.Upload.MaxFileSize', '1MB')); $MaxSize = min($PostMaxSize, $FileMaxSize, $ConfigMaxSize); $c->addDefinition('maxUploadSize', $MaxSize); - // Set file input name + // Set file input name $c->addDefinition('editorFileInputName', $this->editorFileInputName); $Sender->setData('_editorFileInputName', $this->editorFileInputName); - // Save allowed file types + // Save allowed file types $c->addDefinition('allowedFileExtensions', json_encode(c('Garden.Upload.AllowedFileExtensions'))); - // Get max file uploads, to be used for max drops at once. + // Get max file uploads, to be used for max drops at once. $c->addDefinition('maxFileUploads', ini_get('max_file_uploads')); - // Set canUpload definition here, but not Data (set in BeforeBodyBox) because it overwrites. + // Set canUpload definition here, but not Data (set in BeforeBodyBox) because it overwrites. $c->addDefinition('canUpload', $this->canUpload); } /** - * Attach editor anywhere 'BodyBox' is used. It is not being used for - * editing a posted reply, so find another event to hook into. + * Attach editor anywhere 'BodyBox' is used. + * + * It is not being used for editing a posted reply, so find another event to hook into. * * @param Gdn_Form $Sender */ public function gdn_form_beforeBodyBox_handler($Sender, $Args) { - // TODO have some way to prevent this content from getting loaded - // when in embedded. The only problem is figuring out how to know when - // content is embedded. + // TODO have some way to prevent this content from getting loaded when in embedded. + // The only problem is figuring out how to know when content is embedded. $attributes = array(); if (val('Attributes', $Args)) { $attributes = val('Attributes', $Args); } - // TODO move this property to constructor + // TODO move this property to constructor $this->Format = $Sender->getValue('Format'); - // Make sure we have some sort of format. + // Make sure we have some sort of format. if (!$this->Format) { $this->Format = c('Garden.InputFormatter', 'Html'); $Sender->setValue('Format', $this->Format); } - // If force Wysiwyg enabled in settings - if (c('Garden.InputFormatter', 'Wysiwyg') == 'Wysiwyg' - //&& strcasecmp($this->Format, 'wysiwyg') != 0 - && $this->ForceWysiwyg == true - ) { + // If force Wysiwyg enabled in settings + if (c('Garden.InputFormatter', 'Wysiwyg') == 'Wysiwyg' && $this->ForceWysiwyg == true) { $wysiwygBody = Gdn_Format::to($Sender->getValue('Body'), $this->Format); $Sender->setValue('Body', $wysiwygBody); @@ -683,13 +643,10 @@ public function gdn_form_beforeBodyBox_handler($Sender, $Args) { if (in_array(strtolower($this->Format), array_map('strtolower', $this->Formats))) { $c = Gdn::controller(); - // Set minor data for view + // Set minor data for view $c->setData('_EditorInputFormat', $this->Format); - /** - * Get the generated editor toolbar from getEditorToolbar, and assign - * it data object for view. - */ + // Get the generated editor toolbar from getEditorToolbar, and assign it data object for view. if (!isset($c->Data['_EditorToolbar'])) { $editorToolbar = $this->getEditorToolbar($attributes); $this->EventArguments['EditorToolbar'] =& $editorToolbar; @@ -702,9 +659,9 @@ public function gdn_form_beforeBodyBox_handler($Sender, $Args) { $c->addDefinition('canUpload', $this->canUpload); $c->setData('_canUpload', $this->canUpload); - // Determine which controller (post or discussion) is invoking this. - // At the moment they're both the same, but in future you may want - // to know this information to modify it accordingly. + // Determine which controller (post or discussion) is invoking this. + // At the moment they're both the same, but in future you may want + // to know this information to modify it accordingly. $View = $c->fetchView('editor', '', 'plugins/editor'); $Args['BodyBox'] .= $View; @@ -712,45 +669,43 @@ public function gdn_form_beforeBodyBox_handler($Sender, $Args) { } /** + * * * @param PostController $Sender * @param array $Args */ public function postController_editorUpload_create($Sender, $Args = array()) { - - // Require new image thumbnail generator function. Currently it's - // being symlinked from my vhosts/tests directory. When it makes it - // into core, it will be available in functions.general.php + // Require new image thumbnail generator function. Currently it's + // being symlinked from my vhosts/tests directory. When it makes it + // into core, it will be available in functions.general.php require 'generate_thumbnail.php'; - // Grab raw upload data ($_FILES), essentially. It's only needed - // because the methods on the Upload class do not expose all variables. + // Grab raw upload data ($_FILES), essentially. It's only needed + // because the methods on the Upload class do not expose all variables. $fileData = Gdn::request()->getValueFrom(Gdn_Request::INPUT_FILES, $this->editorFileInputName, false); - $discussionID = ($Sender->Request->post('DiscussionID')) - ? $Sender->Request->post('DiscussionID') - : ''; + $discussionID = ($Sender->Request->post('DiscussionID')) ? $Sender->Request->post('DiscussionID') : ''; - // JSON payload of media info will get sent back to the client. + // JSON payload of media info will get sent back to the client. $json = array( - 'error' => 1, - 'feedback' => 'There was a problem.', - 'errors' => array(), - 'payload' => array() + 'error' => 1, + 'feedback' => 'There was a problem.', + 'errors' => array(), + 'payload' => array() ); - // New upload instance + // New upload instance $Upload = new Gdn_Upload(); - // This will validate, such as size maxes, file extensions. Upon doing - // this, $_FILES is set as a protected property, so all the other - // Gdn_Upload methods work on it. + // This will validate, such as size maxes, file extensions. Upon doing + // this, $_FILES is set as a protected property, so all the other + // Gdn_Upload methods work on it. $tmpFilePath = $Upload->validateUpload($this->editorFileInputName); - // Get base destination path for editor uploads + // Get base destination path for editor uploads $this->editorBaseUploadDestinationDir = $this->getBaseUploadDestinationDir(); - // Pass path, if doesn't exist, will create, and determine if valid. + // Pass path, if doesn't exist, will create, and determine if valid. $canUpload = Gdn_Upload::canUpload($this->editorBaseUploadDestinationDir); if ($tmpFilePath && $canUpload) { @@ -758,18 +713,16 @@ public function postController_editorUpload_create($Sender, $Args = array()) { $fileName = $Upload->getUploadedFileName(); list($tmpwidth, $tmpheight, $imageType) = getimagesize($tmpFilePath); - // This will return the absolute destination path, including generated - // filename based on md5_file, and the full path. It - // will create a filename, with extension, and check if its dir can - // be writable. + // This will return the absolute destination path, including generated + // filename based on md5_file, and the full path. It + // will create a filename, with extension, and check if its dir can be writable. $absoluteFileDestination = $this->getAbsoluteDestinationFilePath($tmpFilePath, $fileExtension); // This is returned by SaveAs //$filePathparsed = Gdn_Upload::Parse($absoluteFileDestination); // Save original file to uploads, then manipulate from this location if - // it's a photo. This will also call events in Vanilla so other - // plugins can tie into this. + // it's a photo. This will also call events in Vanilla so other plugins can tie into this. if (empty($imageType)) { $filePathParsed = $Upload->saveAs($tmpFilePath, $absoluteFileDestination, array('source' => 'content')); } else { @@ -778,10 +731,9 @@ public function postController_editorUpload_create($Sender, $Args = array()) { $tmpheight = $filePathParsed['Height']; } - // Determine if image, and thus requires thumbnail generation, or - // simply saving the file. + // Determine if image, and thus requires thumbnail generation, or simply saving the file. - // Not all files will be images. + // Not all files will be images. $thumbHeight = ''; $thumbWidth = ''; $imageHeight = ''; @@ -789,9 +741,9 @@ public function postController_editorUpload_create($Sender, $Args = array()) { $thumbPathParsed = array('SaveName' => ''); $thumbUrl = ''; - // This is a redundant check, because it's in the thumbnail function, - // but there's no point calling it blindly on every file, so just - // check here before calling it. + // This is a redundant check, because it's in the thumbnail function, + // but there's no point calling it blindly on every file, so just + // check here before calling it. $generate_thumbnail = false; if (in_array($fileExtension, array('jpg', 'jpeg', 'gif', 'png', 'bmp', 'ico'))) { $imageHeight = $tmpheight; @@ -799,64 +751,58 @@ public function postController_editorUpload_create($Sender, $Args = array()) { $generate_thumbnail = true; } - // Save data to database using model with media table + // Save data to database using model with media table $Model = new Gdn_Model('Media'); - // Will be passed to model for database insertion/update. - // All thumb vars will be empty. + // Will be passed to model for database insertion/update. + // All thumb vars will be empty. $Media = array( - 'Name' => $fileName, - 'Type' => $fileData['type'], - 'Size' => $fileData['size'], - 'ImageWidth' => $imageWidth, - 'ImageHeight' => $imageHeight, - 'ThumbWidth' => $thumbWidth, - 'ThumbHeight' => $thumbHeight, - 'InsertUserID' => Gdn::session()->UserID, - 'DateInserted' => date('Y-m-d H:i:s'), - 'StorageMethod' => 'local', - 'Path' => $filePathParsed['SaveName'], - 'ThumbPath' => $thumbPathParsed['SaveName'] + 'Name' => $fileName, + 'Type' => $fileData['type'], + 'Size' => $fileData['size'], + 'ImageWidth' => $imageWidth, + 'ImageHeight' => $imageHeight, + 'ThumbWidth' => $thumbWidth, + 'ThumbHeight' => $thumbHeight, + 'InsertUserID' => Gdn::session()->UserID, + 'DateInserted' => date('Y-m-d H:i:s'), + 'StorageMethod' => 'local', + 'Path' => $filePathParsed['SaveName'], + 'ThumbPath' => $thumbPathParsed['SaveName'] ); - // Get MediaID and pass it to client in payload + // Get MediaID and pass it to client in payload $MediaID = $Model->save($Media); $Media['MediaID'] = $MediaID; - // Clear Media cache for discussion, if any. - /*if ($discussionID) { - $cacheKey = sprintf(self::DISCUSSION_MEDIA_CACHE_KEY, $discussionID); - Gdn::cache()->Remove($cacheKey); - }*/ - if ($generate_thumbnail) { $thumbUrl = url('/utility/mediathumbnail/'.$MediaID, true); } $payload = array( - 'MediaID' => $MediaID, - 'Filename' => htmlspecialchars($fileName), - 'Filesize' => $fileData['size'], - 'FormatFilesize' => Gdn_Format::bytes($fileData['size'], 1), - 'type' => $fileData['type'], - 'Thumbnail' => '', - 'FinalImageLocation' => '', - 'Parsed' => $filePathParsed, - 'Media' => (array)$Media, - 'original_url' => $Upload->url($filePathParsed['SaveName']), - 'thumbnail_url' => $thumbUrl, - 'original_width' => $imageWidth, - 'original_height' => $imageHeight + 'MediaID' => $MediaID, + 'Filename' => htmlspecialchars($fileName), + 'Filesize' => $fileData['size'], + 'FormatFilesize' => Gdn_Format::bytes($fileData['size'], 1), + 'type' => $fileData['type'], + 'Thumbnail' => '', + 'FinalImageLocation' => '', + 'Parsed' => $filePathParsed, + 'Media' => (array)$Media, + 'original_url' => $Upload->url($filePathParsed['SaveName']), + 'thumbnail_url' => $thumbUrl, + 'original_width' => $imageWidth, + 'original_height' => $imageHeight ); $json = array( - 'error' => 0, - 'feedback' => 'Editor received file successfully.', - 'payload' => $payload + 'error' => 0, + 'feedback' => 'Editor received file successfully.', + 'payload' => $payload ); } - // Return JSON payload + // Return JSON payload echo json_encode($json); } @@ -871,8 +817,7 @@ public function postController_editorUpload_create($Sender, $Args = array()) { * @return bool Whether attach was successful. */ protected function attachEditorUploads($FileID, $ForeignID, $ForeignType) { - - // Save data to database using model with media table + // Save data to database using model with media table $Model = new Gdn_Model('Media'); $Media = $Model->getID($FileID); @@ -900,14 +845,13 @@ protected function attachEditorUploads($FileID, $ForeignID, $ForeignType) { * @return boolean */ protected function deleteEditorUploads($MediaID, $ForeignID = '', $ForeignType = '') { - - // Save data to database using model with media table + // Save data to database using model with media table $Model = new Gdn_Model('Media'); $Media = (array)$Model->getID($MediaID); $IsOwner = (!empty($Media['InsertUserID']) && Gdn::session()->UserID == $Media['InsertUserID']); - // @todo Per-category edit permission would be better, but this global is far simpler to check here. - // However, this currently matches the permission check in views/attachments.php so keep that in sync. + // @todo Per-category edit permission would be better, but this global is far simpler to check here. + // However, this currently matches the permission check in views/attachments.php so keep that in sync. $CanDelete = ($IsOwner || Gdn::session()->checkPermission('Garden.Moderation.Manage')); if ($Media && $CanDelete) { try { @@ -923,22 +867,6 @@ protected function deleteEditorUploads($MediaID, $ForeignID = '', $ForeignType = if (file_exists($thumbPath)) { unlink($thumbPath); } - - // Clear the cache, if exists. - /*$discussionID = ''; - if ($Media['ForeignTable'] == 'discussion') { - $discussionID = $Media['ForeignID']; - } elseif ($Media['ForeignTable'] == 'comment') { - $commentModel = new CommentModel(); - $commentRow = $commentModel->getID($Media['ForeignID'], DATASET_TYPE_ARRAY); - if ($commentRow) { - $discussionID = $commentRow['DiscussionID']; - } - } - if ($discussionID) { - $cacheKey = sprintf(self::DISCUSSION_MEDIA_CACHE_KEY, $discussionID); - Gdn::cache()->Remove($cacheKey); - }*/ } } catch (Exception $e) { die($e->getMessage()); @@ -956,8 +884,7 @@ protected function deleteEditorUploads($MediaID, $ForeignID = '', $ForeignType = * @param $type */ public function saveUploads($id, $type) { - - // Array of Media IDs, as input is MediaIDs[] + // Array of Media IDs, as input is MediaIDs[] $mediaIds = (array)Gdn::request()->getValue('MediaIDs'); if (count($mediaIds)) { @@ -966,9 +893,9 @@ public function saveUploads($id, $type) { } } - // Array of Media IDs to remove, if any. + // Array of Media IDs to remove, if any. $removeMediaIds = (array)Gdn::request()->getValue('RemoveMediaIDs'); - // Clean it if it's empty. + // Clean it if it's empty. $removeMediaIds = array_filter($removeMediaIds); if (count($removeMediaIds)) { @@ -1059,8 +986,9 @@ public function messagesController_afterConversationSave_handler($Sender, $Args) } /** - * Attach image to each discussion or comment. It will first perform a - * single request against the Media table, then filter out the ones that + * Attach image to each discussion or comment. + * + * It will first perform a single request against the Media table, then filter out the ones that * exist per discussion or comment. * * @param multiple $Controller The controller. @@ -1068,15 +996,14 @@ public function messagesController_afterConversationSave_handler($Sender, $Args) * @param array|object $row The row of data being attached to. */ protected function attachUploadsToComment($Sender, $Type = 'comment', $row = null) { - $param = ucfirst($Type).'ID'; $foreignId = val($param, val(ucfirst($Type), $Sender->EventArguments)); - // Get all media for the page. + // Get all media for the page. $mediaList = $this->mediaCache($Sender); if (is_array($mediaList)) { - // Filter out the ones that don't match. + // Filter out the ones that don't match. $attachments = array_filter($mediaList, function ($attachment) use ($foreignId, $Type) { if (isset($attachment['ForeignID']) && $attachment['ForeignID'] == $foreignId @@ -1117,15 +1044,10 @@ protected function getConversationMessageIDList($id) { $Conversations = array(); $ConversationMessageModel = new Gdn_Model('ConversationMessage'); - // Query the Media table for discussion media. + // Query the Media table for discussion media. if (is_numeric($id)) { - $sqlWhere = array( - 'ConversationID' => $id - ); - - $Conversations = $ConversationMessageModel->getWhere( - $sqlWhere - )->resultArray(); + $sqlWhere = array('ConversationID' => $id); + $Conversations = $ConversationMessageModel->getWhere($sqlWhere)->resultArray(); } $MessageIDList = array(); @@ -1136,9 +1058,9 @@ protected function getConversationMessageIDList($id) { } /** - * Called to prepare data grab, and then cache the results on the software - * level for the request. This will call PreloadDiscussionMedia, which - * will either query the db, or query memcached. + * Called to prepare data grab, and then cache the results on the software level for the request. + * + * This will call PreloadDiscussionMedia, which will either query the db, or query memcached. * * @param mixed $Sender */ @@ -1190,20 +1112,17 @@ protected function cacheAttachedMedia($Sender) { $CommentIDList[] = $Sender->Comment->CommentID; } - // TODO - // Added note for caching here because it was the CommentIDList that - // is the main problem. - // Note about memcaching: - // Main problem with this is when a new comment is posted. It will only - // have that current comment in the list, which, after calling - // PreloadDiscussionMedia, means it will be the only piece of data added - // to the cache, which prevents all the rest of the comments from loading - // their own attachments. Consider either adding to the cache when a new - // file is uploaded, or just getting a list of all comments for a - // discussion. - // This is why memcaching has been disabled for now. There are a couple - // ways to prevent this, but they all seem unnecessary. - + // TODO + // Added note for caching here because it was the CommentIDList that is the main problem. + // Note about memcaching: + // Main problem with this is when a new comment is posted. It will only + // have that current comment in the list, which, after calling + // PreloadDiscussionMedia, means it will be the only piece of data added + // to the cache, which prevents all the rest of the comments from loading + // their own attachments. Consider either adding to the cache when a new + // file is uploaded, or just getting a list of all comments for a discussion. + // This is why memcaching has been disabled for now. There are a couple + // ways to prevent this, but they all seem unnecessary. if (count($CommentIDList)) { $MediaData = $this->preloadDiscussionMedia($DiscussionID, $CommentIDList); } @@ -1235,28 +1154,20 @@ public function preloadDiscussionMedia($discussionID, $commentIDList, $type = 'd $mediaData = array(); $mediaDataDiscussion = array(); $mediaDataComment = array(); - - /*$cacheKey = sprintf(self::DISCUSSION_MEDIA_CACHE_KEY, $discussionID); - $cacheResponse = Gdn::cache()->get($cacheKey); - if ($cacheResponse === Gdn_Cache::CACHEOP_FAILURE) {*/ $mediaModel = new Gdn_Model('Media'); - // Query the Media table for discussion media. + // Query the Media table for discussion media. if ($type === 'discussion') { if (is_numeric($discussionID)) { $sqlWhere = array( - 'ForeignTable' => 'discussion', - 'ForeignID' => $discussionID + 'ForeignTable' => 'discussion', + 'ForeignID' => $discussionID ); - - $mediaDataDiscussion = $mediaModel->getWhere( - $sqlWhere - )->resultArray(); + $mediaDataDiscussion = $mediaModel->getWhere($sqlWhere)->resultArray(); } } - // Query the Media table for comment media. - + // Query the Media table for comment media. if (is_numeric($commentIDList)) { $commentIDList[] = $commentIDList; } @@ -1265,23 +1176,13 @@ public function preloadDiscussionMedia($discussionID, $commentIDList, $type = 'd $commentIDList = array_filter($commentIDList); $sqlWhere = array( - 'ForeignTable' => ($type == 'discussion') ? 'comment' : 'message', - 'ForeignID' => $commentIDList + 'ForeignTable' => ($type == 'discussion') ? 'comment' : 'message', + 'ForeignID' => $commentIDList ); - - $mediaDataComment = $mediaModel->getWhere( - $sqlWhere - )->resultArray(); + $mediaDataComment = $mediaModel->getWhere($sqlWhere)->resultArray(); } $mediaData = array_merge($mediaDataDiscussion, $mediaDataComment); - /* - Gdn::cache()->store($cacheKey, $mediaData, array( - Gdn_Cache::FEATURE_EXPIRY => $this->mediaCacheExpire - )); - } else { - $mediaData = $cacheResponse; - }*/ return $mediaData; } @@ -1333,25 +1234,22 @@ public function getBaseUploadDestinationDir($subdir = false) { */ public function getAbsoluteDestinationFilePath($tmpFilePath, $fileExtension, $uploadDestinationDir = '') { $absolutePath = ''; - $basePath = $this->editorBaseUploadDestinationDir; if ($basePath != '') { $basePath = $this->getBaseUploadDestinationDir(); } - if ($uploadDestinationDir) { $basePath = $uploadDestinationDir; } - // SHA1 of the tmp file - //$fileSHA1 = sha1_file($tmpFilePath); - // Instead just use the RandomString function that - // Gdn_Upload->GenerateTargetName is using. + // SHA1 of the tmp file + // $fileSHA1 = sha1_file($tmpFilePath); + // Instead just use the RandomString function that Gdn_Upload->GenerateTargetName is using. $fileRandomString = strtolower(RandomString(14)); - // Use first two characters from fileMD5 as subdirectory, - // and use the rest as the file name. + // Use first two characters from fileMD5 as subdirectory, + // and use the rest as the file name. $dirlen = 2; $subdir = substr($fileRandomString, 0, $dirlen); $filename = substr($fileRandomString, $dirlen); @@ -1377,11 +1275,8 @@ public function getAbsoluteDestinationFilePath($tmpFilePath, $fileExtension, $up public function validateUploadDestinationPath($path) { $validDestination = true; - // Check if path exists, and if not, create it. - if (!file_exists($path) - && !mkdir($path, 0777, true) - && !is_writable($path) - ) { + // Check if path exists, and if not, create it. + if (!file_exists($path) && !mkdir($path, 0777, true) && !is_writable($path)) { $validDestination = false; } @@ -1409,12 +1304,12 @@ public function settingsController_editor_create($Sender, $Args) { $Formats = array_combine($this->Formats, $this->Formats); $Cf->initialize(array( - 'Garden.InputFormatter' => array('LabelCode' => 'Post Format', 'Control' => 'DropDown', 'Description' => '

Select the default format of the editor for posts in the community.

Note: the editor will auto-detect the format of old posts when editing them and load their original formatting rules. Aside from this exception, the selected post format below will take precedence.

', 'Items' => $Formats), - 'Plugins.editor.ForceWysiwyg' => array('LabelCode' => 'Reinterpret All Posts As Wysiwyg', 'Control' => 'Checkbox', 'Description' => '

Check the below option to tell the editor to reinterpret all old posts as Wysiwyg.

Note: This setting will only take effect if Wysiwyg was chosen as the Post Format above. The purpose of this option is to normalize the editor format. If older posts edited with another format, such as markdown or BBCode, are loaded, this option will force Wysiwyg.

'), - 'Garden.MobileInputFormatter' => array('LabelCode' => 'Mobile Format', 'Control' => 'DropDown', 'Description' => '

Specify an editing format for mobile devices. If mobile devices should have the same experience, specify the same one as above. If users report issues with mobile editing, this is a good option to change.

', 'Items' => $Formats, 'DefaultValue' => c('Garden.MobileInputFormatter')) + 'Garden.InputFormatter' => array('LabelCode' => 'Post Format', 'Control' => 'DropDown', 'Description' => '

Select the default format of the editor for posts in the community.

Note: the editor will auto-detect the format of old posts when editing them and load their original formatting rules. Aside from this exception, the selected post format below will take precedence.

', 'Items' => $Formats), + 'Plugins.editor.ForceWysiwyg' => array('LabelCode' => 'Reinterpret All Posts As Wysiwyg', 'Control' => 'Checkbox', 'Description' => '

Check the below option to tell the editor to reinterpret all old posts as Wysiwyg.

Note: This setting will only take effect if Wysiwyg was chosen as the Post Format above. The purpose of this option is to normalize the editor format. If older posts edited with another format, such as markdown or BBCode, are loaded, this option will force Wysiwyg.

'), + 'Garden.MobileInputFormatter' => array('LabelCode' => 'Mobile Format', 'Control' => 'DropDown', 'Description' => '

Specify an editing format for mobile devices. If mobile devices should have the same experience, specify the same one as above. If users report issues with mobile editing, this is a good option to change.

', 'Items' => $Formats, 'DefaultValue' => c('Garden.MobileInputFormatter')) )); - // Add some JS and CSS to blur out option when Wysiwyg not chosen. + // Add some JS and CSS to blur out option when Wysiwyg not chosen. $c = Gdn::controller(); $c->addJsFile('settings.js', 'plugins/editor'); $Sender->addCssFile('settings.css', 'plugins/editor'); @@ -1422,41 +1317,29 @@ public function settingsController_editor_create($Sender, $Args) { $Sender->addSideMenu(); $Sender->setData('Title', t('Advanced Editor Settings')); $Cf->renderAll(); - //$Sender->Cf = $Cf; - //$Sender->render('settings', '', 'plugins/editor'); } - /* - public function base_GetAppSettingsMenuItems_handler($Sender) { - $Menu = $Sender->EventArguments['SideMenu']; - $Menu->AddItem('Appearance', t('Appearance')); - $Menu->addLink('Appearance', 'Advanced Editor', 'settings/editor', 'Garden.Settings.Manage'); - } - */ - /** - * Every time editor plugin is enabled, disable other known editors that - * may clash with this one. If editor is loaded, then thes other - * editors loaded after, there are CSS rules that hide them. This way, - * the editor plugin always takes precedence. + * When enabled, disable other known editors that may clash with this one. + * + * If editor is loaded, then the other editors loaded after, there are CSS rules that hide them. + * This way, the editor plugin always takes precedence. */ public function setup() { $pluginEditors = array( - 'cleditor', - 'ButtonBar', - 'Emotify', - 'FileUpload' + 'cleditor', + 'ButtonBar', + 'Emotify', + 'FileUpload' ); foreach ($pluginEditors as $pluginName) { Gdn::pluginManager()->disablePlugin($pluginName); } - touchConfig(array( - 'Garden.MobileInputFormatter' => 'TextEx', - 'Plugins.editor.ForceWysiwyg' => false + 'Garden.MobileInputFormatter' => 'TextEx', + 'Plugins.editor.ForceWysiwyg' => false )); - $this->structure(); } @@ -1466,30 +1349,22 @@ public function setup() { * @throws Exception */ public function structure() { - // Set to false by default, so change in config if uploads allowed. + // Set to false by default, so change in config if uploads allowed. touchConfig('Garden.AllowFileUploads', true); $Structure = Gdn::structure(); $Structure - ->table('Category') - ->column('AllowFileUploads', 'tinyint(1)', '1') - ->set(); - } - - public function onDisable() { - //RemoveFromConfig('Plugin.editor.DefaultView'); - } - - public function cleanUp() { - //RemoveFromConfig('Plugin.editor.DefaultView'); + ->table('Category') + ->column('AllowFileUploads', 'tinyint(1)', '1') + ->set(); } /** * Create and display a thumbnail of an uploaded file. */ public function utilityController_mediaThumbnail_create($sender, $media_id) { - // When it makes it into core, it will be available in - // functions.general.php + // When it makes it into core, it will be available in + // functions.general.php require 'generate_thumbnail.php'; $model = new Gdn_Model('Media'); @@ -1499,7 +1374,7 @@ public function utilityController_mediaThumbnail_create($sender, $media_id) { throw notFoundException('File'); } - // Get actual path to the file. + // Get actual path to the file. $local_path = Gdn_Upload::copyLocal($media['Path']); if (!file_exists($local_path)) { throw notFoundException('File'); @@ -1507,43 +1382,41 @@ public function utilityController_mediaThumbnail_create($sender, $media_id) { $file_extension = pathinfo($local_path, PATHINFO_EXTENSION); - // Generate new path for thumbnail + // Generate new path for thumbnail $thumb_path = $this->getBaseUploadDestinationDir().'/'.'thumb'; - // Grab full path with filename, and validate it. + // Grab full path with filename, and validate it. $thumb_destination_path = $this->getAbsoluteDestinationFilePath($local_path, $file_extension, $thumb_path); - // Create thumbnail, and grab debug data from whole process. + // Create thumbnail, and grab debug data from whole process. $thumb_payload = generate_thumbnail($local_path, $thumb_destination_path, array( - // Give preference to height for thumbnail, so height controls. - 'height' => c('Plugins.FileUpload.ThumbnailHeight', 128) + // Give preference to height for thumbnail, so height controls. + 'height' => c('Plugins.FileUpload.ThumbnailHeight', 128) )); if ($thumb_payload['success'] === true) { - // Thumbnail dimensions + // Thumbnail dimensions $thumb_height = round($thumb_payload['result_height']); $thumb_width = round($thumb_payload['result_width']); - // Move the thumbnail to its proper location. Calling SaveAs with - // cloudfiles enabled will trigger the move to cloudfiles, so use - // same path for each arg in SaveAs. The file will be removed from - // the local filesystem. + // Move the thumbnail to its proper location. Calling SaveAs with + // cloudfiles enabled will trigger the move to cloudfiles, so use + // same path for each arg in SaveAs. The file will be removed from the local filesystem. $parsed = Gdn_Upload::parse($thumb_destination_path); $target = $thumb_destination_path; // $parsed['Name']; $Upload = new Gdn_Upload(); $filepath_parsed = $Upload->saveAs($thumb_destination_path, $target, array('source' => 'content')); - // Save thumbnail information to DB. + // Save thumbnail information to DB. $model->save(array( - 'MediaID' => $media_id, - 'StorageMethod' => $filepath_parsed['Type'], - 'ThumbWidth' => $thumb_width, - 'ThumbHeight' => $thumb_height, - 'ThumbPath' => $filepath_parsed['SaveName'] + 'MediaID' => $media_id, + 'StorageMethod' => $filepath_parsed['Type'], + 'ThumbWidth' => $thumb_width, + 'ThumbHeight' => $thumb_height, + 'ThumbPath' => $filepath_parsed['SaveName'] )); - // Remove cf scratch copy, typically in cftemp, if there was actually - // a file pulled in from CF. + // Remove cf scratch copy, typically in cftemp, if there was actually a file pulled in from CF. if (strpos($local_path, 'cftemp') !== false) { if (!unlink($local_path)) { // Maybe add logging for local cf copies not deleted. @@ -1552,7 +1425,7 @@ public function utilityController_mediaThumbnail_create($sender, $media_id) { $url = $filepath_parsed['Url']; } else { - // Fix the thumbnail information so this isn't requested again and again. + // Fix the thumbnail information so this isn't requested again and again. $model->save(array( 'MediaID' => $media_id, 'ImageWidth' => 0, @@ -1566,21 +1439,29 @@ public function utilityController_mediaThumbnail_create($sender, $media_id) { redirect($url, 301); } - // Copy the Spoilers plugin functionality into the editor so that plugin - // can be deprecated without introducing compatibility issues on forums - // that make heavy use of the spoilers plugin, as well as users who have - // become accustom to its [spoiler][/spoiler] syntax. This will also allow - // the spoiler styling and experience to standardize, instead of using - // two distinct styles and experiences. + /** + * Copy the Spoilers plugin functionality into the editor. + * + * Then plugins can be deprecated without introducing compatibility issues on forums + * that make heavy use of the spoilers plugin, as well as users who have + * become accustom to its [spoiler][/spoiler] syntax. This will also allow + * the spoiler styling and experience to standardize, instead of using two distinct styles and experiences. + */ protected function renderSpoilers(&$Sender) { $FormatBody = &$Sender->EventArguments['Object']->FormatBody; - // Fix a wysiwyg but where spoilers + // Fix a wysiwyg but where spoilers $FormatBody = preg_replace('`<.+>\s*(\[/?spoiler\])\s*`', '$1', $FormatBody); $FormatBody = preg_replace_callback("/(\[spoiler(?:=(?:")?([\d\w_',.? ]+)(?:")?)?\])/siu", array($this, 'SpoilerCallback'), $FormatBody); $FormatBody = str_ireplace('[/spoiler]', '', $FormatBody); } + /** + * + * + * @param $Matches + * @return string + */ protected function spoilerCallback($Matches) { $SpoilerText = (count($Matches) > 2) ? $Matches[2]