diff --git a/Gruntfile.js b/Gruntfile.js index b727420..58abcbd 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -18,7 +18,13 @@ module.exports = function(grunt) { options: { spawn: false, } + }, + + dev: { + files: ['app/**'], + tasks: ['deploy'] } + }, jshint: { @@ -34,9 +40,22 @@ module.exports = function(grunt) { copy: { main: { files: [ - {expand: true, src: ['app/package.manifest'], dest: '<%= dest %>', flatten: true}, - {expand: true, src: ['app/views/archetype.html'], dest: '<%= dest %>/views', flatten: true} + {expand: true, cwd: 'app/', src: ['package.manifest'], dest: '<%= dest %>', flatten: true}, + {expand: true, cwd: 'app/views/', src: ['archetype.html'], dest: '<%= dest %>/views', flatten: true} ] + }, + deploy: { + files: [ + {expand: true, cwd: '<%= dest %>/', src: ['**'], dest: '<%= grunt.option("target") %>\\App_Plugins\\Imulus.Archetype', flatten: false}, + ] + } + + }, + + touch: { + options: {}, + webconfig: { + src: ['D:\\dev\\projects\\imulus-neue\\src\\imulus.umbraco\\web.config'] } }, @@ -57,8 +76,8 @@ module.exports = function(grunt) { }, application: { src: [ - 'app/controllers/archetype_controller.js', - 'app/directives/content_item.js' + 'app/controllers/controller.js', + 'app/directives/archetypeproperty.js' ], dest: '<%= dest %>/js/archetype.js' } @@ -75,10 +94,12 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-touch'); - + grunt.registerTask('touchwebconfigifenabled', function() { if (grunt.option("touch")) grunt.task.run("touch:webconfig") }); + grunt.registerTask('deploy', ['default', 'copy:deploy', 'touchwebconfigifenabled']); grunt.registerTask('css:build', ['less']); grunt.registerTask('js:build', ['concat']); - grunt.registerTask('default', ['clean', 'css:build', 'js:build', 'copy']); + grunt.registerTask('default', ['clean', 'css:build', 'js:build', 'copy:main']); }; diff --git a/README.md b/README.md index 28f8384..940bf26 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,7 @@ Archetype ## Deploy ## -Copy the files in `/dist/` to your Umbraco site, at `/App_Plugins/Archetype/` \ No newline at end of file + grunt deploy --target=C:\\path\\to\\umbraco\\site + grunt watch:dev --target=C:\\path\to\\umbraco\\site + + * Add `--touch` to either command to automatically touch the web.config on a deploy \ No newline at end of file diff --git a/app/controllers/archetype_controller.js b/app/controllers/controller.js similarity index 64% rename from app/controllers/archetype_controller.js rename to app/controllers/controller.js index 9926976..bb7c326 100644 --- a/app/controllers/archetype_controller.js +++ b/app/controllers/controller.js @@ -2,15 +2,14 @@ //$scope.model.value = ""; - //test config/model - //$scope.model.config.emptyFieldSetModel = '{ remove: false, properties:[{ type: "textbox", options: { label: "Name" }, data: "" }, { type: "contentPicker", options: { label: "Pick some content" }, data: "" }]}'; - //set default value of the model //this works by checking to see if there is a model; then cascades to the default model then to an empty fieldset - var validDefaultModel = getValidJson("$scope.model.config.defaultModel", $scope.model.config.defaultModel); - var validEmptyFieldsetModel = getValidJson("$scope.model.config.emptyFieldsetModel", $scope.model.config.emptyFieldsetModel); - $scope.model.value = $scope.model.value || (validDefaultModel || { fieldsets: [validEmptyFieldsetModel] }); + //validate the user configs + $scope.model.config.defaultModel = getValidJson("$scope.model.config.defaultModel", $scope.model.config.defaultModel); + $scope.model.config.fieldsetModels = getValidJson("$scope.model.config.fieldsetModels", $scope.model.config.fieldsetModels); + + $scope.model.value = $scope.model.value || ($scope.model.config.defaultModel || { fieldsets: [getEmptyRenderItem($scope.model.config.fieldsetModels[0])] }); //ini $scope.archetypeRenderModel = {}; @@ -20,7 +19,10 @@ //defines the options for the jquery sortable //i used an ng-model="archetypeRenderModel" so the sort updates the right model - $scope.sortableOptions = { + //configuration overrides the default + var configSortableOptions = getValidJson("$scope.model.config.sortableOptions", $scope.model.config.sortableOptions); + + $scope.sortableOptions = configSortableOptions || { axis: 'y', cursor: "move", handle: ".handle", @@ -28,18 +30,26 @@ }, stop: function (ev, ui) { - console.log($scope.archetypeRenderModel); + } }; - $scope.addRow = function ($index) { - if (true) + //handles a fieldset add + $scope.addRow = function (fieldsetAlias, $index) { + if ($scope.canAdd()) { - var validJson = getValidJson("$scope.model.config.emptyFieldsetModel", $scope.model.config.emptyFieldsetModel); - - if (validJson) + if ($scope.model.config.fieldsetModels) { - $scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, validJson); + var newRenderItem = getEmptyRenderItem($scope.getConfigFieldsetByAlias(fieldsetAlias)); + + if (typeof $index != 'undefined') + { + $scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, newRenderItem); + } + else + { + $scope.archetypeRenderModel.fieldsets.push(newRenderItem); + } } } } @@ -64,11 +74,13 @@ return true; } + //helper that returns if an item can be removed $scope.canRemove = function () { return countVisible() > 1; } + //helper that returns if an item can be sorted $scope.canSort = function () { return countVisible() > 1; @@ -79,6 +91,15 @@ $scope.archetypeRenderModel = $scope.model.value; } + //helper to get the correct fieldset from config + $scope.getConfigFieldsetByAlias = function(alias) { + for (var i in $scope.model.config.fieldsetModels) { + if ($scope.model.config.fieldsetModels[i].alias == alias) { + return $scope.model.config.fieldsetModels[i]; + } + } + } + //helper returns valid JS or null function getValidJson(variable, json) { @@ -97,6 +118,7 @@ //developerMode helpers $scope.archetypeRenderModel.toString = stringify; + //encapsulate stringify (should be built into browsers, not sure of IE support) function stringify() { return JSON.stringify(this); } @@ -104,7 +126,7 @@ //watch for changes $scope.$watch('archetypeRenderModel', function (v) { if ($scope.model.config.developerMode) { - console.log(v); + //console.log(v); if (typeof v === 'string') { $scope.archetypeRenderModel = JSON.parse(v); $scope.archetypeRenderModel.toString = stringify; @@ -138,6 +160,12 @@ } } + //helper to add an empty fieldset + function getEmptyRenderItem (fieldsetModel) + { + return eval("({ alias: '" + fieldsetModel.alias + "', remove: false, properties: []})"); + } + //sync things up on save $scope.$on("formSubmitting", function (ev, args) { syncModelToRenderModel(); @@ -146,10 +174,10 @@ //custom js if ($scope.model.config.customJsPath) { assetsService.loadJs($scope.model.config.customJsPath); - } + } //archetype css - assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css"); + assetsService.loadCss("/App_Plugins/Imulus.Archetype/css/archetype.css"); //custom css if($scope.model.config.customCssPath) diff --git a/app/directives/archetypeproperty.js b/app/directives/archetypeproperty.js new file mode 100644 index 0000000..632a675 --- /dev/null +++ b/app/directives/archetypeproperty.js @@ -0,0 +1,89 @@ +angular.module("umbraco").directive('archetypeProperty', function ($compile, $http) { + + function getFieldsetByAlias(fieldsetModels, alias) + { + for (var i in fieldsetModels) + { + if (fieldsetModels[i].alias == alias) + { + return fieldsetModels[i]; + } + } + } + + function getPropertyIdByAlias(properties, alias) + { + for (var i in properties) + { + if (properties[i].alias == alias) { + return i; + } + } + } + + var linker = function (scope, element, attrs) { + + var configFieldsetModel = getFieldsetByAlias(scope.archetypeConfig.fieldsetModels, scope.fieldset.alias); + + var view = configFieldsetModel.properties[scope.propertyConfigIndex].view; + var label = configFieldsetModel.properties[scope.propertyConfigIndex].label; + var config = configFieldsetModel.properties[scope.propertyConfigIndex].config; + var alias = configFieldsetModel.properties[scope.propertyConfigIndex].alias; + + if (view) + { + $http.get(view).success(function (data) { + if (data) { + if (scope.archetypeConfig.developerMode == '1') + { + console.log(scope); + } + + var rawTemplate = data; + + //define the initial model and config + scope.model = {}; + scope.model.config = {}; + + //ini the property value after test to make sure a prop exists in the renderModel + var renderModelPropertyIndex = getPropertyIdByAlias(scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties, alias); + + if (!renderModelPropertyIndex) + { + scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties.push(eval("({alias: '" + alias + "', value:''})")); + renderModelPropertyIndex = getPropertyIdByAlias(scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties, alias); + } + scope.model.value = scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties[renderModelPropertyIndex].value; + + //set the config from the prevalues + scope.model.config = config; + + //some items need an alias + scope.model.alias = "scope-" + scope.$id; + + //watch for changes since there is no two-way binding with the local model.value + scope.$watch('model.value', function (newValue, oldValue) { + scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties[renderModelPropertyIndex].value = newValue; + }); + + element.html(rawTemplate).show(); + $compile(element.contents())(scope); + } + }); + } + } + + return { + restrict: "E", + rep1ace: true, + link: linker, + scope: { + property: '=', + propertyConfigIndex: '=', + archetypeConfig: '=', + fieldset: '=', + fieldsetIndex: '=', + archetypeRenderModel: '=' + } + } +}); \ No newline at end of file diff --git a/app/directives/content_item.js b/app/directives/content_item.js deleted file mode 100644 index 00dc566..0000000 --- a/app/directives/content_item.js +++ /dev/null @@ -1,47 +0,0 @@ -angular.module("umbraco").directive('contentItem', function ($compile, $http) { - - var linker = function (scope, element, attrs) { - - if (scope.content.view) - { - $http.get(scope.content.view).success(function (data) { - if (data) { - var rawTemplate = data; - - //define the initial model and config - scope.model = {}; - scope.model.config = {}; - - //pull these from the content - scope.model.value = scope.content.value; - scope.model.config = scope.content.config; - - //some items need an alias - scope.model.alias = "scope-" + scope.$id; - - //watch for changes since there is no two-way binding with the child values - scope.$watch('model.value', function (newValue, oldValue) { - scope.content.value = newValue; - }); - - //add label - if (true) { - rawTemplate = '' + rawTemplate; - } - - element.html(rawTemplate).show(); - $compile(element.contents())(scope); - } - }); - } - } - - return { - restrict: "E", - rep1ace: true, - link: linker, - scope: { - content: '=' - } - } -}); \ No newline at end of file diff --git a/app/less/archetype.less b/app/less/archetype.less index cf86d0d..7329433 100644 --- a/app/less/archetype.less +++ b/app/less/archetype.less @@ -3,6 +3,7 @@ padding: 5px; margin-bottom: 5px; background-color: #fff; + clear: both; } .archetypeEditor label{ @@ -10,6 +11,12 @@ float: left; } +.archetypeEditor fieldset>label{ + float: left; + width: 200px; + padding-left: 0; +} + .archetypeEditor .multiPropertyTextbox{ margin-bottom: 3px; } @@ -24,6 +31,7 @@ .archetypeEditor .archetypeProperty{ clear: both; + overflow: hidden; } .archetypeEditor .umb-contentpicker{ @@ -37,4 +45,35 @@ min-height: 100px; margin-bottom: 10px; border: 1px solid red; +} + +.archetypeEditor .archetypeFieldsetToolbar { + margin-bottom: 10px; + overflow: hidden; +} + +.archetypeEditor .archetypeFieldsetToolbar li { + float: left; + padding: 2px 4px; + margin-right: 10px; + border: 1px solid #ddd; + cursor: pointer; +} + +.archetypeEditor .archetypeFieldsetHeaderText{ + clear:both; + margin-bottom: 3px; + font-size: 12px; +} + +.archetypeEditor .archetypeFieldsetFooterText{ + clear:both; + margin-bottom: 3px; + font-size: 12px; +} + +.archetypeEditor .archetypeFieldsetHelpText{ + clear:both; + margin-bottom: 3px; + font-size: 12px; } \ No newline at end of file diff --git a/app/package.manifest b/app/package.manifest index 2b33ac1..2a6744d 100644 --- a/app/package.manifest +++ b/app/package.manifest @@ -1,27 +1,56 @@ { propertyEditors: [ { - alias: "Archetype", - name: "Archetype", + alias: "Imulus.Archetype", + name: "Imulus Archetype", editor: { - view: "~/App_Plugins/Archetype/views/archetype.html", + view: "~/App_Plugins/Imulus.Archetype/views/archetype.html", valueType: "JSON" }, prevalues: { fields: [ { - label: "Empty Fieldset Model", - description: "(Required) What would you like a new fieldset model to be?", - key: "emptyFieldsetModel", + label: "Empty Fieldset Models", + description: "(Required) Define your fieldset models?", + key: "fieldsetModels", view: "textarea", + validation: [ + { + type: "Required" + } + ] + }, + { + label: "Hide fieldset toolbar?", + description: "Hides the fieldset toolbar that appears when more than one fieldset model appears.", + key: "hideFieldsetToolbar", + view: "boolean", validation: [ ] }, { - label: "Hide editor controls?", - description: "Hide the add/remove/sort controls", - key: "hideControls", + label: "Hide fieldset editor controls?", + description: "Hides the fieldset add/remove/sort controls.", + key: "hideFieldsetControls", + view: "boolean", + validation: [ + + ] + }, + { + label: "Hide fieldset labels?", + description: "Hides the fieldset labels.", + key: "hideFieldsetLabels", + view: "boolean", + validation: [ + + ] + }, + { + label: "Hide property labels?", + description: "Hides the property labels.", + key: "hidePropertyLabels", view: "boolean", validation: [ @@ -46,8 +75,8 @@ ] }, { - label: "Custom Class", - description: "(Optional) Enter a custom CSS class.", + label: "Custom Wrapper Class", + description: "(Optional) Enter a custom CSS class that will be applied to the wrapper div.", key: "customCssClass", view: "textstring", validation: [ @@ -69,24 +98,32 @@ key: "customJsPath", view: "textstring", validation: [ - + ] }, { label: "Developer Mode", - description: "Turn on for developer options.", + description: "(Advanced) Turn on for developer options.", key: "developerMode", view: "boolean", validation: [ + ] + }, + { + label: "Sortable Options", + description: "(Advanced) Override the default sortable controls with a JSON object.", + key: "sortableOptions", + view: "textarea", + validation: [ + ] } ] } } - ] - , + ], javascript: [ - '~/App_Plugins/Archetype/js/archetype.js' + '~/App_Plugins/Imulus.Archetype/js/archetype.js' ] } \ No newline at end of file diff --git a/app/views/archetype.html b/app/views/archetype.html index 939f480..84c7bb6 100644 --- a/app/views/archetype.html +++ b/app/views/archetype.html @@ -1,16 +1,41 @@ 
+
+ +
diff --git a/package.json b/package.json index 209ff87..8b9f64f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "grunt-contrib-watch": "~0.5.3", "grunt-contrib-jshint": "~0.7.2", "grunt-cli": "~0.1.11", - "grunt-contrib-clean": "~0.5.0" + "grunt-contrib-clean": "~0.5.0", + "grunt-touch": "~0.1.0" } }