├── Scripts ├── 01.01.01.sql ├── Uninstall.sql ├── 01.01.00.sql └── 01.00.00.sql ├── _LegacyReferences └── FiftyOne.Foundation.dll ├── README.md ├── app ├── directives │ ├── map.js │ └── view.js ├── services │ ├── map.js │ ├── visitor.js │ └── visit.js ├── views │ ├── map.html │ └── view.html ├── app.js └── controllers │ ├── map.js │ └── view.js ├── DTOs ├── DateCountDTO.cs ├── MapDTO.cs ├── ReportDTO.cs ├── VisitorDTO.cs ├── DashboardDTO.cs └── VisitDTO.cs ├── Map.ascx ├── View.ascx ├── web.config ├── View.ascx.designer.cs ├── MapSettings.ascx ├── RouteMapper.cs ├── msbuild ├── BuildProperties.targets └── Project.targets ├── packages.config ├── Dnn.WebAnalytics.sln ├── MapSettings.ascx.cs ├── SchedulerJobs └── VisitorJob.cs ├── web.Debug.config ├── web.Release.config ├── Properties └── AssemblyInfo.cs ├── Map.ascx.cs ├── View.ascx.cs ├── .gitattributes ├── plugins ├── datetime-picker │ ├── datetime-picker.tpls.js │ └── datetime-picker.min.js ├── angular-chart │ ├── angular-chart.min.js │ ├── angular-chart.js │ └── angular-chart.min.js.map └── angular-toastr │ ├── angular-toastr.tpls.min.js │ └── angular-toastr.min.css ├── ModuleBase.cs ├── Controllers ├── MapController.cs └── VisitorController.cs ├── DAL ├── WebAnalytics.dbml.layout └── WebAnalytics.dbml ├── Dnn.WebAnalytics.dnn ├── .gitignore ├── Components └── VisitorTracker.cs └── Dnn.WebAnalytics.csproj /Scripts/01.01.01.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visits] 4 | DROP COLUMN ip 5 | GO 6 | -------------------------------------------------------------------------------- /_LegacyReferences/FiftyOne.Foundation.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DNNCommunity/Dnn.WebAnalytics/HEAD/_LegacyReferences/FiftyOne.Foundation.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dnn.WebAnalytics 2 | A Dnn module for capturing information about visitors. Includes a report UX and a map UX to show location information. 3 | -------------------------------------------------------------------------------- /app/directives/map.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.directive('map', function () { 2 | return { 3 | templateUrl: '/DesktopModules/Dnn.WebAnalytics/app/views/map.html', 4 | controller: 'mapController' 5 | }; 6 | }); 7 | -------------------------------------------------------------------------------- /app/directives/view.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.directive('view', function () { 2 | return { 3 | templateUrl: '/DesktopModules/Dnn.WebAnalytics/app/views/view.html?v=' + Date.now(), 4 | controller: 'viewController' 5 | }; 6 | }); -------------------------------------------------------------------------------- /DTOs/DateCountDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class DateCountDTO 6 | { 7 | // initialization 8 | public DateCountDTO() 9 | { 10 | } 11 | 12 | // public properties 13 | public DateTime date { get; set; } 14 | public int count { get; set; } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /Map.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control Language="C#" AutoEventWireup="true" Inherits="Dnn.WebAnalytics.Map" CodeBehind="Map.ascx.cs" %> 2 | 3 |
4 |
5 |
6 | 7 | -------------------------------------------------------------------------------- /DTOs/MapDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class MapDTO 6 | { 7 | // initialization 8 | public MapDTO() 9 | { 10 | } 11 | 12 | // public properties 13 | public Nullable user_id { get; set; } 14 | public string latitude { get; set; } 15 | public string longitude { get; set; } 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /View.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="View.ascx.cs" Inherits="Dnn.WebAnalytics.View" %> 2 | 3 |
4 |
5 |
6 | 7 | 13 | -------------------------------------------------------------------------------- /DTOs/ReportDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class ReportDTO 6 | { 7 | // initialization 8 | public ReportDTO() 9 | { 10 | } 11 | 12 | // public properties 13 | public string field { get; set; } 14 | public int count { get; set; } 15 | public int total { get; set; } 16 | public int percent { get; set; } 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /View.ascx.designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace Dnn.WebAnalytics { 11 | 12 | 13 | public partial class View { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DTOs/VisitorDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class VisitorDTO 6 | { 7 | // initialization 8 | public VisitorDTO() 9 | { 10 | } 11 | 12 | // public properties 13 | public int id { get; set; } 14 | 15 | public int portal_id { get; set; } 16 | 17 | public Nullable user_id { get; set; } 18 | 19 | public DateTime created_on_date { get; set; } 20 | 21 | public string user_username { get; set; } 22 | public string user_displayname { get; set; } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /MapSettings.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control Language="C#" AutoEventWireup="true" Inherits="Dnn.WebAnalytics.MapSettings" CodeBehind="Settings.ascx.cs" %> 2 | <%@ Register TagPrefix="dnn" TagName="Label" Src="~/controls/LabelControl.ascx" %> 3 | 4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /Scripts/Uninstall.sql: -------------------------------------------------------------------------------- 1 | IF NOT OBJECT_ID('{databaseOwner}[{objectQualifier}Community_Visits]') IS NULL 2 | DROP TABLE {databaseOwner}[{objectQualifier}Community_Visits]; 3 | GO 4 | IF NOT OBJECT_ID('{databaseOwner}[{objectQualifier}Community_Visitors]') IS NULL 5 | DROP TABLE {databaseOwner}[{objectQualifier}Community_Visitors]; 6 | GO 7 | 8 | IF EXISTS(SELECT 1 FROM {databaseOwner}[{objectQualifier}Schedule] WHERE [TypeFullName] = N'Dnn.WebAnalyticsLinqToSql.VisitorJob, Dnn.WebAnalyticsLinqToSql') 9 | BEGIN 10 | DELETE FROM {databaseOwner}[{objectQualifier}Schedule] 11 | WHERE 12 | [TypeFullName] = N'Dnn.WebAnalyticsLinqToSql.VisitorJob, Dnn.WebAnalyticsLinqToSql'; 13 | END 14 | 15 | -------------------------------------------------------------------------------- /RouteMapper.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Web.Api; 2 | using System.Web.Http; 3 | 4 | namespace Dnn.WebAnalytics 5 | { 6 | public class RouteMapper : IServiceRouteMapper 7 | { 8 | public void RegisterRoutes(IMapRoute mapRouteManager) 9 | { 10 | // Default 11 | mapRouteManager.MapHttpRoute( 12 | moduleFolderName: "Dnn.WebAnalytics", 13 | routeName: "default", 14 | url: "{controller}/{id}", 15 | defaults: new 16 | { 17 | id = RouteParameter.Optional 18 | }, 19 | namespaces: new[] { "Dnn.WebAnalytics" }); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /DTOs/DashboardDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Dnn.WebAnalytics 5 | { 6 | public class DashboardDTO 7 | { 8 | // initialization 9 | public DashboardDTO() 10 | { 11 | } 12 | 13 | // public properties 14 | public int view_count { get; set; } 15 | public int visit_count { get; set; } 16 | public int visitor_count { get; set; } 17 | public int user_count { get; set; } 18 | 19 | public List views { get; set; } 20 | public List visits { get; set; } 21 | public List visitors { get; set; } 22 | public List users { get; set; } 23 | 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /msbuild/BuildProperties.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DnnCommunity 5 | Dnn.WebAnalytics 6 | _Installation 7 | 8 | $(MSBuildProjectDirectory)\bin 9 | $(MSBuildProjectDirectory)\..\.. 10 | 11 | 01 12 | 01 13 | 02 14 | 00 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/services/map.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.factory('mapService', ['$http', function mapService($http) { 2 | 3 | var base_path = apiUrlBase + "/map"; 4 | 5 | // interface 6 | var service = { 7 | getMapPoints: getMapPoints, 8 | getMapCenter: getMapCenter 9 | }; 10 | return service; 11 | 12 | // implementation 13 | 14 | // get Map Points 15 | function getMapPoints(minutes) { 16 | var request = $http({ 17 | method: "get", 18 | url: base_path + "?minutes=" + minutes 19 | }); 20 | return request; 21 | } 22 | 23 | // get Map Center 24 | function getMapCenter() { 25 | var request = $http({ 26 | method: "get", 27 | url: base_path 28 | }); 29 | return request; 30 | } 31 | }]); 32 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Dnn.WebAnalytics.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 14 3 | VisualStudioVersion = 14.0.25420.1 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dnn.WebAnalytics", "Dnn.WebAnalytics.csproj", "{20304B35-C633-42D4-B745-34B617FA38F4}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {20304B35-C633-42D4-B745-34B617FA38F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {20304B35-C633-42D4-B745-34B617FA38F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {20304B35-C633-42D4-B745-34B617FA38F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {20304B35-C633-42D4-B745-34B617FA38F4}.Release|Any CPU.Build.0 = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(SolutionProperties) = preSolution 19 | HideSolutionNode = FALSE 20 | EndGlobalSection 21 | EndGlobal 22 | -------------------------------------------------------------------------------- /app/views/map.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 | Visitors Online: {{visitors_online}} 21 |
22 |
23 | Users Online: {{users_online}} 24 |
25 |
26 | -------------------------------------------------------------------------------- /MapSettings.ascx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DotNetNuke.Entities.Modules; 3 | using DotNetNuke.Services.Exceptions; 4 | using System.Web.UI.WebControls; 5 | 6 | namespace Dnn.WebAnalytics 7 | { 8 | public partial class MapSettings : ModuleSettingsBase 9 | { 10 | 11 | protected TextBox txtGoogle; 12 | 13 | public override void LoadSettings() 14 | { 15 | try { 16 | if (!Page.IsPostBack) { 17 | if (!string.IsNullOrEmpty((string)ModuleSettings["google_api_key"])) 18 | { 19 | txtGoogle.Text = (string)ModuleSettings["google_api_key"]; 20 | } 21 | } 22 | } catch (Exception exc) { 23 | Exceptions.ProcessModuleLoadException(this, exc); 24 | } 25 | } 26 | 27 | public override void UpdateSettings() 28 | { 29 | try { 30 | ModuleController.Instance.UpdateModuleSetting(ModuleId, "google_api_key", txtGoogle.Text); 31 | } 32 | catch (Exception exc) { 33 | Exceptions.ProcessModuleLoadException(this, exc); 34 | } 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /SchedulerJobs/VisitorJob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class VisitorJob : DotNetNuke.Services.Scheduling.SchedulerClient 6 | { 7 | public VisitorJob(DotNetNuke.Services.Scheduling.ScheduleHistoryItem objScheduleHistoryItem) : base() 8 | { 9 | ScheduleHistoryItem = objScheduleHistoryItem; 10 | } 11 | 12 | public override void DoWork() 13 | { 14 | try { 15 | string strMessage = Processing(); 16 | ScheduleHistoryItem.Succeeded = true; 17 | ScheduleHistoryItem.AddLogNote("Successful. " + strMessage); 18 | } catch (Exception exc) { 19 | ScheduleHistoryItem.Succeeded = false; 20 | ScheduleHistoryItem.AddLogNote("Failed. " + exc.Message); 21 | Errored(ref exc); 22 | DotNetNuke.Services.Exceptions.Exceptions.LogException(exc); 23 | } 24 | } 25 | 26 | public string Processing() 27 | { 28 | string Message = ""; 29 | 30 | VisitController visitController = new VisitController(); 31 | //visitController.WriteVisits(); 32 | visitController.PurgeVisits(); 33 | 34 | return Message; 35 | } 36 | 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | 2 | var dnnWebAnalytics = angular.module('DNN_WebAnalytics', ['ngMessages', 'ngAnimate', 'ui.bootstrap', 'toastr', 'ui.bootstrap.datetimepicker', 'chart.js'], function ($locationProvider) { 3 | $locationProvider.html5Mode({ 4 | enabled: true, 5 | requireBase: false 6 | }); 7 | }); 8 | 9 | dnnWebAnalytics.config(function ($httpProvider) { 10 | $httpProvider.defaults.withCredentials = true; 11 | 12 | var httpHeaders = { 13 | "ModuleId": sf.getModuleId(), 14 | "TabId": sf.getTabId(), 15 | "RequestVerificationToken": sf.getAntiForgeryValue() 16 | }; 17 | angular.extend($httpProvider.defaults.headers.common, httpHeaders); 18 | }); 19 | 20 | dnnWebAnalytics.config(function (toastrConfig) { 21 | angular.extend(toastrConfig, { 22 | positionClass: 'toast-top-right', 23 | timeOut: 3000, 24 | maxOpened: 1, 25 | progressBar: true, 26 | tapToDismiss: true, 27 | autoDismiss: true, 28 | toastClass: 'toastr' 29 | }); 30 | }); 31 | 32 | 33 | //dnnWebAnalytics.config(function (uiGmapGoogleMapApiProvider) { 34 | // uiGmapGoogleMapApiProvider.configure({ 35 | // key: 'AIzaSyBo1a4oXxfBSaUM4yEMvpKWARqyMsA1vD0', 36 | // v: '3.20', 37 | // libraries: 'weather,geometry,visualization' 38 | // }); 39 | //}); -------------------------------------------------------------------------------- /web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /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("WebAnalytics")] 8 | [assembly: AssemblyDescription("WebAnalytics")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Dnn")] 11 | [assembly: AssemblyProduct("WebAnalytics")] 12 | [assembly: AssemblyCopyright("Copyright (c) 2019")] 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("20304b35-c633-42d4-b745-34b617fa38f4")] 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 Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("01.01.02.00")] 34 | [assembly: AssemblyVersion("01.01.02.00")] 35 | [assembly: AssemblyFileVersion("01.01.02.00")] 36 | 37 | -------------------------------------------------------------------------------- /Map.ascx.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Services.Exceptions; 2 | using System; 3 | using DotNetNuke.Web.Client.ClientResourceManagement; 4 | 5 | namespace Dnn.WebAnalytics 6 | { 7 | public partial class Map : ModuleBase 8 | { 9 | public string GoogleAPIKey 10 | { 11 | get 12 | { 13 | if (!string.IsNullOrEmpty((string)Settings["google_api_key"])) 14 | { 15 | return (string)Settings["google_api_key"]; 16 | } 17 | else 18 | { 19 | return string.Empty; 20 | } 21 | } 22 | } 23 | protected new void Page_Load(Object sender, EventArgs e) 24 | { 25 | try 26 | { 27 | base.Page_Load(sender, e); 28 | 29 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://maps.googleapis.com/maps/api/js?key=" + GoogleAPIKey), 20); 30 | } 31 | catch (Exception ex) 32 | { 33 | Exceptions.ProcessModuleLoadException(this, ex); 34 | } 35 | } 36 | protected string ApiUrlBase 37 | { 38 | get 39 | { 40 | if (DotNetNuke.Application.DotNetNukeContext.Current.Application.CurrentVersion.Major < 9) 41 | { 42 | return "/desktopmodules/Dnn.WebAnalytics/api"; 43 | } 44 | return "/api/Dnn.WebAnalytics"; 45 | } 46 | } 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /View.ascx.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Services.Exceptions; 2 | using System; 3 | using DotNetNuke.Entities.Controllers; 4 | using DotNetNuke.Entities.Host; 5 | using DotNetNuke.Entities.Modules.Actions; 6 | using DotNetNuke.Entities.Modules; 7 | 8 | namespace Dnn.WebAnalytics 9 | { 10 | partial class View : ModuleBase, IActionable 11 | { 12 | public ModuleActionCollection ModuleActions 13 | { 14 | get 15 | { 16 | ModuleActionCollection Actions = new ModuleActionCollection(); 17 | if (IsEditable) 18 | { 19 | Actions.Add(GetNextActionID(), "Admin", ModuleActionType.AddContent, "", "", EditUrl("Admin"), false, DotNetNuke.Security.SecurityAccessLevel.Edit, true, false); 20 | } 21 | return Actions; 22 | } 23 | } 24 | 25 | protected new void Page_Load(Object sender, EventArgs e) 26 | { 27 | try 28 | { 29 | base.Page_Load(sender, e); 30 | } 31 | catch (Exception ex) 32 | { 33 | Exceptions.ProcessModuleLoadException(this, ex); 34 | } 35 | } 36 | 37 | protected string ApiUrlBase 38 | { 39 | get 40 | { 41 | if (DotNetNuke.Application.DotNetNukeContext.Current.Application.CurrentVersion.Major < 9) 42 | { 43 | return "/desktopmodules/Dnn.WebAnalytics/api"; 44 | } 45 | return "/api/Dnn.WebAnalytics"; 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DTOs/VisitDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dnn.WebAnalytics 4 | { 5 | public class VisitDTO 6 | { 7 | // initialization 8 | public VisitDTO() 9 | { 10 | } 11 | 12 | // public properties 13 | public Int64 id { get; set; } 14 | 15 | public DateTime date { get; set; } 16 | 17 | public int visitor_id { get; set; } 18 | 19 | public int tab_id { get; set; } 20 | 21 | public string ip { get; set; } 22 | 23 | public string country { get; set; } 24 | 25 | public string region { get; set; } 26 | 27 | public string city { get; set; } 28 | 29 | public string latitude { get; set; } 30 | 31 | public string longitude { get; set; } 32 | 33 | public string language { get; set; } 34 | 35 | public string domain { get; set; } 36 | 37 | public string url { get; set; } 38 | 39 | public string user_agent { get; set; } 40 | 41 | public string device_type { get; set; } 42 | 43 | public string device { get; set; } 44 | 45 | public string platform { get; set; } 46 | 47 | public string browser { get; set; } 48 | 49 | public string referrer_domain { get; set; } 50 | 51 | public string referrer_url { get; set; } 52 | 53 | public string server { get; set; } 54 | 55 | public string activity { get; set; } 56 | 57 | public string campaign { get; set; } 58 | 59 | public Nullable session_id { get; set; } 60 | 61 | public Nullable request_id { get; set; } 62 | 63 | public Nullable last_request_id { get; set; } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/services/visitor.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.factory('visitorService', ['$http', function visitorService($http) { 2 | 3 | var base_path = apiUrlBase + "/visitor"; 4 | 5 | // interface 6 | var service = { 7 | list: list, 8 | get: get, 9 | insert: insert, 10 | update: update, 11 | remove: remove, 12 | save: save 13 | }; 14 | return service; 15 | 16 | // implementation 17 | 18 | // list 19 | function list() { 20 | var request = $http({ 21 | method: "get", 22 | url: base_path 23 | }); 24 | return request; 25 | } 26 | 27 | // get 28 | function get(id) { 29 | var request = $http({ 30 | method: "get", 31 | url: base_path + '/' + id 32 | }); 33 | return request; 34 | } 35 | 36 | // insert 37 | function insert(item) { 38 | var request = $http({ 39 | method: "post", 40 | url: base_path, 41 | data: item 42 | }); 43 | return request; 44 | } 45 | 46 | // update 47 | function update(item) { 48 | var request = $http({ 49 | method: "put", 50 | url: base_path, 51 | data: item 52 | }); 53 | return request; 54 | } 55 | 56 | // delete 57 | function remove(id) { 58 | var request = $http({ 59 | method: "delete", 60 | url: base_path + '/' + id 61 | }); 62 | return request; 63 | } 64 | 65 | // save 66 | function save(item) { 67 | if (item.id > 0) { 68 | return update(item); 69 | } 70 | else { 71 | return insert(item); 72 | } 73 | } 74 | 75 | }]); 76 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /plugins/datetime-picker/datetime-picker.tpls.js: -------------------------------------------------------------------------------- 1 | angular.module('ui.bootstrap.datetimepicker').run(['$templateCache', function($templateCache) { 2 | 'use strict'; 3 | 4 | $templateCache.put('template/date-picker.html', 5 | "
" 6 | ); 7 | 8 | 9 | $templateCache.put('template/time-picker.html', 10 | "
" 11 | ); 12 | 13 | }]); 14 | -------------------------------------------------------------------------------- /app/services/visit.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.factory('visitService', ['$http', '$filter', function visitService($http, $filter) { 2 | 3 | var base_path = apiUrlBase + "/visit"; 4 | 5 | // interface 6 | var service = { 7 | list: list, 8 | get: get, 9 | insert: insert, 10 | update: update, 11 | remove: remove, 12 | save: save, 13 | getDashboard: getDashboard, 14 | getReport: getReport 15 | }; 16 | return service; 17 | 18 | // implementation 19 | 20 | // list 21 | function list() { 22 | var request = $http({ 23 | method: "get", 24 | url: base_path 25 | }); 26 | return request; 27 | } 28 | 29 | // get 30 | function get(id) { 31 | var request = $http({ 32 | method: "get", 33 | url: base_path + '/' + id 34 | }); 35 | return request; 36 | } 37 | 38 | // insert 39 | function insert(item) { 40 | var request = $http({ 41 | method: "post", 42 | url: base_path, 43 | data: item 44 | }); 45 | return request; 46 | } 47 | 48 | // update 49 | function update(item) { 50 | var request = $http({ 51 | method: "put", 52 | url: base_path, 53 | data: item 54 | }); 55 | return request; 56 | } 57 | 58 | // delete 59 | function remove(id) { 60 | var request = $http({ 61 | method: "delete", 62 | url: base_path + '/' + id 63 | }); 64 | return request; 65 | } 66 | 67 | // save 68 | function save(item) { 69 | if (item.id > 0) { 70 | return update(item); 71 | } 72 | else { 73 | return insert(item); 74 | } 75 | } 76 | 77 | // get dashboard 78 | function getDashboard(portal_id, period_start = null, period_end = null) { 79 | 80 | if (period_start) { 81 | period_start = $filter('date')(period_start, 'MM/dd/yyyy'); 82 | } 83 | if (period_end) { 84 | period_end = $filter('date')(period_end, 'MM/dd/yyyy'); 85 | } 86 | 87 | var request = $http({ 88 | method: "get", 89 | url: base_path + '?portal_id=' + portal_id + '&period_start=' + period_start + '&period_end=' + period_end 90 | }); 91 | return request; 92 | } 93 | 94 | // get report 95 | function getReport(field, portal_id, period_start = null, period_end = null, rows) { 96 | 97 | if (period_start) { 98 | period_start = $filter('date')(period_start, 'MM/dd/yyyy'); 99 | } 100 | if (period_end) { 101 | period_end = $filter('date')(period_end, 'MM/dd/yyyy'); 102 | } 103 | 104 | var request = $http({ 105 | method: "get", 106 | url: base_path + '?field=' + field + '&portal_id=' + portal_id + '&period_start=' + period_start + '&period_end=' + period_end + '&rows=' + rows 107 | }); 108 | return request; 109 | } 110 | 111 | }]); 112 | -------------------------------------------------------------------------------- /app/controllers/map.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.controller('mapController', ['$scope', '$q', 'toastr', 'mapService', function ($scope, $q, toastr, mapService) { 2 | 3 | $scope.minutes = "10"; 4 | var markersArray = []; 5 | 6 | $scope.visitors_online = 0; 7 | $scope.users_online = 0; 8 | 9 | var red_flag = "http://maps.google.com/mapfiles/ms/icons/red.png"; 10 | var blue_flag = "http://maps.google.com/mapfiles/ms/icons/blue.png"; 11 | 12 | function clearOverlays() { 13 | for (var i = 0; i < markersArray.length; i++) { 14 | markersArray[i].setMap(null); 15 | } 16 | markersArray = new Array(); 17 | } 18 | 19 | $scope.getMapPoints = function () { 20 | var deferred = $q.defer(); 21 | 22 | var icon; 23 | clearOverlays(); 24 | $scope.visitors_online = 0; 25 | $scope.users_online = 0; 26 | 27 | mapService.getMapPoints($scope.minutes).then( 28 | function (response) { 29 | $scope.map_points = response.data; 30 | 31 | for (var i = 0; i < $scope.map_points.length; i++) { 32 | var map_point = $scope.map_points[i]; 33 | 34 | if (map_point.user_id) { 35 | $scope.users_online++; 36 | icon = blue_flag; 37 | } 38 | else { 39 | $scope.visitors_online++; 40 | icon = red_flag; 41 | } 42 | 43 | var marker = new google.maps.Marker({ 44 | position: new google.maps.LatLng(map_point.latitude, map_point.longitude), 45 | map: $scope.map, 46 | icon: icon 47 | }); 48 | markersArray.push(marker); 49 | } 50 | 51 | deferred.resolve(); 52 | }, 53 | function (response) { 54 | console.log('getMapPoints failed', response); 55 | toastr.error("There was a problem loading the map points", "Error"); 56 | deferred.reject(); 57 | } 58 | ); 59 | return deferred.promise; 60 | }; 61 | 62 | $scope.getMapCenter = function () { 63 | var deferred = $q.defer(); 64 | 65 | mapService.getMapCenter().then( 66 | function (response) { 67 | $scope.map_center = response.data; 68 | 69 | deferred.resolve(); 70 | }, 71 | function (response) { 72 | console.log('getMapCenter failed', response); 73 | toastr.error("There was a problem loading the map center", "Error"); 74 | deferred.reject(); 75 | } 76 | ); 77 | return deferred.promise; 78 | }; 79 | 80 | $scope.initMap = function () { 81 | $scope.map = new google.maps.Map(document.getElementById('map'), { 82 | zoom: 2, 83 | center: new google.maps.LatLng($scope.map_center.latitude, $scope.map_center.longitude), 84 | mapTypeId: google.maps.MapTypeId.ROADMAP 85 | }); 86 | $scope.getMapPoints(); 87 | }; 88 | 89 | init = function () { 90 | var promises = []; 91 | promises.push($scope.getMapCenter()); 92 | promises.push($scope.getMapPoints()); 93 | return $q.all(promises); 94 | }; 95 | init().then(function () { 96 | $scope.initMap(); 97 | }); 98 | }]); 99 | -------------------------------------------------------------------------------- /Scripts/01.01.00.sql: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Remove constraints 4 | */ 5 | 6 | IF EXISTS (SELECT 1 7 | FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 8 | WHERE CONSTRAINT_NAME = N'FK_{objectQualifier}Visits_Visitors' 9 | AND TABLE_NAME = N'{objectQualifier}Visits') 10 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] DROP CONSTRAINT [FK_{objectQualifier}Visits_Visitors]; 11 | GO 12 | 13 | IF EXISTS (SELECT 1 14 | FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 15 | WHERE CONSTRAINT_NAME = N'FK_{objectQualifier}Visits_Tabs' 16 | AND TABLE_NAME = N'{objectQualifier}Visits') 17 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] DROP CONSTRAINT [FK_{objectQualifier}Visits_Tabs]; 18 | GO 19 | 20 | IF EXISTS (SELECT 1 21 | FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 22 | WHERE CONSTRAINT_NAME = N'FK_{objectQualifier}Visitors_Users' 23 | AND TABLE_NAME = N'{objectQualifier}Visitors') 24 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] DROP CONSTRAINT [FK_{objectQualifier}Visitors_Users]; 25 | GO 26 | 27 | IF EXISTS (SELECT 1 28 | FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 29 | WHERE CONSTRAINT_NAME = N'FK_{objectQualifier}Visitors_Portals' 30 | AND TABLE_NAME = N'{objectQualifier}Visitors') 31 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] DROP CONSTRAINT [FK_{objectQualifier}Visitors_Portals]; 32 | GO 33 | 34 | /* 35 | Rename community tables & PK's to prevent conflicts and group the data together 36 | */ 37 | 38 | sp_rename N'{objectQualifier}Visitors', N'{objectQualifier}Community_Visitors'; 39 | GO 40 | 41 | sp_rename @objname = N'[PK_{objectQualifier}Visitors]', @newname = N'PK_{objectQualifier}Community_Visitors'; 42 | GO 43 | 44 | sp_rename N'{objectQualifier}Visits', N'{objectQualifier}Community_Visits'; 45 | GO 46 | 47 | sp_rename @objname = N'[PK_{objectQualifier}Visits]', @newname = N'PK_{objectQualifier}Community_Visits'; 48 | GO 49 | 50 | /* 51 | Add constraints back 52 | */ 53 | 54 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visitors] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Community_Visitors_Portals] FOREIGN KEY([portal_id]) 55 | REFERENCES {databaseOwner}[{objectQualifier}Portals] ([PortalID]) 56 | ON DELETE CASCADE; 57 | GO 58 | 59 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visitors] CHECK CONSTRAINT [FK_{objectQualifier}Community_Visitors_Portals]; 60 | GO 61 | 62 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visitors] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Community_Visitors_Users] FOREIGN KEY([user_id]) 63 | REFERENCES {databaseOwner}[{objectQualifier}Users] ([UserID]) 64 | ON UPDATE CASCADE 65 | ON DELETE CASCADE; 66 | GO 67 | 68 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visitors] CHECK CONSTRAINT [FK_{objectQualifier}Community_Visitors_Users]; 69 | GO 70 | 71 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visits] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Community_Visits_Tabs] FOREIGN KEY([tab_id]) 72 | REFERENCES {databaseOwner}[{objectQualifier}Tabs] ([TabID]); 73 | GO 74 | 75 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visits] CHECK CONSTRAINT [FK_{objectQualifier}Community_Visits_Tabs]; 76 | GO 77 | 78 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visits] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Community_Visits_Visitors] FOREIGN KEY([visitor_id]) 79 | REFERENCES {databaseOwner}[{objectQualifier}Community_Visitors] ([id]) 80 | ON DELETE CASCADE; 81 | GO 82 | 83 | ALTER TABLE {databaseOwner}[{objectQualifier}Community_Visits] CHECK CONSTRAINT [FK_{objectQualifier}Community_Visits_Visitors]; 84 | GO 85 | -------------------------------------------------------------------------------- /ModuleBase.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Entities.Modules; 2 | using DotNetNuke.Framework.JavaScriptLibraries; 3 | using DotNetNuke.Web.Client.ClientResourceManagement; 4 | using System; 5 | 6 | namespace Dnn.WebAnalytics 7 | { 8 | public class ModuleBase : PortalModuleBase 9 | { 10 | protected void Page_Load(Object sender, EventArgs e) 11 | { 12 | JavaScript.RequestRegistration(CommonJs.jQuery); 13 | JavaScript.RequestRegistration(CommonJs.jQueryUI); 14 | 15 | ClientResourceManager.RegisterStyleSheet(this.Page, ResolveUrl("https://use.fontawesome.com/releases/v5.7.2/css/all.css"), 1); 16 | 17 | ClientResourceManager.RegisterStyleSheet(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/plugins/angular-toastr/angular-toastr.min.css"), 1); 18 | 19 | ClientResourceManager.RegisterStyleSheet(this.Page, ResolveUrl("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css"), 2); 20 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"), 5); 21 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/plugins/angular-chart/angular-chart.min.js"), 6); 22 | 23 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular.min.js"), 2); 24 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-messages.min.js"), 3); 25 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-animate.min.js"), 3); 26 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-sanitize.min.js"), 4); 27 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-cookies.min.js"), 4); 28 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-route.min.js"), 4); 29 | 30 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/plugins/angular-toastr/angular-toastr.tpls.min.js"), 5); 31 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/plugins/ui.bootstrap/ui-bootstrap-tpls-2.5.0.min.js"), 6); 32 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/plugins/datetime-picker/datetime-picker.min.js"), 7); 33 | 34 | // app 35 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/app.js"), 7); 36 | 37 | // services 38 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/services/visit.js"), 15); 39 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/services/visitor.js"), 15); ; 40 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/services/map.js"), 15); 41 | 42 | // directives 43 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/directives/view.js"), 15); 44 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/directives/map.js"), 15); 45 | 46 | // controllers 47 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/controllers/view.js"), 15); 48 | ClientResourceManager.RegisterScript(this.Page, ResolveUrl("/DesktopModules/Dnn.WebAnalytics/app/controllers/map.js"), 15); 49 | 50 | 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Controllers/MapController.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Security; 2 | using DotNetNuke.Services.Exceptions; 3 | using DotNetNuke.Web.Api; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Web.Http; 11 | using MaxMind.GeoIP2; 12 | 13 | namespace Dnn.WebAnalytics 14 | { 15 | //[SupportedModules("Dnn.WebAnalytics")] 16 | //[ValidateAntiForgeryToken] 17 | public class MapController : DnnApiController 18 | { 19 | DataContext dc = new DataContext(); 20 | 21 | [HttpGet] 22 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)] 23 | [AllowAnonymous] 24 | public HttpResponseMessage Get(int minutes) 25 | { 26 | try 27 | { 28 | List dtos = new List(); 29 | 30 | List recent_visits = dc.Community_Visits 31 | .Where(i => 32 | i.date >= DateTime.Now.AddMinutes(-minutes) && 33 | i.latitude != "" && i.longitude != "" 34 | ) 35 | .ToList(); 36 | 37 | var recent_unique_visits = recent_visits 38 | .GroupBy(i => i.session_id) 39 | .Select(i => new 40 | { 41 | id = i.Max(o => o.id) 42 | }) 43 | .ToList(); 44 | 45 | var ids = recent_unique_visits.Select(i => i.id).ToList(); 46 | 47 | foreach (long id in ids) 48 | { 49 | var visit = dc.Community_Visits.Where(i => i.id == id).SingleOrDefault(); 50 | if (visit != null) 51 | { 52 | MapDTO mapDTO = new MapDTO() 53 | { 54 | user_id = visit.Community_Visitor.user_id, 55 | latitude = visit.latitude, 56 | longitude = visit.longitude 57 | }; 58 | dtos.Add(mapDTO); 59 | 60 | } 61 | } 62 | 63 | return Request.CreateResponse(HttpStatusCode.OK, dtos); 64 | } 65 | catch (Exception ex) 66 | { 67 | Exceptions.LogException(ex); 68 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 69 | } 70 | } 71 | 72 | [HttpGet] 73 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)] 74 | [AllowAnonymous] 75 | public HttpResponseMessage Get() 76 | { 77 | try 78 | { 79 | MapDTO dto = new MapDTO() 80 | { 81 | latitude = "37.09024", 82 | longitude = "-95.712891" 83 | }; 84 | 85 | string ip_address = this.Request.GetIPAddress(); 86 | 87 | //ip_address = "104.185.202.20"; // for testing on localhost, should resolve to Atlanta, GA... 88 | 89 | using (var objGeoIP2DB = new DatabaseReader(string.Concat(AppDomain.CurrentDomain.BaseDirectory, "App_Data\\GeoIP2-City.mmdb"))) 90 | { 91 | try 92 | { 93 | var objGeoIP2 = objGeoIP2DB.City(ip_address); 94 | if (objGeoIP2 != null) 95 | { 96 | dto.latitude = objGeoIP2.Location.Latitude.ToString(); 97 | dto.longitude = objGeoIP2.Location.Longitude.ToString(); 98 | } 99 | } 100 | catch 101 | { 102 | // IP address cannot be resolved 103 | } 104 | } 105 | 106 | return Request.CreateResponse(HttpStatusCode.OK, dto); 107 | } 108 | catch (Exception ex) 109 | { 110 | Exceptions.LogException(ex); 111 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Scripts/01.00.00.sql: -------------------------------------------------------------------------------- 1 | IF NOT OBJECT_ID('{databaseOwner}[{objectQualifier}Visits]') IS NULL 2 | DROP TABLE {databaseOwner}[{objectQualifier}Visits]; 3 | GO 4 | 5 | CREATE TABLE {databaseOwner}[{objectQualifier}Visits]( 6 | [id] [bigint] IDENTITY(1,1) NOT NULL, 7 | [date] [datetime] NOT NULL, 8 | [visitor_id] [int] NOT NULL, 9 | [tab_id] [int] NOT NULL, 10 | [ip] [nvarchar](50) NOT NULL, 11 | [country] [nvarchar](50) NOT NULL, 12 | [region] [nvarchar](50) NOT NULL, 13 | [city] [nvarchar](50) NOT NULL, 14 | [latitude] [nvarchar](50) NOT NULL, 15 | [longitude] [nvarchar](50) NOT NULL, 16 | [language] [nvarchar](50) NOT NULL, 17 | [domain] [nvarchar](255) NOT NULL, 18 | [url] [nvarchar](2048) NOT NULL, 19 | [user_agent] [nvarchar](512) NOT NULL, 20 | [device_type] [nvarchar](50) NOT NULL, 21 | [device] [nvarchar](255) NOT NULL, 22 | [platform] [nvarchar](255) NOT NULL, 23 | [browser] [nvarchar](255) NOT NULL, 24 | [referrer_domain] [nvarchar](255) NOT NULL, 25 | [referrer_url] [nvarchar](2048) NOT NULL, 26 | [server] [nvarchar](50) NOT NULL, 27 | [campaign] [nvarchar](50) NOT NULL, 28 | [session_id] [uniqueidentifier] NULL, 29 | [request_id] [uniqueidentifier] NULL, 30 | [last_request_id] [uniqueidentifier] NULL, 31 | CONSTRAINT [PK_{objectQualifier}Visits] PRIMARY KEY CLUSTERED 32 | ( 33 | [id] ASC 34 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 35 | ) 36 | GO 37 | 38 | IF NOT OBJECT_ID('{databaseOwner}[{objectQualifier}Visitors]') IS NULL 39 | DROP TABLE {databaseOwner}[{objectQualifier}Visitors]; 40 | GO 41 | 42 | CREATE TABLE {databaseOwner}[{objectQualifier}Visitors]( 43 | [id] [int] IDENTITY(1,1) NOT NULL, 44 | [portal_id] [int] NOT NULL, 45 | [user_id] [int] NULL, 46 | [created_on_date] [datetime] NOT NULL, 47 | CONSTRAINT [PK_{objectQualifier}Visitors] PRIMARY KEY CLUSTERED 48 | ( 49 | [id] ASC 50 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 51 | ) 52 | GO 53 | 54 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Visitors_Portals] FOREIGN KEY([portal_id]) 55 | REFERENCES {databaseOwner}[{objectQualifier}Portals] ([PortalID]) 56 | ON DELETE CASCADE 57 | GO 58 | 59 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] CHECK CONSTRAINT [FK_{objectQualifier}Visitors_Portals] 60 | GO 61 | 62 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Visitors_Users] FOREIGN KEY([user_id]) 63 | REFERENCES {databaseOwner}[{objectQualifier}Users] ([UserID]) 64 | ON UPDATE CASCADE 65 | ON DELETE CASCADE 66 | GO 67 | 68 | ALTER TABLE {databaseOwner}[{objectQualifier}Visitors] CHECK CONSTRAINT [FK_{objectQualifier}Visitors_Users] 69 | GO 70 | 71 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Visits_Tabs] FOREIGN KEY([tab_id]) 72 | REFERENCES {databaseOwner}[{objectQualifier}Tabs] ([TabID]) 73 | GO 74 | 75 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] CHECK CONSTRAINT [FK_{objectQualifier}Visits_Tabs] 76 | GO 77 | 78 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] WITH CHECK ADD CONSTRAINT [FK_{objectQualifier}Visits_Visitors] FOREIGN KEY([visitor_id]) 79 | REFERENCES {databaseOwner}[{objectQualifier}Visitors] ([id]) 80 | ON DELETE CASCADE 81 | 82 | GO 83 | ALTER TABLE {databaseOwner}[{objectQualifier}Visits] CHECK CONSTRAINT [FK_{objectQualifier}Visits_Visitors] 84 | GO 85 | 86 | IF EXISTS(SELECT 1 FROM {databaseOwner}[{objectQualifier}Schedule] WHERE [TypeFullName] = N'Dnn.WebAnalyticsLinqToSql.VisitorJob, Dnn.WebAnalyticsLinqToSql') 87 | BEGIN 88 | UPDATE {databaseOwner}[{objectQualifier}Schedule] 89 | SET 90 | [TimeLapse] = 1, 91 | [TimeLapseMeasurement] = N'd', 92 | [RetryTimeLapse] = 1, 93 | [RetryTimeLapseMeasurement] = N'd', 94 | [RetainHistoryNum] = 10, 95 | [AttachToEvent] = N'', 96 | [CatchUpEnabled] = 0, 97 | [Enabled] = 1, 98 | [ObjectDependencies] = N'', 99 | [Servers] = null, 100 | [FriendlyName] = 'Visitor Tracking Job' 101 | WHERE 102 | [TypeFullName] = N'Dnn.WebAnalyticsLinqToSql.VisitorJob, Dnn.WebAnalyticsLinqToSql'; 103 | END 104 | ELSE 105 | BEGIN 106 | INSERT INTO {databaseOwner}[{objectQualifier}Schedule] 107 | ( [TypeFullName], [TimeLapse], [TimeLapseMeasurement], [RetryTimeLapse], [RetryTimeLapseMeasurement], [RetainHistoryNum], [AttachToEvent], [CatchUpEnabled], [Enabled], [ObjectDependencies], [Servers], [FriendlyName]) 108 | VALUES ( 'Dnn.WebAnalyticsLinqToSql.VisitorJob, Dnn.WebAnalyticsLinqToSql', 1, 'd', 1, 'd', 10, '', 0, 1, '', null, 'Visitor Tracking Job' ); 109 | END 110 | GO 111 | -------------------------------------------------------------------------------- /DAL/WebAnalytics.dbml.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Controllers/VisitorController.cs: -------------------------------------------------------------------------------- 1 | using DotNetNuke.Security; 2 | using DotNetNuke.Services.Exceptions; 3 | using DotNetNuke.Web.Api; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Web.Http; 11 | 12 | namespace Dnn.WebAnalytics 13 | { 14 | [SupportedModules("Dnn.WebAnalytics")] 15 | [ValidateAntiForgeryToken] 16 | public class VisitorController : DnnApiController 17 | { 18 | DataContext dc = new DataContext(); 19 | 20 | [NonAction] 21 | public VisitorDTO ConvertItemToDto(Community_Visitor item) 22 | { 23 | VisitorDTO dto = new VisitorDTO(); 24 | 25 | dto.id = item.id; 26 | dto.portal_id = item.portal_id; 27 | dto.user_id = item.user_id; 28 | dto.created_on_date = item.created_on_date; 29 | 30 | dto.user_username = item.User.Username; 31 | dto.user_displayname = item.User.DisplayName; 32 | 33 | return dto; 34 | } 35 | [NonAction] 36 | public Community_Visitor ConvertDtoToItem(Community_Visitor item, VisitorDTO dto) 37 | { 38 | if (item == null) 39 | { 40 | item = new Community_Visitor(); 41 | } 42 | 43 | if (dto == null) 44 | { 45 | return item; 46 | } 47 | 48 | item.id = dto.id; 49 | item.portal_id = dto.portal_id; 50 | item.user_id = dto.user_id; 51 | item.created_on_date = dto.created_on_date; 52 | 53 | return item; 54 | } 55 | 56 | [HttpGet] 57 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)] 58 | [AllowAnonymous] 59 | public HttpResponseMessage Get() 60 | { 61 | try 62 | { 63 | List dtos = new List(); 64 | 65 | var visitors = dc.Community_Visitors.ToList(); 66 | foreach (Community_Visitor visitor in visitors) 67 | { 68 | VisitorDTO visitorDTO = ConvertItemToDto(visitor); 69 | dtos.Add(visitorDTO); 70 | } 71 | return Request.CreateResponse(HttpStatusCode.OK, dtos); 72 | } 73 | catch (Exception ex) 74 | { 75 | Exceptions.LogException(ex); 76 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 77 | } 78 | } 79 | 80 | [HttpGet] 81 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)] 82 | [AllowAnonymous] 83 | public HttpResponseMessage Get(int id) 84 | { 85 | try 86 | { 87 | Community_Visitor item = dc.Community_Visitors.Where(i => i.id == id).SingleOrDefault(); 88 | 89 | if (item == null) 90 | { 91 | return Request.CreateResponse(HttpStatusCode.NotFound); 92 | } 93 | 94 | return Request.CreateResponse(HttpStatusCode.OK, ConvertItemToDto(item)); 95 | } 96 | catch (Exception ex) 97 | { 98 | Exceptions.LogException(ex); 99 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 100 | } 101 | } 102 | 103 | [HttpPost] 104 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)] 105 | public HttpResponseMessage Post(VisitorDTO dto) 106 | { 107 | try 108 | { 109 | dto = SaveVisitor(dto); 110 | 111 | return Request.CreateResponse(HttpStatusCode.OK, dto); 112 | } 113 | catch (Exception ex) 114 | { 115 | Exceptions.LogException(ex); 116 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 117 | } 118 | } 119 | 120 | [HttpPut] 121 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)] 122 | public HttpResponseMessage Put(VisitorDTO dto) 123 | { 124 | try 125 | { 126 | dto = SaveVisitor(dto); 127 | 128 | return Request.CreateResponse(HttpStatusCode.OK, dto); 129 | } 130 | catch (Exception ex) 131 | { 132 | Exceptions.LogException(ex); 133 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 134 | } 135 | } 136 | 137 | [HttpDelete] 138 | [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)] 139 | public HttpResponseMessage Delete(int id) 140 | { 141 | try 142 | { 143 | Community_Visitor item = dc.Community_Visitors.Where(i => i.id == id).SingleOrDefault(); 144 | 145 | if (item == null) 146 | { 147 | return Request.CreateResponse(HttpStatusCode.NotFound); 148 | } 149 | 150 | dc.Community_Visitors.DeleteOnSubmit(item); 151 | dc.SubmitChanges(); 152 | 153 | return Request.CreateResponse(HttpStatusCode.OK); 154 | } 155 | catch (Exception ex) 156 | { 157 | Exceptions.LogException(ex); 158 | return Request.CreateResponse(HttpStatusCode.InternalServerError, ex); 159 | } 160 | } 161 | 162 | [NonAction] 163 | public VisitorDTO SaveVisitor(VisitorDTO dto) 164 | { 165 | Community_Visitor visitor = dc.Community_Visitors.Where(i => i.id == dto.id).SingleOrDefault(); 166 | 167 | if (visitor == null) 168 | { 169 | visitor = ConvertDtoToItem(null, dto); 170 | visitor.created_on_date = DateTime.Now; 171 | 172 | dc.Community_Visitors.InsertOnSubmit(visitor); 173 | } 174 | 175 | visitor = ConvertDtoToItem(visitor, dto); 176 | 177 | dc.SubmitChanges(); 178 | 179 | return ConvertItemToDto(visitor); 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /plugins/angular-chart/angular-chart.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-chart.js - An angular.js wrapper for Chart.js 3 | * http://jtblin.github.io/angular-chart.js/ 4 | * Version: 1.1.1 5 | * 6 | * Copyright 2016 Jerome Touffe-Blin 7 | * Released under the BSD-2-Clause license 8 | * https://github.com/jtblin/angular-chart.js/blob/master/LICENSE 9 | */ 10 | !function(t){"use strict";if("object"==typeof exports)module.exports=t("undefined"!=typeof angular?angular:require("angular"),"undefined"!=typeof Chart?Chart:require("chart.js"));else if("function"==typeof define&&define.amd)define(["angular","chart"],t);else{if("undefined"==typeof angular)throw new Error("AngularJS framework needs to be included, see https://angularjs.org/");if("undefined"==typeof Chart)throw new Error("Chart.js library needs to be included, see http://jtblin.github.io/angular-chart.js/");t(angular,Chart)}}(function(t,r){"use strict";function e(){var e={responsive:!0},a={Chart:r,getOptions:function(r){var a=r&&e[r]||{};return t.extend({},e,a)}};this.setOptions=function(r,n){n?e[r]=t.merge(e[r]||{},n):(n=r,e=t.merge(e,n)),t.merge(a.Chart.defaults,e)},this.$get=function(){return a}}function a(e,a){function o(t,r,a){var n=D(t,r);if(C(r)&&k(t,r,a,n)){var o=a[0],c=o.getContext("2d");r.chartGetColor=y(r);var i=b(t,r);F(r),r.chart=new e.Chart(c,{type:t,data:i,options:n}),r.$emit("chart-create",r.chart),A(o,r)}}function c(t,r){return!!(t&&r&&t.length&&r.length)&&(Array.isArray(t[0])?t.length===r.length&&t.every(function(t,e){return t.length===r[e].length}):r.reduce(i,0)>0&&t.length===r.length)}function i(t,r){return t+r}function u(r,e,a){var n={point:void 0,points:void 0};return function(o){var c=r.chart.getElementAtEvent||r.chart.getPointAtEvent,i=r.chart.getElementsAtEvent||r.chart.getPointsAtEvent;if(i){var u=i.call(r.chart,o),l=c?c.call(r.chart,o)[0]:void 0;a!==!1&&(t.equals(n.points,u)||t.equals(n.point,l))||(n.point=l,n.points=u,r[e](u,o,l))}}}function l(a,n){for(var o=t.copy(n.chartColors||e.getOptions(a).chartColors||r.defaults.global.colors),c=o.length>16&255,a=r>>8&255,n=255&r;return[e,a,n]}function v(t){var r=t.match(/^rgba?\(([\d,.]+)\)$/);if(!r)throw new Error("Cannot parse rgb value");return t=r[1].split(","),t.map(Number)}function C(t){return t.chartData&&t.chartData.length}function y(t){return"function"==typeof t.chartGetColor?t.chartGetColor:s}function b(t,r){var e=l(t,r);return Array.isArray(r.chartData[0])?m(r.chartLabels,r.chartData,r.chartSeries||[],e,r.chartDatasetOverride):w(r.chartLabels,r.chartData,e,r.chartDatasetOverride)}function m(r,e,a,n,o){return{labels:r,datasets:e.map(function(r,e){var c=t.extend({},n[e],{label:a[e],data:r});return o&&o.length>=e&&t.merge(c,o[e]),c})}}function w(r,e,a,n){var o={labels:r,datasets:[{data:e,backgroundColor:a.map(function(t){return t.pointBackgroundColor}),hoverBackgroundColor:a.map(function(t){return t.backgroundColor})}]};return n&&t.merge(o.datasets[0],n),o}function D(r,a){return t.extend({},e.getOptions(r),a.chartOptions)}function A(r,e){r.onclick=e.chartClick?u(e,"chartClick",!1):t.noop,r.onmousemove=e.chartHover?u(e,"chartHover",!0):t.noop}function B(t,r){Array.isArray(r.chartData[0])?r.chart.data.datasets.forEach(function(r,e){r.data=t[e]}):r.chart.data.datasets[0].data=t,r.chart.update(),r.$emit("chart-update",r.chart)}function $(t){return!t||Array.isArray(t)&&!t.length||"object"==typeof t&&!Object.keys(t).length}function k(t,r,e,n){return!n.responsive||0!==e[0].clientHeight||(a(function(){o(t,r,e)},50,!1),!1)}function F(t){t.chart&&(t.chart.destroy(),t.$emit("chart-destroy",t.chart))}return function(r){return{restrict:"CA",scope:{chartGetColor:"=?",chartType:"=",chartData:"=?",chartLabels:"=?",chartOptions:"=?",chartSeries:"=?",chartColors:"=?",chartClick:"=?",chartHover:"=?",chartDatasetOverride:"=?"},link:function(e,a){function i(t,n){if(!t||!t.length||Array.isArray(t[0])&&!t[0].length)return void F(e);var i=r||e.chartType;if(i)return e.chart&&c(t,n)?B(t,e):void o(i,e,a)}function u(n,c){if(!$(n)&&!t.equals(n,c)){var i=r||e.chartType;i&&o(i,e,a)}}function l(r,n){$(r)||t.equals(r,n)||o(r,e,a)}n&&window.G_vmlCanvasManager.initElement(a[0]),e.$watch("chartData",i,!0),e.$watch("chartSeries",u,!0),e.$watch("chartLabels",u,!0),e.$watch("chartOptions",u,!0),e.$watch("chartColors",u,!0),e.$watch("chartDatasetOverride",u,!0),e.$watch("chartType",l,!1),e.$on("$destroy",function(){F(e)}),e.$on("$resize",function(){e.chart&&e.chart.resize()})}}}}r.defaults.global.multiTooltipTemplate="<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>",r.defaults.global.tooltips.mode="label",r.defaults.global.elements.line.borderWidth=2,r.defaults.global.elements.rectangle.borderWidth=2,r.defaults.global.legend.display=!1,r.defaults.global.colors=["#97BBCD","#DCDCDC","#F7464A","#46BFBD","#FDB45C","#949FB1","#4D5360"];var n="object"==typeof window.G_vmlCanvasManager&&null!==window.G_vmlCanvasManager&&"function"==typeof window.G_vmlCanvasManager.initElement;return n&&(r.defaults.global.animation=!1),t.module("chart.js",[]).provider("ChartJs",e).factory("ChartJsFactory",["ChartJs","$timeout",a]).directive("chartBase",["ChartJsFactory",function(t){return new t}]).directive("chartLine",["ChartJsFactory",function(t){return new t("line")}]).directive("chartBar",["ChartJsFactory",function(t){return new t("bar")}]).directive("chartHorizontalBar",["ChartJsFactory",function(t){return new t("horizontalBar")}]).directive("chartRadar",["ChartJsFactory",function(t){return new t("radar")}]).directive("chartDoughnut",["ChartJsFactory",function(t){return new t("doughnut")}]).directive("chartPie",["ChartJsFactory",function(t){return new t("pie")}]).directive("chartPolarArea",["ChartJsFactory",function(t){return new t("polarArea")}]).directive("chartBubble",["ChartJsFactory",function(t){return new t("bubble")}]).name}); 11 | //# sourceMappingURL=angular-chart.min.js.map 12 | -------------------------------------------------------------------------------- /app/views/view.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 | Search 30 |
31 |
32 | 33 |
34 |
35 | 36 | 37 | 38 |
39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 | 47 |
48 | 49 |
50 |
51 | 52 |

Data Analysis

53 |
54 |
55 |
56 | 57 | 79 |
80 |
81 |
82 |
83 | 84 | 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 | 116 | 117 | 118 | 121 | 124 | 127 | 130 | 131 | 132 |
FieldCountTotalPercent
119 | {{report_row.field}} 120 | 122 | {{report_row.count}} 123 | 125 | {{report_row.total}} 126 | 128 | {{report_row.percent}} 129 |
133 | 134 |
135 |
-------------------------------------------------------------------------------- /msbuild/Project.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 89 | 90 | 91 | 92 | 93 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Dnn.WebAnalytics.dnn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dnn.WebAnalytics 5 | 6 | /Images/icon_extensions_32px.png 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Dnn.WebAnalytics 19 | Dnn.WebAnalytics 20 | 21 | 22 | Supported 23 | 24 | 25 | Dnn.WebAnalytics 26 | Dnn.WebAnalytics 27 | 0 28 | 29 | 30 | 31 | DesktopModules/Dnn.WebAnalytics/View.ascx 32 | False 33 | 34 | Anonymous 35 | 36 | 37 | False 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | DesktopModules\Dnn.WebAnalytics 47 | 48 | resources.zip 49 | 50 | 51 | 52 | 53 | 54 | bin 55 | 56 | Dnn.WebAnalytics.dll 57 | 58 | 59 | FiftyOne.Foundation.dll 60 | 61 | 62 | MaxMind.Db.dll 63 | 64 | 65 | MaxMind.GeoIP2.dll 66 | 67 | 68 | 69 | 70 | 71 | web.config 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | DesktopModules\Dnn.WebAnalytics 93 | 98 | 103 | 108 | 113 | 114 | 115 | 116 | 117 | 118 | Dnn.VisitorsOnline 119 | 120 | /Images/icon_extensions_32px.png 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | Dnn.VisitorsOnline 133 | Dnn.WebAnalytics 134 | 135 | 136 | Supported 137 | 138 | 139 | Dnn.VisitorsOnline 140 | Dnn.VisitorsOnline 141 | 0 142 | 143 | 144 | 145 | DesktopModules/Dnn.WebAnalytics/Map.ascx 146 | False 147 | 148 | Anonymous 149 | 150 | 151 | False 152 | 153 | 154 | Settings 155 | DesktopModules/Dnn.WebAnalytics/MapSettings.ascx 156 | True 157 | VisitorsOnline Settings 158 | Admin 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /app/controllers/view.js: -------------------------------------------------------------------------------- 1 | dnnWebAnalytics.controller('viewController', ['$scope', '$q', 'toastr', '$uibModal', '$filter', 'visitService', 'visitorService', function ($scope, $q, toastr, $uibModal, $filter, visitService, visitorService) { 2 | 3 | $scope.period_start; 4 | $scope.period_end; 5 | 6 | $scope.view_count = 0; 7 | $scope.visit_count = 0; 8 | $scope.visitor_count = 0; 9 | $scope.user_count = 0; 10 | 11 | $scope.chart_labels = []; 12 | $scope.chart; 13 | 14 | $scope.field = "date"; 15 | $scope.rows = "10"; 16 | 17 | $scope.periodStartDatePicker = { 18 | isOpen: false 19 | }; 20 | $scope.periodEndDatePicker = { 21 | isOpen: false 22 | }; 23 | 24 | $scope.chart_series = ['Views', 'Visits', 'Visitors', 'Users']; 25 | $scope.chart_labels = []; 26 | $scope.chart_data = []; 27 | $scope.chart_options = { 28 | scales: { 29 | yAxes: [ 30 | { 31 | id: 'y-axis-1', 32 | type: 'linear', 33 | display: true, 34 | position: 'left' 35 | } 36 | ] 37 | }, 38 | legend: { 39 | display: true, 40 | position: "right" 41 | } 42 | }; 43 | 44 | $scope.pie_chart_labels = []; 45 | $scope.pie_chart_data = []; 46 | $scope.pie_chart_options = { 47 | legend: { 48 | display: true, 49 | position: "right" 50 | } 51 | }; 52 | 53 | $scope.lastWeek = function () { 54 | $scope.period_end = new Date(); 55 | $scope.period_start = new Date(); 56 | $scope.period_start.setDate($scope.period_end.getDate() - 7); 57 | 58 | $scope.getDashboard(); 59 | $scope.getReport(); 60 | }; 61 | $scope.lastMonth = function () { 62 | $scope.period_end = new Date(); 63 | $scope.period_start = new Date(); 64 | $scope.period_start.setDate($scope.period_end.getDate() - 30); 65 | 66 | $scope.getDashboard(); 67 | $scope.getReport(); 68 | }; 69 | $scope.last3Months = function () { 70 | $scope.period_end = new Date(); 71 | $scope.period_start = new Date(); 72 | $scope.period_start.setDate($scope.period_end.getDate() - 90); 73 | 74 | $scope.getDashboard(); 75 | $scope.getReport(); 76 | }; 77 | 78 | $scope.getDashboard = function () { 79 | var deferred = $q.defer(); 80 | 81 | $scope.dashboard_loading = true; 82 | 83 | visitService.getDashboard(portal_id, $scope.period_start, $scope.period_end).then( 84 | function (response) { 85 | var dashboardDTO = response.data; 86 | 87 | console.log(dashboardDTO); 88 | 89 | $scope.view_count = dashboardDTO.view_count; 90 | $scope.visit_count = dashboardDTO.visit_count; 91 | $scope.visitor_count = dashboardDTO.visitor_count; 92 | $scope.user_count = dashboardDTO.user_count; 93 | 94 | $scope.views = dashboardDTO.views; 95 | $scope.visits = dashboardDTO.visits; 96 | $scope.visitors = dashboardDTO.visitors; 97 | $scope.users = dashboardDTO.users; 98 | 99 | var current_date = new Date($scope.period_start.getFullYear(), $scope.period_start.getMonth(), $scope.period_start.getDate()); 100 | var end_date = new Date($scope.period_end.getFullYear(), $scope.period_end.getMonth(), $scope.period_end.getDate()); 101 | var labels = []; 102 | while (current_date <= end_date) { 103 | labels.push($filter('date')(current_date, 'shortDate')); 104 | current_date.setDate(current_date.getDate() + 1); 105 | } 106 | 107 | $scope.chart_series = [ 108 | 'Views (' + $scope.view_count + ')', 109 | 'Visits (' + $scope.visit_count + ')', 110 | 'Visitors (' + $scope.visitor_count + ')', 111 | 'Users (' + $scope.user_count + ')' 112 | ]; 113 | 114 | $scope.chart_labels = labels; 115 | $scope.chart_data = [$scope.views, $scope.visits, $scope.visitors, $scope.users]; 116 | 117 | $scope.dashboard_loading = false; 118 | }, 119 | function (response) { 120 | $scope.dashboard_loading = false; 121 | console.log('getDashboard failed', response); 122 | toastr.error("There was a problem loading the dashboard", "Error"); 123 | deferred.reject(); 124 | } 125 | ); 126 | return deferred.promise; 127 | }; 128 | 129 | $scope.getReport = function () { 130 | var deferred = $q.defer(); 131 | 132 | $scope.report_loading = true; 133 | 134 | visitService.getReport($scope.field, portal_id, $scope.period_start, $scope.period_end, $scope.rows).then( 135 | function (response) { 136 | $scope.report_rows = response.data; 137 | 138 | //console.log('get report', $scope.report_rows); 139 | 140 | $scope.pie_chart_labels = []; 141 | $scope.pie_chart_data = []; 142 | 143 | for (var x = 0; x < $scope.report_rows.length; x++) { 144 | var report_row = $scope.report_rows[x]; 145 | 146 | if (report_row.field.length > 30) { 147 | $scope.pie_chart_labels.push(report_row.field.substring(0, 30) + "..."); 148 | } 149 | else { 150 | $scope.pie_chart_labels.push(report_row.field); 151 | } 152 | 153 | $scope.pie_chart_data.push(report_row.count); 154 | } 155 | 156 | $scope.report_loading = false; 157 | }, 158 | function (response) { 159 | $scope.report_loading = false; 160 | console.log('getReport failed', response); 161 | toastr.error("There was a problem loading the report", "Error"); 162 | deferred.reject(); 163 | } 164 | ); 165 | return deferred.promise; 166 | }; 167 | 168 | $scope.$on('chart-create', function (evt, chart) { 169 | //console.log('chart-create', chart); 170 | if (chart.canvas.id === 'line') { 171 | //console.log('before create', $scope.report_rows); 172 | $scope.chart = chart; 173 | $scope.chart.update(); 174 | //console.log('after create', $scope.report_rows); 175 | } 176 | }); 177 | 178 | init = function () { 179 | var promises = []; 180 | return $q.all(promises); 181 | }; 182 | init(); 183 | $scope.lastWeek(); 184 | }]); 185 | 186 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | /_Installation/ 332 | -------------------------------------------------------------------------------- /plugins/angular-toastr/angular-toastr.tpls.min.js: -------------------------------------------------------------------------------- 1 | !function () { "use strict"; function t(t, e, s, n, o, r, a) { function i() { return w.length } function l(t) { if (1 !== arguments.length || t) if (t) m(t.toastId); else for (var e = 0; e < w.length; e++) m(w[e].toastId) } function c(t, e, s) { var n = v().iconClasses.error; return f(n, t, e, s) } function u(t, e, s) { var n = v().iconClasses.info; return f(n, t, e, s) } function p(t, e, s) { var n = v().iconClasses.success; return f(n, t, e, s) } function g(t, e, s) { var n = v().iconClasses.warning; return f(n, t, e, s) } function d(t, e) { t && t.isOpened && w.indexOf(t) >= 0 && t.scope.refreshTimer(e) } function m(e, s) { function n(t) { for (var e = 0; e < w.length; e++) if (w[e].toastId === t) return w[e] } function o() { return !w.length } var i = n(e); i && !i.deleting && (i.deleting = !0, i.isOpened = !1, t.leave(i.el).then(function () { i.scope.options.onHidden && i.scope.options.onHidden(!!s, i), i.scope.$destroy(); var t = w.indexOf(i); delete x[i.scope.message], w.splice(t, 1); var e = r.maxOpened; e && w.length >= e && w[e - 1].open.resolve(), o() && (O.remove(), O = null, $ = a.defer()) })) } function f(t, e, s, n) { return angular.isObject(s) && (n = s, s = null), C({ iconClass: t, message: e, optionsOverride: n, title: s }) } function v() { return angular.extend({}, r) } function h(e) { if (O) return $.promise; O = angular.element("
"), O.attr("id", e.containerId), O.addClass(e.positionClass), O.css({ "pointer-events": "auto" }); var s = angular.element(document.querySelector(e.target)); if (!s || !s.length) throw "Target for toasts doesn't exist"; return t.enter(O, s).then(function () { $.resolve() }), $.promise } function C(s) { function r() { return g.autoDismiss && g.maxOpened && w.length > g.maxOpened } function i(t, e, s) { function n(e) { if (s[e]) return function () { s[e](t) } } s.allowHtml ? (t.scope.allowHtml = !0, t.scope.title = o.trustAsHtml(e.title), t.scope.message = o.trustAsHtml(e.message)) : (t.scope.title = e.title, t.scope.message = e.message), t.scope.toastType = t.iconClass, t.scope.toastId = t.toastId, t.scope.extraData = s.extraData, t.scope.options = { extendedTimeOut: s.extendedTimeOut, messageClass: s.messageClass, onHidden: s.onHidden, onShown: n("onShown"), onTap: n("onTap"), progressBar: s.progressBar, tapToDismiss: s.tapToDismiss, timeOut: s.timeOut, titleClass: s.titleClass, toastClass: s.toastClass }, s.closeButton && (t.scope.options.closeHtml = s.closeHtml) } function l() { function t(t) { for (var e = ["containerId", "iconClasses", "maxOpened", "newestOnTop", "positionClass", "preventDuplicates", "preventOpenDuplicates", "templates"], s = 0, n = e.length; s < n; s++) delete t[e[s]]; return t } var e = { toastId: T++, isOpened: !1, scope: n.$new(), open: a.defer() }; return e.iconClass = s.iconClass, s.optionsOverride && (angular.extend(g, t(s.optionsOverride)), e.iconClass = s.optionsOverride.iconClass || e.iconClass), i(e, s, g), e.el = c(e.scope), e } function c(t) { var s = angular.element("
"), n = e.get("$compile"); return n(s)(t) } function u() { return g.maxOpened && w.length <= g.maxOpened || !g.maxOpened } function p() { var t = g.preventDuplicates && s.message === B, e = g.preventOpenDuplicates && x[s.message]; return !(!t && !e) || (B = s.message, x[s.message] = !0, !1) } var g = v(); if (!p()) { var d = l(); if (w.push(d), r()) for (var f = w.slice(0, w.length - g.maxOpened), C = 0, $ = f.length; C < $; C++) m(f[C].toastId); return u() && d.open.resolve(), d.open.promise.then(function () { h(g).then(function () { if (d.isOpened = !0, g.newestOnTop) t.enter(d.el, O).then(function () { d.scope.init() }); else { var e = O[0].lastChild ? angular.element(O[0].lastChild) : null; t.enter(d.el, O, e).then(function () { d.scope.init() }) } }) }), d } } var O, T = 0, w = [], B = "", x = {}, $ = a.defer(), b = { active: i, clear: l, error: c, info: u, remove: m, success: p, warning: g, refreshTimer: d }; return b } angular.module("toastr", []).factory("toastr", t), t.$inject = ["$animate", "$injector", "$document", "$rootScope", "$sce", "toastrConfig", "$q"] }(), function () { "use strict"; angular.module("toastr").constant("toastrConfig", { allowHtml: !1, autoDismiss: !1, closeButton: !1, closeHtml: "", containerId: "toast-container", extendedTimeOut: 1e3, iconClasses: { error: "toast-error", info: "toast-info", success: "toast-success", warning: "toast-warning" }, maxOpened: 0, messageClass: "toast-message", newestOnTop: !0, onHidden: null, onShown: null, onTap: null, positionClass: "toast-top-right", preventDuplicates: !1, preventOpenDuplicates: !1, progressBar: !1, tapToDismiss: !0, target: "body", templates: { toast: "directives/toast/toast.html", progressbar: "directives/progressbar/progressbar.html" }, timeOut: 5e3, titleClass: "toast-title", toastClass: "toast" }) }(), function () { "use strict"; function t(t) { function e(t, e, s, n) { function o() { var t = (i - (new Date).getTime()) / a * 100; e.css("width", t + "%") } var r, a, i; n.progressBar = t, t.start = function (t) { r && clearInterval(r), a = parseFloat(t), i = (new Date).getTime() + a, r = setInterval(o, 10) }, t.stop = function () { r && clearInterval(r) }, t.$on("$destroy", function () { clearInterval(r) }) } return { require: "^toast", templateUrl: function () { return t.templates.progressbar }, link: e } } angular.module("toastr").directive("progressBar", t), t.$inject = ["toastrConfig"] }(), function () { "use strict"; function t() { this.progressBar = null, this.startProgressBar = function (t) { this.progressBar && this.progressBar.start(t) }, this.stopProgressBar = function () { this.progressBar && this.progressBar.stop() } } angular.module("toastr").controller("ToastController", t) }(), function () { "use strict"; function t(t, e, s, n) { function o(s, o, r, a) { function i(t) { return a.startProgressBar(t), e(function () { a.stopProgressBar(), n.remove(s.toastId) }, t, 1) } function l() { s.progressBar = !1, a.stopProgressBar() } function c() { return s.options.closeHtml } var u; if (s.toastClass = s.options.toastClass, s.titleClass = s.options.titleClass, s.messageClass = s.options.messageClass, s.progressBar = s.options.progressBar, c()) { var p = angular.element(s.options.closeHtml), g = t.get("$compile"); p.addClass("toast-close-button"), p.attr("ng-click", "close(true, $event)"), g(p)(s), o.children().prepend(p) } s.init = function () { s.options.timeOut && (u = i(s.options.timeOut)), s.options.onShown && s.options.onShown() }, o.on("mouseenter", function () { l(), u && e.cancel(u) }), s.tapToast = function () { angular.isFunction(s.options.onTap) && s.options.onTap(), s.options.tapToDismiss && s.close(!0) }, s.close = function (t, e) { e && angular.isFunction(e.stopPropagation) && e.stopPropagation(), n.remove(s.toastId, t) }, s.refreshTimer = function (t) { u && (e.cancel(u), u = i(t || s.options.timeOut)) }, o.on("mouseleave", function () { 0 === s.options.timeOut && 0 === s.options.extendedTimeOut || (s.$apply(function () { s.progressBar = s.options.progressBar }), u = i(s.options.extendedTimeOut)) }) } return { templateUrl: function () { return s.templates.toast }, controller: "ToastController", link: o } } angular.module("toastr").directive("toast", t), t.$inject = ["$injector", "$interval", "toastrConfig", "toastr"] }(), angular.module("toastr").run(["$templateCache", function (t) { t.put("directives/progressbar/progressbar.html", '
\n'), t.put("directives/toast/toast.html", '
\n
\n
{{title}}
\n
{{message}}
\n
\n
\n
\n \n
\n') }]); 2 | 3 | -------------------------------------------------------------------------------- /plugins/angular-toastr/angular-toastr.min.css: -------------------------------------------------------------------------------- 1 | .toast-title { 2 | font-weight: 700 3 | } 4 | 5 | .toast-message { 6 | word-wrap: break-word 7 | } 8 | 9 | .toast-message a, .toast-message label { 10 | color: #fff 11 | } 12 | 13 | .toast-message a:hover { 14 | color: #ccc; 15 | text-decoration: none 16 | } 17 | 18 | .toast-close-button { 19 | position: relative; 20 | right: -.3em; 21 | top: -.3em; 22 | float: right; 23 | font-size: 20px; 24 | font-weight: 700; 25 | color: #fff; 26 | -webkit-text-shadow: 0 1px 0 #fff; 27 | text-shadow: 0 1px 0 #fff; 28 | opacity: .8 29 | } 30 | 31 | .toast-close-button:focus, .toast-close-button:hover { 32 | color: #000; 33 | text-decoration: none; 34 | cursor: pointer; 35 | opacity: .4 36 | } 37 | 38 | button.toast-close-button { 39 | padding: 0; 40 | cursor: pointer; 41 | background: transparent; 42 | border: 0; 43 | -webkit-appearance: none 44 | } 45 | 46 | .toast-top-center { 47 | top: 0; 48 | right: 0; 49 | width: 100% 50 | } 51 | 52 | .toast-bottom-center { 53 | bottom: 0; 54 | right: 0; 55 | width: 100% 56 | } 57 | 58 | .toast-top-full-width { 59 | top: 0; 60 | right: 0; 61 | width: 100% 62 | } 63 | 64 | .toast-bottom-full-width { 65 | bottom: 0; 66 | right: 0; 67 | width: 100% 68 | } 69 | 70 | .toast-top-left { 71 | top: 12px; 72 | left: 12px 73 | } 74 | 75 | .toast-top-right { 76 | top: 12px; 77 | right: 12px 78 | } 79 | 80 | .toast-bottom-right { 81 | right: 12px; 82 | bottom: 12px 83 | } 84 | 85 | .toast-bottom-left { 86 | bottom: 12px; 87 | left: 12px 88 | } 89 | 90 | #toast-container { 91 | position: fixed; 92 | z-index: 999999 93 | } 94 | 95 | #toast-container * { 96 | box-sizing: border-box 97 | } 98 | 99 | #toast-container .toastr { 100 | position: relative; 101 | overflow: hidden; 102 | margin: 0 0 6px; 103 | padding: 15px 15px 15px 50px; 104 | width: 300px; 105 | border-radius: 3px 3px 3px 3px; 106 | background-position: 15px; 107 | background-repeat: no-repeat; 108 | box-shadow: 0 0 12px #999; 109 | color: #fff; 110 | opacity: .8 111 | } 112 | 113 | #toast-container .toast:hover { 114 | box-shadow: 0 0 12px #000; 115 | opacity: 1; 116 | cursor: pointer 117 | } 118 | 119 | #toast-container .toast.toast-info { 120 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important 121 | } 122 | 123 | #toast-container .toast.toast-error { 124 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important 125 | } 126 | 127 | #toast-container .toast.toast-success { 128 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important 129 | } 130 | 131 | #toast-container .toast.toast-warning { 132 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important 133 | } 134 | 135 | #toast-container.toast-bottom-center .toast, #toast-container.toast-top-center .toastr { 136 | width: 300px; 137 | margin-left: auto; 138 | margin-right: auto 139 | } 140 | 141 | #toast-container.toast-bottom-full-width .toast, #toast-container.toast-top-full-width .toastr { 142 | width: 96%; 143 | margin-left: auto; 144 | margin-right: auto 145 | } 146 | 147 | .toastr { 148 | background-color: #030303 149 | } 150 | 151 | .toast-success { 152 | background-color: #51a351 153 | } 154 | 155 | .toast-error { 156 | background-color: #bd362f 157 | } 158 | 159 | .toast-info { 160 | background-color: #2f96b4 161 | } 162 | 163 | .toast-warning { 164 | background-color: #f89406 165 | } 166 | 167 | progress-bar { 168 | position: absolute; 169 | left: 0; 170 | bottom: 0; 171 | height: 4px; 172 | background-color: #000; 173 | opacity: .4 174 | } 175 | 176 | div[toast] { 177 | opacity: 1 !important 178 | } 179 | 180 | div[toast].ng-enter { 181 | opacity: 0 !important; 182 | transition: opacity .3s linear 183 | } 184 | 185 | div[toast].ng-enter.ng-enter-active { 186 | opacity: 1 !important 187 | } 188 | 189 | div[toast].ng-leave { 190 | opacity: 1; 191 | transition: opacity .3s linear 192 | } 193 | 194 | div[toast].ng-leave.ng-leave-active { 195 | opacity: 0 !important 196 | } 197 | 198 | @media all and (max-width:240px) { 199 | #toast-container .toast.div { 200 | padding: 8px 8px 8px 50px; 201 | width: 11em 202 | } 203 | 204 | #toast-container .toast-close-button { 205 | right: -.2em; 206 | top: -.2em 207 | } 208 | } 209 | 210 | @media all and (min-width:241px) and (max-width:480px) { 211 | #toast-container .toast.div { 212 | padding: 8px 8px 8px 50px; 213 | width: 18em 214 | } 215 | 216 | #toast-container .toast-close-button { 217 | right: -.2em; 218 | top: -.2em 219 | } 220 | } 221 | 222 | @media all and (min-width:481px) and (max-width:768px) { 223 | #toast-container .toast.div { 224 | padding: 15px 15px 15px 50px; 225 | width: 25em 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /Components/VisitorTracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | using DotNetNuke.Entities.Portals; 4 | using DotNetNuke.Entities.Users; 5 | using System.Linq; 6 | using DotNetNuke.Services.Exceptions; 7 | 8 | namespace Dnn.WebAnalytics 9 | { 10 | public class VisitorTracker : IHttpModule 11 | { 12 | private System.Text.RegularExpressions.Regex UserAgentFilter = new System.Text.RegularExpressions.Regex(VisitController.UserAgentFilter, System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase); 13 | VisitController visitController = new VisitController(); 14 | VisitorController visitorController = new VisitorController(); 15 | DataContext dc = new DataContext(); 16 | 17 | public string ModuleName 18 | { 19 | get { return "VisitorTracker"; } 20 | } 21 | 22 | public void Init(HttpApplication application) 23 | { 24 | application.EndRequest += this.OnEndRequest; 25 | } 26 | 27 | public void OnEndRequest(object s, EventArgs e) 28 | { 29 | try 30 | { 31 | HttpContext Context = ((HttpApplication)s).Context; 32 | HttpRequest Request = Context.Request; 33 | HttpResponse Response = Context.Response; 34 | 35 | HttpCookie cookie_visitor = null; 36 | HttpCookie cookie_session = null; 37 | HttpCookie cookie_request = null; 38 | 39 | int visitor_id = 0; 40 | Nullable user_id = null; 41 | Guid session_id = Guid.Empty; 42 | Guid request_id = Guid.Empty; 43 | Guid last_request_id = Guid.Empty; 44 | 45 | PortalSettings _portalSettings = (PortalSettings)Context.Items["PortalSettings"]; 46 | 47 | // get/set cookie if visitor tracking is enabled 48 | cookie_visitor = Request.Cookies["DNNVISITOR"]; 49 | if (cookie_visitor != null) 50 | { 51 | visitor_id = Convert.ToInt32(cookie_visitor.Value); 52 | } 53 | 54 | // update/create visitor 55 | var visitor = dc.Community_Visitors.Where(i => i.id == visitor_id).SingleOrDefault(); 56 | if (visitor == null) 57 | { // create Visitor record 58 | visitor = new Community_Visitor() 59 | { 60 | created_on_date = DateTime.Now 61 | }; 62 | dc.Community_Visitors.InsertOnSubmit(visitor); 63 | } 64 | 65 | // get User if authenticated 66 | if (Request.IsAuthenticated) 67 | { 68 | UserInfo user = UserController.Instance.GetCurrentUserInfo(); 69 | if (user != null) 70 | { 71 | user_id = user.UserID; 72 | } 73 | } 74 | 75 | // update the user_id if not set yet 76 | if (!visitor.user_id.HasValue && user_id.GetValueOrDefault() > 0) 77 | { 78 | visitor.user_id = user_id; 79 | } 80 | dc.SubmitChanges(); 81 | 82 | // only process requests for content pages 83 | if (_portalSettings != null && Request.Url.LocalPath.ToLower().EndsWith("default.aspx")) 84 | { 85 | // filter web crawlers and other bots 86 | if (String.IsNullOrEmpty(Request.UserAgent) == false && UserAgentFilter.Match(Request.UserAgent).Success == false) 87 | { 88 | // get last request cookie value 89 | cookie_request = Request.Cookies["DNNREQUEST"]; 90 | if (cookie_request != null) 91 | { 92 | last_request_id = new Guid(cookie_request.Value); 93 | } 94 | 95 | // create new request cookie 96 | request_id = Guid.NewGuid(); 97 | cookie_request = new HttpCookie("DNNREQUEST"); 98 | cookie_request.Value = request_id.ToString(); 99 | Response.Cookies.Add(cookie_request); 100 | 101 | // get last session cookie value 102 | cookie_session = Request.Cookies["DNNSESSION"]; 103 | if (cookie_session != null) 104 | { 105 | session_id = new Guid(cookie_session.Value); 106 | } 107 | else 108 | { 109 | // create a new session id 110 | session_id = Guid.NewGuid(); 111 | cookie_session = new HttpCookie("DNNSESSION"); 112 | cookie_session.Value = session_id.ToString(); 113 | cookie_session.Expires = DateTime.Now.AddMinutes(30); 114 | Response.Cookies.Add(cookie_session); 115 | } 116 | 117 | // campaign 118 | string campaign = string.Empty; 119 | if (Request.QueryString["campaign"] != null) 120 | { 121 | campaign = Request.QueryString["campaign"]; 122 | } 123 | 124 | 125 | 126 | // create Visitor cookie 127 | cookie_visitor = new HttpCookie("DNNVISITOR"); 128 | cookie_visitor.Value = visitor.id.ToString(); 129 | cookie_visitor.Expires = DateTime.MaxValue; 130 | Response.Cookies.Add(cookie_visitor); 131 | 132 | string domain = Request.Url.Host + Request.ApplicationPath; 133 | if (domain.EndsWith("/")) 134 | { 135 | domain = domain.Substring(0, domain.Length - 1); 136 | } 137 | 138 | // get referrer URL 139 | string url_referrer = string.Empty; 140 | if (Request.UrlReferrer != null) 141 | { 142 | url_referrer = Request.UrlReferrer.ToString(); 143 | } 144 | 145 | string domain_referrer = string.Empty; 146 | if (!string.IsNullOrEmpty(url_referrer)) 147 | { 148 | Uri Uri = new Uri(url_referrer); 149 | domain_referrer = Uri.Host; 150 | } 151 | 152 | // get browser language 153 | string language = string.Empty; 154 | if (Request.UserLanguages != null) 155 | { 156 | if (Request.UserLanguages.Length != 0) 157 | { 158 | language = Request.UserLanguages[0].ToLowerInvariant().Trim(); 159 | } 160 | } 161 | 162 | 163 | // ip address 164 | string ip = Request.UserHostAddress; 165 | 166 | // url 167 | string url = Request.RawUrl; 168 | 169 | //user agenet 170 | string user_agent = Request.UserAgent; 171 | 172 | // create visit object 173 | VisitDTO visitDTO = new VisitDTO() 174 | { 175 | date = DateTime.Now, 176 | visitor_id = visitor.id, 177 | tab_id = _portalSettings.ActiveTab.TabID, 178 | ip = ip, 179 | country = "", 180 | region = "", 181 | city = "", 182 | latitude = "", 183 | longitude = "", 184 | language = language, 185 | domain = domain, 186 | url = url, 187 | user_agent = user_agent, 188 | device_type = "Desktop", 189 | device = "", 190 | platform = "", 191 | browser = "", 192 | referrer_domain = domain_referrer, 193 | referrer_url = url_referrer, 194 | server = "", 195 | activity = "click", 196 | campaign = campaign, 197 | session_id = session_id, 198 | request_id = request_id, 199 | last_request_id = last_request_id 200 | }; 201 | 202 | visitDTO = visitController.ProcessVisit(visitDTO); 203 | 204 | Community_Visit visit = visitController.ConvertDtoToItem(null, visitDTO); 205 | 206 | dc.Community_Visits.InsertOnSubmit(visit); 207 | dc.SubmitChanges(); 208 | } 209 | } 210 | } 211 | catch (Exception ex) 212 | { 213 | Exceptions.LogException(ex); 214 | } 215 | } 216 | 217 | public void Dispose() 218 | { 219 | } 220 | } 221 | } -------------------------------------------------------------------------------- /Dnn.WebAnalytics.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {20304B35-C633-42D4-B745-34B617FA38F4} 8 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 9 | Library 10 | Properties 11 | Dnn.WebAnalytics 12 | Dnn.WebAnalytics 13 | v4.7.2 14 | 512 15 | 16 | 17 | 14.0 18 | 19 | false 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | full 33 | false 34 | ..\..\bin\ 35 | DEBUG;TRACE 36 | prompt 37 | 4 38 | 39 | 40 | pdbonly 41 | true 42 | ..\..\bin\ 43 | TRACE 44 | prompt 45 | 4 46 | 47 | 48 | 49 | packages\DotNetNuke.Core.8.0.0.809\lib\net40\DotNetNuke.dll 50 | False 51 | False 52 | 53 | 54 | packages\DotNetNuke.Web.8.0.0.809\lib\net40\DotNetNuke.Web.dll 55 | False 56 | False 57 | 58 | 59 | packages\DotNetNuke.Web.Client.8.0.0\lib\net40\DotNetNuke.Web.Client.dll 60 | False 61 | False 62 | 63 | 64 | packages\DotNetNuke.Web.8.0.0.809\lib\net40\DotNetNuke.WebUtility.dll 65 | False 66 | False 67 | 68 | 69 | False 70 | _LegacyReferences\FiftyOne.Foundation.dll 71 | True 72 | 73 | 74 | False 75 | packages\MaxMind.Db.2.4.0\lib\net45\MaxMind.Db.dll 76 | True 77 | 78 | 79 | packages\MaxMind.GeoIP2.3.0.0\lib\net45\MaxMind.GeoIP2.dll 80 | False 81 | True 82 | 83 | 84 | packages\DotNetNuke.Core.8.0.0.809\lib\net40\Microsoft.ApplicationBlocks.Data.dll 85 | False 86 | False 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | False 102 | ..\..\bin\System.Web.Http.dll 103 | False 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Map.ascx 115 | ASPXCodeBehind 116 | 117 | 118 | MapSettings.ascx 119 | ASPXCodeBehind 120 | 121 | 122 | View.ascx 123 | ASPXCodeBehind 124 | 125 | 126 | View.ascx 127 | 128 | 129 | 130 | 131 | 132 | ASPXCodeBehind 133 | 134 | 135 | 136 | 137 | 138 | True 139 | True 140 | WebAnalytics.dbml 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | MSLinqToSQLGenerator 178 | WebAnalytics.designer.cs 179 | Designer 180 | 181 | 182 | WebAnalytics.dbml 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | True 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 209 | 210 | 211 | 212 | 219 | -------------------------------------------------------------------------------- /plugins/datetime-picker/datetime-picker.min.js: -------------------------------------------------------------------------------- 1 | // https://github.com/Gillardo/bootstrap-ui-datetime-picker 2 | // Version: 2.6.3 3 | // Released: 2018-04-25 4 | angular.module("ui.bootstrap.datetimepicker",["ui.bootstrap.dateparser","ui.bootstrap.position"]).constant("uiDatetimePickerConfig",{dateFormat:"yyyy-MM-dd HH:mm",defaultTime:"00:00:00",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},initialPicker:"date",reOpenDefault:!1,disableFocusStealing:!1,enableDate:!0,enableTime:!0,buttonBar:{show:!0,now:{show:!0,text:"Now",cls:"btn-sm btn-secondary"},today:{show:!0,text:"Today",cls:"btn-sm btn-secondary"},clear:{show:!0,text:"Clear",cls:"btn-sm btn-secondary"},date:{show:!0,text:"Date",cls:"btn-sm btn-secondary"},time:{show:!0,text:"Time",cls:"btn-sm btn-secondary"},close:{show:!0,text:"Close",cls:"btn-sm btn-secondary"},cancel:{show:!1,text:"Cancel",cls:"btn-sm btn-secondary"}},closeOnDateSelection:!0,closeOnTimeNow:!0,appendToBody:!1,altInputFormats:[],ngModelOptions:{timezone:null},saveAs:!1,readAs:!1}).controller("DateTimePickerController",["$scope","$element","$attrs","$compile","$parse","$document","$timeout","$uibPosition","dateFilter","uibDateParser","uiDatetimePickerConfig","$rootScope",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(a){var b;return angular.version.minor<6?(b=angular.isObject(a.$options)?a.$options:{timezone:null},b.getOption=function(a){return b[a]}):b=a.$options,b}function n(c){var d=w[0],e=b[0].contains(c.target),f=void 0!==d.contains&&d.contains(c.target);!a.isOpen||e||f||a.$apply(function(){a.close(!1)})}function o(c){27===c.which&&a.isOpen?(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.close(!1)}),b[0].focus()):40!==c.which||a.isOpen||(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!0}))}function p(a){return a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()})}function q(b){var c=j.parse(b,x,a.date);if(isNaN(c))for(var d=0;da.datepickerOptions.maxDate)}function t(a,b){var d=a||b;return c.ngRequired||c.required||d?(angular.isNumber(d)&&(d=new Date(d)),d?angular.isDate(d)&&!isNaN(d)?s(d):angular.isDate(new Date(d))&&!isNaN(new Date(d).valueOf())?s(new Date(d)):angular.isString(d)?!isNaN(q(b))&&s(q(b)):!1:!0):!0}var u,v,w,x=k.dateFormat,y={},z=[],A=angular.isDefined(c.closeOnDateSelection)?a.$parent.$eval(c.closeOnDateSelection):k.closeOnDateSelection,B=angular.isDefined(c.closeOnTimeNow)?a.$parent.$eval(c.closeOnTimeNow):k.closeOnTimeNow,C=angular.isDefined(c.datepickerAppendToBody)?a.$parent.$eval(c.datepickerAppendToBody):k.appendToBody,D=angular.isDefined(c.altInputFormats)?a.$parent.$eval(c.altInputFormats):k.altInputFormats,E=angular.isDefined(c.saveAs)?a.$parent.$eval(c.saveAs)||c.saveAs:k.saveAs,F=angular.isDefined(c.readAs)?a.$parent.$eval(c.readAs):k.readAs,G=null;this.init=function(e){function g(a){if(u.$isEmpty(a))return a;var b=new Date(a);return angular.isDate(b)&&!isNaN(b)?b:a}function h(a){return!a||angular.isString(a)||!angular.isDate(a)||isNaN(a)?a:"ISO"===E?a.toISOString():"json"===E?a.toJSON():"number"===E?a.valueOf():i?j.fromTimezone(a,v.getOption("timezone")).toLocaleString():(x=x.replace(/M!/,"MM").replace(/d!/,"dd"),j.filter(j.fromTimezone(a,v.getOption("timezone")),x))}if(u=e,v=m(u),a.buttonBar=angular.isDefined(c.buttonBar)?a.$parent.$eval(c.buttonBar):k.buttonBar,a.enableDate=angular.isDefined(a.enableDate)?a.enableDate:k.enableDate,a.enableTime=angular.isDefined(a.enableTime)?a.enableTime:k.enableTime,a.initialPicker=angular.isDefined(c.initialPicker)?c.initialPicker:a.enableDate?k.initialPicker:"time",a.reOpenDefault=angular.isDefined(c.reOpenDefault)?c.reOpenDefault:k.reOpenDefault,a.disableFocusStealing=angular.isDefined(c.disableFocusStealing)?c.disableFocusStealing:k.disableFocusStealing,"date"===a.initialPicker&&!a.enableDate)throw new Error("datetimePicker can't have initialPicker set to date and have enableDate set to false.");a.showPicker=a.enableDate?a.initialPicker:"time";var i=!1;if(k.html5Types[c.type]?(x=k.html5Types[c.type],i=!0):(x=c.datetimePicker||k.dateFormat,c.$observe("datetimePicker",function(a){var b=a||k.dateFormat;if(b!==x&&(x=b,u.$modelValue=null,!x))throw new Error("datetimePicker must have a date format specified.")})),!x)throw new Error("datetimePicker must have a date format specified.");var l=angular.element('
');l.attr({"ng-model":"date","ng-change":"dateSelection(date)"});var n=angular.element(l.children()[0]);a.datepickerOptions||(a.datepickerOptions={}),i&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),n.attr("datepicker-options","datepickerOptions"),angular.isDefined(a.datepickerOptions.datepickerMode)||(a.datepickerOptions.datepickerMode="day");var s=angular.element(l.children()[1]);a.timepickerOptions||(a.timepickerOptions={showMeridian:!0});for(var y in a.timepickerOptions)s.attr(p(y),"timepickerOptions."+y);angular.forEach(["minDate","maxDate","initDate"],function(b){a.datepickerOptions[b]&&("minDate"===b?a.timepickerOptions.min?s.attr("min","timepickerOptions.min"):s.attr("min","datepickerOptions.minDate"):"maxDate"===b&&(a.timepickerOptions.max?s.attr("max","timepickerOptions.max"):s.attr("max","datepickerOptions.maxDate")))}),i?u.$formatters.push(function(b){return a.date=j.fromTimezone(b,v.getOption("timezone")),b}):(u.$$parserName="datetime",u.$validators.datetime=t,u.$parsers.unshift(r),u.$formatters.push(function(b){return u.$isEmpty(b)?(a.date=b,b):(a.date=j.fromTimezone(b,v.getOption("timezone")),x=x.replace(/M!/,"MM").replace(/d!/,"dd"),j.filter(a.date,x))})),E&&(angular.isFunction(E)?u.$parsers.push(E):u.$parsers.push(h),angular.isFunction(F)?u.$formatters.push(F):u.$formatters.push(g)),u.$viewChangeListeners.push(function(){if(a.timepickerOptions.min){var b=new Date(a.timepickerOptions.min).getHours(),c=new Date(a.timepickerOptions.min).getMinutes(),d=new Date(a.date);d.setHours(b),d.setMinutes(c),a.timepickerOptions.min=d}if(a.timepickerOptions.max){var e=new Date(a.timepickerOptions.max).getHours(),f=new Date(a.timepickerOptions.max).getMinutes(),g=new Date(a.date);g.setHours(e),g.setMinutes(f),a.timepickerOptions.max=g}a.date=q(u.$viewValue)}),b.bind("keydown",o),w=d(l)(a),l.remove(),C?f.find("body").append(w):b.after(w)},a.getText=function(b){return a.buttonBar[b].text||k.buttonBar[b].text},a.getClass=function(b){return a.buttonBar[b].cls||k.buttonBar[b].cls},a.keydown=function(c){27===c.which&&(c.preventDefault(),c.stopPropagation(),a.close(!1),g(function(){b[0].focus()},0))},a.doShow=function(b){return angular.isDefined(a.buttonBar[b].show)?a.buttonBar[b].show:k.buttonBar[b].show},a.dateSelection=function(d,e){if(a.enableTime&&"time"===a.showPicker)if(d||null!=d){if(angular.isDefined(a.date)&&null!=a.date||(a.date=new Date),d&&null!=d){var f=new Date(a.date);f.setHours(d.getHours()),f.setMinutes(d.getMinutes()),f.setSeconds(d.getSeconds()),f.setMilliseconds(d.getMilliseconds()),d=f}}else a.oldDate=a.date;if(angular.isDefined(d)){if(!a.date){var g=angular.isDefined(c.defaultTime)?c.defaultTime:k.defaultTime,h=new Date("2001/01/01 "+g);isNaN(h)||null==d||(d.setHours(h.getHours()),d.setMinutes(h.getMinutes()),d.setSeconds(h.getSeconds()),d.setMilliseconds(h.getMilliseconds()))}a.date=d,d&&a.oldDate&&(d.setDate(a.oldDate.getDate()),d.setMonth(a.oldDate.getMonth()),d.setFullYear(a.oldDate.getFullYear()),delete a.oldDate)}var j=a.date?i(a.date,x):null;b.val(j),u.$setViewValue(j),A&&("time"!==a.showPicker&&null!=j?a.enableTime?a.open("time"):a.close(!1):B&&"time"===a.showPicker&&null!=j&&"now"===e&&a.close(!1))},a.$watch("isOpen",function(c){if(a.dropdownStyle={display:c?"block":"none"},c){y.openDate=a.date;var d=C?h.offset(b):h.position(b);C?a.dropdownStyle.top=d.top+b.prop("offsetHeight")+"px":a.dropdownStyle.top=void 0,a.dropdownStyle.left=d.left+"px",g(function(){a.disableFocusStealing||a.$broadcast("uib:datepicker.focus"),f.bind("click",n)},0,!1),a.open(a.showPicker)}else f.unbind("click",n)}),a.isDisabled=function(b){("today"===b||"now"===b)&&(b=j.fromTimezone(new Date,v.getOption("timezone")));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=j.fromTimezone(new Date(a.datepickerOptions[b]),v.getOption("timezone")):c[b]=new Date(i(a.datepickerOptions[b],"medium")):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.select=function(b,c){angular.isDefined(c)&&(c.preventDefault(),c.stopPropagation());var d=null;if("today"===b||"now"===b){var e=new Date;angular.isDate(a.date)?(d=new Date(a.date),d.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),d.setHours(e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds())):d=e}a.dateSelection(d,b)},a.cancel=function(c){angular.isDefined(c)&&(c.preventDefault(),c.stopPropagation()),b.val(i(G,x)),u.$setViewValue(i(G,x)),a.close(!1)},a.open=function(c,d){angular.isDefined(d)&&(d.preventDefault(),d.stopPropagation()),G=b.val(),g(function(){a.showPicker=c},0),"time"===c&&g(function(){a.date=q(u.$viewValue)},50)},a.close=function(c,d){angular.isDefined(d)&&(d.preventDefault(),d.stopPropagation()),a.isOpen=!1,a.enableDate&&a.enableTime&&(a.showPicker=a.reOpenDefault===!1?"date":a.reOpenDefault),"blur"===v.getOption("updateOn")&&(b[0].focus(),g(function(){b[0].blur()},50)),angular.isDefined(c)?a.whenClosed({args:{closePressed:c,openDate:y.openDate||null,closeDate:a.date}}):b[0].focus()},a.$on("$destroy",function(){a.isOpen===!0&&(l.$$phase||a.$apply(function(){a.close()})),z.forEach(function(a){a()}),w.remove(),b.unbind("keydown",o),f.unbind("click",n)})}]).directive("datetimePicker",function(){return{restrict:"A",require:["ngModel","datetimePicker"],controller:"DateTimePickerController",scope:{isOpen:"=?",datepickerOptions:"=?",timepickerOptions:"=?",enableDate:"=?",enableTime:"=?",initialPicker:"=?",reOpenDefault:"=?",whenClosed:"&"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("datePickerWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/date-picker.html"}}).directive("timePickerWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/time-picker.html"}}),angular.module("ui.bootstrap.datetimepicker").run(["$templateCache",function(a){"use strict";a.put("template/date-picker.html","
"),a.put("template/time-picker.html","
")}]),"object"==typeof exports&&"object"==typeof module&&(module.exports="ui.bootstrap.datetimepicker"); 5 | -------------------------------------------------------------------------------- /DAL/WebAnalytics.dbml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 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 | 110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
142 |
-------------------------------------------------------------------------------- /plugins/angular-chart/angular-chart.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-chart.js - An angular.js wrapper for Chart.js 3 | * http://jtblin.github.io/angular-chart.js/ 4 | * Version: 1.1.1 5 | * 6 | * Copyright 2016 Jerome Touffe-Blin 7 | * Released under the BSD-2-Clause license 8 | * https://github.com/jtblin/angular-chart.js/blob/master/LICENSE 9 | */ 10 | (function (factory) { 11 | 'use strict'; 12 | if (typeof exports === 'object') { 13 | // Node/CommonJS 14 | module.exports = factory( 15 | typeof angular !== 'undefined' ? angular : require('angular'), 16 | typeof Chart !== 'undefined' ? Chart : require('chart.js')); 17 | } else if (typeof define === 'function' && define.amd) { 18 | // AMD. Register as an anonymous module. 19 | define(['angular', 'chart'], factory); 20 | } else { 21 | // Browser globals 22 | if (typeof angular === 'undefined') { 23 | throw new Error('AngularJS framework needs to be included, see https://angularjs.org/'); 24 | } else if (typeof Chart === 'undefined') { 25 | throw new Error('Chart.js library needs to be included, see http://jtblin.github.io/angular-chart.js/'); 26 | } 27 | factory(angular, Chart); 28 | } 29 | }(function (angular, Chart) { 30 | 'use strict'; 31 | 32 | Chart.defaults.global.multiTooltipTemplate = '<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>'; 33 | Chart.defaults.global.tooltips.mode = 'label'; 34 | Chart.defaults.global.elements.line.borderWidth = 2; 35 | Chart.defaults.global.elements.rectangle.borderWidth = 2; 36 | Chart.defaults.global.legend.display = false; 37 | Chart.defaults.global.colors = [ 38 | '#97BBCD', // blue 39 | '#DCDCDC', // light grey 40 | '#F7464A', // red 41 | '#46BFBD', // green 42 | '#FDB45C', // yellow 43 | '#949FB1', // grey 44 | '#4D5360' // dark grey 45 | ]; 46 | 47 | var useExcanvas = typeof window.G_vmlCanvasManager === 'object' && 48 | window.G_vmlCanvasManager !== null && 49 | typeof window.G_vmlCanvasManager.initElement === 'function'; 50 | 51 | if (useExcanvas) Chart.defaults.global.animation = false; 52 | 53 | return angular.module('chart.js', []) 54 | .provider('ChartJs', ChartJsProvider) 55 | .factory('ChartJsFactory', ['ChartJs', '$timeout', ChartJsFactory]) 56 | .directive('chartBase', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory(); }]) 57 | .directive('chartLine', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('line'); }]) 58 | .directive('chartBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('bar'); }]) 59 | .directive('chartHorizontalBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('horizontalBar'); }]) 60 | .directive('chartRadar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('radar'); }]) 61 | .directive('chartDoughnut', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('doughnut'); }]) 62 | .directive('chartPie', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('pie'); }]) 63 | .directive('chartPolarArea', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('polarArea'); }]) 64 | .directive('chartBubble', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('bubble'); }]) 65 | .name; 66 | 67 | /** 68 | * Wrapper for chart.js 69 | * Allows configuring chart js using the provider 70 | * 71 | * angular.module('myModule', ['chart.js']).config(function(ChartJsProvider) { 72 | * ChartJsProvider.setOptions({ responsive: false }); 73 | * ChartJsProvider.setOptions('Line', { responsive: true }); 74 | * }))) 75 | */ 76 | function ChartJsProvider () { 77 | var options = { responsive: true }; 78 | var ChartJs = { 79 | Chart: Chart, 80 | getOptions: function (type) { 81 | var typeOptions = type && options[type] || {}; 82 | return angular.extend({}, options, typeOptions); 83 | } 84 | }; 85 | 86 | /** 87 | * Allow to set global options during configuration 88 | */ 89 | this.setOptions = function (type, customOptions) { 90 | // If no type was specified set option for the global object 91 | if (! customOptions) { 92 | customOptions = type; 93 | options = angular.merge(options, customOptions); 94 | } else { 95 | // Set options for the specific chart 96 | options[type] = angular.merge(options[type] || {}, customOptions); 97 | } 98 | 99 | angular.merge(ChartJs.Chart.defaults, options); 100 | }; 101 | 102 | this.$get = function () { 103 | return ChartJs; 104 | }; 105 | } 106 | 107 | function ChartJsFactory (ChartJs, $timeout) { 108 | return function chart (type) { 109 | return { 110 | restrict: 'CA', 111 | scope: { 112 | chartGetColor: '=?', 113 | chartType: '=', 114 | chartData: '=?', 115 | chartLabels: '=?', 116 | chartOptions: '=?', 117 | chartSeries: '=?', 118 | chartColors: '=?', 119 | chartClick: '=?', 120 | chartHover: '=?', 121 | chartDatasetOverride: '=?' 122 | }, 123 | link: function (scope, elem/*, attrs */) { 124 | if (useExcanvas) window.G_vmlCanvasManager.initElement(elem[0]); 125 | 126 | // Order of setting "watch" matter 127 | scope.$watch('chartData', watchData, true); 128 | scope.$watch('chartSeries', watchOther, true); 129 | scope.$watch('chartLabels', watchOther, true); 130 | scope.$watch('chartOptions', watchOther, true); 131 | scope.$watch('chartColors', watchOther, true); 132 | scope.$watch('chartDatasetOverride', watchOther, true); 133 | scope.$watch('chartType', watchType, false); 134 | 135 | scope.$on('$destroy', function () { 136 | destroyChart(scope); 137 | }); 138 | 139 | scope.$on('$resize', function () { 140 | if (scope.chart) scope.chart.resize(); 141 | }); 142 | 143 | function watchData (newVal, oldVal) { 144 | if (! newVal || ! newVal.length || (Array.isArray(newVal[0]) && ! newVal[0].length)) { 145 | destroyChart(scope); 146 | return; 147 | } 148 | var chartType = type || scope.chartType; 149 | if (! chartType) return; 150 | 151 | if (scope.chart && canUpdateChart(newVal, oldVal)) 152 | return updateChart(newVal, scope); 153 | 154 | createChart(chartType, scope, elem); 155 | } 156 | 157 | function watchOther (newVal, oldVal) { 158 | if (isEmpty(newVal)) return; 159 | if (angular.equals(newVal, oldVal)) return; 160 | var chartType = type || scope.chartType; 161 | if (! chartType) return; 162 | 163 | // chart.update() doesn't work for series and labels 164 | // so we have to re-create the chart entirely 165 | createChart(chartType, scope, elem); 166 | } 167 | 168 | function watchType (newVal, oldVal) { 169 | if (isEmpty(newVal)) return; 170 | if (angular.equals(newVal, oldVal)) return; 171 | createChart(newVal, scope, elem); 172 | } 173 | } 174 | }; 175 | }; 176 | 177 | function createChart (type, scope, elem) { 178 | var options = getChartOptions(type, scope); 179 | if (! hasData(scope) || ! canDisplay(type, scope, elem, options)) return; 180 | 181 | var cvs = elem[0]; 182 | var ctx = cvs.getContext('2d'); 183 | 184 | scope.chartGetColor = getChartColorFn(scope); 185 | var data = getChartData(type, scope); 186 | // Destroy old chart if it exists to avoid ghost charts issue 187 | // https://github.com/jtblin/angular-chart.js/issues/187 188 | destroyChart(scope); 189 | 190 | scope.chart = new ChartJs.Chart(ctx, { 191 | type: type, 192 | data: data, 193 | options: options 194 | }); 195 | scope.$emit('chart-create', scope.chart); 196 | bindEvents(cvs, scope); 197 | } 198 | 199 | function canUpdateChart (newVal, oldVal) { 200 | if (newVal && oldVal && newVal.length && oldVal.length) { 201 | return Array.isArray(newVal[0]) ? 202 | newVal.length === oldVal.length && newVal.every(function (element, index) { 203 | return element.length === oldVal[index].length; }) : 204 | oldVal.reduce(sum, 0) > 0 ? newVal.length === oldVal.length : false; 205 | } 206 | return false; 207 | } 208 | 209 | function sum (carry, val) { 210 | return carry + val; 211 | } 212 | 213 | function getEventHandler (scope, action, triggerOnlyOnChange) { 214 | var lastState = { 215 | point: void 0, 216 | points: void 0 217 | }; 218 | return function (evt) { 219 | var atEvent = scope.chart.getElementAtEvent || scope.chart.getPointAtEvent; 220 | var atEvents = scope.chart.getElementsAtEvent || scope.chart.getPointsAtEvent; 221 | if (atEvents) { 222 | var points = atEvents.call(scope.chart, evt); 223 | var point = atEvent ? atEvent.call(scope.chart, evt)[0] : void 0; 224 | 225 | if (triggerOnlyOnChange === false || 226 | (! angular.equals(lastState.points, points) && ! angular.equals(lastState.point, point)) 227 | ) { 228 | lastState.point = point; 229 | lastState.points = points; 230 | scope[action](points, evt, point); 231 | } 232 | } 233 | }; 234 | } 235 | 236 | function getColors (type, scope) { 237 | var colors = angular.copy(scope.chartColors || 238 | ChartJs.getOptions(type).chartColors || 239 | Chart.defaults.global.colors 240 | ); 241 | var notEnoughColors = colors.length < scope.chartData.length; 242 | while (colors.length < scope.chartData.length) { 243 | colors.push(scope.chartGetColor()); 244 | } 245 | // mutate colors in this case as we don't want 246 | // the colors to change on each refresh 247 | if (notEnoughColors) scope.chartColors = colors; 248 | return colors.map(convertColor); 249 | } 250 | 251 | function convertColor (color) { 252 | // Allows RGB and RGBA colors to be input as a string: e.g.: "rgb(159,204,0)", "rgba(159,204,0, 0.5)" 253 | if (typeof color === 'string' && color[0] === 'r') return getColor(rgbStringToRgb(color)); 254 | // Allows hex colors to be input as a string. 255 | if (typeof color === 'string' && color[0] === '#') return getColor(hexToRgb(color.substr(1))); 256 | // Allows colors to be input as an object, bypassing getColor() entirely 257 | if (typeof color === 'object' && color !== null) return color; 258 | return getRandomColor(); 259 | } 260 | 261 | function getRandomColor () { 262 | var color = [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)]; 263 | return getColor(color); 264 | } 265 | 266 | function getColor (color) { 267 | var alpha = color[3] || 1; 268 | color = color.slice(0, 3); 269 | return { 270 | backgroundColor: rgba(color, 0.2), 271 | pointBackgroundColor: rgba(color, alpha), 272 | pointHoverBackgroundColor: rgba(color, 0.8), 273 | borderColor: rgba(color, alpha), 274 | pointBorderColor: '#fff', 275 | pointHoverBorderColor: rgba(color, alpha) 276 | }; 277 | } 278 | 279 | function getRandomInt (min, max) { 280 | return Math.floor(Math.random() * (max - min + 1)) + min; 281 | } 282 | 283 | function rgba (color, alpha) { 284 | // rgba not supported by IE8 285 | return useExcanvas ? 'rgb(' + color.join(',') + ')' : 'rgba(' + color.concat(alpha).join(',') + ')'; 286 | } 287 | 288 | // Credit: http://stackoverflow.com/a/11508164/1190235 289 | function hexToRgb (hex) { 290 | var bigint = parseInt(hex, 16), 291 | r = (bigint >> 16) & 255, 292 | g = (bigint >> 8) & 255, 293 | b = bigint & 255; 294 | 295 | return [r, g, b]; 296 | } 297 | 298 | function rgbStringToRgb (color) { 299 | var match = color.match(/^rgba?\(([\d,.]+)\)$/); 300 | if (! match) throw new Error('Cannot parse rgb value'); 301 | color = match[1].split(','); 302 | return color.map(Number); 303 | } 304 | 305 | function hasData (scope) { 306 | return scope.chartData && scope.chartData.length; 307 | } 308 | 309 | function getChartColorFn (scope) { 310 | return typeof scope.chartGetColor === 'function' ? scope.chartGetColor : getRandomColor; 311 | } 312 | 313 | function getChartData (type, scope) { 314 | var colors = getColors(type, scope); 315 | return Array.isArray(scope.chartData[0]) ? 316 | getDataSets(scope.chartLabels, scope.chartData, scope.chartSeries || [], colors, scope.chartDatasetOverride) : 317 | getData(scope.chartLabels, scope.chartData, colors, scope.chartDatasetOverride); 318 | } 319 | 320 | function getDataSets (labels, data, series, colors, datasetOverride) { 321 | return { 322 | labels: labels, 323 | datasets: data.map(function (item, i) { 324 | var dataset = angular.extend({}, colors[i], { 325 | label: series[i], 326 | data: item 327 | }); 328 | if (datasetOverride && datasetOverride.length >= i) { 329 | angular.merge(dataset, datasetOverride[i]); 330 | } 331 | return dataset; 332 | }) 333 | }; 334 | } 335 | 336 | function getData (labels, data, colors, datasetOverride) { 337 | var dataset = { 338 | labels: labels, 339 | datasets: [{ 340 | data: data, 341 | backgroundColor: colors.map(function (color) { 342 | return color.pointBackgroundColor; 343 | }), 344 | hoverBackgroundColor: colors.map(function (color) { 345 | return color.backgroundColor; 346 | }) 347 | }] 348 | }; 349 | if (datasetOverride) { 350 | angular.merge(dataset.datasets[0], datasetOverride); 351 | } 352 | return dataset; 353 | } 354 | 355 | function getChartOptions (type, scope) { 356 | return angular.extend({}, ChartJs.getOptions(type), scope.chartOptions); 357 | } 358 | 359 | function bindEvents (cvs, scope) { 360 | cvs.onclick = scope.chartClick ? getEventHandler(scope, 'chartClick', false) : angular.noop; 361 | cvs.onmousemove = scope.chartHover ? getEventHandler(scope, 'chartHover', true) : angular.noop; 362 | } 363 | 364 | function updateChart (values, scope) { 365 | if (Array.isArray(scope.chartData[0])) { 366 | scope.chart.data.datasets.forEach(function (dataset, i) { 367 | dataset.data = values[i]; 368 | }); 369 | } else { 370 | scope.chart.data.datasets[0].data = values; 371 | } 372 | 373 | scope.chart.update(); 374 | scope.$emit('chart-update', scope.chart); 375 | } 376 | 377 | function isEmpty (value) { 378 | return ! value || 379 | (Array.isArray(value) && ! value.length) || 380 | (typeof value === 'object' && ! Object.keys(value).length); 381 | } 382 | 383 | function canDisplay (type, scope, elem, options) { 384 | // TODO: check parent? 385 | if (options.responsive && elem[0].clientHeight === 0) { 386 | $timeout(function () { 387 | createChart(type, scope, elem); 388 | }, 50, false); 389 | return false; 390 | } 391 | return true; 392 | } 393 | 394 | function destroyChart(scope) { 395 | if(! scope.chart) return; 396 | scope.chart.destroy(); 397 | scope.$emit('chart-destroy', scope.chart); 398 | } 399 | } 400 | })); 401 | -------------------------------------------------------------------------------- /plugins/angular-chart/angular-chart.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["angular-chart.js"],"names":["factory","exports","module","angular","require","Chart","define","amd","Error","ChartJsProvider","options","responsive","ChartJs","getOptions","type","typeOptions","extend","this","setOptions","customOptions","merge","defaults","$get","ChartJsFactory","$timeout","createChart","scope","elem","getChartOptions","hasData","canDisplay","cvs","ctx","getContext","chartGetColor","getChartColorFn","data","getChartData","destroyChart","chart","$emit","bindEvents","canUpdateChart","newVal","oldVal","length","Array","isArray","every","element","index","reduce","sum","carry","val","getEventHandler","action","triggerOnlyOnChange","lastState","point","points","evt","atEvent","getElementAtEvent","getPointAtEvent","atEvents","getElementsAtEvent","getPointsAtEvent","call","equals","getColors","colors","copy","chartColors","global","notEnoughColors","chartData","push","map","convertColor","color","getColor","rgbStringToRgb","hexToRgb","substr","getRandomColor","getRandomInt","alpha","slice","backgroundColor","rgba","pointBackgroundColor","pointHoverBackgroundColor","borderColor","pointBorderColor","pointHoverBorderColor","min","max","Math","floor","random","useExcanvas","join","concat","hex","bigint","parseInt","r","g","b","match","split","Number","getDataSets","chartLabels","chartSeries","chartDatasetOverride","getData","labels","series","datasetOverride","datasets","item","i","dataset","label","hoverBackgroundColor","chartOptions","onclick","chartClick","noop","onmousemove","chartHover","updateChart","values","forEach","update","isEmpty","value","Object","keys","clientHeight","destroy","restrict","chartType","link","watchData","watchOther","watchType","window","G_vmlCanvasManager","initElement","$watch","$on","resize","multiTooltipTemplate","tooltips","mode","elements","line","borderWidth","rectangle","legend","display","animation","provider","directive","name"],"mappings":"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","file":"angular-chart.min.js","sourcesContent":["(function (factory) {\n 'use strict';\n if (typeof exports === 'object') {\n // Node/CommonJS\n module.exports = factory(\n typeof angular !== 'undefined' ? angular : require('angular'),\n typeof Chart !== 'undefined' ? Chart : require('chart.js'));\n } else if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(['angular', 'chart'], factory);\n } else {\n // Browser globals\n if (typeof angular === 'undefined') {\n throw new Error('AngularJS framework needs to be included, see https://angularjs.org/');\n } else if (typeof Chart === 'undefined') {\n throw new Error('Chart.js library needs to be included, see http://jtblin.github.io/angular-chart.js/');\n }\n factory(angular, Chart);\n }\n}(function (angular, Chart) {\n 'use strict';\n\n Chart.defaults.global.multiTooltipTemplate = '<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>';\n Chart.defaults.global.tooltips.mode = 'label';\n Chart.defaults.global.elements.line.borderWidth = 2;\n Chart.defaults.global.elements.rectangle.borderWidth = 2;\n Chart.defaults.global.legend.display = false;\n Chart.defaults.global.colors = [\n '#97BBCD', // blue\n '#DCDCDC', // light grey\n '#F7464A', // red\n '#46BFBD', // green\n '#FDB45C', // yellow\n '#949FB1', // grey\n '#4D5360' // dark grey\n ];\n\n var useExcanvas = typeof window.G_vmlCanvasManager === 'object' &&\n window.G_vmlCanvasManager !== null &&\n typeof window.G_vmlCanvasManager.initElement === 'function';\n\n if (useExcanvas) Chart.defaults.global.animation = false;\n\n return angular.module('chart.js', [])\n .provider('ChartJs', ChartJsProvider)\n .factory('ChartJsFactory', ['ChartJs', '$timeout', ChartJsFactory])\n .directive('chartBase', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory(); }])\n .directive('chartLine', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('line'); }])\n .directive('chartBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('bar'); }])\n .directive('chartHorizontalBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('horizontalBar'); }])\n .directive('chartRadar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('radar'); }])\n .directive('chartDoughnut', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('doughnut'); }])\n .directive('chartPie', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('pie'); }])\n .directive('chartPolarArea', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('polarArea'); }])\n .directive('chartBubble', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('bubble'); }])\n .name;\n\n /**\n * Wrapper for chart.js\n * Allows configuring chart js using the provider\n *\n * angular.module('myModule', ['chart.js']).config(function(ChartJsProvider) {\n * ChartJsProvider.setOptions({ responsive: false });\n * ChartJsProvider.setOptions('Line', { responsive: true });\n * })))\n */\n function ChartJsProvider () {\n var options = { responsive: true };\n var ChartJs = {\n Chart: Chart,\n getOptions: function (type) {\n var typeOptions = type && options[type] || {};\n return angular.extend({}, options, typeOptions);\n }\n };\n\n /**\n * Allow to set global options during configuration\n */\n this.setOptions = function (type, customOptions) {\n // If no type was specified set option for the global object\n if (! customOptions) {\n customOptions = type;\n options = angular.merge(options, customOptions);\n } else {\n // Set options for the specific chart\n options[type] = angular.merge(options[type] || {}, customOptions);\n }\n\n angular.merge(ChartJs.Chart.defaults, options);\n };\n\n this.$get = function () {\n return ChartJs;\n };\n }\n\n function ChartJsFactory (ChartJs, $timeout) {\n return function chart (type) {\n return {\n restrict: 'CA',\n scope: {\n chartGetColor: '=?',\n chartType: '=',\n chartData: '=?',\n chartLabels: '=?',\n chartOptions: '=?',\n chartSeries: '=?',\n chartColors: '=?',\n chartClick: '=?',\n chartHover: '=?',\n chartDatasetOverride: '=?'\n },\n link: function (scope, elem/*, attrs */) {\n if (useExcanvas) window.G_vmlCanvasManager.initElement(elem[0]);\n\n // Order of setting \"watch\" matter\n scope.$watch('chartData', watchData, true);\n scope.$watch('chartSeries', watchOther, true);\n scope.$watch('chartLabels', watchOther, true);\n scope.$watch('chartOptions', watchOther, true);\n scope.$watch('chartColors', watchOther, true);\n scope.$watch('chartDatasetOverride', watchOther, true);\n scope.$watch('chartType', watchType, false);\n\n scope.$on('$destroy', function () {\n destroyChart(scope);\n });\n\n scope.$on('$resize', function () {\n if (scope.chart) scope.chart.resize();\n });\n\n function watchData (newVal, oldVal) {\n if (! newVal || ! newVal.length || (Array.isArray(newVal[0]) && ! newVal[0].length)) {\n destroyChart(scope);\n return;\n }\n var chartType = type || scope.chartType;\n if (! chartType) return;\n\n if (scope.chart && canUpdateChart(newVal, oldVal))\n return updateChart(newVal, scope);\n\n createChart(chartType, scope, elem);\n }\n\n function watchOther (newVal, oldVal) {\n if (isEmpty(newVal)) return;\n if (angular.equals(newVal, oldVal)) return;\n var chartType = type || scope.chartType;\n if (! chartType) return;\n\n // chart.update() doesn't work for series and labels\n // so we have to re-create the chart entirely\n createChart(chartType, scope, elem);\n }\n\n function watchType (newVal, oldVal) {\n if (isEmpty(newVal)) return;\n if (angular.equals(newVal, oldVal)) return;\n createChart(newVal, scope, elem);\n }\n }\n };\n };\n\n function createChart (type, scope, elem) {\n var options = getChartOptions(type, scope);\n if (! hasData(scope) || ! canDisplay(type, scope, elem, options)) return;\n\n var cvs = elem[0];\n var ctx = cvs.getContext('2d');\n\n scope.chartGetColor = getChartColorFn(scope);\n var data = getChartData(type, scope);\n // Destroy old chart if it exists to avoid ghost charts issue\n // https://github.com/jtblin/angular-chart.js/issues/187\n destroyChart(scope);\n\n scope.chart = new ChartJs.Chart(ctx, {\n type: type,\n data: data,\n options: options\n });\n scope.$emit('chart-create', scope.chart);\n bindEvents(cvs, scope);\n }\n\n function canUpdateChart (newVal, oldVal) {\n if (newVal && oldVal && newVal.length && oldVal.length) {\n return Array.isArray(newVal[0]) ?\n newVal.length === oldVal.length && newVal.every(function (element, index) {\n return element.length === oldVal[index].length; }) :\n oldVal.reduce(sum, 0) > 0 ? newVal.length === oldVal.length : false;\n }\n return false;\n }\n\n function sum (carry, val) {\n return carry + val;\n }\n\n function getEventHandler (scope, action, triggerOnlyOnChange) {\n var lastState = {\n point: void 0,\n points: void 0\n };\n return function (evt) {\n var atEvent = scope.chart.getElementAtEvent || scope.chart.getPointAtEvent;\n var atEvents = scope.chart.getElementsAtEvent || scope.chart.getPointsAtEvent;\n if (atEvents) {\n var points = atEvents.call(scope.chart, evt);\n var point = atEvent ? atEvent.call(scope.chart, evt)[0] : void 0;\n\n if (triggerOnlyOnChange === false ||\n (! angular.equals(lastState.points, points) && ! angular.equals(lastState.point, point))\n ) {\n lastState.point = point;\n lastState.points = points;\n scope[action](points, evt, point);\n }\n }\n };\n }\n\n function getColors (type, scope) {\n var colors = angular.copy(scope.chartColors ||\n ChartJs.getOptions(type).chartColors ||\n Chart.defaults.global.colors\n );\n var notEnoughColors = colors.length < scope.chartData.length;\n while (colors.length < scope.chartData.length) {\n colors.push(scope.chartGetColor());\n }\n // mutate colors in this case as we don't want\n // the colors to change on each refresh\n if (notEnoughColors) scope.chartColors = colors;\n return colors.map(convertColor);\n }\n\n function convertColor (color) {\n // Allows RGB and RGBA colors to be input as a string: e.g.: \"rgb(159,204,0)\", \"rgba(159,204,0, 0.5)\"\n if (typeof color === 'string' && color[0] === 'r') return getColor(rgbStringToRgb(color));\n // Allows hex colors to be input as a string.\n if (typeof color === 'string' && color[0] === '#') return getColor(hexToRgb(color.substr(1)));\n // Allows colors to be input as an object, bypassing getColor() entirely\n if (typeof color === 'object' && color !== null) return color;\n return getRandomColor();\n }\n\n function getRandomColor () {\n var color = [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];\n return getColor(color);\n }\n\n function getColor (color) {\n var alpha = color[3] || 1;\n color = color.slice(0, 3);\n return {\n backgroundColor: rgba(color, 0.2),\n pointBackgroundColor: rgba(color, alpha),\n pointHoverBackgroundColor: rgba(color, 0.8),\n borderColor: rgba(color, alpha),\n pointBorderColor: '#fff',\n pointHoverBorderColor: rgba(color, alpha)\n };\n }\n\n function getRandomInt (min, max) {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n }\n\n function rgba (color, alpha) {\n // rgba not supported by IE8\n return useExcanvas ? 'rgb(' + color.join(',') + ')' : 'rgba(' + color.concat(alpha).join(',') + ')';\n }\n\n // Credit: http://stackoverflow.com/a/11508164/1190235\n function hexToRgb (hex) {\n var bigint = parseInt(hex, 16),\n r = (bigint >> 16) & 255,\n g = (bigint >> 8) & 255,\n b = bigint & 255;\n\n return [r, g, b];\n }\n\n function rgbStringToRgb (color) {\n var match = color.match(/^rgba?\\(([\\d,.]+)\\)$/);\n if (! match) throw new Error('Cannot parse rgb value');\n color = match[1].split(',');\n return color.map(Number);\n }\n\n function hasData (scope) {\n return scope.chartData && scope.chartData.length;\n }\n\n function getChartColorFn (scope) {\n return typeof scope.chartGetColor === 'function' ? scope.chartGetColor : getRandomColor;\n }\n\n function getChartData (type, scope) {\n var colors = getColors(type, scope);\n return Array.isArray(scope.chartData[0]) ?\n getDataSets(scope.chartLabels, scope.chartData, scope.chartSeries || [], colors, scope.chartDatasetOverride) :\n getData(scope.chartLabels, scope.chartData, colors, scope.chartDatasetOverride);\n }\n\n function getDataSets (labels, data, series, colors, datasetOverride) {\n return {\n labels: labels,\n datasets: data.map(function (item, i) {\n var dataset = angular.extend({}, colors[i], {\n label: series[i],\n data: item\n });\n if (datasetOverride && datasetOverride.length >= i) {\n angular.merge(dataset, datasetOverride[i]);\n }\n return dataset;\n })\n };\n }\n\n function getData (labels, data, colors, datasetOverride) {\n var dataset = {\n labels: labels,\n datasets: [{\n data: data,\n backgroundColor: colors.map(function (color) {\n return color.pointBackgroundColor;\n }),\n hoverBackgroundColor: colors.map(function (color) {\n return color.backgroundColor;\n })\n }]\n };\n if (datasetOverride) {\n angular.merge(dataset.datasets[0], datasetOverride);\n }\n return dataset;\n }\n\n function getChartOptions (type, scope) {\n return angular.extend({}, ChartJs.getOptions(type), scope.chartOptions);\n }\n\n function bindEvents (cvs, scope) {\n cvs.onclick = scope.chartClick ? getEventHandler(scope, 'chartClick', false) : angular.noop;\n cvs.onmousemove = scope.chartHover ? getEventHandler(scope, 'chartHover', true) : angular.noop;\n }\n\n function updateChart (values, scope) {\n if (Array.isArray(scope.chartData[0])) {\n scope.chart.data.datasets.forEach(function (dataset, i) {\n dataset.data = values[i];\n });\n } else {\n scope.chart.data.datasets[0].data = values;\n }\n\n scope.chart.update();\n scope.$emit('chart-update', scope.chart);\n }\n\n function isEmpty (value) {\n return ! value ||\n (Array.isArray(value) && ! value.length) ||\n (typeof value === 'object' && ! Object.keys(value).length);\n }\n\n function canDisplay (type, scope, elem, options) {\n // TODO: check parent?\n if (options.responsive && elem[0].clientHeight === 0) {\n $timeout(function () {\n createChart(type, scope, elem);\n }, 50, false);\n return false;\n }\n return true;\n }\n\n function destroyChart(scope) {\n if(! scope.chart) return;\n scope.chart.destroy();\n scope.$emit('chart-destroy', scope.chart);\n }\n }\n}));\n"]} --------------------------------------------------------------------------------