diff --git a/content/index.md b/content/index.md index ffdee6e..0d8f3d5 100644 --- a/content/index.md +++ b/content/index.md @@ -1,4 +1,4 @@ # Typemill -*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prose, lyrics, manuals, documentations, studies and more. Just download and start.* +Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prose, lyrics, manuals, documentations, studies and more. Just download and start. diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index 0e66c71..cf426aa 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -172,7 +172,7 @@ class SettingsController extends Controller public function showThemes($request, $response, $args) { - $userSettings = $this->c->get('settings'); + $userSettings = $this->c->get('settings'); $themes = $this->getThemes(); $themedata = array(); $fieldsModel = new Fields(); @@ -332,7 +332,7 @@ class SettingsController extends Controller $userInput = isset($params[$themeName]) ? $params[$themeName] : false; $validate = new Validation(); $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); - + if(isset($themeSettings['settings']['images'])) { # get the default settings @@ -375,12 +375,23 @@ class SettingsController extends Controller if($userInput) { - /* validate the user-input */ - $this->validateInput('themes', $themeName, $userInput, $validate); - + # validate the user-input and return image-fields if they are defined + $imageFields = $this->validateInput('themes', $themeName, $userInput, $validate); + /* set user input as theme settings */ $userSettings['themes'][$themeName] = $userInput; } + + # handle images + $images = $request->getUploadedFiles(); + + if(!isset($_SESSION['errors']) && isset($images[$themeName])) + { + $userInput = $this->saveImages($imageFields, $userInput, $userSettings, $images[$themeName]); + + # set user input as theme settings + $userSettings['themes'][$themeName] = $userInput; + } /* check for errors and redirect to path, if errors found */ if(isset($_SESSION['errors'])) @@ -428,11 +439,22 @@ class SettingsController extends Controller else { /* validate the user-input */ - $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate); + $imageFields = $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate); /* use the input data */ $pluginSettings[$pluginName] = $userInput[$pluginName]; } + + # handle images + $images = $request->getUploadedFiles(); + + if(!isset($_SESSION['errors']) && isset($images[$pluginName])) + { + $userInput[$pluginName] = $this->saveImages($imageFields, $userInput[$pluginName], $userSettings, $images[$pluginName]); + + # set user input as theme settings + $pluginSettings[$pluginName] = $userInput[$pluginName]; + } /* deactivate the plugin, if there is no active flag */ if(!isset($userInput[$pluginName]['active'])) @@ -465,6 +487,9 @@ class SettingsController extends Controller /* fetch the original settings from the folder (plugin or theme) to get the field definitions */ $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); + # images get special treatment + $imageFieldDefinitions = array(); + if(isset($originalSettings['forms']['fields'])) { /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ @@ -509,6 +534,12 @@ class SettingsController extends Controller { /* validate user input for this field */ $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition, $skiprequired); + + if($fieldDefinition['type'] == 'image') + { + # we want to return all images-fields for further processing + $imageFieldDefinitions[$fieldName] = $fieldDefinition; + } } if(!$fieldDefinition && $fieldName != 'active') { @@ -516,6 +547,45 @@ class SettingsController extends Controller } } } + + return $imageFieldDefinitions; + } + + protected function saveImages($imageFields, $userInput, $userSettings, $files) + { + + # initiate image processor with standard image sizes + $processImages = new ProcessImage($userSettings['images']); + + if(!$processImages->checkFolders()) + { + $this->c->flash->addMessage('error', 'Please make sure that your media folder exists and is writable.'); + return false; + } + + foreach($imageFields as $fieldName => $imageField) + { + if(isset($userInput[$fieldName])) + { + # handle single input with single file upload + $image = $files[$fieldName]; + + if($image->getError() === UPLOAD_ERR_OK) + { + # not the most elegant, but createImage expects a base64-encoded string. + $imageContent = $image->getStream()->getContents(); + $imageData = base64_encode($imageContent); + $imageSrc = 'data: ' . $image->getClientMediaType() . ';base64,' . $imageData; + + if($processImages->createImage($imageSrc, $image->getClientFilename(), $userSettings['images'], $overwrite = NULL)) + { + # returns image path to media library + $userInput[$fieldName] = $processImages->publishImage(); + } + } + } + } + return $userInput; } /*********************** @@ -751,13 +821,13 @@ class SettingsController extends Controller private function getLanguages() { - return array( - 'en' => 'English', - 'ru' => 'Russian', - 'nl' => 'Dutch, Flemish', - 'de' => 'German', - 'it' => 'Italian', - 'fr' => 'French', - ); - } -} + return array( + 'en' => 'English', + 'ru' => 'Russian', + 'nl' => 'Dutch, Flemish', + 'de' => 'German', + 'it' => 'Italian', + 'fr' => 'French', + ); + } +} diff --git a/system/Models/Fields.php b/system/Models/Fields.php index a304e75..c38a8db 100644 --- a/system/Models/Fields.php +++ b/system/Models/Fields.php @@ -86,7 +86,7 @@ class Fields } else { - $field->unsetAttribute('chhecked'); + $field->unsetAttribute('checked'); } } else diff --git a/system/Models/Folder.php b/system/Models/Folder.php index 587438f..0dcaeda 100644 --- a/system/Models/Folder.php +++ b/system/Models/Folder.php @@ -506,7 +506,9 @@ class Folder { # if it is the first round, create an empty array if(!$i){ $i = 0; $breadcrumb = array();} - + + if(!$searchArray){ return $breadcrumb;} + while($i < count($searchArray)) { if(!isset($content[$searchArray[$i]])){ return false; } diff --git a/system/Models/Validation.php b/system/Models/Validation.php index 14f8b2c..84b0554 100644 --- a/system/Models/Validation.php +++ b/system/Models/Validation.php @@ -27,6 +27,15 @@ class Validation return false; }, 'invalid values'); + Validator::addRule('image_types', function($field, $value, array $params, array $fields) use ($user) + { + $allowed = ['jpg', 'jpeg', 'png']; + $pathinfo = pathinfo($value); + $extension = strtolower($pathinfo['extension']); + if(array_search($extension, $allowed)){ return true; } + return false; + }, 'only jpg, jpeg, png allowed'); + Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user) { $userdata = $user->getUser($value); @@ -434,6 +443,7 @@ class Validation case "image": $v->rule('noHTML', $fieldName); $v->rule('lengthMax', $fieldName, 1000); + $v->rule('image_types', $fieldName); break; default: $v->rule('lengthMax', $fieldName, 1000); diff --git a/system/author/css/style.css b/system/author/css/style.css index 2adabc7..6d2a3b8 100644 --- a/system/author/css/style.css +++ b/system/author/css/style.css @@ -1114,7 +1114,7 @@ ul.cardInfo{ border: 0px; } .cardFields.open{ - max-height: 5000px; + max-height: 20000px; transition: max-height 0.5s ease-in; overflow: hidden; border: 1px; @@ -2490,6 +2490,8 @@ footer a:focus, footer a:hover, footer a:active } .blox .TOC li:before{ color: #bbb; } +.mbfix{ margin-bottom: 0px!important; } + @media only screen and (min-width: 600px) { section{ diff --git a/system/author/editor/publish-controller.twig b/system/author/editor/publish-controller.twig index 10bfaf5..ea6186a 100644 --- a/system/author/editor/publish-controller.twig +++ b/system/author/editor/publish-controller.twig @@ -1,3 +1,5 @@ +{% set itemurl = (item.urlRelWoF == '/') ? '' : item.urlRelWoF %} +
${ errors.message }
@@ -5,8 +7,8 @@
- {{ __('raw mode') }}{{ __('raw') }} - {{ __('visual mode') }}{{ __('visual') }} + {{ __('raw mode') }}{{ __('raw') }} + {{ __('visual mode') }}{{ __('visual') }}
diff --git a/system/author/js/typemillutils.js b/system/author/js/typemillutils.js index a317bcd..f0891fc 100644 --- a/system/author/js/typemillutils.js +++ b/system/author/js/typemillutils.js @@ -5,6 +5,7 @@ let typemillUtilities = { { this.youtubeItems = document.querySelectorAll( ".youtube" ); }, + addYoutubePlayButtons: function(){ if(this.youtubeItems) { @@ -24,12 +25,13 @@ let typemillUtilities = { youtubePlaybutton.classList.add("play-video"); youtubePlaybutton.value = "Play"; - element.parentNode.appendChild(youtubePlaybutton); + element.parentNode.appendChild(youtubePlaybutton); }, - listenToYoutube: function(){ + listenToClick: function(){ document.addEventListener('click', function (event) { + /* listen to youtube */ if (event.target.matches('.play-video')) { var youtubeID = event.target.parentNode.getElementsByClassName('youtube')[0].id; @@ -49,12 +51,66 @@ let typemillUtilities = { videocontainer.innerHTML = ""; videocontainer.appendChild( iframe ); } + + if (event.target.matches('.function-delete-img')) { + + event.preventDefault(); + event.stopPropagation(); + + var imgUploadField = event.target.closest(".img-upload"); + var imgSrc = imgUploadField.getElementsByClassName("function-img-src")[0]; + imgSrc.src = ''; + var imgUrl = imgUploadField.getElementsByClassName("function-img-url")[0]; + imgUrl.value = ''; + + } + + }, true); + }, + + listenToChange: function() + { + document.addEventListener('change', function (changeevent) { + + /* listen to youtube */ + if (changeevent.target.matches('.function-img-file')) { + + if(changeevent.target.files.length > 0) + { + let imageFile = changeevent.target.files[0]; + let size = imageFile.size / 1024 / 1024; + + if (!imageFile.type.match('image.*')) + { + // publishController.errors.message = "Only images are allowed."; + } + else if (size > this.maxsize) + { + // publishController.errors.message = "The maximal size of images is " + this.maxsize + " MB"; + } + else + { + let reader = new FileReader(); + reader.readAsDataURL(imageFile); + reader.onload = function(fileevent) + { + var imgUploadField = changeevent.target.closest(".img-upload"); + var imgSrc = imgUploadField.getElementsByClassName("function-img-src")[0]; + imgSrc.src = fileevent.target.result; + var imgUrl = imgUploadField.getElementsByClassName("function-img-url")[0]; + imgUrl.value = imageFile.name; + } + } + } + } + }, true); }, start: function(){ this.setYoutubeItems(); - this.addYoutubePlayButtons(); - this.listenToYoutube(); - }, + this.addYoutubePlayButtons(); + this.listenToClick(); + this.listenToChange(); + }, }; \ No newline at end of file diff --git a/system/author/languages/de.yaml b/system/author/languages/de.yaml index 52d3abb..f287005 100644 --- a/system/author/languages/de.yaml +++ b/system/author/languages/de.yaml @@ -167,6 +167,7 @@ ULIST: ulist UNKNOWN: Unbekannt UPDATE_USER: Nutzer aktualisieren UPLOAD_FILE: Datei hochladen +UPLOAD_AN_IMAGE: Bild hochladen UPLOAD: hochladen USE_2_TO_20_CHARACTERS: 2 bis 20 Anschläge erlaubt. USE_2_TO_40_CHARACTERS: 2 to 40 Anschläge erlaubt. diff --git a/system/author/layouts/layout.twig b/system/author/layouts/layout.twig index aacdd68..06ef3a3 100644 --- a/system/author/layouts/layout.twig +++ b/system/author/layouts/layout.twig @@ -56,7 +56,11 @@
- + + + \ No newline at end of file diff --git a/system/author/partials/fields.twig b/system/author/partials/fields.twig index 4ad22c6..3123f9f 100644 --- a/system/author/partials/fields.twig +++ b/system/author/partials/fields.twig @@ -5,67 +5,99 @@ {% if field.help %}
?{{__(field.help|slice(0,100))}}
{% endif %} - {% if field.type == 'textarea' %} + {% if field.type == 'image' %} +
+
+
+ +
+
+
+
+ +
{{ __('Upload an image') }}
+
+
+ +
+ + +
+
+ {% if errors[itemName][field.name] %} +
{{ errors[itemName][field.name] | first }}
+ {% endif %} - + {% if field.description %}
{{ __(field.description) }}
{% endif %} - {% elseif field.type == 'paragraph' %} - - {{ markdown(field.getContent()) }} +
+
- {% elseif field.type == 'checkbox' %} + {% else %} + + {% if field.type == 'textarea' %} + + + + {% elseif field.type == 'paragraph' %} - + {{ markdown(field.getContent()) }} - {% elseif field.type == 'checkboxlist' %} - - {% set options = field.getOptions() %} - - {% for value,label in options %} - - + {% endfor %} - - {% elseif field.type == 'radio' %} + {% elseif field.type == 'select' %} - {% set options = field.getOptions() %} + {% set options = field.getOptions() %} - {% for value,label in options %} + + + {% elseif field.type == 'radio' %} + + {% set options = field.getOptions() %} + + {% for value,label in options %} + + + + {% endfor %} - + {% else %} - {% endfor %} + + + {% endif %} + + {% if field.description %}
{{ __(field.description) }}
{% endif %} - {% else %} + {% if errors[itemName][field.name] %} + {{ errors[itemName][field.name] | first }} + {% endif %} - - - {% endif %} - - {% if field.description %}
{{ __(field.description) }}
{% endif %} - - {% if errors[itemName][field.name] %} - {{ errors[itemName][field.name] | first }} {% endif %} \ No newline at end of file diff --git a/system/author/partials/form.twig b/system/author/partials/form.twig index f7742c1..da95e6a 100644 --- a/system/author/partials/form.twig +++ b/system/author/partials/form.twig @@ -3,7 +3,7 @@ {% endif %} -
+
diff --git a/system/author/settings/plugins.twig b/system/author/settings/plugins.twig index b1d13e0..55034b0 100644 --- a/system/author/settings/plugins.twig +++ b/system/author/settings/plugins.twig @@ -6,7 +6,7 @@
- +
diff --git a/system/author/settings/themes.twig b/system/author/settings/themes.twig index 1f51092..69b165f 100644 --- a/system/author/settings/themes.twig +++ b/system/author/settings/themes.twig @@ -15,7 +15,7 @@ {% for themeName, theme in themes %} - +