Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7

# Conflicts:
#	src/Umbraco.Core/Models/UmbracoObjectTypes.cs
#	src/Umbraco.Web.UI.Client/bower.json
#	src/Umbraco.Web.UI.Client/gruntFile.js
#	src/Umbraco.Web.UI.Client/src/installer/_module.js
#	src/Umbraco.Web.UI.Client/test/config/karma.conf.js
This commit is contained in:
Shannon
2017-09-07 22:43:03 +10:00
31 changed files with 775 additions and 854 deletions
+1
View File
@@ -71,6 +71,7 @@ src/Umbraco.Web.UI/[Ww]eb.config
webpihash.txt
node_modules
lib-bower
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
+1 -3
View File
@@ -12,9 +12,7 @@ Umbraco is a free open source Content Management System built on the ASP.NET pla
## Building Umbraco from source ##
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt vs` in `src\Umbraco.Web.UI.Client`.
If you're interested in making changes to Belle without running Visual Studio make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md).
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`.
Note that you can always [download a nightly build](http://nightly.umbraco.org/?container=umbraco-750) so you don't have to build the code yourself.
+27 -27
View File
@@ -5,8 +5,10 @@ SETLOCAL
SET toolsFolder=%CD%\tools\
ECHO Current folder: %CD%
SET nodeFileName=node-v6.9.1-win-x86.7z
SET nodeExtractFolder=%toolsFolder%node.js.691
SET nodeVersion=6.11.2
SET nodeFileName=node-v%nodeVersion%-win-x86.7z
SET nodeExtractFolder=%toolsFolder%node.js.%nodeVersion%
SET nuGetExecutable=%CD%\tools\nuget.exe
IF NOT EXIST "%nuGetExecutable%" (
@@ -25,44 +27,42 @@ FOR /f "delims=" %%A in ('dir "%toolsFolder%7-Zip.CommandLine.*" /b') DO SET "se
MOVE "%sevenZipExePath%tools\7za.exe" "%toolsFolder%7za.exe"
IF NOT EXIST "%nodeExtractFolder%" (
ECHO Downloading http://nodejs.org/dist/v6.9.1/%nodeFileName% to %toolsFolder%%nodeFileName%
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v6.9.1/%nodeFileName%', '%toolsFolder%%nodeFileName%')"
ECHO Downloading http://nodejs.org/dist/v%nodeVersion%/%nodeFileName% to %toolsFolder%%nodeFileName%
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v%nodeVersion%/%nodeFileName%', '%toolsFolder%%nodeFileName%')"
ECHO Extracting %nodeFileName% to %nodeExtractFolder%
"%toolsFolder%\7za.exe" x "%toolsFolder%\%nodeFileName%" -o"%nodeExtractFolder%" -aos > nul
)
FOR /f "delims=" %%A in ('dir "%nodeExtractFolder%\node*" /b') DO SET "nodePath=%nodeExtractFolder%\%%A"
SET drive=%CD:~0,2%
SET nuGetFolder=%drive%\packages\
FOR /f "delims=" %%A in ('dir "%nuGetFolder%npm.*" /b') DO SET "npmPath=%nuGetFolder%%%A\"
IF [%npmPath%] == [] GOTO :installnpm
IF NOT [%npmPath%] == [] GOTO :build
:installnpm
ECHO Downloading npm
ECHO Configured packages folder: %nuGetFolder%
ECHO Installing Npm NuGet Package
"%nuGetExecutable%" install Npm -OutputDirectory %nuGetFolder% -Verbosity detailed
REM Ensures that we look for the just downloaded NPM, not whatever the user has installed on their machine
FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\"
GOTO :build
:build
ECHO Adding Npm and Node to path
ECHO Adding Node to path
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
PATH="%npmPath%";"%nodePath%";%PATH%
PATH="%nodePath%";%PATH%
ECHO Node version is:
call node -v
ECHO npm version is:
call npm -v
SET buildFolder=%CD%
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
CD %CD%\..\src\Umbraco.Web.UI.Client\
ECHO Do npm install and the grunt build of Belle
ECHO Do npm install and the gulp build of Belle
ECHO Clean npm cache
call npm cache clean --quiet
call npm install --quiet
call npm install -g grunt-cli --quiet
ECHO Installing gulp cli
call npm install -g gulp-cli --quiet
ECHO Installing bower
call npm install -g bower --quiet
call grunt build --buildversion=%release%
ECHO Doing npm install
call npm install --quiet
ECHO Executing gulp build
call gulp build --buildversion=%release%
ECHO Move back to the build folder
CD "%buildFolder%"
+10
View File
@@ -232,6 +232,16 @@ namespace Umbraco.Core
/// Guid for a Forms DataSource.
/// </summary>
public static readonly Guid LanguageGuid = new Guid(Language);
/// <summary>
/// Guid for an Identifier Reservation.
/// </summary>
public const string IdReservation = "92849B1E-3904-4713-9356-F646F87C25F4";
/// <summary>
/// Guid for an Identifier Reservation.
/// </summary>
public static readonly Guid IdReservationGuid = new Guid(IdReservation);
}
}
}
@@ -194,5 +194,13 @@ namespace Umbraco.Core.Models
[FriendlyName("DocumentBlueprint")]
[UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)]
DocumentBlueprint
/// <summary>
/// Reserved Identifier
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.IdReservation)]
[FriendlyName("Identifier Reservation")]
IdReservation
}
}
@@ -438,7 +438,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
// note:
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
// but I cannot figure out what was the point, as the node should obviously be new if
// we reach that point - removed.
// see if there's a reserved identifier for this unique id
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
var id = Database.ExecuteScalar<int>(sql);
if (id > 0)
{
nodeDto.NodeId = id;
Database.Update(nodeDto);
}
else
{
Database.Insert(nodeDto);
}
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all media data
/// The full SQL to select all media data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all media ids - used to process the properties for the media item
@@ -168,7 +168,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
//This is a tuple list identifying if the content item came from the cache or not
var content = new List<Tuple<IMedia, bool>>();
var defs = new DocumentDefinitionCollection();
@@ -184,7 +184,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (withCache)
{
var cached = IsolatedCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
if (cached != null && cached.Version == dto.VersionId)
{
@@ -307,7 +307,7 @@ namespace Umbraco.Core.Persistence.Repositories
.From<ContentXmlDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentXmlDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (contentTypeIdsA.Length > 0)
{
xmlIdsQuery.InnerJoin<ContentDto>(SqlSyntax)
@@ -318,7 +318,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType, SqlSyntax);
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
var toRemove = allXmlIds.Except(allMediaIds).ToArray();
@@ -384,7 +384,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
// note:
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
// but I cannot figure out what was the point, as the node should obviously be new if
// we reach that point - removed.
// see if there's a reserved identifier for this unique id
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
var id = Database.ExecuteScalar<int>(sql);
if (id > 0)
{
nodeDto.NodeId = id;
Database.Update(nodeDto);
}
else
{
Database.Insert(nodeDto);
}
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -666,7 +683,7 @@ namespace Umbraco.Core.Persistence.Repositories
private IMedia CreateMediaFromDto(ContentVersionDto dto, Sql docSql)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var media = MediaFactory.BuildEntity(dto, contentType);
var docDef = new DocumentDefinition(dto, contentType);
@@ -686,7 +703,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (EnsureUniqueNaming == false)
return nodeName;
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
return SimilarNodeName.GetUniqueName(names, id, nodeName);
@@ -751,5 +751,34 @@ namespace Umbraco.Core.Services
return exists;
}
}
/// <inheritdoc />
public int ReserveId(Guid key)
{
NodeDto node;
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
var sql = new Sql("SELECT * FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", key, Constants.ObjectTypes.IdReservationGuid);
node = scope.Database.SingleOrDefault<NodeDto>(sql);
if (node != null) throw new InvalidOperationException("An identifier has already been reserved for this Udi.");
node = new NodeDto
{
UniqueId = key,
Text = "RESERVED.ID",
NodeObjectType = Constants.ObjectTypes.IdReservationGuid,
CreateDate = DateTime.Now,
UserId = 0,
ParentId = -1,
Level = 1,
Path = "-1",
SortOrder = 0,
Trashed = false
};
scope.Database.Insert(node);
scope.Complete();
}
return node.NodeId;
}
}
}
+9 -1
View File
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Services
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <param name="filter"></param>
/// <returns></returns>
IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
@@ -286,5 +286,13 @@ namespace Umbraco.Core.Services
/// <param name="umbracoObjectType"><see cref="UmbracoObjectTypes"/></param>
/// <returns>Type of the entity</returns>
Type GetEntityType(UmbracoObjectTypes umbracoObjectType);
/// <summary>
/// Reserves an identifier for a key.
/// </summary>
/// <param name="key">They key.</param>
/// <returns>The identifier.</returns>
/// <remarks>When a new content or a media is saved with the key, it will have the reserved identifier.</remarks>
int ReserveId(Guid key);
}
}
+8
View File
@@ -46,6 +46,10 @@ namespace Umbraco.Core.Services
if (val == null) return Attempt<int>.Fail();
// cache reservations, when something is saved this cache is cleared anyways
//if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
// Attempt.Succeed(val.Value);
try
{
_locker.EnterWriteLock();
@@ -95,6 +99,10 @@ namespace Umbraco.Core.Services
if (val == null) return Attempt<Guid>.Fail();
// cache reservations, when something is saved this cache is cleared anyways
//if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
// Attempt.Succeed(val.Value);
try
{
_locker.EnterWriteLock();
+2 -2
View File
@@ -39,7 +39,7 @@
"globals": {
"angular": false,
"_": false,
"$", false,
"$": false,
"tinymce": false,
"tinyMCE": false,
"FileReader": false,
@@ -47,7 +47,7 @@
"window": false,
"LazyLoad": false,
"ActiveXObject": false,
"Bloodhound", false
"Bloodhound": false
}
}
-97
View File
@@ -1,97 +0,0 @@
#Belle
Umbraco 7 UI, codename "Belle" Built on AngularJS, bower, Lazyload.js and Twitter Bootstrap
##Introduction
Slides from the initial demonstration of Belle done at the Umbraco DK Fest can be found here:
http://rawgithub.com/umbraco/Umbraco.Web.Ui.Client/build/master/Presentation/index.html
##Running the site with mocked data
This won't require any database or setup, as everything is running through node. All you have to do is install
node and grunt on either windows or OSX and the entire setup is ready for you.
###Install node.js
We need node to run tests and automated less compiling and other automated tasks. go to http://nodejs.org. Node.js is a powerfull javascript engine, which allows us to run all our tests and tasks written in javascript locally.
*note:* On windows you might need to restart explorer.exe to register node.
###Install dependencies
Next we need to install all the required packages. This is done with the package tool, included with node.js, open /src/Umbraco.Web.UI.Client in cmd.exe or osx terminal and run the command:
npm install
this will fetch all needed packages to your local machine.
###Install grunt globally
Grunt is a task runner for node.js, and we use it for all automated tasks in the build process. For convenience we need to install it globally on your machine, so it can be used directly in cmd.exe or the terminal.
So run the command:
npm install grunt-cli -g
*note:* On windows you might need to restart explorer.exe to register the grunt cmd.
*note:* On OSX you might need to run:
sudo npm install grunt-cli -g
Now that you have node and grunt installed, you can open `/src/Umbraco.Web.UI.Client` in either `cmd.exe` or terminal and run:
grunt dev
This will build the site, merge less files, run tests and create the /Build folder, and finally open the site in your
browser.
##Limitations
The current prototype simply uses in-memory storage, so no database dependencies. It is aimed at showing UI, not a complete functional client-server setup.
##Project Structure
All project files are located in /src/Umbraco.Web.UI.Client which only contains client-side files, everything
related to asp.net are in /src/Umbraco.Web.UI
after building Belle files are located in /build/belle, with all files following AngularJs
conventions:
###Folders
- */Umbraco.Web.Ui.Client/build/lib:* Dependencies
- */Umbraco.Web.Ui.Client/build/js:* Application javascript files
- */Umbraco.Web.Ui.Client/build/views/common/:* Main application views
- */Umbraco.Web.Ui.Client/build/views/[sectioname]/pagename Editors html
- */Umbraco.Web.Ui.Client/build/views/propertyeditors:* Property Editors html
###Files
- */Umbraco.Web.Ui.Client/build/js/app.js:* Main umbraco application / modules
- */Umbraco.Web.Ui.Client/build/js/loader.js:* lazyload configuration for dependencies
- */Umbraco.Web.Ui.Client/build/js/routes.js:* Application routes
- */Umbraco.Web.Ui.Client/build/js/umbraco.controllers.js:* Application controllers
- */Umbraco.Web.Ui.Client/build/js/umbraco.services.js:* Application services
- */Umbraco.Web.Ui.Client/build/js/umbraco.filters.js:* Application filters
- */Umbraco.Web.Ui.Client/build/js/umbraco.directives.js:* Application directives
- */Umbraco.Web.Ui.Client/build/js/umbraco.resources.js:* Application resources, like content, media, users, members etc
- */Umbraco.Web.Ui.Client/build/js/umbraco.mocks.js:* Fake Application resources, for running the app without a server
##Getting started
The current app is built, following conventions from angularJs and bootstrap. To get started with the applicaton you will need to atleast know the basics of these frameworks
###AngularJS
- Excellent introduction videos on http://www.egghead.io/
- Official guide at: http://docs.angularjs.org/guide/
###Require.js
- Introduction: http://javascriptplayground.com/blog/2012/07/requirejs-amd-tutorial-introduction
- Require.js website: http://requirejs.org/
+49
View File
@@ -30,6 +30,55 @@
"angular-local-storage": "~0.2.3",
"moment": "~2.10.3",
"ace-builds": "^1.2.3",
"font-awesome": "~4.2"
"clipboard": "1.7.1"
},
"install": {
"path": "lib-bower",
"ignore": [
"font-awesome",
"angular",
"bootstrap",
"codemirror"
],
"sources": {
"moment": "bower_components/moment/min/moment-with-locales.js",
"underscore": [
"bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map"
],
"jquery": [
"bower_components/jquery/dist/jquery.min.js",
"bower_components/jquery/dist/jquery.min.map"
],
"angular-dynamic-locale": [
"bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js",
"bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map"
],
"angular-local-storage": [
"bower_components/angular-local-storage/dist/angular-local-storage.min.js",
"bower_components/angular-local-storage/dist/angular-local-storage.min.js.map"
],
"tinymce": [
"bower_components/tinymce/tinymce.min.js"
],
"typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js",
"rgrove-lazyload":"bower_components/rgrove-lazyload/lazyload.js",
"ng-file-upload":"bower_components/ng-file-upload/ng-file-upload.min.js",
"jquery-ui":"bower_components/jquery-ui/jquery-ui.min.js",
"jquery-migrate":"bower_components/jquery-migrate/jquery-migrate.min.js"
}
}
}
-601
View File
@@ -1,601 +0,0 @@
module.exports = function (grunt) {
// Default task.
grunt.registerTask('default', ['jshint:dev', 'build', 'karma:unit']);
grunt.registerTask('dev', ['jshint:dev', 'build-dev', 'webserver', 'open:dev', 'watch']);
grunt.registerTask('docserve', ['docs:api', 'connect:docserver', 'open:docs', 'watch:docs']);
grunt.registerTask('vs', ['jshint:dev', 'build-dev', 'watch']);
//TODO: Too much watching, this brings windows to it's knees when in dev mode
//run by the watch task
grunt.registerTask('watch-js', ['jshint:dev', 'concat', 'copy:app', 'copy:mocks', 'copy:canvasdesigner', 'copy:vs', 'karma:unit']);
grunt.registerTask('watch-less', ['recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'copy:canvasdesigner', 'copy:assets', 'copy:vs']);
grunt.registerTask('watch-html', ['copy:views', 'copy:vs']);
grunt.registerTask('watch-installer', ['concat:install', 'concat:installJs', 'copy:installer', 'copy:vs']);
grunt.registerTask('watch-canvasdesigner', ['copy:canvasdesigner', 'concat:canvasdesignerJs', 'copy:vs']);
grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']);
//triggered from grunt
grunt.registerTask('build', ['concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'bower-install-simple', 'bower', 'copy', 'clean:post']);
//triggered from grunt dev vs or grunt vs
grunt.registerTask('build-dev', ['clean:pre', 'concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'postcss', 'bower-install-simple', 'bower', 'copy']);
//utillity tasks
grunt.registerTask('docs', ['ngdocs']);
grunt.registerTask('webserver', ['connect:devserver']);
// Print a timestamp (useful for when watching)
grunt.registerTask('timestamp', function () {
grunt.log.subhead(Date());
});
// Project configuration.
grunt.initConfig({
buildVersion: grunt.option('buildversion') || '7',
connect: {
devserver: {
options: {
port: 9990,
hostname: '0.0.0.0',
base: './build',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.favicon('images/favicon.ico'),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
testserver: {},
docserver: {
options: {
port: 8880,
hostname: '0.0.0.0',
base: './docs/api',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
},
open: {
dev: {
path: 'http://localhost:9990/belle/'
},
docs: {
path: 'http://localhost:8880/index.html'
}
},
distdir: 'build/belle',
vsdir: '../Umbraco.Web.UI/umbraco',
pkg: grunt.file.readJSON('package.json'),
banner:
'/*! <%= pkg.title || pkg.name %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
src: {
js: ['src/**/*.js', 'src/*.js'],
common: ['src/common/**/*.js'],
controllers: ['src/**/*.controller.js'],
specs: ['test/**/*.spec.js'],
scenarios: ['test/**/*.scenario.js'],
samples: ['sample files/*.js'],
html: ['src/index.html', 'src/install.html'],
everything: ['src/**/*.*', 'test/**/*.*', 'docs/**/*.*'],
tpl: {
app: ['src/views/**/*.html'],
common: ['src/common/**/*.tpl.html']
},
less: ['src/less/belle.less'], // recess:build doesn't accept ** in its file patterns
prod: ['<%= distdir %>/js/*.js']
},
clean: {
pre: ['<%= distdir %>/*'],
post: ['<%= distdir %>/js/*.dev.js']
},
copy: {
assets: {
files: [{ dest: '<%= distdir %>/assets', src: '**', expand: true, cwd: 'src/assets/' }]
},
config: {
files: [{ dest: '<%= distdir %>/../config', src: '**', expand: true, cwd: 'src/config/' }]
},
installer: {
files: [{ dest: '<%= distdir %>/views/install', src: '**/*.html', expand: true, cwd: 'src/installer/steps' }]
},
canvasdesigner: {
files: [
{ dest: '<%= distdir %>/preview', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner' },
{ dest: '<%= distdir %>/preview/editors', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/assets/less', src: '**/*.less', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.config.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.palettes.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.front.js', expand: true, cwd: 'src/canvasdesigner' }
]
},
vendor: {
files: [{ dest: '<%= distdir %>/lib', src: '**', expand: true, cwd: 'lib/' }]
},
views: {
files: [{ dest: '<%= distdir %>/views', src: ['**/*.*', '!**/*.controller.js'], expand: true, cwd: 'src/views' }]
},
app: {
files: [
{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/' }
]
},
mocks: {
files: [{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/common/mocks/' }]
},
vs: {
files: [
//everything except the index.html root file!
//then we need to figure out how to not copy all the test stuff either!?
{ dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' },
{ dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' },
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' },
{ dest: '<%= vsdir %>/preview', src: '**', expand: true, cwd: '<%= distdir %>/preview' },
{ dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' }
]
}
},
karma: {
unit: { configFile: 'test/config/karma.conf.js', keepalive: true },
e2e: { configFile: 'test/config/e2e.js', keepalive: true },
watch: { configFile: 'test/config/unit.js', singleRun: false, autoWatch: true, keepalive: true }
},
concat: {
index: {
src: ['src/index.html'],
dest: '<%= distdir %>/index.html',
options: {
process: true
}
},
install: {
src: ['src/installer/installer.html'],
dest: '<%= distdir %>/installer.html',
options: {
process: true
}
},
installJs: {
src: ['src/installer/**/*.js'],
dest: '<%= distdir %>/js/umbraco.installer.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
canvasdesignerJs: {
src: ['src/canvasdesigner/canvasdesigner.global.js', 'src/canvasdesigner/canvasdesigner.controller.js', 'src/canvasdesigner/editors/*.js', 'src/canvasdesigner/lib/*.js'],
dest: '<%= distdir %>/js/canvasdesigner.panel.js'
},
controllers: {
src: ['src/controllers/**/*.controller.js', 'src/views/**/*.controller.js'],
dest: '<%= distdir %>/js/umbraco.controllers.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
services: {
src: ['src/common/services/*.js'],
dest: '<%= distdir %>/js/umbraco.services.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
security: {
src: ['src/common/security/*.js'],
dest: '<%= distdir %>/js/umbraco.security.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
resources: {
src: ['src/common/resources/*.js'],
dest: '<%= distdir %>/js/umbraco.resources.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
testing: {
src: ['src/common/mocks/*/*.js'],
dest: '<%= distdir %>/js/umbraco.testing.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
directives: {
src: ['src/common/directives/**/*.js'],
dest: '<%= distdir %>/js/umbraco.directives.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
filters: {
src: ['src/common/filters/*.js'],
dest: '<%= distdir %>/js/umbraco.filters.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
}
},
uglify: {
options: {
mangle: true
},
combine: {
files: {
'<%= distdir %>/js/umbraco.min.js': ['<%= distdir %>/js/umbraco.*.js']
}
}
},
recess: {
build: {
files: {
'<%= distdir %>/assets/css/<%= pkg.name %>.css':
['<%= src.less %>']
},
options: {
compile: true,
compress: true
}
},
nonodes: {
files: {
'<%= distdir %>/assets/css/nonodes.style.min.css':
['src/less/pages/nonodes.less']
},
options: {
compile: true,
compress: true
}
},
installer: {
files: {
'<%= distdir %>/assets/css/installer.css':
['src/less/installer.less']
},
options: {
compile: true,
compress: true
}
},
canvasdesigner: {
files: {
'<%= distdir %>/assets/css/canvasdesigner.css':
['src/less/canvas-designer.less', 'src/less/helveticons.less']
},
options: {
compile: true,
compress: true
}
}
},
postcss: {
options: {
processors: [
// add vendor prefixes
require('autoprefixer-core')({
browsers: 'last 2 versions'
})
]
},
dist: {
src: '<%= distdir %>/assets/css/<%= pkg.name %>.css'
}
},
ngTemplateCache: {
views: {
files: {
'<%= distdir %>/js/umbraco.views.js': 'src/views/**/*.html'
},
options: {
trim: 'src/',
module: 'umbraco.views'
}
}
},
watch: {
docs: {
files: ['docs/src/**/*.md'],
tasks: ['watch-docs', 'timestamp']
},
css: {
files: 'src/**/*.less',
tasks: ['watch-less', 'timestamp'],
options: {
livereload: true,
},
},
js: {
files: ['src/**/*.js', 'src/*.js'],
tasks: ['watch-js', 'timestamp'],
},
test: {
files: ['test/**/*.js'],
tasks: ['watch-test', 'timestamp'],
},
installer: {
files: ['src/installer/**/*.*'],
tasks: ['watch-installer', 'timestamp'],
},
canvasdesigner: {
files: ['src/canvasdesigner/**/*.*'],
tasks: ['watch-canvasdesigner', 'timestamp'],
},
html: {
files: ['src/views/**/*.html', 'src/*.html'],
tasks: ['watch-html', 'timestamp']
},
options: {
interval: 500
}
},
ngdocs: {
options: {
dest: 'docs/api',
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
html5Mode: false,
styles: [
'docs/umb-docs.css'
],
image: "https://our.umbraco.org/assets/images/logo.svg"
},
api: {
src: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
title: 'API Documentation'
},
tutorials: {
src: [],
title: ''
}
},
eslint:{
src: ['<%= src.common %>','<%= src.controllers %>'],
options: {quiet: true}
},
jshint: {
dev: {
files: {
src: ['<%= src.common %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globals: {}
}
},
build: {
files: {
src: ['<%= src.prod %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globalstrict: true,
globals: { $: false, jQuery: false, define: false, require: false, window: false }
}
}
},
bower: {
dev: {
dest: '<%= distdir %>/lib',
options: {
expand: true,
ignorePackages: ['bootstrap'],
packageSpecific: {
'moment': {
keepExpandedHierarchy: false,
files: ['min/moment-with-locales.js']
},
'typeahead.js': {
keepExpandedHierarchy: false,
files: ['dist/typeahead.bundle.min.js']
},
'underscore': {
files: ['underscore-min.js', 'underscore-min.map']
},
'rgrove-lazyload': {
files: ['lazyload.js']
},
'bootstrap-social': {
files: ['bootstrap-social.css']
},
'font-awesome': {
files: ['css/font-awesome.min.css', 'fonts/*']
},
"jquery": {
keepExpandedHierarchy: false,
files: ['dist/jquery.min.js', 'dist/jquery.min.map']
},
'jquery-ui': {
keepExpandedHierarchy: false,
files: ['jquery-ui.min.js']
},
'jquery-migrate': {
keepExpandedHierarchy: false,
files: ['jquery-migrate.min.js']
},
'tinymce': {
files: ['plugins/**', 'themes/**', 'tinymce.min.js']
},
'angular-dynamic-locale': {
files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map']
},
'ng-file-upload': {
keepExpandedHierarchy: false,
files: ['ng-file-upload.min.js']
},
'angular-local-storage': {
keepExpandedHierarchy: false,
files: ['dist/angular-local-storage.min.js']
},
'codemirror': {
files: [
'lib/codemirror.js',
'lib/codemirror.css',
'mode/css/*',
'mode/javascript/*',
'mode/xml/*',
'mode/htmlmixed/*',
'addon/search/*',
'addon/edit/*',
'addon/selection/*',
'addon/dialog/*'
]
},
'ace-builds': {
files: [
'src-min-noconflict/ace.js',
'src-min-noconflict/ext-language_tools.js',
'src-min-noconflict/ext-searchbox.js',
'src-min-noconflict/ext-settings_menu.js',
'src-min-noconflict/snippets/text.js',
'src-min-noconflict/snippets/javascript.js',
'src-min-noconflict/theme-chrome.js',
'src-min-noconflict/mode-razor.js',
'src-min-noconflict/mode-javascript.js',
'src-min-noconflict/worker-javascript.js',
]
},
'clipboard': {
keepExpandedHierarchy: false,
files: ['dist/clipboard.min.js']
}
}
}
},
options: {
expand: true
}
},
"bower-install-simple": {
options: {
color: true
},
"dev": {}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-open');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks("grunt-bower-install-simple");
grunt.loadNpmTasks('grunt-bower');
grunt.loadNpmTasks('grunt-ngdocs');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-hustler');
};
+389
View File
@@ -0,0 +1,389 @@
var gulp = require('gulp');
var watch = require('gulp-watch');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var wrap = require("gulp-wrap-js");
var sort = require('gulp-sort');
var connect = require('gulp-connect');
var open = require('gulp-open');
var runSequence = require('run-sequence');
var _ = require('lodash');
var MergeStream = require('merge-stream');
//Less + css
var postcss = require('gulp-postcss');
var less = require('gulp-less');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');
// Documentation
var gulpDocs = require('gulp-ngdocs');
// Testing
var karmaServer = require('karma').Server;
/***************************************************************
Helper functions
***************************************************************/
function processJs(files, out) {
return gulp.src(files)
.pipe(sort())
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
.pipe(gulp.dest(root + targets.js));
console.log(out + " compiled");
}
function processLess(files, out) {
var processors = [
autoprefixer,
cssnano
];
return gulp.src(files)
.pipe(less())
.pipe(postcss(processors))
.pipe(rename(out))
.pipe(gulp.dest(root + targets.css));
console.log(out + " compiled");
}
/***************************************************************
Paths and destinations
Each group is iterated automatically in the setup tasks below
***************************************************************/
var sources = {
//less files used by backoffice and preview
//processed in the less task
less: {
installer: { files: ["src/less/installer.less"], out: "installer.css" },
nonodes: { files: ["src/less/pages/nonodes.less"], out: "nonodes.style.min.css"},
preview: { files: ['src/less/canvas-designer.less', 'src/less/helveticons.less'], out: "canvasdesigner.css" },
umbraco: { files: ["src/less/belle.less"], out: "umbraco.css" }
},
//js files for backoffie
//processed in the js task
js: {
preview: { files: ["src/canvasdesigner/**/*.js"], out: "umbraco.canvasdesigner.js" },
installer: { files: ["src/installer/**/*.js"], out: "umbraco.installer.js" },
controllers: { files: ["src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
directives: { files: ["src/common/directives/**/*.js"], out: "umbraco.directives.js" },
filters: { files: ["src/common/filters/**/*.js"], out: "umbraco.filters.js" },
resources: { files: ["src/common/resources/**/*.js"], out: "umbraco.resources.js" },
services: { files: ["src/common/services/**/*.js"], out: "umbraco.services.js" },
security: { files: ["src/common/security/**/*.js"], out: "umbraco.security.js" }
},
//selectors for copying all views into the build
//processed in the views task
views:{
umbraco: {files: ["src/views/**/*html"], folder: ""},
preview: { files: ["src/canvasdesigner/**/*.html"], folder: "../preview"},
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
},
//globs for file-watching
globs:{
views: "./src/views/**/*.html",
less: "./src/less/**/*.less",
js: "./src/*.js",
lib: "./lib/**/*",
bower: "./lib-bower/**/*",
assets: "./src/assets/**"
}
};
var root = "../Umbraco.Web.UI/Umbraco/";
var targets = {
js: "js/",
lib: "lib/",
views: "views/",
css: "assets/css/",
assets: "assets/"
};
/**************************
* Main tasks for the project to prepare backoffice files
**************************/
// Build - build the files ready for production
gulp.task('build', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "test:unit", cb);
});
// Dev - build the files ready for development and start watchers
gulp.task('dev', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});
// Docserve - build and open the back office documentation
gulp.task('docserve', function(cb) {
runSequence('docs', 'connect:docs', 'open:docs', cb);
});
/**************************
* Task processes and copies all dependencies, either installed by bower, npm or stored locally in the project
**************************/
gulp.task('dependencies', function () {
//bower component specific copy rules
//this is to patch the sometimes wonky rules these libs are distrbuted under
//as we do multiple things in this task, we merge the multiple streams
var stream = new MergeStream();
//Tinymce
stream.add(
gulp.src(["./bower_components/tinymce/plugins/**",
"./bower_components/tinymce/themes/**"],
{ base: "./bower_components/tinymce/" })
.pipe(gulp.dest(root + targets.lib + "/tinymce"))
);
//font-awesome
stream.add(
gulp.src(["./bower_components/font-awesome/fonts/*",
"./bower_components/font-awesome/css/font-awesome.min.css"],
{ base: "./bower_components/font-awesome/" })
.pipe(gulp.dest(root + targets.lib + "/font-awesome"))
);
// ace Editor
stream.add(
gulp.src(["bower_components/ace-builds/src-min-noconflict/ace.js",
"bower_components/ace-builds/src-min-noconflict/ext-language_tools.js",
"bower_components/ace-builds/src-min-noconflict/ext-searchbox.js",
"bower_components/ace-builds/src-min-noconflict/ext-settings_menu.js",
"bower_components/ace-builds/src-min-noconflict/snippets/text.js",
"bower_components/ace-builds/src-min-noconflict/snippets/javascript.js",
"bower_components/ace-builds/src-min-noconflict/theme-chrome.js",
"bower_components/ace-builds/src-min-noconflict/mode-razor.js",
"bower_components/ace-builds/src-min-noconflict/mode-javascript.js",
"bower_components/ace-builds/src-min-noconflict/worker-javascript.js"],
{ base: "./bower_components/ace-builds/" })
.pipe(gulp.dest(root + targets.lib + "/ace-builds"))
);
// code mirror
stream.add(
gulp.src([
"bower_components/codemirror/lib/codemirror.js",
"bower_components/codemirror/lib/codemirror.css",
"bower_components/codemirror/mode/css/*",
"bower_components/codemirror/mode/javascript/*",
"bower_components/codemirror/mode/xml/*",
"bower_components/codemirror/mode/htmlmixed/*",
"bower_components/codemirror/addon/search/*",
"bower_components/codemirror/addon/edit/*",
"bower_components/codemirror/addon/selection/*",
"bower_components/codemirror/addon/dialog/*"],
{ base: "./bower_components/codemirror/" })
.pipe(gulp.dest(root + targets.lib + "/codemirror"))
);
//copy over libs which are not on bower (/lib) and
//libraries that have been managed by bower-installer (/lib-bower)
stream.add(
gulp.src(sources.globs.lib)
.pipe(gulp.dest(root + targets.lib))
);
stream.add(
gulp.src(sources.globs.bower)
.pipe(gulp.dest(root + targets.lib))
);
//Copies all static assets into /root / assets folder
//css, fonts and image files
stream.add(
gulp.src(sources.globs.assets)
.pipe(gulp.dest(root + targets.assets))
);
// Copies all the less files related to the preview into their folder
//these are not pre-processed as preview has its own less combiler client side
stream.add(
gulp.src("src/canvasdesigner/editors/*.less")
.pipe(gulp.dest(root + targets.assets + "/less"))
);
return stream;
});
/**************************
* Copies all angular JS files into their seperate umbraco.*.js file
**************************/
gulp.task('js', function () {
//we run multiple streams, so merge them all together
var stream = new MergeStream();
stream.add(
gulp.src(sources.globs.js)
.pipe(gulp.dest(root + targets.js))
);
_.forEach(sources.js, function (group) {
stream.add (processJs(group.files, group.out) );
});
return stream;
});
gulp.task('less', function () {
var stream = new MergeStream();
_.forEach(sources.less, function (group) {
stream.add( processLess(group.files, group.out) );
});
return stream;
});
gulp.task('views', function () {
var stream = new MergeStream();
_.forEach(sources.views, function (group) {
console.log("copying " + group.files + " to " + root + targets.views + group.folder)
stream.add (
gulp.src(group.files)
.pipe( gulp.dest(root + targets.views + group.folder) )
);
});
return stream;
});
gulp.task('watch', function () {
var stream = new MergeStream();
var watchInterval = 500;
//Setup a watcher for all groups of javascript files
_.forEach(sources.js, function (group) {
if(group.watch !== false){
stream.add(
watch(group.files, { ignoreInitial: true, interval: watchInterval }, function (file) {
console.info(file.path + " has changed, added to: " + group.out);
processJs(group.files, group.out);
})
);
}
});
stream.add(
//watch all less files and trigger the less task
watch(sources.globs.less, { ignoreInitial: true, interval: watchInterval }, function () {
gulp.run(['less']);
})
);
//watch all views - copy single file changes
stream.add(
watch(sources.globs.views, { interval: watchInterval })
.pipe(gulp.dest(root + targets.views))
);
//watch all app js files that will not be merged - copy single file changes
stream.add(
watch(sources.globs.js, { interval: watchInterval })
.pipe(gulp.dest(root + targets.js))
);
return stream;
});
/**************************
* Build Backoffice UI API documentation
**************************/
gulp.task('docs', [], function (cb) {
var options = {
html5Mode: false,
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
dest: 'docs/api',
styles: ['docs/umb-docs.css'],
image: "https://our.umbraco.org/assets/images/logo.svg"
}
return gulpDocs.sections({
api: {
glob: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
api: true,
title: 'API Documentation'
}
})
.pipe(gulpDocs.process(options))
.pipe(gulp.dest('docs/api'));
cb();
});
gulp.task('connect:docs', function (cb) {
connect.server({
root: 'docs/api',
livereload: true,
fallback: 'docs/api/index.html',
port: 8880
});
cb();
});
gulp.task('open:docs', function (cb) {
var options = {
uri: 'http://localhost:8880/index.html'
};
gulp.src(__filename)
.pipe(open(options));
cb();
});
/**************************
* Build tests
**************************/
// Karma test
gulp.task('test:unit', function() {
new karmaServer({
configFile: __dirname + "/test/config/karma.conf.js",
keepalive: true
})
.start();
});
gulp.task('test:e2e', function() {
new karmaServer({
configFile: __dirname + "/test/config/e2e.js",
keepalive: true
})
.start();
});
@@ -193,10 +193,10 @@
// Reset container width
// Required here as we reset the width earlier on and the grid mixins don't override early enough
.navbar-static-top .container,
.navbar-static-top .container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
#grid > .core > .span(@gridColumns);
.navbar-fixed-bottom .container {
width: (@gridColumnWidth * @gridColumns) + (@gridGutterWidth * (@gridColumns - 1));
}
// Fixed to top
+29 -32
View File
@@ -6,7 +6,7 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "git@github.com:umbraco/umbraco-cms.git"
"url": "https://github.com/umbraco/Umbraco-CMS.git"
},
"bugs": {
"url": "https://issues.umbraco.org"
@@ -14,38 +14,35 @@
"engines": {
"node": ">= 0.8.4"
},
"scripts": {
"install": "bower-installer",
"test": "karma start test/config/karma.conf.js --singlerun",
"build": "gulp"
},
"dependencies": {},
"devDependencies": {
"autoprefixer-core": "~5.2.1",
"bower": "^1.4.1",
"eslint": "^0.23.0",
"eslint-plugin-angular": "0.0.13",
"grunt": "~0.4.0",
"grunt-bower": "^0.19.0",
"grunt-bower-install-simple": "^1.1.3",
"grunt-contrib-clean": "~0.4.0",
"grunt-contrib-concat": "~0.1.3",
"grunt-contrib-connect": "~0.3.0",
"grunt-contrib-copy": "~0.7.0",
"grunt-contrib-jshint": "~0.2.0",
"grunt-contrib-uglify": "~0.1.1",
"grunt-contrib-watch": "~0.3.1",
"grunt-eslint": "^15.0.0",
"grunt-html2js": "~0.1.0",
"grunt-hustler": "^4.0.6",
"grunt-karma": "~0.5",
"grunt-ngdocs": "~0.1.2",
"grunt-open": "~0.2.0",
"grunt-postcss": "~0.6.0",
"grunt-recess": "~0.3",
"karma": "~0.9",
"karma-chrome-launcher": "0.0.2",
"karma-coffee-preprocessor": "0.0.1",
"karma-firefox-launcher": "0.0.2",
"karma-jasmine": "0.0.1",
"karma-phantomjs-launcher": "0.0.2",
"karma-requirejs": "0.0.1",
"karma-script-launcher": "0.0.1",
"phantomjs": "~1.9.1-0"
"autoprefixer": "^6.5.0",
"bower-installer": "^1.2.0",
"cssnano": "^3.7.6",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-connect": "^5.0.0",
"gulp-less": "^3.1.0",
"gulp-ngdocs": "^0.3.0",
"gulp-open": "^2.0.0",
"gulp-postcss": "^6.2.0",
"gulp-rename": "^1.2.2",
"gulp-sort": "^2.0.0",
"gulp-watch": "^4.3.10",
"gulp-wrap": "^0.13.0",
"gulp-wrap-js": "^0.4.1",
"jasmine-core": "^2.5.2",
"karma": "^1.7.0",
"karma-jasmine": "^1.1.0",
"karma-phantomjs-launcher": "^1.0.4",
"less": "^2.6.1",
"lodash": "^4.16.3",
"merge-stream": "^1.0.1",
"run-sequence": "^2.1.0"
}
}
@@ -11,8 +11,7 @@ LazyLoad.js([
'../js/umbraco.security.js',
'../ServerVariables',
'../lib/spectrum/spectrum.js',
'../js/canvasdesigner.panel.js',
'../js/umbraco.canvasdesigner.js',
], function () {
jQuery(document).ready(function () {
angular.bootstrap(document, ['Umbraco.canvasdesigner']);
@@ -87,6 +87,7 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
editor.addButton('umbmediapicker', {
icon: 'custom icon-picture',
tooltip: 'Media Picker',
stateSelector: 'img',
onclick: function () {
var selectedElm = editor.selection.getNode(),
@@ -1 +1 @@
angular.module("umbraco.install", ["umbraco.directives"]);
angular.module("umbraco.install", ["umbraco.directives"]);
@@ -356,7 +356,7 @@
.umb-panel-header-content-wrapper {
display: flex;
flex-direction: column;
height: 100px;
height: 99px;
padding: 0 20px;
}
@@ -1,5 +1,7 @@
module.exports = function(karma) {
karma.configure({
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '../..',
@@ -7,31 +9,23 @@ module.exports = function(karma) {
// list of files / patterns to load in the browser
files: [
'lib/../build/belle/lib/jquery/jquery.min.js',
//libraries
'lib-bower/jquery/jquery.min.js',
'lib/angular/1.1.5/angular.js',
'lib/angular/1.1.5/angular-cookies.min.js',
'lib/angular/1.1.5/angular-mocks.js',
'lib/angular/angular-ui-sortable.js',
/*
For angular 1.2:
'lib/angular/1.2/angular.js',
'lib/angular/1.2/angular-route.min.js',
'lib/angular/1.2/angular-touch.min.js',
'lib/angular/1.2/angular-cookies.min.js',
'lib/angular/1.2/angular-animate.min.js',
'lib/angular/1.2/angular-mocks.js',*/
'lib/../build/belle/lib/underscore/underscore-min.js',
'lib/../build/belle/lib/moment/moment-with-locales.js',
'lib-bower/underscore/underscore-min.js',
'lib-bower/moment/moment-with-locales.js',
'lib/umbraco/Extensions.js',
'lib/../build/belle/lib/rgrove-lazyload/lazyload.js',
'lib/../build/belle/lib/angular-local-storage/angular-local-storage.min.js',
'lib-bower/rgrove-lazyload/lazyload.js',
'lib-bower//angular-local-storage/angular-local-storage.min.js',
//app bootstrap and loader
'test/config/app.unit.js',
'src/common/mocks/umbraco.servervariables.js',
//application files
'src/common/directives/**/*.js',
'src/common/filters/*.js',
'src/common/services/*.js',
@@ -39,8 +33,9 @@ module.exports = function(karma) {
'src/common/resources/*.js',
'src/common/mocks/**/*.js',
'src/views/**/*.controller.js',
'test/unit/**/*.spec.js',
{pattern: 'lib/**/*.js', watched: true, served: true, included: false}
//tests
'test/unit/**/*.spec.js'
],
// list of files to exclude
@@ -66,7 +61,7 @@ module.exports = function(karma) {
// level of logging
// possible values: karma.LOG_DISABLE || karma.LOG_ERROR || karma.LOG_WARN || karma.LOG_INFO || karma.LOG_DEBUG
// CLI --log-level debug
logLevel: karma.LOG_INFO,
logLevel: config.LOG_WARN,
// enable / disable watching file and executing tests whenever any file changes
// CLI --auto-watch --no-auto-watch
@@ -2,10 +2,6 @@
@using Umbraco.Web.Templates
@using Newtonsoft.Json.Linq
@*
Razor helpers located at the bottom of this file
*@
@if (Model != null && Model.sections != null)
{
var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1;
@@ -64,29 +60,21 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -2,10 +2,6 @@
@using Umbraco.Web.Templates
@using Newtonsoft.Json.Linq
@*
Razor helpers located at the bottom of this file
*@
@if (Model != null && Model.sections != null)
{
var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1;
@@ -64,29 +60,21 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -1,4 +1,5 @@
@model dynamic
@using Umbraco.Web.Templates
@functions {
public static string EditorView(dynamic contentItem)
@@ -1,2 +1,7 @@
@model dynamic
@Html.Raw(Model.value)
@using Umbraco.Web.Templates
<div class="video-wrapper">
@Html.Raw(Model.value)
</div>
@@ -1,4 +1,6 @@
@inherits UmbracoViewPage<dynamic>
@using Umbraco.Web.Templates
@if (Model.value != null)
{
@@ -1,4 +1,5 @@
@model dynamic
@using Umbraco.Web.Templates
@if (Model.value != null)
{
@@ -13,7 +14,7 @@
}
}
<img src="@url" alt="@Model.value.altText">
<img src="@url" alt="@Model.value.caption">
if (Model.value.caption != null)
{
@@ -4,9 +4,8 @@
@if (Model.editor.config.markup != null)
{
string markup = Model.editor.config.markup.ToString();
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString())));
markup = markup.Replace("#value#", Model.value.ToString());
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
<text>
@@ -1,4 +1,4 @@
[
[
{
"name": "Rich text editor",
"alias": "rte",
@@ -11,6 +11,40 @@
"view": "media",
"icon": "icon-picture"
},
{
"name": "Image wide",
"alias": "media_wide",
"view": "media",
"render": "/App_Plugins/Grid/Editors/Render/media_wide.cshtml",
"icon": "icon-picture"
},
{
"name": "Image wide cropped",
"alias": "media_wide_cropped",
"view": "media",
"render": "media",
"icon": "icon-picture",
"config": {
"size": {
"width": 1920,
"height": 700
}
}
},
{
"name": "Image rounded",
"alias": "media_round",
"view": "media",
"render": "/App_Plugins/Grid/Editors/Render/media_round.cshtml",
"icon": "icon-picture"
},
{
"name": "Image w/ text right",
"alias": "media_text_right",
"view": "/App_Plugins/Grid/Editors/Views/media_with_description.html",
"render": "/App_Plugins/Grid/Editors/Render/media_text_right.cshtml",
"icon": "icon-picture"
},
{
"name": "Macro",
"alias": "macro",
@@ -21,8 +55,29 @@
"name": "Embed",
"alias": "embed",
"view": "embed",
"render": "/App_Plugins/Grid/Editors/Render/embed_videowrapper.cshtml",
"icon": "icon-movie-alt"
},
{
"name": "Banner Headline",
"alias": "banner_headline",
"view": "textstring",
"icon": "icon-coin",
"config": {
"style": "font-size: 36px; line-height: 45px; font-weight: bold; text-align:center",
"markup": "<h1 style='font-size:62px;text-align:center'>#value#</h1>"
}
},
{
"name": "Banner Tagline",
"alias": "banner_tagline",
"view": "textstring",
"icon": "icon-coin",
"config": {
"style": "font-size: 25px; line-height: 35px; font-weight: normal; text-align:center",
"markup": "<h2 style='font-weight: 100; font-size: 40px;text-align:center'>#value#</h2>"
}
},
{
"name": "Headline",
"alias": "headline",
@@ -32,6 +87,36 @@
"style": "font-size: 36px; line-height: 45px; font-weight: bold",
"markup": "<h1>#value#</h1>"
}
},
{
"name": "Headline centered",
"alias": "headline_centered",
"view": "textstring",
"icon": "icon-coin",
"config": {
"style": "font-size: 30px; line-height: 45px; font-weight: bold; text-align:center;",
"markup": "<h1 style='text-align:center;'>#value#</h1>"
}
},
{
"name": "Abstract",
"alias": "abstract",
"view": "textstring",
"icon": "icon-coin",
"config": {
"style": "font-size: 16px; line-height: 20px; font-weight: bold;",
"markup": "<h3>#value#</h3>"
}
},
{
"name": "Paragraph",
"alias": "paragraph",
"view": "textstring",
"icon": "icon-font",
"config": {
"style": "font-size: 16px; line-height: 20px; font-weight: light;",
"markup": "<p>#value#</p>"
}
},
{
"name": "Quote",
@@ -39,8 +124,28 @@
"view": "textstring",
"icon": "icon-quote",
"config": {
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-style: italic; font-size: 18px",
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
"markup": "<blockquote>#value#</blockquote>"
}
},
{
"name": "Quote with description",
"alias": "quote_D",
"view": "/App_Plugins/Grid/Editors/Views/quote_with_description.html",
"render": "/App_Plugins/Grid/Editors/Render/quote_with_description.cshtml",
"icon": "icon-quote",
"config": {
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px"
}
},
{
"name": "Code",
"alias": "code",
"view": "textstring",
"icon": "icon-code",
"config": {
"style": "overflow: auto;padding: 6px 10px;border: 1px solid #ddd;border-radius: 3px;background-color: #f8f8f8;font-size: .9rem;font-family: 'Courier 10 Pitch', Courier, monospace;line-height: 19px;",
"markup": "<pre>#value#</pre>"
}
}
]
@@ -636,8 +636,12 @@
</area>
<area alias="imagecropper">
<key alias="reset">Återställ</key>
<key alias="defineCrop">Definiera beskräning</key>
<key alias="defineCropDescription">Ge beskärningen ett alias och dess standardbredd och -höjd</key>
<key alias="saveCrop">spara beskärning</key>
<key alias="addCrop">Lägg till ny beskärning</key>
</area>
<area alias="rollback">
<area alias="rollback">
<key alias="currentVersion">Nuvarande version</key>
<key alias="diffHelp"><![CDATA[Här visas skillnaderna mellan nuvarande version och vald version<br /><del>Röd</del> text kommer inte att synas i den valda versionen. , <ins>Grön betyder att den har tillkommit</ins>]]></key>
<key alias="documentRolledBack">Dokumentet har återgått till en tidigare version</key>