Shield Security for WordPress - Version 6.7.1

Version Description

  • Current Release = Released: 22nd May, 2018 - Release Notes

  • (v.1) ADDED: [PRO] Admin Notes feature - you can add notes to the Shield plugin in the Insights Dashboard.

  • (v.1) FIXED: A few bugs with the Insights Dashboard.

  • (v.0) ADDED: A simple test cron to demonstrate whether your site crons are running.

  • (v.0) ADDED: [PRO] Full support for new WordPress GDPR Privacy Policy controls for exporting and erasing data.

  • (v.0) ADDED: [PRO] New GDPR guided wizard for exporting/erasing particular data based on custom search results.

  • (v.0) CHANGED: Guided Wizards now load through WP admin to fix ajax problems for poorly configured SSL on some sites

  • (v.0) IMPROVED: Upgraded Bootstrap library to 4.1.1.

  • (v.0) IMPROVED: Compatibility with AIO Events Cal - they like to force their old Twig libraries on everyone else.

Note: The Insights Dashboard is only available on sites with PHP v5.4.0 and above.

Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 6.7.1
Comparing to
See all releases

Code changes from version 6.7.0 to 6.7.1

icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: http://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 6.7.0
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages/
9
  * Author: One Dollar Plugin
3
  * Plugin Name: Shield Security
4
  * Plugin URI: http://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
+ * Version: 6.7.1
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages/
9
  * Author: One Dollar Plugin
