diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/package.manifest b/UmbracoPackages/App_Plugins/brothers.uNesting/package.manifest index 7c748b6..e7c9b1e 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/package.manifest +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/package.manifest @@ -1,6 +1,7 @@ { "javascript": [ - "~/App_Plugins/brothers.uNesting/uNesting.js" + "~/App_Plugins/brothers.uNesting/uNesting.js", + "~/App_Plugins/brothers.uNesting/uNesting.directives.js" ], "css": [ "~/App_Plugins/brothers.uNesting/uNesting.css" diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.css b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.css index 9d44f97..f948ff6 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.css +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.css @@ -90,7 +90,7 @@ position: relative; margin-top: 1px; padding: 20px 0; - color: #1b264f; + color: #000; border-radius: 3px 3px 0 0; border-bottom: none; background: none; @@ -107,8 +107,7 @@ top: 50%; margin-top: -12px; left: 3px; - font-size: 1.4rem; - z-index: 1; + font-size: 20px; } .unesting-item-header-content.--has-icon @@ -130,15 +129,15 @@ bottom: -15px; } -.unesting-item-header:hover .unesting-item-header-inner .unesting-item-header-content +/*.unesting-item-header:hover .unesting-item-header-inner .unesting-item-header-content { padding-right: 130px; -} +}*/ .unesting-item-header-content { line-height: 20px; - color: #1b264f; + color: #000; white-space: nowrap; position: relative; } @@ -189,9 +188,10 @@ un-config { position: absolute; right: 0; - top: 50%; + top: 0; + bottom: 0; + align-items: center; padding: 5px; - margin-top: -22px; display: flex; grid-gap: 8px; } @@ -200,6 +200,7 @@ un-config { transition: opacity .12s ease-in-out; opacity: 0; + background: white; } .unesting-item-header:hover .unesting-icons > * @@ -215,11 +216,11 @@ un-config height: 32px; justify-content: center; align-items: center; - border-radius: 20px; - border: 1px solid #e9e9eb; + border-radius: 16px; cursor: pointer; - color: #1b264f; + color: #000; transition: background 0.12s ease; + border: 1px solid #f6f6f7; } .unesting-icon.is-negative:hover @@ -241,7 +242,7 @@ un-config height: 3px; width: 3px; border-radius: 50%; - background: #1b264f; + background: #000; display: inline-block; margin: 0 1px; opacity: .6; @@ -280,7 +281,7 @@ un-config .unesting-content > .umb-pane { - margin: 30px 20px; + margin: 30px 20px 30px 20px; } .unesting-item.is-active > .unesting-content @@ -324,9 +325,14 @@ un-config border-bottom-color: #e9e9eb; } +.unesting-item .unesting-item .unesting-icons > * +{ + background: #faf8f8; + border-color: #e9e9eb; +} + /* media */ - .unesting-media { display: flex; @@ -431,7 +437,7 @@ un-config background: #f6f4f4; border-radius: 4px; width: 180px; - min-height: 60px; + /*min-height: 60px;*/ max-height: 65px; padding: 10px 15px; display: block; @@ -444,7 +450,7 @@ un-config font-size: 14px; white-space: nowrap; text-overflow: ellipsis; - color: #1b264f; + color: #333; height: 20px; overflow: hidden; margin-bottom: 5px; @@ -514,7 +520,7 @@ un-config { display: block; font-size: 14px; - color: #1b264f; + color: #000; font-weight: 600; margin-bottom: 4px; } @@ -531,9 +537,9 @@ un-config /* doctypepicker */ -.unesting-doctype-textarea +.unesting-doctype-template { - width: 100%; + width: 400px; height: 64px; max-height: 200px; min-height: 32px; @@ -572,7 +578,7 @@ un-config .unesting-item[unesting-alias="newsletterHeadline"] .unesting-text-partial[unesting-index="0"] { font-size: 16px; - color: #1b264f; + color: #000; font-weight: 600; } @@ -594,9 +600,12 @@ un-config content: '“'; } +.umb-nested-content-property-container +{ + margin-bottom: 12px; +} /* generic overrides */ - .btn-info { color: #454957; diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.directives.js b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.directives.js new file mode 100644 index 0000000..f1ea618 --- /dev/null +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.directives.js @@ -0,0 +1,457 @@ + + +angular.module('umbraco.directives').directive('unContent', function ($compile) +{ + return { + restrict: 'E', + scope: { + item: '=', + node: '=', + config: '=' + }, + link: function (scope, element) + { + // get template + var template = scope.config && scope.config.nameTemplate ? scope.config.nameTemplate : null; + + // compile & render template + function render() + { + // merge element values into local scope + angular.merge(scope, scope.item); + + // nothing when no template is specified + if (!template) + { + element.html(''); + //element.html(scope.node.documentType.description); + return; + } + + // create html and compile + element.html(template); + $compile(element.contents())(scope); + } + + //scope.$watch('item', function (value) + //{ + // render(); + //}); + + // first-time rendering + render(); + } + }; +}); + + +angular.module('umbraco.directives').directive('unMedia', function ($http, $compile, umbRequestHelper) +{ + var sizes = { + small: 'width=30&height=30&mode=crop&quality=60', + medium: 'width=60&height=60&mode=crop&quality=60', + large: 'width=100&height=100&mode=crop&quality=60', + wide: 'width=100&height=60&mode=crop&quality=60', + high: 'width=60&height=100&mode=crop&quality=60' + }; + + return { + restrict: 'E', + scope: { + model: '=', + key: '@', + titleKey: '@', + limit: '@', + size: '@', + hideLabel: '@' + }, + + template: '
' + + '
' + + '' + + '{{item.title | unHtml }}' + + '
' + + '+{{count - media.length}}' + + '
', + + link: function (scope, element) + { + scope._size = sizes[scope.size] ? scope.size : 'medium'; + + var resize = sizes[scope._size]; + var limit = scope.limit || 5; + scope.media = []; + + var titleCache = {}; + + + // compile & render template + function render() + { + var ids = scope.model; + + if (!scope.model) + { + return; + } + + if (_.isArray(scope.model)) + { + if (typeof scope.model[0] !== 'string' && !scope.key) + { + console.error("You need the property for nested media"); + return; + } + + ids = _.map(scope.model, function (item) + { + if (typeof item === 'string') + { + return item; + } + + var value = item[scope.key]; + + if (scope.titleKey) + { + titleCache[value] = item[scope.titleKey]; + } + + return value; + }).join(','); + } + + ids = _.filter(ids.split(','), function (id) { return !!id; }); + scope.count = ids.length; + + var query = "ids=" + ids.slice(0, limit).join('&ids='); + var url = $http.get(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/UmbracoApi/Unesting/GetMediaByUdis?" + query); + + umbRequestHelper.resourcePromise(url, 'Failed to retrieve data for media ids').then(function (result) + { + _.each(result, function (src, id) + { + scope.media.push({ + id: id, + src: src + "?" + resize, + title: titleCache[id] + }); + }); + + // hide label if requested + if (scope.media.length > 0 && (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1')) + { + element.closest('.unesting-item-header').addClass('no-label'); + } + }); + } + + render(); + } + }; +}); + + + +angular.module('umbraco.directives').directive('unText', function ($filter) +{ + return { + restrict: 'E', + scope: { + model: '=', + maxLines: '@', + hideLabel: '@' + }, + + template: '
' + + '' + + '
', + + link: function (scope, element) + { + scope.maxLines = +scope.maxLines || 2; + scope.items = []; + + if (!scope.model) + { + return; + } + + // hide label if requested + if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') + { + element.closest('.unesting-item-header').addClass('no-label'); + } + + var partials = _.filter(!_.isArray(scope.model) ? [scope.model] : scope.model, function (text) { return !!text; }); + + scope.items = _.map(partials, function (text) + { + return $filter('unHtml')(text, 1000); + }); + } + }; +}); + + + +angular.module('umbraco.directives').directive('unBoxes', function ($compile) +{ + return { + restrict: 'E', + scope: { + model: '=', + key: '@', + titleKey: '@', + limit: '@', + size: '@', + hideLabel: '@' + }, + + template: '
' + + '
' + + '{{item.headline | unHtml }}' + + '{{item.text | unHtml:200 }}' + + '
' + + '+{{count - items.length}}' + + '
', + + link: function (scope, element) + { + var maxLines = { + medium: 3, + large: 6 + }; + + scope.size = maxLines[scope.size] ? scope.size : 'medium'; + var limit = +scope.limit || 3; + scope.items = []; + + if (!scope.model) + { + return; + } + + // hide label if requested + if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') + { + element.closest('.unesting-item-header').addClass('no-label'); + } + + if (!_.isArray(scope.model)) + { + console.error("The model has to be an array"); + return; + } + + if (_.isObject(scope.model[0]) && !scope.key) + { + console.error("You need the property for nested elements"); + return; + } + + var items = []; + _.each(scope.model, function (item) + { + var max = maxLines[scope.size]; + + if (item) + { + if (typeof item === 'string') + { + items.push({ + maxLines: max, + text: item + }); + } + + var headline = item[scope.titleKey]; + + items.push({ + maxLines: headline ? max - 1 : max, + headline: headline, + text: item[scope.key] + }); + } + }); + + scope.items = items.slice(0, limit); + scope.count = items.length; + } + }; +}); + + + +angular.module('umbraco.directives').directive('unFigure', function ($filter) +{ + return { + restrict: 'E', + scope: { + image: '=', + text: '=', + headline: '=', + size: '@', + maxLines: '@', + hideLabel: '@' + }, + + template: '
' + + '' + + '
' + + '{{headline | unHtml }}' + + '{{text | unHtml:800 }}' + + '
' + + '
', + + link: function (scope, element) + { + scope.hasContent = !!(scope.text || scope.headline || scope.image); + + // hide label if requested + if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') + { + element.closest('.unesting-item-header').toggleClass('no-label', scope.hasContent); + } + + // calculate max lines + scope.maxLines = +scope.maxLines || 3; + if (scope.headline) + { + scope.maxLines -= 1; + } + } + }; +}); + + + +angular.module('umbraco.directives').directive('unConfig', function ($filter) +{ + return { + restrict: 'E', + scope: { + hideLabel: '@', + asColumns: '@' + }, + + template: '', + + link: function (scope, element) + { + if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') + { + element.closest('.unesting-item-header').addClass('no-label'); + } + if (scope.asColumns === true || scope.asColumns === 'true' || scope.asColumns === '1') + { + element.closest('.unesting-item-header').addClass('as-columns'); + } + } + }; +}); + + + +angular.module("umbraco.directives").directive('uNestingContentEditor', [ + + function () + { + var link = function ($scope) + { + // Clone the model because some property editors + // do weird things like updating and config values + // so we want to ensure we start from a fresh every + // time, we'll just sync the value back when we need to + $scope.model = angular.copy($scope.ngModel); + $scope.nodeContext = $scope.model; + + // Find the selected tab + var selectedTab = $scope.model.variants[0].tabs[0]; + + if ($scope.tabAlias) + { + angular.forEach($scope.model.variants[0].tabs, function (tab) + { + if (tab.alias.toLowerCase() === $scope.tabAlias.toLowerCase()) + { + selectedTab = tab; + return; + } + }); + } + + $scope.tabs = $scope.model.variants[0].tabs; + + var contentTab = _.find($scope.tabs, function (tab) + { + var alias = tab.alias.toLowerCase(); + return tab.id !== 0 && alias !== 'unestingsettings' && (!$scope.tabAlias || alias === $scope.tabAlias.toLowerCase()); + }); + + $scope.contentProperties = _.filter(contentTab.properties, function (prop) + { + return prop.propertyAlias.toLowerCase() !== 'unestinghide'; + }); + + //var settingsTab = _.find($scope.tabs, function (tab) + //{ + // var alias = tab.alias.toLowerCase(); + // return tab.id !== 0 && alias === 'unestingsettings'; + //}); + + //$scope.settingsProperties = settingsTab ? settingsTab.properties : []; + $scope.settingsProperties = []; + + // Listen for sync request + var unsubscribe = $scope.$on("ncSyncVal", function (ev, args) + { + if (args.key === $scope.model.key) + { + // Tell inner controls we are submitting + $scope.$broadcast("formSubmitting", { scope: $scope }); + + // Sync the values back + angular.forEach($scope.ngModel.variants[0].tabs, function (tab) + { + if (tab.alias.toLowerCase() === selectedTab.alias.toLowerCase()) + { + + var localPropsMap = selectedTab.properties.reduce(function (map, obj) + { + map[obj.alias] = obj; + return map; + }, {}); + + angular.forEach(tab.properties, function (prop) + { + if (localPropsMap.hasOwnProperty(prop.alias)) + { + prop.value = localPropsMap[prop.alias].value; + } + }); + + } + }); + } + }); + + $scope.$on('$destroy', function () + { + unsubscribe(); + }); + }; + + return { + restrict: "E", + replace: true, + templateUrl: Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/views/propertyeditors/nestedcontent/nestedcontent.editor.html", + scope: { + ngModel: '=', + tabAlias: '=' + }, + link: link + }; + + } +]); \ No newline at end of file diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.docTypePicker.html b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.docTypePicker.html index 82e6275..ef3ae4f 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.docTypePicker.html +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.docTypePicker.html @@ -32,7 +32,8 @@ ng-model="config.ncTabAlias" required> - +
+ diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.editor.html b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.editor.html index 5507570..701b4b6 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.editor.html +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.editor.html @@ -1,4 +1,4 @@ -
+
@@ -11,7 +11,8 @@
-
+ +
diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.html b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.html index 4c89867..b94af14 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.html +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.html @@ -16,7 +16,7 @@
+ ng-class="{ 'is-active' : $parent.realCurrentNode.key === node.key, 'umb-nested-content__item--single' : $parent.singleMode, 'is-hidden': isHidden(model.value[$index]) }">
@@ -67,7 +67,7 @@ - +
diff --git a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.js b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.js index edfbe88..39bb920 100644 --- a/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.js +++ b/UmbracoPackages/App_Plugins/brothers.uNesting/uNesting.js @@ -26,518 +26,6 @@ angular.module('umbraco.services').config(['$httpProvider', function ($httpProvi }]); - - -angular.module("umbraco").controller("brothers.uNesting.DocTypePickerController", function ($scope, $controller) -{ - angular.extend(this, $controller('Umbraco.PropertyEditors.NestedContent.DocTypePickerController', { $scope: $scope })); -}); - - - - -angular.module("umbraco").controller("brothers.uNesting.PropertyEditorController", function ($scope, $controller, $http, $compile) -{ - angular.extend(this, $controller('Umbraco.PropertyEditors.NestedContent.PropertyEditorController', { $scope: $scope })); - - $scope.clickHide = function ($event, node, $index) - { - $event.preventDefault(); - $event.stopPropagation(); - $($event.target).closest('.unesting-item').find('.umb-property[unesting-property="uNestingHide"] .umb-toggle').trigger('click'); - $scope.model.value[$index]['uNestingHide'] = !$scope.model.value[$index]['uNestingHide']; - }; - - $scope.canHide = function (item) - { - return typeof item['uNestingHide'] !== 'undefined'; - }; - - $scope.isHidden = function (index) - { - var item = $scope.model.value[index]; - return item['uNestingHide'] === '1'; - }; -}); - - - -angular.module('umbraco.directives').directive('unContent', function ($compile) -{ - return { - restrict: 'E', - scope: { - item: '=', - node: '=', - config: '=' - }, - link: function (scope, element) - { - // get template - var template = scope.config && scope.config.nameTemplate ? scope.config.nameTemplate : null; - - // compile & render template - function render() - { - // merge element values into local scope - angular.merge(scope, scope.item); - - // nothing when no template is specified - if (!template) - { - element.html(''); - //element.html(scope.node.documentType.description); - return; - } - - // create html and compile - element.html(template); - $compile(element.contents())(scope); - } - - //scope.$watch('item', function (value) - //{ - // render(); - //}); - - // first-time rendering - render(); - } - }; -}); - - -angular.module('umbraco.directives').directive('unMedia', function ($http, $compile, umbRequestHelper) -{ - var sizes = { - small: 'width=30&height=30&mode=crop&quality=60', - medium: 'width=60&height=60&mode=crop&quality=60', - large: 'width=100&height=100&mode=crop&quality=60', - wide: 'width=100&height=60&mode=crop&quality=60', - high: 'width=60&height=100&mode=crop&quality=60' - }; - - return { - restrict: 'E', - scope: { - model: '=', - key: '@', - titleKey: '@', - limit: '@', - size: '@', - hideLabel: '@' - }, - - template: '
' + - '
' + - '' + - '{{item.title | unHtml }}' + - '
' + - '+{{count - media.length}}' + - '
', - - link: function (scope, element) - { - scope._size = sizes[scope.size] ? scope.size : 'medium'; - - var resize = sizes[scope._size]; - var limit = scope.limit || 5; - scope.media = []; - - var titleCache = {}; - - - // compile & render template - function render() - { - var ids = scope.model; - - if (!scope.model) - { - return; - } - - if (_.isArray(scope.model)) - { - if (typeof scope.model[0] !== 'string' && !scope.key) - { - console.error("You need the property for nested media"); - return; - } - - ids = _.map(scope.model, function (item) - { - if (typeof item === 'string') - { - return item; - } - - var value = item[scope.key]; - - if (scope.titleKey) - { - titleCache[value] = item[scope.titleKey]; - } - - return value; - }).join(','); - } - - ids = _.filter(ids.split(','), function (id) { return !!id; }); - scope.count = ids.length; - - var query = "ids=" + ids.slice(0, limit).join('&ids='); - var url = $http.get(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "GetByUdis", query).replace("/Media/", "/MediaExtended/")); - // TODO we have to implement this method in a controller - umbRequestHelper.resourcePromise(url, 'Failed to retrieve data for media ids').then(function (result) - { - _.each(result, function (src, id) - { - scope.media.push({ - id: id, - src: src + "?" + resize, - title: titleCache[id] - }); - }); - - // hide label if requested - if (scope.media.length > 0 && (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1')) - { - element.closest('.unesting-item-header').addClass('no-label'); - } - }); - } - - render(); - } - }; -}); - - - -angular.module('umbraco.directives').directive('unText', function ($filter) -{ - return { - restrict: 'E', - scope: { - model: '=', - maxLines: '@', - hideLabel: '@' - }, - - template: '
' + - '' + - '
', - - link: function (scope, element) - { - scope.maxLines = +scope.maxLines || 2; - scope.items = []; - - if (!scope.model) - { - return; - } - - // hide label if requested - if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') - { - element.closest('.unesting-item-header').addClass('no-label'); - } - - var partials = _.filter(!_.isArray(scope.model) ? [scope.model] : scope.model, function (text) { return !!text; }); - - scope.items = _.map(partials, function (text) - { - return $filter('unHtml')(text, 1000); - }); - } - }; -}); - - - -angular.module('umbraco.directives').directive('unBoxes', function ($compile) -{ - return { - restrict: 'E', - scope: { - model: '=', - key: '@', - titleKey: '@', - limit: '@', - size: '@', - hideLabel: '@' - }, - - template: '
' + - '
' + - '{{item.headline | unHtml }}' + - '{{item.text | unHtml:200 }}' + - '
' + - '+{{count - items.length}}' + - '
', - - link: function (scope, element) - { - var maxLines = { - medium: 3, - large: 6 - }; - - scope.size = maxLines[scope.size] ? scope.size : 'medium'; - var limit = +scope.limit || 3; - scope.items = []; - - if (!scope.model) - { - return; - } - - // hide label if requested - if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') - { - element.closest('.unesting-item-header').addClass('no-label'); - } - - if (!_.isArray(scope.model)) - { - console.error("The model has to be an array"); - return; - } - - if (_.isObject(scope.model[0]) && !scope.key) - { - console.error("You need the property for nested elements"); - return; - } - - var items = []; - _.each(scope.model, function (item) - { - var max = maxLines[scope.size]; - - if (item) - { - if (typeof item === 'string') - { - items.push({ - maxLines: max, - text: item - }); - } - - var headline = item[scope.titleKey]; - - items.push({ - maxLines: headline ? max - 1 : max, - headline: headline, - text: item[scope.key] - }); - } - }); - - scope.items = items.slice(0, limit); - scope.count = items.length; - } - }; -}); - - - -angular.module('umbraco.directives').directive('unFigure', function ($filter) -{ - return { - restrict: 'E', - scope: { - image: '=', - text: '=', - headline: '=', - size: '@', - maxLines: '@', - hideLabel: '@' - }, - - template: '
' + - '' + - '
' + - '{{headline | unHtml }}' + - '{{text | unHtml:800 }}' + - '
' + - '
', - - link: function (scope, element) - { - scope.hasContent = !!(scope.text || scope.headline || scope.image); - - // hide label if requested - if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') - { - element.closest('.unesting-item-header').toggleClass('no-label', scope.hasContent); - } - - // calculate max lines - scope.maxLines = +scope.maxLines || 3; - if (scope.headline) - { - scope.maxLines -= 1; - } - } - }; -}); - - - -angular.module('umbraco.directives').directive('unConfig', function ($filter) -{ - return { - restrict: 'E', - scope: { - hideLabel: '@', - asColumns: '@' - }, - - template: '', - - link: function (scope, element) - { - if (scope.hideLabel === true || scope.hideLabel === 'true' || scope.hideLabel === '1') - { - element.closest('.unesting-item-header').addClass('no-label'); - } - if (scope.asColumns === true || scope.asColumns === 'true' || scope.asColumns === '1') - { - element.closest('.unesting-item-header').addClass('as-columns'); - } - } - }; -}); - - - -angular.module("umbraco.directives").directive('uNestingContentEditor', [ - - function () - { - var link = function ($scope) - { - // Clone the model because some property editors - // do weird things like updating and config values - // so we want to ensure we start from a fresh every - // time, we'll just sync the value back when we need to - $scope.model = angular.copy($scope.ngModel); - $scope.nodeContext = $scope.model; - - // Find the selected tab - var selectedTab = $scope.model.variants[0].tabs[0]; - - if ($scope.tabAlias) - { - angular.forEach($scope.model.variants[0].tabs, function (tab) - { - if (tab.alias.toLowerCase() === $scope.tabAlias.toLowerCase()) - { - selectedTab = tab; - return; - } - }); - } - - $scope.tab = selectedTab; - - // get content and settings properties - $scope.contentProperties = _.filter($scope.tab.properties, function (prop) - { - return prop.propertyAlias.indexOf("uNesting") !== 0; - }); - $scope.settingsProperties = _.filter($scope.tab.properties, function (prop) - { - return prop.propertyAlias.indexOf("uNesting") === 0; - }); - $scope.hasSettings = $scope.settingsProperties.length > 0; - - // Listen for sync request - var unsubscribe = $scope.$on("ncSyncVal", function (ev, args) - { - if (args.key === $scope.model.key) - { - - // Tell inner controls we are submitting - $scope.$broadcast("formSubmitting", { scope: $scope }); - - // Sync the values back - angular.forEach($scope.ngModel.variants[0].tabs, function (tab) - { - if (tab.alias.toLowerCase() === selectedTab.alias.toLowerCase()) - { - - var localPropsMap = selectedTab.properties.reduce(function (map, obj) - { - map[obj.alias] = obj; - return map; - }, {}); - - angular.forEach(tab.properties, function (prop) - { - if (localPropsMap.hasOwnProperty(prop.alias)) - { - prop.value = localPropsMap[prop.alias].value; - } - }); - - } - }); - } - }); - - $scope.$on('$destroy', function () - { - unsubscribe(); - }); - }; - - return { - restrict: "E", - replace: true, - templateUrl: Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/views/propertyeditors/nestedcontent/nestedcontent.editor.html", - scope: { - ngModel: '=', - tabAlias: '=' - }, - link: link - }; - - } -]); - -//angular.module("umbraco.directives").directive('nestedContentSubmitWatcher', function () { -// var link = function (scope) { -// // call the load callback on scope to obtain the ID of this submit watcher -// var id = scope.loadCallback(); -// scope.$on("formSubmitting", function (ev, args) { -// // on the "formSubmitting" event, call the submit callback on scope to notify the nestedContent controller to do it's magic -// if (id === scope.activeSubmitWatcher) { -// scope.submitCallback(); -// } -// }); -// } - -// return { -// restrict: "E", -// replace: true, -// template: "", -// scope: { -// loadCallback: '=', -// submitCallback: '=', -// activeSubmitWatcher: '=' -// }, -// link: link -// } -//}); - - - - angular.module('umbraco').filter('unHtml', function () { return function (html, maxLength) @@ -555,4 +43,635 @@ angular.module('umbraco').filter('unHtml', function () return stripped.length > maxLength ? (stripped.substring(0, maxLength) + '...') : stripped; }; -}); \ No newline at end of file +}); + + +angular.module("umbraco").controller("brothers.uNesting.DocTypePickerController", function ($scope, $controller) +{ + angular.extend(this, $controller('Umbraco.PropertyEditors.NestedContent.DocTypePickerController', { $scope: $scope })); + + $scope.aceOptions = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + autoFocus: false, + advanced: { + fontSize: "14px", + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false, + wrap: true + } + }; +}); + +angular.module("umbraco").controller("brothers.uNesting.PropertyEditorController", [ + "$scope", + "$interpolate", + "$filter", + "$timeout", + "$http", + "contentResource", + "localizationService", + "iconHelper", + "clipboardService", + "eventsService", + "overlayService", + "umbRequestHelper", + + function ($scope, $interpolate, $filter, $timeout, $http, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, umbRequestHelper) + { + var contentTypeAliases = []; + _.each($scope.model.config.contentTypes, function (contentType) + { + contentTypeAliases.push(contentType.ncAlias); + }); + + _.each($scope.model.config.contentTypes, function (contentType) + { + contentType.nameExp = contentType.nameTemplate ? $interpolate(contentType.nameTemplate) : undefined; + }); + + $scope.nodes = []; + $scope.currentNode = undefined; + $scope.realCurrentNode = undefined; + $scope.scaffolds = undefined; + $scope.sorting = false; + $scope.inited = false; + + $scope.minItems = $scope.model.config.minItems || 0; + $scope.maxItems = $scope.model.config.maxItems || 0; + + if ($scope.maxItems === 0) + $scope.maxItems = 1000; + + $scope.singleMode = $scope.minItems === 1 && $scope.maxItems === 1; + $scope.showIcons = Object.toBoolean($scope.model.config.showIcons); + $scope.wideMode = Object.toBoolean($scope.model.config.hideLabel); + $scope.hasContentTypes = $scope.model.config.contentTypes.length > 0; + + $scope.labels = {}; + localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) + { + $scope.labels.grid_addElement = data[0]; + $scope.labels.content_createEmpty = data[1]; + }); + + // helper to force the current form into the dirty state + $scope.setDirty = function () + { + if ($scope.propertyForm) + { + $scope.propertyForm.$setDirty(); + } + }; + + $scope.addNode = function (alias) + { + var scaffold = $scope.getScaffold(alias); + + var newNode = createNode(scaffold, null); + + $scope.currentNode = newNode; + $scope.setDirty(); + }; + + $scope.openNodeTypePicker = function ($event) + { + if ($scope.nodes.length >= $scope.maxItems) + { + return; + } + + $scope.overlayMenu = { + show: false, + style: {}, + filter: $scope.scaffolds.length > 12 ? true : false, + orderBy: "$index", + view: "itempicker", + event: $event, + clickPasteItem: function (item) + { + $scope.pasteFromClipboard(item.data); + $scope.overlayMenu.show = false; + $scope.overlayMenu = null; + }, + submit: function (model) + { + if (model && model.selectedItem) + { + $scope.addNode(model.selectedItem.alias); + } + $scope.overlayMenu.show = false; + $scope.overlayMenu = null; + }, + close: function () + { + $scope.overlayMenu.show = false; + $scope.overlayMenu = null; + } + }; + + // this could be used for future limiting on node types + $scope.overlayMenu.availableItems = []; + _.each($scope.scaffolds, function (scaffold) + { + $scope.overlayMenu.availableItems.push({ + alias: scaffold.contentTypeAlias, + name: scaffold.contentTypeName, + icon: iconHelper.convertFromLegacyIcon(scaffold.icon) + }); + }); + + if ($scope.overlayMenu.availableItems.length === 0) + { + return; + } + + $scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small"; + + $scope.overlayMenu.pasteItems = []; + var availableNodesForPaste = clipboardService.retriveDataOfType("elementType", contentTypeAliases); + _.each(availableNodesForPaste, function (node) + { + $scope.overlayMenu.pasteItems.push({ + alias: node.contentTypeAlias, + name: node.name, //contentTypeName + data: node, + icon: iconHelper.convertFromLegacyIcon(node.icon) + }); + }); + + $scope.overlayMenu.title = $scope.overlayMenu.pasteItems.length > 0 ? $scope.labels.grid_addElement : $scope.labels.content_createEmpty; + + $scope.overlayMenu.clickClearPaste = function ($event) + { + $event.stopPropagation(); + $event.preventDefault(); + clipboardService.clearEntriesOfType("elementType", contentTypeAliases); + $scope.overlayMenu.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. + }; + + if ($scope.overlayMenu.availableItems.length === 1 && $scope.overlayMenu.pasteItems.length === 0) + { + // only one scaffold type - no need to display the picker + $scope.addNode($scope.scaffolds[0].contentTypeAlias); + return; + } + + $scope.overlayMenu.show = true; + }; + + $scope.editNode = function (idx) + { + if ($scope.currentNode && $scope.currentNode.key === $scope.nodes[idx].key) + { + $scope.currentNode = undefined; + } else + { + $scope.currentNode = $scope.nodes[idx]; + } + }; + + $scope.deleteNode = function (idx) + { + if ($scope.nodes.length > $scope.model.config.minItems) + { + $scope.nodes.splice(idx, 1); + $scope.setDirty(); + updateModel(); + } + }; + $scope.requestDeleteNode = function (idx) + { + if ($scope.model.config.confirmDeletes === true) + { + localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) + { + const overlay = { + title: data[1], + content: data[0], + closeButtonLabel: data[2], + submitButtonLabel: data[3], + submitButtonStyle: "danger", + close: function () + { + overlayService.close(); + }, + submit: function () + { + $scope.deleteNode(idx); + overlayService.close(); + } + }; + + overlayService.open(overlay); + }); + } else + { + $scope.deleteNode(idx); + } + }; + + $scope.getName = function (idx) + { + + var name = ""; + + if ($scope.model.value[idx]) + { + + var contentType = $scope.getContentTypeConfig($scope.model.value[idx].ncContentTypeAlias); + + if (contentType != null) + { + // first try getting a name using the configured label template + if (contentType.nameExp) + { + // Run the expression against the stored dictionary value, NOT the node object + var item = $scope.model.value[idx]; + + // Add a temporary index property + item["$index"] = (idx + 1); + + var newName = contentType.nameExp(item); + if (newName && (newName = $.trim(newName))) + { + name = newName; + } + + // Delete the index property as we don't want to persist it + delete item["$index"]; + } + + // if we still do not have a name and we have multiple content types to choose from, use the content type name (same as is shown in the content type picker) + if (!name && $scope.scaffolds.length > 1) + { + var scaffold = $scope.getScaffold(contentType.ncAlias); + if (scaffold) + { + name = scaffold.contentTypeName; + } + } + } + + } + + if (!name) + { + name = "Item " + (idx + 1); + } + + // Update the nodes actual name value + if ($scope.nodes[idx].name !== name) + { + $scope.nodes[idx].name = name; + } + + return name; + }; + + $scope.getIcon = function (idx) + { + var scaffold = $scope.getScaffold($scope.model.value[idx].ncContentTypeAlias); + return scaffold && scaffold.icon ? iconHelper.convertFromLegacyIcon(scaffold.icon) : "icon-folder"; + } + + $scope.sortableOptions = { + axis: "y", + cursor: "move", + handle: '.umb-nested-content__header-bar', + distance: 10, + opacity: 0.7, + tolerance: "pointer", + scroll: true, + start: function (ev, ui) + { + updateModel(); + // Yea, yea, we shouldn't modify the dom, sue me + $("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () + { + tinymce.execCommand("mceRemoveEditor", false, $(this).attr("id")); + $(this).css("visibility", "hidden"); + }); + $scope.$apply(function () + { + $scope.sorting = true; + }); + }, + update: function (ev, ui) + { + $scope.setDirty(); + }, + stop: function (ev, ui) + { + $("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () + { + tinymce.execCommand("mceAddEditor", true, $(this).attr("id")); + $(this).css("visibility", "visible"); + }); + $scope.$apply(function () + { + $scope.sorting = false; + updateModel(); + }); + } + }; + + $scope.getScaffold = function (alias) + { + return _.find($scope.scaffolds, function (scaffold) + { + return scaffold.contentTypeAlias === alias; + }); + } + + $scope.getContentTypeConfig = function (alias) + { + return _.find($scope.model.config.contentTypes, function (contentType) + { + return contentType.ncAlias === alias; + }); + } + + $scope.showCopy = clipboardService.isSupported(); + + $scope.showPaste = false; + + $scope.clickCopy = function ($event, node) + { + + syncCurrentNode(); + + clipboardService.copy("elementType", node.contentTypeAlias, node); + $event.stopPropagation(); + } + + $scope.pasteFromClipboard = function (newNode) + { + + if (newNode === undefined) + { + return; + } + + // generate a new key. + newNode.key = String.CreateGuid(); + + $scope.nodes.push(newNode); + $scope.setDirty(); + //updateModel();// done by setting current node... + + $scope.currentNode = newNode; + } + + function checkAbilityToPasteContent() + { + $scope.showPaste = clipboardService.hasEntriesOfType("elementType", contentTypeAliases); + } + + eventsService.on("clipboardService.storageUpdate", checkAbilityToPasteContent); + + var notSupported = [ + "Umbraco.Tags", + "Umbraco.UploadField", + "Umbraco.ImageCropper" + ]; + + // Initialize + var scaffoldsLoaded = 0; + $scope.scaffolds = []; + _.each($scope.model.config.contentTypes, function (contentType) + { + contentResource.getScaffold(-20, contentType.ncAlias).then(function (scaffold) + { + // make sure it's an element type before allowing the user to create new ones + if (scaffold.isElement) + { + // remove all tabs except the specified tab + //var tabs = scaffold.variants[0].tabs; + //var tab = _.find(tabs, function (tab) + //{ + // return tab.id !== 0 && (tab.alias.toLowerCase() === contentType.ncTabAlias.toLowerCase() || contentType.ncTabAlias === ""); + //}); + //scaffold.variants[0].tabs = []; + //if (tab) + //{ + // scaffold.variants[0].tabs.push(tab); + + // angular.forEach(tab.properties, + // function (property) + // { + // if (_.find(notSupported, function (x) { return x === property.editor; })) + // { + // property.notSupported = true; + // // TODO: Not supported message to be replaced with 'content_nestedContentEditorNotSupported' dictionary key. Currently not possible due to async/timing quirk. + // property.notSupportedMessage = "Property " + property.label + " uses editor " + property.editor + " which is not supported by Nested Content."; + // } + // }); + //} + + // Store the scaffold object + $scope.scaffolds.push(scaffold); + } + + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }, function (error) + { + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }); + }); + + var initIfAllScaffoldsHaveLoaded = function () + { + // Initialize when all scaffolds have loaded + if ($scope.model.config.contentTypes.length === scaffoldsLoaded) + { + // Because we're loading the scaffolds async one at a time, we need to + // sort them explicitly according to the sort order defined by the data type. + contentTypeAliases = []; + _.each($scope.model.config.contentTypes, function (contentType) + { + contentTypeAliases.push(contentType.ncAlias); + }); + $scope.scaffolds = $filter("orderBy")($scope.scaffolds, function (s) + { + return contentTypeAliases.indexOf(s.contentTypeAlias); + }); + + // Convert stored nodes + if ($scope.model.value) + { + for (var i = 0; i < $scope.model.value.length; i++) + { + var item = $scope.model.value[i]; + var scaffold = $scope.getScaffold(item.ncContentTypeAlias); + if (scaffold == null) + { + // No such scaffold - the content type might have been deleted. We need to skip it. + continue; + } + createNode(scaffold, item); + } + } + + // Enforce min items + if ($scope.nodes.length < $scope.model.config.minItems) + { + for (var i = $scope.nodes.length; i < $scope.model.config.minItems; i++) + { + $scope.addNode($scope.scaffolds[0].contentTypeAlias); + } + } + + // If there is only one item, set it as current node + if ($scope.singleMode || ($scope.nodes.length === 1 && $scope.maxItems === 1)) + { + $scope.currentNode = $scope.nodes[0]; + } + + $scope.inited = true; + + checkAbilityToPasteContent(); + } + } + + function createNode(scaffold, fromNcEntry) + { + var node = angular.copy(scaffold); + + node.key = fromNcEntry && fromNcEntry.key ? fromNcEntry.key : String.CreateGuid(); + + var variant = node.variants[0]; + + for (var t = 0; t < variant.tabs.length; t++) + { + var tab = variant.tabs[t]; + + for (var p = 0; p < tab.properties.length; p++) + { + var prop = tab.properties[p]; + + prop.propertyAlias = prop.alias; + prop.alias = $scope.model.alias + "___" + prop.alias; + // Force validation to occur server side as this is the + // only way we can have consistency between mandatory and + // regex validation messages. Not ideal, but it works. + prop.validation = { + mandatory: false, + pattern: "" + }; + + if (fromNcEntry && fromNcEntry[prop.propertyAlias]) + { + prop.value = fromNcEntry[prop.propertyAlias]; + } + } + } + + $scope.nodes.push(node); + + return node; + } + + function convertNodeIntoNCEntry(node) + { + var obj = { + key: node.key, + name: node.name, + ncContentTypeAlias: node.contentTypeAlias + }; + for (var t = 0; t < node.variants[0].tabs.length; t++) + { + var tab = node.variants[0].tabs[t]; + for (var p = 0; p < tab.properties.length; p++) + { + var prop = tab.properties[p]; + if (typeof prop.value !== "function") + { + obj[prop.propertyAlias] = prop.value; + } + } + } + return obj; + } + + function syncCurrentNode() + { + if ($scope.realCurrentNode) + { + $scope.$broadcast("ncSyncVal", { key: $scope.realCurrentNode.key }); + } + } + + function updateModel() + { + syncCurrentNode(); + + if ($scope.inited) + { + var newValues = []; + for (var i = 0; i < $scope.nodes.length; i++) + { + newValues.push(convertNodeIntoNCEntry($scope.nodes[i])); + } + $scope.model.value = newValues; + } + } + + $scope.$watch("currentNode", function (newVal) + { + updateModel(); + $scope.realCurrentNode = newVal; + }); + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) + { + updateModel(); + }); + + $scope.$on("$destroy", function () + { + unsubscribe(); + }); + + + + + $scope.clickHide = function ($event, node, alias) + { + $event.preventDefault(); + $event.stopPropagation(); + + var tab = _.find(node.variants[0].tabs, function (tab) + { + var alias = tab.alias.toLowerCase(); + return tab.id !== 0 && alias !== 'unestingsettings' && (!$scope.tabAlias || alias === $scope.tabAlias.toLowerCase()); + }); + + var property = _.find(tab.properties, function (prop) + { + return tab.id !== 0 && prop.propertyAlias.toLowerCase() === 'unestinghide'; + }); + + if (property) + { + property.value = property.value === '1' ? '0' : '1'; + } + + updateModel(); + }; + + $scope.canHide = function (item) + { + return typeof item['uNestingHide'] !== 'undefined'; + }; + + $scope.isHidden = function (item) + { + return item['uNestingHide'] === '1'; + }; + } +]); \ No newline at end of file diff --git a/UmbracoPackages/MediaExtendedController.cs b/UmbracoPackages/MediaExtendedController.cs deleted file mode 100644 index 2dfdd53..0000000 --- a/UmbracoPackages/MediaExtendedController.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Linq; -using System.Web.Http; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Editors; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi.Filters; -using Constants = Umbraco.Core.Constants; - -namespace UmbracoPackages -{ - /// - /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting - /// access to ALL of the methods on this controller will need access to the media application. - /// - [PluginController("UmbracoApi")] - [UmbracoApplicationAuthorize(Constants.Applications.Media)] - public class MediaExtendedController : ContentControllerBase - { - public MediaExtendedController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } - - - /// - /// Return media for the specified UDIs - /// - /// - /// - public Dictionary GetByUdis([FromUri]Udi[] ids) - { - var foundMedia = Services.MediaService.GetByIds(ids.Select(x => (x as GuidUdi).Guid)); - return foundMedia.ToDictionary(media => (Udi)media.GetUdi(), media => - { - string json = media.GetValue("umbracoFile"); - return JsonConvert.DeserializeObject(json).Src; - }); - } - - - class SrcData - { - public string Src { get; set; } - } - } -} diff --git a/UmbracoPackages/UmbracoPackages.csproj b/UmbracoPackages/UmbracoPackages.csproj index 9a1651b..551ff5f 100644 --- a/UmbracoPackages/UmbracoPackages.csproj +++ b/UmbracoPackages/UmbracoPackages.csproj @@ -395,6 +395,7 @@ + @@ -471,7 +472,7 @@ - + diff --git a/UmbracoPackages/UnestingController.cs b/UmbracoPackages/UnestingController.cs new file mode 100644 index 0000000..047b929 --- /dev/null +++ b/UmbracoPackages/UnestingController.cs @@ -0,0 +1,111 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web.Http; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Services; +using Umbraco.Web; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; + +namespace UmbracoPackages +{ + [PluginController("UmbracoApi")] + [UmbracoApplicationAuthorize(Constants.Applications.Content)] + public class UnestingController : ContentControllerBase + { + private IContentTypeCommonRepository ContentTypeRepository { get; set; } + + + public UnestingController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IContentTypeCommonRepository contentTypeRepository) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + ContentTypeRepository = contentTypeRepository; + } + + + /// + /// Return media for the specified UDIs + /// + /// + /// + public Dictionary GetMediaByUdis([FromUri]Udi[] ids) + { + var foundMedia = Services.MediaService.GetByIds(ids.Select(x => (x as GuidUdi).Guid)); + return foundMedia.ToDictionary(media => (Udi)media.GetUdi(), media => + { + string json = media.GetValue("umbracoFile"); + return JsonConvert.DeserializeObject(json).Src; + }); + } + + + /// + /// Gets an empty content item for the + /// + /// + /// + public Dictionary GetScaffolds([FromUri]string[] a) + { + int parentId = -20; + // string contentTypeAlias, int parentId + + var contentTypes = ContentTypeRepository.GetAllTypes().Where(x => a.Contains(x.Alias, StringComparer.InvariantCultureIgnoreCase)); + + if (!contentTypes.Any()) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + return contentTypes.Select(contentType => + { + var emptyContent = Services.ContentService.Create("", parentId, contentType.Alias, Security.GetUserId().ResultOr(0)); + var mapped = MapToDisplay(emptyContent); + + // TODO we need access to the translation service + // translate the content type name if applicable + //mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(mapped.ContentTypeName); + // if your user type doesn't have access to the Settings section it would not get this property mapped + if (mapped.DocumentType != null) + { + //mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name); + } + + //remove the listview app if it exists + mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); + + return mapped; + }).ToDictionary(x => x.ContentTypeAlias, x => x); + } + + + /// + /// Used to map an instance to a and ensuring a language is present if required + /// + /// + /// + private ContentItemDisplay MapToDisplay(IContent content) + { + var display = Mapper.Map(content); + display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false; + return display; + } + + + class SrcData + { + public string Src { get; set; } + } + } +} diff --git a/UmbracoPackages/Views/Partials/SectionHeader.cshtml b/UmbracoPackages/Views/Partials/SectionHeader.cshtml index ddc0cdd..b4992f5 100644 --- a/UmbracoPackages/Views/Partials/SectionHeader.cshtml +++ b/UmbracoPackages/Views/Partials/SectionHeader.cshtml @@ -1,6 +1,6 @@ @inherits Umbraco.Web.Mvc.UmbracoViewPage
-

@Model.GetProperty("PageTitle").GetValue()

+ @*

@Model.GetProperty("PageTitle").GetValue()

*@
\ No newline at end of file