MainWP Child Reports - Version 1.0

Version Description

  • 3-9-2016 =
  • Fixed: Issue with recreating tables
  • Fixed: Issue with recreating manually deleted tables
  • Fixed: Issue with updating actions on auto-save Post and Page
  • Fixed: Layout and javascript issue when custom branding is applied
  • Added: Feature to copy reports from the Stream plugin
  • Added: Support for recording BackWPup backups
  • Added: Install Plugins, Install Themes, Delete Plugins, Delete Themes action logging
  • Updated: New timeago js library version

  • First version - 07-24-15

Download this release

Release Info

Developer mainwp
Plugin Icon 128x128 MainWP Child Reports
Version 1.0
Comparing to
See all releases

Code changes from version 0.0.1 to 1.0

Files changed (78) hide show
  1. connectors/backupwordpress.php +3 -3
  2. connectors/backwpup.php +76 -0
  3. connectors/installer.php +104 -2
  4. connectors/posts.php +5 -2
  5. connectors/updraftplus.php +2 -2
  6. includes/admin.php +11 -103
  7. includes/connector.php +12 -3
  8. includes/connectors.php +3 -2
  9. includes/dashboard.php +10 -7
  10. includes/db.php +59 -0
  11. includes/functions.php +30 -0
  12. includes/install.php +161 -77
  13. includes/list-table.php +21 -7
  14. includes/live-update.php +8 -2
  15. includes/log.php +6 -2
  16. includes/query.php +7 -2
  17. mainwp-child-reports.php +4 -3
  18. readme.txt +15 -4
  19. ui/admin.css +38 -38
  20. ui/admin.js +25 -15
  21. ui/dashboard.js +0 -113
  22. ui/timeago/{timeago.js → jquery.timeago.js} +31 -27
  23. ui/timeago/locale/jquery.timeago.cz.js +0 -18
  24. ui/timeago/locale/jquery.timeago.he.js +0 -18
  25. ui/timeago/locale/jquery.timeago.sl.js +0 -44
  26. ui/timeago/locale/jquery.timeago.zh-CN.js +0 -20
  27. ui/timeago/locale/jquery.timeago.zh-TW.js +0 -20
  28. ui/timeago/locales/README.md +27 -0
  29. ui/timeago/{locale → locales}/jquery.timeago.ar.js +0 -0
  30. ui/timeago/{locale → locales}/jquery.timeago.bg.js +0 -0
  31. ui/timeago/{locale → locales}/jquery.timeago.bs.js +0 -0
  32. ui/timeago/{locale → locales}/jquery.timeago.ca.js +0 -0
  33. ui/timeago/locales/jquery.timeago.cs.js +24 -0
  34. ui/timeago/{locale → locales}/jquery.timeago.cy.js +0 -0
  35. ui/timeago/{locale → locales}/jquery.timeago.da.js +0 -0
  36. ui/timeago/{locale → locales}/jquery.timeago.de.js +0 -0
  37. ui/timeago/{locale → locales}/jquery.timeago.el.js +0 -0
  38. ui/timeago/{locale → locales}/jquery.timeago.en-short.js +0 -0
  39. ui/timeago/{locale → locales}/jquery.timeago.en.js +7 -7
  40. ui/timeago/locales/jquery.timeago.es-short.js +20 -0
  41. ui/timeago/{locale → locales}/jquery.timeago.es.js +0 -0
  42. ui/timeago/{locale → locales}/jquery.timeago.et.js +0 -0
  43. ui/timeago/{locale → locales}/jquery.timeago.fa.js +0 -0
  44. ui/timeago/{locale → locales}/jquery.timeago.fi.js +0 -0
  45. ui/timeago/{locale → locales}/jquery.timeago.fr-short.js +0 -0
  46. ui/timeago/{locale → locales}/jquery.timeago.fr.js +0 -0
  47. ui/timeago/{locale → locales}/jquery.timeago.gl.js +0 -0
  48. ui/timeago/locales/jquery.timeago.he.js +16 -0
  49. ui/timeago/{locale → locales}/jquery.timeago.hr.js +0 -0
  50. ui/timeago/{locale → locales}/jquery.timeago.hu.js +0 -0
  51. ui/timeago/{locale → locales}/jquery.timeago.hy.js +0 -0
  52. ui/timeago/{locale → locales}/jquery.timeago.id.js +0 -0
  53. ui/timeago/{locale → locales}/jquery.timeago.is.js +0 -0
  54. ui/timeago/{locale → locales}/jquery.timeago.it.js +0 -0
  55. ui/timeago/{locale → locales}/jquery.timeago.ja.js +0 -0
  56. ui/timeago/{locale → locales}/jquery.timeago.jv.js +0 -0
  57. ui/timeago/{locale → locales}/jquery.timeago.ko.js +0 -0
  58. ui/timeago/{locale → locales}/jquery.timeago.lt.js +0 -0
  59. ui/timeago/{locale → locales}/jquery.timeago.mk.js +0 -0
  60. ui/timeago/{locale → locales}/jquery.timeago.nl.js +0 -0
  61. ui/timeago/{locale → locales}/jquery.timeago.no.js +0 -0
  62. ui/timeago/{locale → locales}/jquery.timeago.pl.js +0 -0
  63. ui/timeago/locales/jquery.timeago.pt-br-short.js +20 -0
  64. ui/timeago/{locale → locales}/jquery.timeago.pt-br.js +0 -0
  65. ui/timeago/{locale → locales}/jquery.timeago.pt.js +0 -0
  66. ui/timeago/{locale → locales}/jquery.timeago.ro.js +2 -2
  67. ui/timeago/{locale → locales}/jquery.timeago.rs.js +1 -1
  68. ui/timeago/{locale → locales}/jquery.timeago.ru.js +0 -0
  69. ui/timeago/{locale → locales}/jquery.timeago.sk.js +0 -0
  70. ui/timeago/locales/jquery.timeago.sl.js +40 -0
  71. ui/timeago/{locale → locales}/jquery.timeago.sv.js +0 -0
  72. ui/timeago/{locale → locales}/jquery.timeago.th.js +0 -0
  73. ui/timeago/{locale → locales}/jquery.timeago.tr.js +2 -2
  74. ui/timeago/{locale → locales}/jquery.timeago.uk.js +0 -0
  75. ui/timeago/{locale → locales}/jquery.timeago.uz.js +0 -0
  76. ui/timeago/{locale → locales}/jquery.timeago.vi.js +0 -0
  77. ui/timeago/locales/jquery.timeago.zh-CN.js +20 -0
  78. ui/timeago/locales/jquery.timeago.zh-TW.js +20 -0
connectors/backupwordpress.php CHANGED
@@ -9,18 +9,18 @@ class MainWP_WP_Stream_Connector_Backupwordpress extends MainWP_WP_Stream_Connec
9
  );
10
 
11
  public static function get_label() {
12
- return __( 'Backupwordpress Backups', 'default' );
13
  }
14
 
15
  public static function get_action_labels() {
16
  return array(
17
- 'backupwordpress_backup' => __( 'Backup', 'default' ),
18
  );
19
  }
20
 
21
  public static function get_context_labels() {
22
  return array(
23
- 'backupwordpress_backups' => __( 'Backupwordpress Backups', 'mainwp-child-reports' ),
24
  );
25
  }
26
 
9
  );
10
 
11
  public static function get_label() {
12
+ return __( 'BackupWordPress', 'default' );
13
  }
14
 
15
  public static function get_action_labels() {
16
  return array(
17
+ 'backupwordpress_backup' => __( 'BackupWordPress Backup', 'default' ),
18
  );
19
  }
20
 
21
  public static function get_context_labels() {
22
  return array(
23
+ 'backupwordpress_backups' => __( 'BackupWordPress Backups', 'mainwp-child-reports' ),
24
  );
25
  }
26
 
