├── 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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------
/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"]}
--------------------------------------------------------------------------------