Merge pull request #831 from umbraco/temp-U4-7178-media-library

Temp u4 7178 media library
This commit is contained in:
Shannon Deminick
2015-10-27 11:41:10 +01:00
64 changed files with 2125 additions and 554 deletions

View File

@@ -117,8 +117,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -42, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-42", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = new Guid("bb5f57c9-ce2b-4bb9-b697-4caca783a805"), Text = "Radiobox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -38, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-38", SortOrder = 2, UniqueId = new Guid("fd9f1447-6c61-4a7c-9595-5aa39147d318"), Text = "Folder Browser", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = new Guid("0225af17-b302-49cb-9176-b9f35cab9c17"), Text = "Approved Color", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -36, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-36", SortOrder = 2, UniqueId = new Guid("e4d66c0f-b935-4200-81f0-025f7256b89a"), Text = "Date Picker with time", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.DefaultContentListViewDataTypeId, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-95", SortOrder = 2, UniqueId = new Guid("C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
@@ -196,7 +195,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = -38, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = Constants.System.DefaultMediaListViewDataTypeId, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
//membership property types
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null });
@@ -231,8 +230,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 6, DataTypeId = -90, PropertyEditorAlias = Constants.PropertyEditors.UploadFieldAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, PropertyEditorAlias = Constants.PropertyEditors.NoEditAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, PropertyEditorAlias = Constants.PropertyEditors.DateTimeAlias, DbType = "Date" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 10, DataTypeId = -38, PropertyEditorAlias = Constants.PropertyEditors.FolderBrowserAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 11, DataTypeId = -39, PropertyEditorAlias = Constants.PropertyEditors.DropDownListMultipleAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
@@ -253,7 +251,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 19, DataTypeId = 1038, PropertyEditorAlias = Constants.PropertyEditors.MarkdownEditorAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 20, DataTypeId = 1039, PropertyEditorAlias = Constants.PropertyEditors.UltimatePickerAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 23, DataTypeId = 1042, PropertyEditorAlias = Constants.PropertyEditors.MacroContainerAlias, DbType = "Ntext" });
}
private void CreateCmsDataTypePreValuesData()
@@ -269,6 +266,17 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "Name" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -3, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "asc" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]" });
//layouts for the list view
var cardLayout = "{\"name\": \"Grid\",\"path\": \"views/propertyeditors/listview/layouts/grid/grid.html\", \"icon\": \"icon-thumbnails-small\", \"isSystem\": 1, \"selected\": true}";
var listLayout = "{\"name\": \"List\",\"path\": \"views/propertyeditors/listview/layouts/list/list.html\",\"icon\": \"icon-list\", \"isSystem\": 1,\"selected\": true}";
//defaults for the media list
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -5, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "100" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "VersionDate" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"sort\",\"isSystem\":1, \"header\": \"Sort order\"},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
}
private void CreateUmbracoRelationTypeData()

View File

@@ -394,12 +394,8 @@ namespace Umbraco.Core.Services
using (var repository = RepositoryFactory.CreateMediaRepository(UowProvider.GetUnitOfWork()))
{
var query = Query<IMedia>.Builder;
//if the id is -1, then just get all
if (id != -1)
{
query.Where(x => x.ParentId == id);
}
query.Where(x => x.ParentId == id);
long total;
var medias = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out total, orderBy, orderDirection, filter);
@@ -427,11 +423,8 @@ namespace Umbraco.Core.Services
using (var repository = RepositoryFactory.CreateMediaRepository(UowProvider.GetUnitOfWork()))
{
var query = Query<IMedia>.Builder;
//if the id is -1, then just get all
if (id != -1)
{
query.Where(x => x.ParentId == id);
}
query.Where(x => x.ParentId == id);
var medias = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, filter);
return medias;

View File

@@ -51,7 +51,9 @@
state: "=?",
shortcut: "@?",
label: "@?",
labelKey: "@?"
labelKey: "@?",
icon: "@?",
disabled: "="
}
};

View File