connectors/backwpup.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( class_exists( 'MainWP_WP_Stream_Connector' ) ) {
3
+ class MainWP_WP_Stream_Connector_Backwpup extends MainWP_WP_Stream_Connector {
4
+
5
+ /**
6
+ * Connector slug
7
+ *
8
+ * @var string
9
+ */
10
+ public static $name = 'backwpup_backups';
11
+
12
+ /**
13
+ * Actions registered for this connector
14
+ *
15
+ * @var array
16
+ */
17
+ public static $actions = array(
18
+ 'mainwp_backwpup_backup',
19
+ );
20
+
21
+ /**
22
+ * Return translated connector label
23
+ *
24
+ * @return string Translated connector label
25
+ */
26
+ public static function get_label() {
27
+ return __( 'BackWPup', 'mainwp-child' );
28
+ }
29
+
30
+ /**
31
+ * Return translated action labels
32
+ *
33
+ * @return array Action label translations
34
+ */
35
+ public static function get_action_labels() {
36
+ return array(
37
+ 'mainwp_backwpup_backup' => __( 'BackWPup Backup', 'mainwp-child' ),
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Return translated context labels
43
+ *
44
+ * @return array Context label translations
45
+ */
46
+ public static function get_context_labels() {
47
+ return array(
48
+ 'backwpup_backups' => __( 'BackWPup Backups', 'mainwp-child' ),
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Add action links to Stream drop row in admin list screen
54
+ *
55
+ * @filter wp_stream_action_links_{connector}
56
+ *
57
+ * @param array $links Previous links registered
58
+ * @param int $record Stream record
59
+ *
60
+ * @return array Action links
61
+ */
62
+ public static function action_links( $links, $record ) {
63
+ return $links;
64
+ }
65
+
66
+ public static function callback_mainwp_backwpup_backup( $message, $type, $backup_time ) {
67
+ self::log(
68
+ $message,
69
+ compact( 'type', 'backup_time' ),
70
+ 0,
71
+ array( 'backwpup_backups' => 'mainwp_backwpup_backup' )
72
+ );
73
+ }
74
+ }
75
+ }
76
+
connectors/installer.php CHANGED
@@ -14,6 +14,9 @@ class MainWP_WP_Stream_Connector_Installer extends MainWP_WP_Stream_Connector {
14
  'pre_set_site_transient_update_plugins',
15
  'wp_redirect',
16
  '_core_updated_successfully',
 
 
 
17
  );
18
 
19
  public static function get_label() {
@@ -49,8 +52,107 @@ class MainWP_WP_Stream_Connector_Installer extends MainWP_WP_Stream_Connector {
49
  $links[ __( 'View Release Notes', 'mainwp-child-reports' ) ] = esc_url( sprintf( 'http://codex.wordpress.org/Version_%s', $version ) );
50
  }
51
  return $links;
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  public static function callback_upgrader_process_complete( $upgrader, $extra ) {
55
  $logs = array();
56
  $success = ! is_wp_error( $upgrader->skin->result );
@@ -162,7 +264,7 @@ class MainWP_WP_Stream_Connector_Installer extends MainWP_WP_Stream_Connector {
162
  }
163
 
164
  public static function callback_activate_plugin( $slug, $network_wide ) {
165
- $plugins = get_plugins();
166
  $name = $plugins[ $slug ]['Name'];
167
  $network_wide = $network_wide ? __( 'network wide', 'mainwp-child-reports' ) : null;
168
 
14
  'pre_set_site_transient_update_plugins',
15
  'wp_redirect',
16
  '_core_updated_successfully',
17
+ 'mainwp_child_installPluginTheme',
18
+ 'mainwp_child_plugin_action',
19
+ 'mainwp_child_theme_action'
20
  );
21
 
22
  public static function get_label() {
52
  $links[ __( 'View Release Notes', 'mainwp-child-reports' ) ] = esc_url( sprintf( 'http://codex.wordpress.org/Version_%s', $version ) );
53
  }
54
  return $links;
55
+ }
56
+
57
+ public static function callback_mainwp_child_installPluginTheme($args ) {
58
+ $logs = array();
59
+ $success = isset($args['success']) ? $args['success'] : 0;
60
+ $error = null;
61
+
62
+ if ( ! $success ) {
63
+ $errors = $args['errors'];;
64
+ }
65
+
66
+ // This would have failed down the road anyway
67
+ if ( ! isset( $args['type'] ) ) {
68
+ return false;
69
+ }
70
+
71
+ $type = $args['type'];
72
+ $action = $args['action'];
73
+
74
+ if ( ! in_array( $type, array( 'plugin', 'theme' ) ) ) {
75
+ return;
76
+ }
77
+
78
+ if ( 'install' === $action ) {
79
+ if ( 'plugin' === $type) {
80
+ if ( !isset($args['Name']) || empty($args['Name']))
81
+ return;
82
+ $slug = $args['slug'];
83
+ $name = $args['Name'];
84
+ $version = $args['Version'];
85
+ } else { // theme
86
+ $slug = $args['slug'];
87
+ if ( ! $slug ) {
88
+ return;
89
+ }
90
+ wp_clean_themes_cache();
91
+ $theme = wp_get_theme( $slug );
92
+ $name = $theme->name;
93
+ $version = $theme->version;
94
+ }
95
+ $action = 'installed';
96
+ $message = _x(
97
+ 'Installed %1$s: %2$s %3$s',
98
+ 'Plugin/theme installation. 1: Type (plugin/theme), 2: Plugin/theme name, 3: Plugin/theme version',
99
+ 'mainwp_child_reports'
100
+ );
101
+ $logs[] = compact( 'slug', 'name', 'version', 'message', 'action' );
102
+ } else {
103
+ return false;
104
+ }
105
+
106
+ $context = $type . 's';
107
 
108
+ foreach ( $logs as $log ) {
109
+ $name = isset( $log['name'] ) ? $log['name'] : null;
110
+ $version = isset( $log['version'] ) ? $log['version'] : null;
111
+ $slug = isset( $log['slug'] ) ? $log['slug'] : null;
112
+ $old_version = isset( $log['old_version'] ) ? $log['old_version'] : null;
113
+ $message = isset( $log['message'] ) ? $log['message'] : null;
114
+ $action = isset( $log['action'] ) ? $log['action'] : null;
115
+ self::log(
116
+ $message,
117
+ compact( 'type', 'name', 'version', 'slug', 'success', 'error', 'old_version' ),
118
+ null,
119
+ array( $context => $action )
120
+ );
121
+ }
122
+ }
123
+
124
+
125
+ public static function callback_mainwp_child_plugin_action( $args ) {
126
+ if (!is_array($args) || !isset($args['action']))
127
+ return;
128
+ $action = $args['action'];
129
+ if ($action == 'delete') {
130
+ $name = $args['Name'];
131
+ $network_wide = '';
132
+ self::log(
133
+ __( '"%s" plugin deleted', 'mainwp-child-reports' ),
134
+ compact( 'name', 'plugin', 'network_wide' ),
135
+ null,
136
+ array( 'plugins' => 'deleted' )
137
+ );
138
+ }
139
+ }
140
+
141
+ public static function callback_mainwp_child_theme_action($args) {
142
+ if (!is_array($args) || !isset($args['action']))
143
+ return;
144
+ $action = $args['action'];
145
+ $name = $args['Name'];
146
+ if ($action == 'delete') {
147
+ self::log(
148
+ __( '"%s" theme deleted', 'mainwp-child-reports' ),
149
+ compact( 'name' ),
150
+ null,
151
+ array( 'themes' => 'deleted' )
152
+ );
153
+ }
154
+ }
155
+
156
  public static function callback_upgrader_process_complete( $upgrader, $extra ) {
157
  $logs = array();
158
  $success = ! is_wp_error( $upgrader->skin->result );
264
  }
265
 
266
  public static function callback_activate_plugin( $slug, $network_wide ) {
267
+ $plugins = get_plugins();
268
  $name = $plugins[ $slug ]['Name'];
269
  $network_wide = $network_wide ? __( 'network wide', 'mainwp-child-reports' ) : null;
270
 
connectors/posts.php CHANGED
@@ -16,6 +16,7 @@ class MainWP_WP_Stream_Connector_Posts extends MainWP_WP_Stream_Connector {
16
  public static function get_action_labels() {
17
  return array(
18
  'updated' => __( 'Updated', 'mainwp-child-reports' ),
 
19
  'created' => __( 'Created', 'mainwp-child-reports' ),
20
  'trashed' => __( 'Trashed', 'mainwp-child-reports' ),
21
  'untrashed' => __( 'Restored', 'mainwp-child-reports' ),
@@ -135,11 +136,13 @@ class MainWP_WP_Stream_Connector_Posts extends MainWP_WP_Stream_Connector {
135
  );
136
  $action = 'untrashed';
137
  } else {
138
- $message = _x(
139
  '"%1$s" %2$s updated',
140
  '1: Post title, 2: Post type singular name',
141
  'mainwp_child_reports'
142
- );
 
 
143
  }
144
 
145
  if ( empty( $action ) ) {
16
  public static function get_action_labels() {
17
  return array(
18
  'updated' => __( 'Updated', 'mainwp-child-reports' ),
19
+ 'autosave' => __( 'Auto save', 'mainwp-child-reports' ),
20
  'created' => __( 'Created', 'mainwp-child-reports' ),
21
  'trashed' => __( 'Trashed', 'mainwp-child-reports' ),
22
  'untrashed' => __( 'Restored', 'mainwp-child-reports' ),
136
  );
137
  $action = 'untrashed';
138
  } else {
139
+ $message = _x(
140
  '"%1$s" %2$s updated',
141
  '1: Post title, 2: Post type singular name',
142
  'mainwp_child_reports'
143
+ );
144
+ if (defined( 'DOING_AUTOSAVE' ) )
145
+ $action = 'autosave';
146
  }
147
 
148
  if ( empty( $action ) ) {
connectors/updraftplus.php CHANGED
@@ -9,12 +9,12 @@ class MainWP_WP_Stream_Connector_Updraftplus extends MainWP_WP_Stream_Connector
9
  );
10
 
11
  public static function get_label() {
12
- return __( 'Updraftplus Backups', 'default' );
13
  }
14
 
15
  public static function get_action_labels() {
16
  return array(
17
- 'updraftplus_backup' => __( 'Backup', 'default' ),
18
  );
19
  }
20
 
9
  );
10
 
11
  public static function get_label() {
12
+ return __( 'Updraftplus', 'default' );
13
  }
14
 
15
  public static function get_action_labels() {
16
  return array(
17
+ 'updraftplus_backup' => __( 'Updraftplus Backup', 'default' ),
18
  );
19
  }
20
 
includes/admin.php CHANGED
@@ -34,10 +34,6 @@ class MainWP_WP_Stream_Admin {
34
  // Add admin body class
35
  add_filter( 'admin_body_class', array( __CLASS__, 'admin_body_class' ) );
36
 
37
- // Plugin action links
38
- add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 );
39
- add_filter( 'network_admin_plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 );
40
-
41
  // Load admin scripts and styles
42
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
43
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_menu_css' ) );
@@ -48,8 +44,6 @@ class MainWP_WP_Stream_Admin {
48
  // Reset MainWP Reports settings
49
  add_action( 'wp_ajax_mainwp_wp_stream_defaults', array( __CLASS__, 'wp_ajax_defaults' ) );
50
 
51
- // Uninstall MainWP Reports and Deactivate plugin
52
- add_action( 'wp_ajax_mainwp_wp_stream_uninstall', array( __CLASS__, 'uninstall_plugin' ) );
53
 
54
  // Auto purge setup
55
  add_action( 'wp_loaded', array( __CLASS__, 'purge_schedule_setup' ) );
@@ -77,8 +71,8 @@ class MainWP_WP_Stream_Admin {
77
  break;
78
  case 'child_reports_settings_reset':
79
  printf( '<div class="updated"><p>%s</p></div>', __( 'All site settings have been successfully reset.', 'mainwp-child-reports' ) );
80
- break;
81
- }
82
  }
83
 
84
  public static function hookUpdraftplusBackupComplete($delete_jobdata) {
@@ -228,10 +222,10 @@ class MainWP_WP_Stream_Admin {
228
  public static function admin_enqueue_scripts( $hook ) {
229
  wp_register_script( 'select2', MAINWP_WP_STREAM_URL . 'ui/select2/select2.min.js', array( 'jquery' ), '3.4.5', true );
230
  wp_register_style( 'select2', MAINWP_WP_STREAM_URL . 'ui/select2/select2.css', array(), '3.4.5' );
231
- wp_register_script( 'timeago', MAINWP_WP_STREAM_URL . 'ui/timeago/timeago.js', array(), '0.2.0', true );
232
 
233
- $locale = substr( get_locale(), 0, 2 );
234
- $file_tmpl = 'ui/timeago/locale/jquery.timeago.%s.js';
235
 
236
  if ( file_exists( MAINWP_WP_STREAM_DIR . sprintf( $file_tmpl, $locale ) ) ) {
237
  wp_register_script( 'timeago-locale', MAINWP_WP_STREAM_URL . sprintf( $file_tmpl, $locale ), array( 'timeago' ), '1' );
@@ -244,7 +238,7 @@ class MainWP_WP_Stream_Admin {
244
  $script_screens = array( 'plugins.php', 'user-edit.php', 'user-new.php', 'profile.php' );
245
 
246
  if ( 'index.php' === $hook ) {
247
- wp_enqueue_script( 'mainwp-wp-stream-admin-dashboard', MAINWP_WP_STREAM_URL . 'ui/dashboard.js', array( 'jquery', 'heartbeat' ), MainWP_WP_Stream::VERSION );
248
  } elseif ( in_array( $hook, self::$screen_id ) || in_array( $hook, $script_screens ) ) {
249
  wp_enqueue_script( 'select2' );
250
  wp_enqueue_style( 'select2' );
@@ -252,7 +246,7 @@ class MainWP_WP_Stream_Admin {
252
  wp_enqueue_script( 'timeago' );
253
  wp_enqueue_script( 'timeago-locale' );
254
 
255
- wp_enqueue_script( 'mainwp-wp-stream-admin', MAINWP_WP_STREAM_URL . 'ui/admin.js', array( 'jquery', 'select2', 'heartbeat' ), MainWP_WP_Stream::VERSION );
256
  wp_localize_script(
257
  'mainwp-wp-stream-admin',
258
  'mainwp_wp_stream',
@@ -268,8 +262,9 @@ class MainWP_WP_Stream_Admin {
268
  'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc',
269
  'current_query' => json_encode( $_GET ),
270
  'filters' => self::$list_table ? self::$list_table->get_filters() : false,
 
271
  )
272
- );
273
  }
274
  }
275
 
@@ -290,34 +285,6 @@ class MainWP_WP_Stream_Admin {
290
  include( ABSPATH . WPINC . '/version.php' );
291
  }
292
 
293
- public static function plugin_action_links( $links, $file ) {
294
- if ( plugin_basename( MAINWP_WP_STREAM_DIR . 'stream.php' ) === $file ) {
295
-
296
- // Don't show links in Network Admin if MainWP Reports isn't network enabled
297
- if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( MAINWP_WP_STREAM_PLUGIN ) ) {
298
- return $links;
299
- }
300
-
301
- if ( is_network_admin() ) {
302
- $admin_page_url = add_query_arg( array( 'page' => MainWP_WP_Stream_Network::NETWORK_SETTINGS_PAGE_SLUG ), network_admin_url( self::ADMIN_PARENT_PAGE ) );
303
- } else {
304
- $admin_page_url = add_query_arg( array( 'page' => self::SETTINGS_PAGE_SLUG ), admin_url( self::ADMIN_PARENT_PAGE ) );
305
- }
306
- $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
307
-
308
- $url = add_query_arg(
309
- array(
310
- 'action' => 'mainwp_wp_stream_uninstall',
311
- 'mainwp_wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
312
- ),
313
- admin_url( 'admin-ajax.php' )
314
- );
315
- $links[] = sprintf( '<span id="mainwp_wp_stream_uninstall" class="delete"><a href="%s">%s</a></span>', esc_url( $url ), esc_html__( 'Uninstall', 'mainwp-child-reports' ) );
316
- }
317
-
318
- return $links;
319
- }
320
-
321
  public static function register_update_hook( $file, $callback, $version ) {
322
  if ( ! is_admin() ) {
323
  return;
@@ -438,7 +405,8 @@ class MainWP_WP_Stream_Admin {
438
  check_ajax_referer( 'stream_nonce', 'mainwp_wp_stream_nonce' );
439
 
440
  if ( current_user_can( self::SETTINGS_CAP ) ) {
441
- self::erase_stream_records();
 
442
  wp_redirect(
443
  add_query_arg(
444
  array(
@@ -515,66 +483,6 @@ class MainWP_WP_Stream_Admin {
515
  }
516
  }
517
 
518
- public static function uninstall_plugin() {
519
- global $wpdb;
520
-
521
- check_ajax_referer( 'stream_nonce', 'mainwp_wp_stream_nonce' );
522
-
523
- if ( current_user_can( self::SETTINGS_CAP ) ) {
524
- // Prevent stream action from being fired on plugin
525
- remove_action( 'deactivate_plugin', array( 'MainWP_WP_Stream_Connector_Installer', 'callback' ), null );
526
-
527
- // Plugin is being uninstalled from only one of the multisite blogs
528
- if ( is_multisite() && ! is_plugin_active_for_network( MAINWP_WP_STREAM_PLUGIN ) ) {
529
- $blog_id = get_current_blog_id();
530
-
531
- $wpdb->query( "DELETE FROM {$wpdb->base_prefix}stream WHERE blog_id = $blog_id" );
532
-
533
- delete_option( plugin_basename( MAINWP_WP_STREAM_DIR ) . '_db' );
534
- delete_option( MainWP_WP_Stream_Install::KEY );
535
- delete_option( MainWP_WP_Stream_Settings::KEY );
536
- } else {
537
- // Delete all tables
538
- foreach ( MainWP_WP_Stream_DB::get_instance()->get_table_names() as $table ) {
539
- $wpdb->query( "DROP TABLE $table" );
540
- }
541
-
542
- // Delete database options
543
- if ( is_multisite() ) {
544
- $blogs = wp_get_sites();
545
- foreach ( $blogs as $blog ) {
546
- switch_to_blog( $blog['blog_id'] );
547
- delete_option( plugin_basename( MAINWP_WP_STREAM_DIR ) . '_db' );
548
- delete_option( MainWP_WP_Stream_Install::KEY );
549
- delete_option( MainWP_WP_Stream_Settings::KEY );
550
- }
551
- restore_current_blog();
552
- }
553
-
554
- // Delete database option
555
- delete_site_option( plugin_basename( MAINWP_WP_STREAM_DIR ) . '_db' );
556
- delete_site_option( MainWP_WP_Stream_Install::KEY );
557
- delete_site_option( MainWP_WP_Stream_Settings::KEY );
558
- delete_site_option( MainWP_WP_Stream_Settings::DEFAULTS_KEY );
559
- delete_site_option( MainWP_WP_Stream_Settings::NETWORK_KEY );
560
- delete_site_option( 'dashboard_mainwp_stream_activity_options' );
561
- }
562
-
563
- // Delete scheduled cron event hooks
564
- wp_clear_scheduled_hook( 'stream_auto_purge' ); // Deprecated hook
565
- wp_clear_scheduled_hook( 'mainwp_wp_stream_auto_purge' );
566
-
567
- // Deactivate the plugin
568
- deactivate_plugins( plugin_basename( MAINWP_WP_STREAM_DIR ) . '/stream.php' );
569
-
570
- // Redirect to plugin page
571
- wp_redirect( add_query_arg( array( 'deactivate' => true ), self_admin_url( 'plugins.php' ) ) );
572
- exit;
573
- } else {
574
- wp_die( "You don't have sufficient privileges to do this action." );
575
- }
576
- }
577
-
578
  public static function purge_schedule_setup() {
579
  if ( ! wp_next_scheduled( 'mainwp_wp_stream_auto_purge' ) ) {
580
  wp_schedule_event( time(), 'twicedaily', 'mainwp_wp_stream_auto_purge' );
34
  // Add admin body class
35
  add_filter( 'admin_body_class', array( __CLASS__, 'admin_body_class' ) );
36
 
 
 
 
 
37
  // Load admin scripts and styles
38
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
39
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_menu_css' ) );
44
  // Reset MainWP Reports settings
45
  add_action( 'wp_ajax_mainwp_wp_stream_defaults', array( __CLASS__, 'wp_ajax_defaults' ) );
46
 
 
 
47
 
48
  // Auto purge setup
49
  add_action( 'wp_loaded', array( __CLASS__, 'purge_schedule_setup' ) );
71
  break;
72
  case 'child_reports_settings_reset':
73
  printf( '<div class="updated"><p>%s</p></div>', __( 'All site settings have been successfully reset.', 'mainwp-child-reports' ) );
74
+ break;
75
+ }
76
  }
77
 
78
  public static function hookUpdraftplusBackupComplete($delete_jobdata) {
222
  public static function admin_enqueue_scripts( $hook ) {
223
  wp_register_script( 'select2', MAINWP_WP_STREAM_URL . 'ui/select2/select2.min.js', array( 'jquery' ), '3.4.5', true );
224
  wp_register_style( 'select2', MAINWP_WP_STREAM_URL . 'ui/select2/select2.css', array(), '3.4.5' );
225
+ wp_register_script( 'timeago', MAINWP_WP_STREAM_URL . 'ui/timeago/jquery.timeago.js', array(), '1.4.1', true );
226
 
227
+ $locale = strtolower( substr( get_locale(), 0, 2 ) );
228
+ $file_tmpl = 'ui/timeago/locales/jquery.timeago.%s.js';
229
 
230
  if ( file_exists( MAINWP_WP_STREAM_DIR . sprintf( $file_tmpl, $locale ) ) ) {
231
  wp_register_script( 'timeago-locale', MAINWP_WP_STREAM_URL . sprintf( $file_tmpl, $locale ), array( 'timeago' ), '1' );
238
  $script_screens = array( 'plugins.php', 'user-edit.php', 'user-new.php', 'profile.php' );
239
 
240
  if ( 'index.php' === $hook ) {
241
+
242
  } elseif ( in_array( $hook, self::$screen_id ) || in_array( $hook, $script_screens ) ) {
243
  wp_enqueue_script( 'select2' );
244
  wp_enqueue_style( 'select2' );
246
  wp_enqueue_script( 'timeago' );
247
  wp_enqueue_script( 'timeago-locale' );
248
 
249
+ wp_enqueue_script( 'mainwp-wp-stream-admin', MAINWP_WP_STREAM_URL . 'ui/admin.js', array( 'jquery', 'select2', 'heartbeat' ), MainWP_WP_Stream::VERSION );
250
  wp_localize_script(
251
  'mainwp-wp-stream-admin',
252
  'mainwp_wp_stream',
262
  'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc',
263
  'current_query' => json_encode( $_GET ),
264
  'filters' => self::$list_table ? self::$list_table->get_filters() : false,
265
+ 'locale' => esc_js( $locale )
266
  )
267
+ );
268
  }
269
  }
270
 
285
  include( ABSPATH . WPINC . '/version.php' );
286
  }
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  public static function register_update_hook( $file, $callback, $version ) {
289
  if ( ! is_admin() ) {
290
  return;
405
  check_ajax_referer( 'stream_nonce', 'mainwp_wp_stream_nonce' );
406
 
407
  if ( current_user_can( self::SETTINGS_CAP ) ) {
408
+ self::erase_stream_records();
409
+ MainWP_WP_Stream_Install::check_to_copy_data();
410
  wp_redirect(
411
  add_query_arg(
412
  array(
483
  }
484
  }
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  public static function purge_schedule_setup() {
487
  if ( ! wp_next_scheduled( 'mainwp_wp_stream_auto_purge' ) ) {
488
  wp_schedule_event( time(), 'twicedaily', 'mainwp_wp_stream_auto_purge' );
includes/connector.php CHANGED
@@ -51,16 +51,25 @@ abstract class MainWP_WP_Stream_Connector {
51
  if ( count( $contexts ) == 0 ){
52
  return ;
53
  }
54
-
 
 
 
 
 
 
 
 
55
  $class = get_called_class();
56
-
57
  return MainWP_WP_Stream_Log::get_instance()->log(
58
  $class::$name,
59
  $message,
60
  $args,
61
  $object_id,
62
  $contexts,
63
- $user_id
 
64
  );
65
  }
66
 
51
  if ( count( $contexts ) == 0 ){
52
  return ;
53
  }
54
+
55
+ $created_timestamp = null;
56
+ if (is_array($contexts) && isset($contexts['backwpup_backups'])) {
57
+ $created_timestamp = is_array($args) && isset($args['backup_time']) ? $args['backup_time'] : null;
58
+ $saved_item = MainWP_WP_Stream_Log::get_instance()->get_log( array( 'context' => 'backwpup_backups', 'created' => get_gmt_from_date( date("Y-m-d H:i:s", $created_timestamp ) ) ) );
59
+ if ($saved_item)
60
+ return;
61
+ }
62
+
63
  $class = get_called_class();
64
+
65
  return MainWP_WP_Stream_Log::get_instance()->log(
66
  $class::$name,
67
  $message,
68
  $args,
69
  $object_id,
70
  $contexts,
71
+ $user_id,
72
+ $created_timestamp
73
  );
74
  }
75
 
includes/connectors.php CHANGED
@@ -26,8 +26,9 @@ class MainWP_WP_Stream_Connectors {
26
  'posts',
27
  'users',
28
  'widgets',
29
- 'updraftplus',
30
- 'backupwordpress'
 
31
  );
32
  $classes = array();
33
  foreach ( $connectors as $connector ) {
26
  'posts',
27
  'users',
28
  'widgets',
29
+ 'updraftplus',
30
+ 'backupwordpress',
31
+ 'backwpup'
32
  );
33
  $classes = array();
34
  foreach ( $connectors as $connector ) {
includes/dashboard.php CHANGED
@@ -7,7 +7,7 @@ class MainWP_WP_Stream_Dashboard_Widget {
7
  }
8
 
9
  public static function widget_row( $item, $i = null ) {
10
- require_once MainWP_WP_STREAM_INC_DIR . 'class-wp-stream-author.php';
11
 
12
  $author_meta = mainwp_wp_stream_get_meta( $item->ID, 'author_meta', true );
13
  $author = new MainWP_WP_Stream_Author( (int) $item->author, $author_meta );
@@ -68,18 +68,21 @@ class MainWP_WP_Stream_Dashboard_Widget {
68
  return $send;
69
  }
70
 
71
- public static function gather_updated_items( $last_id, $query = array() ) {
72
  if ( false === $last_id ) {
73
  return '';
74
  }
75
-
76
- $default = array(
77
- 'record_greater_than' => (int) $last_id,
78
- );
 
 
 
79
 
80
  // Filter default
81
  $query = wp_parse_args( $query, $default );
82
-
83
  // Run query
84
  $items = mainwp_wp_stream_query( $query );
85
 
7
  }
8
 
9
  public static function widget_row( $item, $i = null ) {
10
+ require_once MAINWP_WP_STREAM_INC_DIR . 'class-wp-stream-author.php';
11
 
12
  $author_meta = mainwp_wp_stream_get_meta( $item->ID, 'author_meta', true );
13
  $author = new MainWP_WP_Stream_Author( (int) $item->author, $author_meta );
68
  return $send;
69
  }
70
 
71
+ public static function gather_updated_items( $last_id, $query = array(), $last_created = null ) {
72
  if ( false === $last_id ) {
73
  return '';
74
  }
75
+ $default = array();
76
+
77
+ if (!empty($last_created)) {
78
+ $default['created_greater_than'] = $last_created;
79
+ } else {
80
+ $default['record_greater_than'] = (int) $last_id;
81
+ }
82
 
83
  // Filter default
84
  $query = wp_parse_args( $query, $default );
85
+
86
  // Run query
87
  $items = mainwp_wp_stream_query( $query );
88
 
includes/db.php CHANGED
@@ -112,6 +112,65 @@ class MainWP_WP_Stream_DB {
112
  return $result;
113
  }
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  public function insert_meta( $record_id, $key, $val ) {
116
  global $wpdb;
117
 
112
  return $result;
113
  }
114
 
115
+
116
+ public function get_report( $args = array() ) {
117
+ if (!is_array($args))
118
+ return false;
119
+ global $wpdb;
120
+ $where = "";
121
+ $left_join = "";
122
+ if (isset($args['context'])) {
123
+ $left_join = " LEFT JOIN " . self::$table_context . " AS `context` ON `stream`.`ID` = `context`.`record_id` " ;
124
+ }
125
+
126
+ foreach ($args as $key => $value) {
127
+ if ($key == 'context') {
128
+ $where .= $wpdb->prepare( ' `context`.`context` = %s AND ', $value);
129
+ } else
130
+ $where .= $wpdb->prepare( ' `stream`.`' . $key . '` = %s AND ', $value);
131
+ }
132
+
133
+ $where = rtrim($where, "AND ");
134
+
135
+ if (!empty($where)) {
136
+ $where .= " AND blog_id = " . apply_filters( 'blog_id_logged', is_network_admin() ? 0 : get_current_blog_id() );
137
+ $result = $wpdb->get_row( 'SELECT `stream`.* FROM ' . self::$table . ' AS `stream` ' . $left_join . ' WHERE ' . $where );
138
+ return $result;
139
+ }
140
+ return false;
141
+ }
142
+
143
+ public function delete_report( $args = array() ) {
144
+ if (!is_array($args))
145
+ return false;
146
+
147
+ global $wpdb;
148
+ $sql = "";
149
+
150
+ foreach ($args as $key => $value) {
151
+ $sql .= $key ." = " . $value . " AND ";
152
+ }
153
+
154
+ $sql = rtrim($sql, "AND ");
155
+
156
+ if ( ! empty( $sql ) ) {
157
+ $sql .= " AND blog_id = " . apply_filters( 'blog_id_logged', is_network_admin() ? 0 : get_current_blog_id() );
158
+
159
+ $sql = $wpdb->prepare( 'SELECT ID FROM ' . self::$table . ' WHERE %s ', $sql );
160
+ $record_id = $wpdb->get_var( $sql );
161
+ if ($record_id) {
162
+ $sql = $wpdb->prepare( 'DELETE FROM ' . self::$table . ' WHERE %s ', $sql );
163
+ $wpdb->query( $sql );
164
+ $sql = $wpdb->prepare( 'DELETE FROM ' . self::$table_context . ' WHERE record_id = %d ', $record_id );
165
+ $wpdb->query( $sql );
166
+ $sql = $wpdb->prepare( 'DELETE FROM ' . self::$table_meta . ' WHERE record_id = %d ', $record_id );
167
+ $wpdb->query( $sql );
168
+ return true;
169
+ }
170
+ }
171
+ return false;
172
+ }
173
+
174
  public function insert_meta( $record_id, $key, $val ) {
175
  global $wpdb;
176
 
includes/functions.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Converts a time into an ISO 8601 extended formatted string.
5
+ *
6
+ * @param int|bool $time Seconds since unix epoc
7
+ * @param int $offset Hour offset
8
+ *
9
+ * @return string an ISO 8601 extended formatted time
10
+ */
11
+ function mainwp_wp_stream_get_iso_8601_extended_date( $time = false, $offset = 0 ) {
12
+ if ( $time ) {
13
+ $microtime = (float) $time . '.0000';
14
+ } else {
15
+ $microtime = microtime( true );
16
+ }
17
+
18
+ $micro_seconds = sprintf( '%06d', ( $microtime - floor( $microtime ) ) * 1000000 );
19
+ $offset_string = sprintf( 'Etc/GMT%s%s', $offset < 0 ? '+' : '-', abs( $offset ) );
20
+
21
+ $timezone = new DateTimeZone( $offset_string );
22
+ $date = new DateTime( date( 'Y-m-d H:i:s.' . $micro_seconds, $microtime ), $timezone );
23
+
24
+ return sprintf(
25
+ '%s%03d%s',
26
+ $date->format( 'Y-m-d\TH:i:s.' ),
27
+ floor( $date->format( 'u' ) / 1000 ),
28
+ $date->format( 'O' )
29
+ );
30
+ }
includes/install.php CHANGED
@@ -18,6 +18,8 @@ class MainWP_WP_Stream_Install {
18
 
19
  private static $instance = false;
20
 
 
 
21
  public static function get_instance() {
22
  if ( empty( self::$instance ) ) {
23
  self::$instance = new self();
@@ -35,33 +37,72 @@ class MainWP_WP_Stream_Install {
35
  $prefix = $wpdb->base_prefix;
36
 
37
  self::$table_prefix = apply_filters( 'mainwp_wp_stream_db_tables_prefix', $prefix );
 
38
  self::check();
39
  }
40
 
41
  private static function check() {
42
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
43
  return;
44
- }
45
- if ( empty( self::$db_version ) ) {
46
- self::install( self::$current );
47
- self::copy_stream_db_149();
48
- } elseif ( self::$db_version !== self::$current ) {
49
-
 
 
 
 
 
 
 
 
 
50
  }
51
  }
52
-
53
- public static function copy_stream_db_149() {
54
- global $wpdb;
55
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  if ( is_multisite() ) {
57
  return;
58
- }
59
-
60
- $stream_db_version = get_site_option( 'wp_stream_db' );
61
-
62
- if ($stream_db_version !== '1.4.9')
63
- return;
64
-
65
  if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream'" ) !== $wpdb->prefix . "stream" )
66
  return;
67
  if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_context'" ) !== $wpdb->prefix . "stream_context" )
@@ -76,8 +117,7 @@ class MainWP_WP_Stream_Install {
76
  foreach ( $blog_stream as $key => $stream_entry ) {
77
  $prev_entry_id = $stream_entry['ID'];
78
 
79
- unset( $stream_entry['ID'] );
80
-
81
 
82
  $wpdb->insert( $wpdb->prefix . 'mainwp_stream', $stream_entry );
83
  $stream_entry_id = $wpdb->insert_id;
@@ -107,83 +147,127 @@ class MainWP_WP_Stream_Install {
107
 
108
  }
109
 
110
- public static function get_db_version() {
111
  global $wpdb;
 
 
 
 
 
 
 
112
 
113
- $version = get_site_option( self::KEY );
114
-
115
- return $version;
116
- }
 
 
 
 
117
 
118
- public static function update_db_option() {
119
- if ( self::$success_db ) {
120
- $success_op = update_site_option( self::KEY, self::$current );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
 
123
- if ( empty( self::$success_db ) || empty( $success_op ) ) {
124
- wp_die( __( 'There was an error updating the MainWP Child Reports database. Please try again.', 'mainwp-child-reports' ), 'Database Update Error', array( 'response' => 200, 'back_link' => true ) );
125
- }
126
  }
127
 
128
  public static function update_notice_hook() {
129
- if ( ! current_user_can( MainWP_WP_Stream_Admin::VIEW_CAP ) ) {
130
- return;
131
- }
132
-
133
  if ( ! isset( $_REQUEST['mainwp_wp_stream_update'] ) ) {
134
- self::prompt_update();
135
- } elseif ( 'update_and_continue' === $_REQUEST['mainwp_wp_stream_update'] ) {
136
- self::prompt_update_status();
 
 
 
 
 
 
 
 
 
 
137
  }
138
  }
139
-
140
- public static function prompt_update() {
141
- ?>
142
- <div class="error">
143
- <form method="post" action="<?php echo esc_url( remove_query_arg( 'mainwp_wp_stream_update' ) ) ?>">
144
- <?php wp_nonce_field( 'mainwp_wp_stream_update_db' ) ?>
145
- <input type="hidden" name="mainwp_wp_stream_update" value="update_and_continue"/>
146
- <p><strong><?php esc_html_e( 'MainWP Child Reports Database Update Required', 'mainwp-child-reports' ) ?></strong></p>
147
- <p><?php esc_html_e( 'MainWP Child Reports has updated! Before we send you on your way, we need to update your database to the newest version.', 'mainwp-child-reports' ) ?></p>
148
- <p><?php esc_html_e( 'This process could take a little while, so please be patient.', 'mainwp-child-reports' ) ?></p>
149
- <?php submit_button( esc_html__( 'Update Database', 'mainwp-child-reports' ), 'primary', 'stream-update-db-submit' ) ?>
150
- </form>
151
- </div>
152
- <?php
153
- }
154
-
155
- public static function prompt_update_status() {
156
- check_admin_referer( 'mainwp_wp_stream_update_db' );
157
-
158
- self::update_db_option();
159
  ?>
160
  <div class="updated">
161
- <form method="post" action="<?php echo esc_url( remove_query_arg( 'mainwp_wp_stream_update' ) ) ?>" style="display:inline;">
162
- <p><strong><?php esc_html_e( 'Update Complete', 'mainwp-child-reports' ) ?></strong></p>
163
- <p><?php esc_html_e( sprintf( 'Your MainWP Child Reports database has been successfully updated from %1$s to %2$s!', self::$db_version, self::$current ), 'mainwp-child-reports' ) ?></p>
164
- <?php submit_button( esc_html__( 'Continue', 'mainwp-child-reports' ), 'secondary', false ) ?>
 
 
 
 
165
  </form>
166
  </div>
167
  <?php
168
  }
169
-
170
- public static function db_update_versions() {
171
- $db_update_versions = array(
172
- '1.1.4' /* @version 1.1.4 Fix mysql character set issues */,
173
- '1.1.7' /* @version 1.1.7 Modified the ip column to varchar(39) */,
174
- '1.2.8' /* @version 1.2.8 Change the context for Media connectors to the attachment type */,
175
- '1.3.0' /* @version 1.3.0 Backward settings compatibility for old version plugins */,
176
- '1.3.1' /* @version 1.3.1 Update records of Installer to Theme Editor connector */,
177
- '1.4.0' /* @version 1.4.0 Add the author_role column and prepare tables for multisite support */,
178
- '1.4.2' /* @version 1.4.2 Patch to fix rare multisite upgrade not triggering */,
179
- '1.4.5' /* @version 1.4.5 Patch to fix author_meta broken values */,
180
- );
181
-
182
- return apply_filters( 'mainwp_wp_stream_db_update_versions', $db_update_versions );
183
  }
 
 
184
 
185
- public static function update( $db_version, $current, $update_args ) {
186
-
 
187
  }
188
 
189
  public static function install( $current ) {
18
 
19
  private static $instance = false;
20
 
21
+ public static $import_connectors;
22
+
23
  public static function get_instance() {
24
  if ( empty( self::$instance ) ) {
25
  self::$instance = new self();
37
  $prefix = $wpdb->base_prefix;
38
 
39
  self::$table_prefix = apply_filters( 'mainwp_wp_stream_db_tables_prefix', $prefix );
40
+ self::$import_connectors = array('comment', 'editor', 'installer', 'media', 'menus', 'posts', 'users', 'widgets');
41
  self::check();
42
  }
43
 
44
  private static function check() {
45
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
46
  return;
47
+ }
48
+
49
+ global $wpdb;
50
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "mainwp_stream'" ) !== $wpdb->prefix . "mainwp_stream" )
51
+ self::$db_version = false;
52
+
53
+ if ( empty( self::$db_version ) ) {
54
+ self::install( self::$current );
55
+ self::copy_stream_db();
56
+ } elseif ( version_compare( self::$db_version, self::$current, '!=') ) {
57
+ update_site_option( self::KEY, self::$current );
58
+ }
59
+
60
+ if ('yes' == get_option('mainwp_child_reports_check_to_copy_data', false)) {
61
+ add_action( 'all_admin_notices', array( __CLASS__, 'update_notice_hook' ) );
62
  }
63
  }
64
+
65
+ public static function check_to_copy_data() {
66
+ $stream_db_version = get_site_option( 'wp_stream_db' ); // store db version of the plugin stream 1.4.9
67
+ // if (empty($stream_db_version)) {
68
+ // return;
69
+ // } else if (version_compare($stream_db_version, '1.4.9', '=')) {
70
+ // if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream'" ) !== $wpdb->prefix . "stream" )
71
+ // return;
72
+ // if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_context'" ) !== $wpdb->prefix . "stream_context" )
73
+ // return;
74
+ // if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_meta'" ) !== $wpdb->prefix . "stream_meta" )
75
+ // return;
76
+ // } else if (version_compare($stream_db_version, '3.0' , '>=')) {
77
+ // if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream'" ) !== $wpdb->prefix . "stream" )
78
+ // return;
79
+ // if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_meta'" ) !== $wpdb->prefix . "stream_meta" )
80
+ // return;
81
+ // }
82
+
83
+ update_option('mainwp_child_reports_check_to_copy_data', 'yes');
84
+ return;
85
+ }
86
+
87
+ public static function copy_stream_db() {
88
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
89
+ return;
90
+ }
91
+ $stream_db_version = get_site_option( 'wp_stream_db' ); // store db version of the plugin stream 1.4.9
92
+ if (version_compare($stream_db_version, '1.4.9', '='))
93
+ self::copy_stream_149_db();
94
+ else if (version_compare($stream_db_version, '3.0' , '>=')) {
95
+ self::copy_stream_300_db();
96
+ }
97
+ update_option('mainwp_child_reports_copied_data_ok', 'yes');
98
+ update_option('mainwp_child_reports_check_to_copy_data', '');
99
+ }
100
+
101
+ public static function copy_stream_149_db() {
102
+ global $wpdb;
103
  if ( is_multisite() ) {
104
  return;
105
+ }
 
 
 
 
 
 
106
  if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream'" ) !== $wpdb->prefix . "stream" )
107
  return;
108
  if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_context'" ) !== $wpdb->prefix . "stream_context" )
117
  foreach ( $blog_stream as $key => $stream_entry ) {
118
  $prev_entry_id = $stream_entry['ID'];
119
 
120
+ unset( $stream_entry['ID'] );
 
121
 
122
  $wpdb->insert( $wpdb->prefix . 'mainwp_stream', $stream_entry );
123
  $stream_entry_id = $wpdb->insert_id;
147
 
148
  }
149
 
150
+ public static function copy_stream_300_db() {
151
  global $wpdb;
152
+ if ( is_multisite() ) {
153
+ return;
154
+ }
155
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream'" ) !== $wpdb->prefix . "stream" )
156
+ return;
157
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $wpdb->prefix . "stream_meta'" ) !== $wpdb->prefix . "stream_meta" )
158
+ return;
159
 
160
+ $timeout = 20 * 60 * 60; /*20 minutes*/
161
+ $mem = '512M';
162
+ // @codingStandardsIgnoreStart
163
+ @ini_set( 'memory_limit', $mem );
164
+ @set_time_limit( $timeout );
165
+ @ini_set( 'max_execution_time', $timeout );
166
+
167
+ $sql = "SELECT * FROM {$wpdb->prefix}stream";
168
 
169
+ $blog_stream = $wpdb->get_results( $sql, ARRAY_A );
170
+ $printed_connector = array();
171
+
172
+ foreach ( $blog_stream as $key => $stream_entry ) {
173
+
174
+ if (!in_array($stream_entry['connector'], self::$import_connectors)) {
175
+ continue;
176
+ }
177
+
178
+ if ('users' == $stream_entry['connector'] && 'login' == $stream_entry['action']) {
179
+ continue;
180
+ }
181
+
182
+ $prev_entry_id = $stream_entry['ID'];
183
+
184
+ $insert_entry = array(
185
+ 'site_id' => $stream_entry['site_id'],
186
+ 'blog_id' => $stream_entry['blog_id'],
187
+ 'object_id' => $stream_entry['object_id'],
188
+ 'author' => $stream_entry['user_id'],
189
+ 'author_role' => $stream_entry['user_role'],
190
+ 'author_role' => $stream_entry['user_role'],
191
+ 'summary' => $stream_entry['summary'],
192
+ 'visibility' => 'publish',
193
+ 'parent' => 0,
194
+ 'type' => 'stream',
195
+ 'created' => $stream_entry['created'],
196
+ 'ip' => $stream_entry['ip']
197
+ );
198
+
199
+ $wpdb->insert( $wpdb->prefix . 'mainwp_stream', $insert_entry );
200
+ $stream_entry_id = $wpdb->insert_id;
201
+
202
+ $insert_context = array(
203
+ 'record_id' => $stream_entry_id,
204
+ 'context' => $stream_entry['context'],
205
+ 'action' => $stream_entry['action'],
206
+ 'connector' => $stream_entry['connector']
207
+ );
208
+ $wpdb->insert( $wpdb->prefix . 'mainwp_stream_context', $insert_context );
209
+
210
+ $sql = "SELECT * FROM {$wpdb->prefix}stream_meta WHERE record_id = $prev_entry_id";
211
+
212
+ $blog_stream_meta = $wpdb->get_results( $sql, ARRAY_A );
213
+
214
+ foreach ( $blog_stream_meta as $key => $stream_meta ) {
215
+ unset( $stream_meta['meta_id'] );
216
+ $stream_meta['record_id'] = $stream_entry_id;
217
+ $wpdb->insert( $wpdb->prefix . 'mainwp_stream_meta', $stream_meta );
218
+ }
219
  }
220
 
 
 
 
221
  }
222
 
223
  public static function update_notice_hook() {
224
+ // if ( ! current_user_can( WP_Stream_Admin::VIEW_CAP ) ) {
225
+ // return;
226
+ // }
227
+
228
  if ( ! isset( $_REQUEST['mainwp_wp_stream_update'] ) ) {
229
+ self::prompt_copy_data();
230
+ } else {
231
+ check_admin_referer( 'mainwp_wp_stream_update_db' );
232
+ if ( isset( $_REQUEST['mainwp_reports_copy_db_submit'] ) ) {
233
+ self::copy_stream_db();
234
+
235
+ } else if ( isset( $_REQUEST['mainwp_reports_continue_submit'] ) ) {
236
+ update_option('mainwp_child_reports_check_to_copy_data', '');
237
+ }
238
+ }
239
+
240
+ if ('yes' == get_option('mainwp_child_reports_copied_data_ok')) {
241
+ self::prompt_copy_data_status();
242
  }
243
  }
244
+
245
+ public static function prompt_copy_data() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  ?>
247
  <div class="updated">
248
+ <form method="post" action="<?php echo esc_url( remove_query_arg( 'message' ) ) ?>">
249
+ <?php wp_nonce_field( 'mainwp_wp_stream_update_db' ) ?>
250
+ <input type="hidden" name="mainwp_wp_stream_update" value="not_update_and_continue"/>
251
+ <p><strong><?php esc_html_e( 'Do you want to import logs from the Stream plugin?', 'mainwp-child-reports' ) ?></strong></p>
252
+ <p class="submit">
253
+ <?php submit_button( esc_html__( 'Yes', 'mainwp-child-reports' ), 'primary', 'mainwp_reports_copy_db_submit', false ) ?>
254
+ <?php submit_button( esc_html__( 'No', 'mainwp-child-reports' ), 'primary', 'mainwp_reports_continue_submit', false ) ?>
255
+ </p
256
  </form>
257
  </div>
258
  <?php
259
  }
260
+
261
+ public static function prompt_copy_data_status() {
262
+ printf( '<div class="updated"><p>%s</p></div>', __( 'Logs have been successfully imported.', 'mainwp-child-reports' ) );
263
+ delete_option('mainwp_child_reports_copied_data_ok');
 
 
 
 
 
 
 
 
 
 
264
  }
265
+
266
+ public static function get_db_version() {
267
 
268
+ $version = get_site_option( self::KEY );
269
+
270
+ return $version;
271
  }
272
 
273
  public static function install( $current ) {
includes/list-table.php CHANGED
@@ -116,7 +116,7 @@ class MainWP_WP_Stream_List_Table extends WP_List_Table {
116
  $order = 'DESC';
117
  }
118
  if ( ! $orderby = mainwp_wp_stream_filter_input( INPUT_GET, 'orderby' ) ) {
119
- $orderby = '';
120
  }
121
  $args['order'] = $order;
122
  $args['orderby'] = $orderby;
@@ -164,14 +164,28 @@ class MainWP_WP_Stream_List_Table extends WP_List_Table {
164
  function column_default( $item, $column_name ) {
165
  switch ( $column_name ) {
166
  case 'date' :
 
 
 
 
 
 
 
 
 
 
167
  $date_string = sprintf(
168
- '<time datetime="%s" class="relative-time">%s</time>',
169
- $item->created,
170
- get_date_from_gmt( $item->created, 'Y/m/d' )
171
  );
172
- $out = $this->column_link( $date_string, 'date', date( 'Y/m/d', strtotime( $item->created ) ) );
173
- $out .= '<br />';
174
- $out .= get_date_from_gmt( $item->created, 'h:i:s A' );
 
 
 
 
175
  break;
176
 
177
  case 'summary' :
116
  $order = 'DESC';
117
  }
118
  if ( ! $orderby = mainwp_wp_stream_filter_input( INPUT_GET, 'orderby' ) ) {
119
+ $orderby = 'created';
120
  }
121
  $args['order'] = $order;
122
  $args['orderby'] = $orderby;
164
  function column_default( $item, $column_name ) {
165
  switch ( $column_name ) {
166
  case 'date' :
167
+ // $date_string = sprintf(
168
+ // '<time datetime="%s" class="relative-time">%s</time>',
169
+ // $item->created,
170
+ // get_date_from_gmt( $item->created, 'Y/m/d' )
171
+ // );
172
+ // $out = $this->column_link( $date_string, 'date', date( 'Y/m/d', strtotime( $item->created ) ) );
173
+ // $out .= '<br />';
174
+ // $out .= get_date_from_gmt( $item->created, 'h:i:s A' );
175
+ //
176
+ $created = date( 'Y-m-d H:i:s', strtotime( $item->created ) );
177
  $date_string = sprintf(
178
+ '<time datetime="%s" class="relative-time record-created">%s</time>',
179
+ mainwp_wp_stream_get_iso_8601_extended_date( strtotime( $item->created ) ),
180
+ get_date_from_gmt( $created, 'Y/m/d' )
181
  );
182
+ $out = $this->column_link( $date_string, 'date', get_date_from_gmt( $created, 'Y/m/d' ) );
183
+ $out .= '<br />';
184
+ $out .= get_date_from_gmt( $created, 'h:i:s A' );
185
+ $out .= '<span class="timestamp" timestamp="' . strtotime( $item->created ) . '"></span>';
186
+
187
+
188
+
189
  break;
190
 
191
  case 'summary' :
includes/live-update.php CHANGED
@@ -44,8 +44,14 @@ class MainWP_WP_Stream_Live_Update {
44
  if ( ! isset( $data['wp-mainwp-stream-heartbeat-last-id'] ) ) {
45
  return;
46
  }
 
 
 
 
47
 
48
  $last_id = intval( $data['wp-mainwp-stream-heartbeat-last-id'] );
 
 
49
  $query = $data['wp-mainwp-stream-heartbeat-query'];
50
  if ( empty( $query ) ) {
51
  $query = array();
@@ -54,7 +60,7 @@ class MainWP_WP_Stream_Live_Update {
54
  // Decode the query
55
  $query = json_decode( wp_kses_stripslashes( $query ) );
56
 
57
- $updated_items = MainWP_WP_Stream_Dashboard_Widget::gather_updated_items( $last_id, $query );
58
 
59
  if ( ! empty( $updated_items ) ) {
60
  ob_start();
@@ -128,7 +134,7 @@ class MainWP_WP_Stream_Live_Update {
128
  } else {
129
  $response['log'] = 'fail';
130
  }
131
-
132
  return $response;
133
  }
134
 
44
  if ( ! isset( $data['wp-mainwp-stream-heartbeat-last-id'] ) ) {
45
  return;
46
  }
47
+
48
+ if ( ! isset( $data['wp-mainwp-stream-heartbeat-last-created'] ) ) {
49
+ return;
50
+ }
51
 
52
  $last_id = intval( $data['wp-mainwp-stream-heartbeat-last-id'] );
53
+ $last_created = intval( $data['wp-mainwp-stream-heartbeat-last-created'] );
54
+
55
  $query = $data['wp-mainwp-stream-heartbeat-query'];
56
  if ( empty( $query ) ) {
57
  $query = array();
60
  // Decode the query
61
  $query = json_decode( wp_kses_stripslashes( $query ) );
62
 
63
+ $updated_items = MainWP_WP_Stream_Dashboard_Widget::gather_updated_items( $last_id, $query, $last_created );
64
 
65
  if ( ! empty( $updated_items ) ) {
66
  ob_start();
134
  } else {
135
  $response['log'] = 'fail';
136
  }
137
+
138
  return $response;
139
  }
140
 
includes/log.php CHANGED
@@ -22,7 +22,7 @@ class MainWP_WP_Stream_Log {
22
  return self::$instance;
23
  }
24
 
25
- public function log( $connector, $message, $args, $object_id, $contexts, $user_id = null ) {
26
  global $wpdb;
27
 
28
  if ( is_null( $user_id ) ) {
@@ -65,7 +65,7 @@ class MainWP_WP_Stream_Log {
65
  'blog_id' => apply_filters( 'blog_id_logged', is_network_admin() ? 0 : get_current_blog_id() ),
66
  'author' => $user_id,
67
  'author_role' => ! empty( $user->roles ) ? $user->roles[0] : null,
68
- 'created' => current_time( 'mysql', 1 ),
69
  'summary' => vsprintf( $message, $args ),
70
  'parent' => self::$instance->prev_record,
71
  'connector' => $connector,
@@ -79,4 +79,8 @@ class MainWP_WP_Stream_Log {
79
  return $record_id;
80
  }
81
 
 
 
 
 
82
  }
22
  return self::$instance;
23
  }
24
 
25
+ public function log( $connector, $message, $args, $object_id, $contexts, $user_id = null , $created_timestamp = null) {
26
  global $wpdb;
27
 
28
  if ( is_null( $user_id ) ) {
65
  'blog_id' => apply_filters( 'blog_id_logged', is_network_admin() ? 0 : get_current_blog_id() ),
66
  'author' => $user_id,
67
  'author_role' => ! empty( $user->roles ) ? $user->roles[0] : null,
68
+ 'created' => !empty($created_timestamp) ? gmdate("Y-m-d H:i:s", $created_timestamp) : current_time( 'mysql', 1 ),
69
  'summary' => vsprintf( $message, $args ),
70
  'parent' => self::$instance->prev_record,
71
  'connector' => $connector,
79
  return $record_id;
80
  }
81
 
82
+ public function get_log( $agrs = array()) {
83
+ return MainWP_WP_Stream_DB::get_instance()->get_report( $agrs );
84
+ }
85
+
86
  }
includes/query.php CHANGED
@@ -39,6 +39,7 @@ class MainWP_WP_Stream_Query {
39
  'visibility' => null,
40
  // __in params
41
  'record_greater_than' => null,
 
42
  'record__in' => array(),
43
  'record__not_in' => array(),
44
  'record_parent' => '',
@@ -52,7 +53,7 @@ class MainWP_WP_Stream_Query {
52
  'ip__not_in' => array(),
53
  // Order
54
  'order' => 'desc',
55
- 'orderby' => 'ID',
56
  // Meta/Taxonomy sub queries
57
  'meta_query' => array(),
58
  'context_query' => array(),
@@ -143,6 +144,10 @@ class MainWP_WP_Stream_Query {
143
  $where .= $wpdb->prepare( " AND $wpdb->mainwp_reports.ID > %d", (int) $args['record_greater_than'] );
144
  }
145
 
 
 
 
 
146
  if ( $args['record__in'] ) {
147
  $record__in = array_filter( (array) $args['record__in'], 'is_numeric' );
148
  if ( ! empty( $record__in ) ) {
@@ -305,7 +310,7 @@ class MainWP_WP_Stream_Query {
305
  $limits";
306
 
307
  $sql = apply_filters( 'mainwp_wp_stream_query', $sql, $args );
308
-
309
  $results = $wpdb->get_results( $sql );
310
 
311
  if ( 'with-meta' === $fields && is_array( $results ) && $results ) {
39
  'visibility' => null,
40
  // __in params
41
  'record_greater_than' => null,
42
+ 'created_greater_than' => null,
43
  'record__in' => array(),
44
  'record__not_in' => array(),
45
  'record_parent' => '',
53
  'ip__not_in' => array(),
54
  // Order
55
  'order' => 'desc',
56
+ 'orderby' => 'created',
57
  // Meta/Taxonomy sub queries
58
  'meta_query' => array(),
59
  'context_query' => array(),
144
  $where .= $wpdb->prepare( " AND $wpdb->mainwp_reports.ID > %d", (int) $args['record_greater_than'] );
145
  }
146
 
147
+ if ( $args['created_greater_than'] ) {
148
+ $where .= $wpdb->prepare( " AND $wpdb->mainwp_reports.created > %s", (int) $args['created_greater_than'] );
149
+ }
150
+
151
  if ( $args['record__in'] ) {
152
  $record__in = array_filter( (array) $args['record__in'], 'is_numeric' );
153
  if ( ! empty( $record__in ) ) {
310
  $limits";
311
 
312
  $sql = apply_filters( 'mainwp_wp_stream_query', $sql, $args );
313
+
314
  $results = $wpdb->get_results( $sql );
315
 
316
  if ( 'with-meta' === $fields && is_array( $results ) && $results ) {
mainwp-child-reports.php CHANGED
@@ -5,7 +5,7 @@
5
  Description: The MainWP Child Report plugin tracks Child sites for the MainWP Client Reports Extension. The plugin is only useful if you are using MainWP and the Client Reports Extension.
6
  Author: MainWP
7
  Author URI: https://mainwp.com
8
- Version: 0.0.1
9
  */
10
 
11
  /**
@@ -28,7 +28,7 @@
28
 
29
  class MainWP_WP_Stream {
30
 
31
- const VERSION = '0.0.1';
32
 
33
  public static $instance;
34
 
@@ -43,7 +43,8 @@ class MainWP_WP_Stream {
43
  define( 'MAINWP_WP_STREAM_DIR', plugin_dir_path( __FILE__ ) );
44
  define( 'MAINWP_WP_STREAM_URL', plugin_dir_url( __FILE__ ) );
45
  define( 'MAINWP_WP_STREAM_INC_DIR', MAINWP_WP_STREAM_DIR . 'includes/' );
46
-
 
47
  // Load filters polyfill
48
  require_once MAINWP_WP_STREAM_INC_DIR . 'filter-input.php';
49
 
5
  Description: The MainWP Child Report plugin tracks Child sites for the MainWP Client Reports Extension. The plugin is only useful if you are using MainWP and the Client Reports Extension.
6
  Author: MainWP
7
  Author URI: https://mainwp.com
8
+ Version: 1.0
9
  */
10
 
11
  /**
28
 
29
  class MainWP_WP_Stream {
30
 
31
+ const VERSION = '0.0.2';
32
 
33
  public static $instance;
34
 
43
  define( 'MAINWP_WP_STREAM_DIR', plugin_dir_path( __FILE__ ) );
44
  define( 'MAINWP_WP_STREAM_URL', plugin_dir_url( __FILE__ ) );
45
  define( 'MAINWP_WP_STREAM_INC_DIR', MAINWP_WP_STREAM_DIR . 'includes/' );
46
+
47
+ require_once MAINWP_WP_STREAM_INC_DIR . 'functions.php';
48
  // Load filters polyfill
49
  require_once MAINWP_WP_STREAM_INC_DIR . 'filter-input.php';
50
 
readme.txt CHANGED
@@ -6,8 +6,8 @@ Author: mainwp
6
  Author URI: https://mainwp.com
7
  Plugin URI: https://mainwp.com
8
  Requires at least: 3.6
9
- Tested up to: 4.2.4
10
- Stable tag: 0.0.1
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
13
 
@@ -16,11 +16,11 @@ The MainWP Child Report plugin tracks changes to Child sites for the Client Repo
16
 
17
  == Description ==
18
 
19
- **Note: This plugin requires PHP 5.3 or higher to be activated and is only useful if you are using [MainWP](https://wordpress.org/plugins/mainwp/) and the [MainWP Client Reports Extension](https://extensions.mainwp.com/product/mainwp-client-reports-extension/).**
20
 
21
  Install the [MainWP Child Plugin](https://wordpress.org/plugins/mainwp-child/) plugin first.
22
 
23
- The MainWP Child Report plugin communicates changes on your Child sites to the [MainWP Client Reports Extension](https://extensions.mainwp.com/product/mainwp-client-reports-extension/) in order to create the Client Reports.
24
 
25
  Credit to the [Stream Plugin](https://wordpress.org/plugins/stream/) which the MainWP Child Reports plugin is built on.
26
 
@@ -35,4 +35,15 @@ Credit to the [Stream Plugin](https://wordpress.org/plugins/stream/) which the M
35
  2. The MainWP Child Reports Settings Screen
36
 
37
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
38
  * First version - 07-24-15
6
  Author URI: https://mainwp.com
7
  Plugin URI: https://mainwp.com
8
  Requires at least: 3.6
9
+ Tested up to: 4.5
10
+ Stable tag: 1.0
11
  License: GPLv2 or later
12
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
13
 
16
 
17
  == Description ==
18
 
19
+ **Note: This plugin requires PHP 5.3 or higher to be activated and is only useful if you are using [MainWP](https://wordpress.org/plugins/mainwp/) and the [MainWP Client Reports Extension](https://mainwp.com/extension/client-reports/).**
20
 
21
  Install the [MainWP Child Plugin](https://wordpress.org/plugins/mainwp-child/) plugin first.
22
 
23
+ The MainWP Child Report plugin communicates changes on your Child sites to the [MainWP Client Reports Extension](https://mainwp.com/extension/client-reports/) in order to create the Client Reports.
24
 
25
  Credit to the [Stream Plugin](https://wordpress.org/plugins/stream/) which the MainWP Child Reports plugin is built on.
26
 
35
  2. The MainWP Child Reports Settings Screen
36
 
37
  == Changelog ==
38
+
39
+ = 1.0 - 3-9-2016 =
40
+ * Fixed: Issue with recreating tables
41
+ * Fixed: Issue with recreating manually deleted tables
42
+ * Fixed: Issue with updating actions on auto-save Post and Page
43
+ * Fixed: Layout and javascript issue when custom branding is applied
44
+ * Added: Feature to copy reports from the Stream plugin
45
+ * Added: Support for recording BackWPup backups
46
+ * Added: Install Plugins, Install Themes, Delete Plugins, Delete Themes action logging
47
+ * Updated: New timeago js library version
48
+
49
  * First version - 07-24-15
ui/admin.css CHANGED
@@ -1,96 +1,96 @@
1
  /* MainWP Child Reports Records */
2
 
3
- .mainwp-child_page_mainwp_wp_stream .tablenav {
4
  padding-top: 6px;
5
  }
6
 
7
- .mainwp-child_page_mainwp_wp_stream .tablenav.bottom .button {
8
  margin-right: 6px;
9
  }
10
 
11
- .mainwp-child_page_mainwp_wp_stream .tablenav .actions {
12
  padding: 0;
13
  overflow: visible;
14
  }
15
 
16
- .mainwp-child_page_mainwp_wp_stream .chosen-container-single {
17
  margin-top: 3px;
18
  margin-right: 6px;
19
  }
20
 
21
- .mainwp-child_page_mainwp_wp_stream .view-switch {
22
  display: none;
23
  }
24
 
25
- .mainwp-child_page_mainwp_wp_stream #filter-date-range {
26
  float: left;
27
  margin-right: 6px;
28
  }
29
 
30
- .mainwp-child_page_mainwp_wp_stream .column-author {
31
  width: 15%;
32
  }
33
 
34
- .mainwp-child_page_mainwp_wp_stream .column-date,
35
- .mainwp-child_page_mainwp_wp_stream .column-context,
36
- .mainwp-child_page_mainwp_wp_stream .column-action,
37
- .mainwp-child_page_mainwp_wp_stream .column-connector,
38
- .mainwp-child_page_mainwp_wp_stream .column-ip,
39
- .mainwp-child_page_mainwp_wp_stream .column-id {
40
  width: 10%;
41
  }
42
 
43
- .mainwp-child_page_mainwp_wp_stream .stream-filter-object-id {
44
  padding-left: 5px;
45
  visibility: hidden;
46
  }
47
- .mainwp-child_page_mainwp_wp_stream td.summary:hover .stream-filter-object-id {
48
  visibility: visible;
49
  }
50
 
51
  @media only screen and (min-width: 782px) {
52
- .mainwp-child_page_mainwp_wp_stream .tablenav .tablenav-pages {
53
  margin-bottom: 4px;
54
  }
55
  }
56
 
57
  @media only screen and (max-width: 782px) {
58
- .mainwp-child_page_mainwp_wp_stream .tablenav.bottom .displaying-num {
59
  top: -8px;
60
  }
61
  }
62
 
63
  @media only screen and (max-width: 900px) {
64
- .mainwp-child_page_mainwp_wp_stream .fixed .column-context,
65
- .mainwp-child_page_mainwp_wp_stream .fixed .column-action,
66
- .mainwp-child_page_mainwp_wp_stream .fixed .column-connector,
67
- .mainwp-child_page_mainwp_wp_stream .fixed .column-ip,
68
- .mainwp-child_page_mainwp_wp_stream .fixed .column-id {
69
  display: none;
70
  }
71
- .mainwp-child_page_mainwp_wp_stream .fixed .column-date,
72
- .mainwp-child_page_mainwp_wp_stream .fixed .column-author {
73
  display: table-cell;
74
  }
75
- .mainwp-child_page_mainwp_wp_stream .fixed .column-date {
76
  width: 100px;
77
  }
78
- .mainwp-child_page_mainwp_wp_stream .fixed .column-author {
79
  width: 33%;
80
  }
81
  }
82
 
83
  @media only screen and (max-width: 480px) {
84
- .mainwp-child_page_mainwp_wp_stream .fixed .column-author {
85
  display: none;
86
  }
87
  }
88
 
89
- .mainwp-child_page_mainwp_wp_stream .column-author a {
90
  vertical-align : top;
91
  }
92
 
93
- .mainwp-child_page_mainwp_wp_stream .column-author img {
94
  float: left;
95
  margin-right: 10px;
96
  margin-top: 1px;
@@ -98,41 +98,41 @@
98
  height: 40px;
99
  }
100
 
101
- .mainwp-child_page_mainwp_wp_stream .column-author .deleted {
102
  font-style: italic;
103
  color: #aaa;
104
  }
105
 
106
- .mainwp-child_page_mainwp_wp_stream .filter-date-range {
107
  margin-top: -1px;
108
  }
109
 
110
- .mainwp-child_page_mainwp_wp_stream .alignleft.actions input[type=text] {
111
  height: 28px;
112
  line-height: 19px;
113
  }
114
 
115
- .mainwp-child_page_mainwp_wp_stream .date-interval {
116
  display: inline;
117
  }
118
 
119
- .mainwp-child_page_mainwp_wp_stream .select2-container {
120
  margin-right: 6px;
121
  margin-bottom: 6px;
122
  }
123
 
124
- .mainwp-child_page_mainwp_wp_stream .select2-container.select2-allowclear .select2-choice abbr {
125
  margin-top: -2px;
126
  }
127
 
128
 
129
  /* Live Update */
130
 
131
- .mainwp-child_page_mainwp_wp_stream .stream-live-update-checkbox .spinner {
132
  margin-top: 5px;
133
  }
134
 
135
- .mainwp-child_page_mainwp_wp_stream .new-row,
136
  #dashboard_stream_activity .new-row {
137
  background-color: #ffffe0;
138
 
@@ -143,7 +143,7 @@
143
  transition: background 0.5s linear;
144
  }
145
 
146
- .mainwp-child_page_mainwp_wp_stream .new-row.fadeout,
147
  #dashboard_stream_activity .new-row.fadeout {
148
  background-color: transparent;
149
  }
1
  /* MainWP Child Reports Records */
2
 
3
+ .mainwp_wp_stream_screen .tablenav {
4
  padding-top: 6px;
5
  }
6
 
7
+ .mainwp_wp_stream_screen .tablenav.bottom .button {
8
  margin-right: 6px;
9
  }
10
 
11
+ .mainwp_wp_stream_screen .tablenav .actions {
12
  padding: 0;
13
  overflow: visible;
14
  }
15
 
16
+ .mainwp_wp_stream_screen .chosen-container-single {
17
  margin-top: 3px;
18
  margin-right: 6px;
19
  }
20
 
21
+ .mainwp_wp_stream_screen .view-switch {
22
  display: none;
23
  }
24
 
25
+ .mainwp_wp_stream_screen #filter-date-range {
26
  float: left;
27
  margin-right: 6px;
28
  }
29
 
30
+ .mainwp_wp_stream_screen .column-author {
31
  width: 15%;
32
  }
33
 
34
+ .mainwp_wp_stream_screen .column-date,
35
+ .mainwp_wp_stream_screen .column-context,
36
+ .mainwp_wp_stream_screen .column-action,
37
+ .mainwp_wp_stream_screen .column-connector,
38
+ .mainwp_wp_stream_screen .column-ip,
39
+ .mainwp_wp_stream_screen .column-id {
40
  width: 10%;
41
  }
42
 
43
+ .mainwp_wp_stream_screen .stream-filter-object-id {
44
  padding-left: 5px;
45
  visibility: hidden;
46
  }
47
+ .mainwp_wp_stream_screen td.summary:hover .stream-filter-object-id {
48
  visibility: visible;
49
  }
50
 
51
  @media only screen and (min-width: 782px) {
52
+ .mainwp_wp_stream_screen .tablenav .tablenav-pages {
53
  margin-bottom: 4px;
54
  }
55
  }
56
 
57
  @media only screen and (max-width: 782px) {
58
+ .mainwp_wp_stream_screen .tablenav.bottom .displaying-num {
59
  top: -8px;
60
  }
61
  }
62
 
63
  @media only screen and (max-width: 900px) {
64
+ .mainwp_wp_stream_screen .fixed .column-context,
65
+ .mainwp_wp_stream_screen .fixed .column-action,
66
+ .mainwp_wp_stream_screen .fixed .column-connector,
67
+ .mainwp_wp_stream_screen .fixed .column-ip,
68
+ .mainwp_wp_stream_screen .fixed .column-id {
69
  display: none;
70
  }
71
+ .mainwp_wp_stream_screen .fixed .column-date,
72
+ .mainwp_wp_stream_screen .fixed .column-author {
73
  display: table-cell;
74
  }
75
+ .mainwp_wp_stream_screen .fixed .column-date {
76
  width: 100px;
77
  }
78
+ .mainwp_wp_stream_screen .fixed .column-author {
79
  width: 33%;
80
  }
81
  }
82
 
83
  @media only screen and (max-width: 480px) {
84
+ .mainwp_wp_stream_screen .fixed .column-author {
85
  display: none;
86
  }
87
  }
88
 
89
+ .mainwp_wp_stream_screen .column-author a {
90
  vertical-align : top;
91
  }
92
 
93
+ .mainwp_wp_stream_screen .column-author img {
94
  float: left;
95
  margin-right: 10px;
96
  margin-top: 1px;
98
  height: 40px;
99
  }
100
 
101
+ .mainwp_wp_stream_screen .column-author .deleted {
102
  font-style: italic;
103
  color: #aaa;
104
  }
105
 
106
+ .mainwp_wp_stream_screen .filter-date-range {
107
  margin-top: -1px;
108
  }
109
 
110
+ .mainwp_wp_stream_screen .alignleft.actions input[type=text] {
111
  height: 28px;
112
  line-height: 19px;
113
  }
114
 
115
+ .mainwp_wp_stream_screen .date-interval {
116
  display: inline;
117
  }
118
 
119
+ .mainwp_wp_stream_screen .select2-container {
120
  margin-right: 6px;
121
  margin-bottom: 6px;
122
  }
123
 
124
+ .mainwp_wp_stream_screen .select2-container.select2-allowclear .select2-choice abbr {
125
  margin-top: -2px;
126
  }
127
 
128
 
129
  /* Live Update */
130
 
131
+ .mainwp_wp_stream_screen .stream-live-update-checkbox .spinner {
132
  margin-top: 5px;
133
  }
134
 
135
+ .mainwp_wp_stream_screen .new-row,
136
  #dashboard_stream_activity .new-row {
137
  background-color: #ffffe0;
138
 
143
  transition: background 0.5s linear;
144
  }
145
 
146
+ .mainwp_wp_stream_screen .new-row.fadeout,
147
  #dashboard_stream_activity .new-row.fadeout {
148
  background-color: transparent;
149
  }
ui/admin.js CHANGED
@@ -1,7 +1,17 @@
1
  /* globals confirm, mainwp_wp_stream, ajaxurl */
2
  jQuery(function( $ ) {
3
-
4
- $( '.mainwp-child_page_mainwp_wp_stream :input.chosen-select' ).each(function( i, el ) {
 
 
 
 
 
 
 
 
 
 
5
  var args = {},
6
  formatResult = function( record, container ) {
7
  var result = '',
@@ -289,7 +299,7 @@ jQuery(function( $ ) {
289
  }).trigger( 'change' );
290
 
291
  $( window ).load(function() {
292
- $( '.mainwp-child_page_mainwp_wp_stream [type=search]' ).off( 'mousedown' );
293
  });
294
 
295
  // Confirmation on some important actions
@@ -305,12 +315,6 @@ jQuery(function( $ ) {
305
  }
306
  });
307
 
308
- $( '#mainwp_wp_stream_uninstall' ).click(function( e ) {
309
- if ( ! confirm( mainwp_wp_stream.i18n.confirm_uninstall ) ) {
310
- e.preventDefault();
311
- }
312
- });
313
-
314
  // Admin page tabs
315
  var $tabs = $( '.nav-tab-wrapper' ),
316
  $panels = $( '.nav-tab-content table.form-table' ),
@@ -348,34 +352,40 @@ jQuery(function( $ ) {
348
  // Heartbeat for Live Updates
349
  // runs only on stream page (not settings)
350
  $( document ).ready(function() {
351
-
352
  // Only run on page 1 when the order is desc and on page mainwp_wp_stream
353
  if (
354
- 'mainwp-child_page_mainwp_wp_stream' !== mainwp_wp_stream.current_screen ||
355
  '1' !== mainwp_wp_stream.current_page ||
356
  'asc' === mainwp_wp_stream.current_order
357
  ) {
358
  return;
359
  }
360
 
361
- var list_sel = '.mainwp-child_page_mainwp_wp_stream #the-list';
362
 
363
  // Set initial beat to fast. WP is designed to slow this to 15 seconds after 2.5 minutes.
364
  wp.heartbeat.interval( 'fast' );
365
 
366
- $( document ).on( 'heartbeat-send.stream', function( e, data ) {
367
  data['wp-mainwp-stream-heartbeat'] = 'live-update';
368
  var last_item = $( list_sel + ' tr:first .column-id' );
369
  var last_id = 1;
370
  if ( last_item.length !== 0 ) {
371
- last_id = ( '' === last_item.text() ) ? 1 : last_item.text();
 
 
 
 
 
372
  }
 
373
  data['wp-mainwp-stream-heartbeat-last-id'] = last_id;
 
374
  data['wp-mainwp-stream-heartbeat-query'] = mainwp_wp_stream.current_query;
375
  });
376
 
377
  // Listen for "heartbeat-tick" on $(document).
378
- $( document ).on( 'heartbeat-tick.stream', function( e, data ) {
379
 
380
  // If this no rows return then we kill the script
381
  if ( ! data['wp-mainwp-stream-heartbeat'] || 0 === data['wp-mainwp-stream-heartbeat'].length ) {
1
  /* globals confirm, mainwp_wp_stream, ajaxurl */
2
  jQuery(function( $ ) {
3
+ // Shorter timeago strings for English locale
4
+ if ( 'en' === mainwp_wp_stream.locale && 'undefined' !== typeof $.timeago ) {
5
+ $.timeago.settings.strings.seconds = 'seconds';
6
+ $.timeago.settings.strings.minute = 'a minute';
7
+ $.timeago.settings.strings.hour = 'an hour';
8
+ $.timeago.settings.strings.hours = '%d hours';
9
+ $.timeago.settings.strings.month = 'a month';
10
+ $.timeago.settings.strings.year = 'a year';
11
+ }
12
+
13
+
14
+ $( '.mainwp_wp_stream_screen :input.chosen-select' ).each(function( i, el ) {
15
  var args = {},
16
  formatResult = function( record, container ) {
17
  var result = '',
299
  }).trigger( 'change' );
300
 
301
  $( window ).load(function() {
302
+ $( '.mainwp_wp_stream_screen [type=search]' ).off( 'mousedown' );
303
  });
304
 
305
  // Confirmation on some important actions
315
  }
316
  });
317
 
 
 
 
 
 
 
318
  // Admin page tabs
319
  var $tabs = $( '.nav-tab-wrapper' ),
320
  $panels = $( '.nav-tab-content table.form-table' ),
352
  // Heartbeat for Live Updates
353
  // runs only on stream page (not settings)
354
  $( document ).ready(function() {
 
355
  // Only run on page 1 when the order is desc and on page mainwp_wp_stream
356
  if (
357
+ mainwp_wp_stream.current_screen.indexOf('_page_mainwp_wp_stream') == -1 ||
358
  '1' !== mainwp_wp_stream.current_page ||
359
  'asc' === mainwp_wp_stream.current_order
360
  ) {
361
  return;
362
  }
363
 
364
+ var list_sel = '.mainwp_wp_stream_screen #the-list';
365
 
366
  // Set initial beat to fast. WP is designed to slow this to 15 seconds after 2.5 minutes.
367
  wp.heartbeat.interval( 'fast' );
368
 
369
+ $( document ).on( 'heartbeat-send.child_reports', function( e, data ) {
370
  data['wp-mainwp-stream-heartbeat'] = 'live-update';
371
  var last_item = $( list_sel + ' tr:first .column-id' );
372
  var last_id = 1;
373
  if ( last_item.length !== 0 ) {
374
+ last_id = ( '' === last_item.text() ) ? 1 : last_item.text();
375
+ }
376
+ var last_created_item = $( list_sel + ' tr:first .column-date span.timestamp' );
377
+ var last_created = 0;
378
+ if ( last_created_item.length !== 0 ) {
379
+ last_created = last_created_item.data['timestamp'];
380
  }
381
+
382
  data['wp-mainwp-stream-heartbeat-last-id'] = last_id;
383
+ data['wp-mainwp-stream-heartbeat-last-created'] = last_created;
384
  data['wp-mainwp-stream-heartbeat-query'] = mainwp_wp_stream.current_query;
385
  });
386
 
387
  // Listen for "heartbeat-tick" on $(document).
388
+ $( document ).on( 'heartbeat-tick.child_reports', function( e, data ) {
389
 
390
  // If this no rows return then we kill the script
391
  if ( ! data['wp-mainwp-stream-heartbeat'] || 0 === data['wp-mainwp-stream-heartbeat'].length ) {
ui/dashboard.js DELETED
@@ -1,113 +0,0 @@
1
- /* dashboard pagination */
2
- jQuery(function( $ ) {
3
-
4
- $( '#dashboard_stream_activity' ).on( 'click', '.pagination-links a', function( e ) {
5
- e.preventDefault();
6
- var data = {
7
- 'action': 'mainwp_stream_activity_dashboard_update',
8
- 'stream-paged': $( this ).data( 'page' ),
9
- };
10
-
11
- $.post( window.ajaxurl, data, function( response ) {
12
- $( '#dashboard_stream_activity .inside' ).html( response );
13
- });
14
- });
15
-
16
- /**
17
- * Dashboard Live Update
18
- */
19
- $( document ).ready( function() {
20
- wp.heartbeat.interval( 'fast' );
21
-
22
- var widget_sel = '#dashboard_stream_activity';
23
- var list_sel = widget_sel + ' .inside ul';
24
-
25
- $( document ).on( 'heartbeat-send.stream', function( e, data ) {
26
- data['wp-mainwp-stream-heartbeat'] = 'dashboard-update';
27
- var last_item = $( list_sel + ' li:first' );
28
- var last_id = 1;
29
- if ( 0 !== last_item.length ) {
30
- last_id = ( '' === last_item.attr( 'data-id' ) ) ? 1 : last_item.attr( 'data-id' );
31
- }
32
- data['wp-mainwp-stream-heartbeat-last-id'] = last_id;
33
- });
34
-
35
- // Listen for "heartbeat-tick" on $(document).
36
- $(document).on( 'heartbeat-tick.stream', function( e, data ) {
37
-
38
- // If this no rows return then we kill the script
39
- if ( ! data['wp-mainwp-stream-heartbeat'] || 0 === data['wp-mainwp-stream-heartbeat'].length ) {
40
- return;
41
- }
42
-
43
- //Prevent updates from being applied directly to pages that aren't the first in the list
44
- if ( ! $( '#dashboard_stream_activity a.prev-page' ).hasClass( 'disabled' ) ) {
45
- return;
46
- }
47
-
48
- // Get all current rows
49
- var $current_items = $( list_sel + ' li' );
50
-
51
- // Get all new rows
52
- var $new_items = $( data['wp-mainwp-stream-heartbeat'].replace( /(\r\n|\n|\r)/gm, '' ) );
53
-
54
- // Remove all class to tr added by WP and add new row class
55
- $new_items.removeClass().addClass( 'new-row' );
56
-
57
- //Check if first tr has the alternate class
58
- var has_class = $current_items.first().hasClass( 'alternate' );
59
-
60
- // Apply the good class to the list
61
- if ( 1 === $new_items.length && ! has_class ) {
62
- $new_items.addClass( 'alternate' );
63
- } else {
64
- var even_or_odd = ( 0 === $new_items.length % 2 && ! has_class ) ? 'even' : 'odd';
65
- // Add class to nth child because there is more than one element
66
- $new_items.filter( ':nth-child(' + even_or_odd + ')' ).addClass( 'alternate' );
67
- }
68
-
69
- var item_array = [];
70
- $( $new_items ).each(function() {
71
- if ( 'string' === typeof $( this ).html() ) {
72
- item_array.push( $( this ) );
73
- }
74
- });
75
-
76
- // Remove the number of element added to the end of the list table
77
- var show_on_screen = data.per_page || 5;
78
- var slice_items = show_on_screen - ( $new_items.length + $current_items.length );
79
-
80
- if ( slice_items < 0 ) {
81
- $( list_sel + ' li' ).slice( slice_items ).remove();
82
- }
83
-
84
- // Remove the no items paragraph
85
- $( widget_sel + ' p.no-items' ).remove();
86
-
87
- //Use records per page here
88
- var added_items = item_array.slice( 0, data.per_page );
89
-
90
- // Add element to the dom
91
- $( list_sel ).prepend( added_items );
92
-
93
- // Update pagination
94
- var total_items_i18n = data.total_items_i18n || '';
95
- if ( total_items_i18n ) {
96
- $( widget_sel + ' .total-pages' ).text( data.total_pages_i18n );
97
- $( widget_sel + ' .pagination-links' ).find( '.next-page, .last-page') .toggleClass( 'disabled', data.total_pages === $( '.current-page' ).val() );
98
- $( widget_sel + ' .pagination-links .last-page' ).attr( 'data-page', data.total_pages ).attr( 'href', data.last_page_link );
99
- }
100
-
101
- // Remove background after a certain amount of time
102
- setTimeout(function() {
103
- $( '.new-row' ).addClass( 'fadeout' );
104
- setTimeout(function() {
105
- $( list_sel + ' li' ).removeClass( 'new-row fadeout' );
106
- }, 500 );
107
- }, 3000 );
108
-
109
- });
110
-
111
- });
112
-
113
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/{timeago.js → jquery.timeago.js} RENAMED
@@ -3,7 +3,7 @@
3
  * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
4
  *
5
  * @name timeago
6
- * @version 1.3.1
7
  * @requires jQuery v1.2.3+
8
  * @author Ryan McGeary
9
  * @license MIT License - http://www.opensource.org/licenses/mit-license.php
@@ -11,7 +11,7 @@
11
  * For usage and examples, visit:
12
  * http://timeago.yarp.com/
13
  *
14
- * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
15
  */
16
 
17
  (function (factory) {
@@ -23,7 +23,7 @@
23
  factory(jQuery);
24
  }
25
  }(function ($) {
26
- $.timeago = function(timestamp) {
27
  if (timestamp instanceof Date) {
28
  return inWords(timestamp);
29
  } else if (typeof timestamp === "string") {
@@ -39,6 +39,7 @@
39
  $.extend($.timeago, {
40
  settings: {
41
  refreshMillis: 60000,
 
42
  allowFuture: false,
43
  localeTitle: false,
44
  cutoff: 0,
@@ -47,6 +48,7 @@
47
  prefixFromNow: null,
48
  suffixAgo: "ago",
49
  suffixFromNow: "from now",
 
50
  seconds: "less than a minute",
51
  minute: "about a minute",
52
  minutes: "%d minutes",
@@ -62,7 +64,12 @@
62
  numbers: []
63
  }
64
  },
 
65
  inWords: function(distanceMillis) {
 
 
 
 
66
  var $l = this.settings.strings;
67
  var prefix = $l.prefixAgo;
68
  var suffix = $l.suffixAgo;
@@ -73,6 +80,10 @@
73
  }
74
  }
75
 
 
 
 
 
76
  var seconds = Math.abs(distanceMillis) / 1000;
77
  var minutes = seconds / 60;
78
  var hours = minutes / 60;
@@ -101,6 +112,7 @@
101
  if ($l.wordSeparator === undefined) { separator = " "; }
102
  return $.trim([prefix, words, suffix].join(separator));
103
  },
 
104
  parse: function(iso8601) {
105
  var s = $.trim(iso8601);
106
  s = s.replace(/\.\d+/,""); // remove milliseconds
@@ -127,18 +139,18 @@
127
  init: function(){
128
  var refresh_el = $.proxy(refresh, this);
129
  refresh_el();
130
- var $s = $t.settings;
131
  if ($s.refreshMillis > 0) {
132
  this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
133
  }
134
  },
135
  update: function(time){
136
- var parsedTime = $t.parse(time);
137
  $(this).data('timeago', { datetime: parsedTime });
138
  if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
139
  refresh.apply(this);
140
  },
141
- updateFromDOM: function(){
142
  $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
143
  refresh.apply(this);
144
  },
@@ -154,7 +166,7 @@
154
  var fn = action ? functions[action] : functions.init;
155
  if(!fn){
156
  throw new Error("Unknown function name '"+ action +"' for timeago");
157
- }
158
  // each over objects here and call the requested function
159
  this.each(function(){
160
  fn.call(this, options);
@@ -163,11 +175,17 @@
163
  };
164
 
165
  function refresh() {
 
 
 
 
 
 
166
  var data = prepareData(this);
167
  var $s = $t.settings;
168
-
169
  if (!isNaN(data.datetime)) {
170
- if ( $s.cutoff == 0 || distance(data.datetime) < $s.cutoff) {
171
  $(this).text(inWords(data.datetime));
172
  }
173
  }
@@ -176,7 +194,7 @@
176
 
177
  function prepareData(element) {
178
  element = $(element);
179
- if (!element.data("timeago")) {
180
  element.data("timeago", { datetime: $t.datetime(element) });
181
  var text = $.trim(element.text());
182
  if ($t.settings.localeTitle) {
@@ -188,29 +206,15 @@
188
  return element.data("timeago");
189
  }
190
 
191
- function inWords(date) {
192
  return $t.inWords(distance(date));
193
  }
194
 
195
  function distance(date) {
196
- // return (new Date().getTime() - date.getTime());
197
- var UTC_date_string = new Date().toUTCString(); // get current UTC Time in string form
198
- var millis_since_epoch = Date.parse(UTC_date_string); // convert it to milliseconds
199
-
200
- /* The date passed in comes to us in the client's timezone because it was created using the javascript
201
- * new Date() constructor when it was parsed form the DOM. Unforunately, the date in the DOM is from
202
- * the server, and already in UTC. We can account for this discrepancy by subtracting the client's
203
- * timezone offset.
204
- */
205
- var date_millis = date.getTime(); // convert passed in date to milliseconds (still wrong timezone)
206
- var minutes_off = date.getTimezoneOffset(); // get the number of minutes this timezone differs from UTC time
207
- var millis_off = minutes_off*60*1000; // convert from minutes to milliseconds
208
- var date_millis_since_epoch = date_millis - millis_off; // subtract the discrepancy from the date milliseconds
209
-
210
- return (millis_since_epoch - date_millis_since_epoch); // finally, find the difference
211
  }
212
 
213
  // fix for IE6 suckage
214
  document.createElement("abbr");
215
  document.createElement("time");
216
- }));
3
  * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
4
  *
5
  * @name timeago
6
+ * @version 1.4.1
7
  * @requires jQuery v1.2.3+
8
  * @author Ryan McGeary
9
  * @license MIT License - http://www.opensource.org/licenses/mit-license.php
11
  * For usage and examples, visit:
12
  * http://timeago.yarp.com/
13
  *
14
+ * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
15
  */
16
 
17
  (function (factory) {
23
  factory(jQuery);
24
  }
25
  }(function ($) {
26
+ $.timeago = function(timestamp) {
27
  if (timestamp instanceof Date) {
28
  return inWords(timestamp);
29
  } else if (typeof timestamp === "string") {
39
  $.extend($.timeago, {
40
  settings: {
41
  refreshMillis: 60000,
42
+ allowPast: true,
43
  allowFuture: false,
44
  localeTitle: false,
45
  cutoff: 0,
48
  prefixFromNow: null,
49
  suffixAgo: "ago",
50
  suffixFromNow: "from now",
51
+ inPast: 'any moment now',
52
  seconds: "less than a minute",
53
  minute: "about a minute",
54
  minutes: "%d minutes",
64
  numbers: []
65
  }
66
  },
67
+
68
  inWords: function(distanceMillis) {
69
+ if(!this.settings.allowPast && ! this.settings.allowFuture) {
70
+ throw 'timeago allowPast and allowFuture settings can not both be set to false.';
71
+ }
72
+
73
  var $l = this.settings.strings;
74
  var prefix = $l.prefixAgo;
75
  var suffix = $l.suffixAgo;
80
  }
81
  }
82
 
83
+ if(!this.settings.allowPast && distanceMillis >= 0) {
84
+ return this.settings.strings.inPast;
85
+ }
86
+
87
  var seconds = Math.abs(distanceMillis) / 1000;
88
  var minutes = seconds / 60;
89
  var hours = minutes / 60;
112
  if ($l.wordSeparator === undefined) { separator = " "; }
113
  return $.trim([prefix, words, suffix].join(separator));
114
  },
115
+
116
  parse: function(iso8601) {
117
  var s = $.trim(iso8601);
118
  s = s.replace(/\.\d+/,""); // remove milliseconds
139
  init: function(){
140
  var refresh_el = $.proxy(refresh, this);
141
  refresh_el();
142
+ var $s = $t.settings;
143
  if ($s.refreshMillis > 0) {
144
  this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
145
  }
146
  },
147
  update: function(time){
148
+ var parsedTime = $t.parse(time);
149
  $(this).data('timeago', { datetime: parsedTime });
150
  if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
151
  refresh.apply(this);
152
  },
153
+ updateFromDOM: function(){
154
  $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
155
  refresh.apply(this);
156
  },
166
  var fn = action ? functions[action] : functions.init;
167
  if(!fn){
168
  throw new Error("Unknown function name '"+ action +"' for timeago");
169
+ }
170
  // each over objects here and call the requested function
171
  this.each(function(){
172
  fn.call(this, options);
175
  };
176
 
177
  function refresh() {
178
+ //check if it's still visible
179
+ if(!$.contains(document.documentElement,this)){
180
+ //stop if it has been removed
181
+ $(this).timeago("dispose");
182
+ return this;
183
+ }
184
  var data = prepareData(this);
185
  var $s = $t.settings;
186
+
187
  if (!isNaN(data.datetime)) {
188
+ if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
189
  $(this).text(inWords(data.datetime));
190
  }
191
  }
194
 
195
  function prepareData(element) {
196
  element = $(element);
197
+ if (!element.data("timeago")) {
198
  element.data("timeago", { datetime: $t.datetime(element) });
199
  var text = $.trim(element.text());
200
  if ($t.settings.localeTitle) {
206
  return element.data("timeago");
207
  }
208
 
209
+ function inWords(date) {
210
  return $t.inWords(distance(date));
211
  }
212
 
213
  function distance(date) {
214
+ return (new Date().getTime() - date.getTime());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
 
217
  // fix for IE6 suckage
218
  document.createElement("abbr");
219
  document.createElement("time");
220
+ }));
ui/timeago/locale/jquery.timeago.cz.js DELETED
@@ -1,18 +0,0 @@
1
- // Czech
2
- jQuery.timeago.settings.strings = {
3
- prefixAgo: "před",
4
- prefixFromNow: null,
5
- suffixAgo: null,
6
- suffixFromNow: null,
7
- seconds: "méně než minutou",
8
- minute: "minutou",
9
- minutes: "%d minutami",
10
- hour: "hodinou",
11
- hours: "%d hodinami",
12
- day: "1 dnem",
13
- days: "%d dny",
14
- month: "1 měsícem",
15
- months: "%d měsíci",
16
- year: "1 rokem",
17
- years: "%d roky"
18
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/locale/jquery.timeago.he.js DELETED
@@ -1,18 +0,0 @@
1
- // Hebrew
2
- jQuery.timeago.settings.strings = {
3
- prefixAgo: "לפני",
4
- prefixFromNow: "מעכשיו",
5
- suffixAgo: "",
6
- suffixFromNow: "",
7
- seconds: "פחות מדקה",
8
- minute: "דקה",
9
- minutes: "%d דקות",
10
- hour: "שעה",
11
- hours: "%d שעות",
12
- day: "יום",
13
- days: "%d ימים",
14
- month: "חודש",
15
- months: "%d חודשים",
16
- year: "שנה",
17
- years: "%d שנים"
18
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/locale/jquery.timeago.sl.js DELETED
@@ -1,44 +0,0 @@
1
- // Slovenian with support for dual
2
- (function () {
3
- var numpf;
4
- numpf = function (n, d, m) {
5
- if (n == 2) {
6
- return d;
7
- } else {
8
- return m;
9
- }
10
- };
11
-
12
- jQuery.timeago.settings.strings = {
13
- prefixAgo: "pred",
14
- prefixFromNow: "čez",
15
- suffixAgo: null,
16
- suffixFromNow: null,
17
- second: "sekundo",
18
- seconds: function (value) {
19
- return numpf(value, "%d sekundama", "%d sekundami");
20
- },
21
- minute: "minuto",
22
- minutes: function (value) {
23
- return numpf(value, "%d minutama", "%d minutami");
24
- },
25
- hour: "uro",
26
- hours: function (value) {
27
- return numpf(value, "%d urama", "%d urami");
28
- },
29
- day: "dnevom",
30
- days: function (value) {
31
- return numpf(value, "%d dnevi", "%d dnevi");
32
- },
33
- month: "enim mescem",
34
- months: function (value) {
35
- return numpf(value, "%d mesecema", "%d meseci");
36
- },
37
- year: "enim letom",
38
- years: function (value) {
39
- return numpf(value, "%d letoma", "%d leti");
40
- },
41
- wordSeparator: " "
42
- };
43
-
44
- }).call(this);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/locale/jquery.timeago.zh-CN.js DELETED
@@ -1,20 +0,0 @@
1
- // Simplified Chinese
2
- jQuery.timeago.settings.strings = {
3
- prefixAgo: null,
4
- prefixFromNow: "从现在开始",
5
- suffixAgo: "之前",
6
- suffixFromNow: null,
7
- seconds: "不到 1 分钟",
8
- minute: "大约 1 分钟",
9
- minutes: "%d 分钟",
10
- hour: "大约 1 小时",
11
- hours: "大约 %d 小时",
12
- day: "1 天",
13
- days: "%d 天",
14
- month: "大约 1 个月",
15
- months: "%d 月",
16
- year: "大约 1 年",
17
- years: "%d 年",
18
- numbers: [],
19
- wordSeparator: ""
20
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/locale/jquery.timeago.zh-TW.js DELETED
@@ -1,20 +0,0 @@
1
- // Traditional Chinese, zh-tw
2
- jQuery.timeago.settings.strings = {
3
- prefixAgo: null,
4
- prefixFromNow: "從現在開始",
5
- suffixAgo: "之前",
6
- suffixFromNow: null,
7
- seconds: "不到 1 分鐘",
8
- minute: "大約 1 分鐘",
9
- minutes: "%d 分鐘",
10
- hour: "大約 1 小時",
11
- hours: "%d 小時",
12
- day: "大約 1 天",
13
- days: "%d 天",
14
- month: "大約 1 個月",
15
- months: "%d 個月",
16
- year: "大約 1 年",
17
- years: "%d 年",
18
- numbers: [],
19
- wordSeparator: ""
20
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui/timeago/locales/README.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Locale override examples for timeago
2
+
3
+ You can represent time statements in most western languages where
4
+ a prefix and/or suffix is used.
5
+
6
+ The default case is to use suffix only (as in English), which you
7
+ do by providing the `suffixAgo` and `suffixFromNow` settings in
8
+ the strings hash (earlier versions of timeago used the deprecated
9
+ `ago` and `fromNow` options). If present, they are used.
10
+
11
+ 2 minutes [suffixAgo]
12
+ 2 minutes [suffixFromNow]
13
+
14
+ In case you want to use prefix only instead of
15
+ suffix (e.g. Greek), you provide the `prefixAgo` and
16
+ `prefixFromNow` options in the strings hash and leave `suffixAgo`
17
+ and `suffixFromNow` empty or null.
18
+
19
+ [prefixAgo] 2 minutes
20
+ [prefixFromNow] 2 minutes
21
+
22
+ For languages where you want to use a prefix only for future
23
+ tense and prefix/suffix for past tense (for example swedish), you
24
+ can combine the prefix and suffixes as needed.
25
+
26
+ [prefixAgo] 2 minutes [suffixAgo]
27
+ [prefixFromNow] 2 minutes
ui/timeago/{locale → locales}/jquery.timeago.ar.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.bg.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.bs.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.ca.js RENAMED
File without changes
ui/timeago/locales/jquery.timeago.cs.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Czech
2
+ (function() {
3
+ function f(n, d, a) {
4
+ return a[d>=0 ? 0 : a.length==2 || n<5 ? 1 : 2];
5
+ }
6
+
7
+ jQuery.timeago.settings.strings = {
8
+ prefixAgo: 'před',
9
+ prefixFromNow: 'za',
10
+ suffixAgo: null,
11
+ suffixFromNow: null,
12
+ seconds: function(n, d) {return f(n, d, ['méně než minutou', 'méně než minutu'])},
13
+ minute: function(n, d) {return f(n, d, ['minutou', 'minutu'])},
14
+ minutes: function(n, d) {return f(n, d, ['%d minutami', '%d minuty', '%d minut'])},
15
+ hour: function(n, d) {return f(n, d, ['hodinou', 'hodinu'])},
16
+ hours: function(n, d) {return f(n, d, ['%d hodinami', '%d hodiny', '%d hodin'])},
17
+ day: function(n, d) {return f(n, d, ['%d dnem', '%d den'])},
18
+ days: function(n, d) {return f(n, d, ['%d dny', '%d dny', '%d dní'])},
19
+ month: function(n, d) {return f(n, d, ['%d měsícem', '%d měsíc'])},
20
+ months: function(n, d) {return f(n, d, ['%d měsící', '%d měsíce', '%d měsíců'])},
21
+ year: function(n, d) {return f(n, d, ['%d rokem', '%d rok'])},
22
+ years: function(n, d) {return f(n, d, ['%d lety', '%d roky', '%d let'])}
23
+ };
24
+ })();
ui/timeago/{locale → locales}/jquery.timeago.cy.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.da.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.de.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.el.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.en-short.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.en.js RENAMED
@@ -4,17 +4,17 @@ jQuery.timeago.settings.strings = {
4
  prefixFromNow: null,
5
  suffixAgo: "ago",
6
  suffixFromNow: "from now",
7
- seconds: "a minute",
8
- minute: "a minute",
9
  minutes: "%d minutes",
10
- hour: "an hour",
11
- hours: "%d hours",
12
  day: "a day",
13
  days: "%d days",
14
- month: "a month",
15
  months: "%d months",
16
- year: "a year",
17
  years: "%d years",
18
  wordSeparator: " ",
19
  numbers: []
20
- };
4
  prefixFromNow: null,
5
  suffixAgo: "ago",
6
  suffixFromNow: "from now",
7
+ seconds: "less than a minute",
8
+ minute: "about a minute",
9
  minutes: "%d minutes",
10
+ hour: "about an hour",
11
+ hours: "about %d hours",
12
  day: "a day",
13
  days: "%d days",
14
+ month: "about a month",
15
  months: "%d months",
16
+ year: "about a year",
17
  years: "%d years",
18
  wordSeparator: " ",
19
  numbers: []
20
+ };
ui/timeago/locales/jquery.timeago.es-short.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Spanish shortened
2
+ jQuery.timeago.settings.strings = {
3
+ prefixAgo: null,
4
+ prefixFromNow: null,
5
+ suffixAgo: "",
6
+ suffixFromNow: "",
7
+ seconds: "1m",
8
+ minute: "1m",
9
+ minutes: "%dm",
10
+ hour: "1h",
11
+ hours: "%dh",
12
+ day: "1d",
13
+ days: "%dd",
14
+ month: "1me",
15
+ months: "%dme",
16
+ year: "1a",
17
+ years: "%da",
18
+ wordSeparator: " ",
19
+ numbers: []
20
+ };
ui/timeago/{locale → locales}/jquery.timeago.es.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.et.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.fa.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.fi.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.fr-short.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.fr.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.gl.js RENAMED
File without changes
ui/timeago/locales/jquery.timeago.he.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Hebrew
2
+ jQuery.timeago.settings.strings = {
3
+ prefixAgo: "לפני",
4
+ prefixFromNow: "עוד",
5
+ seconds: "פחות מדקה",
6
+ minute: "דקה",
7
+ minutes: "%d דקות",
8
+ hour: "שעה",
9
+ hours: function(number){return (number==2) ? "שעתיים" : "%d שעות"},
10
+ day: "יום",
11
+ days: function(number){return (number==2) ? "יומיים" : "%d ימים"},
12
+ month: "חודש",
13
+ months: function(number){return (number==2) ? "חודשיים" : "%d חודשים"},
14
+ year: "שנה",
15
+ years: function(number){return (number==2) ? "שנתיים" : "%d שנים"}
16
+ };
ui/timeago/{locale → locales}/jquery.timeago.hr.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.hu.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.hy.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.id.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.is.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.it.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.ja.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.jv.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.ko.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.lt.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.mk.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.nl.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.no.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.pl.js RENAMED
File without changes
ui/timeago/locales/jquery.timeago.pt-br-short.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Portuguese Brasil shortened
2
+ jQuery.timeago.settings.strings = {
3
+ prefixAgo: null,
4
+ prefixFromNow: null,
5
+ suffixAgo: "",
6
+ suffixFromNow: "",
7
+ seconds: "1m",
8
+ minute: "1m",
9
+ minutes: "%dm",
10
+ hour: "1h",
11
+ hours: "%dh",
12
+ day: "1d",
13
+ days: "%dd",
14
+ month: "1M",
15
+ months: "%dM",
16
+ year: "1a",
17
+ years: "%da",
18
+ wordSeparator: " ",
19
+ numbers: []
20
+ };
ui/timeago/{locale → locales}/jquery.timeago.pt-br.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.pt.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.ro.js RENAMED
@@ -1,5 +1,5 @@
1
  // Romanian
2
- $.timeago.settings.strings = {
3
  prefixAgo: "acum",
4
  prefixFromNow: "in timp de",
5
  suffixAgo: "",
@@ -15,4 +15,4 @@ $.timeago.settings.strings = {
15
  months: "%d luni",
16
  year: "un an",
17
  years: "%d ani"
18
- };
1
  // Romanian
2
+ jQuery.timeago.settings.strings = {
3
  prefixAgo: "acum",
4
  prefixFromNow: "in timp de",
5
  suffixAgo: "",
15
  months: "%d luni",
16
  year: "un an",
17
  years: "%d ani"
18
+ };
ui/timeago/{locale → locales}/jquery.timeago.rs.js RENAMED
@@ -39,7 +39,7 @@
39
  months: function (value) {
40
  return numpf(value, "%d mesec", "%d meseca", "%d meseci");
41
  },
42
- year: "pre godinu dana",
43
  years: function (value) {
44
  return numpf(value, "%d godinu", "%d godine", "%d godina");
45
  },
39
  months: function (value) {
40
  return numpf(value, "%d mesec", "%d meseca", "%d meseci");
41
  },
42
+ year: "godinu dana",
43
  years: function (value) {
44
  return numpf(value, "%d godinu", "%d godine", "%d godina");
45
  },
ui/timeago/{locale → locales}/jquery.timeago.ru.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.sk.js RENAMED
File without changes
ui/timeago/locales/jquery.timeago.sl.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Slovenian with support for dual
2
+ (function () {
3
+ var numpf;
4
+ numpf = function (n, a) {
5
+ return a[n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0];
6
+ };
7
+
8
+ jQuery.timeago.settings.strings = {
9
+ prefixAgo: null,
10
+ prefixFromNow: "čez",
11
+ suffixAgo: "nazaj",
12
+ suffixFromNow: null,
13
+ second: "sekundo",
14
+ seconds: function (value) {
15
+ return numpf(value, ["%d sekund", "%d sekundo", "%d sekundi", "%d sekunde"]);
16
+ },
17
+ minute: "minuto",
18
+ minutes: function (value) {
19
+ return numpf(value, ["%d minut", "%d minuto", "%d minuti", "%d minute"]);
20
+ },
21
+ hour: "eno uro",
22
+ hours: function (value) {
23
+ return numpf(value, ["%d ur", "%d uro", "%d uri", "%d ure"]);
24
+ },
25
+ day: "en dan",
26
+ days: function (value) {
27
+ return numpf(value, ["%d dni", "%d dan", "%d dneva", "%d dni"]);
28
+ },
29
+ month: "en mesec",
30
+ months: function (value) {
31
+ return numpf(value, ["%d mescov", "%d mesec", "%d mesca", "%d mesce"]);
32
+ },
33
+ year: "eno leto",
34
+ years: function (value) {
35
+ return numpf(value, ["%d let", "%d leto", "%d leti", "%d leta"]);
36
+ },
37
+ wordSeparator: " "
38
+ };
39
+
40
+ }).call(this);
ui/timeago/{locale → locales}/jquery.timeago.sv.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.th.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.tr.js RENAMED
@@ -1,5 +1,5 @@
1
  // Turkish
2
- jQuery.extend($.timeago.settings.strings, {
3
  suffixAgo: 'önce',
4
  suffixFromNow: null,
5
  seconds: '1 dakikadan',
@@ -13,4 +13,4 @@ jQuery.extend($.timeago.settings.strings, {
13
  months: '%d ay',
14
  year: '1 yıl',
15
  years: '%d yıl'
16
- });
1
  // Turkish
2
+ jQuery.timeago.settings.strings = {
3
  suffixAgo: 'önce',
4
  suffixFromNow: null,
5
  seconds: '1 dakikadan',
13
  months: '%d ay',
14
  year: '1 yıl',
15
  years: '%d yıl'
16
+ };
ui/timeago/{locale → locales}/jquery.timeago.uk.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.uz.js RENAMED
File without changes
ui/timeago/{locale → locales}/jquery.timeago.vi.js RENAMED
File without changes
ui/timeago/locales/jquery.timeago.zh-CN.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Simplified Chinese
2
+ jQuery.timeago.settings.strings = {
3
+ prefixAgo: null,
4
+ prefixFromNow: "从现在开始",
5
+ suffixAgo: "之前",
6
+ suffixFromNow: null,
7
+ seconds: "不到1分钟",
8
+ minute: "大约1分钟",
9
+ minutes: "%d分钟",
10
+ hour: "大约1小时",
11
+ hours: "大约%d小时",
12
+ day: "1天",
13
+ days: "%d天",
14
+ month: "大约1个月",
15
+ months: "%d月",
16
+ year: "大约1年",
17
+ years: "%d年",
18
+ numbers: [],
19
+ wordSeparator: ""
20
+ };
ui/timeago/locales/jquery.timeago.zh-TW.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Traditional Chinese, zh-tw
2
+ jQuery.timeago.settings.strings = {
3
+ prefixAgo: null,
4
+ prefixFromNow: "從現在開始",
5
+ suffixAgo: "之前",
6
+ suffixFromNow: null,
7
+ seconds: "不到1分鐘",
8
+ minute: "大約1分鐘",
9
+ minutes: "%d分鐘",
10
+ hour: "大約1小時",
11
+ hours: "%d小時",
12
+ day: "大約1天",
13
+ days: "%d天",
14
+ month: "大約1個月",
15
+ months: "%d個月",
16
+ year: "大約1年",
17
+ years: "%d年",
18
+ numbers: [],
19
+ wordSeparator: ""
20
+ };