├── template ├── module.css ├── README.md ├── _Installation │ ├── ReleaseNotes.md │ ├── SQL │ │ ├── 01.00.00.SqlDataProvider │ │ └── Uninstall.SqlDataProvider │ ├── License.txt │ └── Company.Project.dnn ├── Project.html ├── Common │ ├── ISettingsStore.cs │ ├── Globals.cs │ ├── PortalScopedSettings.cs │ ├── ModuleScopedSettings.cs │ ├── TabModuleScopedSettings.cs │ ├── ProjectApiController.cs │ ├── RouteMapper.cs │ ├── ContextSecurity.cs │ ├── AuditableEntity.cs │ ├── ModuleSettings.cs │ ├── ProjectAuthorizeAttribute.cs │ ├── StringBasedSettings.cs │ └── Localization.cs ├── Repositories │ ├── WidgetBaseRepository.cs │ └── WidgetRepository.cs ├── js │ └── src │ │ ├── Widget.js │ │ ├── Project.js │ │ └── service.js ├── Project.sln ├── Controllers │ ├── SettingsController.cs │ ├── ModuleController.cs │ ├── WidgetsController.cs │ └── WidgetsController_Services.cs ├── Models │ └── Widgets │ │ ├── Widget.cs │ │ └── WidgetBase.cs ├── Properties │ └── AssemblyInfo.cs ├── .gitignore ├── _package.json ├── gulpfile.js ├── Data │ └── RepositoryImpl.cs ├── App_LocalResources │ ├── ClientResources.resx │ └── SharedResources.resx └── Project.csproj ├── .gitignore ├── .gitattributes ├── .yo-rc.json ├── .travis.yml ├── .editorconfig ├── .jshintrc ├── test └── test-app.js ├── package.json ├── generators └── app │ ├── util.js │ └── index.js └── README.md /template/module.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-generator": {} 3 | } -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | # <%= props.organization %> <%= props.projectName %> module 2 | 3 | -------------------------------------------------------------------------------- /template/_Installation/ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | ## Release Notes 2 | Here are some notes 3 | 4 | -------------------------------------------------------------------------------- /template/_Installation/SQL/01.00.00.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /* 2 | SQL Install Scripts 3 | */ 4 | -------------------------------------------------------------------------------- /template/_Installation/SQL/Uninstall.SqlDataProvider: -------------------------------------------------------------------------------- 1 | /* 2 | SQL Uninstall Scripts 3 | */ 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'iojs' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true 17 | } 18 | -------------------------------------------------------------------------------- /template/Project.html: -------------------------------------------------------------------------------- 1 | [JavaScript:{ path: "~/DesktopModules/<%= props.organization %>/<%= props.projectName %>/js/react.min.js"}] 2 | [JavaScript:{ path: "~/DesktopModules/<%= props.organization %>/<%= props.projectName %>/js/<%= props.projectName.toLowerCase() %>.js"}] 3 |
4 | 5 | -------------------------------------------------------------------------------- /template/Common/ISettingsStore.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 4 | { 5 | public interface ISettingsStore 6 | { 7 | T Get(T @default = default(T), [CallerMemberName] string name = null); 8 | void Set(T value, [CallerMemberName] string name = null); 9 | void Save(); 10 | } 11 | } -------------------------------------------------------------------------------- /template/Repositories/WidgetBaseRepository.cs: -------------------------------------------------------------------------------- 1 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Data; 2 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s; 3 | 4 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Repositories 5 | { 6 | public class <%= props.widgetName %>BaseRepository : RepositoryImpl<<%= props.widgetName %>Base> 7 | { 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /template/Repositories/WidgetRepository.cs: -------------------------------------------------------------------------------- 1 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Data; 2 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s; 3 | 4 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Repositories 5 | { 6 | 7 | public class <%= props.widgetName %>Repository : RepositoryImpl<<%= props.widgetName %>> 8 | { 9 | } 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /template/Common/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 2 | { 3 | public static class Globals 4 | { 5 | 6 | public const string ClientResourceFileName = "~/DesktopModules/<%= props.organization %>/<%= props.projectName %>/App_LocalResources/ClientResources.resx"; 7 | public const string SharedResourceFileName = "~/DesktopModules/<%= props.organization %>/<%= props.projectName %>/App_LocalResources/SharedResources.resx"; 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /template/Common/PortalScopedSettings.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Entities.Portals; 2 | 3 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 4 | { 5 | public class PortalScopedSettings : StringBasedSettings 6 | { 7 | public PortalScopedSettings(int portalId) 8 | : base( 9 | name => PortalController.GetPortalSetting(name, portalId, ""), 10 | (name, value) => PortalController.UpdatePortalSetting(portalId, name, value, true) 11 | ) 12 | { } 13 | } 14 | } -------------------------------------------------------------------------------- /template/Common/ModuleScopedSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using DotNetNuke.Entities.Modules; 3 | 4 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 5 | { 6 | public class ModuleScopedSettings : StringBasedSettings 7 | { 8 | public ModuleScopedSettings(int moduleId, Hashtable moduleSettings) 9 | : base( 10 | name => moduleSettings[name] as string, 11 | (name, value) => new ModuleController().UpdateModuleSetting(moduleId, name, value) 12 | ) { } 13 | } 14 | } -------------------------------------------------------------------------------- /template/Common/TabModuleScopedSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using DotNetNuke.Entities.Modules; 3 | 4 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 5 | { 6 | public class TabModuleScopedSettings : StringBasedSettings 7 | { 8 | public TabModuleScopedSettings(int tabModuleId, Hashtable moduleSettings) 9 | : base( 10 | name => moduleSettings[name] as string, 11 | (name, value) => new ModuleController().UpdateTabModuleSetting(tabModuleId, name, value) 12 | ) { } 13 | } 14 | } -------------------------------------------------------------------------------- /test/test-app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var assert = require('yeoman-generator').assert; 5 | var helpers = require('yeoman-generator').test; 6 | var os = require('os'); 7 | 8 | describe('dnn-spa-gulp-react:app', function () { 9 | before(function (done) { 10 | helpers.run(path.join(__dirname, '../generators/app')) 11 | .withOptions({ skipInstall: true }) 12 | .withPrompts({ someOption: true }) 13 | .on('end', done); 14 | }); 15 | 16 | it('creates files', function () { 17 | assert.file([ 18 | 'bower.json', 19 | 'package.json', 20 | '.editorconfig', 21 | '.jshintrc' 22 | ]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /template/Common/ProjectApiController.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Web.Api; 2 | 3 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 4 | { 5 | public abstract class <%= props.projectName %>ApiController : DnnApiController 6 | { 7 | private ModuleSettings _settings; 8 | public ModuleSettings Settings 9 | { 10 | get { return _settings ?? (_settings = ModuleSettings.GetSettings(ActiveModule)); } 11 | set { _settings = value; } 12 | } 13 | 14 | private ContextSecurity _security; 15 | public ContextSecurity Security 16 | { 17 | get { return _security ?? (_security = new ContextSecurity(ActiveModule)); } 18 | set { _security = value; } 19 | } 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /template/Common/RouteMapper.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Web.Api; 2 | 3 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 4 | { 5 | public class RouteMapper : IServiceRouteMapper 6 | { 7 | 8 | #region IServiceRouteMapper 9 | public void RegisterRoutes(IMapRoute routeManager) 10 | { 11 | routeManager.MapHttpRoute("<%= props.organization %>/<%= props.projectName %>", "<%= props.organization %><%= props.projectName %>1", "{controller}/{action}", null, null, new[] { "<%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers" }); 12 | routeManager.MapHttpRoute("<%= props.organization %>/<%= props.projectName %>", "<%= props.organization %><%= props.projectName %>2", "{controller}/{action}/{id}", null, new { id = "\\d*" }, new[] { "<%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers" }); 13 | } 14 | #endregion 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /template/js/src/Widget.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | var <%= props.widgetName %>Component = React.createClass({ 3 | 4 | getInitialState: function() { 5 | return { 6 | moduleId: this.props.moduleId, 7 | service: <%= props.organization %><%= props.projectName %>.modules[this.props.moduleId].service, 8 | security: <%= props.organization %><%= props.projectName %>.modules[this.props.moduleId].security, 9 | settings: <%= props.organization %><%= props.projectName %>.modules[this.props.moduleId].settings, 10 | resources: <%= props.organization %><%= props.projectName %>.modules[this.props.moduleId].resources 11 | } 12 | }, 13 | 14 | shouldComponentUpdate: function(nextProps, nextState) { 15 | return nextState.settings !== this.state.settings; 16 | }, 17 | 18 | render: function() { 19 | return ( 20 |
21 | Hello World 22 |
23 | ); 24 | } 25 | 26 | }); 27 | 28 | module.exports = <%= props.organization %><%= props.projectName %>Component; 29 | -------------------------------------------------------------------------------- /template/Common/ContextSecurity.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Entities.Modules; 2 | using DotNetNuke.Entities.Portals; 3 | using DotNetNuke.Entities.Users; 4 | using DotNetNuke.Security; 5 | using DotNetNuke.Security.Permissions; 6 | 7 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 8 | { 9 | public class ContextSecurity 10 | { 11 | public bool CanView { get; set; } 12 | public bool CanEdit { get; set; } 13 | public bool IsAdmin { get; set; } 14 | public int UserId { get; set; } 15 | 16 | #region ctor 17 | public ContextSecurity(ModuleInfo objModule) 18 | { 19 | UserId = UserController.Instance.GetCurrentUserInfo().UserID; 20 | CanView = ModulePermissionController.CanViewModule(objModule); 21 | CanEdit = ModulePermissionController.HasModulePermission(objModule.ModulePermissions, "EDIT"); 22 | IsAdmin = PortalSecurity.IsInRole(PortalSettings.Current.AdministratorRoleName); 23 | } 24 | #endregion 25 | 26 | } 27 | } -------------------------------------------------------------------------------- /template/Project.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "<%= props.projectName %>", "<%= props.projectName %>.csproj", "{7D61A32C-0F21-453F-A981-BD8E5A3A5304}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {7D61A32C-0F21-453F-A981-BD8E5A3A5304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {7D61A32C-0F21-453F-A981-BD8E5A3A5304}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {7D61A32C-0F21-453F-A981-BD8E5A3A5304}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {7D61A32C-0F21-453F-A981-BD8E5A3A5304}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-dnn-spa-gulp-react", 3 | "version": "0.1.2", 4 | "description": "Yeoman generator for a DNN SPA module using Gulp and React", 5 | "license": "MIT", 6 | "main": "app/index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/donker/generator-dnn-spa-gulp-react.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/donker/generator-dnn-spa-gulp-react/issues" 13 | }, 14 | "homepage": "https://github.com/donker/generator-dnn-spa-gulp-react#readme", 15 | "author": { 16 | "name": "Peter Donker", 17 | "email": "peter@bring2mind.net", 18 | "url": "https://github.com/donker" 19 | }, 20 | "scripts": { 21 | "test": "mocha" 22 | }, 23 | "files": [ 24 | "generators", 25 | "template" 26 | ], 27 | "keywords": [ 28 | "yeoman-generator" 29 | ], 30 | "dependencies": { 31 | "yeoman-generator": "^0.19.0", 32 | "extend": "^3.0.0", 33 | "chalk": "^1.0.0", 34 | "yosay": "^1.0.2" 35 | }, 36 | "devDependencies": { 37 | "mocha": "*" 38 | }, 39 | "directories": {} 40 | } 41 | -------------------------------------------------------------------------------- /template/Controllers/SettingsController.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Web.Http; 4 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common; 5 | using DotNetNuke.Web.Api; 6 | 7 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers 8 | { 9 | 10 | public partial class SettingsController : <%= props.projectName %>ApiController 11 | { 12 | public class SettingsDTO 13 | { 14 | public int MySetting { get; set; } 15 | } 16 | 17 | 18 | #region Service Methods 19 | [HttpPost] 20 | [MapAuthorize(SecurityLevel = SecurityAccessLevel.Edit)] 21 | [ValidateAntiForgeryToken] 22 | public HttpResponseMessage Update(SettingsDTO newSettings) 23 | { 24 | var oldSettings = ModuleSettings.GetSettings(ActiveModule); 25 | oldSettings.MySetting = newSettings.MySetting; 26 | oldSettings.SaveSettings(); 27 | return Request.CreateResponse(HttpStatusCode.OK, oldSettings); 28 | } 29 | #endregion 30 | 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /generators/app/util.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = { 4 | 5 | getFilesRecursive: function(basePath, path) { 6 | var that = this; 7 | var foundPaths = []; 8 | var files = fs.readdirSync(basePath + '/' + path); 9 | files.forEach(function(el, i, arr) { 10 | var newPath = basePath + '/' + path + '/' + el; 11 | var st = fs.statSync(newPath); 12 | if (st.isDirectory()) { 13 | if (path === '') { 14 | foundPaths = foundPaths.concat(that.getFilesRecursive(basePath, el)); 15 | } else { 16 | foundPaths = foundPaths.concat(that.getFilesRecursive(basePath, path + '/' + el)); 17 | } 18 | } else { 19 | if (path === '') { 20 | foundPaths.push(el); 21 | } else { 22 | foundPaths.push(path + '/' + el); 23 | } 24 | } 25 | }); 26 | return foundPaths; 27 | }, 28 | 29 | ensureFolder: function(path) { 30 | try { 31 | stats = fs.lstatSync(path); 32 | if (stats.isDirectory()) { 33 | return; 34 | } 35 | } catch (e) { 36 | fs.mkdir(path); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /template/Common/AuditableEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Runtime.Serialization; 4 | using DotNetNuke.Common.Utilities; 5 | 6 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 7 | { 8 | 9 | [DataContract] 10 | public abstract class AuditableEntity 11 | { 12 | 13 | public void FillAuditFields(IDataReader dr) 14 | { 15 | CreatedByUserID = Convert.ToInt32(Null.SetNull(dr["CreatedByUserID"], CreatedByUserID)); 16 | CreatedOnDate = Convert.ToDateTime(Null.SetNull(dr["CreatedOnDate"], CreatedOnDate)); 17 | LastModifiedByUserID = Convert.ToInt32(Null.SetNull(dr["LastModifiedByUserID"], LastModifiedByUserID)); 18 | LastModifiedOnDate = Convert.ToDateTime(Null.SetNull(dr["LastModifiedOnDate"], LastModifiedOnDate)); 19 | } 20 | 21 | #region Public Properties 22 | [DataMember] 23 | public int CreatedByUserID { get; set; } 24 | [DataMember] 25 | public DateTime CreatedOnDate { get; set; } 26 | [DataMember] 27 | public int LastModifiedByUserID { get; set; } 28 | [DataMember] 29 | public DateTime LastModifiedOnDate { get; set; } 30 | #endregion 31 | 32 | 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /template/_Installation/License.txt: -------------------------------------------------------------------------------- 1 | <%= props.organization %> - <%= props.url %>
2 | Copyright (c) 2002-2015
3 | by <%= props.organization %> 4 |

5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 |

10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions 11 | of the Software. 12 |

13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 14 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 15 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 16 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /template/_Installation/Company.Project.dnn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= props.organization %>_<%= props.projectName %> 8 | <%= props.organization %>/<%= props.projectName %> 9 | 10 | 11 | 12 | <%= props.organization %> <%= props.projectName %> 13 | 0 14 | 15 | 16 | 17 | DesktopModules/<%= props.organization %>/<%= props.projectName %>/<%= props.projectName %>.html 18 | False 19 | 20 | View 21 | 22 | 23 | 0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /template/Models/Widgets/Widget.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using DotNetNuke.ComponentModel.DataAnnotations; 3 | 4 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s 5 | { 6 | 7 | [TableName("vw_<%= props.organization %>_<%= props.projectName %>_<%= props.widgetName %>s")] 8 | [PrimaryKey("<%= props.widgetName %>Id", AutoIncrement = true)] 9 | [Scope("ModuleId")] 10 | [DataContract] 11 | public class <%= props.widgetName %> : <%= props.widgetName %>Base 12 | { 13 | 14 | #region Public Properties 15 | [DataMember] 16 | public string CreatedByUser { get; set; } 17 | [DataMember] 18 | public string LastModifiedByUser { get; set; } 19 | #endregion 20 | 21 | #region Public Methods 22 | public <%= props.widgetName %>Base Get<%= props.widgetName %>Base() 23 | { 24 | <%= props.widgetName %>Base res = new <%= props.widgetName %>Base(); 25 | res.CreatedByUserID = CreatedByUserID; 26 | res.CreatedOnDate = CreatedOnDate; 27 | res.LastModifiedByUserID = LastModifiedByUserID; 28 | res.LastModifiedOnDate = LastModifiedOnDate; 29 | res.<%= props.widgetName %>Id = <%= props.widgetName %>Id; 30 | res.ModuleId = ModuleId; 31 | res.Message = Message; 32 | return res; 33 | } 34 | #endregion 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /template/Controllers/ModuleController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Web.Http; 5 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common; 6 | 7 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers 8 | { 9 | 10 | public partial class ModuleController : <%= props.projectName %>ApiController 11 | { 12 | 13 | public class InitData 14 | { 15 | public ModuleSettings Settings { get; set; } 16 | public IEnumerable<<%= props.widgetName %>> <%= props.widgetName %>s { get; set; } 17 | public ContextSecurity Security { get; set; } 18 | public Dictionary ClientResources { get; set; } 19 | } 20 | 21 | #region Service Methods 22 | [HttpGet] 23 | [<%= props.projectName %>Authorize(SecurityLevel = SecurityAccessLevel.View)] 24 | public HttpResponseMessage InitialData() 25 | { 26 | InitData init = new InitData(); 27 | init.Settings = ModuleSettings.GetSettings(ActiveModule); 28 | init.Security = new ContextSecurity(ActiveModule); 29 | init.ClientResources = Localization.GetResourceFile(PortalSettings, ClientResourceFileName, 30 | System.Threading.Thread.CurrentThread.CurrentCulture.Name); 31 | return Request.CreateResponse(HttpStatusCode.OK, init); 32 | } 33 | #endregion 34 | 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /template/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("<%= props.projectName %> Module")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("<%= props.organization %>")] 11 | [assembly: AssemblyProduct("<%= props.projectName %>")] 12 | [assembly: AssemblyCopyright("Copyright 2015 by <%= props.organization %>")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("5ef01dd5-84a1-49f3-9232-067440288455")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | [assembly: AssemblyVersion("01.00.00")] 34 | [assembly: AssemblyFileVersion("01.00.00")] 35 | -------------------------------------------------------------------------------- /template/js/src/Project.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | var <%= props.organization %><%= props.projectName %>Component = require('./<%= props.widgetName %>'), 3 | <%= props.projectName %>Service = require('./service'); 4 | 5 | ; 6 | (function($, window, document, undefined) { 7 | 8 | $(document).ready(function() { 9 | <%= props.organization %><%= props.projectName %>.loadData(); 10 | }); 11 | 12 | window.<%= props.organization %><%= props.projectName %> = { 13 | modules: {}, 14 | 15 | loadData: function() { 16 | $('.<%= props.organization %><%= props.projectName %>').each(function(i, el) { 17 | var moduleId = $(el).data('moduleid'); 18 | var newModule = { 19 | service: new <%= props.projectName %>Service($, moduleId) 20 | }; 21 | <%= props.organization %><%= props.projectName %>.modules[moduleId] = newModule; 22 | <%= props.organization %><%= props.projectName %>.modules[moduleId].service.getInitialData(function(data) { 23 | <%= props.organization %><%= props.projectName %>.modules[moduleId].settings = data.Settings; 24 | <%= props.organization %><%= props.projectName %>.modules[moduleId].<%= props.widgetName %>s = data.<%= props.widgetName %>s; 25 | <%= props.organization %><%= props.projectName %>.modules[moduleId].security = data.Security; 26 | <%= props.organization %><%= props.projectName %>.modules[moduleId].resources = data.ClientResources; 27 | }); 28 | }); 29 | } 30 | 31 | } 32 | 33 | 34 | })(jQuery, window, document); 35 | -------------------------------------------------------------------------------- /template/Models/Widgets/WidgetBase.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Runtime.Serialization; 4 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common; 5 | using DotNetNuke.ComponentModel.DataAnnotations; 6 | 7 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s 8 | { 9 | [TableName("<%= props.organization %>_<%= props.projectName %>_<%= props.widgetName %>s")] 10 | [PrimaryKey("<%= props.widgetName %>Id", AutoIncrement = true)] 11 | [Scope("ModuleId")] 12 | [DataContract] 13 | public class <%= props.widgetName %>Base : AuditableEntity 14 | { 15 | 16 | #region Public Properties 17 | [DataMember] 18 | public int <%= props.widgetName %>Id { get; set; } 19 | [DataMember] 20 | public int ModuleId { get; set; } 21 | [DataMember] 22 | public string Message { get; set; } 23 | #endregion 24 | 25 | #region Methods 26 | public void Read<%= props.widgetName %>Base(<%= props.widgetName %>Base <%= props.widgetName %>) 27 | { 28 | if (<%= props.widgetName %>.<%= props.widgetName %>Id > -1) 29 | <%= props.widgetName %>Id = <%= props.widgetName %>.<%= props.widgetName %>Id; 30 | 31 | if (<%= props.widgetName %>.ModuleId > -1) 32 | ModuleId = <%= props.widgetName %>.ModuleId; 33 | 34 | if (!String.IsNullOrEmpty(<%= props.widgetName %>.Message)) 35 | Message = <%= props.widgetName %>.Message; 36 | 37 | } 38 | #endregion 39 | 40 | } 41 | } 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /template/js/src/service.js: -------------------------------------------------------------------------------- 1 | var <%= props.organization %><%= props.projectName %>Service = function ($, mid) { 2 | var moduleId = mid; 3 | var baseServicepath = $.dnnSF(moduleId).getServiceRoot('<%= props.organization %>/<%= props.projectName %>'); 4 | 5 | this.ajaxCall = function(type, controller, action, id, data, success, fail) { 6 | // showLoading(); 7 | $.ajax({ 8 | type: type, 9 | url: baseServicepath + controller + '/' + action + (id != null ? '/' + id : ''), 10 | beforeSend: $.dnnSF(moduleId).setModuleHeaders, 11 | data: data 12 | }).done(function(retdata) { 13 | // hideLoading(); 14 | if (success != undefined) { 15 | success(retdata); 16 | } 17 | }).fail(function(xhr, status) { 18 | // showError(xhr.responseText); 19 | if (fail != undefined) { 20 | fail(xhr.responseText); 21 | } 22 | }); 23 | } 24 | 25 | this.getInitialData = function(success) { 26 | this.ajaxCall('GET', 'Module', 'InitialData', null, null, success); 27 | } 28 | 29 | this.updateSettings = function(newSettings, success) { 30 | this.ajaxCall('POST', 'Settings', 'Update', null, newSettings, success); 31 | } 32 | 33 | this.submitPoint = function(new<%= props.widgetName %>, success) { 34 | this.ajaxCall('POST', '<%= props.widgetName %>s', '<%= props.widgetName %>', null, new<%= props.widgetName %>, success); 35 | } 36 | 37 | this.deletePoint = function(id, success) { 38 | this.ajaxCall('POST', '<%= props.widgetName %>s', 'Delete', id, null, success); 39 | } 40 | 41 | } 42 | 43 | module.exports = <%= props.organization %><%= props.projectName %>Service; 44 | -------------------------------------------------------------------------------- /template/Common/ModuleSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Caching; 3 | using DotNetNuke.Common.Utilities; 4 | using DotNetNuke.Entities.Modules; 5 | 6 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 7 | { 8 | public class ModuleSettings 9 | { 10 | 11 | #region Properties 12 | internal ISettingsStore TmsStore; 13 | internal ISettingsStore MsStore; 14 | internal ISettingsStore PsStore; 15 | public int MySetting { get { return TmsStore.Get(1); } set { TmsStore.Set(value); } } 16 | public string Version = typeof(ModuleSettings).Assembly.GetName().Version.ToString(); 17 | #endregion 18 | 19 | #region .ctor 20 | public ModuleSettings(ModuleInfo ctlModule) 21 | { 22 | PsStore = new PortalScopedSettings(ctlModule.PortalID); 23 | MsStore = new ModuleScopedSettings(ctlModule.ModuleID, ctlModule.ModuleSettings); 24 | TmsStore = new TabModuleScopedSettings(ctlModule.TabModuleID, ctlModule.TabModuleSettings); 25 | } 26 | #endregion 27 | 28 | #region Public Members 29 | public void SaveSettings() 30 | { 31 | PsStore.Save(); 32 | MsStore.Save(); 33 | TmsStore.Save(); 34 | } 35 | 36 | public static ModuleSettings GetSettings(ModuleInfo ctlModule) 37 | { 38 | return CBO.GetCachedObject(new CacheItemArgs(CacheKey(ctlModule.TabModuleID), 20, CacheItemPriority.AboveNormal), 39 | cacheItemArgs => new ModuleSettings(ctlModule), 40 | true); 41 | } 42 | 43 | public static string CacheKey(int moduleId) 44 | { 45 | return string.Format("SettingsModule{0}", moduleId); 46 | } 47 | #endregion 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Object Folders 2 | bin/ 3 | obj/ 4 | 5 | #User Specific Files 6 | *.user 7 | *.suo 8 | 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # Others 83 | [Bb]in 84 | [Oo]bj 85 | TestResults 86 | [Tt]est[Rr]esult* 87 | *.Cache 88 | ClientBin 89 | [Ss]tyle[Cc]op.* 90 | ~$* 91 | *.dbmdl 92 | Generated_Code #added for RIA/Silverlight projects 93 | 94 | # mac stuff 95 | *DS_Store 96 | 97 | # Backup & report files from converting an old project file to a newer 98 | # Visual Studio version. Backup files are not needed, because we have git ;-) 99 | _UpgradeReport_Files/ 100 | Backup*/ 101 | UpgradeLog*.XML 102 | 103 | node_modules 104 | bower_components 105 | _Packages 106 | js/*.js 107 | 108 | 109 | -------------------------------------------------------------------------------- /template/Common/ProjectAuthorizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Common; 2 | using DotNetNuke.Entities.Users; 3 | using DotNetNuke.Web.Api; 4 | 5 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 6 | { 7 | public enum SecurityAccessLevel 8 | { 9 | Anonymous = 0, 10 | View = 1, 11 | Edit = 2, 12 | Admin = 3, 13 | Host = 4 14 | } 15 | 16 | public class <%= props.projectName %>AuthorizeAttribute : AuthorizeAttributeBase, IOverrideDefaultAuthLevel 17 | { 18 | public SecurityAccessLevel SecurityLevel { get; set; } 19 | public UserInfo User { get; set; } 20 | 21 | public <%= props.projectName %>AuthorizeAttribute() 22 | { 23 | SecurityLevel = SecurityAccessLevel.Admin; 24 | } 25 | 26 | public <%= props.projectName %>AuthorizeAttribute(SecurityAccessLevel accessLevel) 27 | { 28 | SecurityLevel = accessLevel; 29 | } 30 | 31 | public override bool IsAuthorized(AuthFilterContext context) 32 | { 33 | if (SecurityLevel == SecurityAccessLevel.Anonymous) 34 | { 35 | return true; 36 | } 37 | User = HttpContextSource.Current.Request.IsAuthenticated ? UserController.Instance.GetCurrentUserInfo() : new UserInfo(); 38 | ContextSecurity security = new ContextSecurity(context.ActionContext.Request.FindModuleInfo()); 39 | switch (SecurityLevel) 40 | { 41 | case SecurityAccessLevel.Host: 42 | return User.IsSuperUser; 43 | case SecurityAccessLevel.Admin: 44 | return security.IsAdmin | User.IsSuperUser; 45 | case SecurityAccessLevel.Edit: 46 | return security.CanEdit | security.IsAdmin | User.IsSuperUser; 47 | case SecurityAccessLevel.View: 48 | return security.CanView; 49 | } 50 | 51 | return false; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNN SPA Module generator with Gulp and React 2 | 3 | > A [Yeoman](http://yeoman.io) generator 4 | 5 | This generator scaffolds out a module leveraging DNN's SPA module pattern. Included are references to Gulp and React so if you're using those then you'll hit the road running. This generator *must be run in the DesktopModules folder*. It will create subfolders for the organization and module name based on your input. It will allow you to store common settings such as name, email etc to the generator's settings so you don't need to type them in every time you create a module. 6 | 7 | ## Getting Started 8 | 9 | ### What is Yeoman? (skip if you know Yeoman and have it installed) 10 | 11 | Head over to the [Yeoman site](http://yeoman.io) for in-depth info. Basically it is a code generator based on templates called "generators". You can install Yeoman using npm (i.e. you also need Node js installed first!) as follows: 12 | 13 | ```bash 14 | npm install -g yo 15 | ``` 16 | 17 | ### Installing the DNN SPA module generator 18 | 19 | To install this generator, run: 20 | 21 | ```bash 22 | npm install -g generator-dnn-spa-gulp-react 23 | ``` 24 | 25 | ### Use 26 | 27 | Now, head over to the DesktopModules folder of your project, start a shell and type: 28 | 29 | ```bash 30 | yo dnn-spa-gulp-react 31 | ``` 32 | 33 | This will prompt you with a few questions, some of which are mandatory to answer as they are used to configure the module: 34 | 35 | 1. Project name. Use a short name without special characters or spaces as it is used for namespaces and folders. (e.g. "Map") 36 | 2. Primary object name. This is the name of the primary object of your module that you're managing. You can leave this as-is. Again a short name without special characters or spaces (e.g. MapPoints). 37 | 3. Organization name. Used to create the subfolder and namespaces as well. So like the above without special characters or spaces, please (e.g. DNNConnect). 38 | 4. Url. Url of your organization or any other url you'd like to use for this project. It's used in the DNN manifest. 39 | 5. Your name. Used in the manifest. 40 | 6. Email. Used in the manifest. 41 | 42 | Finally the generator will ask if you wish to overwrite the old settings in the template so that any subsequent module generations will prompt the same name, email, etc. 43 | 44 | ## License 45 | 46 | MIT 47 | -------------------------------------------------------------------------------- /template/Controllers/WidgetsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s; 4 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Repositories; 5 | 6 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers 7 | { 8 | 9 | public partial class <%= props.widgetName %>sController 10 | { 11 | 12 | public static IEnumerable<<%= props.widgetName %>> Get<%= props.widgetName %>s(int moduleId) 13 | { 14 | <%= props.widgetName %>Repository repo = new <%= props.widgetName %>Repository(); 15 | return repo.Get(moduleId); 16 | } 17 | 18 | public static <%= props.widgetName %> Get<%= props.widgetName %>(int <%= props.widgetName %>Id, int moduleId) 19 | { 20 | <%= props.widgetName %>Repository repo = new <%= props.widgetName %>Repository(); 21 | return repo.GetById(<%= props.widgetName %>Id, moduleId); 22 | } 23 | 24 | public static int Add<%= props.widgetName %>(ref <%= props.widgetName %>Base <%= props.widgetName %>, int userId) 25 | { 26 | <%= props.widgetName %>.CreatedByUserID = userId; 27 | <%= props.widgetName %>.CreatedOnDate = DateTime.Now; 28 | <%= props.widgetName %>.LastModifiedByUserID = userId; 29 | <%= props.widgetName %>.LastModifiedOnDate = DateTime.Now; 30 | <%= props.widgetName %>BaseRepository repo = new <%= props.widgetName %>BaseRepository(); 31 | repo.Insert(<%= props.widgetName %>); 32 | return <%= props.widgetName %>.<%= props.widgetName %>Id; 33 | } 34 | 35 | public static void Update<%= props.widgetName %>(<%= props.widgetName %>Base <%= props.widgetName %>, int userId) 36 | { 37 | <%= props.widgetName %>.LastModifiedByUserID = userId; 38 | <%= props.widgetName %>.LastModifiedOnDate = DateTime.Now; 39 | <%= props.widgetName %>BaseRepository repo = new <%= props.widgetName %>BaseRepository(); 40 | repo.Update(<%= props.widgetName %>); 41 | } 42 | 43 | public static void Delete<%= props.widgetName %>(<%= props.widgetName %>Base <%= props.widgetName %>) 44 | { 45 | <%= props.widgetName %>BaseRepository repo = new <%= props.widgetName %>BaseRepository(); 46 | repo.Delete(<%= props.widgetName %>); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /template/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= props.projectName %>", 3 | "version": "01.00.00", 4 | "description": "Demo module used at DNN <%= props.organization %> 2015", 5 | "dnnModule": { 6 | "fullName": "<%= props.organization %>.<%= props.projectName %>", 7 | "friendlyName": "<%= props.projectName %> Module", 8 | "packageName": "<%= props.organization %>_<%= props.projectName %>", 9 | "owner": { 10 | "name": "<%= props.name %>", 11 | "organization": "<%= props.organization %>", 12 | "url": "<%= props.url %>.org", 13 | "email": "<%= props.email %>" 14 | }, 15 | "module": { 16 | "packageName": "<%= props.organization %>_<%= props.projectName %>", 17 | "folderName": "<%= props.organization %>/<%= props.projectName %>", 18 | "azureCompatible": "true", 19 | "iconFile": "DesktopModules\\<%= props.organization %>\\<%= props.projectName %>\\<%= props.projectName.toLowerCase() %>.png" 20 | }, 21 | "tempPath": "./package", 22 | "packagesPath": "./_Packages", 23 | "pathToAssemblies": "./bin", 24 | "pathToScripts": "./_Installation/SQL", 25 | "pathToSupplementaryFiles": "./_Installation", 26 | "excludeFilter": [ 27 | "node_modules", 28 | "bin", 29 | "obj" 30 | ] 31 | }, 32 | "main": "index.js", 33 | "scripts": { 34 | "test": "echo \"Error: no test specified\" && exit 1" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "git+https://github.com/<%= props.organization %>/<%= props.projectName %>.git" 39 | }, 40 | "author": "", 41 | "license": "ISC", 42 | "bugs": { 43 | "url": "https://github.com/DNN-<%= props.organization %>/<%= props.projectName %>/issues" 44 | }, 45 | "homepage": "https://github.com/DNN-<%= props.organization %>/<%= props.projectName %>#readme", 46 | "devDependencies": { 47 | "gulp": "^3.9.0", 48 | "gulp-browserify": "^0.5.1", 49 | "gulp-dnn-manifest": "^0.1.1", 50 | "gulp-dotnet-assembly-info": "^0.1.11", 51 | "gulp-filter": "^3.0.1", 52 | "gulp-markdown": "^1.1.0", 53 | "gulp-minify-css": "^1.2.1", 54 | "gulp-msbuild": "^0.2.13", 55 | "gulp-plumber": "^1.0.1", 56 | "gulp-react": "^3.0.1", 57 | "gulp-rename": "^1.2.2", 58 | "gulp-uglify": "^1.4.1", 59 | "gulp-util": "^3.0.6", 60 | "gulp-zip": "^3.0.2", 61 | "merge2": "^0.3.6", 62 | "react": "^0.13.3", 63 | "react-tools": "^0.13.3", 64 | "reactify": "^1.1.1" 65 | }, 66 | "dependencies": { 67 | "jquery": "^2.1.4" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /template/Controllers/WidgetsController_Services.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Web.Http; 4 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common; 5 | using <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Models.<%= props.widgetName %>s; 6 | using DotNetNuke.Web.Api; 7 | 8 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Controllers 9 | { 10 | 11 | public partial class <%= props.widgetName %>sController : <%= props.projectName %>ApiController 12 | { 13 | 14 | #region Service Methods 15 | [HttpPost] 16 | [ValidateAntiForgeryToken] 17 | [<%= props.projectName %>Authorize(SecurityLevel = SecurityAccessLevel.Pointer)] 18 | public HttpResponseMessage <%= props.widgetName %>(<%= props.widgetName %>Base postData) 19 | { 20 | <%= props.widgetName %> returnData; 21 | postData.ModuleId = ActiveModule.ModuleID; 22 | if (postData.<%= props.widgetName %>Id == -1) 23 | { 24 | Add<%= props.widgetName %>(ref postData, UserInfo.UserID); 25 | returnData = Get<%= props.widgetName %>(postData.<%= props.widgetName %>Id, ActiveModule.ModuleID); 26 | } 27 | else 28 | { 29 | var oldData = Get<%= props.widgetName %>(postData.<%= props.widgetName %>Id, ActiveModule.ModuleID).Get<%= props.widgetName %>Base(); 30 | if (oldData.CreatedByUserID == UserInfo.UserID | Settings.AllowOtherEdit | Security.CanEdit | Security.IsAdmin) 31 | { 32 | oldData.Latitude = postData.Latitude; 33 | oldData.Longitude = postData.Longitude; 34 | oldData.Message = postData.Message; 35 | Update<%= props.widgetName %>(oldData, UserInfo.UserID); 36 | returnData = Get<%= props.widgetName %>(postData.<%= props.widgetName %>Id, ActiveModule.ModuleID); 37 | } 38 | else 39 | { 40 | return Request.CreateResponse(HttpStatusCode.Unauthorized, ""); 41 | } 42 | } 43 | return Request.CreateResponse(HttpStatusCode.OK, returnData); 44 | } 45 | 46 | [HttpPost] 47 | [ValidateAntiForgeryToken] 48 | [<%= props.projectName %>Authorize(SecurityLevel = SecurityAccessLevel.Pointer)] 49 | public HttpResponseMessage Delete(int id) 50 | { 51 | var <%= props.widgetName %> = Get<%= props.widgetName %>(id, ActiveModule.ModuleID); 52 | if (<%= props.widgetName %>.CreatedByUserID == UserInfo.UserID | Settings.AllowOtherEdit | Security.CanEdit | 53 | Security.IsAdmin) 54 | { 55 | Delete<%= props.widgetName %>(<%= props.widgetName %>.Get<%= props.widgetName %>Base()); 56 | return Request.CreateResponse(HttpStatusCode.OK, ""); 57 | } 58 | return Request.CreateResponse(HttpStatusCode.Unauthorized, <%= props.widgetName %>); 59 | } 60 | 61 | #endregion 62 | 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /template/Common/StringBasedSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Runtime.CompilerServices; 5 | using DotNetNuke.Common; 6 | 7 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 8 | { 9 | public class StringBasedSettings : ISettingsStore 10 | { 11 | Dictionary _cache; 12 | Dictionary _dirty; 13 | Func _get; 14 | Action _set; 15 | 16 | /// Function to get a value out of your setting store 17 | /// Action to save a value back to the setting store 18 | public StringBasedSettings(Func get, Action set) 19 | { 20 | Requires.NotNull("Get", get); 21 | Requires.NotNull("Set", set); 22 | 23 | _get = get; 24 | _set = set; 25 | _cache = new Dictionary(); 26 | _dirty = new Dictionary(); 27 | } 28 | 29 | // All changes to the settings are recorded and get only executed on demand 30 | public void Save() 31 | { 32 | foreach (var save in _dirty.Values) save(); 33 | _dirty.Clear(); 34 | } 35 | 36 | // CallerMemberName is used for the name of the setting. No more magic strings 37 | public string Get(string @default = default(string), [CallerMemberName] string name = null) 38 | { 39 | Requires.NotNull("Name", name); 40 | 41 | if (_cache.ContainsKey(name)) 42 | { 43 | return _cache[name]; 44 | } 45 | else 46 | { 47 | var value = _get(name) ?? @default; 48 | _cache[name] = value; 49 | return value; 50 | } 51 | } 52 | 53 | public T Get(T @default = default(T), [CallerMemberName] string name = null) 54 | { 55 | //required to behave Get and Get the same 56 | if (typeof(T) == typeof(string)) return (T)(object)Get((string)(object)@default, name); 57 | 58 | var converter = TypeDescriptor.GetConverter(typeof(T)); 59 | Requires.NotNull("Converter for T", converter); 60 | 61 | try 62 | { 63 | var defaultValueAsString = converter.ConvertToInvariantString(@default); 64 | string value = Get(defaultValueAsString, name); 65 | return (T)converter.ConvertFromInvariantString(value); 66 | } 67 | catch 68 | { 69 | return @default; 70 | } 71 | } 72 | 73 | public void Set(string value, [CallerMemberName] string name = null) 74 | { 75 | Requires.NotNull("Name", name); 76 | var modified = !(_cache.ContainsKey(name) && _cache[name] == value); 77 | 78 | if (modified) 79 | { 80 | _cache[name] = value; 81 | _dirty[name] = () => _set(name, value); 82 | }; 83 | } 84 | 85 | public void Set(T value, [CallerMemberName] string name = null) 86 | { 87 | //required to behave Set and Set the same 88 | if (typeof(T) == typeof(string)) 89 | Set((string)(object)value, name); 90 | else 91 | { 92 | var converter = TypeDescriptor.GetConverter(typeof(T)); 93 | Requires.NotNull("Converter for T", converter); 94 | 95 | Set(converter.ConvertToInvariantString(value), name); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /template/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | msbuild = require('gulp-msbuild'), 3 | browserify = require('gulp-browserify'), 4 | minifyCss = require('gulp-minify-css'), 5 | uglify = require('gulp-uglify'), 6 | assemblyInfo = require('gulp-dotnet-assembly-info'), 7 | plumber = require('gulp-plumber'), 8 | config = require('./package.json'), 9 | zip = require('gulp-zip'), 10 | filter = require('gulp-filter'), 11 | merge = require('merge2'), 12 | gutil = require('gulp-util'), 13 | markdown = require('gulp-markdown'), 14 | rename = require('gulp-rename'), 15 | manifest = require('gulp-dnn-manifest'); 16 | 17 | gulp.task('browserify', function() { 18 | gulp.src('js/src/<%= props.projectName.toLowerCase() %>.js') 19 | .pipe(plumber()) 20 | .pipe(browserify({ 21 | transform: 'reactify', 22 | ignore: 'react' 23 | })) 24 | .pipe(gulp.dest('js/')); 25 | }); 26 | 27 | gulp.task('watch', function() { 28 | gulp.watch('js/src/**/*.js', ['browserify']); 29 | }); 30 | 31 | gulp.task('assemblyInfo', function() { 32 | return gulp 33 | .src('**/AssemblyInfo.cs') 34 | .pipe(assemblyInfo({ 35 | title: config.dnnModule.friendlyName, 36 | description: config.description, 37 | version: config.version, 38 | fileVersion: config.version, 39 | company: config.dnnModule.owner.organization, 40 | copyright: function(value) { 41 | return 'Copyright ' + new Date().getFullYear() + ' by ' + config.dnnModule.owner.organization; 42 | } 43 | })) 44 | .pipe(gulp.dest('.')); 45 | }); 46 | 47 | gulp.task('build', ['assemblyInfo'], function() { 48 | return gulp.src('./<%= props.projectName %>.csproj') 49 | .pipe(msbuild({ 50 | toolsVersion: 12.0, 51 | targets: ['Clean', 'Build'], 52 | errorOnFail: true, 53 | stdout: true, 54 | properties: { 55 | Configuration: 'Release', 56 | OutputPath: config.dnnModule.pathToAssemblies 57 | } 58 | })); 59 | }); 60 | 61 | gulp.task('packageInstall', ['browserify', 'build'], function() { 62 | var packageName = config.dnnModule.fullName + '_' + config.version; 63 | var dirFilter = filter(fileTest); 64 | return merge( 65 | merge( 66 | gulp.src([ 67 | '**/*.html', 68 | '**/*.resx' 69 | ], { 70 | base: '.' 71 | }) 72 | .pipe(dirFilter), 73 | gulp.src(['**/*.css'], { 74 | base: '.' 75 | }) 76 | .pipe(minifyCss()) 77 | .pipe(dirFilter), 78 | gulp.src(['js/*.js', '!js/*.min.js'], { 79 | base: '.' 80 | }) 81 | .pipe(uglify().on('error', gutil.log)), 82 | gulp.src(['js/*.min.js'], { 83 | base: '.' 84 | }) 85 | ) 86 | .pipe(zip('Resources.zip')), 87 | gulp.src(config.dnnModule.pathToSupplementaryFiles + '/*.dnn') 88 | .pipe(manifest(config)), 89 | gulp.src([config.dnnModule.pathToAssemblies + '/*.dll', 90 | config.dnnModule.pathToScripts + '/*.SqlDataProvider', 91 | config.dnnModule.pathToSupplementaryFiles + '/License.txt', 92 | config.dnnModule.pathToSupplementaryFiles + '/ReleaseNotes.txt' 93 | ]), 94 | gulp.src(config.dnnModule.pathToSupplementaryFiles + '/ReleaseNotes.md') 95 | .pipe(markdown()) 96 | .pipe(rename('ReleaseNotes.txt')) 97 | ) 98 | .pipe(zip(packageName + '_Install.zip')) 99 | .pipe(gulp.dest(config.dnnModule.packagesPath)); 100 | }); 101 | 102 | gulp.task('packageSource', ['browserify', 'build'], function() { 103 | var packageName = config.dnnModule.fullName + '_' + config.version; 104 | var dirFilter = filter(fileTest); 105 | return merge( 106 | gulp.src(['**/*.html', 107 | '**/*.css', 108 | 'js/**/*.js', 109 | '**/*.??proj', 110 | '**/*.sln', 111 | '**/*.json', 112 | '**/*.cs', 113 | '**/*.vb', 114 | '**/*.resx', 115 | config.dnnModule.pathToSupplementaryFiles + '**/*.*' 116 | ], { 117 | base: '.' 118 | }) 119 | .pipe(dirFilter) 120 | .pipe(zip('Resources.zip')), 121 | gulp.src(config.dnnModule.pathToSupplementaryFiles + '/*.dnn') 122 | .pipe(manifest(config)), 123 | gulp.src([config.dnnModule.pathToAssemblies + '/*.dll', 124 | config.dnnModule.pathToScripts + '/*.SqlDataProvider', 125 | config.dnnModule.pathToSupplementaryFiles + '/License.txt', 126 | config.dnnModule.pathToSupplementaryFiles + '/ReleaseNotes.txt' 127 | ]) 128 | ) 129 | .pipe(zip(packageName + '_Source.zip')) 130 | .pipe(gulp.dest(config.dnnModule.packagesPath)); 131 | }) 132 | 133 | gulp.task('package', ['packageInstall', 'packageSource'], function() { 134 | return null; 135 | }) 136 | 137 | gulp.task('default', ['browserify']); 138 | 139 | function fileTest(file) { 140 | var res = false; 141 | for (var i = config.dnnModule.excludeFilter.length - 1; i >= 0; i--) { 142 | res = res | file.relative.startsWith(config.dnnModule.excludeFilter[i]); 143 | }; 144 | return !res; 145 | } 146 | 147 | function startsWith(str, strToSearch) { 148 | return str.indexOf(strToSearch) === 0; 149 | } -------------------------------------------------------------------------------- /template/Data/RepositoryImpl.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DotNetNuke.Collections; 3 | using DotNetNuke.Data; 4 | 5 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Data 6 | { 7 | public abstract class RepositoryImpl : IRepository where T : class 8 | { 9 | 10 | public virtual void Delete(T item) 11 | { 12 | using (IDataContext db = DataContext.Instance()) 13 | { 14 | IRepository repo = db.GetRepository(); 15 | repo.Delete(item); 16 | } 17 | } 18 | 19 | public virtual void Delete(string sqlCondition, params object[] args) 20 | { 21 | using (IDataContext db = DataContext.Instance()) 22 | { 23 | IRepository repo = db.GetRepository(); 24 | repo.Delete(sqlCondition, args); 25 | } 26 | } 27 | 28 | public virtual IEnumerable Find(string sqlCondition, params object[] args) 29 | { 30 | IEnumerable list = null; 31 | using (IDataContext db = DataContext.Instance()) 32 | { 33 | IRepository repo = db.GetRepository(); 34 | list = repo.Find(sqlCondition, args); 35 | } 36 | return list; 37 | } 38 | 39 | public virtual IPagedList Find(int pageIndex, int pageSize, string sqlCondition, params object[] args) 40 | { 41 | IPagedList list = null; 42 | using (IDataContext db = DataContext.Instance()) 43 | { 44 | IRepository repo = db.GetRepository(); 45 | list = repo.Find(pageIndex, pageSize, sqlCondition, args); 46 | } 47 | return list; 48 | } 49 | 50 | public virtual IEnumerable Get() 51 | { 52 | IEnumerable list = null; 53 | using (IDataContext db = DataContext.Instance()) 54 | { 55 | IRepository repo = db.GetRepository(); 56 | list = repo.Get(); 57 | } 58 | return list; 59 | } 60 | 61 | public virtual IEnumerable Get(TScopeType scopeValue) 62 | { 63 | IEnumerable list = null; 64 | using (IDataContext db = DataContext.Instance()) 65 | { 66 | IRepository repo = db.GetRepository(); 67 | list = repo.Get(scopeValue); 68 | } 69 | return list; 70 | } 71 | 72 | public virtual T GetById(TProperty id) 73 | { 74 | T item = null; 75 | using (IDataContext db = DataContext.Instance()) 76 | { 77 | IRepository repo = db.GetRepository(); 78 | item = repo.GetById(id); 79 | } 80 | return item; 81 | } 82 | 83 | public virtual T GetById(TProperty id, TScopeType scopeValue) 84 | { 85 | T item = null; 86 | using (IDataContext db = DataContext.Instance()) 87 | { 88 | IRepository repo = db.GetRepository(); 89 | item = repo.GetById(id, scopeValue); 90 | } 91 | return item; 92 | } 93 | 94 | public virtual IPagedList GetPage(int pageIndex, int pageSize) 95 | { 96 | IPagedList list = null; 97 | using (IDataContext db = DataContext.Instance()) 98 | { 99 | IRepository repo = db.GetRepository(); 100 | list = repo.GetPage(pageIndex, pageSize); 101 | } 102 | return list; 103 | } 104 | 105 | public virtual IPagedList GetPage(TScopeType scopeValue, int pageIndex, int pageSize) 106 | { 107 | IPagedList list = null; 108 | using (IDataContext db = DataContext.Instance()) 109 | { 110 | IRepository repo = db.GetRepository(); 111 | list = repo.GetPage(scopeValue, pageIndex, pageSize); 112 | } 113 | return list; 114 | } 115 | 116 | public virtual void Insert(T item) 117 | { 118 | using (IDataContext db = DataContext.Instance()) 119 | { 120 | IRepository repo = db.GetRepository(); 121 | repo.Insert(item); 122 | } 123 | } 124 | 125 | public virtual void Update(T item) 126 | { 127 | using (IDataContext db = DataContext.Instance()) 128 | { 129 | IRepository repo = db.GetRepository(); 130 | repo.Update(item); 131 | } 132 | } 133 | 134 | public virtual void Update(string sqlCondition, params object[] args) 135 | { 136 | using (IDataContext db = DataContext.Instance()) 137 | { 138 | IRepository repo = db.GetRepository(); 139 | repo.Update(sqlCondition, args); 140 | } 141 | } 142 | 143 | } 144 | } 145 | 146 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var yeoman = require('yeoman-generator'), 3 | chalk = require('chalk'), 4 | yosay = require('yosay'), 5 | path = require('path'), 6 | extend = require('extend'), 7 | fs = require('fs'), 8 | util = require('./util.js'); 9 | 10 | module.exports = yeoman.generators.Base.extend({ 11 | 12 | _configPath: '', 13 | _config: {}, 14 | _defaults: { 15 | dnn: { 16 | organization: 'Connect', 17 | url: 'http://dnn-connect.org', 18 | email: 'webmaster@dnn-connect.org', 19 | name: '' 20 | } 21 | }, 22 | 23 | initializing: function() { 24 | 25 | if (path.basename(this.destinationPath()).toLowerCase() !== 'desktopmodules') { 26 | this.log('You must run this in the ' + chalk.red('DesktopModules') + ' folder!!'); 27 | process.exit(); 28 | } 29 | 30 | this._configPath = this.sourceRoot() + '/../../../.yo-rc.json'; 31 | this._config = extend(true, this._defaults, this.fs.readJSON(path.normalize(this._configPath))); 32 | 33 | String.prototype.replaceAll = function(find, replace) { 34 | var str = this; 35 | return str.replace(new RegExp(find, 'g'), replace); 36 | }; 37 | 38 | }, 39 | 40 | prompting: function() { 41 | var done = this.async(); 42 | 43 | this.log(yosay( 44 | 'Welcome to the ' + chalk.red('DNN SPA Module using Gulp and React') + ' generator!' 45 | )); 46 | 47 | var prompts = [{ 48 | type: 'input', 49 | name: 'projectName', 50 | message: 'Project Name', 51 | validate: function(value) { 52 | if (value !== '') { 53 | return true; 54 | } else { 55 | return "You must enter a name for the project"; 56 | } 57 | } 58 | }, { 59 | type: 'input', 60 | name: 'widgetName', 61 | message: 'Name of your primary object', 62 | default: 'Widget' 63 | }, { 64 | type: 'input', 65 | name: 'organization', 66 | message: 'Organization name (also used as subfolder and in namespaces)', 67 | default: this._config.dnn.organization, 68 | validate: function(value) { 69 | if (value !== '') { 70 | return true; 71 | } else { 72 | return "You must enter a name for the organization"; 73 | } 74 | } 75 | }, { 76 | type: 'input', 77 | name: 'url', 78 | message: 'Url or your organization', 79 | default: this._config.dnn.url 80 | }, { 81 | type: 'input', 82 | name: 'name', 83 | message: 'Your name', 84 | default: this._config.dnn.name, 85 | validate: function(value) { 86 | if (value !== '') { 87 | return true; 88 | } else { 89 | return "You must enter your name"; 90 | } 91 | } 92 | }, { 93 | type: 'input', 94 | name: 'email', 95 | message: 'Email address to use for project', 96 | default: this._config.dnn.email 97 | }, { 98 | type: 'confirm', 99 | name: 'overwriteSettings', 100 | message: 'Overwrite previous settings for organization, name and email?', 101 | default: false 102 | }]; 103 | 104 | this.prompt(prompts, function(props) { 105 | this.props = props; 106 | if (this.props.overwriteSettings) { 107 | this._config.dnn.organization = this.props.organization; 108 | this._config.dnn.name = this.props.name; 109 | this._config.dnn.email = this.props.email; 110 | this._config.dnn.url = this.props.url; 111 | this.fs.writeJSON(this._configPath, this._config); 112 | } 113 | this.sourceRoot(this.sourceRoot() + '/../../../template'); 114 | done(); 115 | }.bind(this)); 116 | }, 117 | 118 | writing: { 119 | app: function() { 120 | util.ensureFolder(this.destinationPath(this.props.organization)); 121 | this.destinationRoot(this.props.organization); 122 | util.ensureFolder(this.destinationPath(this.props.projectName)); 123 | this.destinationRoot(this.props.projectName); 124 | }, 125 | 126 | projectfiles: function() { 127 | 128 | var files = util.getFilesRecursive(this.templatePath(), ''); 129 | for (var i = files.length - 1; i >= 0; i--) { 130 | var dest = files[i].replaceAll('Project', this.props.projectName) 131 | .replace('_package', 'package') 132 | .replaceAll('Company', this.props.organization) 133 | .replaceAll('Widget', this.props.widgetName); 134 | this.fs.copyTpl( 135 | this.templatePath(files[i]), 136 | this.destinationPath(dest), { 137 | props: this.props 138 | } 139 | ); 140 | }; 141 | 142 | } 143 | 144 | }, 145 | 146 | copyFiles: function(fileList) { 147 | if (fileList === undefined) { 148 | return; 149 | } 150 | console.log(typeof fileList); 151 | for (var i = fileList.length - 1; i >= 0; i--) { 152 | this.fs.copyTpl( 153 | this.templatePath(fileList[i][0]), 154 | this.destinationPath(fileList[i][1]), { 155 | props: this.props 156 | } 157 | ); 158 | }; 159 | }, 160 | 161 | install: function() { 162 | // this.installDependencies(); 163 | } 164 | 165 | }); 166 | -------------------------------------------------------------------------------- /template/App_LocalResources/ClientResources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /template/App_LocalResources/SharedResources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /template/Project.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {7D61A32C-0F21-453F-A981-BD8E5A3A5304} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | <%= props.organization %>.DNN.Modules.<%= props.projectName %> 15 | <%= props.organization %>.DNN.Modules.<%= props.projectName %> 16 | v4.5 17 | false 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | full 27 | false 28 | ..\..\..\bin\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | false 33 | 34 | 35 | none 36 | true 37 | bin\ 38 | TRACE 39 | prompt 40 | 4 41 | false 42 | 43 | 44 | 45 | _References\DotNetNuke.dll 46 | False 47 | 48 | 49 | False 50 | _References\DotNetNuke.Web.dll 51 | False 52 | 53 | 54 | 55 | 56 | 57 | False 58 | _References\System.Net.Http.Formatting.dll 59 | False 60 | 61 | 62 | 63 | 64 | False 65 | _References\System.Web.Http.dll 66 | False 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 10.0 116 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | True 126 | False 127 | 0 128 | 129 | 130 | http://dnndev.me/desktopmodules/<%= props.projectName %> 131 | True 132 | http://dnndev.me 133 | False 134 | False 135 | 136 | 137 | False 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /template/Common/Localization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Web.Hosting; 6 | using System.Xml.XPath; 7 | using DotNetNuke.Collections.Internal; 8 | using DotNetNuke.Common.Utilities; 9 | using DotNetNuke.Entities.Portals; 10 | using DotNetNuke.Services.Cache; 11 | using DotNetNuke.Services.Localization; 12 | 13 | namespace <%= props.organization %>.DNN.Modules.<%= props.projectName %>.Common 14 | { 15 | public class Localization 16 | { 17 | 18 | public static Dictionary GetResourceFile(PortalSettings portalSettings, string resourceFile, string locale) 19 | { 20 | return 21 | (Dictionary) GetCompiledResourceFileCallBack( 22 | new CacheItemArgs("Compiled-" + resourceFile + "-" + locale + "-" + portalSettings.PortalId, 23 | DataCache.ResourceFilesCacheTimeOut, DataCache.ResourceFilesCachePriority, resourceFile, locale, 24 | portalSettings)); 25 | } 26 | 27 | private static object GetCompiledResourceFileCallBack(CacheItemArgs cacheItemArgs) 28 | { 29 | string resourceFile = (string)cacheItemArgs.Params[0]; 30 | string locale = (string)cacheItemArgs.Params[1]; 31 | PortalSettings portalSettings = (PortalSettings)cacheItemArgs.Params[2]; 32 | string systemLanguage = DotNetNuke.Services.Localization.Localization.SystemLocale; 33 | string defaultLanguage = portalSettings.DefaultLanguage; 34 | string fallbackLanguage = DotNetNuke.Services.Localization.Localization.SystemLocale; 35 | Locale targetLocale = LocaleController.Instance.GetLocale(locale); 36 | if (!String.IsNullOrEmpty(targetLocale.Fallback)) 37 | { 38 | fallbackLanguage = targetLocale.Fallback; 39 | } 40 | 41 | // get system default and merge the specific ones one by one 42 | var res = GetResourceFile(resourceFile); 43 | if (res == null) 44 | { 45 | return new Dictionary(); 46 | } 47 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, systemLanguage, portalSettings.PortalId)); 48 | if (defaultLanguage != systemLanguage) 49 | { 50 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, defaultLanguage, -1)); 51 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, defaultLanguage, portalSettings.PortalId)); 52 | } 53 | if (fallbackLanguage != defaultLanguage) 54 | { 55 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, fallbackLanguage, -1)); 56 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, fallbackLanguage, portalSettings.PortalId)); 57 | } 58 | if (locale != fallbackLanguage) 59 | { 60 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, locale, -1)); 61 | res = MergeResourceFile(res, GetResourceFileName(resourceFile, locale, portalSettings.PortalId)); 62 | } 63 | return res; 64 | } 65 | 66 | private static Dictionary MergeResourceFile(Dictionary current, string resourceFile) 67 | { 68 | var resFile = GetResourceFile(resourceFile); 69 | if (resFile == null) 70 | { 71 | return current; 72 | } 73 | foreach (string key in current.Keys.ToList()) 74 | { 75 | if (resFile.ContainsKey(key)) 76 | { 77 | current[key] = resFile[key]; 78 | } 79 | } 80 | return current; 81 | } 82 | 83 | #region Core Localization 84 | // Copy of methods from core localization provider but which kept these methods private, making it impossible to get a resource file from the cache 85 | public static Dictionary GetResourceFile(string resourceFile) 86 | { 87 | return CBO.GetCachedObject>(new CacheItemArgs(resourceFile, DataCache.ResourceFilesCacheTimeOut, DataCache.ResourceFilesCachePriority), 88 | GetResourceFileCallBack, 89 | true); 90 | } 91 | 92 | private static object GetResourceFileCallBack(CacheItemArgs cacheItemArgs) 93 | { 94 | string cacheKey = cacheItemArgs.CacheKey; 95 | Dictionary resources = null; 96 | 97 | string filePath = null; 98 | try 99 | { 100 | //Get resource file lookup to determine if the resource file even exists 101 | SharedDictionary resourceFileExistsLookup = GetResourceFileLookupDictionary(); 102 | 103 | if (ResourceFileMayExist(resourceFileExistsLookup, cacheKey)) 104 | { 105 | //check if an absolute reference for the resource file was used 106 | if (cacheKey.Contains(":\\") && Path.IsPathRooted(cacheKey)) 107 | { 108 | //if an absolute reference, check that the file exists 109 | if (File.Exists(cacheKey)) 110 | { 111 | filePath = cacheKey; 112 | } 113 | } 114 | 115 | //no filepath found from an absolute reference, try and map the path to get the file path 116 | if (filePath == null) 117 | { 118 | filePath = HostingEnvironment.MapPath(DotNetNuke.Common.Globals.ApplicationPath + cacheKey); 119 | } 120 | 121 | //The file is not in the lookup, or we know it exists as we have found it before 122 | if (File.Exists(filePath)) 123 | { 124 | if (filePath != null) 125 | { 126 | var doc = new XPathDocument(filePath); 127 | resources = new Dictionary(); 128 | foreach (XPathNavigator nav in doc.CreateNavigator().Select("root/data")) 129 | { 130 | if (nav.NodeType != XPathNodeType.Comment) 131 | { 132 | var selectSingleNode = nav.SelectSingleNode("value"); 133 | if (selectSingleNode != null) 134 | { 135 | resources[nav.GetAttribute("name", String.Empty)] = selectSingleNode.Value; 136 | } 137 | } 138 | } 139 | } 140 | cacheItemArgs.CacheDependency = new DNNCacheDependency(filePath); 141 | 142 | //File exists so add it to lookup with value true, so we are safe to try again 143 | using (resourceFileExistsLookup.GetWriteLock()) 144 | { 145 | resourceFileExistsLookup[cacheKey] = true; 146 | } 147 | } 148 | else 149 | { 150 | //File does not exist so add it to lookup with value false, so we don't try again 151 | using (resourceFileExistsLookup.GetWriteLock()) 152 | { 153 | resourceFileExistsLookup[cacheKey] = false; 154 | } 155 | } 156 | } 157 | } 158 | catch (Exception ex) 159 | { 160 | throw new Exception(string.Format("The following resource file caused an error while reading: {0}", filePath), ex); 161 | } 162 | return resources; 163 | } 164 | 165 | private static SharedDictionary GetResourceFileLookupDictionary() 166 | { 167 | return 168 | CBO.GetCachedObject>( 169 | new CacheItemArgs(DataCache.ResourceFileLookupDictionaryCacheKey, DataCache.ResourceFileLookupDictionaryTimeOut, DataCache.ResourceFileLookupDictionaryCachePriority), 170 | c => new SharedDictionary(), 171 | true); 172 | } 173 | 174 | private static bool ResourceFileMayExist(SharedDictionary resourceFileExistsLookup, string cacheKey) 175 | { 176 | bool mayExist; 177 | using (resourceFileExistsLookup.GetReadLock()) 178 | { 179 | mayExist = !resourceFileExistsLookup.ContainsKey(cacheKey) || resourceFileExistsLookup[cacheKey]; 180 | } 181 | return mayExist; 182 | } 183 | 184 | private static string GetResourceFileName(string resourceFileRoot, string language, int portalId) 185 | { 186 | string resourceFile; 187 | language = language.ToLower(); 188 | if (resourceFileRoot != null) 189 | { 190 | if (language == DotNetNuke.Services.Localization.Localization.SystemLocale.ToLower() || String.IsNullOrEmpty(language)) 191 | { 192 | switch (resourceFileRoot.Substring(resourceFileRoot.Length - 5, 5).ToLower()) 193 | { 194 | case ".resx": 195 | resourceFile = resourceFileRoot; 196 | break; 197 | case ".ascx": 198 | resourceFile = resourceFileRoot + ".resx"; 199 | break; 200 | case ".aspx": 201 | resourceFile = resourceFileRoot + ".resx"; 202 | break; 203 | default: 204 | resourceFile = resourceFileRoot + ".ascx.resx"; //a portal module 205 | break; 206 | } 207 | } 208 | else 209 | { 210 | switch (resourceFileRoot.Substring(resourceFileRoot.Length - 5, 5).ToLower()) 211 | { 212 | case ".resx": 213 | resourceFile = resourceFileRoot.Replace(".resx", "." + language + ".resx"); 214 | break; 215 | case ".ascx": 216 | resourceFile = resourceFileRoot.Replace(".ascx", ".ascx." + language + ".resx"); 217 | break; 218 | case ".aspx": 219 | resourceFile = resourceFileRoot.Replace(".aspx", ".aspx." + language + ".resx"); 220 | break; 221 | default: 222 | resourceFile = resourceFileRoot + ".ascx." + language + ".resx"; 223 | break; 224 | } 225 | } 226 | } 227 | else 228 | { 229 | if (language == DotNetNuke.Services.Localization.Localization.SystemLocale.ToLower() || String.IsNullOrEmpty(language)) 230 | { 231 | resourceFile = DotNetNuke.Services.Localization.Localization.SharedResourceFile; 232 | } 233 | else 234 | { 235 | resourceFile = DotNetNuke.Services.Localization.Localization.SharedResourceFile.Replace(".resx", "." + language + ".resx"); 236 | } 237 | } 238 | if (portalId != -1) 239 | { 240 | resourceFile = resourceFile.Replace(".resx", ".Portal-" + portalId + ".resx"); 241 | } 242 | return resourceFile; 243 | } 244 | #endregion 245 | 246 | } 247 | } --------------------------------------------------------------------------------