@@ -0,0 +1,18 @@
(function() {
'use strict';
function EditorSubHeaderDirective() {
var directive = {
transclude: true,
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html'
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorSubHeader', EditorSubHeaderDirective);
})();

View File

@@ -0,0 +1,18 @@
(function() {
'use strict';
function EditorSubHeaderContentLeftDirective() {
var directive = {
transclude: true,
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-left.html'
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorSubHeaderContentLeft', EditorSubHeaderContentLeftDirective);
})();

View File

@@ -0,0 +1,18 @@
(function() {
'use strict';
function EditorSubHeaderContentRightDirective() {
var directive = {
transclude: true,
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-right.html'
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorSubHeaderContentRight', EditorSubHeaderContentRightDirective);
})();

View File

@@ -0,0 +1,18 @@
(function() {
'use strict';
function EditorSubHeaderSectionDirective() {
var directive = {
transclude: true,
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-section.html'
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorSubHeaderSection', EditorSubHeaderSectionDirective);
})();

View File

@@ -8,7 +8,8 @@
replace: true,
templateUrl: 'views/components/umb-breadcrumbs.html',
scope: {
ancestors: "="
ancestors: "=",
entityType: "@"
}
};

View File

@@ -0,0 +1,41 @@
(function() {
'use strict';
function ContentGridDirective() {
function link(scope, el, attr, ctrl) {
scope.clickItem = function(item) {
if(scope.onClick) {
scope.onClick(item);
}
};
scope.selectItem = function(item, $event, $index) {
if(scope.onSelect) {
scope.onSelect(item, $event, $index);
$event.stopPropagation();
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-content-grid.html',
scope: {
content: '=',
contentProperties: "=",
onSelect: '=',
onClick: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbContentGrid', ContentGridDirective);
})();

View File

@@ -0,0 +1,40 @@
(function() {
'use strict';
function FolderGridDirective() {
function link(scope, el, attr, ctrl) {
scope.clickFolder = function(folder) {
if(scope.onClick) {
scope.onClick(folder);
}
};
scope.selectFolder = function(folder, $event, $index) {
if(scope.onSelect) {
scope.onSelect(folder, $event, $index);
}
$event.stopPropagation();
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-folder-grid.html',
scope: {
folders: '=',
onSelect: '=',
onClick: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbFolderGrid', FolderGridDirective);
})();

View File

@@ -0,0 +1,37 @@
(function() {
'use strict';
function ListViewLayoutDirective() {
function link(scope, el, attr, ctrl) {
scope.getContent = function(contentId) {
if(scope.onGetContent) {
scope.onGetContent(contentId);
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-list-view-layout.html',
scope: {
contentId: '=',
folders: '=',
items: '=',
selection: '=',
options: '=',
entityType: '@',
onGetContent: '='
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective);
})();

View File

@@ -5,29 +5,21 @@
function link(scope, el, attr, ctrl) {
scope.folders = [];
scope.mediaItems = [];
var itemMaxHeight = 200;
var itemDefaultHeight = 200;
var itemDefaultWidth = 200;
var itemMaxWidth = 300;
var itemMaxHeight = 300;
function activate() {
scope.folders = [];
scope.mediaItems = [];
for (var i = 0; scope.items.length > i; i++) {
var item = scope.items[i];
setItemData(item);
setOriginalSize(item, itemMaxHeight);
seperateFolderAndMediaItems(item);
}
if(scope.mediaItems.length > 0) {
setFlexValues(scope.mediaItems);
if(scope.items.length > 0) {
setFlexValues(scope.items);
}
}
@@ -35,6 +27,7 @@
function setItemData(item) {
item.isFolder = !mediaHelper.hasFilePropertyType(item);
item.hidden = item.isFolder;
if(!item.isFolder){
item.thumbnail = mediaHelper.resolveFile(item, true);
@@ -45,39 +38,40 @@
function setOriginalSize(item, maxHeight) {
//set to a square by default
item.originalWidth = maxHeight;
item.originalHeight = maxHeight;
item.width = itemDefaultWidth;
item.height = itemDefaultHeight;
item.aspectRatio = 1;
var widthProp = _.find(item.properties, function(v) { return (v.alias === "umbracoWidth"); });
if (widthProp && widthProp.value) {
item.originalWidth = parseInt(widthProp.value, 10);
if (isNaN(item.originalWidth)) {
item.originalWidth = maxHeight;
item.width = parseInt(widthProp.value, 10);
if (isNaN(item.width)) {
item.width = itemDefaultWidth;
}
}
var heightProp = _.find(item.properties, function(v) { return (v.alias === "umbracoHeight"); });
if (heightProp && heightProp.value) {
item.originalHeight = parseInt(heightProp.value, 10);
if (isNaN(item.originalHeight)) {
item.originalHeight = maxHeight;
item.height = parseInt(heightProp.value, 10);
if (isNaN(item.height)) {
item.height = itemDefaultWidth;
}
}
item.aspectRatio = item.originalWidth / item.originalHeight;
item.aspectRatio = item.width / item.height;
}
// set max width and height
if(item.width > itemMaxWidth) {
item.width = itemMaxWidth;
item.height = itemMaxWidth / item.aspectRatio;
}
function seperateFolderAndMediaItems(item) {
if(item.isFolder){
scope.folders.push(item);
} else {
scope.mediaItems.push(item);
}
if(item.height > itemMaxHeight) {
item.height = itemMaxHeight;
item.width = itemMaxHeight * item.aspectRatio;
}
}
@@ -88,13 +82,13 @@
var widestImageAspectRatio = null;
// sort array after image width with the widest image first
flexSortArray = $filter('orderBy')(flexSortArray, 'originalWidth', true);
flexSortArray = $filter('orderBy')(flexSortArray, 'width', true);
// find widest image aspect ratio
widestImageAspectRatio = flexSortArray[0].aspectRatio;
// find smallest image width
smallestImageWidth = flexSortArray[flexSortArray.length - 1].originalWidth;
smallestImageWidth = flexSortArray[flexSortArray.length - 1].width;
for (var i = 0; flexSortArray.length > i; i++) {
@@ -109,7 +103,7 @@
var flexStyle = {
"flex": flex + " 1 " + imageMinWidth + "px",
"max-width": mediaItem.originalWidth + "px"
"max-width": mediaItem.width + "px"
};
mediaItem.flexStyle = flexStyle;
@@ -118,12 +112,29 @@
}
scope.toggleSelectItem = function(item) {
item.selected = !item.selected;
scope.selectItem = function(item, $event, $index) {
if(scope.onSelect) {
scope.onSelect(item, $event, $index);
$event.stopPropagation();
}
};
scope.clickItem = function(item) {
if(scope.onClick) {
scope.onClick(item);
}
};
scope.hoverItemDetails = function(item, $event, hover) {
if(scope.onDetailsHover) {
scope.onDetailsHover(item, $event, hover);
}
};
var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue){
activate();
if(angular.isArray(newValue)) {
activate();
}
});
scope.$on('$destroy', function(){
@@ -137,7 +148,10 @@
replace: true,
templateUrl: 'views/components/umb-media-grid.html',
scope: {
items: '='
items: '=',
onDetailsHover: "=",
onSelect: '=',
onClick: '='
},
link: link
};

View File

@@ -0,0 +1,105 @@
(function() {
'use strict';
function PaginationDirective() {
function link(scope, el, attr, ctrl) {
function activate() {
scope.pagination = [];
var i = 0;
if (scope.totalPages <= 10) {
for (i = 0; i < scope.totalPages; i++) {
scope.pagination.push({
val: (i + 1),
isActive: scope.pageNumber === (i + 1)
});
}
}
else {
//if there is more than 10 pages, we need to do some fancy bits
//get the max index to start
var maxIndex = scope.totalPages - 10;
//set the start, but it can't be below zero
var start = Math.max(scope.pageNumber - 5, 0);
//ensure that it's not too far either
start = Math.min(maxIndex, start);
for (i = start; i < (10 + start) ; i++) {
scope.pagination.push({
val: (i + 1),
isActive: scope.pageNumber === (i + 1)
});
}
//now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing
if (start > 0) {
scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false});
}
//same for the end
if (start < maxIndex) {
scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: scope.totalPages, isActive: false });
}
}
}
scope.next = function() {
if (scope.onNext && scope.pageNumber < scope.totalPages) {
scope.pageNumber++;
scope.onNext(scope.pageNumber);
}
};
scope.prev = function(pageNumber) {
if (scope.onPrev && scope.pageNumber > 1) {
scope.pageNumber--;
scope.onPrev(scope.pageNumber);
}
};
scope.goToPage = function(pageNumber) {
if(scope.onGoToPage) {
scope.pageNumber = pageNumber + 1;
scope.onGoToPage(scope.pageNumber);
}
};
var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
activate();
});
scope.$on('$destroy', function(){
unbindPageNumberWatcher();
});
activate();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-pagination.html',
scope: {
pageNumber: "=",
totalPages: "=",
onNext: "=",
onPrev: "=",
onGoToPage: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbPagination', PaginationDirective);
})();

View File

@@ -0,0 +1,116 @@
(function() {
'use strict';
function StickyBarDirective($rootScope) {
function link(scope, el, attr, ctrl) {
var bar = $(el);
var scrollableContainer = null;
var clonedBar = null;
var cloneIsMade = false;
var barTop = bar.context.offsetTop;
function activate() {
if (attr.scrollableContainer) {
scrollableContainer = $(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
$(window).on('resize.umbStickyBar', determineVisibility);
scope.$on('$destroy', function() {
scrollableContainer.off('.umbStickyBar');
$(window).off('.umbStickyBar');
});
}
function determineVisibility() {
var scrollTop = scrollableContainer.scrollTop();
if (scrollTop > barTop) {
if (!cloneIsMade) {
createClone();
clonedBar.css({
'visibility': 'visible'
});
} else {
calculateSize();
}
} else {
if (cloneIsMade) {
//remove cloned element (switched places with original on creation)
bar.remove();
bar = clonedBar;
clonedBar = null;
bar.removeClass('-umb-sticky-bar');
bar.css({
position: 'relative',
'width': 'auto',
'height': 'auto',
'z-index': 'auto',
'visibility': 'visible'
});
cloneIsMade = false;
}
}
}
function calculateSize() {
clonedBar.css({
width: bar.outerWidth(),
height: bar.height()
});
}
function createClone() {
//switch place with cloned element, to keep binding intact
clonedBar = bar;
bar = clonedBar.clone();
clonedBar.after(bar);
clonedBar.addClass('-umb-sticky-bar');
clonedBar.css({
'position': 'fixed',
'z-index': 500,
'visibility': 'hidden'
});
cloneIsMade = true;
calculateSize();
}
activate();
}
var directive = {
restrict: 'A',
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective);
})();

View File

@@ -0,0 +1,82 @@
(function() {
'use strict';
function TooltipDirective($timeout) {
function link(scope, el, attr, ctrl) {
scope.tooltipStyles = {};
scope.tooltipStyles.left = 0;
scope.tooltipStyles.top = 0;
function activate() {
$timeout(function() {
setTooltipPosition(scope.event);
});
}
function setTooltipPosition(event) {
var viewportWidth = null;
var viewportHeight = null;
var elementHeight = null;
var elementWidth = null;
var position = {
right: "inherit",
left: "inherit",
top: "inherit",
bottom: "inherit"
};
// viewport size
viewportWidth = $(window).innerWidth();
viewportHeight = $(window).innerHeight();
// element size
elementHeight = el.context.clientHeight;
elementWidth = el.context.clientWidth;
position.left = event.pageX - (elementWidth / 2);
position.top = event.pageY;
// check to see if element is outside screen
// outside right
if (position.left + elementWidth > viewportWidth) {
position.right = 0;
position.left = "inherit";
}
// outside bottom
if (position.top + elementHeight > viewportHeight) {
position.bottom = 0;
position.top = "inherit";
}
scope.tooltipStyles = position;
}
activate();
}
var directive = {
restrict: 'E',
transclude: true,
replace: true,
templateUrl: 'views/components/umb-tooltip.html',
scope: {
event: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbTooltip', TooltipDirective);
})();

View File

@@ -10,7 +10,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onKeyup);
};
elm.on("keyup", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("keyup", f);} );
}
};
})
@@ -22,7 +22,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onKeydown);
};
elm.on("keydown", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("keydown", f);} );
}
};
})
@@ -34,7 +34,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onBlur);
};
elm.on("blur", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("blur", f);} );
}
};
})
@@ -46,7 +46,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onFocus);
};
elm.on("focus", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("focus", f);} );
}
};
})
@@ -58,7 +58,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onDragEnter);
};
elm.on("dragenter", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("dragenter", f);} );
}
};
})
@@ -91,7 +91,7 @@ angular.module('umbraco.directives')
};
elm.on("dragleave", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("dragleave", f);} );
};
})
@@ -102,7 +102,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onDragOver);
};
elm.on("dragover", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("dragover", f);} );
}
};
})
@@ -114,7 +114,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onDragStart);
};
elm.on("dragstart", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("dragstart", f);} );
}
};
})
@@ -126,7 +126,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onDragEnd);
};
elm.on("dragend", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("dragend", f);} );
}
};
})
@@ -138,7 +138,7 @@ angular.module('umbraco.directives')
scope.$apply(attrs.onDrop);
};
elm.on("drop", f);
scope.$on("$destroy", function(){ elm.off(f);} );
scope.$on("$destroy", function(){ elm.off("drop", f);} );
}
};
})
@@ -236,8 +236,8 @@ angular.module('umbraco.directives')
//unsub events
scope.$on("$destroy", function(){
element.off(leave_f);
element.off(enter_f);
element.off("mouseleave", leave_f);
element.off("mouseenter", enter_f);
});
}
};

View File

@@ -17,7 +17,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
*
* ##usage
* <pre>
* dataTypeResource.getPrevalyes("Umbraco.MediaPicker", 1234)
* dataTypeResource.getPreValues("Umbraco.MediaPicker", 1234)
* .then(function(prevalues) {
* alert('its gone!');
* });

View File

@@ -392,7 +392,45 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
}),
'Failed to add folder');
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getChildFolders
* @methodOf umbraco.resources.mediaResource
*
* @description
* Retrieves all media children with types used as folders.
* Uses the convention of looking for media items with mediaTypes ending in
* *Folder so will match "Folder", "bannerFolder", "secureFolder" etc,
*
* ##usage
* <pre>
* mediaResource.getChildFolders(1234)
* .then(function(data) {
* alert('folders');
* });
* </pre>
*
* @param {int} parentId Id of the media item to query for child folders
* @returns {Promise} resourcePromise object.
*
*/
getChildFolders: function(parentId){
if(!parentId){
parentId = -1;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildFolders",
[
{ id: parentId }
])),
'Failed to retrieve child folders for media item ' + parentId);
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#emptyRecycleBin

View File

@@ -0,0 +1,112 @@
(function() {
'use strict';
function listViewHelper() {
var firstSelectedIndex = 0;
function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
var start = 0;
var end = 0;
var item = null;
if ($event.shiftKey === true) {
if(selectedIndex > firstSelectedIndex) {
start = firstSelectedIndex;
end = selectedIndex;
for (; end >= start; start++) {
item = items[start];
selectItem(item, selection);
}
} else {
start = firstSelectedIndex;
end = selectedIndex;
for (; end <= start; start--) {
item = items[start];
selectItem(item, selection);
}
}
} else {
if(selectedItem.selected) {
deselectItem(selectedItem, selection);
} else {
selectItem(selectedItem, selection);
}
firstSelectedIndex = selectedIndex;
}
}
function selectItem(item, selection) {
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
isSelected = true;
}
}
if(!isSelected && !item.hidden) {
selection.push({id: item.id});
item.selected = true;
}
}
function deselectItem(item, selection) {
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
selection.splice(i, 1);
item.selected = false;
}
}
}
function clearSelection(items, folders, selection) {
var i = 0;
selection.length = 0;
if(angular.isArray(items)) {
for(i = 0; items.length > i; i++) {
var item = items[i];
item.selected = false;
}
}
if(angular.isArray(items)) {
for(i = 0; folders.length > i; i++) {
var folder = folders[i];
folder.selected = false;
}
}
}
var service = {
selectHandler: selectHandler,
selectItem: selectItem,
deselectItem: deselectItem,
clearSelection: clearSelection
};
return service;
}
angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
})();

View File

@@ -84,6 +84,7 @@
@import "components/umb-editor-navigation.less";
@import "components/umb-editor-sub-views.less";
@import "components/umb-editor-toolbar.less";
@import "components/editor/subheader/umb-editor-sub-header.less";
@import "components/umb-grid-selector.less";
@import "components/umb-child-selector.less";
@import "components/umb-group-builder.less";
@@ -96,7 +97,11 @@
@import "components/umb-load-indicator.less";
@import "components/umb-breadcrumbs.less";
@import "components/umb-media-grid.less";
@import "components/umb-folder-grid.less";
@import "components/umb-content-grid.less";
@import "components/umb-layout-selector.less";
@import "components/tooltip/umb-tooltip.less";
@import "components/tooltip/umb-tooltip-list.less";
@import "components/overlays/umb-overlay-backdrop.less";
@import "components/buttons/umb-button.less";

