Brian Dukes
Work at Engage Software, developing websites on the DNN Platform (a DNN MVP). I also serve Jesus at City Lights Church.
Download the DNN Summit Mobile App now and take the survey at the end of the conference to be entered to win a $100 Amazon gift card!
<%@ Register TagPrefix="dnn" TagName="JQUERY"
src="~/Admin/Skins/jQuery.ascx" %>
<%@ Register TagPrefix="dnn" TagName="JavaScriptLibraryInclude"
src="~/Admin/Skins/JavaScriptLibraryInclude.ascx" %>
<dnn:jQuery runat="server" />
<dnn:JavaScriptLibraryInclude runat="server"
Name="html5shiv"
Version="3.7.3"
SpecificVersion="LatestMajor" />
<dnn:JavaScriptLibraryInclude runat="server"
Name="respond-minmax"
Version="1.4.2"
SpecificVersion="LatestMajor" />
@{
JavaScript.RequestRegistration(
"intl-tel-input",
new Version(14, 0, 6),
SpecificVersion.LatestMajor);
}
[js:{"jsname": "moment", "version": "2.22.2", "specific": "LatestMajor"}]
<%@ Register TagPrefix="dnn"
Namespace="DotNetNuke.Web.Client.ClientResourceManagement"
Assembly="DotNetNuke.Web.Client" %>
<dnn:DnnCssInclude runat="server"
FilePath="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
Name="bootstrap"
Version="4.2.1" />
<dnn:DnnJsInclude runat="server"
FilePath="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
Name="bootstrap"
Version="4.0.0"
ForceProvider="DnnFormBottomProvider"
Priority="99" />
using DotNetNuke.Entities.Modules.Settings;
public class HtmlModuleSettings
{
[ModuleSetting(Prefix = "HtmlText_")]
public bool ReplaceTokens { get; set; } = false;
[ModuleSetting(Prefix = "HtmlText_")]
public bool UseDecorate { get; set; } = true;
[ModuleSetting(Prefix = "HtmlText_")]
public int SearchDescLength { get; set; } = 100;
[ModuleSetting]
public int WorkFlowID { get; set; } = -1;
}
using DotNetNuke.Entities.Modules.Settings;
public class HtmlModuleSettingsRepository
: SettingsRepository<HtmlModuleSettings>
{
}
var repo = new HtmlModuleSettingsRepository();
var moduleSettings = repo.GetSettings(this.ModuleConfiguration);
chkReplaceTokens.Checked = moduleSettings.ReplaceTokens;
cbDecorate.Checked = moduleSettings.UseDecorate;
txtSearchDescLength.Text = moduleSettings.SearchDescLength.ToString();
var repo = new HtmlModuleSettingsRepository();
var moduleSettings = repo.GetSettings(this.ModuleConfiguration);
moduleSettings.ReplaceTokens = chkReplaceTokens.Checked;
moduleSettings.UseDecorate = cbDecorate.Checked;
moduleSettings.SearchDescLength = int.Parse(txtSearchDescLength.Text);
repo.SaveSettings(this.ModuleConfiguration, moduleSettings);
IFileEventHandlers
FileOverwritten
FileDeleted
FileRenamed
FileMoved
FileAdded
FileOverwritten
FileMetadataChanged
FileDownloaded
FolderAdded
FolderDeleted
FolderMoved
FolderRenamed
IFollowerEventHandlers
FollowRequested
UnfollowRequested
IFriendshipEventHandlers
FriendshipRequested
FriendshipAccepted
FriendshipDeleted
IModuleEventHandler
ModuleCreated
ModuleUpdated
ModuleRemoved
ModuleDeleted
IPortalEventHandlers
PortalCreated
IPortalSettingHandlers
PortalSettingUpdated
IProfileEventHandlers
ProfileUpdated
IPortalTemplateEventHandlers
TemplateCreated
IRoleEventHandlers
RoleCreated
RoleDeleted
RoleJoined
RoleLeft
ITabEventHandler
TabCreated
TabUpdated
TabRemoved
TabDeleted
TabRestored
TabMarkedAsPublished
ITabSyncEventHandler
TabSerialize
TabDeserialize
IUserEventHandlers
UserAuthenticated
UserCreated
UserDeleted
UserRemoved
UserApproved
UserUpdated
using System.ComponentModel.Composition;
using DotNetNuke.Entities.Portals;
[Export(typeof(IPortalEventHandlers))]
public class NewPortalHandler : IPortalEventHandlers
{
public void PortalCreated(object sender, PortalCreatedEventArgs args)
{
SetupSite(args.PortalId);
}
}
using System.ComponentModel.Composition;
using DotNetNuke.Entities.Users;
[Export(typeof(IUserEventHandlers))]
public class NewUserHandler : IUserEventHandlers
{
public void UserDeleted(object sender, UserEventArgs args) {}
public void UserRemoved(object sender, UserEventArgs args) {}
public void UserAuthenticated(object sender, UserEventArgs args) {}
public void UserApproved(object sender, UserEventArgs args) {}
public void UserUpdated(object sender, UpdateUserEventArgs args) {}
public void UserCreated(object sender, UserEventArgs args)
{
SetupUser(args.User.UserID);
}
}
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.UI.Modules;
public class StandardModuleInjectionFilter : IModuleInjectionFilter
{
public bool CanInjectModule(ModuleInfo module, PortalSettings portalSettings)
{
var variationTerm = module.Terms.SingleOrDefault(
t => t.GetTermPath().StartsWith(@"\\Behaviors\\A/B Test\\"));
var isInAbTest = variationTerm != null;
if (!isInAbTest)
{
return true;
}
var request = HttpContextSource.Current.Request;
var variationCookie = request.Cookies[$"AB_{module.ModuleID}"];
return variationCookie?.Value == variationTerm.Name;
}
}
<moduleDefinitions>
<moduleDefinition>
<moduleControls>
<!-- … -->
</moduleControls>
<permissions>
<permission code="ENGAGE_AMS"
key="APPROVE_MEMBERSHIP"
name="Approve Membership" />
<permission code="ENGAGE_AMS"
key="SEND_MESSAGE"
name="Send Messages" />
</permissions>
</moduleDefinition>
</moduleDefinitions>
using DotNetNuke.Security;
using DotNetNuke.Web.Api;
[SupportedModules("Engage: AMS Dashboard")]
public class MessageController : DnnApiController
{
private static readonly SecurityAccessLevel Edit = SecurityAccessLevel.Edit;
[DnnModuleAuthorize(AccessLevel = Edit, PermissionKey = "SEND_MESSAGE")]
public HttpResponseMessage Post(PostMessageModel model)
{
var result = MessageSender.Send(
model.From,
model.To,
model.Content);
return this.Request.CreateResponse(HttpStatusCode.OK, result);
}
}
using DotNetNuke.Web.Api;
[DnnAuthorize(AuthTypes = "JWT")]
public class PendingMembershipsController : DnnApiController
{
public HttpResponseMessage Get()
{
var result = Enumerable.Empty<object>()
return this.Request.CreateResponse(HttpStatusCode.OK, result);
}
}
POST https://summit.local/API/JwtAuth/mobile/login HTTP/2.0
Host: summit.local
Content-Type: application/json
Content-Length: 33
{"u":"sitemanager","p":"dnnhost"}
{
"userId":20,
"displayName":"Site Manager",
"accessToken":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzaWQiOiIwOGQ2OTA5OGI4YmU0Y2IzOGFlYmNhNGYxZmMyMDUzMCIsInJvbGUiOlsiUmVnaXN0ZXJlZCBVc2VycyIsIlN1YnNjcmliZXJzIl0sImlzcyI6InN1bW1pdC5sb2NhbCIsImV4cCI6MTU0OTk0NTA5MiwibmJmIjoxNTQ5OTQxMTkyfQ.xtXOEuZn21RR6B8Aps3JUE-JWwHPWTx03FBc1YyOTd0",
"renewalToken":"ZcEN0LbU/iwbmKIkKJsQx1d/wxbwh0CE3J+sGH++XynQVc92rNxNmtspKV/xpTBC"
}
GET https://summit.local/API/EngageAms/PendingMemberships HTTP/2.0
Host: summit.local
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzaWQiOiIwOGQ2OTA5OGI4YmU0Y2IzOGFlYmNhNGYxZmMyMDUzMCIsInJvbGUiOlsiUmVnaXN0ZXJlZCBVc2VycyIsIlN1YnNjcmliZXJzIl0sImlzcyI6InN1bW1pdC5sb2NhbCIsImV4cCI6MTU0OTk0NTA5MiwibmJmIjoxNTQ5OTQxMTkyfQ.xtXOEuZn21RR6B8Aps3JUE-JWwHPWTx03FBc1YyOTd0
[
]
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Users;
using DotNetNuke.Services.Localization;
using global::Dnn.PersonaBar.Library.Prompt;
using global::Dnn.PersonaBar.Library.Prompt.Models;
public class ListPendingMembershipsCommand : IConsoleCommand
{
public void Initialize(string[] args, PortalSettings portalSettings, UserInfo userInfo, int activeTabId)
{
}
public ConsoleResultModel Run()
{
var memberships = Membershipper.GetPending();
return new ConsoleResultModel
{
Data = memberships,
Records = memberships.Length,
FieldOrder = new []
{
nameof(Membership.Username),
nameof(Membership.Date),
}
};
}
public bool IsValid() => true;
public string ValidationMessage => "Don't worry, be happy";
public string LocalResourceFile => "~/DesktopModules/Summit/App_LocalResources/Prompt.resx";
public string ResultHtml => Localization.GetString("ResultHtml", this.LocalResourceFile);
}
using System;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Users;
using DotNetNuke.Services.Exceptions;
using global::Dnn.PersonaBar.Library.Prompt;
using global::Dnn.PersonaBar.Library.Prompt.Attributes;
using global::Dnn.PersonaBar.Library.Prompt.Models;
[ConsoleCommand(name: "send-message", category: "Summit", description: "SendMessage_Description")]
public class SendMessageCommand : ConsoleCommandBase
{
[FlagParameter(flag: FlagTo, description: "SendMessage_FlagTo", type: "String", Required = true)]
private const string FlagTo = "to";
[FlagParameter(FlagFrom, "SendMessage_FlagFrom", "String")]
private const string FlagFrom = "from";
[FlagParameter(FlagContent, "SendMessage_FlagContent", "String", Required = true)]
private const string FlagContent = "content";
private string From { get; set; }
private string To { get; set; }
private string Content { get; set; }
public override void Init(string[] args, PortalSettings portalSettings, UserInfo userInfo, int activeTabId)
{
this.To = GetFlagValue(flag: FlagTo, fieldName: nameof(To), defaultVal: string.Empty, required: true);
this.From = GetFlagValue(FlagFrom, nameof(From), defaultVal: userInfo.Username);
this.Content = GetFlagValue(FlagContent, nameof(Content), string.Empty, required: true);
}
/// <inheritdoc />
public override ConsoleResultModel Run()
{
try
{
MessageSender.Send(this.From, this.To, this.Content);
return new ConsoleResultModel(LocalizeString("Message Sent"));
}
catch (Exception exc)
{
Exceptions.LogException(exc);
return new ConsoleErrorResultModel(exc.Message);
}
}
public override string LocalResourceFile => "~/DesktopModules/Summit/App_LocalResources/Prompt.resx";
}
public class SocialUrlProvider : DotNetNuke.Entities.Urls.ExtensionUrlProvider
{
public override bool AlwaysUsesDnnPagePath(int portalId) => true;
public override Dictionary<string, string> GetProviderPortalSettings() => null;
public override bool CheckForRedirect(…) => true;
public override string ChangeFriendlyUrl(…) => "";
public override string TransformFriendlyUrlToQueryString(…) => "";
// optional, defaults to false
public override bool AlwaysCallForRewrite(int portalId) => true;
}
<component type="UrlProvider">
<urlProvider>
<name>DNN Social Url Extension Provider</name>
<type>DotNetNuke.Modules.SocialUrlProvider.SocialUrlProvider</type>
<settingsControlSrc>DesktopModules/DNN_SocialUrlProvider/Settings.ascx</settingsControlSrc>
<redirectAllUrls>false</redirectAllUrls>
<replaceAllUrls>false</replaceAllUrls>
<rewriteAllUrls>false</rewriteAllUrls>
<desktopModule>Social Groups</desktopModule>
</urlProvider>
</component>
?mode=…
|
&…
|
|
---|---|---|
profilepic
|
userid=330894
|
|
file
|
file=/Images/Logos.jpg
|
|
file
|
url=https://placebear.com/200/100
|
|
securefile
|
fileid=317624
|
|
placeholder
|
w=200&h=100
|
&greyscale=1
|
||
&invert=1
|
||
&contrast=-100 /
&contrast=100
|
-100 to 100 | |
&gamma=0.2 /
&gamma=5.0
|
0.2 to 5 | |
&brightness=-100 /
&brightness=150
|
-255 to 255 | |
&rotateflip=RotateNoneFlipY /
&rotateflip=Rotate180FlipNone
|
None, 90, 180, 270, X, Y, XY |
&text=banner
|
|
&color=BlueViolet
|
|
&backcolor=orange
|
|
&text=DNN%20Summit
|
&w=100
|
|
&size=xxs
|
|
&w=200
|
|
&w=200
|
|
&w=60
|
|
&w=40
|
using System.Collections.Generic;
using System.Linq;
using DotNetNuke.Common;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Services.Sitemap;
public class SummitSitemapProvider : SitemapProvider
{
public override List<SitemapUrl> GetUrls(int portalId, PortalSettings ps, string version)
{
return (from thing in Repository.GetThings()
where !thing.IsDraft
let url = Globals.NavigateURL(
thing.TabId,
string.Empty,
$"slug={thing.Slug}")
select new SitemapUrl
{
LastModified = thing.LastModified,
Priority = 0.6F,
Url = url,
ChangeFrequency = SitemapChangeFrequency.Hourly,
})
.ToList();
}
}
<sitemap defaultProvider="coreSitemapProvider">
<providers>
<clear />
<add name="coreSitemapProvider" type="DotNetNuke.Services.Sitemap.CoreSitemapProvider, DotNetNuke" providerPath="~\Providers\MembershipProviders\Sitemap\CoreSitemapProvider\" />
<add name="summitSitemapProvider" type="Engage.SummitSitemapProvider" />
</providers>
</sitemap>
<component type="Config">
<config>
<configFile>web.config</configFile>
<install>
<configuration>
<nodes configfile="web.config">
<node path="/configuration/dotnetnuke/sitemap/providers"
action="update" key="name" collision="ignore">
<add name="summitSitemapProvider" type="Summit.Dnn.Summit.SummitSitemapProvider" />
</node>
</nodes>
</configuration>
</install>
<uninstall>
<configuration>
<nodes configfile="web.config">
<node path="/configuration/dotnetnuke/sitemap/providers/add[@name='summitSitemapProvider']" action="remove" />
</nodes>
</configuration>
</uninstall>
</config>
</component>
By Brian Dukes
Over the years, DNN has accumulated many helpful little features that get documented once (if at all) and then forgotten, only available to the few who go looking through the source code and discover a gem. Let's review the development features you didn't even know you were missing. I've been digging through DNN's source code for over 12 years, and have accumulated many rarely used features and capabilities built into DNN's platform that may be just the right approach for the solution you're building or the problem you've encountered. This talk will explore some of the latent possibilities that DNN provides if you just know where to look.
Work at Engage Software, developing websites on the DNN Platform (a DNN MVP). I also serve Jesus at City Lights Church.