diff --git a/generators/spa/index.js b/generators/spa/index.js
index 4a325bde..57ba7749 100644
--- a/generators/spa/index.js
+++ b/generators/spa/index.js
@@ -181,6 +181,24 @@ module.exports = class extends DnnGeneratorBase {
template
);
+ this.fs.copyTpl(
+ this.templatePath('common/Data/**'),
+ this.destinationPath(moduleName + '/Data/'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath('common/ViewModels/**'),
+ this.destinationPath(moduleName + '/ViewModels/'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath('common/Providers/**'),
+ this.destinationPath(moduleName + '/Providers/'),
+ template
+ );
+
// Do all templated copies
this.fs.copyTpl(
this.templatePath('../../common/src/**'),
@@ -205,6 +223,12 @@ module.exports = class extends DnnGeneratorBase {
this.destinationPath(moduleName + '/RouteConfig.cs'),
template
);
+
+ this.fs.copyTpl(
+ this.templatePath('common/Constants.cs'),
+ this.destinationPath(moduleName + '/Constants.cs'),
+ template
+ );
this.fs.copyTpl(
this.templatePath('common/manifest.dnn'),
@@ -213,8 +237,38 @@ module.exports = class extends DnnGeneratorBase {
);
this.fs.copyTpl(
- this.templatePath('../../common/csproj/_Project.csproj'),
+ this.templatePath('common/symbols.dnn'),
+ this.destinationPath(moduleName + '/' + moduleName + '_Symbols.dnn'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath('common/License.txt'),
+ this.destinationPath(moduleName + '/License.txt'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath('common/ReleaseNotes.txt'),
+ this.destinationPath(moduleName + '/ReleaseNotes.txt'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath(spaType + '/common/Module.csproj'),
this.destinationPath(moduleName + '/' + moduleName + '.csproj'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath(spaType + '/common/Module.build'),
+ this.destinationPath(moduleName + '/Module.build'),
+ template
+ );
+
+ this.fs.copyTpl(
+ this.templatePath('common/Data/ModuleContext.cs'),
+ this.destinationPath(moduleName + '/Data/' + moduleName + 'Context.cs'),
template
);
@@ -258,7 +312,7 @@ module.exports = class extends DnnGeneratorBase {
'html-webpack-plugin': '^3.2.0',
// eslint-disable-next-line prettier/prettier
'marked': '^0.5.2',
- 'node-sass': '^4.11.0',
+ 'node-sass': '^8.0.0',
'sass-loader': '^7.1.0',
'style-loader': '^0.23.1',
// eslint-disable-next-line prettier/prettier
@@ -268,10 +322,13 @@ module.exports = class extends DnnGeneratorBase {
'webpack-node-externals': '^1.7.2'
},
dependencies: {
- 'prop-types': '^15.6.2',
- // eslint-disable-next-line prettier/prettier
- 'react': '^16.6.3',
- 'react-dom': '^16.6.3'
+ "@testing-library/jest-dom": "^5.16.5",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-scripts": "5.0.1",
+ "web-vitals": "^2.1.4"
}
};
@@ -290,7 +347,6 @@ module.exports = class extends DnnGeneratorBase {
'eslint': '^5.8.0',
'eslint-loader': '^2.1.1',
'eslint-plugin-react': '^7.11.1',
- 'react-hot-loader': '^4.3.12'
};
} else {
this._writeTsConfig();
diff --git a/generators/spa/templates/ReactJS/common/Module.build b/generators/spa/templates/ReactJS/common/Module.build
new file mode 100644
index 00000000..62283058
--- /dev/null
+++ b/generators/spa/templates/ReactJS/common/Module.build
@@ -0,0 +1,53 @@
+
+
+ <%= moduleName %>
+ <%= moduleName %>
+ <%= fullNamespace %>
+ zip
+ $(MSBuildProjectDirectory)\..\..\Build
+ $(MSBuildProjectDirectory)\..\..\Website
+ $(WebsitePath)\Install\Module
+ $(WebsitePath)\DesktopModules\$(ModulePath)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/generators/spa/templates/ReactJS/common/Module.csproj b/generators/spa/templates/ReactJS/common/Module.csproj
new file mode 100644
index 00000000..862151ec
--- /dev/null
+++ b/generators/spa/templates/ReactJS/common/Module.csproj
@@ -0,0 +1,160 @@
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {<%= guid %>}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ <%= fullNamespace %>
+ <%= fullNamespace %>
+ v4.8
+ false
+
+
+
+
+
+ .\
+ true
+
+
+ 12.0
+
+
+
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+ bin\<%= fullNamespace %>.xml
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+ bin\<%= fullNamespace %>.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+ True
+
+
+
+
+
+
+
+
diff --git a/generators/spa/templates/ReactJS/jsx/src/app.jsx b/generators/spa/templates/ReactJS/jsx/src/app.jsx
index 6225b5ac..e9fa749d 100644
--- a/generators/spa/templates/ReactJS/jsx/src/app.jsx
+++ b/generators/spa/templates/ReactJS/jsx/src/app.jsx
@@ -1,12 +1,10 @@
import React from "react";
import * as ReactDOM from "react-dom";
-import Hello from "./components/Hello";
+import Items from "./components/Items";
ReactDOM.render(
-
,
+
+
+ ,
document.getElementById("<%= namespace.toLowerCase() %><%= moduleName %>")
);
\ No newline at end of file
diff --git a/generators/spa/templates/ReactJS/jsx/src/components/Items.jsx b/generators/spa/templates/ReactJS/jsx/src/components/Items.jsx
new file mode 100644
index 00000000..534edd4b
--- /dev/null
+++ b/generators/spa/templates/ReactJS/jsx/src/components/Items.jsx
@@ -0,0 +1,221 @@
+import React from "react";
+import { useState, useEffect } from "react";
+const moduleId = window.getmoduleId();
+const serviceFramework = window.$.ServicesFramework(moduleId);
+const token = serviceFramework.getAntiForgeryValue();
+const tabId = serviceFramework.getTabId();
+const baseUrl ="DesktopModules/<%= moduleName %>/API/";
+
+const Items = () => {
+ const [modal, setModal] = useState(false);
+ const [items, setItems] = useState(null);
+ const [users, setUsers] = useState(null);
+ const [description, setDescription] = useState(null);
+ const [name, setName] = useState(null);
+ const [assignedUser, setUser] = useState(null);
+ const [id, setId] = useState(0);
+ // SETTINGS
+ const [idSetting, setIdSetting] = useState(false);
+ const [nameSetting, setNameSetting] = useState(false);
+ const [descriptionSetting, setDescriptionSetting] = useState(false);
+ const [createdOnDateSetting, setCreatedOnDateSetting] = useState(false);
+
+ const loadItems = () => {
+ const url = baseUrl+"Item/GetList";
+ fetch(url,
+ {
+ method: "GET",
+ headers: {
+ "ModuleId": moduleId,
+ "RequestVerificationToken": token,
+ "TabId": tabId
+ }
+ }).then(res => {
+ return res.json();
+ }).then(data => {
+ setItems(data);
+ });
+ };
+
+ const loadSettings = () => {
+ const url = baseUrl+"Settings/LoadSettings";
+ fetch(url,
+ {
+ method: "GET",
+ headers: {
+ "ModuleId": moduleId,
+ "RequestVerificationToken": token,
+ "TabId": tabId
+ }
+ }).then(res => {
+ return res.json();
+ }).then(data => {
+ setIdSetting(data.itemId === "true" ? true : false);
+ setNameSetting(data.name === "true" ? true : false);
+ setDescriptionSetting(data.description === "true" ? true : false);
+ setCreatedOnDateSetting(data.createdOnDate === "true" ? true : false);
+ });
+ };
+
+ const cancelAdd = () => {
+ setModal(false);
+ resetItem();
+ };
+
+ const resetItem = () => {
+ setName(null);
+ setId(0);
+ setDescription(null);
+ setUser(null);
+ };
+
+ const editItem = (item) => {
+ setName(item.name);
+ setUser(item.assignedUser);
+ setDescription(item.description);
+ setId(item.id);
+ setModal(true);
+ };
+
+ const saveChanges = () => {
+ const item = { id, name, description, assignedUser };
+ const url = baseUrl + "Item/Save";
+ fetch(url,
+ {
+ method: "POST",
+ headers: {
+ "ModuleId": moduleId,
+ "RequestVerificationToken": token,
+ "TabId": tabId,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify(item)
+ }).then(res => {
+ if (res.ok) {
+ resetItem();
+ setModal(false);
+ loadItems();
+ }
+ });
+ };
+
+ const loadUsers = () => {
+ const url = baseUrl + "User/GetList";
+ fetch(url,
+ {
+ method: "GET",
+ headers: {
+ "ModuleId": moduleId,
+ "RequestVerificationToken": token,
+ "TabId": tabId
+ }
+ }).then(res => {
+ return res.json();
+ }).then(data => {
+ setUsers(data);
+ });
+ };
+
+ useEffect(() => {
+ loadItems();
+ loadSettings();
+ loadUsers();
+ }, []);
+
+ const removeItem = (itemId) => {
+ const url = baseUrl + "Item/Delete?itemId=" + itemId;
+
+ if (confirm("Do you want to remove this item?")) {
+ fetch(url,
+ {
+ method: "DELETE",
+ headers: {
+ "ModuleId": moduleId,
+ "RequestVerificationToken": token,
+ "TabId": tabId
+ }
+ }).then(res => {
+ if (!res.ok) {
+ throw new Error("HTTP status " + res.status);
+ } else {
+ loadItems();
+ }
+ });
+ }
+ };
+
+ return (
+
+
Item list
+
+
+
+
+
+ {idSetting && Id | }
+ {nameSetting && Name | }
+ {descriptionSetting && Description | }
+ Created on |
+ |
+
+
+
+ {items && items.map((item) => (
+ {idSetting && {item.id} | }
+ {nameSetting && {item.name} | }
+ {descriptionSetting && {item.description} | }
+ {createdOnDateSetting && {item.createdOnDate} | }
+
+
+
+ |
+
))}
+
+
+ {modal &&
+
+
+
+
+
Create Item
+
+
+
+
+
+
+
+
+
+
+
+
+
}
+
);
+};
+
+export default Items;
diff --git a/generators/spa/templates/common/Components/BaseClasses/ApiControllerBase.cs b/generators/spa/templates/common/Components/BaseClasses/ApiControllerBase.cs
new file mode 100644
index 00000000..0c544be3
--- /dev/null
+++ b/generators/spa/templates/common/Components/BaseClasses/ApiControllerBase.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using DotNetNuke.Web.Api;
+using <%= fullNamespace %>.Data;
+
+namespace <%= fullNamespace %>.Components.BaseClasses
+{
+ public class ApiControllerBase : DnnApiController
+ {
+ private const string DBCONTEXT_KEY = "<%= moduleName %>Context_Instance";
+
+ public <%= moduleName %>Context DbCtx
+ {
+ get
+ {
+ return GetContext();
+ }
+ }
+
+ ///
+ /// Returns a DbContext object for use with this request. Instanciates a new DbContext when requested in the parameter. When using createNewInstance, you should always dispose your DbContext yourself.
+ ///
+ ///
+ ///
+ protected <%= moduleName %>Context GetContext(bool createNewInstance = false)
+ {
+ // if a new instance is requested: return one
+ if (createNewInstance) return new <%= moduleName %>Context();
+
+ // get a reference to the HttpContext
+ var ctx = Request.Properties["MS_HttpContext"] as HttpContextWrapper;
+
+ <%= moduleName %>Context retval = null;
+ // se if we have one in the HttpContext already
+ if (ctx.Items[DBCONTEXT_KEY] == null)
+ {
+ retval = new <%= moduleName %>Context();
+ // store in HttpContext
+ ctx.Items[DBCONTEXT_KEY] = retval;
+ }
+ else
+ {
+ // get from HttpContext
+ retval = (<%= moduleName %>Context)ctx.Items[DBCONTEXT_KEY];
+ }
+
+ return retval;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // get a reference to the HttpContext
+ var ctx = Request.Properties["MS_HttpContext"] as HttpContextWrapper;
+
+ // dispose of stored DbContext
+ if (ctx.Items[DBCONTEXT_KEY] != null)
+ {
+ var dbctx = (<%= moduleName %>Context)ctx.Items[DBCONTEXT_KEY];
+ dbctx.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/generators/spa/templates/common/Constants.cs b/generators/spa/templates/common/Constants.cs
new file mode 100644
index 00000000..f46e659e
--- /dev/null
+++ b/generators/spa/templates/common/Constants.cs
@@ -0,0 +1,27 @@
+/*
+Copyright Upendo Ventures, LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+namespace <%= fullNamespace %>
+{
+ public static class QuickSettings
+ {
+ public const string MODSETTING_Name = "Name";
+ public const string MODSETTING_Description = "Description";
+ public const string MODSETTING_ItemId = "ItemId";
+ public const string MODSETTING_AssignedUserId = "AssignedUserId";
+ public const string MODSETTING_CreatedByUserId = "CreatedByUserId";
+ public const string MODSETTING_CreatedOnDate = "CreatedOnDate";
+ }
+}
\ No newline at end of file
diff --git a/generators/spa/templates/common/Controllers/ItemController.cs b/generators/spa/templates/common/Controllers/ItemController.cs
new file mode 100644
index 00000000..564f56fe
--- /dev/null
+++ b/generators/spa/templates/common/Controllers/ItemController.cs
@@ -0,0 +1,111 @@
+using DotNetNuke.Common;
+using DotNetNuke.Common.Utilities;
+using DotNetNuke.Security;
+using DotNetNuke.UI.Modules;
+using DotNetNuke.Web.Api;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading;
+using System.Web.Http;
+using <%= fullNamespace %>.Components.BaseClasses;
+using <%= fullNamespace %>.Data;
+using <%= fullNamespace %>.ViewModels;
+
+namespace <%= fullNamespace %>.Controllers
+{
+ [SupportedModules("<%= moduleName %>")]
+ [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)]
+
+ public class ItemController : ApiControllerBase
+ {
+ public ItemController() { }
+ [HttpDelete]
+ public HttpResponseMessage Delete(int itemId)
+ {
+ var item = DbCtx.Items.FirstOrDefault(i => i.ItemId == itemId);
+ if (item != null)
+ {
+ DbCtx.Items.Remove(item);
+ DbCtx.SaveChanges();
+ }
+
+ return Request.CreateResponse(System.Net.HttpStatusCode.NoContent);
+ }
+
+ [HttpGet]
+ public HttpResponseMessage Get(int itemId)
+ {
+ var itemVm = new ItemViewModel(DbCtx.Items.FirstOrDefault(i => i.ItemId == itemId));
+
+ return Request.CreateResponse(itemVm);
+ }
+
+ [HttpGet]
+ public HttpResponseMessage GetList()
+ {
+ List retval = new List();
+ List- items;
+
+ items = DbCtx.Items.Where(i => i.ModuleId == ActiveModule.ModuleID).ToList();
+ items.ForEach(i => retval.Add(new ItemViewModel(i, Globals.IsEditMode())));
+
+ return Request.CreateResponse(retval);
+ }
+
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public HttpResponseMessage Save(ItemViewModel item)
+ {
+ if (item.Id > 0)
+ {
+ var t = Update(item);
+ return Request.CreateResponse(System.Net.HttpStatusCode.NoContent);
+ }
+ else
+ {
+ var t = Create(item);
+ return Request.CreateResponse(t.ItemId);
+ }
+
+ }
+
+ private Item Create(ItemViewModel item)
+ {
+ Item t = new Item
+ {
+ ItemName = item.Name,
+ ItemDescription = item.Description,
+ AssignedUserId = item.AssignedUser,
+ ModuleId = ActiveModule.ModuleID,
+ CreatedByUserId = UserInfo.UserID,
+ LastModifiedByUserId = UserInfo.UserID,
+ CreatedOnDate = DateTime.UtcNow,
+ LastModifiedOnDate = DateTime.UtcNow
+ };
+ DbCtx.Items.Add(t);
+ DbCtx.SaveChanges();
+
+ return t;
+ }
+
+ private Item Update(ItemViewModel item)
+ {
+
+ var t = DbCtx.Items.FirstOrDefault(i => i.ItemId == item.Id);
+ if (t != null)
+ {
+ t.ItemName = item.Name;
+ t.ItemDescription = item.Description;
+ t.AssignedUserId = item.AssignedUser;
+ t.LastModifiedByUserId = UserInfo.UserID;
+ t.LastModifiedOnDate = DateTime.UtcNow;
+ }
+ DbCtx.SaveChanges();
+
+ return t;
+ }
+ }
+}
diff --git a/generators/spa/templates/common/Controllers/SettingsController.cs b/generators/spa/templates/common/Controllers/SettingsController.cs
new file mode 100644
index 00000000..b83e92cc
--- /dev/null
+++ b/generators/spa/templates/common/Controllers/SettingsController.cs
@@ -0,0 +1,84 @@
+/*
+Copyright Upendo Ventures, LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+using System.Net.Http;
+using System.Net;
+using System.Web.Http;
+using DotNetNuke.Entities.Modules;
+using DotNetNuke.Web.Api;
+using DotNetNuke.Security;
+using Telerik.Web.UI.Calendar.Utils;
+using <%= fullNamespace %>;
+using <%= fullNamespace %>.ViewModels;
+
+namespace <%= fullNamespace %>.Controllers
+{
+ [SupportedModules("<%= moduleName %>")]
+ [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)]
+ public class SettingsController : DnnApiController
+ {
+ public SettingsController() { }
+
+ [HttpGet] //[baseURL]/settings/load
+
+ public HttpResponseMessage LoadSettings()
+ {
+ var settings = new SettingsViewModel();
+
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_Name))
+ {
+ settings.Name = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_Name].ToString();
+ }
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_Description))
+ {
+ settings.Description = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_Description].ToString();
+ }
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_CreatedByUserId))
+ {
+ settings.CreatedByUserId = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_CreatedByUserId].ToString();
+ }
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_AssignedUserId))
+ {
+ settings.AssignedUserId = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_AssignedUserId].ToString();
+ }
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_ItemId))
+ {
+ settings.ItemId = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_ItemId].ToString();
+ }
+ if (ActiveModule.ModuleSettings.ContainsKey(QuickSettings.MODSETTING_CreatedOnDate))
+ {
+ settings.CreatedOnDate = ActiveModule.ModuleSettings[QuickSettings.MODSETTING_CreatedOnDate].ToString();
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, settings);
+ }
+
+ [HttpPost] //[baseURL]/settings/save
+ [ActionName("save")]
+ [ValidateAntiForgeryToken]
+ public HttpResponseMessage SaveSettings(SettingsViewModel settings)
+ {
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_Name, settings.Name);
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_Description, settings.Description);
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_CreatedByUserId, settings.CreatedByUserId);
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_AssignedUserId, settings.AssignedUserId);
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_ItemId, settings.ItemId);
+ ModuleController.Instance.UpdateModuleSetting(ActiveModule.ModuleID, QuickSettings.MODSETTING_CreatedOnDate, settings.CreatedOnDate);
+
+ return Request.CreateResponse(HttpStatusCode.OK, "Success");
+ }
+ }
+
+}
diff --git a/generators/spa/templates/common/Controllers/UserController.cs b/generators/spa/templates/common/Controllers/UserController.cs
new file mode 100644
index 00000000..b9ce01c5
--- /dev/null
+++ b/generators/spa/templates/common/Controllers/UserController.cs
@@ -0,0 +1,33 @@
+using <%= fullNamespace %>.ViewModels;
+using DotNetNuke.Entities.Users;
+using DotNetNuke.Security;
+using DotNetNuke.Web.Api;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+
+namespace <%= fullNamespace %>.Controllers
+{
+ [SupportedModules("<%= moduleName %>")]
+ [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)]
+ public class UserController : DnnApiController
+ {
+ public UserController() { }
+
+ public HttpResponseMessage Dummy()
+ {
+ return Request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed, "Dummy called");
+ }
+ public HttpResponseMessage GetList()
+ {
+
+ var userlist = DotNetNuke.Entities.Users.UserController.GetUsers(this.PortalSettings.PortalId);
+ var users = userlist.Cast().ToList()
+ .Select(user => new UserViewModel(user))
+ .ToList();
+
+ return Request.CreateResponse(users);
+ }
+ }
+}
diff --git a/generators/spa/templates/common/Data/Item.cs b/generators/spa/templates/common/Data/Item.cs
new file mode 100644
index 00000000..7cd44b3c
--- /dev/null
+++ b/generators/spa/templates/common/Data/Item.cs
@@ -0,0 +1,33 @@
+namespace <%= fullNamespace %>.Data
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+ using System.ComponentModel.DataAnnotations.Schema;
+ using System.Data.Entity.Spatial;
+
+ public partial class Item
+ {
+ [Key]
+ public int ItemId { get; set; }
+
+ [Required]
+ public string ItemName { get; set; }
+
+ [Required]
+ public string ItemDescription { get; set; }
+
+ public int? AssignedUserId { get; set; }
+
+ public int ModuleId { get; set; }
+
+ public DateTime CreatedOnDate { get; set; }
+
+ public int CreatedByUserId { get; set; }
+
+ public DateTime LastModifiedOnDate { get; set; }
+
+ public int LastModifiedByUserId { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/generators/spa/templates/common/Data/ModuleContext.cs b/generators/spa/templates/common/Data/ModuleContext.cs
new file mode 100644
index 00000000..b16fe731
--- /dev/null
+++ b/generators/spa/templates/common/Data/ModuleContext.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Data.Entity;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Linq;
+using DotNetNuke.Data;
+using DotNetNuke.Framework.Providers;
+
+namespace <%= fullNamespace %>.Data
+{
+ public class <%= moduleName %>Context: DbContext
+ {
+ public <%= moduleName %>Context()
+ : base("name=SiteSqlServer")
+ {
+ }
+
+ public virtual DbSet
- Items { get; set; }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ string moduleQualifier = "<%= moduleName %>_";
+ // get the dnn data provider configuration
+ ProviderConfiguration pc = ProviderConfiguration.GetProviderConfiguration("data");
+ // Read the configuration specific information for this provider
+ Provider provider = (Provider)pc.Providers[pc.DefaultProvider];
+ // get the objectQualifier
+ String objectQualifier = provider.Attributes["objectQualifier"];
+ // append an underscore when it's not there
+ if (!string.IsNullOrEmpty(objectQualifier) && !objectQualifier.EndsWith("_"))
+ objectQualifier += "_";
+
+ // map the object to the right table
+ modelBuilder.Entity
- ().ToTable($"{objectQualifier}{moduleQualifier}Items");
+ }
+ }
+}
diff --git a/generators/spa/templates/common/License.txt b/generators/spa/templates/common/License.txt
new file mode 100644
index 00000000..ea13267a
--- /dev/null
+++ b/generators/spa/templates/common/License.txt
@@ -0,0 +1,3 @@
+
<%= moduleName %> Modules Extension for DNN
+<%= company %> <%= companyUrl %>
+Your License Here
diff --git a/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/00.00.01.SqlDataProvider b/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/00.00.01.SqlDataProvider
new file mode 100644
index 00000000..8511abb4
--- /dev/null
+++ b/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/00.00.01.SqlDataProvider
@@ -0,0 +1,42 @@
+/************************************************************/
+/***** SqlDataProvider *****/
+/***** *****/
+/***** *****/
+/***** Note: To manually execute this script you must *****/
+/***** perform a search and replace operation *****/
+/***** for {databaseOwner} and {objectQualifier} *****/
+/***** *****/
+/************************************************************/
+
+IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}<%= moduleName %>_Items]') AND type in (N'U'))
+DROP TABLE {databaseOwner}[{objectQualifier}<%= moduleName %>_Items]
+GO
+
+CREATE TABLE {databaseOwner}{objectQualifier}<%= moduleName %>_Items
+ (
+ ItemId int NOT NULL IDENTITY (1, 1),
+ ItemName nvarchar(MAX) NOT NULL,
+ ItemDescription nvarchar(MAX) NOT NULL,
+ AssignedUserId int NULL,
+ ModuleId int NOT NULL,
+ CreatedOnDate datetime NOT NULL,
+ CreatedByUserId int NOT NULL,
+ LastModifiedOnDate datetime NOT NULL,
+ LastModifiedByUserId int NOT NULL
+ ) ON [PRIMARY]
+ TEXTIMAGE_ON [PRIMARY]
+GO
+
+
+ALTER TABLE {databaseOwner}{objectQualifier}<%= moduleName %>_Items ADD CONSTRAINT
+ PK_{objectQualifier}<%= moduleName %>_Items PRIMARY KEY CLUSTERED
+ (
+ ItemId
+ ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+
+GO
+
+
+/************************************************************/
+/***** SqlDataProvider *****/
+/************************************************************/
diff --git a/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/Uninstall.SqlDataProvider b/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/Uninstall.SqlDataProvider
new file mode 100644
index 00000000..712e6962
--- /dev/null
+++ b/generators/spa/templates/common/Providers/DataProviders/SqlDataProvider/Uninstall.SqlDataProvider
@@ -0,0 +1,3 @@
+IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}<%= moduleName %>_Items]') AND type in (N'U'))
+DROP TABLE {databaseOwner}[{objectQualifier}<%= moduleName %>_Items]
+GO
diff --git a/generators/spa/templates/common/ReleaseNotes.txt b/generators/spa/templates/common/ReleaseNotes.txt
new file mode 100644
index 00000000..c85f014c
--- /dev/null
+++ b/generators/spa/templates/common/ReleaseNotes.txt
@@ -0,0 +1,10 @@
+<%= moduleName %>
+
+ <%= company %>
+ <%= emailAddy %>
+
+
+
+
About the <%= moduleName %>
+
This module is intended as an example of how to build DNN Modules with VueJS.
+
diff --git a/generators/spa/templates/common/RouteConfig.cs b/generators/spa/templates/common/RouteConfig.cs
index 6e16a68d..25a3bc21 100644
--- a/generators/spa/templates/common/RouteConfig.cs
+++ b/generators/spa/templates/common/RouteConfig.cs
@@ -7,15 +7,26 @@
'
*/
using DotNetNuke.Web.Api;
+using System.Web.Http;
-namespace <%= namespace%>.Modules.<%= moduleName %>
+namespace <%= fullNamespace %>
{
- public class RouteConfig : IServiceRouteMapper
+ ///
+ /// The ServiceRouteMapper tells the DNN Web API Framework what routes this module uses
+ ///
+ public class ServiceRouteMapper : IServiceRouteMapper
{
+ ///
+ /// RegisterRoutes is used to register the module's routes
+ ///
+ ///
public void RegisterRoutes(IMapRoute mapRouteManager)
{
- mapRouteManager.MapHttpRoute("<%= namespace%>.Modules.<%= moduleName %>", "<%= namespace%>.Modules.<%= moduleName %>", "{controller}/{action}", new[]
- {"<%= namespace%>.Modules.<%= moduleName %>.Controllers"});
+ mapRouteManager.MapHttpRoute(
+ moduleFolderName: "<%= moduleName %>",
+ routeName: "default",
+ url: "{controller}/{action}",
+ namespaces: new[] { "<%= fullNamespace %>.Controllers" });
}
}
-}
+}
\ No newline at end of file
diff --git a/generators/spa/templates/common/ViewModels/ItemViewModel.cs b/generators/spa/templates/common/ViewModels/ItemViewModel.cs
new file mode 100644
index 00000000..c3e49e61
--- /dev/null
+++ b/generators/spa/templates/common/ViewModels/ItemViewModel.cs
@@ -0,0 +1,46 @@
+using <%= fullNamespace %>.Components;
+using <%= fullNamespace %>.Data;
+using Newtonsoft.Json;
+
+namespace <%= fullNamespace %>.ViewModels
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class ItemViewModel
+ {
+ public ItemViewModel(Item t)
+ {
+ Id = t.ItemId;
+ Name = t.ItemName;
+ Description = t.ItemDescription;
+ AssignedUser = t.AssignedUserId;
+ CreatedOnDate = t.CreatedOnDate.ToShortDateString();
+ }
+
+ public ItemViewModel(Item t, bool canEdit) : this(t)
+ {
+ CanEdit = canEdit;
+ }
+
+
+ public ItemViewModel() { }
+
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("assignedUser")]
+ public int? AssignedUser { get; set; }
+
+ [JsonProperty("canEdit")]
+ public bool CanEdit { get; }
+
+ [JsonProperty("createdOnDate")]
+ public string CreatedOnDate { get; }
+
+ }
+}
\ No newline at end of file
diff --git a/generators/spa/templates/common/ViewModels/SettingsViewModel.cs b/generators/spa/templates/common/ViewModels/SettingsViewModel.cs
new file mode 100644
index 00000000..988de0e5
--- /dev/null
+++ b/generators/spa/templates/common/ViewModels/SettingsViewModel.cs
@@ -0,0 +1,47 @@
+/*
+Copyright Upendo Ventures, LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+using System;
+using Newtonsoft.Json;
+
+namespace <%= fullNamespace %>.ViewModels
+{
+ [Serializable]
+ [JsonObject(MemberSerialization.OptIn)]
+ public class SettingsViewModel
+ {
+ public SettingsViewModel()
+ {
+ }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("itemId")]
+ public string ItemId { get; set; }
+
+ [JsonProperty("assignedUserId")]
+ public string AssignedUserId { get; set; }
+
+ [JsonProperty("createdByUserId")]
+ public string CreatedByUserId { get; set; }
+
+ [JsonProperty("createdOnDate")]
+ public string CreatedOnDate { get; set; }
+ }
+}
diff --git a/generators/spa/templates/common/ViewModels/UserViewModel.cs b/generators/spa/templates/common/ViewModels/UserViewModel.cs
new file mode 100644
index 00000000..b467f743
--- /dev/null
+++ b/generators/spa/templates/common/ViewModels/UserViewModel.cs
@@ -0,0 +1,27 @@
+using DotNetNuke.Entities.Users;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace <%= fullNamespace %>.ViewModels
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class UserViewModel
+ {
+ public UserViewModel(UserInfo t)
+ {
+ Id = t.UserID;
+ Name = t.DisplayName;
+ }
+
+ public UserViewModel() { }
+
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/generators/spa/templates/common/manifest.dnn b/generators/spa/templates/common/manifest.dnn
index 7292f1d7..3ce00da8 100644
--- a/generators/spa/templates/common/manifest.dnn
+++ b/generators/spa/templates/common/manifest.dnn
@@ -49,7 +49,7 @@
- DesktopModules/<%= moduleName %>/View.html
+ DesktopModules/<%= moduleName %>/dist/View.html
False
<%= moduleName %> SPA
View
@@ -58,7 +58,7 @@
Edit
- DesktopModules/<%= moduleName %>/Edit.html
+ DesktopModules/<%= moduleName %>/dist/Edit.html
False
Add/Edit Menu Item
Edit
@@ -69,7 +69,7 @@
QuickSettings
- DesktopModules/<%= moduleName %>/Settings.html
+ DesktopModules/<%= moduleName %>/dist/Settings.html
False
<%= moduleName %> Settings
Edit
@@ -94,12 +94,19 @@
-
+
+ <%= fullNamespace %>.dll
+ bin
+
+
+ EntityFramework.dll
+ bin
+
+
+ EntityFramework.SqlServer.dll
bin
- <%= namespace %>.<%= moduleName %>.dll
- <%= version %>
diff --git a/generators/spa/templates/common/src/Edit.html b/generators/spa/templates/common/src/Edit.html
index efcdb29a..663f5bae 100644
--- a/generators/spa/templates/common/src/Edit.html
+++ b/generators/spa/templates/common/src/Edit.html
@@ -1,3 +1,3 @@
[CSS:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/module.css"}]
-[JavaScript:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/scripts/edit-bundle.js", provider: "DnnFormBottomProvider"}]
+[JavaScript:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/dist/Resources/scripts/edit-bundle.js", provider: "DnnFormBottomProvider"}]
diff --git a/generators/spa/templates/common/src/Resources/scripts/QuickSettings.js b/generators/spa/templates/common/src/Resources/scripts/QuickSettings.js
new file mode 100644
index 00000000..3ce532c1
--- /dev/null
+++ b/generators/spa/templates/common/src/Resources/scripts/QuickSettings.js
@@ -0,0 +1,97 @@
+var dnnspamodule = dnnspamodule || {};
+
+dnnspamodule.quickSettings = function (root, moduleId) {
+ console.log(moduleId);
+ let utils = new common.Utils();
+ let alert = new common.Alert();
+ let parentSelector = "[id='" + root + "']";
+ // Setup your settings service endpoint
+ let service = {
+ baseUrl: "DesktopModules/<%= moduleName %>/API/",
+ framework: $.ServicesFramework(moduleId),
+ controller: "Settings"
+ };
+
+ let SaveSettings = function () {
+
+ let name = $("#Name").is(":checked");
+ let description = $("#Description").is(":checked");
+ let assignedUserId = $("#AssignedUserId").is(":checked");
+ let createdOnDate = $("#CreatedOnDate").is(":checked");
+ let itemId = $("#ItemId").is(":checked");
+
+ let deferred = $.Deferred();
+ let params = {
+ name: name,
+ description: description,
+ assignedUserId: assignedUserId,
+ createdOnDate: createdOnDate,
+ itemId: itemId
+ };
+
+ utils.get("POST", "save", service, params,
+ function (data) {
+
+ deferred.resolve();
+ location.reload();
+ },
+ function (error, exception) {
+ // fail
+ let deferred = $.Deferred();
+ deferred.reject();
+ alert.danger({
+ selector: parentSelector,
+ text: error.responseText,
+ status: error.status
+ });
+ },
+ function () {
+ });
+
+ return deferred.promise();
+ };
+
+ let CancelSettings = function () {
+ let deferred = $.Deferred();
+ deferred.resolve();
+ return deferred.promise();
+ };
+
+ let LoadSettings = function () {
+ let params = {};
+
+ utils.get("GET", "LoadSettings", service, params,
+ function (data) {
+ $("#CreatedOnDate").prop("checked", data.createdOnDate == "true");
+ $("#Name").prop("checked", data.name == "true");
+ $("#Description").prop("checked", data.description == "true");
+ $("#ItemId").prop("checked", data.itemId == "true");
+ },
+ function (error, exception) {
+ // fail
+ console.log("12345657897");
+ console.log(error);
+ alert.danger({
+ selector: parentSelector,
+ text: error.responseText,
+ status: error.status
+ });
+ },
+ function () {
+ });
+ };
+
+ let init = function () {
+ // Wire up the default save and cancel buttons
+ $(root).dnnQuickSettings({
+ moduleId: moduleId,
+ onSave: SaveSettings,
+ onCancel: CancelSettings
+ });
+ LoadSettings();
+ };
+
+ return {
+ init: init
+ };
+};
\ No newline at end of file
diff --git a/generators/spa/templates/common/src/Resources/scripts/common.js b/generators/spa/templates/common/src/Resources/scripts/common.js
new file mode 100644
index 00000000..2d51536d
--- /dev/null
+++ b/generators/spa/templates/common/src/Resources/scripts/common.js
@@ -0,0 +1,152 @@
+var common = common || {};
+
+common.Utils = function () {
+
+ let get = function (httpMethod, action, service, params, success, fail, always) {
+ let jqxhr = $.ajax({
+ url: service.baseUrl + service.controller + "/" + action,
+ beforeSend: service.framework.setModuleHeaders,
+ type: httpMethod,
+ async: true,
+ data: httpMethod == "GET" ? "" : JSON.stringify(params),
+ dataType: httpMethod == "GET" ? "" : "json",
+ contentType: httpMethod == "GET" ? "" : "application/json; charset=UTF-8"
+ }).done(function (data) {
+ if (typeof (success) === "function") {
+ success(data);
+ }
+ }).fail(function (error, exception) {
+ if (typeof (fail) === "function") {
+ fail(error, exception);
+ }
+ }).always(function () {
+ if (typeof (always) === "function") {
+ always();
+ }
+ });
+ };
+
+ let addRewriteQueryString = function (hash, decode) {
+ let path = location.pathname;
+ let queryString = path.substring(path.search("/ctl/") + 1);
+ let keyValues = queryString.split("/");
+
+ for (let i = 0; i < keyValues.length; i += 2) {
+ hash[decode(keyValues[i])] = decode(keyValues[i + 1]);
+ }
+ return hash;
+ };
+
+ let getQueryStrings = function () {
+ let assoc = {};
+ let decode = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); };
+ let queryString = location.search.substring(1);
+ let keyValues = queryString.split("&");
+
+ for (let i = 0; i < keyValues.length; i++) {
+ let key = keyValues[i].split("=");
+ if (key.length > 1) {
+ assoc[decode(key[0])] = decode(key[1]);
+ }
+ }
+ return addRewriteQueryString(assoc, decode);
+ };
+
+ let loading = function (icon, cssClass) {
+ $(icon).toggleClass(cssClass).toggleClass("fa-refresh fa-spin");
+ };
+
+ return {
+ get: get,
+ getQueryStrings: getQueryStrings,
+ addRewriteQueryString: addRewriteQueryString,
+ loading: loading
+ };
+};
+
+
+common.Alert = function () {
+
+ let success = function (message) {
+ message.redirect = typeof (message.redirect) !== "undefined" ? message.redirect : false;
+
+ $("" + message.text + "
")
+ .appendTo($(message.selector))
+ .slideDown(800, function () {
+ if (message.redirect) {
+ setTimeout(function () {
+ location.href = typeof (message.postBack) !== "undefined" ? message.postBack : "/";
+ }, 3000);
+ }
+ });
+ };
+
+ let info = function (message) {
+ message.redirect = typeof (message.redirect) !== "undefined" ? message.redirect : false;
+
+ $("" + message.text + "
")
+ .appendTo($(message.selector))
+ .slideDown(800, function () {
+ if (message.redirect) {
+ setTimeout(function () {
+ location.href = typeof (message.postBack) !== "undefined" ? message.postBack : "/";
+ }, 3000);
+ }
+ });
+ };
+
+ let warning = function (message) {
+ message.redirect = typeof (message.redirect) !== "undefined" ? message.redirect : false;
+
+ $("" + message.text + "
")
+ .appendTo($(message.selector))
+ .slideDown(800, function () {
+ if (message.redirect) {
+ setTimeout(function () {
+ location.href = typeof (message.postBack) !== "undefined" ? message.postBack : "/";
+ }, 3000);
+ }
+ });
+ };
+
+ let danger = function (message) {
+ message.redirect = typeof (message.redirect) !== "undefined" ? message.redirect : false;
+
+ $("" + message.text + "
")
+ .prependTo($(message.selector))
+ .slideDown(800, function () {
+ if (message.redirect) {
+ setTimeout(function () {
+ location.href = typeof (message.postBack) !== "undefined" ? message.postBack : "/";
+ }, 3000);
+ }
+ });
+ };
+
+ let dismiss = function (alert, callback) {
+ let alertPanel = $(alert.selector).find(".alert-panel");
+
+ $.each(alertPanel, function (index, el) {
+
+ $(el).slideUp(800, function () {
+ $(el).remove();
+
+ if (typeof (callback) === "function") {
+ callback();
+ }
+ });
+ });
+
+ if (alertPanel.length == 0 && typeof (callback) === "function") {
+ callback();
+ }
+ };
+
+ return {
+ success: success,
+ info: info,
+ warning: warning,
+ danger: danger,
+ dismiss: dismiss
+ };
+};
diff --git a/generators/spa/templates/common/src/Resources/scripts/useFetch.js b/generators/spa/templates/common/src/Resources/scripts/useFetch.js
new file mode 100644
index 00000000..80311b9e
--- /dev/null
+++ b/generators/spa/templates/common/src/Resources/scripts/useFetch.js
@@ -0,0 +1,47 @@
+import { useState, useEffect } from "react";
+
+const useFetch = (url) => {
+ const [data, setData] = useState(null);
+ const [isPending, setIsPending] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const abortCont = new AbortController();
+ fetch(url,{
+ method: "GET",
+ headers: {
+ "ModuleId": 389,
+ "RequestVerificationToken": "vt4k-ZjhQv_b6yTjs5I0DhumWcY_8sMm52euNE-MzEFLJVg3EtMu5GkKRGtWCgowSCPA58POdpzdprZA0",
+ "TabId": 36
+ },
+ signal: abortCont.signal
+ })
+ .then(res => {
+ if (!res.ok) { // error coming back from server
+ throw Error("could not fetch the data for that resource");
+ }
+ return res.json();
+ })
+ .then(data => {
+ setIsPending(false);
+ setData(data);
+ setError(null);
+ })
+ .catch(err => {
+ if (err.name === "AbortError") {
+ console.log("fetch aborted");
+ } else {
+ // auto catches network / connection error
+ setIsPending(false);
+ setError(err.message);
+ }
+ });
+
+ // abort the fetch
+ return () => abortCont.abort();
+ }, [url]);
+
+ return { data, isPending, error };
+};
+
+export default useFetch;
\ No newline at end of file
diff --git a/generators/spa/templates/common/src/Settings.html b/generators/spa/templates/common/src/Settings.html
index 49bb70be..92233d09 100644
--- a/generators/spa/templates/common/src/Settings.html
+++ b/generators/spa/templates/common/src/Settings.html
@@ -1,3 +1,27 @@
-
-[CSS:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/module.css"}]
-[JavaScript:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/scripts/settings-bundle.js", provider: "DnnFormBottomProvider"}]
+[JavaScript:{ jsname: "JQuery" }]
+[JavaScript:{ jsname: "Knockout" }]
+[JavaScript:{ path: "~/Resources/Shared/scripts/dnn.jquery.js"}]
+[JavaScript:{ path: "~/DesktopModules/<%= moduleName %>/dist/Resources/scripts/common.js"}]
+[JavaScript:{ path: "~/DesktopModules/<%= moduleName %>/dist/Resources/scripts/QuickSettings.js"}]
+
+
+
\ No newline at end of file
diff --git a/generators/spa/templates/common/src/View.html b/generators/spa/templates/common/src/View.html
index 044e14d6..fc423e2e 100644
--- a/generators/spa/templates/common/src/View.html
+++ b/generators/spa/templates/common/src/View.html
@@ -2,8 +2,13 @@
securityAccessLevel : "Edit",
title: "Edit",
titleKey: "EditModule",
- localResourceFile: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/App_LocalResources/View.resx"
+ localResourceFile: "~/DesktopModules/<%= moduleName %>/App_LocalResources/View.resx"
}]
-[CSS:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/module.css"}]
-[JavaScript:{ path: "~/DesktopModules/<%= namespace %>/<%= moduleName %>/Resources/scripts/app-bundle.js", provider: "DnnFormBottomProvider"}]
+
+[CSS:{ path: "~/DesktopModules/<%= moduleName %>/Resources/module.css"}]
+[JavaScript:{ path: "~/DesktopModules/<%= moduleName %>/dist/Resources/scripts/app-bundle.js", provider: "DnnFormBottomProvider"}]