View File

@@ -0,0 +1,53 @@
.umb-editor-sub-header {
padding: 15px 0;
margin-bottom: 30px;
background: #ffffff;
display: flex;
justify-content: space-between;
margin-top: -30px;
position: relative;
}
.umb-editor-sub-header.-umb-sticky-bar {
box-shadow:
0 5px 0 rgba(0, 0, 0, 0.08),
0 1px 0 rgba(0, 0, 0, 0.16);
transition: box-shadow 1s;
}
.umb-editor-sub-header__content-left {
margin-right: auto;
}
.umb-editor-sub-header__content-right {
margin-left: auto;
}
.umb-editor-sub-header__content-left,
.umb-editor-sub-header__content-right {
display: flex;
align-items: stretch;
}
.umb-editor-sub-header__section {
border-left: 1px solid @grayLight;
display: flex;
align-items: center;
padding-left: 20px;
padding-right: 20px;
}
.umb-editor-sub-header__content-left .umb-editor-sub-header__section:first-child {
border-left: none;
padding-left: 0;
}
.umb-editor-sub-header__content-right .umb-editor-sub-header__section {
border-left: none;
border-right: 1px solid @grayLight;
}
.umb-editor-sub-header__content-right .umb-editor-sub-header__section:last-child {
border-right: none;
padding-right: 0;
}

View File

@@ -0,0 +1,19 @@
.umb-tooltip-list {
list-style: none;
margin-left: 0;
margin-bottom: 0;
padding: 10px;
}
.umb-tooltip-list__item {
margin-bottom: 5px;
}
.umb-tooltip-list__item:last-child {
margin-bottom: 0;
}
.umb-tooltip-list__item-label {
font-weight: bold;
margin-bottom: -3px;
}

View File

@@ -0,0 +1,13 @@
.umb-tooltip {
position: fixed;
display: flex;
background: #ffffff;
padding: 10px;
z-index: 1000;
max-width: 200px;
font-size: 12px;
animation-duration: 0.1s;
animation-timing-function: ease-in;
animation: fadeIn;
margin-top: 15px;
}

View File

@@ -0,0 +1,124 @@
.umb-content-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
.umb-content-grid__item {
background: @grayLighter;
flex: 0 1 200px;
cursor: pointer;
position: relative;
margin: 10px;
border: 1px solid #ffffff;
user-select: none;
}
.umb-content-grid__item:hover {
border: 1px solid @blue;
}
.umb-content-grid__item:hover .umb-content-grid__action {
opacity: 1;
}
.umb-content-grid__icon-container {
background: lighten(@grayLight, 7%);
height: 75px;
display: flex;
align-items: center;
justify-content: center;
}
.umb-content-grid__icon {
font-size: 40px;
color: lighten(@gray, 20%);
}
.umb-content-grid__icon.-light {
color: @grayLight;
}
.umb-content-grid__content {
box-sizing: border-box;
padding: 15px;
}
.umb-content-grid__item-name {
font-weight: bold;
color: @grayDark;
margin-bottom: 10px;
color: black;
padding-bottom: 10px;
line-height: 1.4em;
border-bottom: 1px solid @grayLight;
}
.umb-content-grid__item-name.-light {
color: @grayLight;
}
.umb-content-grid__details-list {
list-style: none;
margin-bottom: 0;
margin-left: 0;
font-size: 11px;
}
.umb-content-grid__details-list.-light {
color: @grayLight;
}
.umb-content-grid__details-label {
font-weight: bold;
display: inline-block;
}
.umb-content-grid__details-value {
display: inline-block;
}
.umb-content-grid__action {
position: absolute;
opacity: 0;
top: 10px;
right: 10px;
border: 1px solid #ffffff;
width: 25px;
height: 25px;
background: rgba(0, 0, 0, 0.4);
border-radius: 50px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
color: #ffffff;
cursor: pointer;
}
.umb-content-grid__action:hover {
background: @blue;
transition: background 0.1s;
}
.umb-content-grid__action.-selected {
opacity: 1;
background: @blue;
}
.umb-content-grid__item:hover .umb-content-grid__action:not(.-selected) {
opacity: 1;
animation: fadeIn;
animation-duration: 0.2s;
animation-timing-function: ease-in-out;
}
.umb-content-grid__no-items {
font-size: 16px;
font-weight: bold;
color: @grayLight;
padding-top: 50px;
padding-bottom: 50px;
}

View File

@@ -0,0 +1,81 @@
.umb-folder-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
margin-bottom: 30px;
}
.umb-folder-grid__folder {
background: @grayLighter;
margin: 5px;
display: flex;
align-items: center;
padding: 10px 20px;
box-sizing: border-box;
flex: 1 1 200px;
border: 1px solid transparent;
transition: border 0.2s;
position: relative;
justify-content: space-between;
cursor: pointer;
user-select: none;
}
.umb-folder-grid__folder:focus,
.umb-folder-grid__folder:active {
text-decoration: none;
}
.umb-folder-grid__folder:hover {
text-decoration: none;
border: 1px solid @blue;
}
.umb-folder-grid__folder-description {
display: flex;
align-items: center;
}
.umb-folder-grid__folder-icon {
font-size: 20px;
color: @gray;
margin-right: 20px;
}
.umb-folder-grid__folder-name {
font-size: 13px;
}
.umb-folder-grid__action {
opacity: 0;
border: 1px solid #ffffff;
width: 25px;
height: 25px;
background: rgba(0, 0, 0, 0.4);
border-radius: 50px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
color: #ffffff;
margin-left: 7px;
cursor: pointer;
}
.umb-folder-grid__action:hover {
background: @blue;
transition: background 0.1s;
}
.umb-folder-grid__action.-selected {
opacity: 1;
background: @blue;
}
.umb-folder-grid__folder:hover .umb-folder-grid__action:not(.-selected) {
opacity: 1;
animation: fadeIn;
animation-duration: 0.2s;
animation-timing-function: ease-in-out;
}

View File

@@ -9,7 +9,6 @@
cursor: pointer;
height: 30px;
width: 30px;
margin: 0 8px;
font-size: 20px;
display: flex;
justify-content: center;

View File