plugin-spec.php CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "properties": {
3
- "version": "6.7.0",
4
- "release_timestamp": 1526904000,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
1
  {
2
  "properties": {
3
+ "version": "6.7.1",
4
+ "release_timestamp": 1526972443,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.5.0
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 4.9
11
- Stable tag: 6.7.0
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
@@ -353,11 +353,11 @@ If you don't want to support the work, no problem! You can still continue to use
353
 
354
  You can [go Pro for just $1/month](http://icwp.io/aa).
355
 
356
- = 6.7.0 - Current Release =
357
- *Released: 21st May, 2018* - [Release Notes](http://icwp.io/cx)
358
 
359
- * **(v.0)** ADDED: All-New Insights Dashboard providing a high-level overview of your site security, with recommendations.
360
- * **(v.0)** ADDED: Helpful, explanatory videos directly into the Guided Welcome Wizard.
361
  * **(v.0)** ADDED: A simple test cron to demonstrate whether your site crons are running.
362
  * **(v.0)** ADDED: [**PRO**] Full support for new WordPress GDPR Privacy Policy controls for exporting and erasing data.
363
  * **(v.0)** ADDED: [**PRO**] New GDPR guided wizard for exporting/erasing particular data based on custom search results.
@@ -370,6 +370,8 @@ Note: The Insights Dashboard is only available on sites with PHP v5.4.0 and abov
370
  = 6.7 Series =
371
  *Released: 21st May, 2018* - [Release Notes](http://icwp.io/cx)
372
 
 
 
373
  * **(v.0)** ADDED: All-New Insights Dashboard providing a high-level overview of your site security, with recommendations.
374
  * **(v.0)** ADDED: Helpful, explanatory videos directly into the Guided Welcome Wizard.
375
  * **(v.0)** ADDED: A simple test cron to demonstrate whether your site crons are running.
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 4.9
11
+ Stable tag: 6.7.1
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
353
 
354
  You can [go Pro for just $1/month](http://icwp.io/aa).
355
 
356
+ = 6.7.1 - Current Release =
357
+ *Released: 22nd May, 2018* - [Release Notes](http://icwp.io/cx)
358
 
359
+ * **(v.1)** ADDED: [**PRO**] Admin Notes feature - you can add notes to the Shield plugin in the Insights Dashboard.
360
+ * **(v.1)** FIXED: A few bugs with the Insights Dashboard.
361
  * **(v.0)** ADDED: A simple test cron to demonstrate whether your site crons are running.
362
  * **(v.0)** ADDED: [**PRO**] Full support for new WordPress GDPR Privacy Policy controls for exporting and erasing data.
363
  * **(v.0)** ADDED: [**PRO**] New GDPR guided wizard for exporting/erasing particular data based on custom search results.
370
  = 6.7 Series =
371
  *Released: 21st May, 2018* - [Release Notes](http://icwp.io/cx)
372
 
373
+ * **(v.1)** FIXED: A few bugs with the Insights Dashboard
374
+ * **(v.1)** ADDED: [**PRO**] Admin Notes feature - you can now add notes to the Shield plugin in the Insights Dashboard.
375
  * **(v.0)** ADDED: All-New Insights Dashboard providing a high-level overview of your site security, with recommendations.
376
  * **(v.0)** ADDED: Helpful, explanatory videos directly into the Guided Welcome Wizard.
377
  * **(v.0)** ADDED: A simple test cron to demonstrate whether your site crons are running.
resources/js/plugin.js CHANGED
@@ -88,5 +88,78 @@ var iCWP_WPSF_OptionsFormSubmit = new function () {
88
  };
89
  }();
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  iCWP_WPSF_OptionsPages.initialise();
92
- iCWP_WPSF_OptionsFormSubmit.initialise();
 
88
  };
89
  }();
90
 
91
+ var iCWP_WPSF_InsightsAdminNotes = new function () {
92
+
93
+ var bRequestCurrentlyRunning = false;
94
+
95
+ /**
96
+ */
97
+ var renderNotes = function ( event ) {
98
+
99
+ jQuery.post( ajaxurl, icwp_wpsf_vars_insights.ajax_admin_notes_render,
100
+ function ( oResponse ) {
101
+ if ( oResponse.success ) {
102
+ jQuery( '#AdminNotesContainer' ).html( oResponse.data.html );
103
+ }
104
+ else {
105
+ var sMessage = 'Communications error with site.';
106
+ if ( oResponse.data.message !== undefined ) {
107
+ sMessage = oResponse.data.message;
108
+ }
109
+ alert( sMessage );
110
+ }
111
+ }
112
+ ).always( function () {
113
+ iCWP_WPSF_BodyOverlay.hide();
114
+ }
115
+ );
116
+ };
117
+
118
+ /**
119
+ */
120
+ var submitForm = function ( event ) {
121
+ iCWP_WPSF_BodyOverlay.show();
122
+
123
+ if ( bRequestCurrentlyRunning ) {
124
+ return false;
125
+ }
126
+ bRequestCurrentlyRunning = true;
127
+ event.preventDefault();
128
+
129
+ jQuery.post( ajaxurl, jQuery( this ).serialize(),
130
+ function ( oResponse ) {
131
+ if ( oResponse.success ) {
132
+ renderNotes(); // this will remove the overlay
133
+ }
134
+ else {
135
+ var sMessage = 'Communications error with site.';
136
+ if ( oResponse.data.message !== undefined ) {
137
+ sMessage = oResponse.data.message;
138
+ }
139
+ alert( sMessage );
140
+ iCWP_WPSF_BodyOverlay.hide();
141
+ }
142
+ }
143
+ ).always( function () {
144
+ bRequestCurrentlyRunning = false;
145
+ }
146
+ );
147
+ };
148
+
149
+ this.initialise = function () {
150
+ jQuery( document ).ready( function () {
151
+ jQuery( document ).on( "keydown", "form#NewAdminNote", function ( e ) {
152
+ /* if ( e.ctrlKey && e.keyCode === 13 ) {
153
+ can't get ctrl+return to submit!
154
+ console.log( e );
155
+ submitForm( e );
156
+ } */
157
+ } );
158
+ jQuery( document ).on( "submit", "form#NewAdminNote", submitForm );
159
+ } );
160
+ };
161
+ }();
162
+
163
  iCWP_WPSF_OptionsPages.initialise();
164
+ iCWP_WPSF_OptionsFormSubmit.initialise();
165
+ iCWP_WPSF_InsightsAdminNotes.initialise();
src/common/icwp-wpfunctions.php CHANGED
@@ -404,7 +404,7 @@ class ICWP_WPSF_WpFunctions extends ICWP_WPSF_Foundation {
404
  */
405
  public function hasCoreUpdate() {
406
  $aUpdates = $this->getCoreUpdates();
407
- return ( !isset( $aUpdates[ 0 ]->response ) || 'latest' == $aUpdates[ 0 ]->response );
408
  }
409
 
410
  public function redirectHere() {
404
  */
405
  public function hasCoreUpdate() {
406
  $aUpdates = $this->getCoreUpdates();
407
+ return ( isset( $aUpdates[ 0 ]->response ) && 'latest' != $aUpdates[ 0 ]->response );
408
  }
409
 
410
  public function redirectHere() {
src/config/feature-plugin.php CHANGED
@@ -399,6 +399,14 @@
399
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
400
  "importexport_cron_name": "autoimport",
401
  "href_privacy_policy": "http://icwp.io/wpshieldprivacypolicy",
 
 
 
 
 
 
 
 
402
  "active_plugin_features": [
403
  {
404
  "slug": "insights",
399
  "tracking_post_url": "https://tracking.icontrolwp.com/track/plugin/shield",
400
  "importexport_cron_name": "autoimport",
401
  "href_privacy_policy": "http://icwp.io/wpshieldprivacypolicy",
402
+ "db_notes_name": "notes",
403
+ "db_notes_table_columns": [
404
+ "id",
405
+ "wp_username",
406
+ "note",
407
+ "created_at",
408
+ "deleted_at"
409
+ ],
410
  "active_plugin_features": [
411
  {
412
  "slug": "insights",
src/features/admin_access_restriction.php CHANGED
@@ -361,6 +361,38 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
361
  return $this;
362
  }
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  /**
365
  * @param array $aOptionsParams
366
  * @return array
361
  return $this;
362
  }
363
 
364
+ /**
365
+ * @param array $aAllNotices
366
+ * @return array
367
+ */
368
+ public function addInsightsNoticeData( $aAllNotices ) {
369
+
370
+ $aNotices = array(
371
+ 'title' => _wpsf__( 'Security Admin Protection' ),
372
+ 'messages' => array()
373
+ );
374
+
375
+ {//sec admin
376
+ if ( !( $this->isModuleEnabled() && $this->hasAccessKey() ) ) {
377
+ $aNotices[ 'messages' ][ 'sec_admin' ] = array(
378
+ 'title' => 'Security Plugin Unprotected',
379
+ 'message' => sprintf(
380
+ _wpsf__( "The Security Admin protection is not active." ),
381
+ $this->getConn()->getHumanName()
382
+ ),
383
+ 'href' => $this->getUrl_AdminPage(),
384
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
385
+ 'rec' => _wpsf__( 'Security Admin should be turned-on to protect your security settings.' )
386
+ );
387
+ }
388
+ }
389
+
390
+ $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
391
+ $aAllNotices[ 'sec_admin' ] = $aNotices;
392
+
393
+ return $aAllNotices;
394
+ }
395
+
396
  /**
397
  * @param array $aOptionsParams
398
  * @return array
src/features/base.php CHANGED
@@ -120,6 +120,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
120
  $nMenuPriority = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
121
  add_filter( $this->prefix( 'filter_plugin_submenu_items' ), array( $this, 'filter_addPluginSubMenuItem' ), $nMenuPriority );
122
  add_filter( $this->prefix( 'collect_module_summary_data' ), array( $this, 'addModuleSummaryData' ), $nMenuPriority );
 
123
  add_action( $this->prefix( 'plugin_shutdown' ), array( $this, 'action_doFeatureShutdown' ) );
124
  add_action( $this->prefix( 'delete_plugin' ), array( $this, 'deletePluginOptions' ) );
125
  add_filter( $this->prefix( 'aggregate_all_plugin_options' ), array( $this, 'aggregateOptionsValues' ) );
@@ -627,6 +628,14 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
627
  return $aSummaryData;
628
  }
629
 
 
 
 
 
 
 
 
 
630
  /**
631
  * @return array
632
  */
120
  $nMenuPriority = isset( $aModProps[ 'menu_priority' ] ) ? $aModProps[ 'menu_priority' ] : 100;
121
  add_filter( $this->prefix( 'filter_plugin_submenu_items' ), array( $this, 'filter_addPluginSubMenuItem' ), $nMenuPriority );
122
  add_filter( $this->prefix( 'collect_module_summary_data' ), array( $this, 'addModuleSummaryData' ), $nMenuPriority );
123
+ add_filter( $this->prefix( 'collect_notices' ), array( $this, 'addInsightsNoticeData' ) );
124
  add_action( $this->prefix( 'plugin_shutdown' ), array( $this, 'action_doFeatureShutdown' ) );
125
  add_action( $this->prefix( 'delete_plugin' ), array( $this, 'deletePluginOptions' ) );
126
  add_filter( $this->prefix( 'aggregate_all_plugin_options' ), array( $this, 'aggregateOptionsValues' ) );
628
  return $aSummaryData;
629
  }
630
 
631
+ /**
632
+ * @param array $aAllNotices
633
+ * @return array
634
+ */
635
+ public function addInsightsNoticeData( $aAllNotices ) {
636
+ return $aAllNotices;
637
+ }
638
+
639
  /**
640
  * @return array
641
  */
src/features/hack_protect.php CHANGED
@@ -637,6 +637,106 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
637
  return $aWarnings;
638
  }
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
  /**
641
  * @param array $aOptionsParams
642
  * @return array
637
  return $aWarnings;
638
  }
639
 
640
+ /**
641
+ * @param array $aAllNotices
642
+ * @return array
643
+ */
644
+ public function addInsightsNoticeData( $aAllNotices ) {
645
+ $aNotices = array(
646
+ 'title' => _wpsf__( 'Scans' ),
647
+ 'messages' => array()
648
+ );
649
+
650
+ {// Core files
651
+ if ( !$this->isWcfScanEnabled() ) {
652
+ $aNotices[ 'messages' ][ 'wcf' ] = array(
653
+ 'title' => 'WP Core Files',
654
+ 'message' => _wpsf__( 'Core File scanner is not enabled.' ),
655
+ 'href' => $this->getUrl_AdminPage(),
656
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
657
+ 'rec' => _wpsf__( 'Automatic WordPress Core File scanner should be turned-on.' )
658
+ );
659
+ }
660
+ else if ( $this->getScanHasProblem( 'wcf' ) ) {
661
+ $aNotices[ 'messages' ][ 'wcf' ] = array(
662
+ 'title' => 'WP Core Files',
663
+ 'message' => _wpsf__( 'Modified WordPress core files found.' ),
664
+ 'href' => $this->getUrl_Wizard( 'wcf' ),
665
+ 'action' => _wpsf__( 'Run Scan' ),
666
+ 'rec' => _wpsf__( 'Scan WP core files and repair any files that are flagged as modified.' )
667
+ );
668
+ }
669
+ }
670
+
671
+ {// Unrecognised
672
+ if ( !$this->isUfcEnabled() ) {
673
+ $aNotices[ 'messages' ][ 'ufc' ] = array(
674
+ 'title' => 'Unrecognised Files',
675
+ 'message' => _wpsf__( 'Unrecognised File scanner is not enabled.' ),
676
+ 'href' => $this->getUrl_AdminPage(),
677
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
678
+ 'rec' => _wpsf__( 'Automatic scanning for non-WordPress core files is recommended.' )
679
+ );
680
+ }
681
+ else if ( $this->getScanHasProblem( 'ufc' ) ) {
682
+ $aNotices[ 'messages' ][ 'ufc' ] = array(
683
+ 'title' => 'Unrecognised Files',
684
+ 'message' => _wpsf__( 'Unrecognised files found in WordPress Core directory.' ),
685
+ 'href' => $this->getUrl_Wizard( 'ufc' ),
686
+ 'action' => _wpsf__( 'Run Scan' ),
687
+ 'rec' => _wpsf__( 'Scan and remove any files that are not meant to be in the WP core directories.' )
688
+ );
689
+ }
690
+ }
691
+
692
+ {// Plugin/Theme Guard
693
+ if ( !$this->isPtgEnabled() ) {
694
+ $aNotices[ 'messages' ][ 'ptg' ] = array(
695
+ 'title' => 'Plugin/Theme Guard',
696
+ 'message' => _wpsf__( 'Automatic Plugin/Themes Guard is not enabled.' ),
697
+ 'href' => $this->getUrl_AdminPage(),
698
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
699
+ 'rec' => _wpsf__( 'Automatic detection of plugin/theme modifications is recommended.' )
700
+ );
701
+ }
702
+ else if ( $this->getScanHasProblem( 'ptg' ) ) {
703
+ $aNotices[ 'messages' ][ 'ptg' ] = array(
704
+ 'title' => 'Plugin/Theme Guard',
705
+ 'message' => _wpsf__( 'A plugin/theme was found to have been modified.' ),
706
+ 'href' => $this->getUrl_Wizard( 'ptg' ),
707
+ 'action' => _wpsf__( 'Run Scan' ),
708
+ 'rec' => _wpsf__( 'Reviewing modifications to your plugins/themes is recommended.' )
709
+ );
710
+ }
711
+ }
712
+
713
+ {// Vulnerability Scanner
714
+ if ( !$this->isWpvulnEnabled() ) {
715
+ $aNotices[ 'messages' ][ 'wpv' ] = array(
716
+ 'title' => 'Vulnerability Scanner',
717
+ 'message' => _wpsf__( 'Plugin Vulnerability Scanner is not enabled.' ),
718
+ 'href' => $this->getUrl_AdminPage(),
719
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
720
+ 'rec' => _wpsf__( 'Automatic detection of plugin vulnerabilities is recommended.' )
721
+ );
722
+ }
723
+ else if ( $this->getScanHasProblem( 'wpv' ) ) {
724
+ $aNotices[ 'messages' ][ 'wpv' ] = array(
725
+ 'title' => 'Vulnerable Plugins',
726
+ 'message' => _wpsf__( 'At least 1 plugin has known vulnerabilities.' ),
727
+ 'href' => $this->loadWp()->getAdminUrl_Plugins( true ),
728
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Plugins' ) ),
729
+ 'rec' => _wpsf__( 'Plugins with known vulnerabilities should be updated, removed, or replaced.' )
730
+ );
731
+ }
732
+ }
733
+
734
+ $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
735
+
736
+ $aAllNotices[ 'scans' ] = $aNotices;
737
+ return $aAllNotices;
738
+ }
739
+
740
  /**
741
  * @param array $aOptionsParams
742
  * @return array
src/features/insights.php CHANGED
@@ -12,19 +12,20 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
12
  * @param array $aData
13
  */
14
  protected function displayModulePage( $aData = array() ) {
15
- $oWp = $this->loadWp();
16
 
17
  $aRecentAuditTrail = $this->getRecentAuditTrailEntries();
18
  $aSecNotices = $this->getNotices();
 
 
19
  $aData = array(
20
  'vars' => array(
21
- 'activation_url' => $oWp->getHomeUrl(),
22
  'summary' => $this->getInsightsModsSummary(),
23
  'audit_trail_recent' => $aRecentAuditTrail,
24
  'insight_events' => $this->getRecentEvents(),
25
  'insight_notices' => $aSecNotices,
26
  'insight_notices_count' => count( $aSecNotices ),
27
  'insight_stats' => $this->getStats(),
 
28
  ),
29
  'inputs' => array(
30
  'license_key' => array(
@@ -33,14 +34,12 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
33
  )
34
  ),
35
  'ajax' => array(
36
- 'license_handling' => $this->getAjaxActionData( 'license_handling' ),
37
- 'connection_debug' => $this->getAjaxActionData( 'connection_debug' )
38
  ),
39
- 'aHrefs' => array(
40
  'shield_pro_url' => 'http://icwp.io/shieldpro',
41
  'shield_pro_more_info_url' => 'http://icwp.io/shld1',
42
- 'iframe_url' => $this->getDef( 'landing_page_url' ),
43
- 'keyless_cp' => $this->getDef( 'keyless_cp' ),
44
  ),
45
  'flags' => array(
46
  'has_audit_trail_entries' => !empty( $aRecentAuditTrail ),
@@ -48,13 +47,114 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
48
  'show_standard_options' => false,
49
  'show_alt_content' => true,
50
  'is_pro' => $this->isPremium(),
51
- 'has_notices' => count( $aSecNotices ) > 0
 
 
52
  ),
53
  'strings' => $this->getDisplayStrings(),
54
  );
55
  echo $this->renderTemplate( '/wpadmin_pages/insights/index.twig', $aData, true );
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * @return array
60
  */
@@ -86,14 +186,31 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
86
  * @return string[]
87
  */
88
  protected function getNotices() {
89
- return array(
90
- 'site' => $this->getNoticesSite(),
91
- 'shield' => $this->getNoticesShield(),
92
- 'scans' => $this->getNoticesScans(),
93
- 'plugins' => $this->getNoticesPlugins(),
94
- 'themes' => $this->getNoticesThemes(),
95
- 'core' => $this->getNoticesCore(),
96
- 'user' => $this->getNoticesUsers(),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  );
98
  }
99
 
@@ -154,76 +271,14 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
154
  }
155
  }
156
 
157
- $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
158
- return $aNotices;
159
- }
160
-
161
- /**
162
- * @return array
163
- */
164
- protected function getNoticesUsers() {
165
- $oWpUsers = $this->loadWpUsers();
166
-
167
- /** @var ICWP_WPSF_FeatureHandler_UserManagement $oModUsers */
168
- $oModUsers = $this->getConn()->getModule( 'user_management' );
169
-
170
- $aNotices = array(
171
- 'title' => _wpsf__( 'Users' ),
172
- 'messages' => array()
173
- );
174
-
175
- { //admin user
176
- $oAdmin = $oWpUsers->getUserByUsername( 'admin' );
177
- if ( !empty( $oAdmin ) && user_can( $oAdmin, 'manage_options' ) ) {
178
- $aNotices[ 'messages' ][ 'admin' ] = array(
179
- 'title' => 'Admin User',
180
- 'message' => sprintf( _wpsf__( "Default 'admin' user still available." ) ),
181
  'href' => '',
182
- 'rec' => _wpsf__( "Default 'admin' user should be disabled or removed." )
183
- );
184
- }
185
- }
186
-
187
- {//password policies
188
- if ( !$oModUsers->isPasswordPoliciesEnabled() ) {
189
- $aNotices[ 'messages' ][ 'password' ] = array(
190
- 'title' => 'Password Policies',
191
- 'message' => _wpsf__( "Strong password policies are not enforced." ),
192
- 'href' => $oModUsers->getUrl_AdminPage(),
193
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
194
- 'rec' => _wpsf__( 'Password policies should be turned-on.' )
195
- );
196
- }
197
- }
198
-
199
- $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
200
- return $aNotices;
201
- }
202
-
203
- /**
204
- * @return array
205
- */
206
- protected function getNoticesShield() {
207
-
208
- /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oModSecAdmin */
209
- $oModSecAdmin = $this->getConn()->getModule( 'admin_access_restriction' );
210
-
211
- $aNotices = array(
212
- 'title' => _wpsf__( 'Shield Security' ),
213
- 'messages' => array()
214
- );
215
-
216
- {//sec admin
217
- if ( !( $oModSecAdmin->isModuleEnabled() && $oModSecAdmin->hasAccessKey() ) ) {
218
- $aNotices[ 'messages' ][ 'sec_admin' ] = array(
219
- 'title' => 'Security Admin',
220
- 'message' => sprintf(
221
- _wpsf__( "The Security Admin protection is not active." ),
222
- $this->getConn()->getHumanName()
223
- ),
224
- 'href' => $oModSecAdmin->getUrl_AdminPage(),
225
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
226
- 'rec' => _wpsf__( 'Security Admin should be turned-on to protect your security settings.' )
227
  );
228
  }
229
  }
@@ -242,8 +297,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
242
  'messages' => array()
243
  );
244
 
245
- // Inactive
246
- {
247
  $nCount = 0;
248
  $aActivePlugs = $oWpPlugins->getActivePlugins();
249
  foreach ( $oWpPlugins->getPlugins() as $sFile => $aPlugData ) {
@@ -262,8 +316,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
262
  }
263
  }
264
 
265
- // updates
266
- {
267
  $nCount = count( $oWpPlugins->getUpdates() );
268
  if ( $nCount > 0 ) {
269
  $aNotices[ 'messages' ][ 'updates' ] = array(
@@ -290,9 +343,8 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
290
  'messages' => array()
291
  );
292
 
293
- // Inactive
294
- {
295
- $nInactive = count( $oWpT->getThemes() ) - 1;
296
  if ( $nInactive > 0 ) {
297
  $aNotices[ 'messages' ][ 'inactive' ] = array(
298
  'title' => 'Inactive',
@@ -304,8 +356,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
304
  }
305
  }
306
 
307
- // updates
308
- {
309
  $nCount = count( $oWpT->getUpdates() );
310
  if ( $nCount > 0 ) {
311
  $aNotices[ 'messages' ][ 'updates' ] = array(
@@ -332,8 +383,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
332
  'messages' => array()
333
  );
334
 
335
- // updates
336
- {
337
  if ( $oWp->hasCoreUpdate() ) {
338
  $aNotices[ 'messages' ][ 'updates' ] = array(
339
  'title' => 'Updates',
@@ -345,8 +395,7 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
345
  }
346
  }
347
 
348
- // updates
349
- {
350
  if ( !$oWp->canCoreUpdateAutomatically() ) {
351
  $aNotices[ 'messages' ][ 'updates_auto' ] = array(
352
  'title' => 'Auto Updates',
@@ -358,31 +407,6 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
358
  }
359
  }
360
 
361
- { // Disallow file edit
362
- if ( current_user_can( 'edit_plugins' ) ) { //assumes current user is admin
363
- $aNotices[ 'messages' ][ 'disallow_file_edit' ] = array(
364
- 'title' => 'Code Editor',
365
- 'message' => _wpsf__( 'Direct editing of plugin/theme files is permitted.' ),
366
- 'href' => $this->getConn()->getModule( 'lockdown' )->getUrl_AdminPage(),
367
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
368
- 'rec' => _wpsf__( 'WP Plugin file editing should be disabled.' )
369
- );
370
- }
371
- }
372
-
373
- { // db password strength
374
- $this->loadAutoload();
375
- $nStrength = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( DB_PASSWORD )[ 'score' ];
376
- if ( $nStrength < 4 ) {
377
- $aNotices[ 'messages' ][ 'db_strength' ] = array(
378
- 'title' => 'DB Password',
379
- 'message' => _wpsf__( 'DB Password appears to be weak.' ),
380
- 'href' => '',
381
- 'rec' => _wpsf__( 'The database password should be strong.' )
382
- );
383
- }
384
- }
385
-
386
  $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
387
  return $aNotices;
388
  }
@@ -390,105 +414,24 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
390
  /**
391
  * @return array
392
  */
393
- protected function getNoticesScans() {
394
- $aNotices = array(
395
- 'title' => _wpsf__( 'Scans' ),
396
- 'messages' => array()
397
- );
398
-
399
- /** @var ICWP_WPSF_FeatureHandler_HackProtect $oModHg */
400
- $oModHg = $this->getConn()->getModule( 'hack_protect' );
401
-
402
- // Core files
403
- {
404
- if ( !$oModHg->isWcfScanEnabled() ) {
405
- $aNotices[ 'messages' ][ 'wcf' ] = array(
406
- 'title' => 'WP Core Files',
407
- 'message' => _wpsf__( 'Core File scanner is not enabled.' ),
408
- 'href' => $oModHg->getUrl_AdminPage(),
409
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
410
- 'rec' => _wpsf__( 'Automatic WordPress Core File scanner should be turned-on.' )
411
- );
412
- }
413
- else if ( $oModHg->getScanHasProblem( 'wcf' ) ) {
414
- $aNotices[ 'messages' ][ 'wcf' ] = array(
415
- 'title' => 'WP Core Files',
416
- 'message' => _wpsf__( 'Modified WordPress core files found.' ),
417
- 'href' => $oModHg->getUrl_Wizard( 'wcf' ),
418
- 'action' => _wpsf__( 'Run Scan' ),
419
- 'rec' => _wpsf__( 'Scan WP core files and repair any files that are flagged as modified.' )
420
- );
421
- }
422
- }
423
-
424
- // Unrecognised
425
- {
426
- if ( !$oModHg->isUfcEnabled() ) {
427
- $aNotices[ 'messages' ][ 'ufc' ] = array(
428
- 'title' => 'Unrecognised Files',
429
- 'message' => _wpsf__( 'Unrecognised File scanner is not enabled.' ),
430
- 'href' => $oModHg->getUrl_AdminPage(),
431
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
432
- 'rec' => _wpsf__( 'Automatic scanning for non-WordPress core files is recommended.' )
433
- );
434
- }
435
- else if ( $oModHg->getScanHasProblem( 'ufc' ) ) {
436
- $aNotices[ 'messages' ][ 'ufc' ] = array(
437
- 'title' => 'Unrecognised Files',
438
- 'message' => _wpsf__( 'Unrecognised files found in WordPress Core directory.' ),
439
- 'href' => $oModHg->getUrl_Wizard( 'ufc' ),
440
- 'action' => _wpsf__( 'Run Scan' ),
441
- 'rec' => _wpsf__( 'Scan and remove any files that are not meant to be in the WP core directories.' )
442
- );
443
- }
444
- }
445
 
446
- // Plugin/Theme Guard
447
- {
448
- if ( !$oModHg->isPtgEnabled() ) {
449
- $aNotices[ 'messages' ][ 'ptg' ] = array(
450
- 'title' => 'Plugin/Theme Guard',
451
- 'message' => _wpsf__( 'Automatic Plugin/Themes Guard is not enabled.' ),
452
- 'href' => $oModHg->getUrl_AdminPage(),
453
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
454
- 'rec' => _wpsf__( 'Automatic detection of plugin/theme modifications is recommended.' )
455
- );
456
- }
457
- else if ( $oModHg->getScanHasProblem( 'ptg' ) ) {
458
- $aNotices[ 'messages' ][ 'ptg' ] = array(
459
- 'title' => 'Plugin/Theme Guard',
460
- 'message' => _wpsf__( 'A plugin/theme was found to have been modified.' ),
461
- 'href' => $oModHg->getUrl_Wizard( 'ptg' ),
462
- 'action' => _wpsf__( 'Run Scan' ),
463
- 'rec' => _wpsf__( 'Reviewing modifications to your plugins/themes is recommended.' )
464
- );
465
- }
466
- }
467
 
468
- // Vulnerability Scanner
469
- {
470
- if ( !$oModHg->isWpvulnEnabled() ) {
471
- $aNotices[ 'messages' ][ 'wpv' ] = array(
472
- 'title' => 'Vulnerability Scanner',
473
- 'message' => _wpsf__( 'Plugin Vulnerability Scanner is not enabled.' ),
474
- 'href' => $oModHg->getUrl_AdminPage(),
475
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
476
- 'rec' => _wpsf__( 'Automatic detection of plugin vulnerabilities is recommended.' )
477
- );
478
- }
479
- else if ( $oModHg->getScanHasProblem( 'wpv' ) ) {
480
- $aNotices[ 'messages' ][ 'wpv' ] = array(
481
- 'title' => 'Vulnerable Plugins',
482
- 'message' => _wpsf__( 'At least 1 plugin has known vulnerabilities.' ),
483
- 'href' => $this->loadWp()->getAdminUrl_Plugins( true ),
484
- 'action' => sprintf( 'Go To %s', _wpsf__( 'Plugins' ) ),
485
- 'rec' => _wpsf__( 'Plugins with known vulnerabilities should be updated, removed, or replaced.' )
486
- );
487
- }
488
  }
489
 
490
- $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
491
- return $aNotices;
492
  }
493
 
494
  /**
@@ -598,63 +541,6 @@ class ICWP_WPSF_FeatureHandler_Insights extends ICWP_WPSF_FeatureHandler_BaseWps
598
  return $aItems;
599
  }
600
 
601
- /**
602
- * @param array $aOptionsParams
603
- * @return array
604
- * @throws Exception
605
- */
606
- protected function loadStrings_SectionTitles( $aOptionsParams ) {
607
-
608
- $sSectionSlug = $aOptionsParams[ 'slug' ];
609
- switch ( $sSectionSlug ) {
610
-
611
- case 'section_email_options' :
612
- $sTitle = _wpsf__( 'Email Options' );
613
- break;
614
-
615
- default:
616
- throw new Exception( sprintf( 'A section slug was defined but with no associated strings. Slug: "%s".', $sSectionSlug ) );
617
- }
618
- $aOptionsParams[ 'title' ] = $sTitle;
619
- return $aOptionsParams;
620
- }
621
-
622
- /**
623
- * @param array $aOptionsParams
624
- * @return array
625
- * @throws Exception
626
- */
627
- protected function loadStrings_Options( $aOptionsParams ) {
628
-
629
- $sKey = $aOptionsParams[ 'key' ];
630
- switch ( $sKey ) {
631
- case 'send_email_throttle_limit' :
632
- $sName = _wpsf__( 'Email Throttle Limit' );
633
- $sSummary = _wpsf__( 'Limit Emails Per Second' );
634
- $sDescription = _wpsf__( 'You throttle emails sent by this plugin by limiting the number of emails sent every second. This is useful in case you get hit by a bot attack. Zero (0) turns this off. Suggested: 10' );
635
- break;
636
-
637
- default:
638
- throw new Exception( sprintf( 'An option has been defined but without strings assigned to it. Option key: "%s".', $sKey ) );
639
- }
640
-
641
- $aOptionsParams[ 'name' ] = $sName;
642
- $aOptionsParams[ 'summary' ] = $sSummary;
643
- $aOptionsParams[ 'description' ] = $sDescription;
644
- return $aOptionsParams;
645
- }
646
-
647
- /**
648
- * This is the point where you would want to do any options verification
649
- */
650
- protected function doPrePluginOptionsSave() {
651
- $sLimit = $this->getOpt( 'send_email_throttle_limit' );
652
- if ( !is_numeric( $sLimit ) || $sLimit < 0 ) {
653
- $sLimit = 0;
654
- }
655
- $this->setOpt( 'send_email_throttle_limit', $sLimit );
656
- }
657
-
658
  /**
659
  * @return string[]
660
  */
12
  * @param array $aData
13
  */
14
  protected function displayModulePage( $aData = array() ) {
 
15
 
16
  $aRecentAuditTrail = $this->getRecentAuditTrailEntries();
17
  $aSecNotices = $this->getNotices();
18
+ $aNotes = $this->getNotes();
19
+
20
  $aData = array(
21
  'vars' => array(
 
22
  'summary' => $this->getInsightsModsSummary(),
23
  'audit_trail_recent' => $aRecentAuditTrail,
24
  'insight_events' => $this->getRecentEvents(),
25
  'insight_notices' => $aSecNotices,
26
  'insight_notices_count' => count( $aSecNotices ),
27
  'insight_stats' => $this->getStats(),
28
+ 'insight_notes' => $aNotes,
29
  ),
30
  'inputs' => array(
31
  'license_key' => array(
34
  )
35
  ),
36
  'ajax' => array(
37
+ 'admin_note_new' => $this->getAjaxActionData( 'admin_note_new' ),
38
+ 'admin_notes_render' => $this->getAjaxActionData( 'admin_notes_render' ),
39
  ),
40
+ 'hrefs' => array(
41
  'shield_pro_url' => 'http://icwp.io/shieldpro',
42
  'shield_pro_more_info_url' => 'http://icwp.io/shld1',
 
 
43
  ),
44
  'flags' => array(
45
  'has_audit_trail_entries' => !empty( $aRecentAuditTrail ),
47
  'show_standard_options' => false,
48
  'show_alt_content' => true,
49
  'is_pro' => $this->isPremium(),
50
+ 'has_notices' => count( $aSecNotices ) > 0,
51
+ 'has_notes' => count( $aNotes ) > 0,
52
+ 'can_notes' => $this->isPremium() //not the way to determine
53
  ),
54
  'strings' => $this->getDisplayStrings(),
55
  );
56
  echo $this->renderTemplate( '/wpadmin_pages/insights/index.twig', $aData, true );
57
  }
58
 
59
+ public function insertCustomJsVars() {
60
+
61
+ if ( $this->isThisModulePage() ) {
62
+ wp_localize_script(
63
+ $this->prefix( 'plugin' ),
64
+ 'icwp_wpsf_vars_insights',
65
+ array(
66
+ 'ajax_admin_notes_render' => $this->getAjaxActionData( 'admin_notes_render' ),
67
+ )
68
+ );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param array $aAjaxResponse
74
+ * @return array
75
+ */
76
+ public function handleAuthAjax( $aAjaxResponse ) {
77
+
78
+ if ( empty( $aAjaxResponse ) ) {
79
+ switch ( $this->loadDP()->request( 'exec' ) ) {
80
+
81
+ case 'admin_note_new':
82
+ $aAjaxResponse = $this->ajaxExec_AdminNoteNew();
83
+ break;
84
+
85
+ case 'admin_notes_render':
86
+ $aAjaxResponse = $this->ajaxExec_AdminNotesRender();
87
+ break;
88
+
89
+ default:
90
+ break;
91
+ }
92
+ }
93
+ return parent::handleAuthAjax( $aAjaxResponse );
94
+ }
95
+
96
+ /**
97
+ * @return array
98
+ */
99
+ protected function ajaxExec_AdminNoteNew() {
100
+ $oDP = $this->loadDP();
101
+ /** @var ICWP_WPSF_FeatureHandler_Plugin $oMod */
102
+ $oMod = $this->getConn()->getModule( 'plugin' );
103
+ $sNote = trim( $oDP->post( 'admin_note', '' ) );
104
+
105
+ $bSuccess = false;
106
+ if ( !$oMod->getCanAdminNotes() ) {
107
+ $sMessage = _wpsf__( 'Sorry, Admin Notes is only available for Shield Pro.' );
108
+ }
109
+ else if ( empty( $sNote ) ) {
110
+ $sMessage = _wpsf__( 'Sorry, but it appears your note was empty.' );
111
+ }
112
+ else {
113
+ /** @var ICWP_WPSF_Processor_Plugin $oP */
114
+ $oP = $this->getConn()->getModule( 'plugin' )->getProcessor();
115
+ $bSuccess = $oP->getSubProcessorNotes()
116
+ ->getQueryCreator()
117
+ ->create( $sNote ) !== false;
118
+
119
+ $sMessage = $bSuccess ? _wpsf__( 'Note created successfully.' ) : _wpsf__( 'Note could not be created.' );
120
+ }
121
+ return array(
122
+ 'success' => $bSuccess,
123
+ 'message' => $sMessage
124
+ );
125
+ }
126
+
127
+ /**
128
+ * @return array
129
+ */
130
+ protected function ajaxExec_AdminNotesRender() {
131
+ $oDP = $this->loadDP();
132
+ /** @var ICWP_WPSF_FeatureHandler_Plugin $oMod */
133
+ $oMod = $this->getConn()->getModule( 'plugin' );
134
+ $sNote = trim( $oDP->post( 'admin_note', '' ) );
135
+
136
+ $aNotes = $this->getNotes();
137
+ $sHtml = $this->renderTemplate(
138
+ '/wpadmin_pages/insights/admin_notes_table.twig',
139
+ array(
140
+ 'vars' => array(
141
+ 'insight_notes' => $aNotes,
142
+ ),
143
+ 'flags' => array(
144
+ 'has_notes' => count( $aNotes ) > 0,
145
+ 'can_notes' => $this->isPremium() //not the way to determine
146
+ ),
147
+ ),
148
+ true
149
+ );
150
+
151
+ $bSuccess = true;
152
+ return array(
153
+ 'success' => $bSuccess,
154
+ 'html' => $sHtml
155
+ );
156
+ }
157
+
158
  /**
159
  * @return array
160
  */
186
  * @return string[]
187
  */
188
  protected function getNotices() {
189
+
190
+ $aAll = apply_filters(
191
+ $this->prefix( 'collect_notices' ),
192
+ array(
193
+ 'plugins' => $this->getNoticesPlugins(),
194
+ 'themes' => $this->getNoticesThemes(),
195
+ 'core' => $this->getNoticesCore(),
196
+ )
197
+ );
198
+
199
+ // order and then remove empties
200
+ return array_filter(
201
+ array_merge(
202
+ array(
203
+ 'site' => array(),
204
+ 'sec_admin' => array(),
205
+ 'scans' => array(),
206
+ 'core' => array(),
207
+ 'plugins' => array(),
208
+ 'themes' => array(),
209
+ 'users' => array(),
210
+ 'lockdown' => array(),
211
+ ),
212
+ $aAll
213
+ )
214
  );
215
  }
216
 
271
  }
272
  }
273
 
274
+ { // db password strength
275
+ $nStrength = ( new \ZxcvbnPhp\Zxcvbn() )->passwordStrength( DB_PASSWORD )[ 'score' ];
276
+ if ( $nStrength < 4 ) {
277
+ $aNotices[ 'messages' ][ 'db_strength' ] = array(
278
+ 'title' => 'DB Password',
279
+ 'message' => _wpsf__( 'DB Password appears to be weak.' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  'href' => '',
281
+ 'rec' => _wpsf__( 'The database password should be strong.' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  );
283
  }
284
  }
297
  'messages' => array()
298
  );
299
 
300
+ {// Inactive
 
301
  $nCount = 0;
302
  $aActivePlugs = $oWpPlugins->getActivePlugins();
303
  foreach ( $oWpPlugins->getPlugins() as $sFile => $aPlugData ) {
316
  }
317
  }
318
 
319
+ {// updates
 
320
  $nCount = count( $oWpPlugins->getUpdates() );
321
  if ( $nCount > 0 ) {
322
  $aNotices[ 'messages' ][ 'updates' ] = array(
343
  'messages' => array()
344
  );
345
 
346
+ {// Inactive
347
+ $nInactive = count( $oWpT->getThemes() ) - ( $oWpT->isActiveThemeAChild() ? 2 : 1 );
 
348
  if ( $nInactive > 0 ) {
349
  $aNotices[ 'messages' ][ 'inactive' ] = array(
350
  'title' => 'Inactive',
356
  }
357
  }
358
 
359
+ {// updates
 
360
  $nCount = count( $oWpT->getUpdates() );
361
  if ( $nCount > 0 ) {
362
  $aNotices[ 'messages' ][ 'updates' ] = array(
383
  'messages' => array()
384
  );
385
 
386
+ {// updates
 
387
  if ( $oWp->hasCoreUpdate() ) {
388
  $aNotices[ 'messages' ][ 'updates' ] = array(
389
  'title' => 'Updates',
395
  }
396
  }
397
 
398
+ {// autoupdates
 
399
  if ( !$oWp->canCoreUpdateAutomatically() ) {
400
  $aNotices[ 'messages' ][ 'updates_auto' ] = array(
401
  'title' => 'Auto Updates',
407
  }
408
  }
409
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
411
  return $aNotices;
412
  }
414
  /**
415
  * @return array
416
  */
417
+ protected function getNotes() {
418
+ /** @var ICWP_WPSF_Processor_Plugin $oProc */
419
+ $oProc = $this->getConn()->getModule( 'plugin' )->getProcessor();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
+ $oRetriever = $oProc->getSubProcessorNotes()
422
+ ->getQueryRetriever();
423
+ $aNotes = $oRetriever->setLimit( 10 )
424
+ ->setResultsAsVo( false )
425
+ ->all();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
+ $oWP = $this->loadWp();
428
+ foreach ( $aNotes as $oItem ) {
429
+ $oItem->created_at = $oWP->getTimeStringForDisplay( $oItem->created_at );
430
+ $oItem->note = stripslashes( sanitize_text_field( $oItem->note ) );
431
+ $oItem->wp_username = sanitize_text_field( $oItem->wp_username );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  }
433
 
434
+ return $aNotes;
 
435
  }
436
 
437
  /**
541
  return $aItems;
542
  }
543
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
  /**
545
  * @return string[]
546
  */
src/features/lockdown.php CHANGED
@@ -50,6 +50,34 @@ class ICWP_WPSF_FeatureHandler_Lockdown extends ICWP_WPSF_FeatureHandler_BaseWps
50
  return parent::isReadyToExecute() && !$this->isVisitorWhitelisted();
51
  }
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  /**
54
  * @param array $aOptionsParams
55
  * @return array
50
  return parent::isReadyToExecute() && !$this->isVisitorWhitelisted();
51
  }
52
 
53
+ /**
54
+ * @param array $aAllNotices
55
+ * @return array
56
+ */
57
+ public function addInsightsNoticeData( $aAllNotices ) {
58
+ $aNotices = array(
59
+ 'title' => _wpsf__( 'Lockdown' ),
60
+ 'messages' => array()
61
+ );
62
+
63
+ { //edit plugins
64
+ if ( current_user_can( 'edit_plugins' ) ) { //assumes current user is admin
65
+ $aNotices[ 'messages' ][ 'disallow_file_edit' ] = array(
66
+ 'title' => 'Code Editor',
67
+ 'message' => _wpsf__( 'Direct editing of plugin/theme files is permitted.' ),
68
+ 'href' => $this->getUrl_AdminPage(),
69
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
70
+ 'rec' => _wpsf__( 'WP Plugin file editing should be disabled.' )
71
+ );
72
+ }
73
+ }
74
+
75
+ $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
76
+
77
+ $aAllNotices[ 'lockdown' ] = $aNotices;
78
+ return $aAllNotices;
79
+ }
80
+
81
  /**
82
  * @param array $aOptionsParams
83
  * @return array
src/features/plugin.php CHANGED
@@ -671,6 +671,20 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
671
  return $this;
672
  }
673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  /**
675
  * @param array $aOptionsParams
676
  * @return array
@@ -749,6 +763,12 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
749
  ->getHumanName() );
750
  break;
751
 
 
 
 
 
 
 
752
  case 'enable_tracking' :
753
  $sName = sprintf( _wpsf__( 'Enable %s Module' ), _wpsf__( 'Information Gathering' ) );
754
  $sSummary = _wpsf__( 'Permit Anonymous Usage Information Gathering' );
671
  return $this;
672
  }
673
 
674
+ /**
675
+ * @return bool
676
+ */
677
+ public function getCanAdminNotes() {
678
+ return true||$this->isPremium();
679
+ }
680
+
681
+ /**
682
+ * @return string
683
+ */
684
+ public function getDbNameNotes() {
685
+ return $this->prefixOptionKey( $this->getDef( 'db_notes_name' ) );
686
+ }
687
+
688
  /**
689
  * @param array $aOptionsParams
690
  * @return array
763
  ->getHumanName() );
764
  break;
765
 
766
+ case 'enable_notes' :
767
+ $sName = sprintf( _wpsf__( 'Enable %s' ), _wpsf__( 'Admin Notes' ) );
768
+ $sSummary = _wpsf__( 'Turn-On Admin Notes Section In Insights Dashboard' );
769
+ $sDescription = _wpsf__( 'When turned-on it enables administrators to enter custom notes in the Insights Dashboard.' );
770
+ break;
771
+
772
  case 'enable_tracking' :
773
  $sName = sprintf( _wpsf__( 'Enable %s Module' ), _wpsf__( 'Information Gathering' ) );
774
  $sSummary = _wpsf__( 'Permit Anonymous Usage Information Gathering' );
src/features/user_management.php CHANGED
@@ -41,7 +41,8 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
41
 
42
  return array(
43
  'strings' => $this->getDisplayStrings(),
44
- 'time_now' => sprintf( _wpsf__( 'now: %s' ), date_i18n( $sTimeFormat.' '.$sDateFormat, $this->loadDP()->time() ) ),
 
45
  'sUserSessionsTable' => $sUserSessionsTable
46
  );
47
  }
@@ -203,6 +204,48 @@ class ICWP_WPSF_FeatureHandler_UserManagement extends ICWP_WPSF_FeatureHandler_B
203
  return $this->getOptIs( 'pass_prevent_pwned', 'Y' );
204
  }
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  /**
207
  * @param array $aOptionsParams
208
  * @return array
41
 
42
  return array(
43
  'strings' => $this->getDisplayStrings(),
44
+ 'time_now' => sprintf( _wpsf__( 'now: %s' ), date_i18n( $sTimeFormat.' '.$sDateFormat, $this->loadDP()
45
+ ->time() ) ),
46
  'sUserSessionsTable' => $sUserSessionsTable
47
  );
48
  }
204
  return $this->getOptIs( 'pass_prevent_pwned', 'Y' );
205
  }
206
 
207
+ /**
208
+ * @param array $aAllNotices
209
+ * @return array
210
+ */
211
+ public function addInsightsNoticeData( $aAllNotices ) {
212
+ $oWpUsers = $this->loadWpUsers();
213
+
214
+ $aNotices = array(
215
+ 'title' => _wpsf__( 'Users' ),
216
+ 'messages' => array()
217
+ );
218
+
219
+ { //admin user
220
+ $oAdmin = $oWpUsers->getUserByUsername( 'admin' );
221
+ if ( !empty( $oAdmin ) && user_can( $oAdmin, 'manage_options' ) ) {
222
+ $aNotices[ 'messages' ][ 'admin' ] = array(
223
+ 'title' => 'Admin User',
224
+ 'message' => sprintf( _wpsf__( "Default 'admin' user still available." ) ),
225
+ 'href' => '',
226
+ 'rec' => _wpsf__( "Default 'admin' user should be disabled or removed." )
227
+ );
228
+ }
229
+ }
230
+
231
+ {//password policies
232
+ if ( !$this->isPasswordPoliciesEnabled() ) {
233
+ $aNotices[ 'messages' ][ 'password' ] = array(
234
+ 'title' => 'Password Policies',
235
+ 'message' => _wpsf__( "Strong password policies are not enforced." ),
236
+ 'href' => $this->getUrl_AdminPage(),
237
+ 'action' => sprintf( 'Go To %s', _wpsf__( 'Options' ) ),
238
+ 'rec' => _wpsf__( 'Password policies should be turned-on.' )
239
+ );
240
+ }
241
+ }
242
+
243
+ $aNotices[ 'count' ] = count( $aNotices[ 'messages' ] );
244
+
245
+ $aAllNotices[ 'users' ] = $aNotices;
246
+ return $aAllNotices;
247
+ }
248
+
249
  /**
250
  * @param array $aOptionsParams
251
  * @return array
src/processors/basedb.php CHANGED
@@ -293,4 +293,11 @@ abstract class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
293
  protected function getAutoExpirePeriod() {
294
  return null;
295
  }
 
 
 
 
 
 
 
296
  }
293
  protected function getAutoExpirePeriod() {
294
  return null;
295
  }
296
+
297
+ /**
298
+ * @return string
299
+ */
300
+ protected function getQueryDir() {
301
+ return dirname( dirname( __FILE__ ) ).'/query/';
302
+ }
303
  }
src/processors/plugin.php CHANGED
@@ -96,6 +96,19 @@ class ICWP_WPSF_Processor_Plugin extends ICWP_WPSF_Processor_BasePlugin {
96
  return $oProc;
97
  }
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  /**
100
  */
101
  public function dumpTrackingData() {
96
  return $oProc;
97
  }
98
 
99
+ /**
100
+ * @return ICWP_WPSF_Processor_Plugin_Notes
101
+ */
102
+ public function getSubProcessorNotes() {
103
+ $oProc = $this->getSubProcessor( 'notes' );
104
+ if ( is_null( $oProc ) ) {
105
+ require_once( dirname( __FILE__ ).'/plugin_notes.php' );
106
+ $oProc = new ICWP_WPSF_Processor_Plugin_Notes( $this->getFeature() );
107
+ $this->aSubProcessors[ 'notes' ] = $oProc;
108
+ }
109
+ return $oProc;
110
+ }
111
+
112
  /**
113
  */
114
  public function dumpTrackingData() {
src/processors/plugin_notes.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( class_exists( 'ICWP_WPSF_Processor_Plugin_Notes' ) ) {
4
+ return;
5
+ }
6
+
7
+ require_once( dirname( __FILE__ ).'/basedb.php' );
8
+
9
+ class ICWP_WPSF_Processor_Plugin_Notes extends ICWP_WPSF_BaseDbProcessor {
10
+
11
+ /**
12
+ * @param ICWP_WPSF_FeatureHandler_Plugin $oFeatureOptions
13
+ * @throws Exception
14
+ */
15
+ public function __construct( ICWP_WPSF_FeatureHandler_Plugin $oFeatureOptions ) {
16
+ parent::__construct( $oFeatureOptions, $oFeatureOptions->getDbNameNotes() );
17
+ }
18
+
19
+ public function run() {
20
+ }
21
+
22
+ /**
23
+ * @return string
24
+ */
25
+ public function getCreateTableSql() {
26
+ $sSqlTables = "CREATE TABLE %s (
27
+ id int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
28
+ wp_username varchar(255) NOT NULL DEFAULT 'unknown',
29
+ note TEXT,
30
+ created_at int(15) UNSIGNED NOT NULL DEFAULT 0,
31
+ deleted_at int(15) UNSIGNED NOT NULL DEFAULT 0,
32
+ PRIMARY KEY (id)
33
+ ) %s;";
34
+ return sprintf( $sSqlTables, $this->getTableName(), $this->loadDbProcessor()->getCharCollate() );
35
+ }
36
+
37
+ /**
38
+ * @return array
39
+ */
40
+ protected function getTableColumnsByDefinition() {
41
+ $aDef = $this->getFeature()->getDef( 'db_notes_table_columns' );
42
+ return ( is_array( $aDef ) ? $aDef : array() );
43
+ }
44
+
45
+ /**
46
+ * @param string $nId
47
+ * @return bool|int
48
+ */
49
+ public function deleteNote( $nId ) {
50
+ return $this->getQueryDeleter()->delete( $nId );
51
+ }
52
+
53
+ /**
54
+ * @param string $sNote
55
+ * @return bool|int
56
+ */
57
+ public function insertNote( $sNote ) {
58
+ return $this->getQueryCreator()->create( $sNote );
59
+ }
60
+
61
+ /**
62
+ * @return ICWP_WPSF_Query_PluginNotes_Create
63
+ */
64
+ public function getQueryCreator() {
65
+ require_once( $this->getQueryDir().'plugin_notes_create.php' );
66
+ $oQ = new ICWP_WPSF_Query_PluginNotes_Create();
67
+ return $oQ->setTable( $this->getTableName() );
68
+ }
69
+
70
+ /**
71
+ * @return ICWP_WPSF_Query_PluginNotes_Delete
72
+ */
73
+ public function getQueryDeleter() {
74
+ require_once( $this->getQueryDir().'plugin_notes_delete.php' );
75
+ $oQ = new ICWP_WPSF_Query_PluginNotes_Delete();
76
+ return $oQ->setTable( $this->getTableName() );
77
+ }
78
+
79
+ /**
80
+ * @return ICWP_WPSF_Query_PluginNotes_Retrieve
81
+ */
82
+ public function getQueryRetriever() {
83
+ require_once( $this->getQueryDir().'plugin_notes_retrieve.php' );
84
+ $oQ = new ICWP_WPSF_Query_PluginNotes_Retrieve();
85
+ return $oQ->setTable( $this->getTableName() );
86
+ }
87
+ }
src/processors/sessions.php CHANGED
@@ -262,7 +262,7 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
262
  * @return ICWP_WPSF_Query_Sessions_Retrieve
263
  */
264
  public function getSessionRetriever() {
265
- require_once( dirname( dirname( __FILE__ ) ).'/query/sessions_retrieve.php' );
266
  $oRetrieve = new ICWP_WPSF_Query_Sessions_Retrieve();
267
  return $oRetrieve->setTable( $this->getTableName() );
268
  }
@@ -271,7 +271,7 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
271
  * @return ICWP_WPSF_Query_Sessions_Update
272
  */
273
  public function getSessionUpdater() {
274
- require_once( dirname( dirname( __FILE__ ) ).'/query/sessions_update.php' );
275
  $oUpdate = new ICWP_WPSF_Query_Sessions_Update();
276
  return $oUpdate->setTable( $this->getTableName() );
277
  }
262
  * @return ICWP_WPSF_Query_Sessions_Retrieve
263
  */
264
  public function getSessionRetriever() {
265
+ require_once( $this->getQueryDir().'sessions_retrieve.php' );
266
  $oRetrieve = new ICWP_WPSF_Query_Sessions_Retrieve();
267
  return $oRetrieve->setTable( $this->getTableName() );
268
  }
271
  * @return ICWP_WPSF_Query_Sessions_Update
272
  */
273
  public function getSessionUpdater() {
274
+ require_once( $this->getQueryDir().'sessions_update.php' );
275
  $oUpdate = new ICWP_WPSF_Query_Sessions_Update();
276
  return $oUpdate->setTable( $this->getTableName() );
277
  }
src/query/ICWP_WPSF_NoteVO.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ICWP_WPSF_NoteVO {
4
+
5
+ /**
6
+ * @var stdClass
7
+ */
8
+ protected $oRowData;
9
+
10
+ /**
11
+ * @param stdClass $oRowData
12
+ */
13
+ public function __construct( $oRowData ) {
14
+ $this->oRowData = $oRowData;
15
+ }
16
+
17
+ /**
18
+ * @return int
19
+ */
20
+ public function getCreatedAt() {
21
+ return $this->getRowData()->created_at;
22
+ }
23
+
24
+ /**
25
+ * @return int
26
+ */
27
+ public function getId() {
28
+ return $this->getRowData()->id;
29
+ }
30
+
31
+ /**
32
+ * @return string
33
+ */
34
+ public function getNote() {
35
+ return $this->getRowData()->note;
36
+ }
37
+
38
+ /**
39
+ * @return int
40
+ */
41
+ public function getUsername() {
42
+ return $this->getRowData()->wp_username;
43
+ }
44
+
45
+ /**
46
+ * @return int
47
+ */
48
+ public function isDeleted() {
49
+ return $this->getRowData()->deleted_at > 0;
50
+ }
51
+
52
+ /**
53
+ * @return stdClass
54
+ */
55
+ public function getRowData() {
56
+ return $this->oRowData;
57
+ }
58
+
59
+ /**
60
+ * @param stdClass $oRowData
61
+ * @return $this
62
+ */
63
+ public function setRowData( $oRowData ) {
64
+ $this->oRowData = $oRowData;
65
+ return $this;
66
+ }
67
+ }
src/query/base.php CHANGED
@@ -6,11 +6,28 @@ if ( class_exists( 'ICWP_WPSF_Query_Base', false ) ) {
6
 
7
  class ICWP_WPSF_Query_Base extends ICWP_WPSF_Foundation {
8
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * @var string
11
  */
12
  protected $sTable;
13
 
 
 
 
 
 
 
 
14
  /**
15
  * @return string
16
  */
@@ -18,6 +35,38 @@ class ICWP_WPSF_Query_Base extends ICWP_WPSF_Foundation {
18
  return $this->sTable;
19
  }
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  /**
22
  * @param string $sTable
23
  * @return $this
6
 
7
  class ICWP_WPSF_Query_Base extends ICWP_WPSF_Foundation {
8
 
9
+ /**
10
+ * @var bool
11
+ */
12
+ protected $bResultsAsVo;
13
+
14
+ /**
15
+ * @var int
16
+ */
17
+ protected $nLimit = 0;
18
+
19
  /**
20
  * @var string
21
  */
22
  protected $sTable;
23
 
24
+ /**
25
+ * @return int
26
+ */
27
+ public function getLimit() {
28
+ return max( (int)$this->nLimit, 0 );
29
+ }
30
+
31
  /**
32
  * @return string
33
  */
35
  return $this->sTable;
36
  }
37
 
38
+ /**
39
+ * @return bool
40
+ */
41
+ public function hasLimit() {
42
+ return $this->getLimit() > 0;
43
+ }
44
+
45
+ /**
46
+ * @return bool
47
+ */
48
+ public function isResultsAsVo() {
49
+ return $this->bResultsAsVo;
50
+ }
51
+
52
+ /**
53
+ * @param int $nLimit
54
+ * @return $this
55
+ */
56
+ public function setLimit( $nLimit ) {
57
+ $this->nLimit = $nLimit;
58
+ return $this;
59
+ }
60
+
61
+ /**
62
+ * @param bool $bResultsAsVo
63
+ * @return $this
64
+ */
65
+ public function setResultsAsVo( $bResultsAsVo ) {
66
+ $this->bResultsAsVo = $bResultsAsVo;
67
+ return $this;
68
+ }
69
+
70
  /**
71
  * @param string $sTable
72
  * @return $this
src/query/base_find.php CHANGED
@@ -8,11 +8,6 @@ require_once( dirname( __FILE__ ).'/base.php' );
8
 
9
  class ICWP_WPSF_Query_Base_Find extends ICWP_WPSF_Query_Base {
10
 
11
- /**
12
- * @var int
13
- */
14
- protected $nLimit = 0;
15
-
16
  /**
17
  * @var string
18
  */
@@ -35,42 +30,28 @@ class ICWP_WPSF_Query_Base_Find extends ICWP_WPSF_Query_Base {
35
  return (string)$this->sTerm;
36
  }
37
 
38
- /**
39
- * @return int
40
- */
41
- public function getLimit() {
42
- return max( (int)$this->nLimit, 0 );
43
- }
44
-
45
- /**
46
- * @return bool
47
- */
48
- public function hasLimit() {
49
- return $this->getLimit() > 0;
50
- }
51
-
52
  /**
53
  * @return array
54
  */
55
  public function getColumns() {
56
  if ( empty( $this->aColumns ) || !is_array( $this->aColumns ) ) {
57
- $this->aColumns = array( 'wp_username', 'message' );
58
  }
59
  return $this->aColumns;
60
  }
61
 
62
  /**
63
- * @return bool
64
  */
65
- public function hasSearchTerm() {
66
- return strlen( $this->getTerm() ) > 0;
67
  }
68
 
69
  /**
70
  * @return bool
71
  */
72
- public function isResultsAsVo() {
73
- return $this->bResultsAsVo;
74
  }
75
 
76
  /**
@@ -82,15 +63,6 @@ class ICWP_WPSF_Query_Base_Find extends ICWP_WPSF_Query_Base {
82
  return $this;
83
  }
84
 
85
- /**
86
- * @param bool $bResultsAsVo
87
- * @return $this
88
- */
89
- public function setResultsAsVo( $bResultsAsVo ) {
90
- $this->bResultsAsVo = $bResultsAsVo;
91
- return $this;
92
- }
93
-
94
  /**
95
  * @param string $sTerm
96
  * @return $this
@@ -99,13 +71,4 @@ class ICWP_WPSF_Query_Base_Find extends ICWP_WPSF_Query_Base {
99
  $this->sTerm = $sTerm;
100
  return $this;
101
  }
102
-
103
- /**
104
- * @param int $nLimit
105
- * @return $this
106
- */
107
- public function setLimit( $nLimit ) {
108
- $this->nLimit = $nLimit;
109
- return $this;
110
- }
111
  }
8
 
9
  class ICWP_WPSF_Query_Base_Find extends ICWP_WPSF_Query_Base {
10
 
 
 
 
 
 
11
  /**
12
  * @var string
13
  */
30
  return (string)$this->sTerm;
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  /**
34
  * @return array
35
  */
36
  public function getColumns() {
37
  if ( empty( $this->aColumns ) || !is_array( $this->aColumns ) ) {
38
+ $this->aColumns = $this->getDefaultColumns();
39
  }
40
  return $this->aColumns;
41
  }
42
 
43
  /**
44
+ * @return array
45
  */
46
+ protected function getDefaultColumns() {
47
+ return array( 'wp_username', 'message' );
48
  }
49
 
50
  /**
51
  * @return bool
52
  */
53
+ public function hasSearchTerm() {
54
+ return strlen( $this->getTerm() ) > 0;
55
  }
56
 
57
  /**
63
  return $this;
64
  }
65
 
 
 
 
 
 
 
 
 
 
66
  /**
67
  * @param string $sTerm
68
  * @return $this
71
  $this->sTerm = $sTerm;
72
  return $this;
73
  }
 
 
 
 
 
 
 
 
 
74
  }
src/query/plugin_notes_create.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( class_exists( 'ICWP_WPSF_Query_PluginNotes_Create', false ) ) {
4
+ return;
5
+ }
6
+
7
+ require_once( dirname( __FILE__ ).'/base.php' );
8
+
9
+ class ICWP_WPSF_Query_PluginNotes_Create extends ICWP_WPSF_Query_Base {
10
+
11
+ /**
12
+ * @param string $sNote
13
+ * @return bool|int
14
+ */
15
+ public function create( $sNote ) {
16
+
17
+ $oUser = $this->loadWpUsers()->getCurrentWpUser();
18
+ $aNewData = array(
19
+ 'wp_username' => ( $oUser instanceof WP_User ) ? $oUser->user_login : 'unknown',
20
+ 'note' => esc_sql( $sNote ),
21
+ 'created_at' => $this->loadDP()->time(),
22
+ );
23
+ $mResult = $this->loadDbProcessor()
24
+ ->insertDataIntoTable( $this->getTable(), $aNewData );
25
+ return ( $mResult === false ) ? false : $mResult > 0;
26
+ }
27
+ }
src/query/plugin_notes_delete.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( class_exists( 'ICWP_WPSF_Query_PluginNotes_Delete', false ) ) {
4
+ return;
5
+ }
6
+
7
+ require_once( dirname( __FILE__ ).'/base.php' );
8
+
9
+ class ICWP_WPSF_Query_PluginNotes_Delete extends ICWP_WPSF_Query_Base {
10
+
11
+ /**
12
+ * @param int $nId
13
+ * @return bool|int
14
+ */
15
+ public function delete( $nId ) {
16
+ return $this->loadDbProcessor()
17
+ ->deleteRowsFromTableWhere( $this->getTable(), array( 'id' => (int)$nId ) );
18
+ }
19
+ }
src/query/plugin_notes_find.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( class_exists( 'ICWP_WPSF_Query_PluginNotes_Find', false ) ) {
4
+ return;
5
+ }
6
+
7
+ require_once( dirname( __FILE__ ).'/base_find.php' );
8
+
9
+ class ICWP_WPSF_Query_PluginNotes_Find extends ICWP_WPSF_Query_Base_Find {
10
+
11
+ public function __construct() {
12
+ $this->init();
13
+ }
14
+
15
+ /**
16
+ * @return ICWP_WPSF_NoteVO[]|stdClass[]
17
+ */
18
+ public function all() {
19
+ return $this->query_Search();
20
+ }
21
+
22
+ /**
23
+ * @return ICWP_WPSF_NoteVO[]|stdClass[]
24
+ */
25
+ protected function query_Search() {
26
+
27
+ $sQuery = "
28
+ SELECT *
29
+ FROM `%s`
30
+ %s
31
+ ORDER BY `created_at` DESC
32
+ %s
33
+ ";
34
+ $sQuery = sprintf(
35
+ $sQuery,
36
+ $this->getTable(),
37
+ $this->buildWherePhrase(),
38
+ $this->hasLimit() ? sprintf( 'LIMIT %s', $this->getLimit() ) : ''
39
+ );
40
+
41
+ $aData = $this->loadDbProcessor()
42
+ ->selectCustom( $sQuery, OBJECT_K );
43
+
44
+ if ( $this->isResultsAsVo() ) {
45
+ foreach ( $aData as $nKey => $oResult ) {
46
+ $aData[ $nKey ] = new ICWP_WPSF_NoteVO( $oResult );
47
+ }
48
+ }
49
+ return $aData;
50
+ }
51
+
52
+ /**
53
+ * @return string
54
+ */
55
+ protected function buildWherePhrase() {
56
+ $sPhrase = '';
57
+
58
+ if ( $this->hasSearchTerm() ) {
59
+ $sTerm = str_replace( '"', '', esc_sql( trim( $this->getTerm() ) ) );
60
+
61
+ $sWhereTemplate = '`%s` LIKE "%%%s%%"';
62
+ $aColumnWheres = $this->getColumns();
63
+ foreach ( $aColumnWheres as $nKey => $sColumn ) {
64
+ $aColumnWheres[ $nKey ] = sprintf( $sWhereTemplate, $sColumn, $sTerm );
65
+ }
66
+ $sPhrase = sprintf( 'WHERE %s', implode( ' OR ', $aColumnWheres ) );
67
+ }
68
+
69
+ return $sPhrase;
70
+ }
71
+
72
+ /**
73
+ * @return array
74
+ */
75
+ protected function getDefaultColumns() {
76
+ return array( 'wp_username', 'note' );
77
+ }
78
+
79
+ protected function init() {
80
+ require_once( dirname( __FILE__ ).'/ICWP_WPSF_NoteVO.php' );
81
+ }
82
+ }
src/query/plugin_notes_retrieve.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( class_exists( 'ICWP_WPSF_Query_PluginNotes_Retrieve', false ) ) {
4
+ return;
5
+ }
6
+
7
+ require_once( dirname( __FILE__ ).'/base.php' );
8
+
9
+ class ICWP_WPSF_Query_PluginNotes_Retrieve extends ICWP_WPSF_Query_Base {
10
+
11
+ public function __construct() {
12
+ $this->init();
13
+ }
14
+
15
+ /**
16
+ * @return ICWP_WPSF_NoteVO[]|stdClass[]
17
+ */
18
+ public function all() {
19
+ return $this->query_retrieve();
20
+ }
21
+
22
+ /**
23
+ * @param int $nId
24
+ * @return ICWP_WPSF_NoteVO[]|stdClass[]
25
+ */
26
+ public function retrieveById( $nId ) {
27
+ return $this->query_retrieve( $nId );
28
+ }
29
+
30
+ /**
31
+ * @param int $nId
32
+ * @return ICWP_WPSF_NoteVO[]|stdClass[]
33
+ */
34
+ protected function query_retrieve( $nId = null ) {
35
+ $sQuery = "
36
+ SELECT *
37
+ FROM `%s`
38
+ WHERE
39
+ `deleted_at` = 0
40
+ %s
41
+ ORDER BY `created_at` DESC
42
+ %s
43
+ ";
44
+ $sQuery = sprintf(
45
+ $sQuery,
46
+ $this->getTable(),
47
+ is_null( $nId ) ? '' : "AND `id` = ".esc_sql( (int)$nId ),
48
+ $this->hasLimit() ? sprintf( 'LIMIT %s', $this->getLimit() ) : ''
49
+ );
50
+
51
+ $aData = $this->loadDbProcessor()
52
+ ->selectCustom( $sQuery, OBJECT_K );
53
+
54
+ if ( $this->isResultsAsVo() ) {
55
+ foreach ( $aData as $nKey => $oResult ) {
56
+ $aData[ $nKey ] = new ICWP_WPSF_NoteVO( $oResult );
57
+ }
58
+ }
59
+ return $aData;
60
+ }
61
+
62
+ protected function init() {
63
+ require_once( dirname( __FILE__ ).'/ICWP_WPSF_NoteVO.php' );
64
+ }
65
+ }
templates/twig/wpadmin_pages/insights/admin_notes.twig ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="SectionAdminNotes" class="insights_widget card w-100 p-0">
2
+ <div class="card-header">
3
+ <h5 class="card-title">Administrator Notes</h5>
4
+ <h6 class="card-subtitle mb-2 text-muted">All your admin notes</h6>
5
+ </div>
6
+ <div class="card-body overflow_container">
7
+
8
+ <div id="AdminNotesContainer" class="overflow_inner p-0">
9
+ {% include '/wpadmin_pages/insights/admin_notes_table.twig' %}
10
+ </div>
11
+
12
+ </div>
13
+ <div class="card-footer p-0" style="margin-bottom: -1px;">
14
+ <form class="form-inline" id="NewAdminNote" action="#" method="post">
15
+ {% for name,val in ajax.admin_note_new %}
16
+ <input type="hidden" name="{{ name }}" value="{{ val }}" />
17
+ {% endfor %}
18
+ <label class="sr-only" for="AdminNote">Note</label>
19
+ <div class="input-group w-100">
20
+ <div class="input-group-prepend">
21
+ <div class="input-group-text" style="border-radius: 0">New Note:</div>
22
+ </div>
23
+ <textarea class="form-control" id="AdminNote" name="admin_note" rows="2"
24
+ placeholder="{% if flags.can_notes %}Enter new note here{% else %}Sorry, Admin Notes is a Pro-only feature{% endif %}"
25
+ ></textarea>
26
+ <div class="input-group-append">
27
+ <button type="submit" class="btn btn-info" style="border-radius: 0;" name="subbutt"
28
+ {% if not flags.can_notes %}disabled="disabled"{% endif %}
29
+ >Add Note</button>
30
+ </div>
31
+ </div>
32
+ </form>
33
+ </div>
34
+ </div>
templates/twig/wpadmin_pages/insights/admin_notes_table.twig ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if flags.has_notes %}
2
+ <table class="table table-hover mb-0">
3
+ <thead><tr>
4
+ <th>Note</th>
5
+ <th>User</th>
6
+ <th>Date</th>
7
+ </tr></thead>
8
+ {% for note in vars.insight_notes %}
9
+ <tr class="message_row">
10
+ <td>{{ note.note }}</td>
11
+ <td>{{ note.wp_username }}</td>
12
+ <td>{{ note.created_at }}</td>
13
+ </tr>
14
+ {% endfor %}
15
+ </table>
16
+ {% else %}
17
+ <div class="alert alert-info">There are no admin notes.</div>
18
+ {% endif %}
templates/twig/wpadmin_pages/insights/index.twig CHANGED
@@ -7,10 +7,10 @@
7
 
8
  {% block body_main %}
9
  <div class="row">
10
- <div class="col-lg-6">
11
  {% include '/wpadmin_pages/insights/title.twig' %}
12
  </div>
13
- <div class="col-lg-6">
14
  {% include '/wpadmin_pages/insights/stats.twig' %}
15
  </div>
16
  </div>
@@ -18,13 +18,20 @@
18
  <div class="col-lg-7">
19
  {% include '/wpadmin_pages/insights/notices.twig' %}
20
  </div>
 
 
 
 
 
 
 
 
21
  <div class="col-lg-5">
22
  {% include '/wpadmin_pages/insights/recent_events.twig' %}
23
  </div>
24
  </div>
25
  <div class="row">
26
  <div class="col">
27
- {% include '/wpadmin_pages/insights/audit_trail.twig' %}
28
  </div>
29
  </div>
30
  {% endblock %}
@@ -60,22 +67,23 @@
60
  .overflow_inner dl {
61
  margin-bottom: 1px;
62
  }
63
-
64
  .title_row th,
65
  .title_row td {
66
  background-color: #fbfbfb;
67
  }
68
-
69
  .message_row th,
70
  .message_row td {
71
  padding-bottom: 5px;
72
  border-top: 1px solid #e9edf1; /** make bootstrap border lighter */
73
  }
74
-
75
  .icon-sign {
76
  font-size: 14px;
77
  }
78
 
 
 
 
 
79
  #SectionAuditTrail table {
80
  font-size: 12px;
81
  }
@@ -88,7 +96,7 @@
88
  #odp-BodyFoot {
89
  position: sticky;
90
  bottom: 0;
91
- z-index: 1;
92
  margin-top: 30px;
93
  }
94
  #SectionStats {
7
 
8
  {% block body_main %}
9
  <div class="row">
10
+ <div class="col-6">
11
  {% include '/wpadmin_pages/insights/title.twig' %}
12
  </div>
13
+ <div class="col-6">
14
  {% include '/wpadmin_pages/insights/stats.twig' %}
15
  </div>
16
  </div>
18
  <div class="col-lg-7">
19
  {% include '/wpadmin_pages/insights/notices.twig' %}
20
  </div>
21
+ <div class="col-lg-5">
22
+ {% include '/wpadmin_pages/insights/admin_notes.twig' %}
23
+ </div>
24
+ </div>
25
+ <div class="row">
26
+ <div class="col-lg-7">
27
+ {% include '/wpadmin_pages/insights/audit_trail.twig' %}
28
+ </div>
29
  <div class="col-lg-5">
30
  {% include '/wpadmin_pages/insights/recent_events.twig' %}
31
  </div>
32
  </div>
33
  <div class="row">
34
  <div class="col">
 
35
  </div>
36
  </div>
37
  {% endblock %}
67
  .overflow_inner dl {
68
  margin-bottom: 1px;
69
  }
 
70
  .title_row th,
71
  .title_row td {
72
  background-color: #fbfbfb;
73
  }
 
74
  .message_row th,
75
  .message_row td {
76
  padding-bottom: 5px;
77
  border-top: 1px solid #e9edf1; /** make bootstrap border lighter */
78
  }
 
79
  .icon-sign {
80
  font-size: 14px;
81
  }
82
 
83
+ #NewAdminNote textarea {
84
+ height: 64px;
85
+ }
86
+
87
  #SectionAuditTrail table {
88
  font-size: 12px;
89
  }
96
  #odp-BodyFoot {
97
  position: sticky;
98
  bottom: 0;
99
+ z-index: 2;
100
  margin-top: 30px;
101
  }
102
  #SectionStats {
templates/twig/wpadmin_pages/insights/stats.twig CHANGED
@@ -1,6 +1,6 @@
1
  <div class="row" id="SectionStats">
2
  {% for stat in vars.insight_stats %}
3
- <div class="col-sm-3">
4
  <div class="stat card"
5
  {% if stat.tooltip is defined %}
6
  title="{{ stat.tooltip }}"
1
  <div class="row" id="SectionStats">
2
  {% for stat in vars.insight_stats %}
3
+ <div class="col-md-3">
4
  <div class="stat card"
5
  {% if stat.tooltip is defined %}
6
  title="{{ stat.tooltip }}"