Merge branch 'develop' into feature/packaging

Conflicts:
	.gitignore
	Gruntfile.js
	package.json
This commit is contained in:
Tom Fulton
2014-01-16 23:33:53 -07:00
26 changed files with 1448 additions and 326 deletions
+45 -1
View File
@@ -12,4 +12,48 @@ $RECYCLE.BIN/
dist/
node_modules/
pkg/
pkg/tmp/
pkg/*.nupkg
[Dd]ebug/
[Bb]uild/
[Rr]elease/
[Bb]in/
[Oo]bj/
[Pp]ackages/
*.aps
*.bak
*.backup
*.build.csdef
*.cache
*.ilk
*.lib
*.log
*.meta
*.ncb
*.obj
*.orig
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.scc
*.sdf
*.sln.docstates
*.suo
*.tlb
*.tlh
*.tli
*.tmp
*.user
*.vspscc
*.[Pp]ublish.xml
*_i.c
*_p.c
.builds
.svn
.hg
.hgignore
+44 -3
View File
@@ -2,6 +2,9 @@ module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
dest: 'dist',
version: '0.0.0',
package_dir: 'pkg',
package_temp_dir: '<%= package_dir %>/tmp/',
watch: {
less: {
@@ -56,22 +59,56 @@ module.exports = function(grunt) {
application: {
src: [
'app/controllers/controller.js',
'app/directives/archetypeproperty.js'
'app/controllers/config.controller.js',
'app/directives/archetypeproperty.js',
'app/services/propertyeditor.js'
],
dest: '<%= dest %>/js/archetype.js'
}
},
nugetpack: {
dist: {
src: '<%= package_temp_dir %>/nuget/package.nuspec',
dest: '<%= package_dir %>'
}
},
template: {
'nuget_manifest': {
'options': {
'data': {
version: '<%= version %>',
files: [{ path: '..\\..\\..\\<%= dest %>\\**', target: 'content\\App_Plugins\\Archetype'}]
}
},
'files': {
'<%= package_temp_dir %>/nuget/package.nuspec': ['<%= package_dir %>/nuget/package.nuspec']
}
}
},
clean: {
build: ['<%= dest %>'],
package_temp: ['<%= package_temp_dir %>']
},
copy: {
build: {
files: [
{expand: true, cwd: 'app/', src: ['package.manifest'], dest: '<%= dest %>', flatten: true},
{expand: true, cwd: 'app/views/', src: ['archetype.html'], dest: '<%= dest %>/views', flatten: true}
{expand: true, cwd: 'app/config/', src: ['config.views.js'], dest: '<%= dest %>/js', flatten: true},
{expand: true, cwd: 'app/views/', src: ['archetype.html', 'archetype.config.html'], dest: '<%= dest %>/views', flatten: true}
]
},
deploy: {
files: [
{expand: true, cwd: '<%= dest %>/', src: ['**'], dest: '<%= grunt.option("target") %>\\App_Plugins\\Imulus.Archetype', flatten: false},
{expand: true, cwd: '<%= dest %>/', src: ['**'], dest: '<%= grunt.option("target") %>\\App_Plugins\\Archetype', flatten: false},
]
},
nuget_prepare: {
files: [
{expand: true, cwd: '<%= dest %>/', src: ['**'], dest: '<%= package_temp_dir %>/nuget/content/', flatten: false},
]
},
umbracopackage: {
@@ -105,6 +142,7 @@ module.exports = function(grunt) {
},
clean: ['<%= dest %>']
}
});
@@ -115,10 +153,13 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-nuget');
grunt.loadNpmTasks('grunt-template');
grunt.loadNpmTasks('grunt-touch');
grunt.loadTasks('tasks');
grunt.registerTask('package:nuget', ['default', 'clean:package_temp', 'copy:nuget_prepare', 'template:nuget_manifest', 'nugetpack', 'clean:package_temp']);
grunt.registerTask('touchwebconfigifenabled', function() { if (grunt.option("touch")) grunt.task.run("touch:webconfig") });
grunt.registerTask('deploy', ['default', 'copy:deploy', 'touchwebconfigifenabled']);
grunt.registerTask('package:umbraco', ['copy:umbracopackage', 'umbracoPackage']);
-153
View File
@@ -41,164 +41,11 @@ Your /App_Plugins should contain this structure:
## Prevalue Configs ##
Most of the fields are self evident, the following fields will be discussed further:
###Empty Fieldset Models###
This option is required and defines the fieldsets and their properties.
If you get a YSOD regarding truncating of data, it's b/c Umbraco has a arbitrarily low limit on the number of characters (2500) allowed as prevalues in the DB.
See: http://issues.umbraco.org/issue/U4-2120
An example follows below:
( //note that this needs to start with an open parentheses and notice the closing one at the end of this. This avoids issues with only seeing [object].
[ //this array holds the array of fieldset objects
{ //fieldset object
alias: "FS1", //fieldset alias (unique)
tooltip: "This is for fieldset 1", //used as a helper text
icon: "/umbraco/Images/aboutNew.png", //a custom icon
label: "Fieldset 1", //the fieldset label
headerText: "Please fill out the boxes.", // text to be prepended to the fieldset properties?
footerText: "Thanks!", //text to be appended to the fieldset properties?
properties:[ //an array of property objects
{
alias: "firstName", //uniquely name the property (unique to the fieldset)
label: "First Name", //a label to use
helpText: "(Required)", //a note that will appear under the label
view: "/umbraco/views/propertyeditors/textbox/textbox.html", //a path to a view
value: "", //set to none or set to a default; can be json (if the view understands it)
config: { //pass configs to the view; configs are based on the view used
}
},
{ //another property
alias: "lastName",
label: "Last Name",
helpText: "(Required)",
view: "/umbraco/views/propertyeditors/textbox/textbox.html",
value: "",
config: {
}
},
{
alias: "age",
label: "Age",
helpText: "(Optional)",
view: "/umbraco/views/propertyeditors/textbox/textbox.html",
value: "",
config: {
}
},
{
alias: "blah",
label: "Blah",
helpText: "",
view: "/umbraco/views/propertyeditors/contentpicker/contentpicker.html",
value: "",
config: {
}
}
]
},
{ //another fieldset
alias: "FS2",
tooltip: "This is for fieldset 2",
icon: "/umbraco/Images/about.png",
label: "Fieldset 2",
headerText: "Please fill out the boxes.",
footerText: "Thanks!",
properties:[
{
alias: "foo",
label: "Foo",
helpText: "(Required)",
view: "/umbraco/views/propertyeditors/textbox/textbox.html",
value: "",
config: {
}
},
{
alias: "bar",
label: "Bar",
helpText: "",
view: "/umbraco/views/propertyeditors/textbox/textbox.html",
value: "",
config: {
}
}
]
}
]
)//close it
###Default Model###
Here is the spot to define a starting value. This model uses only the essential data that must be saved. This example shows multiple fieldsets with multiple properties:
( //again use a parenthesis to avoid [object]
{
"fieldsets": [
{
"alias": "FS2",
"properties": [
{
"alias": "bar",
"value": "Field"
},
{
"alias": "foo",
"value": "Another"
}
]
},
{
"alias": "FS2",
"properties": [
{
"alias": "foo",
"value": "The Foo Field"
},
{
"alias": "bar",
"value": "The Bar Field"
}
]
},
{
"alias": "FS1",
"properties": [
{
"alias": "age",
"value": "Old"
},
{
"alias": "lastName",
"value": "Giszewski"
},
{
"alias": "firstName",
"value": "Kevin"
},
{
"alias": "blah",
"value": ""
}
]
}
]
}
)
###Developer Mode###
By turning this on, you will get a textarea visible with the data model (not prevalue model). You can change the contents of the textarea for real-time editing. Additionally when in developer mode, the console will reflect the scope object of each property.
###Sortable Options###
Just in case you need to pass something into the jQueryUI sort options, you can override the default behavior by passing in a new JS snippet here.
##View Compatibility##
The basis of compatibility is based on the following:
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>
Binary file not shown.
+136
View File
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">true</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">false</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig>packages.config</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
+27
View File
@@ -0,0 +1,27 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Archetype.Umbraco", "Umbraco.Archetype\Archetype.Umbraco.csproj", "{7D185D41-4228-4978-ACB8-83F9A48AD94F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D84AA4ED-85A6-484F-9E72-BE7AC9835821}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7D185D41-4228-4978-ACB8-83F9A48AD94F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D185D41-4228-4978-ACB8-83F9A48AD94F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D185D41-4228-4978-ACB8-83F9A48AD94F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D185D41-4228-4978-ACB8-83F9A48AD94F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7D185D41-4228-4978-ACB8-83F9A48AD94F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Archetype.Umbraco</RootNamespace>
<AssemblyName>Archetype</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoMapper">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
</Reference>
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="businesslogic">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\businesslogic.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core">
<HintPath>..\packages\ClientDependency.1.7.0.4\lib\ClientDependency.Core.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core.Mvc">
<HintPath>..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll</HintPath>
</Reference>
<Reference Include="cms">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\cms.dll</HintPath>
</Reference>
<Reference Include="controls">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\controls.dll</HintPath>
</Reference>
<Reference Include="CookComputing.XmlRpcV2">
<HintPath>..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll</HintPath>
</Reference>
<Reference Include="Examine">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="HtmlAgilityPack">
<HintPath>..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="interfaces">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\interfaces.dll</HintPath>
</Reference>
<Reference Include="log4net">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\log4net.dll</HintPath>
</Reference>
<Reference Include="Lucene.Net">
<HintPath>..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationBlocks.Data">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\Microsoft.ApplicationBlocks.Data.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Helpers">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\Microsoft.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Mvc.FixedDisplayModes">
<HintPath>..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.1\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll</HintPath>
</Reference>
<Reference Include="MiniProfiler">
<HintPath>..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll</HintPath>
</Reference>
<Reference Include="MySql.Data">
<HintPath>..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SQLCE4Umbraco">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\SQLCE4Umbraco.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="TidyNet">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\TidyNet.dll</HintPath>
</Reference>
<Reference Include="umbraco">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.dll</HintPath>
</Reference>
<Reference Include="Umbraco.Core">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\Umbraco.Core.dll</HintPath>
</Reference>
<Reference Include="umbraco.DataLayer">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.DataLayer.dll</HintPath>
</Reference>
<Reference Include="umbraco.editorControls">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.editorControls.dll</HintPath>
</Reference>
<Reference Include="umbraco.MacroEngines">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.MacroEngines.dll</HintPath>
</Reference>
<Reference Include="umbraco.providers">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.providers.dll</HintPath>
</Reference>
<Reference Include="Umbraco.Web.UI">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\Umbraco.Web.UI.dll</HintPath>
</Reference>
<Reference Include="umbraco.XmlSerializers">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\umbraco.XmlSerializers.dll</HintPath>
</Reference>
<Reference Include="UmbracoExamine">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\UmbracoExamine.dll</HintPath>
</Reference>
<Reference Include="UrlRewritingNet.UrlRewriter">
<HintPath>..\packages\UmbracoCms.Core.7.0.1\lib\UrlRewritingNet.UrlRewriter.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\Extensions.cs" />
<Compile Include="Models\Archetype.cs" />
<Compile Include="Models\Fieldset.cs" />
<Compile Include="Models\Property.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PropertyConverters\ArchetypeValueConverter.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Archetype.Umbraco.Extensions
{
public static class Extensions
{
//lifted from the core as it is marked 'internal'
public static bool DetectIsJson(this string input)
{
input = input.Trim();
return input.StartsWith("{") && input.EndsWith("}")
|| input.StartsWith("[") && input.EndsWith("]");
}
}
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Archetype.Umbraco.Models
{
public class Archetype
{
public IEnumerable<Fieldset> Fieldsets { get; set; }
public Archetype()
{
Fieldsets = new List<Fieldset>();
}
}
}
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
namespace Archetype.Umbraco.Models
{
public class Fieldset
{
public string Alias { get; set; }
public IEnumerable<Property> Properties;
public Fieldset()
{
Properties = new List<Property>();
}
public string GetValue(string propertyAlias)
{
return GetValue<string>(propertyAlias);
}
public T GetValue<T>(string propertyAlias)
{
var property = GetProperty(propertyAlias);
if (property == null || string.IsNullOrEmpty(property.Value))
return default(T);
var convertAttempt = property.Value.TryConvertTo<T>();
if (convertAttempt.Success)
return convertAttempt.Result;
return default(T);
}
private Property GetProperty(string propertyAlias)
{
return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(propertyAlias));
}
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Archetype.Umbraco.Models
{
public class Property
{
public string Alias { get; set; }
public string Value { get; set; }
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Archetype")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Archetype")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e37e94f9-c7ba-4b54-b7e1-64419b3dba0b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Models.PublishedContent;
using Archetype.Umbraco.Models;
using Archetype.Umbraco.Extensions;
namespace Archetype.Umbraco.PropertyConverters
{
/* based on the Tim Geyssens sample at: https://github.com/TimGeyssens/MatrixPropEditor/blob/master/SamplePropertyValueConverter/SamplePropertyValueConverter/MatrixValueConverter.cs */
[PropertyValueType(typeof(Archetype.Umbraco.Models.Archetype))]
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
public class ArchetypeValueConverter : PropertyValueConverterBase
{
public override bool IsConverter(PublishedPropertyType propertyType)
{
return propertyType.PropertyEditorAlias.Equals("Imulus.Archetype");
}
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
if (source == null) return null;
var sourceString = source.ToString();
if (sourceString.DetectIsJson())
{
try
{
var archetype = JsonConvert.DeserializeObject <Archetype.Umbraco.Models.Archetype> (sourceString);
return archetype;
}
catch (Exception ex)
{
return null;
}
}
return sourceString;
}
}
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="ClientDependency" version="1.7.0.4" targetFramework="net45" />
<package id="ClientDependency-Mvc" version="1.7.0.4" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
<package id="MySql.Data" version="6.6.5" targetFramework="net45" />
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="UmbracoCms.Core" version="7.0.1" targetFramework="net45" />
<package id="xmlrpcnet" version="2.5.0" targetFramework="net45" />
</packages>
+22
View File
@@ -0,0 +1,22 @@
[
{
"name": "Textstring",
"path": "/umbraco/views/propertyeditors/textbox/textbox.html"
},
{
"name": "Content Picker",
"path": "/umbraco/views/propertyeditors/contentpicker/contentpicker.html"
},
{
"name": "Boolean",
"path": "/umbraco/views/propertyeditors/boolean/boolean.html"
},
{
"name": "Checkbox List",
"path": "/umbraco/views/propertyeditors/checkboxlist/checkboxlist.html"
},
{
"name": "Radio Buttons",
"path": "/umbraco/views/propertyeditors/radiobuttons/radiobuttons.html"
}
]
+239
View File
@@ -0,0 +1,239 @@
angular.module("umbraco").controller("Imulus.ArchetypeConfigController", function ($scope, $http, assetsService, propertyEditorService) {
//$scope.model.value = "";
//console.log($scope.model.value);
var newPropertyModel = '{alias: "", remove: false, collapse: false, label: "", helpText: "", view: "", value: "", config: ""}';
var newFieldsetModel = '{alias: "", remove: false, collapse: false, labelTemplate: "", tooltip: "", icon: "", label: "", headerText: "", footerText: "", properties:[' + newPropertyModel + ']}';
var defaultFieldsetConfigModel = eval("({showAdvancedOptions: false, hideFieldsetToolbar: false, enableMultipleFieldsets: false, hideFieldsetControls: false, hideFieldsetLabels: false, hidePropertyLabel: false, maxFieldsets: null, fieldsets: [" + newFieldsetModel + "]})");
$scope.model.value = $scope.model.value || defaultFieldsetConfigModel;
initConfigRenderModel();
//get the available views
propertyEditorService.getViews().then(function(data){
$scope.availableViews = data;
});
$scope.sortableOptions = {
axis: 'y',
cursor: "move",
handle: ".handle",
update: function (ev, ui) {
},
stop: function (ev, ui) {
}
};
$scope.focusFieldset = function(fieldset){
var iniState;
if(fieldset)
{
iniState = fieldset.collapse;
}
_.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){
if($scope.archetypeConfigRenderModel.fieldsets.length == 1 && fieldset.remove == false)
{
fieldset.collapse = false;
return;
}
if(fieldset.label)
{
fieldset.collapse = true;
}
else
{
fieldset.collapse = false;
}
});
if(iniState)
{
fieldset.collapse = !iniState;
}
}
//ini
$scope.focusFieldset();
$scope.focusProperty = function(properties, property){
var iniState;
if(property)
{
iniState = property.collapse;
}
_.each(properties, function(property){
if(property.label)
{
property.collapse = true;
}
else
{
property.collapse = false;
}
});
if(iniState)
{
property.collapse = !iniState;
}
}
//setup JSON.stringify helpers
$scope.archetypeConfigRenderModel.toString = stringify;
//encapsulate stringify (should be built into browsers, not sure of IE support)
function stringify() {
return JSON.stringify(this);
}
//watch for changes
$scope.$watch('archetypeConfigRenderModel', function (v) {
//console.log(v);
if (typeof v === 'string') {
$scope.archetypeConfigRenderModel = JSON.parse(v);
$scope.archetypeConfigRenderModel.toString = stringify;
}
}, true);
//helper that returns if an item can be removed
$scope.canRemoveFieldset = function ()
{
return countVisibleFieldset() > 1;
}
//helper that returns if an item can be sorted
$scope.canSortFieldset = function ()
{
return countVisibleFieldset() > 1;
}
//helper that returns if an item can be removed
$scope.canRemoveProperty = function (fieldset)
{
return countVisibleProperty(fieldset) > 1;
}
//helper that returns if an item can be sorted
$scope.canSortProperty = function (fieldset)
{
return countVisibleProperty(fieldset) > 1;
}
//helper to count what is visible
function countVisibleFieldset()
{
var count = 0;
_.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){
if (fieldset.remove == false) {
count++;
}
});
return count;
}
function countVisibleProperty(fieldset)
{
var count = 0;
for (var i in fieldset.properties) {
if (fieldset.properties[i].remove == false) {
count++;
}
}
return count;
}
//handles a fieldset add
$scope.addFieldsetRow = function ($index, $event) {
$scope.archetypeConfigRenderModel.fieldsets.splice($index + 1, 0, eval("(" + newFieldsetModel + ")"));
$scope.focusFieldset();
}
//rather than splice the archetypeConfigRenderModel, we're hiding this and cleaning onFormSubmitting
$scope.removeFieldsetRow = function ($index) {
if ($scope.canRemoveFieldset()) {
if (confirm('Are you sure you want to remove this?')) {
$scope.archetypeConfigRenderModel.fieldsets[$index].remove = true;
}
}
}
//handles a property add
$scope.addPropertyRow = function (fieldset, $index) {
fieldset.properties.splice($index + 1, 0, eval("(" + newPropertyModel + ")"));
}
//rather than splice the archetypeConfigRenderModel, we're hiding this and cleaning onFormSubmitting
$scope.removePropertyRow = function (fieldset, $index) {
if ($scope.canRemoveProperty(fieldset)) {
if (confirm('Are you sure you want to remove this?')) {
fieldset.properties[$index].remove = true;
}
}
}
//helper to ini the render model
function initConfigRenderModel()
{
$scope.archetypeConfigRenderModel = $scope.model.value;
_.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){
fieldset.remove = false;
if(fieldset.label)
{
fieldset.collapse = true;
}
_.each(fieldset.properties, function(fieldset){
fieldset.remove = false;
});
});
}
//sync things up on save
$scope.$on("formSubmitting", function (ev, args) {
syncModelToRenderModel();
});
//helper to sync the model to the renderModel
function syncModelToRenderModel()
{
$scope.model.value = $scope.archetypeConfigRenderModel;
var fieldsets = [];
_.each($scope.archetypeConfigRenderModel.fieldsets, function(fieldset){
//check fieldsets
if (!fieldset.remove) {
fieldsets.push(fieldset);
var properties = [];
_.each(fieldset.properties, function(property){
if (!property.remove) {
properties.push(property);
}
});
fieldset.properties = properties;
}
});
$scope.model.value.fieldsets = fieldsets;
}
//archetype css
assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css");
});
+176 -32
View File
@@ -1,28 +1,41 @@
angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, assetsService) {
angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, $interpolate, assetsService, angularHelper, notificationsService) {
//$scope.model.value = "";
//set default value of the model
//this works by checking to see if there is a model; then cascades to the default model then to an empty fieldset
//validate the user configs
$scope.model.config.defaultModel = getValidJson("$scope.model.config.defaultModel", $scope.model.config.defaultModel);
$scope.model.config.fieldsetModels = getValidJson("$scope.model.config.fieldsetModels", $scope.model.config.fieldsetModels);
var form = angularHelper.getCurrentForm($scope);
$scope.model.value = $scope.model.value || ($scope.model.config.defaultModel || { fieldsets: [getEmptyRenderItem($scope.model.config.fieldsetModels[0])] });
$scope.model.config = $scope.model.config.archetypeConfig;
$scope.model.value = $scope.model.value || { fieldsets: [getEmptyRenderFieldset($scope.model.config.fieldsets[0])] };
//ini
$scope.archetypeRenderModel = {};
initArchetypeRenderModel();
//helper to get $eval the labelTemplate
$scope.getFieldsetTitle = function(fieldsetConfigModel, fieldsetIndex) {
var fieldset = $scope.archetypeRenderModel.fieldsets[fieldsetIndex];
var template = fieldsetConfigModel.labelTemplate;
var rgx = /{{(.*?)}}*/g;
var results;
var parsedTemplate = template;
while ((results = rgx.exec(template)) !== null) {
var propertyAlias = results[1];
var propertyValue = $scope.getPropertyValueByAlias(fieldset, propertyAlias);
parsedTemplate = parsedTemplate.replace(results[0], propertyValue);
}
return parsedTemplate;
};
/* add/remove/sort */
//defines the options for the jquery sortable
//i used an ng-model="archetypeRenderModel" so the sort updates the right model
//configuration overrides the default
var configSortableOptions = getValidJson("$scope.model.config.sortableOptions", $scope.model.config.sortableOptions);
$scope.sortableOptions = configSortableOptions || {
$scope.sortableOptions = {
axis: 'y',
cursor: "move",
handle: ".handle",
@@ -38,19 +51,21 @@
$scope.addRow = function (fieldsetAlias, $index) {
if ($scope.canAdd())
{
if ($scope.model.config.fieldsetModels)
if ($scope.model.config.fieldsets)
{
var newRenderItem = getEmptyRenderItem($scope.getConfigFieldsetByAlias(fieldsetAlias));
var newFieldset = getEmptyRenderFieldset($scope.getConfigFieldsetByAlias(fieldsetAlias));
if (typeof $index != 'undefined')
{
$scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, newRenderItem);
$scope.archetypeRenderModel.fieldsets.splice($index + 1, 0, newFieldset);
}
else
{
$scope.archetypeRenderModel.fieldsets.push(newRenderItem);
$scope.archetypeRenderModel.fieldsets.push(newFieldset);
}
}
newFieldset.collapse = true;
$scope.focusFieldset(newFieldset);
}
}
@@ -66,9 +81,9 @@
//helpers for determining if a user can do something
$scope.canAdd = function ()
{
if ($scope.model.config.maxProperties)
if ($scope.model.config.maxFieldsets)
{
return countVisible() < $scope.model.config.maxProperties;
return countVisible() < $scope.model.config.maxFieldsets;
}
return true;
@@ -89,16 +104,58 @@
//helper, ini the render model from the server (model.value)
function initArchetypeRenderModel() {
$scope.archetypeRenderModel = $scope.model.value;
_.each($scope.archetypeRenderModel.fieldsets, function (fieldset)
{
fieldset.remove = false;
fieldset.collapse = false;
fieldset.isValid = true;
});
}
//helper to get the correct fieldset from config
$scope.getConfigFieldsetByAlias = function(alias) {
for (var i in $scope.model.config.fieldsetModels) {
if ($scope.model.config.fieldsetModels[i].alias == alias) {
return $scope.model.config.fieldsetModels[i];
return _.find($scope.model.config.fieldsets, function(fieldset){
return fieldset.alias == alias;
});
}
//helper to get a property by alias from a fieldset
$scope.getPropertyValueByAlias = function(fieldset, propertyAlias) {
var property = _.find(fieldset.properties, function(p) {
return p.alias == propertyAlias;
});
return (typeof property !== 'undefined') ? property.value : '';
};
//helper for collapsing
$scope.focusFieldset = function(fieldset){
var iniState;
if(fieldset)
{
iniState = fieldset.collapse;
}
_.each($scope.archetypeRenderModel.fieldsets, function(fieldset){
if($scope.archetypeRenderModel.fieldsets.length == 1 && fieldset.remove == false)
{
fieldset.collapse = false;
return;
}
fieldset.collapse = true;
});
if(iniState)
{
fieldset.collapse = !iniState;
}
}
//ini
$scope.focusFieldset();
//helper returns valid JS or null
function getValidJson(variable, json)
@@ -126,7 +183,7 @@
//watch for changes
$scope.$watch('archetypeRenderModel', function (v) {
if ($scope.model.config.developerMode) {
//console.log(v);
console.log(v);
if (typeof v === 'string') {
$scope.archetypeRenderModel = JSON.parse(v);
$scope.archetypeRenderModel.toString = stringify;
@@ -139,11 +196,11 @@
{
var count = 0;
for (var i in $scope.archetypeRenderModel.fieldsets) {
if ($scope.archetypeRenderModel.fieldsets[i].remove == false) {
_.each($scope.archetypeRenderModel.fieldsets, function(fieldset){
if (fieldset.remove == false) {
count++;
}
}
});
return count;
}
@@ -153,24 +210,111 @@
{
$scope.model.value = { fieldsets: [] };
for (var i in $scope.archetypeRenderModel.fieldsets) {
if (!$scope.archetypeRenderModel.fieldsets[i].remove) {
//remove the 'remove' property as this is only for sorting/internal use
delete $scope.archetypeRenderModel.fieldsets[i].remove;
$scope.model.value.fieldsets.push($scope.archetypeRenderModel.fieldsets[i]);
_.each($scope.archetypeRenderModel.fieldsets, function(fieldset){
if (typeof fieldset != 'function' && !fieldset.remove){
//clone and clean
var tempFieldset = JSON.parse(JSON.stringify(fieldset));
delete tempFieldset.remove;
delete tempFieldset.isValid;
delete tempFieldset.collapse;
_.each(tempFieldset.properties, function(property){
delete property.isValid;
});
$scope.model.value.fieldsets.push(tempFieldset);
}
}
});
}
//helper to add an empty fieldset
function getEmptyRenderItem (fieldsetModel)
function getEmptyRenderFieldset (fieldsetModel)
{
return eval("({ alias: '" + fieldsetModel.alias + "', remove: false, properties: []})");
}
//helper for validation
function getValidation()
{
var validation = {}
validation.isValid = true;
validation.requiredAliases = [];
validation.invalidProperties = [];
//determine which fields are required
_.each($scope.model.config.fieldsets, function(fieldset){
_.each(fieldset.properties, function(property){
if(property.required)
{
validation.requiredAliases.push(property.alias);
}
});
});
//if nothing required; let's go
if(validation.requiredAliases.length == 0)
{
return validation;
}
//otherwise we need to check the required aliases
_.each($scope.archetypeRenderModel.fieldsets, function(fieldset){
fieldset.isValid = true;
_.each(fieldset.properties, function(property){
property.isValid = true;
//if a required field
if(_.find(validation.requiredAliases, function(alias){ return alias == property.alias }))
{
//TODO: do a better validation test
if(property.value == ""){
fieldset.isValid = false;
property.isValid = false;
validation.isValid = false;
validation.invalidProperties.push(property);
}
}
});
});
if($scope.model.config.developerMode == '1')
{
console.log(validation);
}
return validation;
}
$scope.getPropertyValidity = function(fieldsetIndex, alias)
{
if($scope.archetypeRenderModel.fieldsets[fieldsetIndex])
{
var property = _.find($scope.archetypeRenderModel.fieldsets[fieldsetIndex].properties, function(property){
return property.alias == alias;
});
}
return (typeof property == 'undefined') ? true : property.isValid;
}
//sync things up on save
$scope.$on("formSubmitting", function (ev, args) {
syncModelToRenderModel();
var validation = getValidation();
if(!validation.isValid)
{
notificationsService.warning("Cannot Save Document", "The document could not be saved because of missing required fields.")
form.$setValidity("archetypeError", false);
}
else
{
syncModelToRenderModel();
form.$setValidity("archetypeError", true);
}
});
//custom js
@@ -179,7 +323,7 @@
}
//archetype css
assetsService.loadCss("/App_Plugins/Imulus.Archetype/css/archetype.css");
assetsService.loadCss("/App_Plugins/Archetype/css/archetype.css");
//custom css
if($scope.model.config.customCssPath)
+47 -6
View File
@@ -1,12 +1,12 @@
angular.module("umbraco").directive('archetypeProperty', function ($compile, $http) {
function getFieldsetByAlias(fieldsetModels, alias)
function getFieldsetByAlias(fieldsets, alias)
{
for (var i in fieldsetModels)
for (var i in fieldsets)
{
if (fieldsetModels[i].alias == alias)
if (fieldsets[i].alias == alias)
{
return fieldsetModels[i];
return fieldsets[i];
}
}
}
@@ -23,13 +23,54 @@
var linker = function (scope, element, attrs) {
var configFieldsetModel = getFieldsetByAlias(scope.archetypeConfig.fieldsetModels, scope.fieldset.alias);
var configFieldsetModel = getFieldsetByAlias(scope.archetypeConfig.fieldsets, scope.fieldset.alias);
var view = configFieldsetModel.properties[scope.propertyConfigIndex].view;
var label = configFieldsetModel.properties[scope.propertyConfigIndex].label;
var config = configFieldsetModel.properties[scope.propertyConfigIndex].config;
var alias = configFieldsetModel.properties[scope.propertyConfigIndex].alias;
var defaultValue = configFieldsetModel.properties[scope.propertyConfigIndex].value;
//try to convert the config to a JS object
if(config && typeof config == 'string'){
try{
if(scope.archetypeConfig.developerMode == '1'){
console.log("Trying to eval config: " + config);
}
config = eval("(" + config + ")");
}
catch(exception)
{
if(scope.archetypeConfig.developerMode == '1'){
console.log("Failed to eval config.");
}
}
}
if(config && scope.archetypeConfig.developerMode == '1'){
console.log("Config post-eval: " + config);
}
//try to convert the defaultValue to a JS object
if(defaultValue && typeof defaultValue == 'string'){
try{
if(scope.archetypeConfig.developerMode == '1'){
console.log("Trying to eval default value: " + defaultValue);
}
defaultValue = eval("(" + defaultValue + ")");
}
catch(exception)
{
if(scope.archetypeConfig.developerMode == '1'){
console.log("Failed to eval defaultValue.");
}
}
}
if(defaultValue && scope.archetypeConfig.developerMode == '1'){
console.log("Default value post-eval: " + defaultValue);
}
if (view)
{
$http.get(view).success(function (data) {
@@ -50,7 +91,7 @@
if (!renderModelPropertyIndex)
{
scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties.push(eval("({alias: '" + alias + "', value:''})"));
scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties.push(eval("({alias: '" + alias + "', value:'" + defaultValue + "'})"));
renderModelPropertyIndex = getPropertyIdByAlias(scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties, alias);
}
scope.model.value = scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex].properties[renderModelPropertyIndex].value;
+111
View File
@@ -32,6 +32,7 @@
.archetypeEditor .archetypeProperty{
clear: both;
overflow: hidden;
margin-top: 3px;
}
.archetypeEditor .umb-contentpicker{
@@ -76,4 +77,114 @@
clear:both;
margin-bottom: 3px;
font-size: 12px;
}
.archetypeConfig {
margin-bottom: 20px;
}
.archetypeConfig ul{
list-style: none;
}
.archetypeConfig .archetypeEditorControls{
float: right;
}
.archetypeConfig li{
background-color: #fff;
}
.archetypeConfig label{
width: 120px;
}
.archetypeConfig .archetypeFieldsetWrapper{
border: 1px solid #ccc;
padding: 10px 5px;
margin-bottom: 10px;
}
.archetypeConfig .archetypeFieldsetOption input[type=text]{
width: 600px;
}
.archetypeConfig .archetypeDeveloperModel {
width: 88%;
margin-left: 30px;
height: 100px;
}
.archetypeConfig .archetypePropertiesWrapper{
border: 1px solid #ddd;
padding: 10px;
margin: 10px;
margin-left: 50px;
width: 665px;
}
.archetypeConfig .archetypePropertiesWrapper ul{
padding: 0;
margin: 0;
}
.archetypeConfig .archetypePropertiesWrapper li{
padding: 10px;
margin-bottom: 10px;
background-color: #eee;
}
.archetypeConfig .archetypePropertiesWrapper li:last-child{
margin-bottom: 0;
}
.archetypeConfig .archetypeFieldsets {
margin-left: 0;
}
.archetypeConfig .archetypeFieldsetTitle {
overflow: hidden;
}
.archetypeConfig .archetypeFieldsetCollapser{
margin-top: 10px;
}
.archetypeConfig .archetypeOptions label{
width: 200px;
}
.archetypeConfig .archetypeAdvancedOptions label{
width: 400px;
}
.archetypeConfig .archetypeDeveloperModel {
margin-bottom: 10px;
}
.archetypeConfig .archetypeMaxFieldsets{
border: 1px solid #ddd;
width: 25px;
text-align: right;
}
.archetypeConfig .archetypePropertyTitle{
overflow: hidden;
}
.archetypeConfig .archetypePropertyCollapser{
margin-top: 10px;
}
.archetypeEditor .archetypeFieldsetError{
border: 1px solid red;
}
.archetypeEditor .archetypePropertyError{
border: 1px solid red;
background-color: #8b0000;
color: #fff;
margin-bottom: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
+11 -110
View File
@@ -1,129 +1,30 @@
{
propertyEditors: [
propertyEditors: [
{
alias: "Imulus.Archetype",
name: "Imulus Archetype",
name: "Archetype",
editor: {
view: "~/App_Plugins/Imulus.Archetype/views/archetype.html",
view: "~/App_Plugins/Archetype/views/archetype.html",
valueType: "JSON"
},
},
prevalues: {
fields: [
{
label: "Empty Fieldset Models",
description: "(Required) Define your fieldset models?",
key: "fieldsetModels",
view: "textarea",
fields: [
{
label: "Config",
description: "(Required) Describe your Archetype.",
key: "archetypeConfig",
view: "/App_Plugins/Archetype/views/archetype.config.html",
validation: [
{
type: "Required"
}
]
},
{
label: "Hide fieldset toolbar?",
description: "Hides the fieldset toolbar that appears when more than one fieldset model appears.",
key: "hideFieldsetToolbar",
view: "boolean",
validation: [
]
},
{
label: "Hide fieldset editor controls?",
description: "Hides the fieldset add/remove/sort controls.",
key: "hideFieldsetControls",
view: "boolean",
validation: [
]
},
{
label: "Hide fieldset labels?",
description: "Hides the fieldset labels.",
key: "hideFieldsetLabels",
view: "boolean",
validation: [
]
},
{
label: "Hide property labels?",
description: "Hides the property labels.",
key: "hidePropertyLabels",
view: "boolean",
validation: [
]
},
{
label: "Default Model",
description: "(Optional) What would you the default model to be? Otherwise the model will defer to the Empty Fieldset Model for new items.",
key: "defaultModel",
view: "textarea",
validation: [
]
},
{
label: "Max Fieldsets",
description: "(Optional) How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.",
key: "maxProperties",
view: "number",
validation: [
]
},
{
label: "Custom Wrapper Class",
description: "(Optional) Enter a custom CSS class that will be applied to the wrapper div.",
key: "customCssClass",
view: "textstring",
validation: [
]
},
{
label: "CSS File",
description: "(Optional) Enter a path for a custom CSS file to be included on the page.",
key: "customCssPath",
view: "textstring",
validation: [
]
},
{
label: "JS File",
description: "(Optional) Enter a path for a custom JS file to be included on the page.",
key: "customJsPath",
view: "textstring",
validation: [
]
},
{
label: "Developer Mode",
description: "(Advanced) Turn on for developer options.",
key: "developerMode",
view: "boolean",
validation: [
]
},
{
label: "Sortable Options",
description: "(Advanced) Override the default sortable controls with a JSON object.",
key: "sortableOptions",
view: "textarea",
validation: [
]
}
]
}
}
],
javascript: [
'~/App_Plugins/Imulus.Archetype/js/archetype.js'
'~/App_Plugins/Archetype/js/archetype.js'
]
}
+9
View File
@@ -0,0 +1,9 @@
angular.module('umbraco').factory('propertyEditorService', function($q, $http, umbRequestHelper){
return {
getViews: function() {
return umbRequestHelper.resourcePromise(
$http.get("/App_Plugins/Archetype/js/config.views.js"), 'Failed to retreive config for views.'
);
}
};
});
+119
View File
@@ -0,0 +1,119 @@
<div class="archetypeConfig" ng-controller="Imulus.ArchetypeConfigController">
<ul class="archetypeFieldsets" ui-sortable="sortableOptions" ng-model="archetypeConfigRenderModel.fieldsets">
<li ng-repeat="fieldset in archetypeConfigRenderModel.fieldsets" ng-hide="fieldset.remove">
<div class="archetypeFieldsetWrapper" >
<div class="archetypeEditorControls" ng-show="archetypeConfigRenderModel.enableMultipleFieldsets">
<i class="icon icon-add" ng-click="addFieldsetRow($index, $event)"></i>
<i class="icon icon-delete" ng-click="removeFieldsetRow($index)" ng-show="canRemoveFieldset()"></i>
<i class="icon icon-navigation handle" ng-show="canSortFieldset()"></i>
</div>
<div class="archetypeFieldsetTitle" ng-click="focusFieldset(fieldset)">{{fieldset.label}}</div>
<div class="archetypeFieldsetCollapser" ng-hide="fieldset.collapse && fieldset.label">
<div class="archetypeFieldsetOption">
<label>Label</label><input type="text" ng-model="fieldset.label" />
</div>
<div class="archetypeFieldsetOption">
<label>Alias</label><input type="text" ng-model="fieldset.alias" />
</div>
<div class="archetypeFieldsetOption">
<label>Label Template</label><input type="text" ng-model="fieldset.labelTemplate" />
</div>
<div class="archetypeFieldsetOption">
<label>Tooltip</label><input type="text" ng-model="fieldset.tooltip" />
</div>
<div class="archetypeFieldsetOption">
<label>Icon</label><input type="text" ng-model="fieldset.icon" />
</div>
<div class="archetypeFieldsetOption">
<label>Header Text</label><input type="text" ng-model="fieldset.headerText" />
</div>
<div class="archetypeFieldsetOption">
<label>Footer Text</label><input type="text" ng-model="fieldset.footerText" />
</div>
<div class="archetypeFieldsetOption">
<label>Properties</label>
</div>
<div class="archetypePropertiesWrapper">
<ul ui-sortable="sortableOptions" ng-model="fieldset.properties">
<li ng-repeat="property in fieldset.properties" ng-hide="property.remove">
<div class="archetypeEditorControls">
<i class="icon icon-add" ng-click="addPropertyRow(fieldset, $index)"></i>
<i class="icon icon-delete" ng-click="removePropertyRow(fieldset, $index)" ng-show="canRemoveProperty(fieldset)"></i>
<i class="icon icon-navigation handle" ng-show="canSortProperty(fieldset)"></i>
</div>
<div class="archetypePropertyTitle" ng-click="focusProperty(fieldset.properties, property)">{{property.label}}</div>
<div class="archetypePropertyCollapser" ng-hide="property.collapse && property.label">
<div>
<label>Label</label><input type="text" ng-model="property.label" />
</div>
<div>
<label>Alias</label><input type="text" ng-model="property.alias" />
</div>
<div>
<label>Help Text</label><input type="text" ng-model="property.helpText" />
</div>
<div>
<label>View</label><select ng-model="property.view" ng-options="view.path as view.name for view in availableViews"></select>
</div>
<div>
<label>Default Value</label><input type="text" ng-model="property.value" />
</div>
<div>
<label>Config</label><textarea ng-model="property.config" /></textarea>
</div>
<div>
<label>Required</label><input type="checkbox" ng-model="property.required" />
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</li>
</ul>
<div class="archetypeOptions">
<div>
<label>Toggle Advanced Options<small>Show advanced options.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.showAdvancedOptions"/>
</div>
</div>
<div class="archetypeAdvancedOptions" ng-show="archetypeConfigRenderModel.showAdvancedOptions">
<div>
<label>Hide fieldset editor controls?<small>Hides the fieldset add/remove/sort controls.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.hideFieldsetControls"/>
</div>
<div>
<label>Hide fieldset labels?<small>Hides the fieldset labels.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.hideFieldsetLabels"/>
</div>
<div>
<label>Hide property labels?<small>Hides the property labels.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.hidePropertyLabels"/>
</div>
<div>
<label>Max fieldsets<small>How many Fieldsets are allowed? Entering '1' will disable the controls. Default is unlimited.</small></label><input type="text" class="archetypeMaxFieldsets" ng-model="archetypeConfigRenderModel.maxFieldsets"/>
</div>
<div>
<label>Enable multiple fieldsets?<small>Allows multiple types of fieldsets within this archetype.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.enableMultipleFieldsets"/>
</div>
<div>
<label>Hide fieldset toolbar?<small>Hides the fieldset toolbar that appears when more than one fieldset model appears.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.hideFieldsetToolbar"/>
</div>
<div>
<label>Custom Wrapper Class<small>(Optional) Enter a custom CSS class that will be applied to the wrapper div.</small></label><input type="text" ng-model="archetypeConfigRenderModel.customCssClass"/>
</div>
<div>
<label>CSS File<small>(Optional) Enter a path for a custom CSS file to be included on the page.</small></label><input type="text" ng-model="archetypeConfigRenderModel.customCssPath"/>
</div>
<div>
<label>JS File<small>(Optional) Enter a path for a custom JS file to be included on the page.</small></label><input type="text" ng-model="archetypeConfigRenderModel.customJsPath"/>
</div>
<div>
<label>Toggle Developer Mode<small>Turn on for developer options.</small></label><input type="checkbox" ng-model="archetypeConfigRenderModel.developerMode"/>
</div>
<div>
<label>Config Model<small>Be careful editing the text below, it controls the schema for this archetype.</small></label>
<textarea class="archetypeDeveloperModel" ng-model="archetypeConfigRenderModel"></textarea>
</div>
</div>
</div>
+22 -20
View File
@@ -1,8 +1,8 @@
<div class="archetypeEditor ng-class:model.config.customCssClass" ng-controller="Imulus.ArchetypeController">
<textarea class="archetypeDeveloperModel" ng-show="model.config.developerMode" ng-model="archetypeRenderModel"></textarea>
<div class="archetypeFieldsetToolbar" ng-show="model.config.fieldsetModels.length > 1 && model.config.hideFieldsetToolbar != '1'">
<div class="archetypeFieldsetToolbar" ng-show="model.config.fieldsets.length > 1 && model.config.hideFieldsetToolbar != '1'">
<ul>
<li ng-repeat="fieldsetModel in model.config.fieldsetModels" ng-click="addRow(fieldsetModel.alias)">
<li ng-repeat="fieldsetModel in model.config.fieldsets" ng-click="addRow(fieldsetModel.alias)">
<img ng-src='{{fieldsetModel.icon}}' title="{{fieldsetModel.tooltip}}" />
<span>{{fieldsetModel.label}}</span>
</li>
@@ -10,32 +10,34 @@
</div>
<ul ui-sortable="sortableOptions" ng-model="archetypeRenderModel.fieldsets">
<li ng-repeat="fieldset in archetypeRenderModel.fieldsets" ng-hide="fieldset.remove">
<fieldset ng-class="fieldset.alias" ng-init="fieldsetConfigModel=getConfigFieldsetByAlias(fieldset.alias)">
<label ng-hide="model.config.hideFieldsetLabels == '1'">
<fieldset ng-class="{archetypeFieldsetError: !fieldset.isValid}" ng-init="fieldsetConfigModel = getConfigFieldsetByAlias(fieldset.alias)">
<label ng-hide="model.config.hideFieldsetLabels == '1'" ng-click="focusFieldset(fieldset)">
<img ng-src='{{fieldsetConfigModel.icon}}' title="{{fieldsetConfigModel.tooltip}}"/>
<span>{{fieldsetConfigModel.label}}</span>
<span ng-bind="getFieldsetTitle(fieldsetConfigModel, $index)"></span>
</label>
<div class="archetypeEditorControls" ng-hide="model.config.hideFieldsetControls || model.config.maxProperties == '1'">
<div class="archetypeEditorControls" ng-hide="model.config.hideFieldsetControls || model.config.maxFieldsets == '1'">
<i class="icon icon-add" ng-click="addRow(fieldset.alias, $index)" ng-show="canAdd()"></i>
<i class="icon icon-delete" ng-click="removeRow($index)" ng-show="canRemove()"></i>
<i class="icon icon-navigation handle" ng-show="canSort()"></i>
</div>
<div class="archetypeFieldsetHeaderText" ng-show="fieldsetConfigModel.headerText">
<em>{{fieldsetConfigModel.headerText}}</em>
</div>
<div class="archetypeProperty" ng-repeat="property in fieldsetConfigModel.properties">
<label ng-hide="archetypeConfig.hidePropertyLabels == '1'">
<span>{{property.label}}</span>
<div class="archetypeFieldsetHelpText" ng-show="property.helpText">
<em>{{property.helpText}}</em>
</div>
</label>
<div class="arechetypeCollapser" ng-hide="fieldset.collapse">
<div class="archetypeFieldsetHeaderText" ng-show="fieldsetConfigModel.headerText">
<em>{{fieldsetConfigModel.headerText}}</em>
</div>
<div ng-class="{archetypePropertyError: getPropertyValidity($parent.$index, property.alias) === false}" class="archetypeProperty" ng-repeat="property in fieldsetConfigModel.properties">
<label ng-hide="archetypeConfig.hidePropertyLabels == '1'">
<span>{{property.label}}</span>
<div class="archetypeFieldsetHelpText" ng-show="property.helpText">
<em>{{property.helpText}}</em>
</div>
</label>
<archetype-property class="archetypeEditor ng-class:property.alias" property="property" fieldset-index="$parent.$index" fieldset="fieldset" archetype-config="model.config" property-config-index="$index" archetype-render-model="archetypeRenderModel"></archetype-property>
<archetype-property class="archetypeEditor ng-class:property.alias" property="property" fieldset-index="$parent.$index" fieldset="fieldset" archetype-config="model.config" property-config-index="$index" archetype-render-model="archetypeRenderModel"></archetype-property>
</div>
<div class="archetypeFieldsetFooterText" ng-show="fieldsetConfigModel.footerText">
<em>{{fieldsetConfigModel.footerText}}</em>
</div>
</div>
<div class="archetypeFieldsetFooterText" ng-show="fieldsetConfigModel.footerText">
<em>{{fieldsetConfigModel.footerText}}</em>
</div>
</fieldset>
</li>
</ul>
+4 -1
View File
@@ -15,6 +15,9 @@
"grunt-touch": "~0.1.0",
"guid": "0.0.12",
"adm-zip": "~0.4.3",
"rimraf": "~2.2.5"
"rimraf": "~2.2.5",
"grunt-nuget": "~0.1.1",
"grunt-template": "~0.2.2",
"grunt-touch": "~0.1.0"
}
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>Archetype</id>
<version><%= version %></version>
<title>Archetype</title>
<authors>imulus, tomfulton</authors>
<owners>imulus, tomfulton</owners>
<projectUrl>http://github.com/imulus/archetype</projectUrl>
<description><![CDATA[Archetype]]></description>
<tags>umbraco</tags>
<iconUrl>http://github.com/imulus/archetype/raw/master/assets/logo.png</iconUrl>
<licenseUrl>http://www.opensource.org/licenses/mit-license.php</licenseUrl>
</metadata>
<files>
<% for (var file in files) { %>
<file src="<%- files[file].path %>" target="<%= files[file].target %>" />
<% } %>
</files>
</package>