@@ -6,11 +6,10 @@
animation: fadeIn 0.5s;
padding: 15px;
position: relative;
z-index: 1;
}
.umb-list-view__trigger {
margin-bottom: 20px;
margin-bottom: 20px;
}
.umb-list-view__box.-open {
@@ -47,9 +46,7 @@
.umb-list-view__settings {
border: 1px dashed #d9d9d9;
border-top: none;
border-radius: 0 0 5px 5px;
padding: 50px 20px 20px 20px;
position: relative;
top: -20px;
z-index: 0;
padding: 20px;
}

View File

@@ -2,83 +2,23 @@
display: flex;
flex-wrap: wrap;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
width: 100%;
margin-bottom: 30px;
}
/* ---------- FOLDERS --------- */
.umb-media-grid__folders {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
margin-bottom: 20px;
}
.umb-media-grid__folder {
background: @grayLighter;
margin: 5px;
display: flex;
align-items: center;
padding: 10px 30px 10px 20px;
box-sizing: border-box;
flex: 1 1 200px;
border: 1px solid transparent;
transition: border 0.2s;
position: relative;
}
.umb-media-grid__folder.-selected {
background: rgba(255, 255, 255, 0.6);
}
.umb-media-grid__folder:focus,
.umb-media-grid__folder:active {
text-decoration: none;
}
.umb-media-grid__folder:hover {
text-decoration: none;
border: 1px solid @blue;
}
.umb-media-grid__folder-icon {
font-size: 20px;
color: @gray;
margin-right: 20px;
}
.umb-media-grid__folder-name {
font-size: 13px;
}
/* ---------- MEDIA ---------- */
.umb-media-grid__media {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
align-items: center;
}
.umb-media-grid__item {
margin: 7px;
margin: 2px;
position: relative;
background: @grayLighter;
overflow: hidden;
}
.umb-media-grid__item:hover {
text-decoration: none;
}
.umb-media-grid__item-image-placeholder {
width: 200px;
height: 200px;
display: inline-block;
}
.umb-media-grid__item-image {
width: 100%;
max-width: 100%;
@@ -91,33 +31,36 @@
}
.umb-media-grid__item-overlay {
display: none;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
padding: 5px;
padding: 5px 10px;
box-sizing: border-box;
font-size: 13px;
overflow: hidden;
color: white;
text-align: center;
white-space: nowrap;
background: black;
background: rgba(0, 0, 0, 0.4);
background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.75));
}
.umb-media-grid__item:hover .umb-media-grid__item-overlay {
display: block;
opacity: 1;
animation: fadeIn;
animation-duration: 0.2s;
animation-timing-function: ease-in-out;
}
.umb-media-grid__item-name {
text-align: center;
color: @gray;
font-size: 12px;
position: absolute;
bottom: 20px;
right: 0;
left: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.umb-media-grid__item-icon {
@@ -129,44 +72,56 @@
transform: translate(-50%,-50%);
}
.umb-media-grid__select {
.umb-media-grid__actions {
position: absolute;
z-index: 2;
width: 100%;
top: 0;
padding: 10px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
}
.umb-media-grid__action {
opacity: 0;
border: 1px solid #ffffff;
width: 30px;
height: 30px;
width: 25px;
height: 25px;
background: rgba(0, 0, 0, 0.4);
position: absolute;
top: 10px;
right: 10px;
z-index: 10;
border-radius: 50px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
color: #ffffff;
margin-left: 7px;
cursor: pointer;
}
.umb-media-grid__select.-size-20 {
width: 20px;
height: 20px;
.umb-media-grid__action.-not-clickable {
cursor: default;
}
.umb-media-grid__select:hover {
.umb-media-grid__action:first-child {
margin-left: 0;
}
.umb-media-grid__action:hover {
background: @blue;
transition: background 0.2s;
transition: background 0.1s;
}
.umb-media-grid__select.-selected {
.umb-media-grid__action.-selected {
opacity: 1;
background: @blue;
}
.umb-media-grid__select-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
}
.umb-media-grid__item:hover .umb-media-grid__select,
.umb-media-grid__folder:hover .umb-media-grid__select{
.umb-media-grid__item:hover .umb-media-grid__action:not(.-selected),
.umb-media-grid__folder:hover .umb-media-grid__action:not(.-selected) {
opacity: 1;
transition: opacity 0.2s;
animation: fadeIn;
animation-duration: 0.2s;
animation-timing-function: ease-in-out;
}

View File

@@ -87,6 +87,10 @@ form {
margin: 0 0 @baseLineHeight;
}
form.-no-margin-bottom {
margin-bottom: 0;
}
fieldset {
padding: 0;
margin: 0;

View File

@@ -511,4 +511,4 @@ height:1px;
margin: 10px 0;
overflow: hidden;
}
}

View File

@@ -0,0 +1,114 @@
(function() {
"use strict";
function CopyOverlay($scope, localizationService, eventsService) {
var vm = this;
vm.hideSearch = hideSearch;
vm.selectResult = selectResult;
vm.onSearchResults = onSearchResults;
var dialogOptions = $scope.model;
var searchText = "Search...";
var node = dialogOptions.currentNode;
localizationService.localize("general_search").then(function (value) {
searchText = value + "...";
});
$scope.model.relateToOriginal = true;
$scope.dialogTreeEventHandler = $({});
vm.searchInfo = {
searchFromId: null,
searchFromName: null,
showSearch: false,
results: [],
selectedSearchResults: []
};
function nodeSelectHandler(ev, args) {
args.event.preventDefault();
args.event.stopPropagation();
if (args.node.metaData.listViewNode) {
//check if list view 'search' node was selected
vm.searchInfo.showSearch = true;
vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
}
else {
//eventsService.emit("editors.content.copyController.select", args);
if ($scope.model.target) {
//un-select if there's a current one selected
$scope.model.target.selected = false;
}
$scope.model.target = args.node;
$scope.model.target.selected = true;
}
}
function nodeExpandedHandler(ev, args) {
if (angular.isArray(args.children)) {
//iterate children
_.each(args.children, function (child) {
//check if any of the items are list views, if so we need to add a custom
// child: A node to activate the search
if (child.metaData.isContainer) {
child.hasChildren = true;
child.children = [
{
level: child.level + 1,
hasChildren: false,
name: searchText,
metaData: {
listViewNode: child,
},
cssClass: "icon umb-tree-icon sprTree icon-search",
cssClasses: ["not-published"]
}
];
}
});
}
}
function hideSearch() {
vm.searchInfo.showSearch = false;
vm.searchInfo.searchFromId = null;
vm.searchInfo.searchFromName = null;
vm.searchInfo.results = [];
}
// method to select a search result
function selectResult(evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
}
//callback when there are search results
function onSearchResults(results) {
vm.searchInfo.results = results;
vm.searchInfo.showSearch = true;
}
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
$scope.$on('$destroy', function () {
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
});
}
angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay);
})();

View File

@@ -0,0 +1,38 @@
<div ng-controller="Umbraco.Overlays.CopyOverlay as vm">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="vm.hideSearch"
search-callback="vm.onSearchResults"
search-from-id="{{vm.searchInfo.searchFromId}}"
search-from-name="{{vm.searchInfo.searchFromName}}"
show-search="{{vm.searchInfo.showSearch}}"
section="{{model.section}}">
</umb-tree-search-box>
</div>
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
<umb-tree-search-results
ng-if="vm.searchInfo.showSearch"
results="vm.searchInfo.results"
select-result-callback="vm.selectResult">
</umb-tree-search-results>
</div>
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
<umb-tree
section="{{model.section}}"
hideheader="false"
hideoptions="true"
isdialog="true"
eventhandler="dialogTreeEventHandler"
enablecheckboxes="true">
</umb-tree>
</div>
<div class="umb-control-group -no-border">
<input type="checkbox" class="pull-left" style="margin-right: 10px;" ng-model="model.relateToOriginal" />
<label>Relate to original</label>
</div>
</div>

View File

@@ -0,0 +1,114 @@
(function() {
"use strict";
function MoveOverlay($scope, localizationService, eventsService) {
var vm = this;
vm.hideSearch = hideSearch;
vm.selectResult = selectResult;
vm.onSearchResults = onSearchResults;
var dialogOptions = $scope.model;
var searchText = "Search...";
var node = dialogOptions.currentNode;
localizationService.localize("general_search").then(function (value) {
searchText = value + "...";
});
$scope.model.relateToOriginal = true;
$scope.dialogTreeEventHandler = $({});
vm.searchInfo = {
searchFromId: null,
searchFromName: null,
showSearch: false,
results: [],
selectedSearchResults: []
};
function nodeSelectHandler(ev, args) {
args.event.preventDefault();
args.event.stopPropagation();
if (args.node.metaData.listViewNode) {
//check if list view 'search' node was selected
vm.searchInfo.showSearch = true;
vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
}
else {
//eventsService.emit("editors.content.copyController.select", args);
if ($scope.model.target) {
//un-select if there's a current one selected
$scope.model.target.selected = false;
}
$scope.model.target = args.node;
$scope.model.target.selected = true;
}
}
function nodeExpandedHandler(ev, args) {
if (angular.isArray(args.children)) {
//iterate children
_.each(args.children, function (child) {
//check if any of the items are list views, if so we need to add a custom
// child: A node to activate the search
if (child.metaData.isContainer) {
child.hasChildren = true;
child.children = [
{
level: child.level + 1,
hasChildren: false,
name: searchText,
metaData: {
listViewNode: child,
},
cssClass: "icon umb-tree-icon sprTree icon-search",
cssClasses: ["not-published"]
}
];
}
});
}
}
function hideSearch() {
vm.searchInfo.showSearch = false;
vm.searchInfo.searchFromId = null;
vm.searchInfo.searchFromName = null;
vm.searchInfo.results = [];
}
// method to select a search result
function selectResult(evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
}
//callback when there are search results
function onSearchResults(results) {
vm.searchInfo.results = results;
vm.searchInfo.showSearch = true;
}
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
$scope.$on('$destroy', function () {
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
});
}
angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay);
})();

View File

@@ -0,0 +1,33 @@
<div ng-controller="Umbraco.Overlays.MoveOverlay as vm">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="vm.hideSearch"
search-callback="vm.onSearchResults"
search-from-id="{{vm.searchInfo.searchFromId}}"
search-from-name="{{vm.searchInfo.searchFromName}}"
show-search="{{vm.searchInfo.showSearch}}"
section="{{model.section}}">
</umb-tree-search-box>
</div>
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
<umb-tree-search-results
ng-if="vm.searchInfo.showSearch"
results="vm.searchInfo.results"
select-result-callback="vm.selectResult">
</umb-tree-search-results>
</div>
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
<umb-tree
section="{{model.section}}"
hideheader="false"
hideoptions="true"
isdialog="true"
eventhandler="dialogTreeEventHandler"
enablecheckboxes="true">
</umb-tree>
</div>
</div>

View File

@@ -10,20 +10,23 @@
<a ng-if="type === 'link'" href="{{href}}" class="btn umb-button__button {{style}}" hotkey="{{shortcut}}">
<span class="umb-button__content" ng-class="{'-hidden': state !== 'init'}">
<i ng-if="icon && buttonStyle==='link'" class="{{icon}}"></i>
<localize ng-if="labelKey" key="{{labelKey}}">{{label}}</localize>
<span ng-if="!labelKey">{{label}}</span>
</span>
</a>
<button ng-if="type === 'button'" type="button" class="btn umb-button__button {{style}}" ng-click="action(model)" hotkey="{{shortcut}}">
<button ng-if="type === 'button'" type="button" class="btn umb-button__button {{style}}" ng-click="action(model)" hotkey="{{shortcut}}" ng-disabled="disabled">
<span class="umb-button__content" ng-class="{'-hidden': state !== 'init'}">
<i ng-if="icon && buttonStyle==='link'" class="{{icon}}"></i>
<localize ng-if="labelKey" key="{{labelKey}}">{{label}}</localize>
<span ng-if="!labelKey">{{label}}</span>
</span>
</button>
<button ng-if="type === 'submit'" type="submit" class="btn umb-button__button {{style}}" hotkey="{{shortcut}}">
<button ng-if="type === 'submit'" type="submit" class="btn umb-button__button {{style}}" hotkey="{{shortcut}}" ng-disabled="disabled">
<span class="umb-button__content" ng-class="{'-hidden': state !== 'init'}">
<i ng-if="icon && buttonStyle==='link'" class="{{icon}}"></i>
<localize ng-if="labelKey" key="{{labelKey}}">{{label}}</localize>
<span ng-if="!labelKey">{{label}}</span>
</span>

View File

@@ -0,0 +1 @@
<div class="umb-editor-sub-header__content-left" ng-transclude></div>

View File

@@ -0,0 +1 @@
<div class="umb-editor-sub-header__content-right" ng-transclude></div>

View File

@@ -0,0 +1 @@
<div class="umb-editor-sub-header__section" ng-transclude></div>

View File

@@ -0,0 +1,6 @@
<div
class="umb-editor-sub-header"
umb-sticky-bar
scrollable-container=".umb-editor-container"
ng-transclude>
</div>

View File

@@ -1,7 +1,7 @@
<ul class="umb-breadcrumbs">
<li class="umb-breadcrumbs__ancestor" ng-repeat="ancestor in ancestors">
<a ng-if="!$last" href="#/content/content/edit/{{ancestor.id}}" class="umb-breadcrumbs__ancestor-link">{{ancestor.name}}</a>
<a ng-if="!$last" href="#/{{entityType}}/{{entityType}}/edit/{{ancestor.id}}" class="umb-breadcrumbs__ancestor-link">{{ancestor.name}}</a>
<span ng-if="!$last" class="umb-breadcrumbs__seperator">&#47;</span>
<span class="umb-breadcrumbs__ancestor-text" ng-if="$last">{{ancestor.name}}</span>

View File

@@ -0,0 +1,32 @@
<div class="umb-content-grid">
<div
class="umb-content-grid__item"
ng-repeat="item in content"
ng-class="{'-selected': item.selected}"
ng-click="clickItem(item)">
<i ng-if="onSelect" class="icon-check umb-content-grid__action" ng-click="selectItem(item, $event, $index)" ng-class="{'-selected': item.selected}"></i>
<div class="umb-content-grid__icon-container">
<i class="{{ item.icon }} umb-content-grid__icon" ng-class="{'-light': !item.published}"></i>
</div>
<div class="umb-content-grid__content">
<div class="umb-content-grid__item-name" ng-class="{'-light': !item.published}">{{ item.name }}</div>
<ul class="umb-content-grid__details-list" ng-class="{'-light': !item.published}">
<li class="umb-content-grid__details-item" ng-repeat="property in contentProperties">
<div class="umb-content-grid__details-label">{{ property.header }}:</div>
<div class="umb-content-grid__details-value">{{ item[property.alias] }}</div>
</li>
</ul>
</div>
</div>
<div ng-if="!content" class="umb-content-grid__no-items">There are no items to show</div>
</div>

View File

@@ -0,0 +1,18 @@
<div class="umb-folder-grid">
<div
class="umb-folder-grid__folder"
ng-repeat="folder in folders"
ng-class="{'-selected': folder.selected}"
ng-click="clickFolder(folder)">
<div class="umb-folder-grid__folder-description">
<i class="umb-folder-grid__folder-icon {{ folder.icon }}"></i>
<div class="umb-folder-grid__folder-name">{{ folder.name }}</div>
</div>
<i class="icon-check umb-folder-grid__action" ng-click="selectFolder(folder, $event, $index)" ng-class="{'-selected': folder.selected}"></i>
</div>
</div>

View File

@@ -0,0 +1,3 @@
<div>
<div ng-include="options.layout.activeLayout.path"></div>
</div>

View File

@@ -1,39 +1,21 @@
<div class="umb-media-grid">
<div class="umb-media-grid__folders">
<a href="" ng-click="clickItem(item)" ng-repeat="item in items" ng-style="item.flexStyle" class="umb-media-grid__item" ng-hide="item.hidden">
<a href="#{{folder.editPath}}" class="umb-media-grid__folder" ng-repeat="folder in folders">
<div class="umb-media-grid__actions">
<i ng-if="onDetailsHover" class="icon-info umb-media-grid__action -not-clickable" ng-mouseover="hoverItemDetails(item, $event, true)" ng-mouseleave="hoverItemDetails(item, $event, false)"></i>
<i ng-if="onSelect" class="icon-check umb-media-grid__action" ng-click="selectItem(item, $event, $index)" ng-class="{'-selected': item.selected}" prevent-default></i>
</div>
<i class="umb-media-grid__folder-icon {{ folder.icon }}"></i>
<div class="umb-media-grid__item-overlay">
<div class="umb-media-grid__item-name">{{item.name}}</div>
</div>
<div class="umb-media-grid__folder-name">{{ folder.name }}</div>
<img class="umb-media-grid__item-image" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" ng-class="{'-faded': item.selected}" />
<img class="umb-media-grid__item-image" ng-if="!item.thumbnail" src="assets/img/transparent.png" alt="{{item.name}}" />
<div class="umb-media-grid__select -size-20" ng-click="toggleSelectItem(folder)" ng-class="{'-selected': folder.selected}" prevent-default>
<i class="icon-check umb-media-grid__select-icon"></i>
</div>
<i class="umb-media-grid__item-icon {{ item.icon }}" ng-if="!item.thumbnail"></i>
</a>
</div>
<div class="umb-media-grid__media">
<a href="#{{item.editPath}}" ng-repeat="item in mediaItems" ng-style="item.flexStyle" class="umb-media-grid__item">
<div class="umb-media-grid__select" ng-click="toggleSelectItem(item)" ng-class="{'-selected': item.selected}" prevent-default>
<i class="icon-check umb-media-grid__select-icon"></i>
</div>
<div class="umb-media-grid__item-overlay">{{item.name}}</div>
<img class="umb-media-grid__item-image" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" ng-class="{'-faded': item.selected}" />
<img class="umb-media-grid__item-image" ng-if="!item.thumbnail" src="assets/img/transparent.png" alt="{{item.name}}" />
<i class="umb-media-grid__item-icon {{ item.icon }}" ng-if="!item.thumbnail"></i>
</a>
</div>
</a>
</div>

View File

@@ -0,0 +1,26 @@
<div class="pagination" ng-show="pagination.length > 1">
<ul>
<li ng-class="{disabled:pageNumber <= 1}">
<a href="#" ng-click="prev()" prevent-default>
<localize key="general_previous">Previous</localize>
</a>
</li>
<li ng-repeat="pgn in pagination"
ng-class="{active:pgn.isActive}">
<a href="#" ng-click="goToPage(pgn.val - 1)" prevent-default
ng-bind="pgn.name ? pgn.name : pgn.val"
ng-if="pgn.val != '...'"></a>
<span ng-bind="pgn.val" ng-if="pgn.val == '...'"></span>
</li>
<li ng-class="{disabled:pageNumber >= totalPages}">
<a href="#" ng-click="next()" prevent-default>
<localize key="general_next">Next</localize>
</a>
</li>
</ul>
</div>

View File

@@ -0,0 +1 @@
<div class="umb-tooltip shadow-depth-2" ng-style="tooltipStyles" ng-transclude></div>

View File

@@ -38,7 +38,13 @@
<umb-editor-footer>
<umb-editor-footer-content-left>
<umb-breadcrumbs ancestors="ancestors" ng-if="ancestors && ancestors.length > 0"></umb-breadcrumbs>
<umb-breadcrumbs
ng-if="ancestors && ancestors.length > 0"
ancestors="ancestors"
entity-type="content">
</umb-breadcrumbs>
</umb-editor-footer-content-left>

View File

@@ -43,7 +43,7 @@ function FormsController($scope, $route, $cookieStore, packageResource) {
$scope.complete = function(result){
var url = window.location.href + "?init=true";
$cookieStore.put("umbPackageInstallId", result.packageGuid);
$cookieStore.put("umbPackageInstallId", result.packageGuid);
window.location.reload(true);
};
@@ -186,26 +186,29 @@ function startupLatestEditsController($scope) {
}
angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController);
function MediaFolderBrowserDashboardController($rootScope, $scope, assetsService, $routeParams, $timeout, $element, $location, umbRequestHelper,navigationService, mediaResource, $cookies, mediaHelper) {
var dialogOptions = $scope.dialogOptions;
function MediaFolderBrowserDashboardController($rootScope, $scope, contentTypeResource) {
$scope.filesUploading = [];
$scope.nodeId = -1;
$scope.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
//get the system media listview
contentTypeResource.getPropertyTypeScaffold(-96)
.then(function(dt) {
function loadChildren() {
mediaResource.getChildren($scope.nodeId)
.then(function(data) {
$scope.images = data.items;
});
}
$scope.fakeProperty = {
alias: "contents",
config: dt.config,
description: "",
editor: dt.editor,
hideLabel: true,
id: 1,
label: "Contents:",
validation: {
mandatory: false,
pattern: null
},
value: "",
view: dt.view
};
$scope.onUploadComplete = function () {
navigationService.reloadSection("media");
loadChildren();
}
loadChildren();
});
}
angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController);

View File

@@ -1,19 +1,3 @@
<div ng-controller="Umbraco.Dashboard.MediaFolderBrowserDashboardController">
<umb-file-dropzone
ng-if="creating == null"
parent-id="{{nodeId}}"
compact="[images.length > 0}"
files-uploaded="onUploadComplete"
accept="{{ acceptedFileTypes }}">
</umb-file-dropzone>
<umb-photo-folder ng-show="images.length > 0"
min-height="105"
min-width="220"
on-click="clickHandler"
ng-model="images" />
<umb-editor ng-if="fakeProperty" model="fakeProperty"></umb-editor>
</div>

View File

@@ -35,7 +35,13 @@
<umb-editor-footer>
<umb-editor-footer-content-left>
<umb-breadcrumbs ng-if="ancestors && ancestors.length > 0" ancestors="ancestors"></umb-breadcrumbs>
<umb-breadcrumbs
ng-if="ancestors && ancestors.length > 0"
ancestors="ancestors"
entity-type="media">
</umb-breadcrumbs>
</umb-editor-footer-content-left>

View File

@@ -1,31 +1,29 @@
angular.module("umbraco")
//this controller is obsolete and should not be used anymore
//it proxies everything to the system media list view which has overtaken
//all the work this property editor used to perform
.controller("Umbraco.PropertyEditors.FolderBrowserController",
function ($rootScope, $scope, $routeParams, $timeout, editorState, navigationService, mediaResource, mediaHelper) {
function ($rootScope, $scope, contentTypeResource) {
//get the system media listview
contentTypeResource.getPropertyTypeScaffold(-96)
.then(function(dt) {
var dialogOptions = $scope.dialogOptions;
$scope.creating = $routeParams.create;
$scope.nodeId = $routeParams.id;
$scope.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
loadChildren();
$scope.fakeProperty = {
alias: "contents",
config: dt.config,
description: "",
editor: dt.editor,
hideLabel: true,
id: 1,
label: "Contents:",
validation: {
mandatory: false,
pattern: null
},
value: "",
view: dt.view
};
function loadChildren() {
mediaResource.getChildren($scope.nodeId)
.then(function(data) {
$scope.images = data.items;
});
}
$scope.onUploadComplete = function () {
//sync the tree - don't force reload since we're not updating this particular node (i.e. its name or anything),
// then we'll get the resulting tree node which we can then use to reload it's children.
var path = editorState.current.path;
navigationService.syncTree({ tree: "media", path: path, forceReload: false }).then(function (syncArgs) {
navigationService.reloadNode(syncArgs.node);
});
loadChildren();
}
});
});

View File

@@ -1,16 +1,3 @@
<div ng-controller="Umbraco.PropertyEditors.FolderBrowserController">
<umb-file-dropzone
ng-if="creating == null"
parent-id="{{nodeId}}"
compact="{{images.length > 0}}"
files-uploaded="onUploadComplete"
accept="{{acceptedFileTypes}}">
</umb-file-dropzone>
<umb-photo-folder ng-show="!creating && images.length > 0"
min-height="105"
min-width="220"
on-click="clickHandler"
ng-model="images" />
<umb-editor ng-if="fakeProperty" model="fakeProperty"></umb-editor>
</div>

View File

@@ -1,5 +1,17 @@
<div ng-controller="Umbraco.PropertyEditors.ListView.GridLayoutController as vm">
<div
ng-if="entityType !== 'media'">
<umb-content-grid
content="items"
content-properties="options.includeProperties"
on-click="vm.clickItem"
on-select="vm.selectItem">
</umb-content-grid>
</div>
<div
ng-if="entityType === 'media'"
on-drag-leave="vm.dragLeave()"
@@ -10,15 +22,36 @@
parent-id="{{vm.nodeId}}"
files-uploaded="vm.onUploadComplete"
accept="{{vm.acceptedFileTypes}}"
hide-dropzone="{{!vm.activeDrag && listViewResultSet.items.length > 0 }}"
compact="{{ listViewResultSet.items.length > 0 }}"
hide-dropzone="{{!vm.activeDrag && items.length > 0 }}"
compact="{{ items.length > 0 }}"
files-queued="vm.onFilesQueue">
</umb-file-dropzone>
<umb-folder-grid
ng-if="folders.length > 0"
folders="folders"
on-select="vm.selectFolder"
on-click="vm.clickItem">
</umb-folder-grid>
<umb-media-grid
items="listViewResultSet.items">
items="items"
on-details-hover="vm.hoverMediaItemDetails"
on-select="vm.selectItem"
on-click="vm.clickItem">
</umb-media-grid>
<umb-tooltip
ng-if="vm.mediaDetailsTooltip.show"
event="vm.mediaDetailsTooltip.event">
<ul class="umb-tooltip-list">
<li class="umb-tooltip-list__item" ng-repeat="property in options.includeProperties">
<div class="umb-tooltip-list__item-label">{{ property.header }}</div>
<div class="umb-tooltip-list__item-value">{{ vm.mediaDetailsTooltip.item[property.alias] }}</div>
</li>
</ul>
</umb-tooltip>
<div>
</div>

View File

@@ -9,18 +9,23 @@
(function() {
"use strict";
function ListViewGridLayoutController($scope, $routeParams, mediaHelper) {
function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper) {
var vm = this;
vm.nodeId = $routeParams.id;
vm.nodeId = $scope.contentId;
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
vm.activeDrag = false;
vm.mediaDetailsTooltip = {};
vm.dragEnter = dragEnter;
vm.dragLeave = dragLeave;
vm.onFilesQueue = onFilesQueue;
vm.onUploadComplete = onUploadComplete;
vm.hoverMediaItemDetails = hoverMediaItemDetails;
vm.selectItem = selectItem;
vm.selectFolder = selectFolder;
vm.clickItem = clickItem;
function dragEnter(el, event) {
vm.activeDrag = true;
@@ -35,10 +40,35 @@
}
function onUploadComplete() {
$scope.getContent($scope.contentId);
}
// call reload function on list view parent controller
$scope.reloadView($scope.contentId);
function hoverMediaItemDetails(item, event, hover) {
if (hover && !vm.mediaDetailsTooltip.show) {
vm.mediaDetailsTooltip.event = event;
vm.mediaDetailsTooltip.item = item;
vm.mediaDetailsTooltip.show = true;
} else if (!hover && vm.mediaDetailsTooltip.show) {
vm.mediaDetailsTooltip.show = false;
}
}
function selectItem(selectedItem, $event, index) {
listViewHelper.selectHandler(selectedItem, index, $scope.items, $scope.selection, $event);
}
function selectFolder(selectedItem, $event, index) {
listViewHelper.selectHandler(selectedItem, index, $scope.folders, $scope.selection, $event);
}
function clickItem(item) {
$location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
}
}

View File

@@ -1,51 +1,54 @@
<table class="table table-striped">
<thead>
<tr>
<td style="width: 35px">
<input type="checkbox" ng-click="selectAll($event)" ng-checked="isSelectedAll()">
</td>
<td>
<a href="#" ng-click="sort('Name', true)" prevent-default class="sortable">
<localize key="general_name">Name</localize>
<i class="icon" ng-class="{'icon-navigation-up': isSortDirection('Name', 'asc'), 'icon-navigation-down': isSortDirection('Name', 'desc')}"></i>
</a>
</td>
<div ng-controller="Umbraco.PropertyEditors.ListView.ListLayoutController as vm">
<td ng-repeat="column in options.includeProperties">
<a href="#" ng-click="sort(column.alias, column.allowSorting)" ng-class="{'sortable':column.allowSorting}" prevent-default>
<span ng-bind="column.header"></span>
<i class="icon" ng-class="{'icon-navigation-up': isSortDirection(column.alias, 'asc'), 'icon-navigation-down': isSortDirection(column.alias, 'desc')}"></i>
</a>
</td>
</tr>
</thead>
<table class="table table-striped">
<thead>
<tr>
<td style="width: 35px">
<input type="checkbox" ng-click="vm.selectAll($event)" ng-checked="vm.isSelectedAll()">
</td>
<td>
<a href="#" ng-click="vm.sort('Name', true)" prevent-default class="sortable">
<localize key="general_name">Name</localize>
<i class="icon" ng-class="{'icon-navigation-up': vm.isSortDirection('Name', 'asc'), 'icon-navigation-down': vm.isSortDirection('Name', 'desc')}"></i>
</a>
</td>
<tbody ng-show="listViewResultSet.totalItems === 0">
<tr>
<td colspan="{{options.includeProperties.length + 2}}">
<p><localize key="content_listViewNoItems">There are no items show in the list.</localize></p>
</td>
</tr>
</tbody>
<td ng-repeat="column in options.includeProperties">
<a href="#" ng-click="vm.sort(column.alias, column.allowSorting)" ng-class="{'sortable':column.allowSorting}" prevent-default>
<span ng-bind="column.header"></span>
<i class="icon" ng-class="{'icon-navigation-up': vm.isSortDirection(column.alias, 'asc'), 'icon-navigation-down': vm.isSortDirection(column.alias, 'desc')}"></i>
</a>
</td>
</tr>
</thead>
<tbody ng-show="listViewResultSet.totalItems > 0">
<tr ng-repeat="result in listViewResultSet.items"
ng-class="{selected:result.selected}">
<tbody ng-show="items.length === 0">
<tr>
<td colspan="{{options.includeProperties.length + 2}}">
<p><localize key="content_listViewNoItems">There are no items show in the list.</localize></p>
</td>
</tr>
</tbody>
<td>
<i class="icon {{result.icon}}" ng-class="getIcon(result)"></i>
<input type="checkbox" ng-model="result.selected" no-dirty-check>
</td>
<td>
<a ng-class="{inactive: (entityType === 'content' && !result.published) || isTrashed}"
href="#{{result.editPath}}"
ng-bind="result.name"></a>
</td>
<tbody ng-show="items.length > 0">
<tr ng-repeat="item in items"
ng-class="{selected:item.selected}">
<td ng-repeat="column in options.includeProperties">
<span>{{result[column.alias]}}</span>
</td>
</tr>
</tbody>
<td>
<i class="icon {{item.icon}}" ng-class="getIcon(item)"></i>
<input type="checkbox" ng-click="vm.selectItem(item)" ng-model="item.selected" no-dirty-check>
</td>
<td>
<a ng-class="{inactive: (entityType === 'content' && !item.published) || isTrashed}"
href="#{{item.editPath}}"
ng-bind="item.name"></a>
</td>
</table>
<td ng-repeat="column in options.includeProperties">
<span>{{item[column.alias]}}</span>
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,99 @@
(function() {
"use strict";
function ListViewListLayoutController($scope) {
var vm = this;
vm.selectItem = selectItem;
vm.selectAll = selectAll;
vm.isSelectedAll = isSelectedAll;
vm.isSortDirection = isSortDirection;
vm.sort = sort;
function selectAll($event) {
var checkbox = $event.target;
var clearSelection = false;
if (!angular.isArray($scope.items)) {
return;
}
$scope.selection.length = 0;
for (var i = 0; i < $scope.items.length; i++) {
var entity = $scope.items[i];
if (checkbox.checked) {
$scope.selection.push({id: entity.id});
} else {
clearSelection = true;
}
entity.selected = checkbox.checked;
}
if (clearSelection) {
$scope.selection.length = 0;
}
}
function selectItem(item) {
var selection = $scope.selection;
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
isSelected = true;
selection.splice(i, 1);
item.selected = false;
}
}
if (!isSelected) {
selection.push({id: item.id});
item.selected = true;
}
}
function isSelectedAll() {
if (!angular.isArray($scope.items)) {
return false;
}
return _.every($scope.items, function(item) {
return item.selected;
});
}
function isSortDirection(col, direction) {
return $scope.options.orderBy.toUpperCase() == col.toUpperCase() && $scope.options.orderDirection == direction;
}
function sort(field, allow) {
if (allow) {
$scope.options.orderBy = field;
if ($scope.options.orderDirection === "desc") {
$scope.options.orderDirection = "asc";
}
else {
$scope.options.orderDirection = "desc";
}
$scope.getContent($scope.contentId);
}
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController);
})();

View File

@@ -1,6 +1,6 @@
function listViewController($rootScope, $scope, $routeParams, $injector, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q) {
function listViewController($rootScope, $scope, $routeParams, $injector, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) {
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
// that isn't created yet, if we continue this will use the parent id in the route params which isn't what
// we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove
// the list view tab entirely when it's new.
@@ -11,7 +11,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
//Now we need to check if this is for media, members or content because that will depend on the resources we use
var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback;
//check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals)
if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) {
$scope.entityType = "member";
@@ -31,12 +31,12 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) {
$scope.entityType = "media";
contentResource = $injector.get('mediaResource');
getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes;
getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes;
}
else {
$scope.entityType = "content";
contentResource = $injector.get('contentResource');
getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes;
getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes;
}
getListResultsCallback = contentResource.getChildren;
deleteItemCallback = contentResource.deleteById;
@@ -51,8 +51,8 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$scope.pagination = [];
$scope.isNew = false;
$scope.actionInProgress = false;
$scope.layout = {};
$scope.layout.activeLayout = {};
$scope.selection = [];
$scope.folders = [];
$scope.listViewResultSet = {
totalPages: 0,
items: []
@@ -68,17 +68,20 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
{ alias: 'updateDate', header: 'Last edited', isSystem: 1 },
{ alias: 'updater', header: 'Last edited by', isSystem: 1 }
],
layout: {
layouts: $scope.model.config.layouts,
activeLayout: getFirstAllowedLayout($scope.model.config.layouts)
},
allowBulkPublish: true,
allowBulkUnpublish: true,
allowBulkDelete: true,
allowBulkCopy: true,
allowBulkMove: true,
allowBulkDelete: true,
};
// set active layout
$scope.layout.activeLayout = getFirstAllowedLayout($scope.model.config.layouts);
//update all of the system includeProperties to enable sorting
_.each($scope.options.includeProperties, function(e, i) {
if (e.isSystem) {
//NOTE: special case for contentTypeAlias, it's a system property that cannot be sorted
@@ -87,7 +90,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
if (e.alias != "contentTypeAlias") {
e.allowSorting = true;
}
//localize the header
var key = getLocalizedKey(e.alias);
localizationService.localize(key).then(function (v) {
@@ -140,50 +143,21 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
}
}
$scope.isSortDirection = function (col, direction) {
return $scope.options.orderBy.toUpperCase() == col.toUpperCase() && $scope.options.orderDirection == direction;
}
$scope.next = function() {
if ($scope.options.pageNumber < $scope.listViewResultSet.totalPages) {
$scope.options.pageNumber++;
$scope.reloadView($scope.contentId);
//TODO: this would be nice but causes the whole view to reload
//$location.search("page", $scope.options.pageNumber);
}
$scope.next = function(pageNumber) {
$scope.options.pageNumber = pageNumber;
$scope.reloadView($scope.contentId);
};
$scope.goToPage = function(pageNumber) {
$scope.options.pageNumber = pageNumber + 1;
$scope.reloadView($scope.contentId);
//TODO: this would be nice but causes the whole view to reload
//$location.search("page", $scope.options.pageNumber);
$scope.options.pageNumber = pageNumber;
$scope.reloadView($scope.contentId);
};
$scope.sort = function(field, allow) {
if (allow) {
$scope.options.orderBy = field;
if ($scope.options.orderDirection === "desc") {
$scope.options.orderDirection = "asc";
}
else {
$scope.options.orderDirection = "desc";
}
$scope.reloadView($scope.contentId);
}
$scope.prev = function(pageNumber) {
$scope.options.pageNumber = pageNumber;
$scope.reloadView($scope.contentId);
};
$scope.prev = function() {
if ($scope.options.pageNumber > 1) {
$scope.options.pageNumber--;
$scope.reloadView($scope.contentId);
//TODO: this would be nice but causes the whole view to reload
//$location.search("page", $scope.options.pageNumber);
}
};
/*Loads the search results, based on parameters set in prev,next,sort and so on*/
/*Pagination is done by an array of objects, due angularJS's funky way of monitoring state
@@ -191,6 +165,17 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$scope.reloadView = function (id) {
listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection);
if($scope.entityType === 'media') {
mediaResource.getChildFolders($scope.contentId)
.then(function(folders) {
$scope.folders = folders;
});
}
getListResultsCallback(id, $scope.options).then(function (data) {
$scope.actionInProgress = false;
@@ -214,45 +199,6 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$scope.reloadView(id);
}
$scope.pagination = [];
//list 10 pages as per normal
if ($scope.listViewResultSet.totalPages <= 10) {
for (var i = 0; i < $scope.listViewResultSet.totalPages; i++) {
$scope.pagination.push({
val: (i + 1),
isActive: $scope.options.pageNumber == (i + 1)
});
}
}
else {
//if there is more than 10 pages, we need to do some fancy bits
//get the max index to start
var maxIndex = $scope.listViewResultSet.totalPages - 10;
//set the start, but it can't be below zero
var start = Math.max($scope.options.pageNumber - 5, 0);
//ensure that it's not too far either
start = Math.min(maxIndex, start);
for (var i = start; i < (10 + start) ; i++) {
$scope.pagination.push({
val: (i + 1),
isActive: $scope.options.pageNumber == (i + 1)
});
}
//now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing
if (start > 0) {
$scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false});
}
//same for the end
if (start < maxIndex) {
$scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: $scope.listViewResultSet.totalPages, isActive: false });
}
}
});
};
@@ -272,33 +218,20 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$($event.target).next().focus();
}
$scope.selectAll = function($event) {
var checkbox = $event.target;
if (!angular.isArray($scope.listViewResultSet.items)) {
return;
}
for (var i = 0; i < $scope.listViewResultSet.items.length; i++) {
var entity = $scope.listViewResultSet.items[i];
entity.selected = checkbox.checked;
}
};
$scope.isSelectedAll = function() {
if (!angular.isArray($scope.listViewResultSet.items)) {
return false;
}
return _.every($scope.listViewResultSet.items, function(item) {
return item.selected;
});
};
$scope.isAnythingSelected = function() {
if (!angular.isArray($scope.listViewResultSet.items)) {
return false;
}
return _.some($scope.listViewResultSet.items, function(item) {
return item.selected;
});
if ($scope.selection.length === 0) {
return false;
} else {
return true;
}
};
$scope.selectedItemsCount = function() {
return $scope.selection.length;
};
$scope.clearSelection = function() {
listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection);
};
$scope.getIcon = function(entry) {
@@ -318,9 +251,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
}
function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) {
var selected = _.filter($scope.listViewResultSet.items, function (item) {
return item.selected;
});
var selected = $scope.selection;
if (selected.length === 0)
return;
if (confirmMsg && !confirm(confirmMsg))
@@ -340,25 +271,89 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
$scope.delete = function () {
applySelected(
function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])) },
function (count, total) { return "Deleted " + count + " out of " + total + " document" + (total > 1 ? "s" : "") },
function (total) { return "Deleted " + total + " document" + (total > 1 ? "s" : "") },
function (count, total) { return "Deleted " + count + " out of " + total + " item" + (total > 1 ? "s" : "") },
function (total) { return "Deleted " + total + " item" + (total > 1 ? "s" : "") },
"Sure you want to delete?");
};
$scope.publish = function () {
applySelected(
function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); },
function (count, total) { return "Published " + count + " out of " + total + " document" + (total > 1 ? "s" : "") },
function (total) { return "Published " + total + " document" + (total > 1 ? "s" : "") });
function (count, total) { return "Published " + count + " out of " + total + " item" + (total > 1 ? "s" : "") },
function (total) { return "Published " + total + " item" + (total > 1 ? "s" : "") });
};
$scope.unpublish = function() {
applySelected(
function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); },
function (count, total) { return "Unpublished " + count + " out of " + total + " document" + (total > 1 ? "s" : "") },
function (total) { return "Unpublished " + total + " document" + (total > 1 ? "s" : "") });
function (count, total) { return "Unpublished " + count + " out of " + total + " item" + (total > 1 ? "s" : "") },
function (total) { return "Unpublished " + total + " item" + (total > 1 ? "s" : "") });
};
$scope.move = function() {
$scope.moveDialog = {};
$scope.moveDialog.title = "Move";
$scope.moveDialog.section = $scope.entityType;
$scope.moveDialog.currentNode = $scope.contentId;
$scope.moveDialog.view = "move";
$scope.moveDialog.show = true;
$scope.moveDialog.submit = function(model) {
if(model.target) {
performMove(model.target);
}
$scope.moveDialog.show = false;
$scope.moveDialog = null;
};
$scope.moveDialog.close = function(oldModel) {
$scope.moveDialog.show = false;
$scope.moveDialog = null;
};
};
function performMove(target) {
applySelected(
function(selected, index) {return contentResource.move({parentId: target.id, id: getIdCallback(selected[index])}); },
function(count, total) {return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); },
function(total) {return "Moved " + total + " item" + (total > 1 ? "s" : ""); });
}
$scope.copy = function() {
$scope.copyDialog = {};
$scope.copyDialog.title = "Copy";
$scope.copyDialog.section = $scope.entityType;
$scope.copyDialog.currentNode = $scope.contentId;
$scope.copyDialog.view = "copy";
$scope.copyDialog.show = true;
$scope.copyDialog.submit = function(model) {
if(model.target) {
performCopy(model.target, model.relateToOriginal);
}
$scope.copyDialog.show = false;
$scope.copyDialog = null;
};
$scope.copyDialog.close = function(oldModel) {
$scope.copyDialog.show = false;
$scope.copyDialog = null;
};
};
function performCopy(target, relateToOriginal) {
applySelected(
function(selected, index) {return contentResource.copy({parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal}); },
function(count, total) {return "Copied " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); },
function(total) {return "Copied " + total + " item" + (total > 1 ? "s" : ""); });
}
function getCustomPropertyValue(alias, properties) {
var value = '';
var index = 0;
@@ -388,9 +383,9 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
var alias = e.alias;
// First try to pull the value directly from the alias (e.g. updatedBy)
// First try to pull the value directly from the alias (e.g. updatedBy)
var value = result[alias];
// If this returns an object, look for the name property of that (e.g. owner.name)
if (value === Object(value)) {
value = value['name'];
@@ -421,14 +416,18 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
};
function initView() {
if ($routeParams.id) {
$scope.listViewAllowedTypes = getContentTypesCallback($routeParams.id);
$scope.contentId = $routeParams.id;
$scope.isTrashed = $routeParams.id === "-20" || $routeParams.id === "-21";
$scope.reloadView($scope.contentId);
//default to root id if the id is undefined
var id = $routeParams.id;
if(id === undefined){
id = -1;
}
$scope.listViewAllowedTypes = getContentTypesCallback(id);
$scope.contentId = id;
$scope.isTrashed = id === "-20" || id === "-21";
$scope.reloadView($scope.contentId);
};
function getLocalizedKey(alias) {
@@ -462,4 +461,4 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController);
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController);

View File

@@ -1,14 +1,17 @@
<div class="umb-editor umb-listview" ng-controller="Umbraco.PropertyEditors.ListViewController" ng-switch="isNew">
<div class="row-fluid" ng-switch-when="true">
<div class="row-fluid" ng-switch-when="true">
</div>
</div>
<div class="row-fluid" ng-switch-when="false">
<div class="row-fluid" ng-switch-when="false">
<div class="umb-sub-header clearfix">
<div class="pull-left">
<div class="btn-group" ng-show="listViewAllowedTypes && listViewAllowedTypes.length > 0">
<umb-editor-sub-header>
<umb-editor-sub-header-content-left>
<umb-editor-sub-header-section ng-if="listViewAllowedTypes && listViewAllowedTypes.length > 0 && !isAnythingSelected()">
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<localize key="actions_create">Create</localize>
<span class="caret"></span>
@@ -22,77 +25,144 @@
</li>
</ul>
</div>
</umb-editor-sub-header-section>
<div class="btn-group" ng-show="isAnythingSelected()" ng-if="entityType === 'content' && !isTrashed && options.allowBulkPublish">
<a class="btn btn-success" ng-disabled="actionInProgress" ng-click="publish()" prevent-default>
<localize key="actions_publish">Publish</localize>
</a>
</div>
<div class="btn-group" ng-show="isAnythingSelected()" ng-if="entityType === 'content' && !isTrashed && options.allowBulkUnpublish">
<a class="btn btn-warning" ng-disabled="actionInProgress" ng-click="unpublish()" prevent-default>
<localize key="actions_unpublish">Unpublish</localize>
</a>
</div>
<div class="btn-group" ng-show="isAnythingSelected()" ng-if="options.allowBulkDelete">
<a class="btn btn-danger" ng-disabled="actionInProgress" ng-click="delete()" prevent-default>
<localize key="actions_delete">Delete</localize>
</a>
</div>
</div>
<umb-editor-sub-header-section ng-if="isAnythingSelected()">
<umb-button
type="button"
label="Cancel"
action="clearSelection()"
disabled="actionInProgress">
</umb-button>
</umb-editor-sub-header-section>
<div class="pull-right">
<umb-editor-sub-header-section ng-if="isAnythingSelected()">
<strong ng-show="!actionInProgress">{{ selectedItemsCount() }} of {{ listViewResultSet.items.length }} selected</strong>
<strong ng-show="actionInProgress" ng-bind="bulkStatus"></strong>
<form class="form-search pull-right" novalidate ng-show="!actionInProgress">
<div class="umb-loader-wrapper -bottom" ng-show="actionInProgress">
<div class="umb-loader"></div>
</div>
</umb-editor-sub-header-section>
</umb-editor-sub-header-content-left>
<umb-editor-sub-header-content-right>
<umb-editor-sub-header-section ng-if="!isAnythingSelected()">
<umb-layout-selector
ng-if="options.layout.layouts"
layouts="options.layout.layouts"
active-layout="options.layout.activeLayout">
</umb-layout-selector>
</umb-editor-sub-header-section>
<umb-editor-sub-header-section ng-if="!actionInProgress && !isAnythingSelected()">
<form class="form-search -no-margin-bottom pull-right" novalidate>
<div class="inner-addon left-addon">
<i class="icon icon-search" ng-click="enterSearch($event)"></i>
<input type="text" class="form-control" localize="placeholder" placeholder="@general_typeToSearch" ng-model="options.filter" prevent-enter-submit no-dirty-check>
</div>
</form>
</umb-editor-sub-header-section>
<umb-layout-selector
ng-if="model.config.layouts"
layouts="model.config.layouts"
active-layout="layout.activeLayout">
</umb-layout-selector>
<umb-editor-sub-header-section ng-if="isAnythingSelected()">
</div>
<umb-button
ng-if="entityType === 'content' && !isTrashed && options.allowBulkPublish"
type="button"
button-style="link"
label="Publish"
key="actions_publish"
icon="icon-globe"
action="publish()"
disabled="actionInProgress">
</umb-button>
<div class="umb-loader-wrapper" ng-show="actionInProgress">
<div class="umb-loader"></div>
</div>
<umb-button
ng-if="entityType === 'content' && !isTrashed && options.allowBulkUnpublish"
type="button"
button-style="link"
label="Unpublish"
key="actions_unpublish"
icon="icon-block"
action="unpublish()"
disabled="actionInProgress">
</umb-button>
<span ng-bind="bulkStatus" ng-show="isAnythingSelected()" class="pull-right"></span>
<umb-button
ng-if="entityType === 'content' && options.allowBulkCopy"
type="button"
button-style="link"
label="Copy"
key="actions_copy"
icon="icon-documents"
action="copy()"
disabled="actionInProgress">
</umb-button>
</div>
<umb-button
ng-if="options.allowBulkMove"
type="button"
button-style="link"
label="Move"
key="actions_move"
icon="icon-enter"
action="move()"
disabled="actionInProgress">
</umb-button>
<div ng-include="layout.activeLayout.path"></div>
<umb-button
ng-if="options.allowBulkDelete"
type="button"
button-style="link"
label="Delete"
key="actions_delete"
icon="icon-trash"
action="delete()"
disabled="actionInProgress">
</umb-button>
<div class="pagination" ng-show="pagination.length > 1">
</umb-editor-sub-header-section>
<ul>
<li ng-class="{disabled:options.pageNumber <= 1}">
<a href="#" ng-click="prev()" prevent-default>
<localize key="general_previous">Previous</localize>
</a>
</li>
</umb-editor-sub-header-content-right>
<li ng-repeat="pgn in pagination"
ng-class="{active:pgn.isActive}">
</umb-editor-sub-header>
<a href="#" ng-click="goToPage(pgn.val - 1)" prevent-default
ng-bind="pgn.name ? pgn.name : pgn.val"
ng-if="pgn.val != '...'"></a>
<span ng-bind="pgn.val" ng-if="pgn.val == '...'"></span>
</li>
<umb-list-view-layout
content-id="contentId"
folders="folders"
items="listViewResultSet.items"
selection="selection"
options="options"
entity-type="{{entityType}}"
on-get-content="reloadView">
</umb-list-view-layout>
<li ng-class="{disabled:options.pageNumber >= listViewResultSet.totalPages}">
<a href="#" ng-click="next()" prevent-default>
<localize key="general_next">Next</localize>
</a>
</li>
</ul>
</div>
<umb-pagination
ng-if="listViewResultSet.totalPages"
page-number="options.pageNumber"
total-pages="listViewResultSet.totalPages"
on-next="next"
on-prev="prev"
on-go-to-page="goToPage">
</umb-pagination>
</div>
<umb-overlay
ng-if="moveDialog.show"
model="moveDialog"
position="right"
view="moveDialog.view">
</umb-overlay>
<umb-overlay
ng-if="copyDialog.show"
model="copyDialog"
position="right"
view="copyDialog.view">
</umb-overlay>
</div>

View File

@@ -79,7 +79,7 @@
<system.web>
<customErrors mode="RemoteOnly" />
<trace enabled="true" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
<httpRuntime requestValidationMode="2.0" enableVersionHeader="false" targetFramework="4.5" />
<httpRuntime requestValidationMode="2.0" enableVersionHeader="false" targetFramework="4.5" maxRequestLength="1048576" />
<!--
If you are deploying to a cloud environment that has multiple web server instances,
you should change session state mode from "InProc" to "Custom". In addition,
@@ -223,6 +223,13 @@
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
<!-- Increase the default upload file size limit -->
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>

View File

@@ -602,8 +602,9 @@ namespace Umbraco.Web.Editors
//cannot move if the content item is not allowed at the root
if (toMove.ContentType.AllowedAsRoot == false)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(Services.TextService.Localize("moveOrCopy/notAllowedAtRoot")));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedAtRoot"), "");
throw new HttpResponseException( Request.CreateValidationErrorResponse(notificationModel));
}
}
else
@@ -614,19 +615,23 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
//check if the item is allowed under this one
if (parent.ContentType.AllowedContentTypes.Select(x => x.Id).ToArray()
.Any(x => x.Value == toMove.ContentType.Id) == false)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(Services.TextService.Localize("moveOrCopy/notAllowedByContentType")));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByContentType"), "");
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
}
// Check on paths
if ((string.Format(",{0},", parent.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(Services.TextService.Localize("moveOrCopy/notAllowedByPath")));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), "");
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
}
}

View File

@@ -46,7 +46,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Gets the content json for the content id
/// Gets the datatype json for the datatype id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>

View File

@@ -118,6 +118,21 @@ namespace Umbraco.Web.Editors
return foundMedia.Select(Mapper.Map<IMedia, MediaItemDisplay>);
}
/// <summary>
/// Returns media items known to be a container of other media items
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>))]
public IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>> GetChildFolders(int id = -1)
{
//Suggested convention for folder mediatypes - we can make this more or less complicated as long as we document it...
//if you create a media type, which has an alias that ends with ...Folder then its a folder: ex: "secureFolder", "bannerFolder", "Folder"
var folderTypes = Services.ContentTypeService.GetAllMediaTypes().ToArray().Where(x => x.Alias.EndsWith("Folder")).Select(x => x.Id);
var children = (id < 0) ? Services.MediaService.GetRootMedia() : Services.MediaService.GetById(id).Children();
return children.Where(x => folderTypes.Contains(x.ContentTypeId)).Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>);
}
/// <summary>
/// Returns the root media objects
@@ -409,7 +424,7 @@ namespace Umbraco.Web.Editors
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer");
}
//ensure the user has access to this folder by parent id!
if (CheckPermissions(
new Dictionary<string, object>(),
@@ -423,9 +438,47 @@ namespace Umbraco.Web.Editors
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning)));
}
var tempFiles = new PostedFiles();
var mediaService = ApplicationContext.Services.MediaService;
//in case we pass a path with a folder in it, we will create it and upload media to it.
if (result.FormData.ContainsKey("path"))
{
var folders = result.FormData["path"].Split('/');
for (int i = 0; i < folders.Length-1; i++)
{
var folderName = folders[i];
//get current parent
var mediaRoot = mediaService.GetById(parentId);
//if the media root is null, something went wrong, we'll abort
if (mediaRoot == null)
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "The folder: " + folderName + " could not be used for storing images, its ID: " + parentId + " returned null");
//look for matching folder
var folderMediaItem = mediaRoot.Children().FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Core.Constants.Conventions.MediaTypes.Folder);
if (folderMediaItem == null)
{
//if null, create a folder
folderMediaItem = mediaService.CreateMedia(folderName, mediaRoot, Constants.Conventions.MediaTypes.Folder);
mediaService.Save(folderMediaItem);
//set the media root to the folder id so uploaded files will end there.
parentId = folderMediaItem.Id;
}
else
{
parentId = folderMediaItem.Id;
}
}
}
//get the files
foreach (var file in result.FileData)
{
@@ -439,7 +492,6 @@ namespace Umbraco.Web.Editors
if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext))
mediaType = Constants.Conventions.MediaTypes.Image;
var mediaService = ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(fileName, parentId, mediaType, Security.CurrentUser.Id);
var fileInfo = new FileInfo(file.LocalFileName);
@@ -533,8 +585,9 @@ namespace Umbraco.Web.Editors
//cannot move if the content item is not allowed at the root
if (toMove.ContentType.AllowedAsRoot == false)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(ui.Text("moveOrCopy", "notAllowedAtRoot", Security.CurrentUser)));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedAtRoot"), "");
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
}
}
else
@@ -549,15 +602,17 @@ namespace Umbraco.Web.Editors
if (parent.ContentType.AllowedContentTypes.Select(x => x.Id).ToArray()
.Any(x => x.Value == toMove.ContentType.Id) == false)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(ui.Text("moveOrCopy", "notAllowedByContentType", Security.CurrentUser)));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByContentType"), "");
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
}
// Check on paths
if ((string.Format(",{0},", parent.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1)
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse(ui.Text("moveOrCopy", "notAllowedByPath", Security.CurrentUser)));
var notificationModel = new SimpleNotificationModel();
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), "");
throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel));
}
}

View File

@@ -140,7 +140,7 @@ namespace Umbraco.Web.Editors
}
else
{
var contentItem = Services.ContentService.GetById(contentId);
var contentItem = Services.MediaService.GetById(contentId);
if (contentItem == null)
{
return Enumerable.Empty<ContentTypeBasic>();

View File

@@ -1,15 +1,17 @@
using System.ComponentModel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
[PropertyEditor(Constants.PropertyEditors.FolderBrowserAlias, "Folder Browser", "folderbrowser", HideLabel=true, Icon="icon-folder", Group="media")]
[Obsolete("This is no longer used by default, use the ListViewPropertyEditor instead")]
[PropertyEditor(Constants.PropertyEditors.FolderBrowserAlias, "(Obsolete) Folder Browser", "folderbrowser", HideLabel=true, Icon="icon-folder", Group="media")]
public class FolderBrowserPropertyEditor : PropertyEditor
{
}
}