├── ext_icon.png ├── Resources ├── Public │ ├── Images │ │ ├── Help │ │ │ ├── form.png │ │ │ ├── tree.png │ │ │ ├── modify.png │ │ │ ├── pannel-1.png │ │ │ ├── pannel-2.png │ │ │ └── duplication.jpg │ │ ├── easter-egg.png │ │ └── drag-drop-file.png │ ├── Icons │ │ └── icon-color-picker.png │ ├── Contrib │ │ ├── fine-uploader │ │ │ ├── edit.gif │ │ │ ├── pause.gif │ │ │ ├── retry.gif │ │ │ ├── trash.gif │ │ │ ├── continue.gif │ │ │ ├── loading.gif │ │ │ ├── processing.gif │ │ │ ├── placeholders │ │ │ │ ├── waiting-generic.png │ │ │ │ └── not_available-generic.png │ │ │ ├── iframe.xss.response.js │ │ │ ├── templates │ │ │ │ ├── default.html │ │ │ │ └── simple-thumbnails.html │ │ │ └── fine-uploader.min.css │ │ ├── hailpixel-color-picker │ │ │ ├── static │ │ │ │ └── scripts │ │ │ │ │ ├── main.js │ │ │ │ │ ├── collections │ │ │ │ │ └── colors.js │ │ │ │ │ ├── models │ │ │ │ │ └── color.js │ │ │ │ │ ├── routes │ │ │ │ │ └── router.js │ │ │ │ │ └── views │ │ │ │ │ ├── color.js │ │ │ │ │ └── app.js │ │ │ └── index.htm │ │ └── twitter-bootstrap │ │ │ └── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ ├── JavaScript │ │ ├── Fields │ │ │ ├── SiteFactory.Field.Select.js │ │ │ ├── SiteFactory.Field.ColorPicker.js │ │ │ ├── SiteFactory.Field.Text.js │ │ │ └── SiteFactory.Field.ImageUploadPicker.js │ │ ├── SiteFactory.Main.js │ │ ├── SiteFactory.Menu.js │ │ ├── SiteFactory.FadeOut.js │ │ ├── SiteFactory.Field.js │ │ └── SiteFactory.Form.js │ └── StyleSheets │ │ └── site-factory-fine-uploader.css └── Private │ ├── Partials │ ├── Form │ │ ├── FieldsTypes │ │ │ ├── Hidden.html │ │ │ ├── Select.html │ │ │ ├── Checkbox.html │ │ │ └── Text.html │ │ ├── Tooltips │ │ │ ├── Error.html │ │ │ ├── Question.html │ │ │ └── Warning.html │ │ ├── Fields │ │ │ ├── Text.html │ │ │ ├── Checkbox.html │ │ │ ├── Select.html │ │ │ ├── ColorPicker.html │ │ │ └── ImageUpload.html │ │ └── Field.html │ ├── New │ │ ├── EasterEgg.html │ │ ├── Pannels.html │ │ ├── FieldBody.html │ │ └── FormStaticMenu.html │ └── Index │ │ ├── ModelSitesList.html │ │ └── DuplicatedSitesList.html │ ├── Language │ ├── locallang_em.xlf │ ├── locallang_sitefactory.xlf │ ├── fr.locallang_em.xlf │ ├── fr.locallang_sitefactory.xlf │ ├── FieldsLocallang.xlf │ └── FineUploader.xlf │ ├── Layouts │ └── Backend │ │ └── Default.html │ └── Templates │ └── Administration │ ├── Index.html │ ├── Help.html │ ├── ProcessCopy.html │ └── New.html ├── Configuration └── TypoScript │ ├── Default │ ├── DefaultConfiguration.ts │ ├── FieldsTypesConfiguration.ts │ ├── FieldsConfiguration.ts │ └── DuplicationConfiguration.ts │ └── FieldsExample │ ├── constants.txt │ └── setup.txt ├── ext_conf_template.txt ├── ext_tables.sql ├── Classes ├── Utility │ ├── AjaxInterface.php │ ├── ExtensionManagerUtility.php │ └── FileUtility.php ├── Form │ ├── Fields │ │ ├── FieldInterface.php │ │ ├── ColorPickerField.php │ │ ├── CheckboxField.php │ │ ├── TextField.php │ │ ├── ImageUploadPickerField.php │ │ ├── FieldsTypes.php │ │ ├── SelectField.php │ │ └── Field.php │ ├── Validation │ │ ├── AbstractValidator.php │ │ ├── IntegerValidator.php │ │ ├── TwitterUrlValidator.php │ │ ├── FacebookUrlValidator.php │ │ ├── DomainNameValidator.php │ │ ├── HexadecimalValidator.php │ │ ├── NotEmptyValidator.php │ │ └── SelectOptionsValidator.php │ ├── FieldsConfigurationPresets.php │ └── FieldValidation.php ├── Duplication │ ├── DuplicationProcessInterface.php │ └── Process │ │ ├── BackendConstantsAssignationProcess.php │ │ ├── LinkToPageBackendLayoutProcess.php │ │ ├── SaveSiteConfigurationProcess.php │ │ ├── TreeUidAssociationProcess.php │ │ ├── PagesDuplicationProcess.php │ │ ├── SysFileMountsProcess.php │ │ ├── LinkToPageMediaProcess.php │ │ └── UploadedFilesProcess.php ├── ViewHelpers │ ├── Be │ │ ├── ErrorMessageViewHelper.php │ │ └── ImportAssetViewHelper.php │ └── AddSlashesViewHelper.php ├── Controller │ ├── AbstractController.php │ └── AjaxController.php ├── Domain │ ├── Repository │ │ ├── PagesRepository.php │ │ └── SaveRepository.php │ └── Model │ │ ├── Pages.php │ │ └── Save.php └── Core │ └── CacheManager.php ├── composer.json ├── ext_typoscript_setup.txt ├── ext_emconf.php ├── ext_tables.php └── ext_localconf.php /ext_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/ext_icon.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/form.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/tree.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/modify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/modify.png -------------------------------------------------------------------------------- /Resources/Public/Images/easter-egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/easter-egg.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/pannel-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/pannel-1.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/pannel-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/pannel-2.png -------------------------------------------------------------------------------- /Resources/Public/Images/drag-drop-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/drag-drop-file.png -------------------------------------------------------------------------------- /Resources/Public/Icons/icon-color-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Icons/icon-color-picker.png -------------------------------------------------------------------------------- /Resources/Public/Images/Help/duplication.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Images/Help/duplication.jpg -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/edit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/edit.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/pause.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/pause.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/retry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/retry.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/trash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/trash.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/continue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/continue.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/loading.gif -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/FieldsTypes/Hidden.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/processing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/processing.gif -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/main.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | $(function() { 3 | window.dapp = new app.SwatchAppView(); 4 | Backbone.history.start(); 5 | }); -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/placeholders/waiting-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/placeholders/waiting-generic.png -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/placeholders/not_available-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/fine-uploader/placeholders/not_available-generic.png -------------------------------------------------------------------------------- /Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romm/TYPO3-Site-Factory/HEAD/Resources/Public/Contrib/twitter-bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/iframe.xss.response.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | var match = /(\{.*\})/.exec(document.body.innerHTML); 4 | if (match) { 5 | parent.postMessage(match[1], "*"); 6 | } 7 | }()); 8 | -------------------------------------------------------------------------------- /Configuration/TypoScript/Default/DefaultConfiguration.ts: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | constantsPaths { 3 | configurationPaths { 4 | 0 = module.tx_sitefactory.config 5 | } 6 | pagesPaths { 7 | 0 = module.tx_sitefactory.pages 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /ext_conf_template.txt: -------------------------------------------------------------------------------- 1 | # cat=Basic; type=int+; label=LLL:EXT:site_factory/Resources/Private/Language/locallang_em.xlf:model_sites_pid 2 | modelSitesPid = 0 3 | 4 | # cat=Basic; type=int+; label=LLL:EXT:site_factory/Resources/Private/Language/locallang_em.xlf:copy_destination_uid 5 | copyDestination = 0 -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/FieldsTypes/Select.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Configuration/TypoScript/FieldsExample/constants.txt: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | config { 3 | siteTitle = My New Site 4 | logo = EXT:site_factory/ext_icon.png 5 | backgroundColor = #FF8700 6 | facebook = https://www.facebook.com/MyNewSite 7 | twitter = https://twitter.com/MyNewSite 8 | } 9 | pages { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/FieldsTypes/Checkbox.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Tooltips/Error.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Tooltips/Question.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Tooltips/Warning.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/FieldsTypes/Text.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/collections/colors.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | $(function() { 4 | "use strict"; 5 | 6 | app.ColorList = Backbone.Collection.extend({ 7 | model: app.Color, 8 | 9 | addFromHex: function(hex) { 10 | var c = Color(hex); 11 | this.add({color: c}); 12 | } 13 | }); 14 | 15 | // Global color collection 16 | app.Colors = new app.ColorList(); 17 | }); -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Fields/Text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {field.displayValue} 16 | -------------------------------------------------------------------------------- /ext_tables.sql: -------------------------------------------------------------------------------- 1 | # 2 | # Table structure extend for table 'tx_sitefactory_domain_model_save' 3 | # 4 | CREATE TABLE tx_sitefactory_domain_model_save ( 5 | uid int(11) NOT NULL auto_increment, 6 | pid int(11) NOT NULL, 7 | 8 | root_page_uid int(11) NOT NULL, 9 | date int(11) DEFAULT 0, 10 | configuration TEXT DEFAULT '', 11 | 12 | PRIMARY KEY (uid) 13 | ); 14 | 15 | CREATE TABLE sys_template ( 16 | site_factory_template tinyint(1) unsigned DEFAULT '0' NOT NULL, 17 | ); 18 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Fields/Checkbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {field.displayValue} 16 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Fields/Select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {field.displayValue} 16 | -------------------------------------------------------------------------------- /Configuration/TypoScript/Default/FieldsTypesConfiguration.ts: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | fieldsTypes { 3 | text { 4 | class = Romm\SiteFactory\Form\Fields\TextField 5 | } 6 | select { 7 | class = Romm\SiteFactory\Form\Fields\SelectField 8 | } 9 | checkbox { 10 | class = Romm\SiteFactory\Form\Fields\CheckboxField 11 | } 12 | color_picker { 13 | class = Romm\SiteFactory\Form\Fields\ColorPickerField 14 | } 15 | image_upload { 16 | class = Romm\SiteFactory\Form\Fields\ImageUploadPickerField 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Classes/Utility/AjaxInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Utility; 15 | 16 | interface AjaxInterface 17 | { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Resources/Private/Language/locallang_em.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Uid of the page containing the model sites 9 | 10 | 11 | 12 | Uid of the page where the sites will be duplicated 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Classes/Form/Fields/FieldInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | /** 17 | * @todo 18 | */ 19 | interface FieldInterface 20 | { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/Fields/SiteFactory.Field.Select.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function() { 2 | var instances = SiteFactory.Form.GetAllInstances(); 3 | for (var i = 0; i < instances.length; i++) { 4 | var formInstance = instances[i]; 5 | var selectFields = formInstance.getFieldsByFieldType('select'); 6 | for(var index in selectFields) { 7 | if (selectFields.hasOwnProperty(index)) { 8 | jQuery(selectFields[index].input).on( 9 | 'change', 10 | {field: selectFields[index]}, 11 | function(event) { 12 | event.data.field.validate(); 13 | } 14 | ); 15 | } 16 | } 17 | } 18 | }); -------------------------------------------------------------------------------- /Classes/Form/Validation/AbstractValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | abstract class AbstractValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator 17 | { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Resources/Private/Language/locallang_sitefactory.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Site Factory 9 | 10 | 11 | Use this module to create your own sites. 12 | 13 | 14 | Use this module to create your own sites. 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Resources/Private/Language/fr.locallang_em.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Uid of the page containing the model sites 9 | Uid de la page contenant les sites modèles 10 | 11 | 12 | 13 | Uid of the page where the sites will be duplicated 14 | Uid de la page vers laquelle seront dupliqué les sites 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/models/color.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | $(function() { 4 | "use strict"; 5 | 6 | app.Color = Backbone.Model.extend({ 7 | defaults: { 8 | color: new Color() 9 | }, 10 | 11 | /** 12 | * Setter and getter for manipulable color objects 13 | */ 14 | color: function(color) { 15 | if(color) { 16 | this.set({color: color}) 17 | } 18 | return this.get("color"); 19 | }, 20 | 21 | hslCss: function() { 22 | return this.color().hslString(); 23 | }, 24 | 25 | rgbCss: function() { 26 | return this.color().rgbString(); 27 | }, 28 | 29 | hexCss: function() { 30 | return this.color().hexString(); 31 | } 32 | 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /Classes/Duplication/DuplicationProcessInterface.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication; 15 | 16 | /** 17 | * Class containing functions called when a site is being duplicated. 18 | */ 19 | interface DuplicationProcessInterface 20 | { 21 | 22 | /** 23 | * Do the process of your duplication step in this function. 24 | */ 25 | public function run(); 26 | } 27 | -------------------------------------------------------------------------------- /Resources/Private/Partials/New/EasterEgg.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Resources/Public/StyleSheets/site-factory-fine-uploader.css: -------------------------------------------------------------------------------- 1 | .qq-uploader { 2 | min-height: 96px; 3 | padding: 10px 5px 0; 4 | margin: 5px; 5 | border: none; 6 | } 7 | 8 | .qq-uploader[qq-drop-area-text] { 9 | border: 1px dashed #CCC; 10 | } 11 | .qq-uploader[qq-drop-area-text]:before { 12 | background: url('/typo3conf/ext/site_factory/Resources/Public/Images/drag-drop-file.png') no-repeat 99%; 13 | } 14 | .qq-uploader:before { 15 | display: none; 16 | padding-top: 5px; 17 | height: 96px; 18 | } 19 | .qq-uploader:hover:before { 20 | display: block; 21 | top: 0; 22 | } 23 | 24 | .qq-upload-list { 25 | margin: 10px 0; 26 | } 27 | 28 | .qq-upload-button-selector { 29 | margin-top: 20px; 30 | } 31 | 32 | .file-uploader-container { 33 | border: 1px solid #CCC; 34 | border-radius: 4px; 35 | } 36 | 37 | dialog { 38 | padding: 0px; 39 | border: none; 40 | background: none; 41 | } -------------------------------------------------------------------------------- /Resources/Private/Partials/Index/ModelSitesList.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 |
6 |
    7 | 8 |
  • 9 |  – {model} 10 |
  • 11 |
    12 |
13 |
14 | 15 |
16 | 17 |   18 | 19 |
20 |
21 |
-------------------------------------------------------------------------------- /Resources/Private/Language/fr.locallang_sitefactory.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Site Factory 9 | Usine à site 10 | 11 | 12 | Use this module to create your own sites. 13 | Utilisez ce module pour créer des mini-sites. 14 | 15 | 16 | Use this module to create your own sites. 17 | Utilisez ce module pour créer des mini-sites. 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Configuration/TypoScript/Default/FieldsConfiguration.ts: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | fields { 3 | # A list containing all the model sites existing on the TYPO3 instance. 4 | modelSite { 5 | type = select 6 | position = first 7 | label = form.field.label.model_site 8 | hint = form.field.hint.model_site 9 | options = Romm\SiteFactory\Form\FieldsConfigurationPresets->getModelSitesList 10 | hideInSiteModification = 1 11 | } 12 | 13 | # The name of the new site. 14 | siteTitle { 15 | type = text 16 | position = after:modelSite 17 | label = form.field.label.site_name 18 | hint = form.field.hint.site_name 19 | placeholder = form.field.default_value.site_name 20 | defaultValue = form.field.default_value.site_name 21 | hideInSiteModification = 1 22 | validation { 23 | # We want at least one character for the name. 24 | noEmpty { 25 | validator = Romm\SiteFactory\Form\Validation\NotEmptyValidator 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Field.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | TODO: WRONG FIELD TYPE 22 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/Fields/SiteFactory.Field.ColorPicker.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function() { 2 | function changePreviewColor(field, color) { 3 | var previewElement = field.findElement('.color-picker-preview .color-picker-preview-cube'); 4 | previewElement.css('background-color', color); 5 | } 6 | 7 | var instances = SiteFactory.Form.GetAllInstances(); 8 | for (var i = 0; i < instances.length; i++) { 9 | var formInstance = instances[i]; 10 | var fields = formInstance.getFieldsByType('color_picker'); 11 | 12 | for(var index in fields) { 13 | if (fields.hasOwnProperty(index)) { 14 | fields[index].fillColorPickerField = function(value) { 15 | this.input.val(value); 16 | changePreviewColor(this, value); 17 | }; 18 | 19 | jQuery(fields[index].input).on( 20 | 'change, keyup', 21 | {field: fields[index]}, 22 | function(event) { 23 | var field = event.data.field; 24 | changePreviewColor(field, field.input.val()) 25 | } 26 | ); 27 | } 28 | } 29 | } 30 | }); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "romm/site-factory", 3 | "type": "typo3-cms-extension", 4 | "description": "Replicate and modify an existing website model very easily with a flexible and lean design. Read the code examples to understand and master all the TypoScript configuration, or extend the existing duplication processes. Based on freesite (created by Kasper Skårhøj) this project was originaly conceived by Cyril Wolfangel and is developped and maintained by Romain Canon. Join the project on https://github.com/romaincanon/TYPO3-Site-Factory", 5 | "keywords": [ 6 | "typo3", "site", "factory" 7 | ], 8 | "license": "GPL-3.0+", 9 | "authors": [ 10 | { 11 | "name": "Romain Canon", 12 | "email": "romain.hydrocanon@gmail.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.5" 17 | }, 18 | "replace": { 19 | "site_factory": "self.version", 20 | "typo3-ter/site-factory": "self.version" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Romm\\SiteFactory\\": "Classes/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Classes/ViewHelpers/Be/ErrorMessageViewHelper.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\ViewHelpers\Be; 15 | 16 | use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper; 17 | 18 | /** 19 | * ViewHelper to convert an array containing error messages into a human 20 | * readable string. 21 | */ 22 | class ErrorMessageViewHelper extends AbstractBackendViewHelper 23 | { 24 | 25 | /** 26 | * @param array $errors Error messages. 27 | * @return string 28 | */ 29 | public function render($errors = []) 30 | { 31 | $result = ''; 32 | 33 | if (is_array($errors)) { 34 | $result = implode("\n\r", $errors); 35 | } 36 | 37 | return $result; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/routes/router.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | $(function() { 4 | "use strict"; 5 | 6 | var ColorRouter = Backbone.Router.extend({ 7 | routes: { 8 | "*colors": "setColors" 9 | }, 10 | 11 | initialize: function(ops) { 12 | app.Colors.on("add", this.pushColorState, this); 13 | app.Colors.on("remove", this.pushColorState, this); 14 | }, 15 | 16 | setColors: function(param) { 17 | var colors = param.split(","); 18 | 19 | colors = _.reject(colors, function(color) { 20 | return color.length == 0; 21 | }); 22 | 23 | app.Colors.reset(); 24 | 25 | _.each(colors, function(color) { 26 | app.Colors.addFromHex("#" + color); 27 | }); 28 | }, 29 | 30 | /** 31 | * Pushes the current Colors state to the path 32 | */ 33 | pushColorState: function() { 34 | var hash = app.Colors.reduce(function(memo, color) { 35 | return memo + color.hexCss().substr(1) + ','; 36 | }, ""); 37 | 38 | this.navigate(hash, {trigger: false, replace: true}); 39 | } 40 | }); 41 | 42 | app.Router = new ColorRouter(); 43 | }); -------------------------------------------------------------------------------- /Classes/Controller/AbstractController.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Controller; 15 | 16 | use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; 17 | use Romm\SiteFactory\Core\Core; 18 | use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; 19 | 20 | /** 21 | * Controller managing the duplication of sites. 22 | */ 23 | class AbstractController extends ActionController 24 | { 25 | 26 | /** 27 | * Is called before any action. 28 | */ 29 | public function initializeAction() 30 | { 31 | Core::loadJquery(); 32 | } 33 | 34 | /** 35 | * @param ViewInterface $view 36 | */ 37 | protected function initializeView(ViewInterface $view) 38 | { 39 | $this->view->assign('pathSite', $_SERVER['SERVER_NAME']); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ext_typoscript_setup.txt: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | persistence { 3 | storagePid = 0 4 | classes { 5 | Romm\SiteFactory\Domain\Model\Save { 6 | mapping { 7 | tableName = tx_sitefactory_domain_model_save 8 | columns { 9 | uid.mapOnProperty = uid 10 | pid.mapOnProperty = pid 11 | root_page_uid.mapOnProperty = rootPageUid 12 | date.mapOnProperty = date 13 | configuration.mapOnProperty = configuration 14 | } 15 | } 16 | } 17 | 18 | # Pages model full definition. 19 | Romm\SiteFactory\Domain\Model\Pages { 20 | mapping { 21 | tableName = pages 22 | columns { 23 | uid.mapOnProperty = uid 24 | pid.mapOnProperty = pid 25 | title.mapOnProperty = title 26 | hidden.mapOnProperty = hidden 27 | deleted.mapOnProperty = deleted 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Resources/Private/Language/FieldsLocallang.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | The value "%s" is not a correct. 9 | 10 | 11 | The value "%s" does not match a hexadecimal value. 12 | 13 | 14 | This value is empty and must be filled. 15 | 16 | 17 | This value must match a domain name. 18 | 19 | 20 | This value must match a valid Facebook url. 21 | 22 | 23 | This value must match a valid Twitter url. 24 | 25 | 26 | This value must be an integer. 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/Fields/SiteFactory.Field.Text.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function() { 2 | var instances = SiteFactory.Form.GetAllInstances(); 3 | for (var i = 0; i < instances.length; i++) { 4 | var formInstance = instances[i]; 5 | var textFields = formInstance.getFieldsByFieldType('text'); 6 | 7 | for(var index in textFields) { 8 | if (textFields.hasOwnProperty(index)) { 9 | /* 10 | * The event fires 500ms after the last action done on the input. It 11 | * means that if you write a letter 200ms after the first letter, 12 | * the event will eventually fire 500ms later (700ms after the first 13 | * one). 14 | */ 15 | jQuery(textFields[index].input).on( 16 | 'change keyup', 17 | {field: textFields[index]}, 18 | function(event) { 19 | var field = event.data.field; 20 | var input = field.input; 21 | if (jQuery(input).val() == input.lastVal) return; 22 | 23 | clearInterval(jQuery(input).data('evalTimer')); 24 | 25 | jQuery(input).data('evalTimer', setInterval( 26 | function() { 27 | clearInterval(jQuery(input).data('evalTimer')); 28 | input.lastVal = jQuery(input).val(); 29 | 30 | field.validate(); 31 | }, 32 | 500 33 | )); 34 | } 35 | ); 36 | } 37 | } 38 | } 39 | }); -------------------------------------------------------------------------------- /Resources/Private/Partials/New/Pannels.html: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | 31 |
32 | 42 |
-------------------------------------------------------------------------------- /Classes/Form/Validation/IntegerValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class IntegerValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value matches a domain name value. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | if (filter_var($field->getValue(), FILTER_VALIDATE_INT) === false) { 33 | $this->addError( 34 | $this->translateErrorMessage( 35 | 'fields.validation.integer_value', 36 | Core::getExtensionKey() 37 | ), 38 | 1431105694 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Classes/Form/Validation/TwitterUrlValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class TwitterUrlValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value matches a Twitter URL. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | if (!preg_match('/^$|^(https:\/\/)?(www.)?twitter.com\/.+$/', $field->getValue())) { 33 | $this->addError( 34 | $this->translateErrorMessage( 35 | 'fields.validation.twitter_url_value', 36 | Core::getExtensionKey() 37 | ), 38 | 1431105145 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Classes/Form/Validation/FacebookUrlValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class FacebookUrlValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value matches a Facebook URL. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | if (!preg_match('/^$|^(https:\/\/)?(www.)?facebook.com\/.+$/', $field->getValue())) { 33 | $this->addError( 34 | $this->translateErrorMessage( 35 | 'fields.validation.facebook_url_value', 36 | Core::getExtensionKey() 37 | ), 38 | 1431104928 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Classes/Form/Validation/DomainNameValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class DomainNameValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value matches a domain name value. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | if (!preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/', $field->getValue())) { 33 | $this->addError( 34 | $this->translateErrorMessage( 35 | 'fields.validation.domain_name_value', 36 | Core::getExtensionKey() 37 | ), 38 | 1431104484 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Fields/ColorPicker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 |
27 | 28 |
{field.displayValue}
29 |
30 |
31 |
-------------------------------------------------------------------------------- /ext_emconf.php: -------------------------------------------------------------------------------- 1 | 'Site Factory', 4 | 'state' => 'beta', 5 | 'version' => '0.2.0', 6 | 'description' => 'Replicate and modify an existing website model very easily with a flexible and lean design. Read the code examples to understand and master all the TypoScript configuration, or extend the existing duplication processes. Based on freesite (created by Kasper Skårhøj) this project was originaly conceived by Cyril Wolfangel and is developped and maintained by Romain Canon. Join the project on https://github.com/romaincanon/TYPO3-Site-Factory', 7 | 'category' => 'module', 8 | 9 | 'constraints' => [ 10 | 'depends' => [ 11 | 'php' => '5.5.0', 12 | 'extbase' => '6.2', 13 | 'fluid' => '6.2', 14 | 'typo3' => '6.2.0-7.6.99' 15 | ], 16 | 'conflicts' => [], 17 | 'suggests' => [] 18 | ], 19 | 20 | 'author' => 'Romain CANON', 21 | 'author_email' => 'romain.hydrocanon@gmail.com', 22 | 23 | 'shy' => '', 24 | 'priority' => '', 25 | 'module' => '', 26 | 'internal' => '', 27 | 'uploadfolder' => true, 28 | 'createDirs' => 'uploads/tx_sitefactory/_processed_/', 29 | 'modify_tables' => '', 30 | 'clearCacheOnLoad' => 1, 31 | 'lockType' => '' 32 | ]; 33 | -------------------------------------------------------------------------------- /Classes/Form/Validation/HexadecimalValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class HexadecimalValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value matches a hexadecimal value. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | if (!preg_match('/^#[0123456789ABCDEF]{6}$/', strtoupper($field->getValue()))) { 33 | $this->addError( 34 | $this->translateErrorMessage( 35 | 'fields.validation.wrong_hexadecimal_value', 36 | Core::getExtensionKey(), 37 | ['s' => $field->getValue()] 38 | ), 39 | 1430127326 40 | ); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Classes/Duplication/Process/BackendConstantsAssignationProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 17 | use Romm\SiteFactory\Utility\ConstantManagerUtility; 18 | 19 | /** 20 | * Class containing functions called when a site is being duplicated. 21 | * See function "run" for more information. 22 | */ 23 | class BackendConstantsAssignationProcess extends AbstractDuplicationProcess 24 | { 25 | 26 | /** 27 | * Manages the constants by setting the new values put in the fields, and 28 | * linking the pages of the model site with the duplicated one's. 29 | */ 30 | public function run() 31 | { 32 | ConstantManagerUtility::manageTemplateConstants( 33 | $this->getModelPageUid(), 34 | $this->getDuplicatedPageUid(), 35 | $this->getFieldsValues(), 36 | $this->getDuplicationData('pagesUidAssociation') 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Classes/Domain/Repository/PagesRepository.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Domain\Repository; 15 | 16 | use TYPO3\CMS\Extbase\Persistence\Repository; 17 | use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; 18 | 19 | /** 20 | * The repository for the "Pages" model. 21 | */ 22 | class PagesRepository extends Repository 23 | { 24 | 25 | /** 26 | * Finds a page without taking care of the deleted/hidden flags and the 27 | * storage page. 28 | * 29 | * @param int $uid The uid of the page you want to find. 30 | * @return array|QueryResultInterface 31 | */ 32 | public function findByUidWithoutCondition($uid) 33 | { 34 | $query = $this->createQuery(); 35 | $query->getQuerySettings()->setRespectStoragePage(false); 36 | $query->getQuerySettings()->setIgnoreEnableFields(true); 37 | 38 | $query->matching( 39 | $query->equals('uid', $uid) 40 | ); 41 | 42 | return $query->execute(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Classes/Form/Validation/NotEmptyValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class NotEmptyValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the field value is not empty. 27 | * 28 | * @param AbstractField $field The field. 29 | */ 30 | protected function isValid($field) 31 | { 32 | $value = $field->getValue(); 33 | if ( 34 | $value === null || 35 | $value === '' || 36 | (is_array($value) && empty($value)) 37 | ) { 38 | $this->addError( 39 | $this->translateErrorMessage( 40 | 'fields.validation.empty_value', 41 | Core::getExtensionKey() 42 | ), 43 | 1431103424 44 | ); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Classes/Form/Validation/SelectOptionsValidator.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Validation; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\SelectField; 18 | 19 | /** 20 | * Custom validator for the Site Factory. 21 | */ 22 | class SelectOptionsValidator extends AbstractValidator 23 | { 24 | 25 | /** 26 | * Checks if the select field current value is in its available options. If 27 | * not, an error is thrown. 28 | * 29 | * @param SelectField $field The select field. 30 | */ 31 | protected function isValid($field) 32 | { 33 | if (!array_key_exists($field->getValue(), $field->getOptions())) { 34 | $this->addError( 35 | $this->translateErrorMessage( 36 | 'fields.validation.select.wrong_option_value', 37 | Core::getExtensionKey(), 38 | ['s' => $field->getValue()] 39 | ), 40 | 1430127401 41 | ); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Classes/ViewHelpers/AddSlashesViewHelper.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\ViewHelpers; 15 | 16 | use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper; 17 | 18 | /** 19 | * ViewHelper to escape a string. 20 | */ 21 | class AddSlashesViewHelper extends AbstractTagBasedViewHelper 22 | { 23 | 24 | /** 25 | * Escapes a string. 26 | * 27 | * @param string $value String to format. 28 | * @param bool $onlyDoubleQuotes If only the double quotes should be escaped. 29 | * @return string The altered string. 30 | */ 31 | public function render($value = null, $onlyDoubleQuotes = false) 32 | { 33 | if ($value === null) { 34 | $value = $this->renderChildren(); 35 | } 36 | if (!is_string($value)) { 37 | return $value; 38 | } 39 | 40 | if ($onlyDoubleQuotes) { 41 | $return = addcslashes($value, '\"'); 42 | } else { 43 | $return = addslashes($value); 44 | } 45 | 46 | return $return; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Resources/Private/Partials/New/FieldBody.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 |
4 | 15 |
16 |
17 | 18 |
19 | 20 | 21 |
22 | 30 | 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /Classes/Form/Fields/ColorPickerField.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | use Romm\SiteFactory\Form\Validation\HexadecimalValidator; 17 | 18 | /** 19 | * A class allowing to manage the fields configuration. 20 | * 21 | * See $allowedFieldTypes, $requiredFieldsConfiguration and $translatedFields 22 | * for further information. 23 | */ 24 | class ColorPickerField extends TextField 25 | { 26 | 27 | /** @var mixed The field type of the field : text, checkbox, select, etc.. */ 28 | protected $fieldType = AbstractField::FIELD_TYPE_TEXT; 29 | 30 | /** @var array Array containing the JavaScript files which will be imported. */ 31 | protected $javaScriptFilesNewAction = [ 32 | 'EXT:site_factory/Resources/Public/JavaScript/Fields/SiteFactory.Field.ColorPicker.js' 33 | ]; 34 | 35 | /** @var array Array containing the default rules for the field. */ 36 | protected $localValidation = [ 37 | 'hexadecimal' => [ 38 | 'validator' => HexadecimalValidator::class, 39 | 'error' => 'form.field.error.picker_color' 40 | ] 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /Classes/Form/Fields/CheckboxField.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | /** 17 | * A class allowing to manage the fields configuration. 18 | * 19 | * See $allowedFieldTypes, $requiredFieldsConfiguration and $translatedFields 20 | * for further information. 21 | */ 22 | class CheckboxField extends AbstractField 23 | { 24 | 25 | /** @var array Array containing the properties that must be filled for the field. */ 26 | protected $requiredFieldsConfiguration = []; 27 | 28 | /** @var mixed The field type of the field : text, checkbox, select, etc.. */ 29 | protected $fieldType = AbstractField::FIELD_TYPE_CHECKBOX; 30 | 31 | /** 32 | * Returns a human readable version of the value. Useful for select fields, 33 | * for example. 34 | * Override this function in your class if you want a custom behavior. 35 | * 36 | * @return mixed The value of the field, in a human readable version. 37 | */ 38 | public function getDisplayValue() 39 | { 40 | if ($this->value) { 41 | $value = '✓'; 42 | } else { 43 | $value = $this->value; 44 | } 45 | 46 | return $value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Classes/Core/CacheManager.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Core; 15 | 16 | use \TYPO3\CMS\Core\Cache\CacheManager as TYPO3CacheManager; 17 | use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; 18 | 19 | /** 20 | * Class managing the cache instances for the extension. 21 | */ 22 | class CacheManager 23 | { 24 | 25 | const CACHE_MAIN = 'cache_site_factory_main'; 26 | const CACHE_PROCESSED = 'cache_site_factory_processed'; 27 | 28 | /** 29 | * @var TYPO3CacheManager 30 | */ 31 | protected static $cacheManager; 32 | 33 | /** 34 | * Returns a cache instance. 35 | * 36 | * @param string $name Name of the cache. Must be one of the class' constants. 37 | * @return FrontendInterface 38 | */ 39 | public static function getCacheInstance($name) 40 | { 41 | if (!self::$cacheManager) { 42 | self::$cacheManager = Core::getObjectManager()->get(TYPO3CacheManager::class); 43 | } 44 | 45 | $cacheInstance = null; 46 | switch ($name) { 47 | case self::CACHE_MAIN: 48 | case self::CACHE_PROCESSED: 49 | $cacheInstance = self::$cacheManager->getCache($name); 50 | } 51 | 52 | return $cacheInstance; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Resources/Private/Layouts/Backend/Default.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 | 4 | 16 | 17 | 21 | 22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
-------------------------------------------------------------------------------- /ext_tables.php: -------------------------------------------------------------------------------- 1 | 'index,new,submit,processCopy,help', 17 | 'Duplication' => 'ajaxProcessDuplication', 18 | 'Ajax' => 'dispatch' 19 | ], 20 | [ 21 | 'access' => 'user,group', 22 | 'icon' => 'EXT:' . $extensionKey . '/ext_icon.png', 23 | 'labels' => 'LLL:EXT:' . $extensionKey . '/Resources/Private/Language/locallang_sitefactory.xlf', 24 | ] 25 | ); 26 | } 27 | 28 | \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile($extensionKey, 'Configuration/TypoScript/Default', 'Site Factory - Default settings'); 29 | \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile($extensionKey, 'Configuration/TypoScript/FieldsExample', 'Site Factory - Fields example'); 30 | 31 | // Extending locallang files. 32 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['EXT:' . $extensionKey . '/Resources/Private/Language/locallang.xlf'] = [ 33 | 'EXT:' . $extensionKey . '/Resources/Private/Language/FineUploader.xlf', 34 | 'EXT:' . $extensionKey . '/Resources/Private/Language/FieldsLocallang.xlf' 35 | ]; 36 | }, 37 | $_EXTKEY 38 | ); 39 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/SiteFactory.Main.js: -------------------------------------------------------------------------------- 1 | // Declaring SiteFactory namespace. 2 | window.SiteFactory = { 3 | ajaxUrl: '' 4 | }; 5 | 6 | // Instantiating tooltips. 7 | jQuery(document).ready(function() { 8 | jQuery('.factory-tooltip').tooltip({ 9 | container: 'body' 10 | }); 11 | }); 12 | 13 | /** 14 | * Converts an integer timer to a human-readable string. 15 | * Example: "100" will be converted to "01:40". 16 | * 17 | * @returns {string} The converted date. 18 | */ 19 | String.prototype.toHHMMSS = function () { 20 | var sec_num = parseInt(this, 10); 21 | var hours = Math.floor(sec_num / 3600); 22 | var minutes = Math.floor((sec_num - (hours * 3600)) / 60); 23 | var seconds = sec_num - (hours * 3600) - (minutes * 60); 24 | 25 | if (hours < 10) {hours = '0' + hours;} 26 | if (minutes < 10) {minutes = '0' + minutes;} 27 | if (seconds < 10) {seconds = '0' + seconds;} 28 | 29 | var time = ''; 30 | if (hours != 0) time += hours + ':'; 31 | time += minutes + ':' + seconds; 32 | 33 | return time; 34 | }; 35 | 36 | /** 37 | * Conflict between jQuery and prototype: we need to overload the following 38 | * function to get the Bootstrap's tooltip plugin work fine. 39 | */ 40 | jQuery.fn.tooltip.Constructor.prototype.hide = function () { 41 | var that = this; 42 | var jQuerytip = this.tip(); 43 | var e = jQuery.Event('hide.bs.' + this.type); 44 | this.$element.removeAttr('aria-describedby'); 45 | function complete() { 46 | if (that.hoverState != 'in') jQuerytip.detach(); 47 | that.$element.trigger('hidden.bs.' + that.type); 48 | } 49 | this.$element.triggerHandler(e); // Here's the modification: "triggerHandler" instead of "trigger" 50 | if (e.isDefaultPrevented()) return; 51 | jQuerytip.removeClass('in'); 52 | jQuery.support.transition && this.$tip.hasClass('fade') ? 53 | jQuerytip 54 | .one('bsTransitionEnd', complete) 55 | .emulateTransitionEnd(150) : 56 | complete(); 57 | this.hoverState = null; 58 | return this; 59 | }; -------------------------------------------------------------------------------- /Resources/Private/Partials/Form/Fields/ImageUpload.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |   19 |
20 |
21 |
22 | 23 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Classes/Domain/Model/Pages.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Domain\Model; 15 | 16 | use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; 17 | 18 | /** 19 | * Model for the database "pages" table. 20 | */ 21 | class Pages extends AbstractEntity 22 | { 23 | 24 | /** 25 | * The title of the page. 26 | * 27 | * @var string 28 | */ 29 | protected $title = ''; 30 | 31 | /** 32 | * The hidden flag of the page. 33 | * 34 | * @var int 35 | */ 36 | protected $hidden = 0; 37 | 38 | /** 39 | * The deleted flag of the page. 40 | * 41 | * @var int 42 | */ 43 | protected $deleted = 0; 44 | 45 | /********************* 46 | * SETTERS & GETTERS * 47 | *********************/ 48 | /** 49 | * @return string 50 | */ 51 | public function getTitle() 52 | { 53 | return $this->title; 54 | } 55 | 56 | /** 57 | * @param string 58 | */ 59 | public function setTitle($title) 60 | { 61 | $this->title = $title; 62 | } 63 | 64 | /** 65 | * @return int 66 | */ 67 | public function getHidden() 68 | { 69 | return $this->hidden; 70 | } 71 | 72 | /** 73 | * @param int 74 | */ 75 | public function setHidden($hidden) 76 | { 77 | $this->hidden = $hidden; 78 | } 79 | 80 | /** 81 | * @return int 82 | */ 83 | public function getDeleted() 84 | { 85 | return $this->deleted; 86 | } 87 | 88 | /** 89 | * @param int 90 | */ 91 | public function setDeleted($deleted) 92 | { 93 | $this->deleted = $deleted; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /ext_localconf.php: -------------------------------------------------------------------------------- 1 | 'TYPO3\\CMS\\Core\\Cache\\Backend\\SimpleFileBackend', 14 | 'frontend' => 'TYPO3\\CMS\\Core\\Cache\\Frontend\\VariableFrontend', 15 | 'groups' => ['all', 'system', 'pages'] 16 | ]; 17 | 18 | // Registering the caches for this extension. 19 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_' . $extensionKey . '_processed'] = [ 20 | 'backend' => 'TYPO3\\CMS\\Core\\Cache\\Backend\\SimpleFileBackend', 21 | 'frontend' => 'TYPO3\\CMS\\Core\\Cache\\Frontend\\VariableFrontend', 22 | 'options' => [ 23 | 'cacheDirectory' => Romm\SiteFactory\Core\Core::getProcessedFolderPath() 24 | ] 25 | ]; 26 | 27 | // Including main TypoScript files. 28 | $includeTypoScriptSyntax = ''; 29 | $typoScriptFiles = [ 30 | 'EXT:' . $extensionKey . '/Configuration/TypoScript/Default/DefaultConfiguration.ts', 31 | 'EXT:' . $extensionKey . '/Configuration/TypoScript/Default/FieldsTypesConfiguration.ts', 32 | 'EXT:' . $extensionKey . '/Configuration/TypoScript/Default/FieldsConfiguration.ts', 33 | 'EXT:' . $extensionKey . '/Configuration/TypoScript/Default/DuplicationConfiguration.ts' 34 | ]; 35 | foreach ($typoScriptFiles as $filePath) { 36 | \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup(sprintf($includeTypoScriptSyntax, $filePath)); 37 | } 38 | } 39 | }, 40 | $_EXTKEY 41 | ); 42 | -------------------------------------------------------------------------------- /Classes/ViewHelpers/Be/ImportAssetViewHelper.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\ViewHelpers\Be; 15 | 16 | use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper; 17 | use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; 18 | 19 | /** 20 | * ViewHelper to include CSS or JavaScript assets. 21 | */ 22 | class ImportAssetViewHelper extends AbstractBackendViewHelper 23 | { 24 | 25 | /** 26 | * Includes the given CSS or JavaScript files. 27 | * 28 | * @param array $cssFiles CSS files. 29 | * @param array $jsFiles JavaScript files. 30 | */ 31 | public function render($cssFiles = [], $jsFiles = []) 32 | { 33 | $pageRenderer = (version_compare(TYPO3_version, '7.0', '<')) 34 | ? $this->getDocInstance()->getPageRenderer() 35 | : $this->getPageRenderer(); 36 | 37 | foreach ($cssFiles as $value) { 38 | $path = $this->getFileRealPath($value); 39 | $pageRenderer->addCssFile($path); 40 | } 41 | 42 | foreach ($jsFiles as $value) { 43 | $path = $this->getFileRealPath($value); 44 | $pageRenderer->addJsLibrary($path, $path); 45 | } 46 | } 47 | 48 | /** 49 | * Returns a file path correct value by finding the 'EXT:xxx' values. 50 | * 51 | * @param string $path The path to the file. 52 | * @return string The correct path; 53 | */ 54 | private function getFileRealPath($path) 55 | { 56 | if (preg_match('/^EXT:([^\/]*)\/(.*)$/', $path, $res)) { 57 | $extRelPath = ExtensionManagementUtility::extRelPath($res[1]); 58 | $path = str_replace('EXT:' . $res[1] . '/', $extRelPath, $path); 59 | } 60 | 61 | return $path; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Index/DuplicatedSitesList.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 |
6 |
7 | 8 |

{site.configuration.page.title}

9 | 10 |

11 | 12 | @{site.date} 13 |

14 | 15 |
16 |
17 |
    18 | 19 | 20 |
  • 21 | 22 | 23 | 24 | 25 | {field.label} 26 | 27 |
    28 | 29 | 30 | 31 |
  • 32 |
    33 |
    34 |
35 |
36 |
37 | 38 |
39 | 40 |   41 | 42 |
43 |
44 | 45 |
46 | 47 |
48 | 49 | 50 | 51 | 52 |
53 |
54 |
55 |
56 |
-------------------------------------------------------------------------------- /Classes/Duplication/Process/LinkToPageBackendLayoutProcess.php: -------------------------------------------------------------------------------- 1 | 12 | // * All rights reserved 13 | // * 14 | // * This script is part of the TYPO3 project. The TYPO3 project is 15 | // * free software; you can redistribute it and/or modify 16 | // * it under the terms of the GNU General Public License as published by 17 | // * the Free Software Foundation; either version 3 of the License, or 18 | // * (at your option) any later version. 19 | // * 20 | // * The GNU General Public License can be found at 21 | // * http://www.gnu.org/copyleft/gpl.html. 22 | // * 23 | // * This script is distributed in the hope that it will be useful, 24 | // * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | // * GNU General Public License for more details. 27 | // * 28 | // * This copyright notice MUST APPEAR in all copies of the script! 29 | // ***************************************************************/ 30 | // 31 | //use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 32 | // 33 | ///** 34 | // * Class containing functions called when a site is being duplicated. 35 | // */ 36 | //class LinkToPageBackendLayoutProcess extends AbstractDuplicationProcess { 37 | // public function ajaxRun(&$params) { 38 | // $data = array(); 39 | // if (isset($params['fieldsValues']['pageBackendLayout'])) { 40 | // $data['backend_layout'] = $params['fieldsValues']['pageBackendLayout']; 41 | // } 42 | // if (isset($params['fieldsValues']['subPagesBackendLayout'])) { 43 | // $data['backend_layout_next_level'] = $params['fieldsValues']['subPagesBackendLayout']; 44 | // } 45 | // 46 | // if (!empty($data)) { 47 | // $this->database->exec_UPDATEquery( 48 | // 'pages', 49 | // 'uid=' . intval($params['duplicationData']['duplicatedPageUid']), 50 | // $data 51 | // ); 52 | // } 53 | // } 54 | //} 55 | -------------------------------------------------------------------------------- /Classes/Duplication/Process/SaveSiteConfigurationProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Domain\Model\Save; 18 | use Romm\SiteFactory\Domain\Repository\SaveRepository; 19 | use TYPO3\CMS\Core\Utility\ArrayUtility; 20 | use TYPO3\CMS\Core\Utility\GeneralUtility; 21 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 22 | 23 | /** 24 | * Class containing functions called when a site is being duplicated. 25 | * See function "run" for more information. 26 | */ 27 | class SaveSiteConfigurationProcess extends AbstractDuplicationProcess 28 | { 29 | 30 | /** 31 | * When a site is being saved, this function will save all the fields values 32 | * in the DataBase, for further usage. 33 | */ 34 | public function run() 35 | { 36 | $objectManager = Core::getObjectManager(); 37 | 38 | /** @var SaveRepository $saveRepository */ 39 | $saveRepository = $objectManager->get(SaveRepository::class); 40 | 41 | $saveObject = $saveRepository->findOneByRootPageUid($this->getDuplicatedPageUid()); 42 | 43 | $newObject = false; 44 | if (empty($saveObject)) { 45 | $newObject = true; 46 | /** @var Save $saveObject */ 47 | $saveObject = GeneralUtility::makeInstance(Save::class); 48 | $saveObject->setRootPageUid($this->getDuplicatedPageUid()); 49 | } 50 | 51 | $configuration = $this->getDuplicationData(); 52 | ArrayUtility::mergeRecursiveWithOverrule( 53 | $configuration, 54 | ['fieldsValues' => $this->getFieldsValues()] 55 | ); 56 | $saveObject->setConfiguration(json_encode($configuration)); 57 | 58 | if ($newObject) { 59 | $saveRepository->add($saveObject); 60 | } else { 61 | $saveRepository->update($saveObject); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Classes/Form/Fields/TextField.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | 18 | /** 19 | * A class allowing to manage the fields configuration. 20 | * 21 | * See $allowedFieldTypes, $requiredFieldsConfiguration and $translatedFields 22 | * for further information. 23 | */ 24 | class TextField extends AbstractField 25 | { 26 | 27 | /** @var mixed The field type of the field : text, checkbox, select, etc.. */ 28 | protected $fieldType = AbstractField::FIELD_TYPE_TEXT; 29 | 30 | /** @var array Array containing the JavaScript files which will be imported. */ 31 | protected $javaScriptFilesNewAction = [ 32 | 'EXT:site_factory/Resources/Public/JavaScript/Fields/SiteFactory.Field.Text.js' 33 | ]; 34 | 35 | /** @var array Array containing the properties that must be filled for the field. */ 36 | protected $requiredFieldsConfiguration = []; 37 | 38 | /** 39 | * The placeholder of the field, mainly useful for the form. 40 | * 41 | * @var string 42 | * @fill 43 | */ 44 | protected $placeholder = ''; 45 | 46 | /** 47 | * Sets the default value of the field. 48 | * 49 | * @param string $defaultValue 50 | * @return $this 51 | */ 52 | public function setDefaultValue($defaultValue) 53 | { 54 | $this->defaultValue = Core::translate((string)$defaultValue); 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * Sets the placeholder of the field. 61 | * 62 | * @param string $placeholder 63 | * @return $this 64 | */ 65 | public function setPlaceholder($placeholder) 66 | { 67 | $this->placeholder = Core::translate((string)$placeholder); 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * @return string The placeholder of the field. 74 | */ 75 | public function getPlaceholder() 76 | { 77 | return $this->placeholder; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Classes/Form/Fields/ImageUploadPickerField.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * This file is part of the TYPO3 Site Factory project. 7 | * It is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License, either 9 | * version 3 of the License, or any later version. 10 | * 11 | * For the full copyright and license information, see: 12 | * http://www.gnu.org/licenses/gpl-3.0.html 13 | */ 14 | 15 | namespace Romm\SiteFactory\Form\Fields; 16 | 17 | /** 18 | * A class allowing to manage the fields configuration. 19 | * 20 | * See $allowedFieldTypes, $requiredFieldsConfiguration and $translatedFields 21 | * for further information. 22 | */ 23 | class ImageUploadPickerField extends AbstractField 24 | { 25 | 26 | /** @var mixed The field type of the field : text, checkbox, select, etc.. */ 27 | protected $fieldType = AbstractField::FIELD_TYPE_HIDDEN; 28 | 29 | /** @var array Array containing the JavaScript files which will be imported. */ 30 | protected $javaScriptFilesNewAction = [ 31 | 'EXT:site_factory/Resources/Public/Contrib/fine-uploader/fine-uploader.min.js', 32 | 'EXT:site_factory/Resources/Public/JavaScript/Fields/SiteFactory.Field.ImageUploadPicker.js' 33 | ]; 34 | 35 | /** @var array Array containing the JavaScript files which will be imported. */ 36 | protected $cssFilesNewAction = [ 37 | 'EXT:site_factory/Resources/Public/Contrib/fine-uploader/fine-uploader-new.min.css', 38 | 'EXT:site_factory/Resources/Public/StyleSheets/site-factory-fine-uploader.css' 39 | ]; 40 | 41 | /** @var array Array containing the properties that must be filled for the field. */ 42 | protected $requiredFieldsConfiguration = []; 43 | 44 | /** 45 | * Returns a human readable version of the value. Useful for select fields, 46 | * for example. 47 | * Override this function in your class if you want a custom behavior. 48 | * 49 | * @return mixed The value of the field, in a human readable version. 50 | */ 51 | public function getDisplayValue() 52 | { 53 | if (substr($this->getValue(), 0, 4) == 'new:') { 54 | $value = substr($this->getValue(), 4, strlen($this->getValue()) - 4); 55 | } else { 56 | $value = $this->value; 57 | } 58 | 59 | return $value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Classes/Form/Fields/FieldsTypes.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | use Romm\SiteFactory\Utility\TypoScriptUtility; 17 | 18 | /** 19 | * @todo 20 | */ 21 | class FieldsTypes 22 | { 23 | 24 | /** @var array Cache for the fields types configuration for various pages. */ 25 | private static $fieldsTypes = []; 26 | 27 | /** 28 | * Returns an array containing the fields types configuration, based on the 29 | * TypoScript configuration of a given page. 30 | * 31 | * @param $pageUid int The id of the page you want the configuration of. 32 | * @return array The fields types configuration as an array : key is the name and value is the configuration. 33 | * @throws \Exception 34 | */ 35 | public static function getFieldsTypesConfiguration($pageUid) 36 | { 37 | if (!isset(self::$fieldsTypes[$pageUid])) { 38 | $fieldsTypesConfiguration = TypoScriptUtility::getExtensionConfigurationFromPath('fieldsTypes', $pageUid); 39 | self::$fieldsTypes[$pageUid] = []; 40 | 41 | foreach ($fieldsTypesConfiguration as $fieldTypeName => $fieldTypeConfiguration) { 42 | if (!isset($fieldTypeConfiguration['class'])) { 43 | throw new \Exception('The field type "' . $fieldTypeName . '" should have a value for "class"', 1423770370); 44 | } 45 | 46 | self::$fieldsTypes[$pageUid][$fieldTypeName] = $fieldTypeConfiguration; 47 | } 48 | } 49 | 50 | return self::$fieldsTypes[$pageUid]; 51 | } 52 | 53 | /** 54 | * Returns the different fields types names, based on the TypoScript 55 | * configuration of a given page. 56 | * 57 | * @param $pageUid int The id of the page you want the fields types of. 58 | * @return array The fields types names. 59 | */ 60 | public static function getFieldsTypes($pageUid) 61 | { 62 | $fieldsTypesConfiguration = self::getFieldsTypesConfiguration($pageUid); 63 | 64 | return array_keys($fieldsTypesConfiguration); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Resources/Private/Language/FineUploader.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Upload message 9 | 10 | 11 | 12 | Drop files here 13 | 14 | 15 | Select file 16 | 17 | 18 | Processing dropped files… 19 | 20 | 21 | Retry 22 | 23 | 24 | 25 | {file} is empty, please select files again without it. 26 | 27 | 28 | Image is too tall. 29 | 30 | 31 | Image is too wide. 32 | 33 | 34 | Image is not tall enough. 35 | 36 | 37 | Image is not wide enough. 38 | 39 | 40 | {file} is too small, minimum file size is {minSizeLimit}. 41 | 42 | 43 | No files to upload. 44 | 45 | 46 | The files are being uploaded, if you leave now the upload will be canceled. 47 | 48 | 49 | Retry failed - you have reached your file limit. 50 | 51 | 52 | {file} is too large, maximum file size is {sizeLimit}. 53 | 54 | 55 | Too many items ({netItems}) would be uploaded. Item limit is {itemLimit}. 56 | 57 | 58 | {file} has an invalid extension. Valid extension(s): {extensions}. 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Classes/Form/FieldsConfigurationPresets.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form; 15 | 16 | use TYPO3\CMS\Backend\Utility\BackendUtility; 17 | use TYPO3\CMS\Backend\View\BackendLayoutView; 18 | use TYPO3\CMS\Core\Utility\GeneralUtility; 19 | use Romm\SiteFactory\Core\Core; 20 | 21 | /** 22 | * Class containing custom user functions for fields' TypoScript configuration. 23 | */ 24 | class FieldsConfigurationPresets 25 | { 26 | 27 | /** 28 | * Gets an ordered list of the pages with the "Model site" flag set. 29 | * 30 | * @return array The model sites in an array. Empty array if no model was found. 31 | */ 32 | public static function getModelSitesList() 33 | { 34 | $modelSitesPid = Core::getExtensionConfiguration('modelSitesPid'); 35 | 36 | $aModelSites = BackendUtility::getRecordsByField('pages', 'pid', $modelSitesPid); 37 | $orderedModelSites = []; 38 | 39 | if (is_array($aModelSites)) { 40 | foreach ($aModelSites as $modelSite) { 41 | $orderedModelSites[$modelSite['uid']] = $modelSite['title'] . ' (' . $modelSite['uid'] . ')'; 42 | } 43 | } 44 | 45 | return $orderedModelSites; 46 | } 47 | 48 | /** 49 | * Gets an ordered list of the backend layouts. 50 | * 51 | * @return array The backend layouts in an array. Empty array if none was found. 52 | */ 53 | public static function getBackendLayoutsList() 54 | { 55 | /** @var BackendLayoutView $backendLayoutView */ 56 | $backendLayoutView = GeneralUtility::makeInstance(BackendLayoutView::class); 57 | 58 | $items = []; 59 | $params = [ 60 | 'table' => 'pages', 61 | 'field' => 'backend_layout', 62 | 'row' => BackendUtility::getRecord('pages', 1, '*'), // @todo: manage "page" uid! 63 | 'items' => &$items 64 | ]; 65 | $backendLayoutView->addBackendLayoutItems($params); 66 | 67 | $result = []; 68 | foreach ($GLOBALS['TCA']['pages']['columns']['backend_layout']['config']['items'] as $item) { 69 | $result[$item[1]] = Core::translate($item[0]); 70 | } 71 | foreach ($params['items'] as $item) { 72 | $result[$item[1]] = $item[0]; 73 | } 74 | 75 | return $result; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Classes/Form/FieldValidation.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | use Romm\SiteFactory\Utility\AjaxInterface; 19 | 20 | /** 21 | * Class containing functions allowing to check if a field is correctly filled, 22 | * depending on the configuration which has been set in TypoScript. 23 | * 24 | * See the following classes for further information: 25 | * - \Romm\SiteFactory\Core\Configuration\Configuration 26 | * - \Romm\SiteFactory\Core\Configuration\FieldsConfiguration 27 | */ 28 | class FieldValidation implements AjaxInterface 29 | { 30 | 31 | /** 32 | * Checks if a given field is correctly filled. 33 | * 34 | * @param AbstractField $field The field. 35 | * @return array An array with 2 indexes: 36 | * - fieldLabel: The translated label of the field. 37 | * - validationResult: List of TYPO3\CMS\Extbase\Error\Result 38 | */ 39 | public function validateField(AbstractField $field) 40 | { 41 | $fieldValidation = ['fieldLabel' => Core::translate($field->getLabel())]; 42 | 43 | $validationResult = $field->validate()->getValidationResult(); 44 | $fieldValidation['validationResult'] = $validationResult; 45 | 46 | return $fieldValidation; 47 | } 48 | 49 | /** 50 | * Ajax implementation of the function "validateField". Will display the 51 | * result encoded in JSON. 52 | * 53 | * @param array $arguments Parameters. 54 | * @return string JSON encoded result. 55 | */ 56 | public function ajaxValidateField($arguments) 57 | { 58 | // @todo : Exception ? 59 | if (!isset($arguments['fieldName']) || !isset($arguments['value']) || !isset($arguments['pageUid'])) { 60 | return ''; 61 | } 62 | 63 | // @todo : check pageUid 64 | $field = Fields\Field::getField($arguments['fieldName'], $arguments['pageUid']); 65 | $field->setValue($arguments['value']); 66 | 67 | $validation = $this->validateField($field); 68 | 69 | $validationResult = Core::convertValidationResultToArray($validation['validationResult']); 70 | 71 | $validation['validationResult'] = $validationResult; 72 | 73 | return $validation; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Resources/Private/Templates/Administration/Index.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 | 4 | 5 | ############################## 6 | 7 | The index page of the module, which contains: 8 | - A list of all the model sites 9 | - A list of all the already duplicated sites 10 | 11 | Useful variables: 12 | - {modelSitesList}: array of model sites. 13 | - {savedSites}: array of already duplicated sites. 14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 |

22 | 23 | 24 |

25 | 26 |
27 | 28 |

29 | 30 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 |   50 |
51 |
52 |
53 | 54 |
55 | 56 |

57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 |    65 |
66 |
67 |
68 |
69 | 70 |
71 | 72 | ############################## 73 | 74 | Contains the buttons which will appear in the header menu. 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/Fields/SiteFactory.Field.ImageUploadPicker.js: -------------------------------------------------------------------------------- 1 | SiteFactory.FineUploaderDefaultSettings = function() { 2 | // For further Site Factory usage. 3 | this.element = null; 4 | this.formElement = null; 5 | this.fieldName = ''; 6 | 7 | // Validation settings for files. 8 | this.validation = { 9 | allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'], 10 | itemLimit: 1, 11 | sizeLimit: 409600000 // 400 kB = 400 * 1024 bytes 12 | }; 13 | 14 | // Custom classes added to element on certain events. 15 | this.classes = { 16 | fail: 'alert alert-danger counter-errors', 17 | success: 'alert alert-info' 18 | }; 19 | 20 | // Id of the HTML element containing the Fine Uploader template. 21 | this.template = 'qq-template-validation'; 22 | 23 | // TYPO3 request handler when a new file is added. 24 | this.request = { 25 | endpoint: SiteFactory.ajaxUrl, 26 | paramsInBody: false, 27 | params: { 28 | //ajaxID: 'ajaxDispatcher', 29 | request: { 30 | function: 'Romm\\SiteFactory\\Utility\\FileUtility->ajaxMoveUploadedFileToSiteFactoryFolder' 31 | } 32 | } 33 | }; 34 | 35 | // Request managing the already existing files for a field. 36 | this.session = { 37 | endpoint: SiteFactory.ajaxUrl, 38 | params: { 39 | //ajaxID: 'ajaxDispatcher', 40 | request: { 41 | function: 'Romm\\SiteFactory\\Utility\\FileUtility->getExistingFiles' 42 | }, 43 | fieldSettings: null 44 | } 45 | }; 46 | 47 | // Setting up the delete functionality. 48 | this.deleteFile = { 49 | enabled: true, 50 | forceConfirm: true, 51 | endpoint: SiteFactory.ajaxUrl + '&dummy=' 52 | }; 53 | 54 | // Paths to the thumbnails. 55 | this.thumbnails = { 56 | placeholders: { 57 | waitingPath: '', 58 | notAvailablePath: '' 59 | } 60 | }; 61 | 62 | // Initializing the messages object which are filled in the Fluid template. 63 | this.messages = {}; 64 | 65 | // Callback functions customization. 66 | this.callbacks = { 67 | // When a file has been uploaded. 68 | onComplete: function(id, name, response) { 69 | // Changing the value of the form element to the path of the file. 70 | var formElement = window[this._options.formId]; 71 | var fieldName = this._options.fieldName; 72 | var fieldElement = formElement.getFieldByName(fieldName); 73 | fieldElement.input.val('new:' + response['tmpFilePath']); 74 | }, 75 | 76 | // When a delete request is sent. 77 | onSubmitDelete: function (id) { 78 | this.setDeleteFileParams( 79 | { 80 | fileName: this.getUuid(id), 81 | request: { 82 | function: 'Romm\\SiteFactory\\Utility\\FileUtility->deleteFile' 83 | } 84 | }, 85 | id 86 | ); 87 | }, 88 | 89 | // When a delete request has been done. 90 | onDeleteComplete: function () { 91 | // Emptying the value of the form element. 92 | var formElement = window[this._options.formId]; 93 | var fieldName = this._options.fieldName; 94 | var fieldElement = formElement.getFieldByName(fieldName); 95 | fieldElement.input.val(''); 96 | } 97 | }; 98 | }; -------------------------------------------------------------------------------- /Classes/Utility/ExtensionManagerUtility.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Utility; 15 | 16 | use TYPO3\CMS\Core\Utility\GeneralUtility; 17 | use Romm\SiteFactory\Core\Core; 18 | 19 | /** 20 | * Set of functions for the extension's configuration in the extension manager. 21 | */ 22 | class ExtensionManagerUtility 23 | { 24 | 25 | /** 26 | * Returns a select containing all the Backend user groups. 27 | * 28 | * @param array $options Current options of the field. 29 | * @return string The HTML code containing the '; 34 | 35 | $backendUserGroups = GeneralUtility::array_merge( 36 | [0 => ['uid' => -1, 'title' => '']], 37 | Core::getDatabase()->exec_SELECTgetRows( 38 | 'uid, title', 39 | 'be_groups', 40 | '1=1' 41 | ) 42 | ); 43 | 44 | foreach ($backendUserGroups as $group) { 45 | $selected = ($group['uid'] == $options['fieldValue']) ? ' selected="selected"' : ''; 46 | $uidLabel = ($group['uid'] != -1) ? ' [' . $group['uid'] . ']' : ''; 47 | 48 | $html .= ''; 49 | } 50 | 51 | $html .= ''; 52 | 53 | return $html; 54 | } 55 | 56 | /** 57 | * Returns a select containing all the Backend users. 58 | * 59 | * @param array $options Current options of the field. 60 | * @return string The HTML code containing the '; 65 | 66 | $backendUserGroups = GeneralUtility::array_merge( 67 | [0 => ['uid' => -1, 'title' => '']], 68 | Core::getDatabase()->exec_SELECTgetRows( 69 | 'uid, username', 70 | 'be_users', 71 | '1=1' 72 | ) 73 | ); 74 | 75 | foreach ($backendUserGroups as $group) { 76 | $selected = ($group['uid'] == $options['fieldValue']) ? ' selected="selected"' : ''; 77 | $uidLabel = ($group['uid'] != -1) ? ' [' . $group['uid'] . ']' : ''; 78 | 79 | $html .= ''; 80 | } 81 | 82 | $html .= ''; 83 | 84 | return $html; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Configuration/TypoScript/FieldsExample/setup.txt: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | fields { 3 | # The domain name used for the new site. 4 | domain { 5 | type = text 6 | label = form.field.label.domain_name 7 | hint = form.field.hint.domain_name 8 | placeholder = www.example.com 9 | validation { 10 | # Obviously we want a correct domain name here... 11 | correctDomainName { 12 | validator = Romm\SiteFactory\Form\Validation\DomainNameValidator 13 | } 14 | } 15 | } 16 | 17 | # The color of the body's background. 18 | backgroundColor { 19 | type = color_picker 20 | label = form.field.label.background_color 21 | hint = form.field.hint.color_picker_color 22 | placeholder = #FFFFFF 23 | defaultValue = #FFFFFF 24 | } 25 | 26 | # The link to the Facebook page. 27 | facebook { 28 | type = text 29 | label = form.field.label.facebook 30 | hint = form.field.hint.facebook 31 | placeholder = https://www.facebook.com/Example 32 | validation { 33 | facebookUrl { 34 | validator = Romm\SiteFactory\Form\Validation\FacebookUrlValidator 35 | } 36 | } 37 | } 38 | 39 | # The link to the Twitter page. 40 | twitter { 41 | type = text 42 | label = form.field.label.twitter 43 | hint = form.field.hint.twitter 44 | placeholder = https://twitter.com/Example 45 | validation { 46 | twitterUrl { 47 | validator = Romm\SiteFactory\Form\Validation\TwitterUrlValidator 48 | } 49 | } 50 | } 51 | 52 | # The name for the duplicated be_users 53 | # backendUserUsername { 54 | # type = text 55 | # label = form.field.label.backend_user_username 56 | # hint = form.field.hint.backend_user_username 57 | # placeholder = form.field.label.backend_user_username 58 | # defaultValue = form.field.label.backend_user_username 59 | # validation { 60 | # # We want at least one character for the name. 61 | # noEmpty { 62 | # validator = Romm\SiteFactory\Form\Validation\NotEmptyValidator 63 | # } 64 | # } 65 | # } 66 | 67 | # The logo (image) of the site. 68 | logo { 69 | type = image_upload 70 | label = form.field.label.logo 71 | hint = form.field.hint.logo 72 | 73 | # Contains the HTML template of the uploader. 74 | partialsHeader.1 = Form/Fields/Header/ImageUploadHeader 75 | 76 | settings { 77 | # The file will be moved to the file mount created during the duplication. 78 | moveToFileMount = 1 79 | 80 | linkToPageMedia { 81 | page = homePage 82 | } 83 | 84 | itemLimit = 1 85 | # sizeLimit: 400 kB = 400 * 1024 bytes 86 | sizeLimit = 409600 87 | allowedExtensions { 88 | 0 = jpg 89 | 1 = jepg 90 | 2 = png 91 | 3 = gif 92 | } 93 | } 94 | } 95 | 96 | # The header (image) of the site. 97 | banner { 98 | type = image_upload 99 | label = form.field.label.banner 100 | hint = form.field.hint.banner 101 | 102 | settings { 103 | # The file will be moved to the file mount created during the duplication. 104 | moveToFileMount = 1 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /Classes/Utility/FileUtility.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Utility; 15 | 16 | use Romm\SiteFactory\Core\Core; 17 | use TYPO3\CMS\Core\Utility\GeneralUtility; 18 | use TYPO3\CMS\Core\Utility\PathUtility; 19 | 20 | /** 21 | * Set of functions to manipulate files. 22 | * Used with the JavaScript library FineUploader. 23 | * 24 | * @todo make me become a service 25 | */ 26 | class FileUtility implements AjaxInterface 27 | { 28 | 29 | const FILE_KEY = 'qqfile'; 30 | 31 | /** 32 | * Processing files handling. 33 | */ 34 | public function ajaxMoveUploadedFileToSiteFactoryFolder() 35 | { 36 | $file = (isset($_FILES[self::FILE_KEY])) ? 37 | $_FILES[self::FILE_KEY] : 38 | null; 39 | if ($file) { 40 | $fileExtension = strtolower(substr(strrchr($file['name'], '.'), 1)); 41 | $tmpFileName = md5(uniqid(rand(), true)) . '.' . $fileExtension; 42 | $tmpFilePath = PATH_site . Core::getProcessedFolderPath() . $tmpFileName; 43 | 44 | move_uploaded_file($file['tmp_name'], $tmpFilePath); 45 | 46 | return [ 47 | 'tmpFilePath' => $tmpFilePath, 48 | 'newUuid' => $tmpFileName, 49 | 'success' => true 50 | ]; 51 | } 52 | } 53 | 54 | /** 55 | * Deletes a specific file from the processing folder. 56 | */ 57 | public function deleteFile() 58 | { 59 | $fileName = GeneralUtility::_GP('fileName'); 60 | $filePath = PATH_site . Core::getProcessedFolderPath() . $fileName; 61 | if (file_exists($filePath)) { 62 | unlink($filePath); 63 | } 64 | } 65 | 66 | /** 67 | * Handles the existing files of a Fine Uploader form. 68 | * The values are stored in the GET/POST var at the index "fieldValue". 69 | * 70 | * @return string 71 | */ 72 | public function getExistingFiles() 73 | { 74 | $files = []; 75 | 76 | $fieldValue = GeneralUtility::_GP('fieldValue'); 77 | if ($fieldValue != '') { 78 | // $imagePath = GeneralUtility::getFileAbsFileName($fieldValue); 79 | $imagePath = str_replace('new:', '', $fieldValue); 80 | $imageName = PathUtility::basename($imagePath); 81 | $imageDirectoryPath = PathUtility::dirname($imagePath); 82 | $imageDirectoryPath = PathUtility::getRelativePath(PATH_site, $imageDirectoryPath); 83 | $imageUrl = GeneralUtility::locationHeaderUrl('/' . $imageDirectoryPath . $imageName); 84 | 85 | if (file_exists($imagePath)) { 86 | $files[] = [ 87 | 'name' => $imageName, 88 | 'uuid' => $imageUrl, 89 | 'thumbnailUrl' => $imageUrl 90 | ]; 91 | } 92 | } 93 | 94 | return $files; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Classes/Domain/Model/Save.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Domain\Model; 15 | 16 | use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; 17 | 18 | /** 19 | * Save model, containing information about the sites that have been duplicated 20 | * or modified. 21 | * 22 | * When a site is created/modified, the submitted form's data is saved in JSON 23 | * format in the property "configuration". 24 | */ 25 | class Save extends AbstractEntity 26 | { 27 | 28 | /** 29 | * The uid of the root page of the duplicated site. 30 | * 31 | * @var int 32 | */ 33 | protected $rootPageUid = 0; 34 | 35 | /** 36 | * Page 37 | * 38 | * @var Pages 39 | */ 40 | protected $page = null; 41 | 42 | /** 43 | * The date of the the creation/modification. 44 | * 45 | * @var int 46 | */ 47 | protected $date = 0; 48 | 49 | /** 50 | * The submitted form's data in JSON format. 51 | * 52 | * @var string 53 | */ 54 | protected $configuration = ''; 55 | 56 | /** 57 | * Construction function. 58 | * 59 | * @return Save 60 | */ 61 | public function __construct() 62 | { 63 | // Setting date to actual time. 64 | $this->setDate(time()); 65 | } 66 | 67 | /********************* 68 | * SETTERS & GETTERS * 69 | *********************/ 70 | /** 71 | * @param int 72 | */ 73 | public function setRootPageUid($rootPageUid) 74 | { 75 | $this->rootPageUid = $rootPageUid; 76 | } 77 | 78 | /** 79 | * @return int 80 | */ 81 | public function getRootPageUid() 82 | { 83 | return $this->rootPageUid; 84 | } 85 | 86 | /** 87 | * @param Pages 88 | */ 89 | public function setPage(Pages $page) 90 | { 91 | $this->page = $page; 92 | } 93 | 94 | /** 95 | * @return Pages 96 | */ 97 | public function getPage() 98 | { 99 | return $this->page; 100 | } 101 | 102 | /** 103 | * @param int 104 | */ 105 | public function setDate($date) 106 | { 107 | $this->date = $date; 108 | } 109 | 110 | /** 111 | * @return int 112 | */ 113 | public function getDate() 114 | { 115 | return $this->date; 116 | } 117 | 118 | /** 119 | * @param string 120 | */ 121 | public function setConfiguration($configuration) 122 | { 123 | $this->configuration = $configuration; 124 | } 125 | 126 | /** 127 | * @return array 128 | */ 129 | public function getConfiguration() 130 | { 131 | $configuration = (is_array($this->configuration)) 132 | ? $this->configuration 133 | : json_decode($this->configuration, true); 134 | 135 | return $configuration; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /Classes/Form/Fields/SelectField.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | use Romm\SiteFactory\Form\Validation\SelectOptionsValidator; 17 | use TYPO3\CMS\Core\Utility\GeneralUtility; 18 | 19 | /** 20 | * A class allowing to manage the fields configuration. 21 | * 22 | * See $allowedFieldTypes, $requiredFieldsConfiguration and $translatedFields 23 | * for further information. 24 | */ 25 | class SelectField extends AbstractField 26 | { 27 | 28 | /** @var mixed The field type of the field : text, checkbox, select, etc.. */ 29 | protected $fieldType = AbstractField::FIELD_TYPE_SELECT; 30 | 31 | /** @var array Array containing the JavaScript files which will be imported. */ 32 | protected $javaScriptFilesNewAction = [ 33 | 'EXT:site_factory/Resources/Public/JavaScript/Fields/SiteFactory.Field.Select.js' 34 | ]; 35 | 36 | /** @var array Array containing the default rules for the field. */ 37 | protected $localValidation = [ 38 | 'options' => [ 39 | 'validator' => SelectOptionsValidator::class, 40 | 'error' => 'form.field.error.picker_color' // @todo 41 | ] 42 | ]; 43 | 44 | /** @var array Array containing the properties that must be filled for the field. */ 45 | protected $requiredFieldsConfiguration = ['options']; 46 | 47 | /** 48 | * The options of the field, mainly useful for the form. 49 | * 50 | * @var 51 | * @fill 52 | */ 53 | protected $options; 54 | 55 | /** 56 | * Sets the options of the field. 57 | * 58 | * @param array|string $options 59 | * @return $this 60 | */ 61 | public function setOptions($options) 62 | { 63 | $this->options = $options; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * @return array The options of the field. 70 | */ 71 | public function getOptions() 72 | { 73 | if (!is_array($this->options)) { 74 | $params = []; 75 | $options = GeneralUtility::callUserFunction($this->options, $params, $this); 76 | if ($options) { 77 | $this->options = (array)$options; 78 | } 79 | } 80 | 81 | return $this->options; 82 | } 83 | 84 | /** 85 | * Returns a human readable version of the value. Useful for select fields, 86 | * for example. 87 | * Override this function in your class if you want a custom behavior. 88 | * 89 | * @return mixed The value of the field, in a human readable version. 90 | */ 91 | public function getDisplayValue() 92 | { 93 | $options = $this->getOptions(); 94 | if (array_key_exists($this->getValue(), $options)) { 95 | $value = $options[$this->getValue()]; 96 | } else { 97 | $value = $this->value; 98 | } 99 | 100 | return $value; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Classes/Duplication/Process/TreeUidAssociationProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 17 | 18 | /** 19 | * Class containing functions called when a site is being duplicated. 20 | * See function "run" for more information. 21 | */ 22 | class TreeUidAssociationProcess extends AbstractDuplicationProcess 23 | { 24 | 25 | /** 26 | * After a node duplication, by knowing the source node's uid, and the 27 | * duplicated node's uid, this function will be able to associate every old 28 | * page of the node's tree with the new duplicated node's uid. 29 | */ 30 | public function run() 31 | { 32 | $modelPageUid = $this->getModelPageUid(); 33 | if (!$modelPageUid) { 34 | return; 35 | } 36 | $duplicatedPageUid = $this->getDuplicatedPageUid(); 37 | if (!$duplicatedPageUid) { 38 | return; 39 | } 40 | 41 | // Processing the pages uid association. 42 | $treeUidAssociation = $this->getTreeUidAssociationRecursive($modelPageUid, $duplicatedPageUid); 43 | 44 | $this->addNotice( 45 | 'duplication_process.pages_association.notice.amount_pages', 46 | 1431985778, 47 | ['d' => count($treeUidAssociation)] 48 | ); 49 | 50 | $this->setDuplicationDataValue('pagesUidAssociation', $treeUidAssociation); 51 | } 52 | 53 | /** 54 | * Recursive function for processing the function "getTreeUidAssociation". 55 | * Will check the sub pages of the old and new page. 56 | * 57 | * @param integer $oldUid The uid of the model page. 58 | * @param integer $newUid The uid of the duplicated page. 59 | * @return array An array containing association between the pages. 60 | */ 61 | private function getTreeUidAssociationRecursive($oldUid, $newUid) 62 | { 63 | $uidAssociation = [$oldUid => $newUid]; 64 | 65 | $oldChildren = $this->database->exec_SELECTgetRows( 66 | 'uid', 67 | 'pages', 68 | 'deleted=0 AND pid=' . $oldUid, 69 | '', 70 | 'sorting ASC' 71 | ); 72 | 73 | $newChildren = $this->database->exec_SELECTgetRows( 74 | 'uid', 75 | 'pages', 76 | 'deleted=0 AND pid=' . $newUid, 77 | '', 78 | 'sorting ASC' 79 | ); 80 | 81 | if (array_keys($oldChildren) == array_keys($newChildren)) { 82 | foreach ($oldChildren as $key => $oldChildUid) { 83 | $childrenAssociation = $this->getTreeUidAssociationRecursive($oldChildUid['uid'], $newChildren[$key]['uid']); 84 | 85 | foreach ($childrenAssociation as $childrenAssociationOldUid => $childrenAssociationNewUid) { 86 | $uidAssociation[$childrenAssociationOldUid] = $childrenAssociationNewUid; 87 | } 88 | } 89 | } 90 | 91 | return $uidAssociation; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Resources/Private/Templates/Administration/Help.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers\Be} 2 | 3 |
5 | 6 | 7 | 8 | 9 | 10 |

Déploiement semi-automatisé de sites

11 | 12 |

13 | Ce module permet de générer des sites et de décliner des modèles disponibles. 14 |

15 |

16 | Veuillez suivre pas-à-pas les instructions ci-dessous pour procéder à la création d'un nouveau site. 17 |

18 | 19 |
20 | 21 |

Créer un nouveau site

22 | 23 |

24 | Pour créer un nouveau site, cliquez sur le bouton prévu à cet effet dans la barre en haut de cette fenêtre, ou cliquez ici. 25 |

26 |

27 | Un formulaire va s'afficher. Vous devrez remplir tous les champs correctement, jusqu'à ce qu'aucune erreur ne soit trouvée. Pour avoir des indications sur l'état du formulaire, référez-vous au panneau situé à droite de l'écran. 28 |

29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 |

37 | Astuce : Vous pouvez avoir des informations sur un champ en particulier en passant votre curseur au dessus de son icône associée. 38 |

39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 |

49 | Lorsque le formulaire est valide, lancez le processus de création. Ne quittez pas la page tant que le processus ne s'est pas terminé. 50 |

51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 |

59 | Une fois le site dupliqué, vous pouvez le retrouver dans l'arborescence de TYPO3. 60 |

61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |

Modifier un site

69 | 70 |

71 | Il est possible de modifier les valeurs associées à un site. Pour ce faire, rendez vous sur la page d'accueil. En bas de page, vous retrouverez la liste des sites déjà dupliquer. Cliquez sur le bouton d'édition, et suivez le processus normal. 72 |

73 | 74 |
75 | 76 |
77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
-------------------------------------------------------------------------------- /Resources/Private/Partials/New/FormStaticMenu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |
7 | 8 |
9 |

10 |   11 |

12 |
13 | 14 |
15 |
16 | 17 |   18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | {field.label} 30 |
    31 | 32 |
  • {error.message}
  • 33 |
    34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 | 42 |    43 | 44 | 45 |
46 | 47 |
48 |
49 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 |   58 | 59 |
60 |   61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 | 74 |
75 | 76 |
-------------------------------------------------------------------------------- /Classes/Duplication/Process/PagesDuplicationProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use TYPO3\CMS\Backend\Tree\Pagetree\PagetreeNode; 17 | use TYPO3\CMS\Core\Utility\GeneralUtility; 18 | use TYPO3\CMS\Backend\Tree\Pagetree\Commands; 19 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 20 | 21 | /** 22 | * Class containing functions called when a site is being duplicated. 23 | * See function "run" for more information. 24 | */ 25 | class PagesDuplicationProcess extends AbstractDuplicationProcess 26 | { 27 | 28 | /** 29 | * Will duplicate the model site's page and its sub-pages in the configured 30 | * destination. 31 | */ 32 | public function run() 33 | { 34 | $modelPageUid = $this->getModelPageUid(); 35 | if (!$modelPageUid) { 36 | return; 37 | } 38 | 39 | $copyDestination = intval($this->getDuplicationData('copyDestination')); 40 | 41 | // Testing if the values and $copyDestination is valid. 42 | $testCopyDestination = $this->database->exec_SELECTgetSingleRow('uid', 'pages', 'deleted=0 AND uid=' . intval($copyDestination)); 43 | if ($testCopyDestination === false) { 44 | $this->addError( 45 | 'duplication_process.pages_duplication.error.wrong_destination_uid', 46 | 1431372959, 47 | ['d' => $copyDestination] 48 | ); 49 | 50 | return; 51 | } 52 | 53 | // Calling duplication process. 54 | $duplicatedPageUid = $this->copyNodeToDestination($modelPageUid, $copyDestination); 55 | $this->setDuplicationDataValue('duplicatedPageUid', $duplicatedPageUid); 56 | 57 | // Updating the new page's title with the given one. 58 | $siteTitle = $this->getField('siteTitle'); 59 | if ($siteTitle) { 60 | $this->database->exec_UPDATEquery( 61 | 'pages', 62 | 'uid=' . $duplicatedPageUid, 63 | ['title' => $siteTitle->getValue()] 64 | ); 65 | } 66 | } 67 | 68 | /** 69 | * Copies the source node directly as the first child of the destination 70 | * node and returns the created node. 71 | * 72 | * @param integer $nodeUid The node which will be duplicated. 73 | * @param integer $destinationUid The uid of the new node's parent. 74 | * @return integer The uid of the new node's first page. 75 | */ 76 | private function copyNodeToDestination($nodeUid, $destinationUid) 77 | { 78 | $beUserSave = $GLOBALS['BE_USER']; 79 | 80 | $GLOBALS['BE_USER']->uc['copyLevels'] = 100; 81 | $GLOBALS['BE_USER']->workspace = 0; 82 | 83 | $nodeData = new \stdClass(); 84 | $nodeData->serializeClassName = PagetreeNode::class; 85 | $nodeData->id = $nodeUid; 86 | $nodeData->type = 'pages'; 87 | 88 | /** @var PagetreeNode $node */ 89 | $node = GeneralUtility::makeInstance(PagetreeNode::class, (array)$nodeData); 90 | 91 | $duplicatedPageUid = Commands::copyNode($node, $destinationUid); 92 | 93 | $GLOBALS['BE_USER'] = $beUserSave; 94 | 95 | return $duplicatedPageUid; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Palette de couleurs 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
    17 |
  • 18 |
    19 |

    20 | Cliquez pour enregistrer 21 |
    22 |
    23 |
  • 24 |
25 |
26 |
27 | 28 | 29 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/views/color.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | // http://www.sitepoint.com/url-parameters-jquery/ 3 | $.urlParam = function(name){ 4 | var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); 5 | if (results==null){ 6 | return null; 7 | } 8 | else{ 9 | return results[1] || 0; 10 | } 11 | } 12 | 13 | $(function() { 14 | "use strict"; 15 | 16 | app.ColorView = Backbone.View.extend({ 17 | 18 | tagName: "li", 19 | className: "swatch", 20 | 21 | template: _.template( $("#template-color").html() ), 22 | 23 | isIncrementing: false, 24 | 25 | events: { 26 | "click .destroy": "destroy", 27 | "click .meta-details": "toggleDetails", 28 | "change .rgb .number-widget input": "changeColorRgb", 29 | "change .hsl .number-widget input": "changeColorHsl", 30 | "mousedown .number-widget .up": "incrementValue", 31 | "mousedown .number-widget .down": "decrementValue" 32 | }, 33 | 34 | initialize: function() { 35 | this.model.on('change', this.render, this); 36 | this.model.on('destroy', this.setupRemove, this); 37 | }, 38 | 39 | render: function() { 40 | var color = this.model.color(); 41 | 42 | this.$el.html(this.template({ 43 | colorHex: color.hexString(), 44 | r: color.red(), 45 | g: color.green(), 46 | b: color.blue(), 47 | h: color.hue(), 48 | s: color.saturation(), 49 | l: color.lightness() 50 | })); 51 | 52 | this.$('.color').css({ 53 | backgroundColor: color.hslString() 54 | }); 55 | 56 | if(color.lightness() > 50) { 57 | this.$el.addClass('light'); 58 | } 59 | 60 | var form = window.opener.SiteFactory.Form.GetInstanceByName($.urlParam('formId')); 61 | form.getFieldByName($.urlParam('field')).fillColorPickerField(color.hexString()); 62 | 63 | window.close(); 64 | 65 | return this; 66 | }, 67 | 68 | destroy: function(event) { 69 | event.preventDefault(); 70 | this.model.destroy(); 71 | }, 72 | 73 | setupRemove: function() { 74 | this.$el.addClass("destroyed").css("z-index", 0); 75 | setTimeout(_.bind(function() { 76 | this.remove(); 77 | }, this), 500); 78 | }, 79 | 80 | toggleDetails: function(event) { 81 | event.preventDefault(); 82 | this.$el.toggleClass("show-details"); 83 | }, 84 | 85 | changeColorRgb: function(event) { 86 | var r = this.$('[data-type="red"]').val(), 87 | g = this.$('[data-type="green"]').val(), 88 | b = this.$('[data-type="blue"]').val(); 89 | this.model.color().red(r) 90 | .green(g) 91 | .blue(b); 92 | this.model.trigger("change"); 93 | }, 94 | 95 | changeColorHsl: function(event) { 96 | var h = this.$('[data-type="hue"]').val(), 97 | s = this.$('[data-type="saturation"]').val(), 98 | l = this.$('[data-type="lightness"]').val(); 99 | this.model.color().hue(h) 100 | .saturation(s) 101 | .lightness(l); 102 | this.model.trigger("change"); 103 | }, 104 | 105 | incrementValue: function(event) { 106 | event.preventDefault(); 107 | var type = $(event.target).parents('.number-widget').find('input').data('type'); 108 | this.repeater(type, 1); 109 | }, 110 | 111 | decrementValue: function(event) { 112 | event.preventDefault(); 113 | var type = $(event.target).parents('.number-widget').find('input').data('type'); 114 | this.repeater(type, -1); 115 | }, 116 | 117 | repeater: function(type, amount) { 118 | var model = this.model, 119 | color = model.color(); 120 | 121 | var action = function() { 122 | color[type](color[type]() + amount); 123 | model.trigger("change"); 124 | } 125 | action(); 126 | 127 | var intervalId = setInterval(action, 100); 128 | 129 | $(window).one("mouseup", function() { 130 | clearInterval(intervalId); 131 | }); 132 | } 133 | }); 134 | }); -------------------------------------------------------------------------------- /Resources/Private/Templates/Administration/ProcessCopy.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 |
5 | 6 | 7 | 8 | 9 | 14 | 15 | 23 | 24 |
25 |

26 |

27 |

28 | 29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |
    42 | 43 | 44 | 45 |
46 |
47 | 48 |

49 | 50 | «  51 | 52 |

53 | 54 |
55 | 56 |
57 | 58 | 59 |
  • 60 |
    61 | 62 | [00:00] 63 |
    64 |
      65 | 66 |   67 | 68 |
      69 |
      70 |
    • 71 |
      72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
      -------------------------------------------------------------------------------- /Classes/Controller/AjaxController.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Controller; 15 | 16 | use Romm\SiteFactory\Utility\AjaxInterface; 17 | use TYPO3\CMS\Core\Utility\GeneralUtility; 18 | use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; 19 | use TYPO3\CMS\Extbase\Mvc\Dispatcher; 20 | use TYPO3\CMS\Extbase\Mvc\Web\Request; 21 | use TYPO3\CMS\Extbase\Mvc\Web\Response; 22 | 23 | class AjaxController extends ActionController 24 | { 25 | public function dispatchAction() 26 | { 27 | $requestArguments = GeneralUtility::_GP('request'); 28 | $result = []; 29 | 30 | if (null !== $requestArguments 31 | && is_array($requestArguments) 32 | ) { 33 | if (true === isset($requestArguments['function'])) { 34 | $result = $this->dispatchUserFunction($requestArguments); 35 | } elseif (true === isset($requestArguments['mvc']) 36 | && is_array($requestArguments['mvc']) 37 | ) { 38 | $result = $this->dispatchControllerAction($requestArguments); 39 | } 40 | } 41 | 42 | if (is_array($result)) { 43 | $result = json_encode($result); 44 | } 45 | 46 | return $result; 47 | } 48 | 49 | public function dispatchUserFunction($requestArguments) 50 | { 51 | $result = []; 52 | list($className) = GeneralUtility::trimExplode('->', $requestArguments['function']); 53 | 54 | if (class_exists($className) 55 | && in_array(AjaxInterface::class, class_implements($className)) 56 | ) { 57 | $parameters = (true === isset($requestArguments['arguments'])) 58 | ? $requestArguments['arguments'] 59 | : []; 60 | 61 | $result = GeneralUtility::callUserFunction($requestArguments['function'], $parameters, $this); 62 | } 63 | 64 | return $result; 65 | } 66 | 67 | public function dispatchControllerAction($requestArguments) { 68 | $result = []; 69 | $extensionName = (true === isset($requestArguments['mvc']['extensionName'])) 70 | ? $requestArguments['mvc']['extensionName'] 71 | : null; 72 | $controllerName = (true === isset($requestArguments['mvc']['controller'])) 73 | ? $requestArguments['mvc']['controller'] 74 | : null; 75 | $vendorName = (true === isset($requestArguments['mvc']['vendor'])) 76 | ? $requestArguments['mvc']['vendor'] 77 | : null; 78 | $actionName = (true === isset($requestArguments['mvc']['action'])) 79 | ? $requestArguments['mvc']['action'] 80 | : null; 81 | $arguments = (true === isset($requestArguments['arguments'])) 82 | ? $requestArguments['arguments'] 83 | : []; 84 | 85 | if ($extensionName && $vendorName && $controllerName && $actionName) { 86 | /** @var Request $request */ 87 | $request = GeneralUtility::makeInstance(Request::class); 88 | $request->setControllerExtensionName($extensionName); 89 | $request->setControllerVendorName($vendorName); 90 | $request->setControllerName($controllerName); 91 | $request->setControllerActionName($actionName); 92 | $request->setArguments($arguments); 93 | 94 | /** @var Response $response */ 95 | $response = GeneralUtility::makeInstance(Response::class); 96 | 97 | /** @var Dispatcher $dispatcher */ 98 | $dispatcher = GeneralUtility::makeInstance(Dispatcher::class); 99 | $dispatcher->dispatch($request, $response); 100 | 101 | $result = $response->getContent(); 102 | } 103 | 104 | return $result; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Configuration/TypoScript/Default/DuplicationConfiguration.ts: -------------------------------------------------------------------------------- 1 | module.tx_sitefactory { 2 | duplicationProcesses { 3 | # Will process the entire site's duplication: all the pages, sub pages and contents. 4 | pagesDuplication { 5 | class = Romm\SiteFactory\Duplication\Process\PagesDuplicationProcess 6 | label = duplication_process.pages_duplication 7 | } 8 | 9 | # Used after "pagesDuplication", it will provide an array containing a full association 10 | # between the uids of the model site's pages and the uids of the duplicated site's pages. 11 | treeUidAssociation { 12 | class = Romm\SiteFactory\Duplication\Process\TreeUidAssociationProcess 13 | label = duplication_process.pages_association 14 | } 15 | 16 | # Creates a new "sys_filemounts" record. 17 | # 18 | # Available settings: 19 | # - path: Path of the folder created on the server. 20 | # If none given, "user_upload" is used. 21 | # - newRecordName: Will save the new "sys_filemounts" record's uid at this index. 22 | # It can then be used later (e.g. link this record to a backend user). 23 | # If none is given, "fileMountUid" is used. 24 | sysFileMounts { 25 | class = Romm\SiteFactory\Duplication\Process\SysFileMountsProcess 26 | label = duplication_process.mount_point_creation 27 | settings { 28 | path = user_upload/ 29 | createdRecordName = fileMountUid 30 | } 31 | } 32 | 33 | # Creates a new "be_groups" record. 34 | # 35 | # Available settings: 36 | # - modelUid: Required! 37 | # Uid of the backend user group model, which will be duplicated for the new site. 38 | # - sysFileMountUid: The uid of the file mount which will be linked to the backend user group. 39 | # Can be an integer, or "data:foo" where foo refers to the value of "settings.createdRecordName" 40 | # for the file mount creation process (default value is "fileMountUid"). 41 | # - createdRecordName: Will save the new "be_group" record's uid at this index. 42 | # It can then be used later (e.g. link this record to a backend user). 43 | # If none is given, "backendUserGroupUid" is used. 44 | backendUserGroupCreation { 45 | class = Romm\SiteFactory\Duplication\Process\BackendUserGroupCreationProcess 46 | label = duplication_process.backend_usergroup_creation 47 | settings { 48 | modelUid = 49 | sysFileMountUid = data:fileMountUid 50 | createdRecordName = backendUserGroupUid 51 | } 52 | } 53 | 54 | backendUserCreation { 55 | class = Romm\SiteFactory\Duplication\Process\BackendUserCreationProcess 56 | label = duplication_process.backend_user_creation 57 | settings { 58 | modelUid = 59 | sysFileMountUid = data:fileMountUid 60 | createdRecordName = backendUserUid 61 | } 62 | } 63 | 64 | uploadedFiles { 65 | class = Romm\SiteFactory\Duplication\Process\UploadedFilesProcess 66 | label = duplication_process.uploaded_files 67 | usedInSiteModification = 1 68 | } 69 | 70 | backendConstantsAssignation { 71 | class = Romm\SiteFactory\Duplication\Process\BackendConstantsAssignationProcess 72 | label = duplication_process.backend_constants_assignation 73 | usedInSiteModification = 1 74 | } 75 | 76 | saveSiteConfiguration { 77 | class = Romm\SiteFactory\Duplication\Process\SaveSiteConfigurationProcess 78 | label = duplication_process.save_site_configuration 79 | usedInSiteModification = 1 80 | } 81 | } 82 | 83 | duplication { 84 | 10 < module.tx_sitefactory.duplicationProcesses.pagesDuplication 85 | 20 < module.tx_sitefactory.duplicationProcesses.treeUidAssociation 86 | 30 < module.tx_sitefactory.duplicationProcesses.sysFileMounts 87 | # 40 < module.tx_sitefactory.duplicationProcesses.backendUserGroupCreation 88 | # 50 < module.tx_sitefactory.duplicationProcesses.backendUserCreation 89 | 60 < module.tx_sitefactory.duplicationProcesses.uploadedFiles 90 | 70 < module.tx_sitefactory.duplicationProcesses.backendConstantsAssignation 91 | 80 < module.tx_sitefactory.duplicationProcesses.linkToPageMedia 92 | 90 < module.tx_sitefactory.duplicationProcesses.LinkToPageBackendLayout 93 | 100 < module.tx_sitefactory.duplicationProcesses.saveSiteConfiguration 94 | } 95 | } -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/templates/default.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 73 | 74 | Fine Uploader default UI 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/fine-uploader.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Fine Uploader 3 | * 4 | * Copyright 2015, Widen Enterprises, Inc. info@fineuploader.com 5 | * 6 | * Version: 5.2.1 7 | * 8 | * Homepage: http://fineuploader.com 9 | * 10 | * Repository: git://github.com/FineUploader/fine-uploader.git 11 | * 12 | * Licensed only under the Widen Commercial License (http://fineuploader.com/licensing). 13 | */ 14 | 15 | 16 | /*! fine-uploader 2015-04-21 */ 17 | 18 | .qq-uploader{position:relative;width:100%}.qq-upload-button{display:block;width:105px;padding:7px 0;text-align:center;background:#800;border-bottom:1px solid #DDD;color:#FFF}.qq-upload-button-hover{background:#C00}.qq-upload-button-focus{outline:1px dotted #000}.qq-upload-drop-area,.qq-upload-extra-drop-area{position:absolute;top:0;left:0;width:100%;height:100%;min-height:30px;z-index:2;background:#FF9797;text-align:center}.qq-upload-drop-area span{display:block;position:absolute;top:50%;width:100%;margin-top:-8px;font-size:16px}.qq-upload-extra-drop-area{position:relative;margin-top:50px;font-size:16px;padding-top:30px;height:20px;min-height:40px}.qq-upload-drop-area-active{background:#FF7171}.qq-upload-list{margin:0;padding:0;list-style:none}.qq-upload-list li{margin:0;padding:9px;line-height:15px;font-size:16px;background-color:#FFF0BD}.qq-upload-file,.qq-upload-spinner,.qq-upload-size,.qq-upload-cancel,.qq-upload-retry,.qq-upload-failed-text,.qq-upload-delete,.qq-upload-pause,.qq-upload-continue{margin-right:12px;display:inline}.qq-upload-file{}.qq-upload-spinner{display:inline-block;background:url(loading.gif);width:15px;height:15px;vertical-align:text-bottom}.qq-drop-processing{display:block}.qq-drop-processing-spinner{display:inline-block;background:url(processing.gif);width:24px;height:24px;vertical-align:text-bottom}.qq-upload-delete,.qq-upload-pause,.qq-upload-continue{display:inline}.qq-upload-retry,.qq-upload-delete,.qq-upload-cancel,.qq-upload-pause,.qq-upload-continue{color:#000}.qq-upload-size,.qq-upload-cancel,.qq-upload-retry,.qq-upload-delete,.qq-upload-pause,.qq-upload-continue{font-size:12px;font-weight:400}.qq-upload-failed-text{display:none;font-style:italic;font-weight:700}.qq-upload-failed-icon{display:none;width:15px;height:15px;vertical-align:text-bottom}.qq-upload-fail .qq-upload-failed-text{display:inline}.qq-upload-retrying .qq-upload-failed-text{display:inline;color:#D60000}.qq-upload-list li.qq-upload-success{background-color:#5DA30C;color:#FFF}.qq-upload-list li.qq-upload-fail{background-color:#D60000;color:#FFF}.qq-progress-bar{display:block;background:-moz-linear-gradient(top,rgba(30,87,153,1) 0,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,rgba(30,87,153,1)),color-stop(50%,rgba(41,137,216,1)),color-stop(51%,rgba(32,124,202,1)),color-stop(100%,rgba(125,185,232,1)));background:-webkit-linear-gradient(top,rgba(30,87,153,1) 0,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%);background:-o-linear-gradient(top,rgba(30,87,153,1) 0,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%);background:-ms-linear-gradient(top,rgba(30,87,153,1) 0,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%);background:linear-gradient(to bottom,rgba(30,87,153,1) 0,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%);width:0;height:15px;border-radius:6px;margin-bottom:3px}.qq-total-progress-bar{height:25px;border-radius:9px}.qq-total-progress-bar-container{margin:9px}INPUT.qq-edit-filename{position:absolute;opacity:0;filter:alpha(opacity=0);z-index:-1;-ms-filter:"alpha(Opacity=0)"}.qq-upload-file.qq-editable{cursor:pointer}.qq-edit-filename-icon.qq-editable{display:inline-block;cursor:pointer}INPUT.qq-edit-filename.qq-editing{position:static;margin-top:-5px;margin-right:10px;margin-bottom:-5px;opacity:1;filter:alpha(opacity=100);-ms-filter:"alpha(Opacity=100)"}.qq-edit-filename-icon{display:none;background:url(edit.gif);width:15px;height:15px;vertical-align:text-bottom;margin-right:5px}.qq-hide{display:none}.qq-uploader DIALOG{display:none}.qq-uploader DIALOG[open]{display:block}.qq-uploader DIALOG{display:none}.qq-uploader DIALOG[open]{display:block}.qq-uploader DIALOG .qq-dialog-buttons{text-align:center;padding-top:10px}.qq-uploader DIALOG .qq-dialog-buttons BUTTON{margin-left:5px;margin-right:5px}.qq-uploader DIALOG .qq-dialog-message-selector{padding-bottom:10px}.qq-uploader DIALOG::backdrop{background-color:rgba(0,0,0,.7)} 19 | /*! 2015-04-21 */ 20 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/SiteFactory.Menu.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** @namespace SiteFactory */ 4 | 5 | /** 6 | * @typedef {SiteFactory.Form} SiteFactoryForm 7 | * @param {SiteFactoryForm} formElement 8 | * @param {Object} menuElement 9 | */ 10 | SiteFactory.Menu = function(formElement, menuElement) { 11 | /** 12 | * Contains the form bound to this menu. 13 | * 14 | * @type {SiteFactoryForm} 15 | */ 16 | this.formElement = formElement; 17 | 18 | this.element = menuElement; 19 | 20 | var localMenu = this; 21 | 22 | this.refreshErrorsMenu = function () { 23 | var errorsCounter = 0; 24 | var fields = this.formElement.getFields(); 25 | var errorsContainer = this.findElement('.counter-errors .errors-container'); 26 | errorsContainer.removeClass('has-error'); 27 | errorsContainer.html(''); 28 | 29 | // Looping on fields. 30 | for (var key in fields) { 31 | if (fields.hasOwnProperty(key)) { 32 | // If the current field has at least one error. 33 | if (fields[key].getErrors().length > 0) { 34 | errorsContainer.addClass('has-error'); 35 | this.findElement('.counter-errors').removeClass('hidden'); 36 | 37 | // We list all the errors. 38 | var containerElem = document.createElement('div'); 39 | var errorTitleElem = document.createElement('strong'); 40 | 41 | errorTitleElem.appendChild(document.createTextNode(fields[key].label)); 42 | containerElem.appendChild(errorTitleElem); 43 | 44 | var errorsListElem = document.createElement('ul'); 45 | 46 | for (var i = 0; i < fields[key].getErrors().length; i++) { 47 | errorsCounter++; 48 | var errorElem = document.createElement('li'); 49 | errorElem.appendChild(document.createTextNode(fields[key].getErrors()[i])); 50 | 51 | errorsListElem.appendChild(errorElem); 52 | } 53 | 54 | containerElem.appendChild(errorsListElem); 55 | 56 | errorsContainer.append(containerElem); 57 | } 58 | } 59 | } 60 | 61 | 62 | // Managing the messages in the static menu. 63 | var errorElements = this.findElement('.has-error'); 64 | var submitButton = this.findElement('.submit-button button'); 65 | if (errorElements.length == 0) { 66 | this.findElement('.info-success').removeClass('hidden'); 67 | this.findElement('.counter-errors').addClass('hidden'); 68 | submitButton.removeClass('btn-default'); 69 | submitButton.removeClass('btn-danger'); 70 | submitButton.addClass('btn-success'); 71 | } 72 | else { 73 | this.findElement('.info-success').addClass('hidden'); 74 | submitButton.removeClass('btn-default'); 75 | submitButton.removeClass('btn-success'); 76 | submitButton.addClass('btn-danger'); 77 | 78 | // Label (errors count). 79 | var counterLabelElem = this.findElement('.counter-errors .counter-label'); 80 | var counterLabel = (errorsCounter == 1) 81 | ? counterLabelElem.attr('data-default-label-single') 82 | : counterLabelElem.attr('data-default-label-multiple'); 83 | counterLabel = counterLabel.replace('%s', errorsCounter); 84 | counterLabelElem.html(counterLabel); 85 | } 86 | }; 87 | 88 | jQuery(document).ready(function() { 89 | var menu = localMenu.findElement('.fixed-menu > *'); 90 | 91 | /* 92 | * This functions take care of the right menu's responsive behaviour. When 93 | * the windows is smaller than 768px, the menu disappears and is accessible 94 | * via a "menu button". 95 | */ 96 | localMenu.findElement('.menu-btn-sm').click(function() { 97 | var glyph = jQuery(this).find('.glyphicon'); 98 | menu.toggle(100, function() { 99 | if (menu.is(':visible') == true) { 100 | glyph.removeClass('glyphicon-chevron-left'); 101 | glyph.addClass('glyphicon-chevron-right'); 102 | } 103 | else { 104 | glyph.removeClass('glyphicon-chevron-right'); 105 | glyph.addClass('glyphicon-chevron-left'); 106 | } 107 | }); 108 | }); 109 | 110 | jQuery(window).resize(function() { 111 | if (jQuery(this).width() >= 768) { 112 | var menuButton = localMenu.findElement('.menu-btn-sm'); 113 | var glyph = menuButton.find('.glyphicon'); 114 | 115 | menu.show(); 116 | glyph.removeClass('glyphicon-chevron-left'); 117 | glyph.addClass('glyphicon-chevron-right'); 118 | } 119 | }); 120 | }); 121 | 122 | this.findElement = function(selector) { 123 | return jQuery(this.element).find(selector); 124 | }; 125 | }; -------------------------------------------------------------------------------- /Classes/Domain/Repository/SaveRepository.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Domain\Repository; 15 | 16 | use Romm\SiteFactory\Domain\Model\Pages; 17 | use Romm\SiteFactory\Domain\Repository\PagesRepository; 18 | use TYPO3\CMS\Extbase\Persistence\Generic\Query; 19 | use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult; 20 | use TYPO3\CMS\Extbase\Persistence\QueryInterface; 21 | use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; 22 | use TYPO3\CMS\Extbase\Persistence\Repository; 23 | use Romm\SiteFactory\Core\Core; 24 | use Romm\SiteFactory\Domain\Model\Save; 25 | 26 | /** 27 | * The repository for the "Save" model. 28 | * 29 | * @method Save|null findOneByRootPageUid(int $pageUid) 30 | */ 31 | class SaveRepository extends Repository 32 | { 33 | 34 | /** 35 | * Returns the last record for a given root page uid. 36 | * 37 | * @param int $rootPageUid The root page uid. 38 | * @return array|QueryResultInterface 39 | */ 40 | public function findLastByRootPageUid($rootPageUid) 41 | { 42 | /** @var Query $query */ 43 | $query = $this->createQuery(); 44 | 45 | $query->matching( 46 | $query->logicalAnd( 47 | $query->equals('rootPageUid', intval($rootPageUid)) 48 | ) 49 | ) 50 | ->setOrderings(['date' => QueryInterface::ORDER_DESCENDING]) 51 | ->setLimit(1); 52 | 53 | $result = $query->execute(); 54 | $result = ($result) ? 55 | $result[0] : 56 | null; 57 | 58 | return $result; 59 | } 60 | 61 | /** 62 | * Returns all records grouped by "root_page_uid". 63 | * 64 | * @return array|QueryResultInterface 65 | */ 66 | public function findAllByDistinctRootPageUid() 67 | { 68 | /** @var Query $query */ 69 | $query = $this->createQuery(); 70 | 71 | return $query 72 | ->statement('SELECT * ' 73 | . 'FROM (SELECT * FROM tx_sitefactory_domain_model_save ORDER BY tx_sitefactory_domain_model_save.date ' . QueryInterface::ORDER_DESCENDING . ') AS save ' 74 | . 'JOIN pages ON pages.uid=save.root_page_uid ' 75 | . 'WHERE pages.deleted=0 ' 76 | . 'GROUP BY root_page_uid ' 77 | ) 78 | ->execute(); 79 | } 80 | 81 | /** 82 | * Creates a new "Save" record and add it to current persistence. 83 | * 84 | * @param Save $save The save instance. 85 | */ 86 | public function createSave(Save $save) 87 | { 88 | $this->add($save); 89 | $this->persistenceManager->persistAll(); 90 | } 91 | 92 | /** 93 | * Will set the page linked to the "Save" records (based on their attribute 94 | * "rootPageUid"). 95 | * 96 | * @param array|Save $records An array containing "Save" models, or a single "Save" model. 97 | * @return array|null Returns the array sent with modified "page" attribute, or null if the parameter was empty. 98 | */ 99 | public function attachPage($records) 100 | { 101 | if (!$records) { 102 | return null; 103 | } 104 | 105 | $objectManager = Core::getObjectManager(); 106 | /** @var PagesRepository $pagesRepository */ 107 | $pagesRepository = $objectManager->get(PagesRepository::class); 108 | 109 | if (is_array($records) || $records instanceof QueryResult) { 110 | /** @var Save[] $records */ 111 | foreach ($records as $key => $record) { 112 | $page = $pagesRepository->findByUidWithoutCondition($record->getRootPageUid()); 113 | $records[$key]->setPage($page[0]); 114 | } 115 | } elseif ($records instanceof Save) { 116 | /** @var Pages $page */ 117 | $page = $pagesRepository->findByUidWithoutCondition($records->getRootPageUid()); 118 | $records->setPage($page[0]); 119 | } 120 | 121 | return $records; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Resources/Private/Templates/Administration/New.html: -------------------------------------------------------------------------------- 1 | {namespace sf=Romm\SiteFactory\ViewHelpers} 2 | 3 | 4 | 5 | ############################## 6 | 7 | Page containing the form to create/edit a site. 8 | Fields will be created dynamically, using the configuration which declares the fields options. 9 | 10 | Useful variables: 11 | - {formId}: a unique id given to the form, which allows to instantiate several form if needed. 12 | - {fieldsConfiguration}: array containing all the fields and their configuration. 13 | - {refreshForm}: if true, the form might be refreshed after the page is loaded. 14 | 15 | 16 | 23 | 24 | 28 | 29 | 30 | 33 | 34 | 35 |
      36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
      57 |

      58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |

      67 | 68 |
      69 | 70 | 71 | 72 | 73 | 74 |
      75 |   76 |
      77 |
      78 | 79 | 80 | 81 |
      82 |
      83 | 84 |
      85 |   86 |
      87 |
      88 |
      89 |
      90 | 91 |
      92 |
      93 | 94 | ############################## 95 | 96 | Contains the buttons which will appear in the header menu. 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/SiteFactory.FadeOut.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** @namespace SiteFactory */ 4 | 5 | /*************************************************************************** 6 | * Managing fadeout elements. These are tall texts that are partially hidden. 7 | * When clicking on the "plus" button, it will show the entire text. 8 | ***************************************************************************/ 9 | SiteFactory.FadeOut = { 10 | initialize: function() { 11 | jQuery(document).ready(function() { 12 | // Initial process. 13 | SiteFactory.FadeOut.checkFadeoutElementsHeight(); 14 | 15 | // Processing on window's resizing. 16 | window.onresize = function() { 17 | SiteFactory.FadeOut.checkFadeoutElementsHeight(); 18 | }; 19 | 20 | /** 21 | * When clicking on a fadeout toggle link, we toggle the fadeout element's 22 | * displaying. 23 | */ 24 | jQuery('.fadeout-area .fadeout-toggle').on('click', function(e) { 25 | e.preventDefault(); 26 | 27 | SiteFactory.FadeOut.toggleFadeOut(jQuery(this)); 28 | }); 29 | }); 30 | }, 31 | 32 | /** 33 | * This function will check if a fadeout element's height is smaller than its 34 | * "data-height" attribute. If so, the fadeout is set. 35 | */ 36 | checkFadeoutElementsHeight: function() { 37 | jQuery('.fadeout-container').each(function() { 38 | var fadeoutArea = jQuery('.fadeout-toggle[href="#' + jQuery(this).attr('id') + '"]').parents('.fadeout-area'); 39 | 40 | if (jQuery(this).hasClass('fadeout-extended')) { 41 | jQuery(this).css('height', 'initial'); 42 | } 43 | 44 | // Fadeout is set. 45 | if (jQuery(this).attr('data-height') < jQuery(this).find('.fadeout-content').height()) { 46 | if (!jQuery(this).hasClass('fadeout-extended')) { 47 | jQuery(this).css('height', jQuery(this).attr('data-height')); 48 | 49 | fadeoutArea.find('.fadeout-background').show(); 50 | SiteFactory.FadeOut.toggleFadeOutLabel(fadeoutArea.find('.fadeout-toggle'), false); 51 | } 52 | } 53 | // Fadeout is unset. 54 | else { 55 | jQuery(this).css('height', 'initial'); 56 | jQuery(this).removeClass('fadeout-extended'); 57 | 58 | fadeoutArea.hide(); 59 | } 60 | }) 61 | }, 62 | 63 | /** 64 | * This function will toggle the fadeout link's label and icon depending 65 | * on if it's extended or not. 66 | */ 67 | toggleFadeOutLabel: function(elem, extend) { 68 | var label = elem.find('.fadeout-toggle-label'); 69 | var icon = elem.find('.fadeout-toggle-icon'); 70 | 71 | // Click on the "plus". 72 | if (extend) { 73 | label.html(elem.attr('data-hide-label')); 74 | icon.removeClass('glyphicon-plus'); 75 | icon.addClass('glyphicon-minus'); 76 | } 77 | // Click on the "minus". 78 | else { 79 | icon.removeClass('glyphicon-minus'); 80 | icon.addClass('glyphicon-plus'); 81 | if (elem.attr('data-show-label')) { 82 | label.html(elem.attr('data-show-label')); 83 | } 84 | } 85 | }, 86 | 87 | toggleFadeOut: function(elem) { 88 | // Cancel process if the fadeout is toggling. 89 | if (elem.hasClass('toggling')) return; 90 | elem.addClass('toggling'); 91 | 92 | var target = jQuery(elem.attr('href')); 93 | var height = target.find('.fadeout-content').height(); 94 | 95 | // Process fadeout extending. 96 | if (!target.hasClass('fadeout-extended')) { 97 | target.addClass('fadeout-extended'); 98 | 99 | // Hiding fadeout's background. 100 | elem.parents('.fadeout-area').addClass('fadeout-disabled'); 101 | 102 | // Toggling label. 103 | if (elem.attr('data-hide-label')) { 104 | if (!elem.attr('data-show-label')) 105 | elem.attr('data-show-label', elem.find('.fadeout-toggle-label').html()); 106 | 107 | SiteFactory.FadeOut.toggleFadeOutLabel(elem, true); 108 | } 109 | } 110 | // Cancel fadeout extending. 111 | else { 112 | height = target.attr('data-height'); 113 | target.removeClass('fadeout-extended'); 114 | 115 | // Show fadeout extending. 116 | elem.parents('.fadeout-area').removeClass('fadeout-disabled'); 117 | 118 | // Toggling label. 119 | SiteFactory.FadeOut.toggleFadeOutLabel(elem, false); 120 | } 121 | 122 | // Processing animation. 123 | target.animate( 124 | { height: height }, 125 | 500, 126 | function() { elem.removeClass('toggling'); } 127 | ); 128 | } 129 | }; 130 | 131 | SiteFactory.FadeOut.initialize(); -------------------------------------------------------------------------------- /Resources/Public/Contrib/fine-uploader/templates/simple-thumbnails.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 75 | 76 | Fine Uploader default UI with thumbnails 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Resources/Public/Contrib/hailpixel-color-picker/static/scripts/views/app.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | $(function() { 4 | "use strict"; 5 | 6 | app.SwatchAppView = Backbone.View.extend({ 7 | el: "#appframe", 8 | 9 | isTouchMove: false, 10 | startSaturation: 0, 11 | 12 | events: { 13 | "click #header-tab": "toggleheader" 14 | }, 15 | 16 | initialize: function() { 17 | app.Colors.on("add", this.addOne, this); 18 | app.Colors.on("reset", this.addAll, this); 19 | app.Colors.on("remove", this.layout, this); 20 | 21 | this.editModel = new app.Color({h: 180, s: 50, l: 50}); 22 | this.editModel.on("change", this.render, this); 23 | 24 | if('ontouchstart' in document.documentElement) { 25 | this.$("#edit").bind("touchstart", _.bind(this.touchstart, this)) 26 | .bind("touchmove", _.bind(this.touchmove, this)) 27 | .bind("touchend", _.bind(this.touchend, this)) 28 | .bind("gesturestart", _.bind(this.gesturestart, this)) 29 | .bind("gesturechange", _.bind(this.gesturechange, this)); 30 | 31 | } else { 32 | this.$("#constraints").mousemove(_.bind(this.mousemove, this)) 33 | .scroll(_.bind(this.scroll, this)) 34 | .click(_.bind(this.grabColor, this)) 35 | .scrollTop(500); 36 | } 37 | 38 | $(window).resize(_.bind(this.layout, this)); 39 | }, 40 | 41 | render: function() { 42 | this.$("#edit").css({ 43 | "background": this.editModel.hslCss() 44 | }); 45 | 46 | this.$("#edit h2").html(this.editModel.hexCss()); 47 | }, 48 | 49 | layout: function() { 50 | var w = $(window).width(), 51 | sliceSize = Math.floor(w / (app.Colors.length + 2)); 52 | 53 | this.$('#colors .swatch:not(#edit):not(.destroyed)').each(function(i, el) { 54 | $(el).css({ 55 | left: i * sliceSize, 56 | width: sliceSize, 57 | }) 58 | }); 59 | 60 | this.$("#edit").css({ 61 | left: app.Colors.length * sliceSize, 62 | }); 63 | }, 64 | 65 | addOne: function(color) { 66 | var view = new app.ColorView({model: color}); 67 | this.$("#colors").append(view.render().el); 68 | 69 | view.$el.css({ 70 | left: this.$("#edit").css("left"), 71 | width: this.$("#edit").css("width") 72 | }); 73 | 74 | // defer the render for a frame 75 | setTimeout(_.bind(function() { 76 | view.$el.addClass("animating"); 77 | this.layout(); 78 | }, this), 0); 79 | }, 80 | 81 | addAll: function() { 82 | this.$("#colors li:not(#edit)").remove(); 83 | app.Colors.each(this.addOne, this); 84 | 85 | if(app.Colors.length == 0) { 86 | this.layout(); 87 | } 88 | }, 89 | 90 | toggleheader: function(event) { 91 | this.$el.toggleClass("show-header"); 92 | }, 93 | 94 | grabColor: function(event) { 95 | app.Colors.add({ 96 | color: new Color(this.editModel.color().rgb()) 97 | }); 98 | }, 99 | 100 | move: function(px, py) { 101 | var editEl = this.$("#edit"), 102 | w = editEl.width(), 103 | h = editEl.height(), 104 | x, y, offset, hue, lit; 105 | 106 | offset = editEl.offset(); 107 | 108 | x = Math.max(0, px - offset.left); 109 | y = Math.max(0, py - offset.top); 110 | 111 | hue = Math.floor(x / w * 360), 112 | lit = Math.floor(y / h * 100); 113 | 114 | this.editModel.color().hue(hue) 115 | .lightness(lit); 116 | this.editModel.trigger("change"); 117 | }, 118 | 119 | scroll: function(event) { 120 | var col, offset = this.$("#constraints").scrollTop() / 10; 121 | offset = Math.max(0, Math.min(100, offset)); 122 | this.editModel.color().saturation(offset); 123 | this.editModel.trigger("change"); 124 | }, 125 | 126 | mousemove: function(event) { 127 | this.move(event.pageX, event.pageY); 128 | }, 129 | 130 | touchstart: function(event) { 131 | event.preventDefault(); 132 | this.isTouchMoved = false; 133 | }, 134 | 135 | touchmove: function(event) { 136 | this.move(event.originalEvent.touches[0].pageX, event.originalEvent.touches[0].pageY); 137 | this.isTouchMoved = true; 138 | }, 139 | 140 | touchend: function(event) { 141 | if(! this.isTouchMoved) { 142 | this.grabColor(); 143 | } 144 | }, 145 | 146 | gesturestart: function(event) { 147 | event.preventDefault(); 148 | this.startSaturation = this.editModel.get("s"); 149 | }, 150 | 151 | gesturechange: function(event) { 152 | 153 | if(event.originalEvent.scale) { 154 | var offset = Math.max(0, Math.min(100, this.startSaturation * event.originalEvent.scale)); 155 | this.editModel.color().saturation(offset); 156 | this.editModel.trigger("change"); 157 | } 158 | } 159 | 160 | }); 161 | 162 | }); -------------------------------------------------------------------------------- /Classes/Duplication/Process/SysFileMountsProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use TYPO3\CMS\Core\Utility\GeneralUtility; 17 | use Romm\SiteFactory\Core\Core; 18 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 19 | use TYPO3\CMS\Extbase\Domain\Model\FileMount; 20 | use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; 21 | 22 | /** 23 | * Class containing functions called when a site is being duplicated. 24 | * See function "run" for more information. 25 | * 26 | * Available duplication settings: 27 | * - path: Path of the folder created on the server. 28 | * If none given, "user_upload" is used. 29 | * - createdRecordName: Will save the new "sys_filemounts" record's uid at this index. 30 | * It can then be used later (e.g. link this record to a backend user group). 31 | * If none is given, "fileMountUid" is used. 32 | */ 33 | class SysFileMountsProcess extends AbstractDuplicationProcess 34 | { 35 | 36 | /** 37 | * Default path used to create the folder for the file mount. Can be 38 | * overwritten with "settings.path" in the duplication TypoScript 39 | * configuration. 40 | * 41 | * @var string 42 | */ 43 | private static $defaultPath = 'user_upload'; 44 | 45 | /** 46 | * Default name of the index used to store the uid of the created 47 | * "sys_filemounts" record. 48 | * 49 | * @var string 50 | */ 51 | private static $defaultCreatedRecordName = 'fileMountUid'; 52 | 53 | /** 54 | * Will create a file mount on the duplicated page. A directory will also be 55 | * created in fileadmin. The site's name will be used for both the file 56 | * mount's name and the directory created in fileadmin. 57 | */ 58 | public function run() 59 | { 60 | $siteTitle = $this->getField('siteTitle'); 61 | if ($siteTitle) { 62 | $fileMountUid = $this->manageSysFileMounts($siteTitle->getValue()); 63 | 64 | if ($fileMountUid !== false) { 65 | // Checking if "settings.createdRecordName" can be used, use self::$defaultCreatedRecordName otherwise. 66 | $createdRecordName = $this->getProcessSettings('createdRecordName'); 67 | $createdRecordName = (!empty($createdRecordName)) 68 | ? $createdRecordName 69 | : self::$defaultCreatedRecordName; 70 | 71 | $this->setDuplicationDataValue($createdRecordName, $fileMountUid); 72 | } else { 73 | $this->addError('duplication_process.mount_point_creation.error.error_creation', 1431426127); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * @param string $siteTitle The name of the new site, will be used for both the file mount's name and the directory created in fileadmin. 80 | * @return null|int Returns null if the file mount could not be created, or the uid of the last inserted "sys_filemounts" record. 81 | */ 82 | private function manageSysFileMounts($siteTitle) 83 | { 84 | $pathFirstPart = ($this->getProcessSettings('path')) 85 | ? $this->getProcessSettings('path') 86 | : self::$defaultPath; 87 | $pathFirstPart = (substr($pathFirstPart, -1, 1) == '/') 88 | ? $pathFirstPart 89 | : $pathFirstPart . '/'; 90 | 91 | $folderPath = $pathFirstPart . GeneralUtility::strtolower($siteTitle); 92 | $folderPath = Core::formatAccentsInString($folderPath); 93 | $folderPath = preg_replace('/\s+/', ' ', $folderPath); 94 | $folderPath = preg_replace('/\s/', '_', $folderPath); 95 | $folderPath = '/' . $folderPath . '/'; 96 | 97 | // @todo: manage warning when overriding a folder? 98 | GeneralUtility::mkdir_deep(PATH_site . 'fileadmin' . $folderPath); 99 | 100 | /** @var FileMount $fileMount */ 101 | $fileMount = GeneralUtility::makeInstance(FileMount::class); 102 | $fileMount->setPath($folderPath); 103 | $fileMount->setTitle($siteTitle); 104 | $fileMount->setIsAbsolutePath(true); 105 | // @todo: seems it must be on pid=0, check? 106 | $fileMount->setPid(0); 107 | 108 | /** @var PersistenceManager $persistenceManager */ 109 | $persistenceManager = $this->objectManager->get(PersistenceManager::class); 110 | $persistenceManager->add($fileMount); 111 | $persistenceManager->persistAll(); 112 | 113 | return $fileMount->getUid(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Classes/Duplication/Process/LinkToPageMediaProcess.php: -------------------------------------------------------------------------------- 1 | 12 | // * All rights reserved 13 | // * 14 | // * This script is part of the TYPO3 project. The TYPO3 project is 15 | // * free software; you can redistribute it and/or modify 16 | // * it under the terms of the GNU General Public License as published by 17 | // * the Free Software Foundation; either version 3 of the License, or 18 | // * (at your option) any later version. 19 | // * 20 | // * The GNU General Public License can be found at 21 | // * http://www.gnu.org/copyleft/gpl.html. 22 | // * 23 | // * This script is distributed in the hope that it will be useful, 24 | // * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | // * GNU General Public License for more details. 27 | // * 28 | // * This copyright notice MUST APPEAR in all copies of the script! 29 | // ***************************************************************/ 30 | // 31 | //use TYPO3\CMS\Core\Utility\GeneralUtility; 32 | //use TYPO3\CMS\Extbase\Utility\ArrayUtility; 33 | //use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 34 | //use Romm\SiteFactory\Utility\ConstantManagerUtility; 35 | // 36 | ///** 37 | // * Class containing functions called when a site is being duplicated. 38 | // */ 39 | //class LinkToPageMediaProcess extends AbstractDuplicationProcess { 40 | // public function ajaxRun(&$params) { 41 | // /** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */ 42 | // $objectManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager'); 43 | // /** @var \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser $typoScriptParser */ 44 | // $typoScriptParser = $objectManager->get('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser'); 45 | // 46 | // // Getting the constants of the duplicated page. 47 | // $constantsSiteFactory = ConstantManagerUtility::getPageTemplateConstants($params['duplicationData']['duplicatedPageUid'], false); 48 | // 49 | // $typoScriptParser->parse($constantsSiteFactory); 50 | // $pagesAssociations = $typoScriptParser->getVal('siteFactory.pages', $typoScriptParser->setup); 51 | // 52 | // if (!empty($pagesAssociations)) { 53 | // $pagesAssociations = $pagesAssociations[1]; 54 | // // TODO! 55 | // foreach($params['fieldsValues'] as $fieldName => $fieldConfiguration) { 56 | // $fieldCustomConfiguration = array(); 57 | // try { 58 | // $fieldCustomConfiguration = ArrayUtility::getValueByPath($fieldConfiguration, 'config.linkToPageMedia'); 59 | // } catch(\Exception $e) {} 60 | // 61 | // if (!empty($fieldCustomConfiguration)) { 62 | // if(!isset($fieldCustomConfiguration['page'])) { 63 | // throw new \Exception('The field "' . $fieldName . '" must contain the value "page" for the configuration "linkToPageMedia".', 1422616542); 64 | // } 65 | // 66 | // $targetedPage = null; 67 | // if (isset($pagesAssociations[$fieldCustomConfiguration['page']])) { 68 | // $targetedPage = $pagesAssociations[$fieldCustomConfiguration['page']]; 69 | // } 70 | // 71 | // if ($targetedPage) { 72 | // $this->database->exec_DELETEquery( 73 | // 'sys_file_reference', 74 | // 'uid_foreign=' . $targetedPage . 75 | // ' AND tablenames="pages"' . 76 | // ' AND fieldname="media"' . 77 | // ' AND table_local="sys_file"' 78 | // ); 79 | // 80 | // $data = array( 81 | // 'pid' => $targetedPage, 82 | // 'tstamp' => time(), 83 | // 'crdate' => time(), 84 | // 'cruser_id' => '1', // @TODO ! 85 | // 'uid_local' => $fieldConfiguration['value'], 86 | // 'uid_foreign' => $targetedPage, 87 | // 'tablenames' => 'pages', 88 | // 'fieldname' => 'media', 89 | // 'table_local' => 'sys_file' 90 | // ); 91 | // 92 | // $this->database->exec_INSERTquery( 93 | // 'sys_file_reference', 94 | // $data 95 | // ); 96 | // } 97 | // } 98 | // } 99 | // } 100 | // } 101 | //} 102 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/SiteFactory.Field.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** @namespace SiteFactory */ 4 | 5 | SiteFactory.Field = function(formElement, fieldElement) { 6 | this.formElement = formElement; 7 | this.element = fieldElement; 8 | this.name = fieldElement.attr('data-name'); 9 | this.type = fieldElement.attr('data-type'); 10 | this.fieldType = fieldElement.attr('data-fieldtype'); 11 | this.label = fieldElement.attr('data-label'); 12 | this.input = jQuery(fieldElement).find('.field-container :input'); 13 | this.parentElement = jQuery(fieldElement).parents('.form-group'); 14 | this.errorTooltip = this.parentElement.find('.factory-tooltip-error'); 15 | this.errors = []; 16 | 17 | var localField = this; 18 | 19 | /** 20 | * This function will evaluate the given field, based on the validation 21 | * defined in its configuration. The evaluation is done via Ajax, and tells 22 | * if the field is correctly filled or not. 23 | * 24 | * If the field does not match the validation, error messages are returned. 25 | */ 26 | this.validate = function() { 27 | var element = this.input; 28 | var value = element.val(); 29 | 30 | var loading = element.parents('.form-group').find('.form-evaluation-loading'); 31 | loading.show(); 32 | 33 | var glyphicons = element.parents('.form-group').find('.form-infos .glyphicon'); 34 | glyphicons.hide(); 35 | 36 | // Ajax callback functions. 37 | var ajaxFunctions = { 38 | // Success callback function. 39 | success: function(result) { 40 | loading.hide(); 41 | result = jQuery.parseJSON(result); 42 | var parentElement = jQuery(element).parents('.form-group'); 43 | var errorTooltip = parentElement.find('.factory-tooltip-error'); 44 | 45 | // The field has at least one error. 46 | if (jQuery(result['validationResult']['errors']).length > 0) { 47 | localField.errors = []; 48 | localField.addErrors(result['validationResult']['errors']).showErrors(); 49 | } 50 | else { 51 | // No error occurred. 52 | localField.resetErrors(); 53 | parentElement.removeClass('has-error'); 54 | parentElement.addClass('has-success'); 55 | errorTooltip.hide(); 56 | } 57 | }, 58 | // Error callback function. 59 | error: function(xhr, status, error) {}, 60 | // Complete callback function. 61 | complete: function() { 62 | if (typeof localField.formElement.menu != 'undefined') 63 | localField.formElement.menu.refreshErrorsMenu(); 64 | } 65 | }; 66 | 67 | jQuery.ajax({ 68 | async: 'true', 69 | url: SiteFactory.ajaxUrl, 70 | type: 'GET', 71 | dataType: 'html', 72 | data: { 73 | request: { 74 | function: 'Romm\\SiteFactory\\Form\\FieldValidation->ajaxValidateField', 75 | arguments: { 76 | fieldName: localField.name, 77 | value: value, 78 | pageUid: localField.formElement.modelSiteId 79 | } 80 | } 81 | }, 82 | success: function(result) { 83 | ajaxFunctions['success'](result); 84 | }, 85 | error: function(xhr, status, error) { 86 | ajaxFunctions['error'](xhr, status, error); 87 | }, 88 | complete: function() { 89 | ajaxFunctions['complete'](); 90 | } 91 | }); 92 | }; 93 | 94 | /** 95 | * Adds an error message to the field. 96 | * 97 | * @param {string} error 98 | * @returns SiteFactory.Field 99 | */ 100 | this.addError = function(error) { 101 | this.errors.push(error); 102 | return this; 103 | }; 104 | 105 | /** 106 | * Adds several error messages to the field. 107 | * 108 | * @param {[]} errors 109 | * @returns SiteFactory.Field 110 | */ 111 | this.addErrors = function(errors) { 112 | for (var i= 0; i < errors.length; i++) 113 | this.addError(errors[i].toString()); 114 | return this; 115 | }; 116 | 117 | /** 118 | * Will empty the array that contains the errors of the field. 119 | */ 120 | this.resetErrors = function() { 121 | this.errors = []; 122 | }; 123 | 124 | /** 125 | * Returns the field's errors array. 126 | * 127 | * @returns {[]} 128 | */ 129 | this.getErrors = function() { 130 | return this.errors; 131 | }; 132 | 133 | /** 134 | * Handles errors display. 135 | * 136 | * @returns {SiteFactory.Field} 137 | */ 138 | this.showErrors = function() { 139 | this.parentElement.removeClass('has-success'); 140 | this.parentElement.addClass('has-error'); 141 | this.errorTooltip.removeClass('factory-tooltip-disabled'); 142 | this.errorTooltip.attr('data-original-title', this.getErrorMessageTooltip()); 143 | this.errorTooltip.show(); 144 | return this; 145 | }; 146 | 147 | /** 148 | * Returns a line-break message. 149 | * 150 | * @returns {string} 151 | */ 152 | this.getErrorMessageTooltip = function() { 153 | var errorMessage = ''; 154 | for (var i= 0; i < this.errors.length; i++) { 155 | if (i > 0) 156 | errorMessage += '\n\r\n\r'; 157 | errorMessage += this.errors[i]; 158 | } 159 | 160 | return errorMessage; 161 | }; 162 | 163 | this.findElement = function(selector) { 164 | return jQuery(this.element).find(selector); 165 | }; 166 | }; -------------------------------------------------------------------------------- /Classes/Form/Fields/Field.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Form\Fields; 15 | 16 | use Romm\SiteFactory\Utility\TypoScriptUtility; 17 | use TYPO3\CMS\Core\Utility\GeneralUtility; 18 | use Romm\SiteFactory\Core\Core; 19 | 20 | /** 21 | * A class allowing to manage the fields configuration. 22 | */ 23 | class Field 24 | { 25 | 26 | /** 27 | * Storage for the fields configuration on several pages. 28 | * 29 | * @var array 30 | */ 31 | private static $fieldsConfiguration = []; 32 | 33 | /** 34 | * Storage for the fields on several pages. 35 | * 36 | * @var array 37 | */ 38 | private static $field = []; 39 | 40 | /** 41 | * Returns all the fields of a given page. 42 | * 43 | * @param $pageUid int The id of the page you want the fields of. 44 | * @param $checkHideInSiteModification bool If true, it will check if the field has the key "hideInSiteModification"; if it does, it will not be sent. 45 | * @return AbstractField[] Array containing fields. 46 | * @throws \Exception 47 | */ 48 | public static function getFields($pageUid, $checkHideInSiteModification = false) 49 | { 50 | $fieldsConfiguration = self::getFieldsConfiguration($pageUid); 51 | $fields = []; 52 | 53 | foreach ($fieldsConfiguration as $fieldName => $fieldConfiguration) { 54 | $field = self::getField($fieldName, $pageUid); 55 | 56 | // If $checkHideInSiteModification is true, we add the field only if it must be activated in a site modification. 57 | if (!$checkHideInSiteModification || ($checkHideInSiteModification && !$field->getHideInSiteModification())) { 58 | $fields[$fieldName] = $field; 59 | } 60 | } 61 | 62 | return $fields; 63 | } 64 | 65 | /** 66 | * Returns a specific field of a given page. 67 | * 68 | * @param $fieldName string The name of the field. 69 | * @param $pageUid int The id of the page you want the field of. 70 | * @return AbstractField The field. 71 | * @throws \Exception 72 | */ 73 | public static function getField($fieldName, $pageUid) 74 | { 75 | if (!isset(self::$field[$pageUid][$fieldName])) { 76 | $fieldsConfiguration = self::getFieldsConfiguration($pageUid); 77 | 78 | // Checking if the field exists. 79 | if (!isset($fieldsConfiguration[$fieldName])) { 80 | throw new \Exception('The field "' . $fieldName . '" does not exist.', 1423772429); 81 | } 82 | 83 | $fieldConfiguration = $fieldsConfiguration[$fieldName]; 84 | 85 | // The field must have a type. 86 | if (!isset($fieldConfiguration['type'])) { 87 | throw new \Exception('The field "' . $fieldName . '" should have a value for "type".', 1423770475); 88 | } 89 | 90 | // Checking if the type of the field exists. 91 | $fieldType = $fieldConfiguration['type']; 92 | if (!in_array($fieldType, FieldsTypes::getFieldsTypes($pageUid))) { 93 | throw new \Exception("[Field: $fieldName] The field type {$fieldConfiguration['type']} is not allowed. Try one of the following: " . implode(', ', FieldsTypes::getFieldsTypes($pageUid)) . '.', 1423770969); 94 | } 95 | 96 | $fieldsTypesConfiguration = FieldsTypes::getFieldsTypesConfiguration($pageUid); 97 | $field = GeneralUtility::makeInstance($fieldsTypesConfiguration[$fieldType]['class'], $fieldName, $fieldConfiguration['type']); 98 | if (!$field instanceof AbstractField) { 99 | throw new \Exception('The class ' . $fieldsTypesConfiguration[$fieldType]['class'] . ' must extend ' . AbstractField::class . '.', 1423771432); 100 | } 101 | 102 | $field->fillConfiguration($fieldConfiguration); 103 | 104 | self::$field[$pageUid][$fieldName] = $field; 105 | } 106 | 107 | return self::$field[$pageUid][$fieldName]; 108 | } 109 | 110 | /** 111 | * Returns the fields configuration of a given page. 112 | * 113 | * @param $pageUid int The id of the page you want the fields of. 114 | * @return array The fields configuration. 115 | */ 116 | private static function getFieldsConfiguration($pageUid) 117 | { 118 | if (!isset(self::$fieldsConfiguration[$pageUid])) { 119 | self::$fieldsConfiguration[$pageUid] = Core::sortArrayByPositionValue(TypoScriptUtility::getExtensionConfigurationFromPath('fields', $pageUid)); 120 | } 121 | 122 | return self::$fieldsConfiguration[$pageUid]; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Classes/Duplication/Process/UploadedFilesProcess.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * This file is part of the TYPO3 Site Factory project. 6 | * It is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License, either 8 | * version 3 of the License, or any later version. 9 | * 10 | * For the full copyright and license information, see: 11 | * http://www.gnu.org/licenses/gpl-3.0.html 12 | */ 13 | 14 | namespace Romm\SiteFactory\Duplication\Process; 15 | 16 | use Romm\SiteFactory\Duplication\AbstractDuplicationProcess; 17 | use Romm\SiteFactory\Form\Fields\AbstractField; 18 | use TYPO3\CMS\Core\Resource\Driver\LocalDriver; 19 | use TYPO3\CMS\Core\Resource\File; 20 | use TYPO3\CMS\Core\Resource\Folder; 21 | use TYPO3\CMS\Core\Resource\ProcessedFile; 22 | use TYPO3\CMS\Core\Resource\ProcessedFileRepository; 23 | use TYPO3\CMS\Core\Resource\ResourceFactory; 24 | use TYPO3\CMS\Extbase\Domain\Model\FileMount; 25 | use TYPO3\CMS\Extbase\Domain\Repository\FileMountRepository; 26 | 27 | /** 28 | * Class containing functions called when a site is being duplicated. 29 | * See function "run" for more information. 30 | */ 31 | class UploadedFilesProcess extends AbstractDuplicationProcess 32 | { 33 | 34 | /** 35 | * Gets all the fields which contains files, and upload them to the given 36 | * file mount. 37 | */ 38 | public function run() 39 | { 40 | /** @var AbstractField[] $filesFields */ 41 | $filesFields = []; 42 | foreach ($this->getFields() as $field) { 43 | if ($field->getSettings('moveToFileMount') && $field->getValue() != '') { 44 | if (substr($field->getValue(), 0, 4) == 'new:') { 45 | $field->setValue(substr($field->getValue(), 4, strlen($field->getValue()) - 4)); 46 | $filesFields[] = $field; 47 | } 48 | } 49 | } 50 | 51 | if (!empty($filesFields)) { 52 | $fileMountUid = $this->getDuplicationData('fileMountUid'); 53 | 54 | if ($fileMountUid) { 55 | /** @var FileMountRepository $fileMountRepository */ 56 | $fileMountRepository = $this->objectManager->get(FileMountRepository::class); 57 | 58 | /** @var FileMount $fileMount */ 59 | $fileMount = $fileMountRepository->findByUid($fileMountUid); 60 | if ($fileMount) { 61 | $filesMoved = []; 62 | 63 | /** @var ResourceFactory $resourceFactory */ 64 | $resourceFactory = $this->objectManager->get(ResourceFactory::class); 65 | $storage = $resourceFactory->getDefaultStorage(); 66 | 67 | /** @var Folder $folder */ 68 | $folderPath = substr($fileMount->getPath(), 1, strlen($fileMount->getPath())); 69 | $folder = $this->objectManager->get(Folder::class, $storage, $folderPath, 'SiteFactory'); 70 | 71 | /** @var LocalDriver $driver */ 72 | $driver = $resourceFactory->getDriverObject($storage->getDriverType(), $storage->getConfiguration()); 73 | $driver->processConfiguration(); 74 | 75 | foreach ($filesFields as $field) { 76 | $name = $field->getName(); 77 | $path = $field->getValue(); 78 | $fileExtension = substr(strrchr($path, '.'), 1); 79 | $identifier = $folderPath . $name . '.' . $fileExtension; 80 | 81 | if (file_exists($path)) { 82 | /** @var File $file */ 83 | if ($driver->fileExists($identifier)) { 84 | $file = $storage->getFile($identifier); 85 | $storage->replaceFile($file, $path); 86 | 87 | /** @var ProcessedFileRepository $processedFileRepository */ 88 | $processedFileRepository = $this->objectManager->get(ProcessedFileRepository::class); 89 | /** @var ProcessedFile[] $processedFiles */ 90 | $processedFiles = $processedFileRepository->findAllByOriginalFile($file); 91 | 92 | foreach ($processedFiles as $processedFile) { 93 | $processedFile->delete(); 94 | } 95 | } else { 96 | $file = $storage->addFile($path, $folder, $name . '.' . $fileExtension, 'replace'); 97 | } 98 | 99 | $this->getField($field->getName())->setValue($driver->getPublicUrl($identifier)); 100 | $filesMoved[$name] = $file->getName(); 101 | } 102 | } 103 | 104 | if (!empty($filesMoved)) { 105 | $this->addNotice( 106 | 'duplication_process.uploaded_files.notice.success', 107 | 1435421057, 108 | [$folder->getPublicUrl(), '"' . implode('", ', $filesMoved) . '"'] 109 | ); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/SiteFactory.Form.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** @namespace SiteFactory */ 4 | 5 | /** 6 | * @typedef {SiteFactory.Field} SiteFactoryField 7 | */ 8 | SiteFactory.Form = { 9 | /** 10 | * Repository containing all instances of forms. 11 | */ 12 | repository: [], 13 | 14 | GetAllInstances: function() { 15 | return this.repository; 16 | }, 17 | 18 | GetInstanceByName: function(name) { 19 | for (var i = 0; i < this.repository.length; i++) { 20 | var formInstance = this.repository[i]; 21 | 22 | if (formInstance.element === name) { 23 | return formInstance; 24 | } 25 | } 26 | 27 | return null; 28 | }, 29 | 30 | Instance: function(element) { 31 | SiteFactory.Form.repository.push(this); 32 | 33 | this.element = element; 34 | this.fields = {}; 35 | this.modelSiteId = 0; 36 | /** 37 | * @type {SiteFactory.Menu} 38 | */ 39 | this.menu = null; 40 | var localForm = this; 41 | 42 | this.repository = []; 43 | 44 | /** 45 | * Gets all form fields. 46 | * 47 | * @return {Object}} 48 | */ 49 | this.getFields = function() { 50 | if (Object.keys(this.fields).length == 0) { 51 | jQuery('.form-field').each(function() { 52 | var field = new SiteFactory.Field(localForm, jQuery(this)); 53 | localForm.fields[field.name] = field; 54 | }); 55 | } 56 | 57 | return this.fields; 58 | }; 59 | 60 | /** 61 | * Gets all fields, filtered on the given type (text, select, color_picker, 62 | * etc.). 63 | * 64 | * @param {string} type The type you want to filter on. 65 | * @return {Object} 66 | */ 67 | this.getFieldsByType = function(type) { 68 | var fields = this.getFields(), 69 | typedFields = {}; 70 | 71 | for(var index in fields) 72 | if (fields.hasOwnProperty(index)) 73 | if (fields[index].type == type) 74 | typedFields[index] = fields[index]; 75 | 76 | return typedFields; 77 | }; 78 | 79 | /** 80 | * Gets all fields, filtered on the given field type (text, select, etc.). 81 | * 82 | * @param {string} type The type you want to filter on. 83 | * @return {Object} 84 | */ 85 | this.getFieldsByFieldType = function(type) { 86 | var fields = this.getFields(), 87 | typedFields = {}; 88 | 89 | for(var index in fields) 90 | if (fields.hasOwnProperty(index)) 91 | if (fields[index].fieldType == type) 92 | typedFields[index] = fields[index]; 93 | 94 | return typedFields; 95 | }; 96 | 97 | /** 98 | * Gets a field from its name. 99 | * 100 | * @param {string} name The name of the field. 101 | * @return {SiteFactory.Field} 102 | */ 103 | this.getFieldByName = function(name) { 104 | var fields = this.getFields(); 105 | 106 | for(var index in fields) 107 | if (fields.hasOwnProperty(index)) 108 | if (fields[index].name == name) 109 | return this.fields[index]; 110 | 111 | return null; 112 | }; 113 | 114 | /** 115 | * This function is called when clicking on a button/link (should come from the 116 | * header). It will submit the main site creation form. 117 | * 118 | * @param {string} action The name of the controller's action. 119 | * @return {boolean} 120 | */ 121 | this.submit = function(action) { 122 | this.findElement('input[field-name="action"]').attr('value', action); 123 | jQuery('#' + this.element).parents('form').submit(); 124 | 125 | return false; 126 | }; 127 | 128 | this.findElement = function(selector) { 129 | return jQuery('#' + this.element).find(selector); 130 | }; 131 | 132 | this.refreshSiteNameHeader = function(val) { 133 | jQuery('.static-site-name span.content').html(val); 134 | }; 135 | 136 | jQuery(document).ready(function() { 137 | var menu = jQuery('.static-menu[data-site-factory-form="' + localForm.element + '"]'); 138 | if (typeof menu != 'undefined') 139 | localForm.menu = new SiteFactory.Menu(localForm, menu); 140 | 141 | // Reloading page after changing model site. 142 | var modelSiteField = localForm.getFieldByName('modelSite'); 143 | if (modelSiteField) { 144 | var modelSiteSelect = modelSiteField.input; 145 | localForm.modelSiteId = modelSiteSelect.val(); 146 | jQuery(modelSiteSelect).on( 147 | 'change', 148 | {form: localForm}, 149 | function(event) { 150 | localForm.modelSiteId = modelSiteSelect.val(); 151 | event.data.form.findElement('input[data-name="changeModelSiteId"]').val(localForm.modelSiteId); 152 | event.data.form.submit('new'); 153 | } 154 | ); 155 | } 156 | else { 157 | var modifySiteElement = localForm.findElement('input[data-name="modifySiteId"]'); 158 | 159 | if (modifySiteElement.length > 0) 160 | localForm.modelSiteId = modifySiteElement.val(); 161 | } 162 | 163 | if (typeof refreshForm !== 'undefined') { 164 | var fields = localForm.getFields(); 165 | for(var index in fields) 166 | if (fields.hasOwnProperty(index)) 167 | localForm.fields[index].validate(); 168 | } 169 | 170 | // Refreshing the static site's name in the header, depending on the input events. 171 | var siteNameField = localForm.getFieldByName('siteTitle'); 172 | if (siteNameField) { 173 | localForm.refreshSiteNameHeader(siteNameField.input.val()); 174 | 175 | siteNameField.input.on('change keyup', function() { 176 | localForm.refreshSiteNameHeader(jQuery(this).val()); 177 | }); 178 | } 179 | }); 180 | 181 | return this; 182 | } 183 | }; --------------------------------------------------------------------------------