Simple History - Version 2.36.0

Version Description

Download this release

Release Info

Developer eskapism
Plugin Icon 128x128 Simple History
Version 2.36.0
Comparing to
See all releases

Code changes from version 2.35.1 to 2.36.0

composer.json CHANGED
@@ -26,5 +26,10 @@
26
  "dist": {
27
  "url": "https://downloads.wordpress.org/plugin/simple-history.zip",
28
  "type": "zip"
 
 
 
 
 
29
  }
30
  }
26
  "dist": {
27
  "url": "https://downloads.wordpress.org/plugin/simple-history.zip",
28
  "type": "zip"
29
+ },
30
+ "scripts": {
31
+ "lint": "./vendor/bin/phpcs .",
32
+ "lint-fix": "./vendor/bin/phpcbf .",
33
+ "test": "./vendor/bin/phpunit"
34
  }
35
  }
css/github-markdown.css CHANGED
@@ -4,124 +4,127 @@ License: MIT © Sindre Sorhus
4
  */
5
 
6
  @font-face {
7
- font-family: octicons-anchor;
8
- src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');
 
9
  }
10
 
11
  .markdown-body {
12
- -ms-text-size-adjust: 100%;
13
- -webkit-text-size-adjust: 100%;
14
- color: #333;
15
- overflow: hidden;
16
- font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
17
- font-size: 16px;
18
- line-height: 1.6;
19
- word-wrap: break-word;
 
20
  }
21
 
22
  .markdown-body a {
23
- background: transparent;
24
  }
25
 
26
  .markdown-body a:active,
27
  .markdown-body a:hover {
28
- outline: 0;
29
  }
30
 
31
  .markdown-body strong {
32
- font-weight: bold;
33
  }
34
 
35
  .markdown-body h1 {
36
- font-size: 2em;
37
- margin: 0.67em 0;
38
  }
39
 
40
  .markdown-body img {
41
- border: 0;
42
  }
43
 
44
  .markdown-body hr {
45
- box-sizing: content-box;
46
- height: 0;
47
  }
48
 
49
  .markdown-body pre {
50
- overflow: auto;
51
  }
52
 
53
  .markdown-body code,
54
  .markdown-body kbd,
55
  .markdown-body pre {
56
- font-family: monospace, monospace;
57
- font-size: 1em;
58
  }
59
 
60
  .markdown-body input {
61
- color: inherit;
62
- font: inherit;
63
- margin: 0;
64
  }
65
 
66
  .markdown-body html input[disabled] {
67
- cursor: default;
68
  }
69
 
70
  .markdown-body input {
71
- line-height: normal;
72
  }
73
 
74
  .markdown-body input[type="checkbox"] {
75
- box-sizing: border-box;
76
- padding: 0;
77
  }
78
 
79
  .markdown-body table {
80
- border-collapse: collapse;
81
- border-spacing: 0;
82
  }
83
 
84
  .markdown-body td,
85
  .markdown-body th {
86
- padding: 0;
87
  }
88
 
89
  .markdown-body * {
90
- box-sizing: border-box;
91
  }
92
 
93
  .markdown-body input {
94
- font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
 
95
  }
96
 
97
  .markdown-body a {
98
- color: #4183c4;
99
- text-decoration: none;
100
  }
101
 
102
  .markdown-body a:hover,
103
  .markdown-body a:active {
104
- text-decoration: underline;
105
  }
106
 
107
  .markdown-body hr {
108
- height: 0;
109
- margin: 15px 0;
110
- overflow: hidden;
111
- background: transparent;
112
- border: 0;
113
- border-bottom: 1px solid #ddd;
114
  }
115
 
116
  .markdown-body hr:before {
117
- display: table;
118
- content: "";
119
  }
120
 
121
  .markdown-body hr:after {
122
- display: table;
123
- clear: both;
124
- content: "";
125
  }
126
 
127
  .markdown-body h1,
@@ -130,115 +133,115 @@ License: MIT © Sindre Sorhus
130
  .markdown-body h4,
131
  .markdown-body h5,
132
  .markdown-body h6 {
133
- margin-top: 15px;
134
- margin-bottom: 15px;
135
- line-height: 1.1;
136
  }
137
 
138
  .markdown-body h1 {
139
- font-size: 30px;
140
  }
141
 
142
  .markdown-body h2 {
143
- font-size: 21px;
144
  }
145
 
146
  .markdown-body h3 {
147
- font-size: 16px;
148
  }
149
 
150
  .markdown-body h4 {
151
- font-size: 14px;
152
  }
153
 
154
  .markdown-body h5 {
155
- font-size: 12px;
156
  }
157
 
158
  .markdown-body h6 {
159
- font-size: 11px;
160
  }
161
 
162
  .markdown-body blockquote {
163
- margin: 0;
164
  }
165
 
166
  .markdown-body ul,
167
  .markdown-body ol {
168
- padding: 0;
169
- margin-top: 0;
170
- margin-bottom: 0;
171
  }
172
 
173
  .markdown-body ol ol,
174
  .markdown-body ul ol {
175
- list-style-type: lower-roman;
176
  }
177
 
178
  .markdown-body ul ul ol,
179
  .markdown-body ul ol ol,
180
  .markdown-body ol ul ol,
181
  .markdown-body ol ol ol {
182
- list-style-type: lower-alpha;
183
  }
184
 
185
  .markdown-body dd {
186
- margin-left: 0;
187
  }
188
 
189
  .markdown-body code {
190
- font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
191
- font-size: 12px;
192
  }
193
 
194
  .markdown-body pre {
195
- margin-top: 0;
196
- margin-bottom: 0;
197
- font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
198
  }
199
 
200
  .markdown-body .octicon {
201
- font: normal normal normal 16px/1 octicons-anchor;
202
- display: inline-block;
203
- text-decoration: none;
204
- text-rendering: auto;
205
- -webkit-font-smoothing: antialiased;
206
- -moz-osx-font-smoothing: grayscale;
207
- -webkit-user-select: none;
208
- -moz-user-select: none;
209
- -ms-user-select: none;
210
- user-select: none;
211
  }
212
 
213
  .markdown-body .octicon-link:before {
214
- content: '\f05c';
215
  }
216
 
217
- .markdown-body>*:first-child {
218
- margin-top: 0 !important;
219
  }
220
 
221
- .markdown-body>*:last-child {
222
- margin-bottom: 0 !important;
223
  }
224
 
225
  .markdown-body a:not(:link):not(:visited) {
226
- color: inherit;
227
- text-decoration: none;
228
  }
229
 
230
  .markdown-body .anchor {
231
- position: absolute;
232
- top: 0;
233
- left: 0;
234
- display: block;
235
- padding-right: 6px;
236
- padding-left: 30px;
237
- margin-left: -30px;
238
  }
239
 
240
  .markdown-body .anchor:focus {
241
- outline: none;
242
  }
243
 
244
  .markdown-body h1,
@@ -247,11 +250,11 @@ License: MIT © Sindre Sorhus
247
  .markdown-body h4,
248
  .markdown-body h5,
249
  .markdown-body h6 {
250
- position: relative;
251
- margin-top: 1em;
252
- margin-bottom: 16px;
253
- font-weight: bold;
254
- line-height: 1.4;
255
  }
256
 
257
  .markdown-body h1 .octicon-link,
@@ -260,9 +263,9 @@ License: MIT © Sindre Sorhus
260
  .markdown-body h4 .octicon-link,
261
  .markdown-body h5 .octicon-link,
262
  .markdown-body h6 .octicon-link {
263
- display: none;
264
- color: #000;
265
- vertical-align: middle;
266
  }
267
 
268
  .markdown-body h1:hover .anchor,
@@ -271,9 +274,9 @@ License: MIT © Sindre Sorhus
271
  .markdown-body h4:hover .anchor,
272
  .markdown-body h5:hover .anchor,
273
  .markdown-body h6:hover .anchor {
274
- padding-left: 8px;
275
- margin-left: -30px;
276
- text-decoration: none;
277
  }
278
 
279
  .markdown-body h1:hover .anchor .octicon-link,
@@ -282,63 +285,63 @@ License: MIT © Sindre Sorhus
282
  .markdown-body h4:hover .anchor .octicon-link,
283
  .markdown-body h5:hover .anchor .octicon-link,
284
  .markdown-body h6:hover .anchor .octicon-link {
285
- display: inline-block;
286
  }
287
 
288
  .markdown-body h1 {
289
- padding-bottom: 0.3em;
290
- font-size: 2.25em;
291
- line-height: 1.2;
292
- border-bottom: 1px solid #eee;
293
  }
294
 
295
  .markdown-body h1 .anchor {
296
- line-height: 1;
297
  }
298
 
299
  .markdown-body h2 {
300
- padding-bottom: 0.3em;
301
- font-size: 1.75em;
302
- line-height: 1.225;
303
- border-bottom: 1px solid #eee;
304
  }
305
 
306
  .markdown-body h2 .anchor {
307
- line-height: 1;
308
  }
309
 
310
  .markdown-body h3 {
311
- font-size: 1.5em;
312
- line-height: 1.43;
313
  }
314
 
315
  .markdown-body h3 .anchor {
316
- line-height: 1.2;
317
  }
318
 
319
  .markdown-body h4 {
320
- font-size: 1.25em;
321
  }
322
 
323
  .markdown-body h4 .anchor {
324
- line-height: 1.2;
325
  }
326
 
327
  .markdown-body h5 {
328
- font-size: 1em;
329
  }
330
 
331
  .markdown-body h5 .anchor {
332
- line-height: 1.1;
333
  }
334
 
335
  .markdown-body h6 {
336
- font-size: 1em;
337
- color: #777;
338
  }
339
 
340
  .markdown-body h6 .anchor {
341
- line-height: 1.1;
342
  }
343
 
344
  .markdown-body p,
@@ -348,180 +351,180 @@ License: MIT © Sindre Sorhus
348
  .markdown-body dl,
349
  .markdown-body table,
350
  .markdown-body pre {
351
- margin-top: 0;
352
- margin-bottom: 16px;
353
  }
354
 
355
  .markdown-body hr {
356
- height: 4px;
357
- padding: 0;
358
- margin: 16px 0;
359
- background-color: #e7e7e7;
360
- border: 0 none;
361
  }
362
 
363
  .markdown-body ul,
364
  .markdown-body ol {
365
- padding-left: 2em;
366
  }
367
 
368
  .markdown-body ul ul,
369
  .markdown-body ul ol,
370
  .markdown-body ol ol,
371
  .markdown-body ol ul {
372
- margin-top: 0;
373
- margin-bottom: 0;
374
  }
375
 
376
- .markdown-body li>p {
377
- margin-top: 16px;
378
  }
379
 
380
  .markdown-body dl {
381
- padding: 0;
382
  }
383
 
384
  .markdown-body dl dt {
385
- padding: 0;
386
- margin-top: 16px;
387
- font-size: 1em;
388
- font-style: italic;
389
- font-weight: bold;
390
  }
391
 
392
  .markdown-body dl dd {
393
- padding: 0 16px;
394
- margin-bottom: 16px;
395
  }
396
 
397
  .markdown-body blockquote {
398
- padding: 0 15px;
399
- color: #777;
400
- border-left: 4px solid #ddd;
401
  }
402
 
403
- .markdown-body blockquote>:first-child {
404
- margin-top: 0;
405
  }
406
 
407
- .markdown-body blockquote>:last-child {
408
- margin-bottom: 0;
409
  }
410
 
411
  .markdown-body table {
412
- display: block;
413
- width: 100%;
414
- overflow: auto;
415
- word-break: normal;
416
- word-break: keep-all;
417
  }
418
 
419
  .markdown-body table th {
420
- font-weight: bold;
421
  }
422
 
423
  .markdown-body table th,
424
  .markdown-body table td {
425
- padding: 6px 13px;
426
- border: 1px solid #ddd;
427
  }
428
 
429
  .markdown-body table tr {
430
- background-color: #fff;
431
- border-top: 1px solid #ccc;
432
  }
433
 
434
  .markdown-body table tr:nth-child(2n) {
435
- background-color: #f8f8f8;
436
  }
437
 
438
  .markdown-body img {
439
- max-width: 100%;
440
- box-sizing: border-box;
441
  }
442
 
443
  .markdown-body code {
444
- padding: 0;
445
- padding-top: 0.2em;
446
- padding-bottom: 0.2em;
447
- margin: 0;
448
- font-size: 85%;
449
- background-color: rgba(0,0,0,0.04);
450
- border-radius: 3px;
451
  }
452
 
453
  .markdown-body code:before,
454
  .markdown-body code:after {
455
- letter-spacing: -0.2em;
456
- content: "\00a0";
457
  }
458
 
459
- .markdown-body pre>code {
460
- padding: 0;
461
- margin: 0;
462
- font-size: 100%;
463
- word-break: normal;
464
- white-space: pre;
465
- background: transparent;
466
- border: 0;
467
  }
468
 
469
  .markdown-body .highlight {
470
- margin-bottom: 16px;
471
  }
472
 
473
  .markdown-body .highlight pre,
474
  .markdown-body pre {
475
- padding: 16px;
476
- overflow: auto;
477
- font-size: 85%;
478
- line-height: 1.45;
479
- background-color: #f7f7f7;
480
- border-radius: 3px;
481
  }
482
 
483
  .markdown-body .highlight pre {
484
- margin-bottom: 0;
485
- word-break: normal;
486
  }
487
 
488
  .markdown-body pre {
489
- word-wrap: normal;
490
  }
491
 
492
  .markdown-body pre code {
493
- display: inline;
494
- max-width: initial;
495
- padding: 0;
496
- margin: 0;
497
- overflow: initial;
498
- line-height: inherit;
499
- word-wrap: normal;
500
- background-color: transparent;
501
- border: 0;
502
  }
503
 
504
  .markdown-body pre code:before,
505
  .markdown-body pre code:after {
506
- content: normal;
507
  }
508
 
509
  .markdown-body kbd {
510
- display: inline-block;
511
- padding: 3px 5px;
512
- font-size: 11px;
513
- line-height: 10px;
514
- color: #555;
515
- vertical-align: middle;
516
- background-color: #fcfcfc;
517
- border: solid 1px #ccc;
518
- border-bottom-color: #bbb;
519
- border-radius: 3px;
520
- box-shadow: inset 0 -1px 0 #bbb;
521
  }
522
 
523
  .markdown-body .pl-c {
524
- color: #969896;
525
  }
526
 
527
  .markdown-body .pl-c1,
@@ -533,12 +536,12 @@ License: MIT © Sindre Sorhus
533
  .markdown-body .pl-s3,
534
  .markdown-body .pl-sc,
535
  .markdown-body .pl-sv {
536
- color: #0086b3;
537
  }
538
 
539
  .markdown-body .pl-e,
540
  .markdown-body .pl-en {
541
- color: #795da3;
542
  }
543
 
544
  .markdown-body .pl-s1 .pl-s2,
@@ -547,17 +550,17 @@ License: MIT © Sindre Sorhus
547
  .markdown-body .pl-stj,
548
  .markdown-body .pl-vo,
549
  .markdown-body .pl-vpf {
550
- color: #333;
551
  }
552
 
553
  .markdown-body .pl-ent {
554
- color: #63a35c;
555
  }
556
 
557
  .markdown-body .pl-k,
558
  .markdown-body .pl-s,
559
  .markdown-body .pl-st {
560
- color: #a71d5d;
561
  }
562
 
563
  .markdown-body .pl-pds,
@@ -568,102 +571,102 @@ License: MIT © Sindre Sorhus
568
  .markdown-body .pl-sr .pl-sra,
569
  .markdown-body .pl-sr .pl-sre,
570
  .markdown-body .pl-src {
571
- color: #183691;
572
  }
573
 
574
  .markdown-body .pl-v {
575
- color: #ed6a43;
576
  }
577
 
578
  .markdown-body .pl-id {
579
- color: #b52a1d;
580
  }
581
 
582
  .markdown-body .pl-ii {
583
- background-color: #b52a1d;
584
- color: #f8f8f8;
585
  }
586
 
587
  .markdown-body .pl-sr .pl-cce {
588
- color: #63a35c;
589
- font-weight: bold;
590
  }
591
 
592
  .markdown-body .pl-ml {
593
- color: #693a17;
594
  }
595
 
596
  .markdown-body .pl-mh,
597
  .markdown-body .pl-mh .pl-en,
598
  .markdown-body .pl-ms {
599
- color: #1d3e81;
600
- font-weight: bold;
601
  }
602
 
603
  .markdown-body .pl-mq {
604
- color: #008080;
605
  }
606
 
607
  .markdown-body .pl-mi {
608
- color: #333;
609
- font-style: italic;
610
  }
611
 
612
  .markdown-body .pl-mb {
613
- color: #333;
614
- font-weight: bold;
615
  }
616
 
617
  .markdown-body .pl-md,
618
  .markdown-body .pl-mdhf {
619
- background-color: #ffecec;
620
- color: #bd2c00;
621
  }
622
 
623
  .markdown-body .pl-mdht,
624
  .markdown-body .pl-mi1 {
625
- background-color: #eaffea;
626
- color: #55a532;
627
  }
628
 
629
  .markdown-body .pl-mdr {
630
- color: #795da3;
631
- font-weight: bold;
632
  }
633
 
634
  .markdown-body .pl-mo {
635
- color: #1d3e81;
636
  }
637
 
638
  .markdown-body kbd {
639
- display: inline-block;
640
- padding: 3px 5px;
641
- font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
642
- line-height: 10px;
643
- color: #555;
644
- vertical-align: middle;
645
- background-color: #fcfcfc;
646
- border: solid 1px #ccc;
647
- border-bottom-color: #bbb;
648
- border-radius: 3px;
649
- box-shadow: inset 0 -1px 0 #bbb;
650
  }
651
 
652
  .markdown-body .task-list-item {
653
- list-style-type: none;
654
  }
655
 
656
- .markdown-body .task-list-item+.task-list-item {
657
- margin-top: 3px;
658
  }
659
 
660
  .markdown-body .task-list-item input {
661
- margin: 0 0.35em 0.25em -1.6em;
662
- vertical-align: middle;
663
  }
664
 
665
- .markdown-body :checked+.radio-label {
666
- z-index: 1;
667
- position: relative;
668
- border-color: #4183c4;
669
- }
4
  */
5
 
6
  @font-face {
7
+ font-family: octicons-anchor;
8
+ src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==)
9
+ format("woff");
10
  }
11
 
12
  .markdown-body {
13
+ -ms-text-size-adjust: 100%;
14
+ -webkit-text-size-adjust: 100%;
15
+ color: #333;
16
+ overflow: hidden;
17
+ font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans,
18
+ sans-serif;
19
+ font-size: 16px;
20
+ line-height: 1.6;
21
+ word-wrap: break-word;
22
  }
23
 
24
  .markdown-body a {
25
+ background: transparent;
26
  }
27
 
28
  .markdown-body a:active,
29
  .markdown-body a:hover {
30
+ outline: 0;
31
  }
32
 
33
  .markdown-body strong {
34
+ font-weight: bold;
35
  }
36
 
37
  .markdown-body h1 {
38
+ font-size: 2em;
39
+ margin: 0.67em 0;
40
  }
41
 
42
  .markdown-body img {
43
+ border: 0;
44
  }
45
 
46
  .markdown-body hr {
47
+ box-sizing: content-box;
48
+ height: 0;
49
  }
50
 
51
  .markdown-body pre {
52
+ overflow: auto;
53
  }
54
 
55
  .markdown-body code,
56
  .markdown-body kbd,
57
  .markdown-body pre {
58
+ font-family: monospace, monospace;
59
+ font-size: 1em;
60
  }
61
 
62
  .markdown-body input {
63
+ color: inherit;
64
+ font: inherit;
65
+ margin: 0;
66
  }
67
 
68
  .markdown-body html input[disabled] {
69
+ cursor: default;
70
  }
71
 
72
  .markdown-body input {
73
+ line-height: normal;
74
  }
75
 
76
  .markdown-body input[type="checkbox"] {
77
+ box-sizing: border-box;
78
+ padding: 0;
79
  }
80
 
81
  .markdown-body table {
82
+ border-collapse: collapse;
83
+ border-spacing: 0;
84
  }
85
 
86
  .markdown-body td,
87
  .markdown-body th {
88
+ padding: 0;
89
  }
90
 
91
  .markdown-body * {
92
+ box-sizing: border-box;
93
  }
94
 
95
  .markdown-body input {
96
+ font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean,
97
+ sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
98
  }
99
 
100
  .markdown-body a {
101
+ color: #4183c4;
102
+ text-decoration: none;
103
  }
104
 
105
  .markdown-body a:hover,
106
  .markdown-body a:active {
107
+ text-decoration: underline;
108
  }
109
 
110
  .markdown-body hr {
111
+ height: 0;
112
+ margin: 15px 0;
113
+ overflow: hidden;
114
+ background: transparent;
115
+ border: 0;
116
+ border-bottom: 1px solid #ddd;
117
  }
118
 
119
  .markdown-body hr:before {
120
+ display: table;
121
+ content: "";
122
  }
123
 
124
  .markdown-body hr:after {
125
+ display: table;
126
+ clear: both;
127
+ content: "";
128
  }
129
 
130
  .markdown-body h1,
133
  .markdown-body h4,
134
  .markdown-body h5,
135
  .markdown-body h6 {
136
+ margin-top: 15px;
137
+ margin-bottom: 15px;
138
+ line-height: 1.1;
139
  }
140
 
141
  .markdown-body h1 {
142
+ font-size: 30px;
143
  }
144
 
145
  .markdown-body h2 {
146
+ font-size: 21px;
147
  }
148
 
149
  .markdown-body h3 {
150
+ font-size: 16px;
151
  }
152
 
153
  .markdown-body h4 {
154
+ font-size: 14px;
155
  }
156
 
157
  .markdown-body h5 {
158
+ font-size: 12px;
159
  }
160
 
161
  .markdown-body h6 {
162
+ font-size: 11px;
163
  }
164
 
165
  .markdown-body blockquote {
166
+ margin: 0;
167
  }
168
 
169
  .markdown-body ul,
170
  .markdown-body ol {
171
+ padding: 0;
172
+ margin-top: 0;
173
+ margin-bottom: 0;
174
  }
175
 
176
  .markdown-body ol ol,
177
  .markdown-body ul ol {
178
+ list-style-type: lower-roman;
179
  }
180
 
181
  .markdown-body ul ul ol,
182
  .markdown-body ul ol ol,
183
  .markdown-body ol ul ol,
184
  .markdown-body ol ol ol {
185
+ list-style-type: lower-alpha;
186
  }
187
 
188
  .markdown-body dd {
189
+ margin-left: 0;
190
  }
191
 
192
  .markdown-body code {
193
+ font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
194
+ font-size: 12px;
195
  }
196
 
197
  .markdown-body pre {
198
+ margin-top: 0;
199
+ margin-bottom: 0;
200
+ font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
201
  }
202
 
203
  .markdown-body .octicon {
204
+ font: normal normal normal 16px/1 octicons-anchor;
205
+ display: inline-block;
206
+ text-decoration: none;
207
+ text-rendering: auto;
208
+ -webkit-font-smoothing: antialiased;
209
+ -moz-osx-font-smoothing: grayscale;
210
+ -webkit-user-select: none;
211
+ -moz-user-select: none;
212
+ -ms-user-select: none;
213
+ user-select: none;
214
  }
215
 
216
  .markdown-body .octicon-link:before {
217
+ content: "\f05c";
218
  }
219
 
220
+ .markdown-body > *:first-child {
221
+ margin-top: 0 !important;
222
  }
223
 
224
+ .markdown-body > *:last-child {
225
+ margin-bottom: 0 !important;
226
  }
227
 
228
  .markdown-body a:not(:link):not(:visited) {
229
+ color: inherit;
230
+ text-decoration: none;
231
  }
232
 
233
  .markdown-body .anchor {
234
+ position: absolute;
235
+ top: 0;
236
+ left: 0;
237
+ display: block;
238
+ padding-right: 6px;
239
+ padding-left: 30px;
240
+ margin-left: -30px;
241
  }
242
 
243
  .markdown-body .anchor:focus {
244
+ outline: none;
245
  }
246
 
247
  .markdown-body h1,
250
  .markdown-body h4,
251
  .markdown-body h5,
252
  .markdown-body h6 {
253
+ position: relative;
254
+ margin-top: 1em;
255
+ margin-bottom: 16px;
256
+ font-weight: bold;
257
+ line-height: 1.4;
258
  }
259
 
260
  .markdown-body h1 .octicon-link,
263
  .markdown-body h4 .octicon-link,
264
  .markdown-body h5 .octicon-link,
265
  .markdown-body h6 .octicon-link {
266
+ display: none;
267
+ color: #000;
268
+ vertical-align: middle;
269
  }
270
 
271
  .markdown-body h1:hover .anchor,
274
  .markdown-body h4:hover .anchor,
275
  .markdown-body h5:hover .anchor,
276
  .markdown-body h6:hover .anchor {
277
+ padding-left: 8px;
278
+ margin-left: -30px;
279
+ text-decoration: none;
280
  }
281
 
282
  .markdown-body h1:hover .anchor .octicon-link,
285
  .markdown-body h4:hover .anchor .octicon-link,
286
  .markdown-body h5:hover .anchor .octicon-link,
287
  .markdown-body h6:hover .anchor .octicon-link {
288
+ display: inline-block;
289
  }
290
 
291
  .markdown-body h1 {
292
+ padding-bottom: 0.3em;
293
+ font-size: 2.25em;
294
+ line-height: 1.2;
295
+ border-bottom: 1px solid #eee;
296
  }
297
 
298
  .markdown-body h1 .anchor {
299
+ line-height: 1;
300
  }
301
 
302
  .markdown-body h2 {
303
+ padding-bottom: 0.3em;
304
+ font-size: 1.75em;
305
+ line-height: 1.225;
306
+ border-bottom: 1px solid #eee;
307
  }
308
 
309
  .markdown-body h2 .anchor {
310
+ line-height: 1;
311
  }
312
 
313
  .markdown-body h3 {
314
+ font-size: 1.5em;
315
+ line-height: 1.43;
316
  }
317
 
318
  .markdown-body h3 .anchor {
319
+ line-height: 1.2;
320
  }
321
 
322
  .markdown-body h4 {
323
+ font-size: 1.25em;
324
  }
325
 
326
  .markdown-body h4 .anchor {
327
+ line-height: 1.2;
328
  }
329
 
330
  .markdown-body h5 {
331
+ font-size: 1em;
332
  }
333
 
334
  .markdown-body h5 .anchor {
335
+ line-height: 1.1;
336
  }
337
 
338
  .markdown-body h6 {
339
+ font-size: 1em;
340
+ color: #777;
341
  }
342
 
343
  .markdown-body h6 .anchor {
344
+ line-height: 1.1;
345
  }
346
 
347
  .markdown-body p,
351
  .markdown-body dl,
352
  .markdown-body table,
353
  .markdown-body pre {
354
+ margin-top: 0;
355
+ margin-bottom: 16px;
356
  }
357
 
358
  .markdown-body hr {
359
+ height: 4px;
360
+ padding: 0;
361
+ margin: 16px 0;
362
+ background-color: #e7e7e7;
363
+ border: 0 none;
364
  }
365
 
366
  .markdown-body ul,
367
  .markdown-body ol {
368
+ padding-left: 2em;
369
  }
370
 
371
  .markdown-body ul ul,
372
  .markdown-body ul ol,
373
  .markdown-body ol ol,
374
  .markdown-body ol ul {
375
+ margin-top: 0;
376
+ margin-bottom: 0;
377
  }
378
 
379
+ .markdown-body li > p {
380
+ margin-top: 16px;
381
  }
382
 
383
  .markdown-body dl {
384
+ padding: 0;
385
  }
386
 
387
  .markdown-body dl dt {
388
+ padding: 0;
389
+ margin-top: 16px;
390
+ font-size: 1em;
391
+ font-style: italic;
392
+ font-weight: bold;
393
  }
394
 
395
  .markdown-body dl dd {
396
+ padding: 0 16px;
397
+ margin-bottom: 16px;
398
  }
399
 
400
  .markdown-body blockquote {
401
+ padding: 0 15px;
402
+ color: #777;
403
+ border-left: 4px solid #ddd;
404
  }
405
 
406
+ .markdown-body blockquote > :first-child {
407
+ margin-top: 0;
408
  }
409
 
410
+ .markdown-body blockquote > :last-child {
411
+ margin-bottom: 0;
412
  }
413
 
414
  .markdown-body table {
415
+ display: block;
416
+ width: 100%;
417
+ overflow: auto;
418
+ word-break: normal;
419
+ word-break: keep-all;
420
  }
421
 
422
  .markdown-body table th {
423
+ font-weight: bold;
424
  }
425
 
426
  .markdown-body table th,
427
  .markdown-body table td {
428
+ padding: 6px 13px;
429
+ border: 1px solid #ddd;
430
  }
431
 
432
  .markdown-body table tr {
433
+ background-color: #fff;
434
+ border-top: 1px solid #ccc;
435
  }
436
 
437
  .markdown-body table tr:nth-child(2n) {
438
+ background-color: #f8f8f8;
439
  }
440
 
441
  .markdown-body img {
442
+ max-width: 100%;
443
+ box-sizing: border-box;
444
  }
445
 
446
  .markdown-body code {
447
+ padding: 0;
448
+ padding-top: 0.2em;
449
+ padding-bottom: 0.2em;
450
+ margin: 0;
451
+ font-size: 85%;
452
+ background-color: rgba(0, 0, 0, 0.04);
453
+ border-radius: 3px;
454
  }
455
 
456
  .markdown-body code:before,
457
  .markdown-body code:after {
458
+ letter-spacing: -0.2em;
459
+ content: "\00a0";
460
  }
461
 
462
+ .markdown-body pre > code {
463
+ padding: 0;
464
+ margin: 0;
465
+ font-size: 100%;
466
+ word-break: normal;
467
+ white-space: pre;
468
+ background: transparent;
469
+ border: 0;
470
  }
471
 
472
  .markdown-body .highlight {
473
+ margin-bottom: 16px;
474
  }
475
 
476
  .markdown-body .highlight pre,
477
  .markdown-body pre {
478
+ padding: 16px;
479
+ overflow: auto;
480
+ font-size: 85%;
481
+ line-height: 1.45;
482
+ background-color: #f7f7f7;
483
+ border-radius: 3px;
484
  }
485
 
486
  .markdown-body .highlight pre {
487
+ margin-bottom: 0;
488
+ word-break: normal;
489
  }
490
 
491
  .markdown-body pre {
492
+ word-wrap: normal;
493
  }
494
 
495
  .markdown-body pre code {
496
+ display: inline;
497
+ max-width: initial;
498
+ padding: 0;
499
+ margin: 0;
500
+ overflow: initial;
501
+ line-height: inherit;
502
+ word-wrap: normal;
503
+ background-color: transparent;
504
+ border: 0;
505
  }
506
 
507
  .markdown-body pre code:before,
508
  .markdown-body pre code:after {
509
+ content: normal;
510
  }
511
 
512
  .markdown-body kbd {
513
+ display: inline-block;
514
+ padding: 3px 5px;
515
+ font-size: 11px;
516
+ line-height: 10px;
517
+ color: #555;
518
+ vertical-align: middle;
519
+ background-color: #fcfcfc;
520
+ border: solid 1px #ccc;
521
+ border-bottom-color: #bbb;
522
+ border-radius: 3px;
523
+ box-shadow: inset 0 -1px 0 #bbb;
524
  }
525
 
526
  .markdown-body .pl-c {
527
+ color: #969896;
528
  }
529
 
530
  .markdown-body .pl-c1,
536
  .markdown-body .pl-s3,
537
  .markdown-body .pl-sc,
538
  .markdown-body .pl-sv {
539
+ color: #0086b3;
540
  }
541
 
542
  .markdown-body .pl-e,
543
  .markdown-body .pl-en {
544
+ color: #795da3;
545
  }
546
 
547
  .markdown-body .pl-s1 .pl-s2,
550
  .markdown-body .pl-stj,
551
  .markdown-body .pl-vo,
552
  .markdown-body .pl-vpf {
553
+ color: #333;
554
  }
555
 
556
  .markdown-body .pl-ent {
557
+ color: #63a35c;
558
  }
559
 
560
  .markdown-body .pl-k,
561
  .markdown-body .pl-s,
562
  .markdown-body .pl-st {
563
+ color: #a71d5d;
564
  }
565
 
566
  .markdown-body .pl-pds,
571
  .markdown-body .pl-sr .pl-sra,
572
  .markdown-body .pl-sr .pl-sre,
573
  .markdown-body .pl-src {
574
+ color: #183691;
575
  }
576
 
577
  .markdown-body .pl-v {
578
+ color: #ed6a43;
579
  }
580
 
581
  .markdown-body .pl-id {
582
+ color: #b52a1d;
583
  }
584
 
585
  .markdown-body .pl-ii {
586
+ background-color: #b52a1d;
587
+ color: #f8f8f8;
588
  }
589
 
590
  .markdown-body .pl-sr .pl-cce {
591
+ color: #63a35c;
592
+ font-weight: bold;
593
  }
594
 
595
  .markdown-body .pl-ml {
596
+ color: #693a17;
597
  }
598
 
599
  .markdown-body .pl-mh,
600
  .markdown-body .pl-mh .pl-en,
601
  .markdown-body .pl-ms {
602
+ color: #1d3e81;
603
+ font-weight: bold;
604
  }
605
 
606
  .markdown-body .pl-mq {
607
+ color: #008080;
608
  }
609
 
610
  .markdown-body .pl-mi {
611
+ color: #333;
612
+ font-style: italic;
613
  }
614
 
615
  .markdown-body .pl-mb {
616
+ color: #333;
617
+ font-weight: bold;
618
  }
619
 
620
  .markdown-body .pl-md,
621
  .markdown-body .pl-mdhf {
622
+ background-color: #ffecec;
623
+ color: #bd2c00;
624
  }
625
 
626
  .markdown-body .pl-mdht,
627
  .markdown-body .pl-mi1 {
628
+ background-color: #eaffea;
629
+ color: #55a532;
630
  }
631
 
632
  .markdown-body .pl-mdr {
633
+ color: #795da3;
634
+ font-weight: bold;
635
  }
636
 
637
  .markdown-body .pl-mo {
638
+ color: #1d3e81;
639
  }
640
 
641
  .markdown-body kbd {
642
+ display: inline-block;
643
+ padding: 3px 5px;
644
+ font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
645
+ line-height: 10px;
646
+ color: #555;
647
+ vertical-align: middle;
648
+ background-color: #fcfcfc;
649
+ border: solid 1px #ccc;
650
+ border-bottom-color: #bbb;
651
+ border-radius: 3px;
652
+ box-shadow: inset 0 -1px 0 #bbb;
653
  }
654
 
655
  .markdown-body .task-list-item {
656
+ list-style-type: none;
657
  }
658
 
659
+ .markdown-body .task-list-item + .task-list-item {
660
+ margin-top: 3px;
661
  }
662
 
663
  .markdown-body .task-list-item input {
664
+ margin: 0 0.35em 0.25em -1.6em;
665
+ vertical-align: middle;
666
  }
667
 
668
+ .markdown-body :checked + .radio-label {
669
+ z-index: 1;
670
+ position: relative;
671
+ border-color: #4183c4;
672
+ }
css/styles.css CHANGED
@@ -1,10 +1,3 @@
1
- /*
2
-
3
- The spinner that wp uses:;
4
- /wp-includes/images/spinner.gif
5
-
6
- */
7
-
8
  /* clearfix */
9
  .SimpleHistory__cf:before,
10
  .SimpleHistory__cf:after {
@@ -218,11 +211,6 @@ Style different log levels.
218
  color: #eee;
219
  }
220
 
221
- .SimpleHistoryLogitem:hover {
222
- /* same bg color as twitter uses on hover */
223
- /*background: rgb(245, 248, 250);*/
224
- }
225
-
226
  .SimpleHistoryLogitem a {
227
  text-decoration: none;
228
  }
@@ -600,14 +588,14 @@ Pagination, below logRows
600
  }
601
 
602
  .SimpleHistoryPaginationLinks .SimpleHistoryPaginationLink {
603
- vertical-align: baseline;
604
- min-width: 30px;
605
- min-height: 30px;
606
- margin: 0;
607
- padding: 0 4px;
608
- font-size: 16px;
609
- line-height: 1.625;
610
- text-align: center;
611
  }
612
 
613
  .SimpleHistoryPaginationLink.SimpleHistoryPaginationLink.disabled {
@@ -619,8 +607,8 @@ Pagination, below logRows
619
 
620
  .SimpleHistoryPaginationCurrentPage {
621
  margin: 0 2px 0 0;
622
- font-size: 13px;
623
- text-align: center;
624
  }
625
 
626
  .SimpleHistoryPaginationInput .total-pages {
@@ -969,5 +957,18 @@ Modal window with detailss
969
  box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, 0.8);
970
  }
971
 
972
- .SimpleHistory__diff__contentsInner {
 
 
 
 
 
 
 
 
 
 
 
 
 
973
  }
 
 
 
 
 
 
 
1
  /* clearfix */
2
  .SimpleHistory__cf:before,
3
  .SimpleHistory__cf:after {
211
  color: #eee;
212
  }
213
 
 
 
 
 
 
214
  .SimpleHistoryLogitem a {
215
  text-decoration: none;
216
  }
588
  }
589
 
590
  .SimpleHistoryPaginationLinks .SimpleHistoryPaginationLink {
591
+ vertical-align: baseline;
592
+ min-width: 30px;
593
+ min-height: 30px;
594
+ margin: 0;
595
+ padding: 0 4px;
596
+ font-size: 16px;
597
+ line-height: 1.625;
598
+ text-align: center;
599
  }
600
 
601
  .SimpleHistoryPaginationLink.SimpleHistoryPaginationLink.disabled {
607
 
608
  .SimpleHistoryPaginationCurrentPage {
609
  margin: 0 2px 0 0;
610
+ font-size: 13px;
611
+ text-align: center;
612
  }
613
 
614
  .SimpleHistoryPaginationInput .total-pages {
957
  box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, 0.8);
958
  }
959
 
960
+ .SimpleHistory__diff.SimpleHistory__diff {
961
+ border-spacing: 1px;
962
+ }
963
+
964
+ .SimpleHistory__diff.SimpleHistory__diff td,
965
+ .SimpleHistory__diff.SimpleHistory__diff td:first-child {
966
+ text-align: left;
967
+ white-space: normal;
968
+ font-size: 13px;
969
+ line-height: 1.3;
970
+ padding: 0.25em 0.5em;
971
+ padding: 0.5em 0.5em 0.5em 2em;
972
+ color: rgb(75, 75, 75);
973
+ font-family: "Open Sans", sans-serif;
974
  }
dropins/SimpleHistoryFilterDropin.css CHANGED
@@ -1,13 +1,7 @@
1
-
2
-
3
  .SimpleHistory__filters {
4
- /*float: right;
5
- width: 300px;
6
- margin-right: -340px;
7
- */
8
  /* hide by default while log is loading */
9
- opacity: .5;
10
- transition: all .25s ease-out;
11
  }
12
 
13
  .SimpleHistory__filters__filter--date {
@@ -16,7 +10,6 @@
16
  .SimpleHistory__filters__filter--date.select2-container {
17
  }
18
 
19
-
20
  .SimpleHistory--isLoaded .SimpleHistory__filters {
21
  opacity: 1;
22
  }
@@ -26,8 +19,8 @@
26
  width: 310px;
27
  }
28
 
29
- .SimpleHistory__filters__form input[type=text],
30
- .SimpleHistory__filters__form input[type=search] {
31
  /* width: 100%; */
32
  /* width: 310px; */
33
  }
@@ -48,7 +41,6 @@
48
  }
49
 
50
  @media (max-width: 600px) {
51
-
52
  .SimpleHistory__filters__filterLabel {
53
  display: block;
54
  width: auto;
@@ -57,7 +49,6 @@
57
  .SimpleHistory__filters__filterSubmitWrap {
58
  margin-left: 0;
59
  }
60
-
61
  }
62
 
63
  @media (min-width: 600px) {
@@ -67,13 +58,14 @@
67
  line-height: 41px;
68
  }
69
 
70
- .SimpleHistoryWrap .wp-admin select[multiple].SimpleHistory__filters__filter--date {
 
 
71
  height: 2.25em;
72
  overflow: hidden;
73
  }
74
  }
75
 
76
-
77
  /* always label as blocks on dashboard because we don't know the width beacuse of columns */
78
  .postbox .SimpleHistory__filters__filterLabel {
79
  display: block;
@@ -83,8 +75,6 @@
83
  margin-left: 0;
84
  }
85
 
86
-
87
-
88
  /**
89
  * Search results in filter
90
  */
@@ -127,43 +117,45 @@
127
 
128
  /* when showing more filters */
129
  .SimpleHistory__filters.is-showingMoreFilters {
130
-
131
  }
132
 
133
- .SimpleHistory__filters.is-showingMoreFilters .SimpleHistoryFilterDropin-searchInput {
 
134
  /*width: 310px;*/
135
  }
136
 
137
- .SimpleHistory__filters.is-showingMoreFilters .SimpleHistoryFilterDropin-showMoreFilters--first {
 
138
  display: none;
139
  }
140
 
141
- .SimpleHistory__filters.is-showingMoreFilters .SimpleHistory__filters__moreFilters {
 
142
  display: block;
143
  }
144
 
145
- .SimpleHistory__filters.is-showingMoreFilters .SimpleHistoryFilterDropin-doFilterButton--first {
 
146
  display: none;
147
  }
148
 
149
-
150
  /* dashboard */
151
  .postbox .SimpleHistory__filters {
152
  margin-left: 13px;
153
  margin-right: 13px;
154
  }
155
 
156
-
157
  /* day filter */
158
  /* hidden by default, shown by js when selecting "custom range" in dates picker */
159
  .SimpleHistory__filters__filter--dayValuesWrap {
160
- display: block;
161
  visibility: hidden;
162
  opacity: 0;
163
  margin-left: 155px;
164
  max-height: 0;
165
  overflow: hidden;
166
- transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 1s;
 
167
  }
168
 
169
  .is-customDateFilterActive .SimpleHistory__filters__filter--dayValuesWrap {
@@ -171,7 +163,8 @@
171
  opacity: 1;
172
  max-height: 150px;
173
  margin-top: 1em;
174
- transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 0s;
 
175
  }
176
 
177
  .is-customDateFilterActive .SimpleHistory__filters__filterRow--date {
 
 
1
  .SimpleHistory__filters {
 
 
 
 
2
  /* hide by default while log is loading */
3
+ opacity: 0.5;
4
+ transition: all 0.25s ease-out;
5
  }
6
 
7
  .SimpleHistory__filters__filter--date {
10
  .SimpleHistory__filters__filter--date.select2-container {
11
  }
12
 
 
13
  .SimpleHistory--isLoaded .SimpleHistory__filters {
14
  opacity: 1;
15
  }
19
  width: 310px;
20
  }
21
 
22
+ .SimpleHistory__filters__form input[type="text"],
23
+ .SimpleHistory__filters__form input[type="search"] {
24
  /* width: 100%; */
25
  /* width: 310px; */
26
  }
41
  }
42
 
43
  @media (max-width: 600px) {
 
44
  .SimpleHistory__filters__filterLabel {
45
  display: block;
46
  width: auto;
49
  .SimpleHistory__filters__filterSubmitWrap {
50
  margin-left: 0;
51
  }
 
52
  }
53
 
54
  @media (min-width: 600px) {
58
  line-height: 41px;
59
  }
60
 
61
+ .SimpleHistoryWrap
62
+ .wp-admin
63
+ select[multiple].SimpleHistory__filters__filter--date {
64
  height: 2.25em;
65
  overflow: hidden;
66
  }
67
  }
68
 
 
69
  /* always label as blocks on dashboard because we don't know the width beacuse of columns */
70
  .postbox .SimpleHistory__filters__filterLabel {
71
  display: block;
75
  margin-left: 0;
76
  }
77
 
 
 
78
  /**
79
  * Search results in filter
80
  */
117
 
118
  /* when showing more filters */
119
  .SimpleHistory__filters.is-showingMoreFilters {
 
120
  }
121
 
122
+ .SimpleHistory__filters.is-showingMoreFilters
123
+ .SimpleHistoryFilterDropin-searchInput {
124
  /*width: 310px;*/
125
  }
126
 
127
+ .SimpleHistory__filters.is-showingMoreFilters
128
+ .SimpleHistoryFilterDropin-showMoreFilters--first {
129
  display: none;
130
  }
131
 
132
+ .SimpleHistory__filters.is-showingMoreFilters
133
+ .SimpleHistory__filters__moreFilters {
134
  display: block;
135
  }
136
 
137
+ .SimpleHistory__filters.is-showingMoreFilters
138
+ .SimpleHistoryFilterDropin-doFilterButton--first {
139
  display: none;
140
  }
141
 
 
142
  /* dashboard */
143
  .postbox .SimpleHistory__filters {
144
  margin-left: 13px;
145
  margin-right: 13px;
146
  }
147
 
 
148
  /* day filter */
149
  /* hidden by default, shown by js when selecting "custom range" in dates picker */
150
  .SimpleHistory__filters__filter--dayValuesWrap {
151
+ display: block;
152
  visibility: hidden;
153
  opacity: 0;
154
  margin-left: 155px;
155
  max-height: 0;
156
  overflow: hidden;
157
+ transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out,
158
+ visibility 0s 1s;
159
  }
160
 
161
  .is-customDateFilterActive .SimpleHistory__filters__filter--dayValuesWrap {
163
  opacity: 1;
164
  max-height: 150px;
165
  margin-top: 1em;
166
+ transition: max-height 0.25s ease-in-out, opacity 0.25s ease-in-out,
167
+ visibility 0s 0s;
168
  }
169
 
170
  .is-customDateFilterActive .SimpleHistory__filters__filterRow--date {
dropins/SimpleHistoryFilterDropin.php CHANGED
@@ -518,7 +518,7 @@ class SimpleHistoryFilterDropin
518
  wp_send_json_success($data);
519
  } // function
520
 
521
- function add_gravatar_to_user_array(& $val, $index = null)
522
  {
523
  $val->text = sprintf(
524
  '%1$s - %2$s',
518
  wp_send_json_success($data);
519
  } // function
520
 
521
+ function add_gravatar_to_user_array(&$val, $index = null)
522
  {
523
  $val->text = sprintf(
524
  '%1$s - %2$s',
dropins/SimpleHistoryIpInfoDropin.css CHANGED
@@ -2,7 +2,7 @@
2
  }
3
 
4
  .SimpleHistoryLogitem__anonUserWithIp__theIp:hover {
5
- /* cursor: pointer;
6
  text-decoration: underline;*/
7
  }
8
 
@@ -22,7 +22,7 @@
22
  padding: 10px;
23
  z-index: 5;
24
  /* fade out then hide visibility */
25
- transition: visibility 0 .25s, opacity .25s;
26
  }
27
 
28
  .SimpleHistoryIpInfoDropin__popup p {
@@ -33,7 +33,7 @@
33
  visibility: visible;
34
  opacity: 1;
35
  /* make visible immediately and start opacity directly */
36
- transition: visibility 0, opacity .25s 0s;
37
  }
38
 
39
  .SimpleHistoryIpInfoDropin__popupArrow {
@@ -53,7 +53,8 @@
53
  left: 40px;
54
  }
55
 
56
- .SimpleHistoryIpInfoDropin__popupArrow:after, .SimpleHistoryIpInfoDropin__popupArrow:before {
 
57
  bottom: 100%;
58
  left: 50%;
59
  border: solid transparent;
@@ -83,7 +84,6 @@
83
  }
84
 
85
  .SimpleHistoryIpInfoDropin__ipInfoTable tr {
86
-
87
  }
88
 
89
  .SimpleHistoryIpInfoDropin__ipInfoTable th,
@@ -111,14 +111,14 @@
111
  text-align: right;
112
  font-size: 12px;
113
  color: #888;
114
- margin-top: .5em !important;
115
  }
116
 
117
  .SimpleHistoryIpInfoDropin__popupClose {
118
  position: absolute;
119
  width: 21px;
120
  height: 20px;
121
- background-color: rgba(200, 200, 200, .5);
122
  border-radius: 50%;
123
  top: 5px;
124
  right: 5px;
2
  }
3
 
4
  .SimpleHistoryLogitem__anonUserWithIp__theIp:hover {
5
+ /* cursor: pointer;
6
  text-decoration: underline;*/
7
  }
8
 
22
  padding: 10px;
23
  z-index: 5;
24
  /* fade out then hide visibility */
25
+ transition: visibility 0 0.25s, opacity 0.25s;
26
  }
27
 
28
  .SimpleHistoryIpInfoDropin__popup p {
33
  visibility: visible;
34
  opacity: 1;
35
  /* make visible immediately and start opacity directly */
36
+ transition: visibility 0, opacity 0.25s 0s;
37
  }
38
 
39
  .SimpleHistoryIpInfoDropin__popupArrow {
53
  left: 40px;
54
  }
55
 
56
+ .SimpleHistoryIpInfoDropin__popupArrow:after,
57
+ .SimpleHistoryIpInfoDropin__popupArrow:before {
58
  bottom: 100%;
59
  left: 50%;
60
  border: solid transparent;
84
  }
85
 
86
  .SimpleHistoryIpInfoDropin__ipInfoTable tr {
 
87
  }
88
 
89
  .SimpleHistoryIpInfoDropin__ipInfoTable th,
111
  text-align: right;
112
  font-size: 12px;
113
  color: #888;
114
+ margin-top: 0.5em !important;
115
  }
116
 
117
  .SimpleHistoryIpInfoDropin__popupClose {
118
  position: absolute;
119
  width: 21px;
120
  height: 20px;
121
+ background-color: rgba(200, 200, 200, 0.5);
122
  border-radius: 50%;
123
  top: 5px;
124
  right: 5px;
dropins/SimpleHistoryNewRowsNotifierDropin.css CHANGED
@@ -5,8 +5,8 @@
5
  background: white;
6
  line-height: 40px;
7
  background: rgba(0, 255, 30, 0.15);
8
- -webkit-transition: max-height .5s ease-out, background 0s;
9
- transition: max-height .5s ease-out, background 0s;
10
  }
11
 
12
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows {
@@ -15,12 +15,12 @@
15
  }
16
 
17
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows:before {
18
- content: '\f463';
19
  font: 400 20px/1 dashicons;
20
  -webkit-font-smoothing: antialiased;
21
  display: inline-block;
22
  vertical-align: middle;
23
- margin-right: .5em;
24
  }
25
 
26
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows:hover {
5
  background: white;
6
  line-height: 40px;
7
  background: rgba(0, 255, 30, 0.15);
8
+ -webkit-transition: max-height 0.5s ease-out, background 0s;
9
+ transition: max-height 0.5s ease-out, background 0s;
10
  }
11
 
12
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows {
15
  }
16
 
17
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows:before {
18
+ content: "\f463";
19
  font: 400 20px/1 dashicons;
20
  -webkit-font-smoothing: antialiased;
21
  display: inline-block;
22
  vertical-align: middle;
23
+ margin-right: 0.5em;
24
  }
25
 
26
  .SimpleHistoryDropin__NewRowsNotifier--haveNewRows:hover {
dropins/SimpleHistoryPluginPatchesDropin.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  defined('ABSPATH') or die();
3
 
4
  /*
1
  <?php
2
+
3
  defined('ABSPATH') or die();
4
 
5
  /*
dropins/SimpleHistorySettingsStatsDropin.css CHANGED
@@ -2,9 +2,6 @@
2
  font-size: 1.4em;
3
  }
4
 
5
- .SimpleHistoryStats__graphs {
6
-
7
- }
8
  .SimpleHistoryStats__graph {
9
  float: left;
10
  width: 50%;
2
  font-size: 1.4em;
3
  }
4
 
 
 
 
5
  .SimpleHistoryStats__graph {
6
  float: left;
7
  width: 50%;
dropins/SimpleHistorySidebarDropin.php CHANGED
@@ -14,15 +14,13 @@ class SimpleHistorySidebarDropin
14
 
15
  private $sh;
16
 
17
- function __construct($sh)
18
  {
19
 
20
  $this->sh = $sh;
21
 
22
  add_action('simple_history/enqueue_admin_scripts', array( $this, 'enqueue_admin_scripts' ));
23
  add_action('simple_history/history_page/after_gui', array( $this, 'output_sidebar_html' ));
24
-
25
- // add_action("simple_history/dropin/sidebar/sidebar_html", array($this, "example_output"));
26
  add_action('simple_history/dropin/sidebar/sidebar_html', array( $this, 'default_sidebar_contents' ));
27
  }
28
 
@@ -50,16 +48,23 @@ class SimpleHistorySidebarDropin
50
  // Box about donation
51
  $headline = _x('Donate to support development', 'Sidebar box', 'simple-history');
52
 
53
- $body = sprintf(
54
  _x('If you like and use Simple History you should <a href="%1$s">donate to keep this plugin free</a>.', 'Sidebar box', 'simple-history'),
55
- 'http://eskapism.se/sida/donate/'
 
 
 
 
 
56
  );
 
57
 
58
  $boxDonate = '
59
  <div class="postbox">
60
  <h3 class="hndle">' . $headline . '</h3>
61
  <div class="inside">
62
- <p>' . $body . '</p>
 
63
  </div>
64
  </div>
65
  ';
@@ -142,10 +147,9 @@ class SimpleHistorySidebarDropin
142
  */
143
  $arrBoxes = apply_filters('simple_history/SidebarDropin/default_sidebar_boxes', $arrBoxes);
144
 
145
- // echo $arrBoxes[array_rand($arrBoxes)];
146
- echo implode('', $arrBoxes); // show all
147
 
148
- // Box to encourage people translate plugin
149
  $current_locale = get_locale();
150
 
151
  /** WordPress Translation Install API. This file exists only since 4.0. */
@@ -168,12 +172,9 @@ class SimpleHistorySidebarDropin
168
  </p>
169
 
170
  <p>
171
- If you\'re interested in translating it please check out the <a href="https://developer.wordpress.org/plugins/internationalization/localization/">localization</a> part of the Plugin Handbook for info on how to translate plugins.
172
- </p>
173
-
174
- <p>
175
- When you\'re done with your translation email it to me at <a href="mailto:par.thernstrom@gmail.com" rel="nofollow">par.thernstrom@gmail.com</a>
176
- or <a href="https://github.com/bonny/WordPress-Simple-History/" rel="nofollow">add a pull request</a>.
177
  </p>
178
  </div>
179
  </div>
@@ -191,21 +192,8 @@ class SimpleHistorySidebarDropin
191
  } // End if().
192
  }
193
 
194
- public function example_output()
195
- {
196
- ?>
197
- <div class="postbox">
198
- <h3 class="hndle">Example title</h3>
199
- <div class="inside">
200
- <p>Example content. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Inquit, dasne adolescenti veniam? Non laboro, inquit, de nomine. In quibus doctissimi illi veteres inesse quiddam caeleste et divinum putaverunt. Duo Reges: constructio interrete. Indicant pueri, in quibus ut in speculis natura cernitur. Quod ea non occurrentia fingunt, vincunt Aristonem; Quod quidem iam fit etiam in Academia. Aliter enim nosmet ipsos nosse non possumus.</p>
201
- </div>
202
- </div>
203
- <?php
204
- }
205
-
206
  public function enqueue_admin_scripts()
207
  {
208
-
209
  $file_url = plugin_dir_url(__FILE__);
210
 
211
  wp_enqueue_style('simple_history_SidebarDropin', $file_url . 'SimpleHistorySidebarDropin.css', null, SIMPLE_HISTORY_VERSION);
14
 
15
  private $sh;
16
 
17
+ public function __construct($sh)
18
  {
19
 
20
  $this->sh = $sh;
21
 
22
  add_action('simple_history/enqueue_admin_scripts', array( $this, 'enqueue_admin_scripts' ));
23
  add_action('simple_history/history_page/after_gui', array( $this, 'output_sidebar_html' ));
 
 
24
  add_action('simple_history/dropin/sidebar/sidebar_html', array( $this, 'default_sidebar_contents' ));
25
  }
26
 
48
  // Box about donation
49
  $headline = _x('Donate to support development', 'Sidebar box', 'simple-history');
50
 
51
+ $bodyDonate = sprintf(
52
  _x('If you like and use Simple History you should <a href="%1$s">donate to keep this plugin free</a>.', 'Sidebar box', 'simple-history'),
53
+ 'https://eskapism.se/sida/donate/'
54
+ );
55
+
56
+ $bodyGithubSponsors = sprintf(
57
+ _x('You can also <a href="%1$s">sponsor me at Github</a>.', 'Sidebar box', 'simple-history'),
58
+ 'https://github.com/sponsors/bonny/'
59
  );
60
+
61
 
62
  $boxDonate = '
63
  <div class="postbox">
64
  <h3 class="hndle">' . $headline . '</h3>
65
  <div class="inside">
66
+ <p>' . $bodyDonate . '</p>
67
+ <p>' . $bodyGithubSponsors . '</p>
68
  </div>
69
  </div>
70
  ';
147
  */
148
  $arrBoxes = apply_filters('simple_history/SidebarDropin/default_sidebar_boxes', $arrBoxes);
149
 
150
+ echo implode('', $arrBoxes);
 
151
 
152
+ // Box to encourage people translate plugin.
153
  $current_locale = get_locale();
154
 
155
  /** WordPress Translation Install API. This file exists only since 4.0. */
172
  </p>
173
 
174
  <p>
175
+ If you\'re interested in translating it please check out the
176
+ <a href="https://developer.wordpress.org/plugins/internationalization/localization/">localization</a>
177
+ part of the Plugin Handbook for info on how to translate plugins.
 
 
 
178
  </p>
179
  </div>
180
  </div>
192
  } // End if().
193
  }
194
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  public function enqueue_admin_scripts()
196
  {
 
197
  $file_url = plugin_dir_url(__FILE__);
198
 
199
  wp_enqueue_style('simple_history_SidebarDropin', $file_url . 'SimpleHistorySidebarDropin.css', null, SIMPLE_HISTORY_VERSION);
examples/examples.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Examples on how to customize Simple History.
4
  *
1
  <?php
2
+
3
  /**
4
  * Examples on how to customize Simple History.
5
  *
inc/SimpleHistory.php CHANGED
@@ -574,7 +574,7 @@ class SimpleHistory
574
  <div class="SimpleHistory-modal__contentInner">
575
  <img class="SimpleHistory-modal__contentSpinner" src="<?php echo esc_url(
576
  admin_url('/images/spinner.gif')
577
- ); ?>" alt="">
578
  </div>
579
  <div class="SimpleHistory-modal__contentClose">
580
  <button class="button">✕</button>
@@ -604,7 +604,8 @@ class SimpleHistory
604
  if (method_exists($one_logger['instance'], 'adminJS')) {
605
  $one_logger['instance']->adminJS();
606
  }
607
- }}
 
608
  }
609
 
610
  /**
574
  <div class="SimpleHistory-modal__contentInner">
575
  <img class="SimpleHistory-modal__contentSpinner" src="<?php echo esc_url(
576
  admin_url('/images/spinner.gif')
577
+ ); ?>" alt="">
578
  </div>
579
  <div class="SimpleHistory-modal__contentClose">
580
  <button class="button">✕</button>
604
  if (method_exists($one_logger['instance'], 'adminJS')) {
605
  $one_logger['instance']->adminJS();
606
  }
607
+ }
608
+ }
609
  }
610
 
611
  /**
inc/SimpleHistoryLogQuery.php CHANGED
@@ -784,5 +784,5 @@ class SimpleHistoryLogQuery
784
  wp_cache_set($cache_key, $arr_return, $cache_group);
785
 
786
  return $arr_return;
787
- } // query
788
- } // class
784
  wp_cache_set($cache_key, $arr_return, $cache_group);
785
 
786
  return $arr_return;
787
+ }
788
+ }
inc/helpers.php CHANGED
@@ -35,7 +35,7 @@ function simple_history_add($args)
35
  $message = "{$context["object_type"]} {$context["object_name"]} {$context["action"]}";
36
 
37
  SimpleLogger()->info($message, $context);
38
- } // simple_history_add
39
 
40
  /**
41
  * Pretty much same as wp_text_diff() but with this you can set leading and trailing context lines
35
  $message = "{$context["object_type"]} {$context["object_name"]} {$context["action"]}";
36
 
37
  SimpleLogger()->info($message, $context);
38
+ }
39
 
40
  /**
41
  * Pretty much same as wp_text_diff() but with this you can set leading and trailing context lines
inc/oldversions.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show an admin message if old PHP version.
4
+ */
5
+ function simple_history_old_version_admin_notice()
6
+ {
7
+ $ok_wp_version = version_compare($GLOBALS['wp_version'], '5.2', '>=');
8
+ $ok_php_version = version_compare(phpversion(), '5.6', '>=');
9
+ ?>
10
+ <div class="updated error">
11
+ <?php
12
+ if (!$ok_php_version) {
13
+ echo '<p>';
14
+ printf(
15
+ /* translators: 1: PHP version */
16
+ esc_html(
17
+ __(
18
+ 'Simple History is a great plugin, but to use it your server must have at least PHP 5.6 installed (you have version %s).',
19
+ 'simple-history'
20
+ )
21
+ ),
22
+ phpversion() // 1
23
+ );
24
+ echo '</p>';
25
+ }
26
+
27
+ if (!$ok_wp_version) {
28
+ echo '<p>';
29
+ printf(
30
+ /* translators: 1: WordPress version */
31
+ esc_html(
32
+ __(
33
+ 'Simple History requires WordPress version 5.2 or higher (you have version %s).',
34
+ 'simple-history'
35
+ )
36
+ ),
37
+ $GLOBALS['wp_version'] // 1
38
+ );
39
+ echo '</p>';
40
+ }?>
41
+ </div>
42
+ <?php
43
+ }
index.php CHANGED
@@ -1,11 +1,12 @@
1
  <?php
 
2
  /**
3
  * Plugin Name: Simple History
4
  * Plugin URI: http://simple-history.com
5
  * Text Domain: simple-history
6
  * Domain Path: /languages
7
  * Description: Plugin that logs various things that occur in WordPress and then presents those events in a very nice GUI.
8
- * Version: 2.35.1
9
  * Author: Pär Thernström
10
  * Author URI: http://simple-history.com/
11
  * License: GPL2
@@ -34,36 +35,21 @@ if (!defined('WPINC')) {
34
 
35
  // Plugin requires at least WordPress version "4.5.1", because usage of functions like wp_get_raw_referer.
36
  // true if version ok, false if too old version.
37
- $ok_wp_version = version_compare($GLOBALS['wp_version'], '4.5.1', '>=');
38
- $ok_php_version = version_compare(phpversion(), '5.3', '>=');
39
 
40
  if ($ok_php_version && $ok_wp_version) {
41
  /**
42
  * Register function that is called when plugin is installed
43
  *
44
- * @TODO: make activatigon multi site aware, as in https://github.com/scribu/wp-proper-network-activation
45
  * register_activation_hook( trailingslashit(WP_PLUGIN_DIR) . trailingslashit( plugin_basename(__DIR__) ) . "index.php" , array("SimpleHistory", "on_plugin_activate" ) );
46
  */
47
-
48
- if (!defined('SIMPLE_HISTORY_VERSION')) {
49
- define('SIMPLE_HISTORY_VERSION', '2.35.1');
50
- }
51
-
52
- if (!defined('SIMPLE_HISTORY_PATH')) {
53
- define('SIMPLE_HISTORY_PATH', plugin_dir_path(__FILE__));
54
- }
55
-
56
- if (!defined('SIMPLE_HISTORY_BASENAME')) {
57
- define('SIMPLE_HISTORY_BASENAME', plugin_basename(__FILE__));
58
- }
59
-
60
- if (!defined('SIMPLE_HISTORY_DIR_URL')) {
61
- define('SIMPLE_HISTORY_DIR_URL', plugin_dir_url(__FILE__));
62
- }
63
-
64
- if (!defined('SIMPLE_HISTORY_FILE')) {
65
- define('SIMPLE_HISTORY_FILE', __FILE__);
66
- }
67
 
68
  /** Load required files */
69
  require_once __DIR__ . '/inc/SimpleHistory.php';
@@ -74,48 +60,6 @@ if ($ok_php_version && $ok_wp_version) {
74
  SimpleHistory::get_instance();
75
  } else {
76
  // User is running to old version of php, add admin notice about that.
 
77
  add_action('admin_notices', 'simple_history_old_version_admin_notice');
78
-
79
- /**
80
- * Show an admin message if old PHP version.
81
- */
82
- function simple_history_old_version_admin_notice()
83
- {
84
- $ok_wp_version = version_compare($GLOBALS['wp_version'], '4.5.1', '>=');
85
- $ok_php_version = version_compare(phpversion(), '5.3', '>=');
86
- ?>
87
- <div class="updated error">
88
- <?php
89
- if (!$ok_php_version) {
90
- echo '<p>';
91
- printf(
92
- /* translators: 1: PHP version */
93
- esc_html(
94
- __(
95
- 'Simple History is a great plugin, but to use it your server must have at least PHP 5.3 installed (you have version %s).',
96
- 'simple-history'
97
- )
98
- ),
99
- phpversion() // 1
100
- );
101
- echo '</p>';
102
- }
103
-
104
- if (!$ok_wp_version) {
105
- echo '<p>';
106
- printf(
107
- /* translators: 1: WordPress version */
108
- esc_html(
109
- __(
110
- 'Simple History requires WordPress version 4.5.1 or higher (you have version %s).',
111
- 'simple-history'
112
- )
113
- ),
114
- $GLOBALS['wp_version'] // 1
115
- );
116
- echo '</p>';
117
- }?>
118
- </div>
119
- <?php
120
- }
121
  } // End if().
1
  <?php
2
+
3
  /**
4
  * Plugin Name: Simple History
5
  * Plugin URI: http://simple-history.com
6
  * Text Domain: simple-history
7
  * Domain Path: /languages
8
  * Description: Plugin that logs various things that occur in WordPress and then presents those events in a very nice GUI.
9
+ * Version: 2.36.0
10
  * Author: Pär Thernström
11
  * Author URI: http://simple-history.com/
12
  * License: GPL2
35
 
36
  // Plugin requires at least WordPress version "4.5.1", because usage of functions like wp_get_raw_referer.
37
  // true if version ok, false if too old version.
38
+ $ok_wp_version = version_compare($GLOBALS['wp_version'], '5.2', '>=');
39
+ $ok_php_version = version_compare(phpversion(), '5.6', '>=');
40
 
41
  if ($ok_php_version && $ok_wp_version) {
42
  /**
43
  * Register function that is called when plugin is installed
44
  *
45
+ * @TODO: make activation multi site aware, as in https://github.com/scribu/wp-proper-network-activation
46
  * register_activation_hook( trailingslashit(WP_PLUGIN_DIR) . trailingslashit( plugin_basename(__DIR__) ) . "index.php" , array("SimpleHistory", "on_plugin_activate" ) );
47
  */
48
+ define('SIMPLE_HISTORY_VERSION', '2.36.0');
49
+ define('SIMPLE_HISTORY_PATH', plugin_dir_path(__FILE__));
50
+ define('SIMPLE_HISTORY_BASENAME', plugin_basename(__FILE__));
51
+ define('SIMPLE_HISTORY_DIR_URL', plugin_dir_url(__FILE__));
52
+ define('SIMPLE_HISTORY_FILE', __FILE__);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  /** Load required files */
55
  require_once __DIR__ . '/inc/SimpleHistory.php';
60
  SimpleHistory::get_instance();
61
  } else {
62
  // User is running to old version of php, add admin notice about that.
63
+ require_once __DIR__ . '/inc/oldversions.php';
64
  add_action('admin_notices', 'simple_history_old_version_admin_notice');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  } // End if().
loggers/Plugin_BeaverBuilder.php CHANGED
@@ -79,21 +79,21 @@ if (!class_exists('Plugin_BeaverBuilder')) {
79
  }
80
 
81
  function save_template($post_id)
82
- {
83
  $post = get_post($post_id);
84
- $context = array(
85
- 'layout_name' => $post->post_name
86
- );
87
- $this->noticeMessage('template_saved', $context);
88
- }
89
 
90
  function save_draft($post_id, $publish)
91
- {
92
- $context = array(
93
- 'layout_name' => $post_id
94
- );
95
- $this->noticeMessage('draft_saved', $context);
96
- }
97
 
98
  function save_layout($post_id, $publish, $data, $settings)
99
  {
@@ -101,7 +101,7 @@ if (!class_exists('Plugin_BeaverBuilder')) {
101
  $context = array(
102
  'layout_name' => $post->post_name
103
  );
104
- if ( $publish ) {
105
  $this->noticeMessage('layout_saved', $context);
106
  }
107
  }
79
  }
80
 
81
  function save_template($post_id)
82
+ {
83
  $post = get_post($post_id);
84
+ $context = array(
85
+ 'layout_name' => $post->post_name
86
+ );
87
+ $this->noticeMessage('template_saved', $context);
88
+ }
89
 
90
  function save_draft($post_id, $publish)
91
+ {
92
+ $context = array(
93
+ 'layout_name' => $post_id
94
+ );
95
+ $this->noticeMessage('draft_saved', $context);
96
+ }
97
 
98
  function save_layout($post_id, $publish, $data, $settings)
99
  {
101
  $context = array(
102
  'layout_name' => $post->post_name
103
  );
104
+ if ($publish) {
105
  $this->noticeMessage('layout_saved', $context);
106
  }
107
  }
loggers/SimpleCommentsLogger.php CHANGED
@@ -747,7 +747,7 @@ class SimpleCommentsLogger extends SimpleLogger
747
  // Edit link sometimes does not contain comment ID
748
  // Probably because comment has been removed or something
749
  // So only continue if link does not end with "=""
750
- if ($edit_comment_link && $edit_comment_link[ strlen($edit_comment_link) -1 ] !== '=') {
751
  $output .= sprintf(
752
  '
753
  <tr>
747
  // Edit link sometimes does not contain comment ID
748
  // Probably because comment has been removed or something
749
  // So only continue if link does not end with "=""
750
+ if ($edit_comment_link && $edit_comment_link[ strlen($edit_comment_link) - 1 ] !== '=') {
751
  $output .= sprintf(
752
  '
753
  <tr>
loggers/SimpleLogger.php CHANGED
@@ -12,1622 +12,1630 @@ defined('ABSPATH') or die();
12
  */
13
  class SimpleLogger
14
  {
15
- /**
16
- * Unique slug for this logger
17
- * Will be saved in DB and used to associate each log row with its logger
18
- */
19
- public $slug = __CLASS__;
20
-
21
- /**
22
- * Will contain the untranslated messages from getInfo()
23
- *
24
- * By adding your messages here they will be stored both translated and non-translated
25
- * You then log something like this:
26
- * <code>
27
- * $this->info( $this->messages["POST_UPDATED"] );
28
- * </code>
29
- * or with the shortcut
30
- * <code>
31
- * $this->infoMessage("POST_UPDATED");
32
- * </code>
33
- * which results in the original, untranslated, string being added to the log and database
34
- * the translated string are then only used when showing the log in the GUI
35
- */
36
- public $messages;
37
-
38
- /**
39
- * ID of last inserted row
40
- *
41
- * @var int $lastInsertID Database row primary key.
42
- */
43
- public $lastInsertID;
44
-
45
- /**
46
- * Context of last inserted row.
47
- *
48
- * @var int $lastInsertContext Context used for the last insert.
49
- * @since 2.2x
50
- */
51
- public $lastInsertContext;
52
-
53
- /**
54
- * Simple History instance.
55
- *
56
- * @var object $simpleHistory Simple history instance.
57
- */
58
- public $simpleHistory;
59
-
60
- /**
61
- * Constructor. Remember to call this as parent constructor if making a childlogger
62
- *
63
- * @param $simpleHistory history class objectinstance
64
- */
65
- public function __construct($simpleHistory = null)
66
- {
67
- global $wpdb;
68
-
69
- $this->db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
70
- $this->db_table_contexts =
71
- $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
72
-
73
- $this->simpleHistory = $simpleHistory;
74
- }
75
-
76
- /**
77
- * Method that is called automagically when logger is loaded by Simple History
78
- * Add your init stuff here
79
- */
80
- public function loaded()
81
- {
82
- }
83
-
84
- /**
85
- * Get array with information about this logger
86
- *
87
- * @return array
88
- */
89
  public function getInfo()
90
- {
91
- $arr_info = array(
92
- // The logger slug. Defaulting to the class name is nice and logical I think
93
- 'slug' => __CLASS__,
94
-
95
- // Shown on the info-tab in settings, use these fields to tell
96
- // an admin what your logger is used for
97
- 'name' => 'SimpleLogger',
98
- 'description' => 'The built in logger for Simple History',
99
-
100
- // Capability required to view log entries from this logger
101
- 'capability' => 'edit_pages',
102
- 'messages' => array(
103
- // No pre-defined variants
104
- // when adding messages __() or _x() must be used
105
- )
106
- );
107
-
108
- return $arr_info;
109
- }
110
-
111
- /**
112
- * Return single array entry from the array in getInfo()
113
- * Returns the value of the key if value exists, or null
114
- *
115
- * @since 2.5.4
116
- * @return Mixed
117
- */
118
  public function getInfoValueByKey($key)
119
- {
120
- $arr_info = $this->getInfo();
121
-
122
- return isset($arr_info[$key]) ? $arr_info[$key] : null;
123
- }
124
-
125
- /**
126
- * Returns the capability required to read log rows from this logger
127
- *
128
- * @return $string capability
129
- */
130
- public function getCapability()
131
- {
132
- $arr_info = $this->getInfo();
133
-
134
- $capability = 'manage_options';
135
-
136
- if (!empty($arr_info['capability'])) {
137
- $capability = $arr_info['capability'];
138
- }
139
-
140
- return $capability;
141
- }
142
-
143
- /**
144
- * Interpolates context values into the message placeholders.
145
- *
146
- * @param string $message
147
- * @param array $context
148
- * @param array $row Currently not always passed, because loggers need to be updated to support this...
149
- */
150
  public function interpolate($message, $context = array(), $row = null)
151
- {
152
- if (!is_array($context)) {
153
- return $message;
154
- }
155
-
156
- /**
157
- * Filter the context used to create the message from the message template
158
- *
159
- * @since 2.2.4
160
- */
161
- $context = apply_filters(
162
- 'simple_history/logger/interpolate/context',
163
- $context,
164
- $message,
165
- $row
166
- );
167
-
168
- // Build a replacement array with braces around the context keys
169
- $replace = array();
170
- foreach ($context as $key => $val) {
171
- // Both key and val must be strings or number (for vals)
172
- if (is_string($key) || is_numeric($key)) {
173
- // key ok
174
- }
175
-
176
- if (is_string($val) || is_numeric($val)) {
177
- // val ok
178
- } else {
179
- // not a value we can replace
180
- continue;
181
- }
182
-
183
- $replace['{' . $key . '}'] = $val;
184
- }
185
-
186
- // Interpolate replacement values into the message and return
187
- /*
188
- if ( ! is_string( $message )) {
189
- echo "message:";
190
- var_dump($message);exit;
191
- }
192
- //*/
193
- /*
194
- if ( ! is_string( $replace )) {
195
- echo "replace: \n";
196
- var_dump($replace);
197
- }
198
- // */
199
-
200
- return strtr($message, $replace);
201
- }
202
-
203
- /**
204
- * Returns header output for a log row
205
- * Format should be common for all log rows and should be like:
206
- * Username (user role) · Date
207
- *
208
- * @return string HTML
209
- */
210
  public function getLogRowHeaderOutput($row)
211
- {
212
- // HTML for initiator
213
- $initiator_html = '';
214
-
215
- $initiator = $row->initiator;
216
- $context = $row->context;
217
-
218
- switch ($initiator) {
219
- case 'wp':
220
- $initiator_html .=
221
- '<strong class="SimpleHistoryLogitem__inlineDivided">WordPress</strong> ';
222
- break;
223
-
224
- case 'wp_cli':
225
- $initiator_html .=
226
- '<strong class="SimpleHistoryLogitem__inlineDivided">WP-CLI</strong> ';
227
- break;
228
-
229
- // wp_user = wordpress uses, but user may have been deleted since log entry was added
230
- case 'wp_user':
231
- $user_id = isset($row->context['_user_id'])
232
- ? $row->context['_user_id']
233
- : null;
234
-
235
- if ($user_id > 0 && ($user = get_user_by('id', $user_id))) {
236
- // Sender is user and user still exists
237
- $is_current_user =
238
- get_current_user_id() == $user_id ? true : false;
239
-
240
- // get user role, as done in user-edit.php
241
- $wp_roles = $GLOBALS['wp_roles'];
242
- $all_roles = (array) $wp_roles->roles;
243
- $user_roles = array_intersect(
244
- array_values((array) $user->roles),
245
- array_keys((array) $wp_roles->roles)
246
- );
247
- $user_role = array_shift($user_roles);
248
-
249
- $user_display_name = $user->display_name;
250
-
251
- /*
252
- * If user who logged this is the currently logged in user
253
- * skip name and email and use just "You"
254
- *
255
- * @param bool If you should be used
256
- * @since 2.1
257
- */
258
- $use_you = apply_filters(
259
- 'simple_history/header_initiator_use_you',
260
- true
261
- );
262
-
263
- if ($use_you && $is_current_user) {
264
- $tmpl_initiator_html = '
265
  <a href="%6$s" class="SimpleHistoryLogitem__headerUserProfileLink">
266
  <strong class="SimpleHistoryLogitem__inlineDivided">%5$s</strong>
267
  </a>
268
  ';
269
- } else {
270
- $tmpl_initiator_html = '
271
  <a href="%6$s" class="SimpleHistoryLogitem__headerUserProfileLink">
272
  <strong class="SimpleHistoryLogitem__inlineDivided">%3$s</strong>
273
  <span class="SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__headerEmail">%2$s</span>
274
  </a>
275
  ';
276
- }
277
-
278
- /**
279
- * Filter the format for the user output
280
- *
281
- * @since 2.0
282
- *
283
- * @param string $format.
284
- */
285
- $tmpl_initiator_html = apply_filters(
286
- 'simple_history/header_initiator_html_existing_user',
287
- $tmpl_initiator_html
288
- );
289
-
290
- $initiator_html .= sprintf(
291
- $tmpl_initiator_html,
292
- esc_html($user->user_login), // 1
293
- esc_html($user->user_email), // 2
294
- esc_html($user_display_name), // 3
295
- $user_role, // 4
296
- _x(
297
- 'You',
298
- 'header output when initiator is the currently logged in user',
299
- 'simple-history'
300
- ), // 5
301
- get_edit_user_link($user_id) // 6
302
- );
303
- } elseif ($user_id > 0) {
304
- // Sender was a user, but user is deleted now
305
- // output all info we have
306
- // _user_id
307
- // _username
308
- // _user_login
309
- // _user_email
310
- $initiator_html .= sprintf(
311
- '<strong class="SimpleHistoryLogitem__inlineDivided">' .
312
- __(
313
- 'Deleted user (had id %1$s, email %2$s, login %3$s)',
314
- 'simple-history'
315
- ) .
316
- '</strong>',
317
- esc_html($context['_user_id']), // 1
318
- esc_html($context['_user_email']), // 2
319
- esc_html($context['_user_login']) // 3
320
- );
321
- } // End if().
322
-
323
- break;
324
-
325
- case 'web_user':
326
- /*
327
- Note: server_remote_addr may not show visiting/attacking ip, if server is behind...stuff..
328
- Can be behind varnish cashe, or browser can for example use compression in chrome mobile
329
- then the real ip is behind _server_http_x_forwarded_for_0 or similar
330
- _server_remote_addr 66.249.81.222
331
- _server_http_x_forwarded_for_0 5.35.187.212
332
- */
333
-
334
- // Check if additional IP addresses are stored, from http_x_forwarded_for and so on
335
- $arr_found_additional_ip_headers = $this->get_event_ip_number_headers(
336
- $row
337
- );
338
-
339
- if (empty($context['_server_remote_addr'])) {
340
- $initiator_html .=
341
- "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
342
- __('Anonymous web user', 'simple-history') .
343
- '</strong> ';
344
- } else {
345
- $initiator_html .=
346
- "<strong class='SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__anonUserWithIp'>";
347
-
348
- // if ( sizeof( $arr_found_additional_ip_headers ) ) {
349
- // $iplookup_link = sprintf('https://ipinfo.io/%1$s', esc_attr($context["_server_remote_addr"]));
350
- // $ip_numbers_joined = wp_sprintf_l('%l', array("_server_remote_addr" => $context["_server_remote_addr"]) + $arr_found_additional_ip_headers);
351
- /*
352
- $initiator_html .= sprintf(
353
- __('Anonymous user with multiple IP addresses detected: %1$s', "simple-history"),
354
- "<a target='_blank' href={$iplookup_link} class='SimpleHistoryLogitem__anonUserWithIp__theIp'>" . esc_html( $ip_numbers_joined ) . "</a>"
355
- );*/
356
-
357
- /*
358
- print_r($arr_found_additional_ip_headers);
359
- Array
360
- (
361
- [_server_http_x_forwarded_for_0] => 5.35.187.212
362
- [_server_http_x_forwarded_for_1] => 83.251.97.21
363
- )
364
- */
365
-
366
- // } else {
367
- // single ip address
368
- $iplookup_link = sprintf(
369
- 'https://ipinfo.io/%1$s',
370
- esc_attr($context['_server_remote_addr'])
371
- );
372
-
373
- $initiator_html .= sprintf(
374
- __('Anonymous user from %1$s', 'simple-history'),
375
- "<a target='_blank' href={$iplookup_link} class='SimpleHistoryLogitem__anonUserWithIp__theIp'>" .
376
- esc_html($context['_server_remote_addr']) .
377
- '</a>'
378
- );
379
-
380
- // } // multiple ip
381
- $initiator_html .= '</strong> ';
382
-
383
- // $initiator_html .= "<strong>" . __("<br><br>Unknown user from {$context["_server_remote_addr"]}") . "</strong>";
384
- // $initiator_html .= "<strong>" . __("<br><br>{$context["_server_remote_addr"]}") . "</strong>";
385
- // $initiator_html .= "<strong>" . __("<br><br>User from IP {$context["_server_remote_addr"]}") . "</strong>";
386
- // $initiator_html .= "<strong>" . __("<br><br>Non-logged in user from IP {$context["_server_remote_addr"]}") . "</strong>";
387
- } // End if().
388
-
389
- break;
390
-
391
- case 'other':
392
- $initiator_html .=
393
- "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
394
- _x(
395
- 'Other',
396
- 'Event header output, when initiator is unknown',
397
- 'simple-history'
398
- ) .
399
- '</strong>';
400
- break;
401
-
402
- // no initiator
403
- case null:
404
- // $initiator_html .= "<strong class='SimpleHistoryLogitem__inlineDivided'>Null</strong>";
405
- break;
406
-
407
- default:
408
- $initiator_html .=
409
- "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
410
- esc_html($initiator) .
411
- '</strong>';
412
- } // End switch().
413
-
414
- /**
415
- * Filter generated html for the initiator row header html
416
- *
417
- * @since 2.0
418
- *
419
- * @param string $initiator_html
420
- * @param object $row Log row
421
- */
422
- $initiator_html = apply_filters(
423
- 'simple_history/row_header_initiator_output',
424
- $initiator_html,
425
- $row
426
- );
427
-
428
- // HTML for date
429
- // Date (should...) always exist
430
- // http://developers.whatwg.org/text-level-semantics.html#the-time-element
431
- $date_html = '';
432
- $str_when = '';
433
-
434
- // $row->date is in GMT
435
- $date_datetime = new DateTime($row->date, new DateTimeZone('GMT'));
436
-
437
- // Current datetime in GMT
438
- $time_current = strtotime(current_time('mysql', 1));
439
-
440
- /**
441
- * Filter how many seconds as most that can pass since an
442
- * event occured to show "nn minutes ago" (human diff time-format) instead of exact date
443
- *
444
- * @since 2.0
445
- *
446
- * @param int $time_ago_max_time Seconds
447
- */
448
- $time_ago_max_time = DAY_IN_SECONDS * 2;
449
- $time_ago_max_time = apply_filters(
450
- 'simple_history/header_time_ago_max_time',
451
- $time_ago_max_time
452
- );
453
-
454
- /**
455
- * Filter how many seconds as most that can pass since an
456
- * event occured to show "just now" instead of exact date
457
- *
458
- * @since 2.0
459
- *
460
- * @param int $time_ago_max_time Seconds
461
- */
462
- $time_ago_just_now_max_time = 30;
463
- $time_ago_just_now_max_time = apply_filters(
464
- 'simple_history/header_just_now_max_time',
465
- $time_ago_just_now_max_time
466
- );
467
-
468
- $date_format = get_option('date_format');
469
- $time_format = get_option('time_format');
470
- $date_and_time_format = $date_format . ' - ' . $time_format;
471
-
472
- // Show local time as hours an minutes when event is recent.
473
- $local_date_format = $time_format;
474
-
475
- // Show local time as date and hours when event is a bit older.
476
- if ($time_current - HOUR_IN_SECONDS * 6 >
477
- $date_datetime->getTimestamp()
478
- ) {
479
- $local_date_format = $date_and_time_format;
480
- }
481
-
482
- if ($time_current - $date_datetime->getTimestamp() <=
483
- $time_ago_just_now_max_time
484
- ) {
485
- // Show "just now" if event is very recent.
486
- $str_when = __('Just now', 'simple-history');
487
- } elseif ($time_current - $date_datetime->getTimestamp() >
488
- $time_ago_max_time
489
- ) {
490
- /* Translators: Date format for log row header, see http://php.net/date */
491
- $datef = __('M j, Y \a\t G:i', 'simple-history');
492
- $str_when = date_i18n(
493
- $datef,
494
- strtotime(get_date_from_gmt($row->date))
495
- );
496
- } else {
497
- // Show "nn minutes ago" when event is xx seconds ago or earlier
498
- $date_human_time_diff = human_time_diff(
499
- $date_datetime->getTimestamp(),
500
- $time_current
501
- );
502
- /* Translators: 1: last modified date and time in human time diff-format */
503
- $str_when = sprintf(
504
- __('%1$s ago', 'simple-history'),
505
- $date_human_time_diff
506
- );
507
- }
508
-
509
- $item_permalink = admin_url( apply_filters( 'simple_history/admin_location', 'index' ) . '.php?page=simple_history_page' );
510
- if ( ! empty( $row->id ) ) {
511
- $item_permalink .= "#item/{$row->id}";
512
- }
513
-
514
- // Datetime attribute on <time> element.
515
- $str_datetime_title = sprintf(
516
- __('%1$s local time %3$s (%2$s GMT time)', 'simple-history'),
517
- get_date_from_gmt(
518
- $date_datetime->format('Y-m-d H:i:s'),
519
- $date_and_time_format
520
- ), // 1 local time
521
- $date_datetime->format($date_and_time_format), // GMT time
522
- PHP_EOL // 3, new line
523
- );
524
-
525
- // Time and date before live updated relative date.
526
- $str_datetime_local = sprintf(
527
- '%1$s',
528
- get_date_from_gmt(
529
- $date_datetime->format('Y-m-d H:i:s'),
530
- $local_date_format
531
- ) // 1 local time
532
- );
533
-
534
- // HTML for whole span with date info.
535
- $date_html =
536
- "<span class='SimpleHistoryLogitem__permalink SimpleHistoryLogitem__when SimpleHistoryLogitem__inlineDivided'>";
537
- $date_html .= "<a class='' href='{$item_permalink}'>";
538
- $date_html .= sprintf(
539
- '<span title="%1$s">%4$s (<time datetime="%3$s" class="SimpleHistoryLogitem__when__liveRelative">%2$s</time>)</span>',
540
- esc_attr($str_datetime_title), // 1 datetime attribute
541
- esc_html($str_when), // 2 date text, visible in log, but overridden by JS relative date script.
542
- $date_datetime->format(DateTime::RFC3339), // 3
543
- esc_html($str_datetime_local) // 4
544
- );
545
- $date_html .= '</a>';
546
- $date_html .= '</span>';
547
-
548
- /**
549
- * Filter the output of the date section of the header.
550
- *
551
- * @since 2.5.1
552
- *
553
- * @param String $date_html
554
- * @param array $row
555
- */
556
- $date_html = apply_filters(
557
- 'simple_history/row_header_date_output',
558
- $date_html,
559
- $row
560
- );
561
-
562
- // Logger "via" info in header, i.e. output some extra
563
- // info next to the time to make it more clear what plugin etc.
564
- // that "caused" this event
565
- $via_html = '';
566
- $logger_name_via = $this->getInfoValueByKey('name_via');
567
-
568
- if ($logger_name_via) {
569
- $via_html =
570
- "<span class='SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__via'>";
571
- $via_html .= $logger_name_via;
572
- $via_html .= '</span>';
573
- }
574
-
575
- // Loglevel
576
- // SimpleHistoryLogitem--loglevel-warning
577
- /*
578
- $level_html = sprintf(
579
- '<span class="SimpleHistoryLogitem--logleveltag SimpleHistoryLogitem--logleveltag-%1$s">%1$s</span>',
580
- $row->level
581
- );
582
- */
583
-
584
- // Glue together final result
585
- $template = '
 
 
 
586
  %1$s
587
  %2$s
588
  %3$s
589
  ';
590
- // if ( ! $initiator_html ) {
591
- // $template = '%2$s';
592
- // }
593
- $html = sprintf(
594
- $template,
595
- $initiator_html, // 1
596
- $date_html, // 2
597
- $via_html // 3
598
- );
599
-
600
- /**
601
- * Filter generated html for the log row header
602
- *
603
- * @since 2.0
604
- *
605
- * @param string $html
606
- * @param object $row Log row
607
- */
608
- $html = apply_filters('simple_history/row_header_output', $html, $row);
609
-
610
- return $html;
611
- }
612
-
613
- /**
614
- * Returns the plain text version of this entry
615
- * Used in for example CSV-exports.
616
- * Defaults to log message with context interpolated.
617
- * Keep format as plain and simple as possible.
618
- * Links are ok, for example to link to users or posts.
619
- * Tags will be stripped when text is used for CSV-exports and so on.
620
- * Keep it on a single line. No <p> or <br> and so on.
621
- *
622
- * Example output:
623
- * Edited post "About the company"
624
- *
625
- * Message should sound like it's coming from the user.
626
- * Image that the name of the user is added in front of the text:
627
- * Jessie James: Edited post "About the company"
628
- */
629
- public function getLogRowPlainTextOutput($row)
630
- {
631
- $message = $row->message;
632
- $message_key = isset($row->context['_message_key'])
633
- ? $row->context['_message_key']
634
- : null;
635
-
636
- // Message is translated here, but translation must be added in
637
- // plain text before
638
- if (empty($message_key)) {
639
- // Message key did not exist, so check if we should translate using textdomain
640
- if (!empty($row->context['_gettext_domain'])) {
641
- $message = __($message, $row->context['_gettext_domain']);
642
- }
643
- } else {
644
- // Check that messages does exist
645
- // If we for example disable a Logger we may have references
646
- // to message keys that are unavailable. If so then fallback to message.
647
- if (isset($this->messages[$message_key]['translated_text'])) {
648
- $message = $this->messages[$message_key]['translated_text'];
649
- } else {
650
- // Not message exists for message key. Just keep using message.
651
- }
652
- }
653
-
654
- $html = $this->interpolate($message, $row->context, $row);
655
-
656
- // All messages are escaped by default.
657
- // If you need unescaped output override this method
658
- // in your own logger
659
- $html = esc_html($html);
660
-
661
- /**
662
- * Filter generated output for plain text output
663
- *
664
- * @since 2.0
665
- *
666
- * @param string $html
667
- * @param object $row Log row
668
- */
669
- $html = apply_filters(
670
- 'simple_history/row_plain_text_output',
671
- $html,
672
- $row
673
- );
674
-
675
- return $html;
676
- }
677
-
678
- /**
679
- * Get output for image
680
- * Image can be for example gravar if sender is user,
681
- * or other images if sender i system, wordpress, and so on
682
- */
683
- public function getLogRowSenderImageOutput($row)
684
- {
685
- $sender_image_html = '';
686
- $sender_image_size = 32;
687
-
688
- $initiator = $row->initiator;
689
-
690
- switch ($initiator) {
691
- // wp_user = wordpress uses, but user may have been deleted since log entry was added
692
- case 'wp_user':
693
- $user_id = isset($row->context['_user_id'])
694
- ? $row->context['_user_id']
695
- : null;
696
-
697
- if ($user_id > 0 && ($user = get_user_by('id', $user_id))) {
698
- // Sender was user
699
- $sender_image_html = $this->simpleHistory->get_avatar(
700
- $user->user_email,
701
- $sender_image_size
702
- );
703
- } elseif ($user_id > 0) {
704
- // Sender was a user, but user is deleted now
705
- $sender_image_html = $this->simpleHistory->get_avatar(
706
- '',
707
- $sender_image_size
708
- );
709
- } else {
710
- $sender_image_html = $this->simpleHistory->get_avatar(
711
- '',
712
- $sender_image_size
713
- );
714
- }
715
-
716
- break;
717
- }
718
-
719
- /**
720
- * Filter generated output for row image (sender image)
721
- *
722
- * @since 2.0
723
- *
724
- * @param string $sender_image_html
725
- * @param object $row Log row
726
- */
727
- $sender_image_html = apply_filters(
728
- 'simple_history/row_sender_image_output',
729
- $sender_image_html,
730
- $row
731
- );
732
-
733
- return $sender_image_html;
734
- }
735
-
736
- /**
737
- * Use this method to output detailed output for a log row
738
- * Example usage is if a user has uploaded an image then a
739
- * thumbnail of that image can bo outputed here
740
- *
741
- * @param object $row
742
- * @return string HTML-formatted output
743
- */
744
- public function getLogRowDetailsOutput($row)
745
- {
746
- $html = '';
747
-
748
- /**
749
- * Filter generated output for details
750
- *
751
- * @since 2.0
752
- *
753
- * @param string $html
754
- * @param object $row Log row
755
- */
756
- $html = apply_filters('simple_history/row_details_output', $html, $row);
757
-
758
- return $html;
759
- }
760
-
761
- /**
762
- * System is unusable.
763
- *
764
- * @param string $message
765
- * @param array $context
766
- * @return null
767
- */
768
- public function emergency($message, array $context = array())
769
- {
770
- return $this->log(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
771
- }
772
-
773
- /**
774
- * System is unusable.
775
- *
776
- * @param string $message key from getInfo messages array
777
- * @param array $context
778
- * @return null
779
- */
780
- public function emergencyMessage($message, array $context = array())
781
- {
782
- return $this->logByMessageKey(
783
- SimpleLoggerLogLevels::EMERGENCY,
784
- $message,
785
- $context
786
- );
787
- }
788
-
789
- /**
790
- * Log with message
791
- * Called from infoMessage(), errorMessage(), and so on
792
- *
793
- * Call like this:
794
- *
795
- * return $this->logByMessageKey(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
796
- */
797
- private function logByMessageKey(
798
- $SimpleLoggerLogLevelsLevel,
799
- $messageKey,
800
- $context
801
- ) {
802
- // When logging by message then the key must exist
803
- if (!isset($this->messages[$messageKey]['untranslated_text'])) {
804
- return;
805
- }
806
-
807
- /**
808
- * Filter so plugins etc. can shortut logging
809
- *
810
- * @since 2.0.20
811
- *
812
- * @param true yes, we default to do the logging
813
- * @param string logger slug
814
- * @param string messageKey
815
- * @param string log level
816
- * @param array context
817
- * @return bool false to abort logging
818
- */
819
- $doLog = apply_filters(
820
- 'simple_history/simple_logger/log_message_key',
821
- true,
822
- $this->slug,
823
- $messageKey,
824
- $SimpleLoggerLogLevelsLevel,
825
- $context
826
- );
827
-
828
- if (!$doLog) {
829
- return;
830
- }
831
-
832
- $context['_message_key'] = $messageKey;
833
- $message = $this->messages[$messageKey]['untranslated_text'];
834
-
835
- $this->log($SimpleLoggerLogLevelsLevel, $message, $context);
836
- }
837
-
838
- /**
839
- * Action must be taken immediately.
840
- *
841
- * @param string $message
842
- * @param array $context
843
- * @return null
844
- */
845
- public function alert($message, array $context = array())
846
- {
847
- return $this->log(SimpleLoggerLogLevels::ALERT, $message, $context);
848
- }
849
-
850
- /**
851
- * Action must be taken immediately.
852
- *
853
- * @param string $message key from getInfo messages array
854
- * @param array $context
855
- * @return null
856
- */
857
- public function alertMessage($message, array $context = array())
858
- {
859
- return $this->logByMessageKey(
860
- SimpleLoggerLogLevels::ALERT,
861
- $message,
862
- $context
863
- );
864
- }
865
-
866
- /**
867
- * Critical conditions.
868
- *
869
- * Example: Application component unavailable, unexpected exception.
870
- *
871
- * @param string $message
872
- * @param array $context
873
- * @return null
874
- */
875
- public function critical($message, array $context = array())
876
- {
877
- return $this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
878
- }
879
-
880
- /**
881
- * Critical conditions.
882
- *
883
- * @param string $message key from getInfo messages array
884
- * @param array $context
885
- * @return null
886
- */
887
- public function criticalMessage($message, array $context = array())
888
- {
889
- if (!isset($this->messages[$message]['untranslated_text'])) {
890
- return;
891
- }
892
-
893
- $context['_message_key'] = $message;
894
- $message = $this->messages[$message]['untranslated_text'];
895
-
896
- $this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
897
- }
898
-
899
- /**
900
- * Runtime errors that do not require immediate action but should typically
901
- * be logged and monitored.
902
- *
903
- * @param string $message
904
- * @param array $context
905
- * @return null
906
- */
907
- public function error($message, array $context = array())
908
- {
909
- return $this->log(SimpleLoggerLogLevels::ERROR, $message, $context);
910
- }
911
-
912
- /**
913
- * Runtime errors that do not require immediate action but should typically
914
- * be logged and monitored.
915
- *
916
- * @param string $message key from getInfo messages array
917
- * @param array $context
918
- * @return null
919
- */
920
- public function errorMessage($message, array $context = array())
921
- {
922
- return $this->logByMessageKey(
923
- SimpleLoggerLogLevels::ERROR,
924
- $message,
925
- $context
926
- );
927
- }
928
-
929
- /**
930
- * Exceptional occurrences that are not errors.
931
- *
932
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
933
- * that are not necessarily wrong.
934
- *
935
- * @param string $message
936
- * @param array $context
937
- * @return null
938
- */
939
- public function warning($message, array $context = array())
940
- {
941
- return $this->log(SimpleLoggerLogLevels::WARNING, $message, $context);
942
- }
943
-
944
- /**
945
- * Exceptional occurrences that are not errors.
946
- *
947
- * @param string $message key from getInfo messages array
948
- * @param array $context
949
- * @return null
950
- */
951
- public function warningMessage($message, array $context = array())
952
- {
953
- return $this->logByMessageKey(
954
- SimpleLoggerLogLevels::WARNING,
955
- $message,
956
- $context
957
- );
958
- }
959
-
960
- /**
961
- * Normal but significant events.
962
- *
963
- * @param string $message
964
- * @param array $context
965
- * @return null
966
- */
967
- public function notice($message, array $context = array())
968
- {
969
- return $this->log(SimpleLoggerLogLevels::NOTICE, $message, $context);
970
- }
971
-
972
- /**
973
- * Normal but significant events.
974
- *
975
- * @param string $message key from getInfo messages array
976
- * @param array $context
977
- * @return null
978
- */
979
- public function noticeMessage($message, array $context = array())
980
- {
981
- return $this->logByMessageKey(
982
- SimpleLoggerLogLevels::NOTICE,
983
- $message,
984
- $context
985
- );
986
- }
987
-
988
- /**
989
- * Interesting events.
990
- *
991
- * Example: User logs in, SQL logs.
992
- *
993
- * @param string $message
994
- * @param array $context
995
- * @return null
996
- */
997
- public function info($message, array $context = array())
998
- {
999
- return $this->log(SimpleLoggerLogLevels::INFO, $message, $context);
1000
- }
1001
-
1002
- /**
1003
- * Interesting events.
1004
- *
1005
- * Example: User logs in, SQL logs.
1006
- *
1007
- * @param string $message key from getInfo messages array
1008
- * @param array $context
1009
- * @return null
1010
- */
1011
- public function infoMessage($message, array $context = array())
1012
- {
1013
- return $this->logByMessageKey(
1014
- SimpleLoggerLogLevels::INFO,
1015
- $message,
1016
- $context
1017
- );
1018
- }
1019
-
1020
- /**
1021
- * Detailed debug information.
1022
- *
1023
- * @param string $message
1024
- * @param array $context
1025
- * @return null
1026
- */
1027
- public function debug($message, array $context = array())
1028
- {
1029
- return $this->log(SimpleLoggerLogLevels::DEBUG, $message, $context);
1030
- }
1031
-
1032
- /**
1033
- * Detailed debug information.
1034
- *
1035
- * @param string $message key from getInfo messages array
1036
- * @param array $context
1037
- * @return null
1038
- */
1039
- public function debugMessage($message, array $context = array())
1040
- {
1041
- return $this->logByMessageKey(
1042
- SimpleLoggerLogLevels::DEBUG,
1043
- $message,
1044
- $context
1045
- );
1046
- }
1047
-
1048
- /**
1049
- * Logs with an arbitrary level.
1050
- *
1051
- * @param mixed $level The log level. Default "info".
1052
- * @param string $message The log message. Default "".
1053
- * @param array $context The log context. Default empty array.
1054
- * @return class SimpleLogger instance
1055
- */
1056
- public function log($level = 'info', $message = '', $context = array())
1057
- {
1058
- global $wpdb;
1059
-
1060
- // Check that passed args are of correct types.
1061
- if (!is_string($level) || !is_string($message)) {
1062
- return $this;
1063
- }
1064
-
1065
- // Context must be array, but can be passed as null and so on.
1066
- if (!is_array($context)) {
1067
- $context = array();
1068
- }
1069
-
1070
- // Don't go on if message is empty.
1071
- if (empty($message)) {
1072
- return $this;
1073
- }
1074
-
1075
- /**
1076
- * Filter that makes it possible to shortcut this log.
1077
- * Return bool false to cancel.
1078
- *
1079
- * @since 2.3.1
1080
- */
1081
- $do_log = apply_filters(
1082
- 'simple_history/log/do_log',
1083
- true,
1084
- $level,
1085
- $message,
1086
- $context,
1087
- $this
1088
- );
1089
- if (false === $do_log) {
1090
- return $this;
1091
- }
1092
-
1093
- /**
1094
- * Easy shortcut method to disable logging of messages from
1095
- * a specific logger.
1096
- *
1097
- * Example filter name:
1098
- * simple_history/log/do_log/SimpleUserLogger
1099
- * simple_history/log/do_log/SimplePostLogger
1100
- *
1101
- * Example to disable logging of any user login/logout/failed login activity:
1102
- * add_filter('simple_history/log/do_log/SimpleUserLogger', '__return_false')
1103
- *
1104
- * @since 2.nn
1105
- */
1106
- $do_log = apply_filters(
1107
- "simple_history/log/do_log/{$this->slug}",
1108
- true
1109
- );
1110
- if (false === $do_log) {
1111
- return $this;
1112
- }
1113
-
1114
- /**
1115
- * Easy shortcut method to disable logging of messages from
1116
- * a specific logger and message.
1117
- *
1118
- * Example filter name:
1119
- * simple_history/log/do_log/SimpleUserLogger/user_logged_in
1120
- * simple_history/log/do_log/SimplePostLogger/post_updated
1121
- *
1122
- * @since 2.nn
1123
- */
1124
- $message_key = isset($context['_message_key'])
1125
- ? $context['_message_key']
1126
- : null;
1127
- $do_log = apply_filters(
1128
- "simple_history/log/do_log/{$this->slug}/{$message_key}",
1129
- true
1130
- );
1131
- if (false === $do_log) {
1132
- return $this;
1133
- }
1134
-
1135
- // Check if $message is a translated message, and if so then fetch original
1136
- $sh_latest_translations =
1137
- $this->simpleHistory->gettextLatestTranslations;
1138
-
1139
- if (!empty($sh_latest_translations)) {
1140
- if (isset($sh_latest_translations[$message])) {
1141
- // Translation of this phrase was found, so use original phrase instead of translated one
1142
- // Store textdomain since it's required to translate
1143
- $context['_gettext_domain'] =
1144
- $sh_latest_translations[$message]['domain'];
1145
-
1146
- // These are good to keep when debugging
1147
- // $context["_gettext_org_message"] = $sh_latest_translations[$message]["text"];
1148
- // $context["_gettext_translated_message"] = $sh_latest_translations[$message]["translation"];
1149
- $message = $sh_latest_translations[$message]['text'];
1150
- }
1151
- }
1152
-
1153
- /**
1154
- * Filter arguments passed to log funtion
1155
- *
1156
- * @since 2.0
1157
- *
1158
- * @param string $level
1159
- * @param string $message
1160
- * @param array $context
1161
- * @param object SimpleLogger object
1162
- */
1163
- apply_filters(
1164
- 'simple_history/log_arguments',
1165
- $level,
1166
- $message,
1167
- $context,
1168
- $this
1169
- );
1170
- $context = apply_filters(
1171
- 'simple_history/log_argument/context',
1172
- $context,
1173
- $level,
1174
- $message,
1175
- $this
1176
- );
1177
- $level = apply_filters(
1178
- 'simple_history/log_argument/level',
1179
- $level,
1180
- $context,
1181
- $message,
1182
- $this
1183
- );
1184
- $message = apply_filters(
1185
- 'simple_history/log_argument/message',
1186
- $message,
1187
- $level,
1188
- $context,
1189
- $this
1190
- );
1191
-
1192
- /*
1193
- Store date as GMT date, i.e. not local date/time
1194
- * Some info here:
1195
- * http://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
1196
- */
1197
- $localtime = current_time('mysql', 1);
1198
-
1199
- $db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
1200
-
1201
- /**
1202
- * Filter db table used for simple history events
1203
- *
1204
- * @since 2.0
1205
- *
1206
- * @param string $db_table
1207
- */
1208
- $db_table = apply_filters('simple_history/db_table', $db_table);
1209
-
1210
- $data = array(
1211
- 'logger' => $this->slug,
1212
- 'level' => $level,
1213
- 'date' => $localtime,
1214
- 'message' => $message
1215
- );
1216
-
1217
- // Allow date to be overriden.
1218
- // Date must be in format 'Y-m-d H:i:s'.
1219
- if (isset($context['_date'])) {
1220
- $data['date'] = $context['_date'];
1221
- unset($context['_date']);
1222
- }
1223
-
1224
- // Add occasions id.
1225
- $occasions_id = null;
1226
- if (isset($context['_occasionsID'])) {
1227
- // Minimize risk of similar loggers logging same messages and such and resulting in same occasions id
1228
- // by always adding logger slug.
1229
- $occasions_data = array(
1230
- '_occasionsID' => $context['_occasionsID'],
1231
- '_loggerSlug' => $this->slug
1232
- );
1233
- $occasions_id = md5(json_encode($occasions_data));
1234
- unset($context['_occasionsID']);
1235
- } else {
1236
- // No occasions id specified, create one bases on the data array.
1237
- $occasions_data = $data + $context;
1238
-
1239
- // Don't include date in context data.
1240
- unset($occasions_data['date']);
1241
-
1242
- $occasions_id = md5(json_encode($occasions_data));
1243
- }
1244
-
1245
- $data['occasionsID'] = $occasions_id;
1246
-
1247
- // Log initiator, defaults to current user if exists, or other if not user exist
1248
- if (isset($context['_initiator'])) {
1249
- // Manually set in context
1250
- $data['initiator'] = $context['_initiator'];
1251
- unset($context['_initiator']);
1252
- } else {
1253
- // No initiator set, try to determine
1254
- // Default to other
1255
- $data['initiator'] = SimpleLoggerLogInitiators::OTHER;
1256
-
1257
- // Check if user is responsible.
1258
- if (function_exists('wp_get_current_user')) {
1259
- $current_user = wp_get_current_user();
1260
-
1261
- if (isset($current_user->ID) && $current_user->ID) {
1262
- $data['initiator'] = SimpleLoggerLogInitiators::WP_USER;
1263
- $context['_user_id'] = $current_user->ID;
1264
- $context['_user_login'] = $current_user->user_login;
1265
- $context['_user_email'] = $current_user->user_email;
1266
- }
1267
- }
1268
-
1269
- // If cron then set WordPress as responsible
1270
- if (defined('DOING_CRON') && DOING_CRON) {
1271
- // Seems to be wp cron running and doing this.
1272
- $data['initiator'] = SimpleLoggerLogInitiators::WORDPRESS;
1273
- $context['_wp_cron_running'] = true;
1274
-
1275
- // To aid debugging we log the current filter and a list of all filters.
1276
- global $wp_current_filter;
1277
- $context['_wp_cron_current_filter'] = current_filter();
1278
- }
1279
-
1280
- // If running as CLI and WP_CLI_PHP_USED is set then it is WP CLI that is doing it
1281
- // How to log this? Is this a user, is it WordPress, or what?
1282
- // I'm thinking:
1283
- // - it is a user that is manually doing this, on purpose, with intent, so not auto wordpress
1284
- // - it is a specific user, but we don't know who
1285
- // - sounds like a special case, set initiator to wp_cli
1286
- // Can be used by plugins/themes to check if WP-CLI is running or not
1287
- if (defined('WP_CLI') && WP_CLI) {
1288
- $data['initiator'] = SimpleLoggerLogInitiators::WP_CLI;
1289
- }
1290
- } // End if().
1291
-
1292
- // Detect XML-RPC calls and append to context, if not already there.
1293
- if (defined('XMLRPC_REQUEST') &&
1294
- XMLRPC_REQUEST &&
1295
- !isset($context['_xmlrpc_request'])
1296
- ) {
1297
- $context['_xmlrpc_request'] = true;
1298
- }
1299
-
1300
- // Detect REST calls and append to context, if not already there.
1301
- $isRestApiRequest =
1302
- (defined('REST_API_REQUEST') && REST_API_REQUEST) ||
1303
- (defined('REST_REQUEST') && REST_REQUEST);
1304
- if ($isRestApiRequest) {
1305
- $context['_rest_api_request'] = true;
1306
- }
1307
-
1308
- // Trim message.
1309
- $data['message'] = trim($data['message']);
1310
-
1311
- /**
1312
- * Filter data to be saved to db.
1313
- *
1314
- * @since 2.0
1315
- *
1316
- * @param array $data
1317
- */
1318
- $data = apply_filters('simple_history/log_insert_data', $data);
1319
-
1320
- // Insert data into db.
1321
- $result = $wpdb->insert($db_table, $data);
1322
-
1323
- $data_parent_row = null;
1324
-
1325
- // Only save context if able to store row.
1326
- if (false === $result) {
1327
- $history_inserted_id = null;
1328
- } else {
1329
- $history_inserted_id = $wpdb->insert_id;
1330
-
1331
- $db_table_contexts =
1332
- $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
1333
-
1334
- /**
1335
- * Filter table name for contexts.
1336
- *
1337
- * @since 2.0
1338
- *
1339
- * @param string $db_table_contexts
1340
- */
1341
- $db_table_contexts = apply_filters(
1342
- 'simple_history/logger_db_table_contexts',
1343
- $db_table_contexts
1344
- );
1345
-
1346
- if (!is_array($context)) {
1347
- $context = array();
1348
- }
1349
-
1350
- // Append user id to context, if not already added.
1351
- if (!isset($context['_user_id'])) {
1352
- // wp_get_current_user is not available early.
1353
- // http://codex.wordpress.org/Function_Reference/wp_get_current_user
1354
- // https://core.trac.wordpress.org/ticket/14024
1355
- if (function_exists('wp_get_current_user')) {
1356
- $current_user = wp_get_current_user();
1357
-
1358
- if (isset($current_user->ID) && $current_user->ID) {
1359
- $context['_user_id'] = $current_user->ID;
1360
- $context['_user_login'] = $current_user->user_login;
1361
- $context['_user_email'] = $current_user->user_email;
1362
- }
1363
- }
1364
- }
1365
-
1366
- // Add remote addr to context.
1367
- if (!isset($context['_server_remote_addr'])) {
1368
- $remote_addr = empty($_SERVER['REMOTE_ADDR'])
1369
- ? ''
1370
- : wp_unslash($_SERVER['REMOTE_ADDR']);
1371
-
1372
- /**
1373
- * Filter to control if ip addresses should be anonymized or not.
1374
- *
1375
- * @since 2.22
1376
- *
1377
- * @param bool true to anonymize ip address, false to keep original ip address.
1378
- * @return bool
1379
- */
1380
- $anonymize_ip_address = apply_filters(
1381
- 'simple_history/privacy/anonymize_ip_address',
1382
- true
1383
- );
1384
-
1385
- if ($anonymize_ip_address &&
1386
- function_exists('wp_privacy_anonymize_ip')
1387
- ) {
1388
- $remote_addr = wp_privacy_anonymize_ip($remote_addr);
1389
- }
1390
-
1391
- $context['_server_remote_addr'] = $remote_addr;
1392
-
1393
- // If web server is behind a load balancer then the ip address will always be the same
1394
- // See bug report: https://wordpress.org/support/topic/use-x-forwarded-for-http-header-when-logging-remote_addr?replies=1#post-6422981
1395
- // Note that the x-forwarded-for header can contain multiple ips, comma separated
1396
- // Also note that the header can be faked
1397
- // Ref: http://stackoverflow.com/questions/753645/how-do-i-get-the-correct-ip-from-http-x-forwarded-for-if-it-contains-multiple-ip
1398
- // Ref: http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
1399
- // Check for IP in lots of headers
1400
- // Based on code found here:
1401
- // http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
1402
- $ip_keys = $this->get_ip_number_header_keys();
1403
-
1404
- foreach ($ip_keys as $key) {
1405
- if (array_key_exists($key, $_SERVER) === true) {
1406
- // Loop through all IPs.
1407
- $ip_loop_num = 0;
1408
- foreach (explode(',', $_SERVER[$key]) as $ip) {
1409
- // trim for safety measures.
1410
- $ip = trim($ip);
1411
-
1412
- // attempt to validate IP.
1413
- if ($this->validate_ip($ip)) {
1414
- // valid, add to context, with loop index appended so we can store many IPs.
1415
- $key_lower = strtolower($key);
1416
-
1417
- if ($anonymize_ip_address &&
1418
- function_exists('wp_privacy_anonymize_ip')
1419
- ) {
1420
- $ip = wp_privacy_anonymize_ip($ip);
1421
- }
1422
-
1423
- $context[
1424
- "_server_{$key_lower}_{$ip_loop_num}"
1425
- ] = $ip;
1426
- }
1427
-
1428
- $ip_loop_num++;
1429
- }
1430
- }
1431
- }
1432
- } // End if().
1433
-
1434
- // Append http referer.
1435
- if (!isset($context['_server_http_referer']) &&
1436
- isset($_SERVER['HTTP_REFERER'])
1437
- ) {
1438
- $context['_server_http_referer'] = $_SERVER['HTTP_REFERER'];
1439
- }
1440
-
1441
- /**
1442
- * Filter the context to store for this event/row
1443
- *
1444
- * @since 2.0.29
1445
- *
1446
- * @param array $context Array with all context data to store. Modify and return this.
1447
- * @param array $data Array with data used for parent row.
1448
- * @param array $this Reference to this logger instance.
1449
- */
1450
- $context = apply_filters(
1451
- 'simple_history/log_insert_context',
1452
- $context,
1453
- $data,
1454
- $this
1455
- );
1456
- $data_parent_row = $data;
1457
-
1458
- // Insert all context values into db.
1459
- $this->append_context($history_inserted_id, $context);
1460
- } // End if().
1461
-
1462
- $this->lastInsertID = $history_inserted_id;
1463
- $this->lastInsertContext = $context;
1464
-
1465
- $this->simpleHistory->get_cache_incrementor(true);
1466
-
1467
- /**
1468
- * Action that is called after an event has been logged
1469
- *
1470
- * @since 2.5.1
1471
- *
1472
- * @param array $context Array with all context data that was used to log event.
1473
- * @param array $data Array with data used for parent row.
1474
- * @param array $this Reference to this logger instance
1475
- */
1476
- do_action(
1477
- 'simple_history/log/inserted',
1478
- $context,
1479
- $data_parent_row,
1480
- $this
1481
- );
1482
-
1483
- // Return $this so we can chain methods.
1484
- return $this;
1485
- } // log
1486
-
1487
- /**
1488
- * Append new info to the context of history item with id $post_logger->lastInsertID.
1489
- *
1490
- * @param int $history_id The id of the history row to add context to.
1491
- * @param array $context Context to append to existing context for the row.
1492
- * @return bool True if context was added, false if not (beacuse row_id or context is empty).
1493
- */
1494
- public function append_context($history_id, $context)
1495
- {
1496
- if (empty($history_id) || empty($context)) {
1497
- return false;
1498
- }
1499
-
1500
- global $wpdb;
1501
-
1502
- $db_table_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
1503
-
1504
- foreach ($context as $key => $value) {
1505
- // Everything except strings should be json_encoded, ie. arrays and objects.
1506
- if (!is_string($value)) {
1507
- $value = simpleHistory::json_encode($value);
1508
- }
1509
-
1510
- $data = array(
1511
- 'history_id' => $history_id,
1512
- 'key' => $key,
1513
- 'value' => $value
1514
- );
1515
-
1516
- $result = $wpdb->insert($db_table_contexts, $data);
1517
- }
1518
-
1519
- return true;
1520
- }
1521
-
1522
- /**
1523
- * Returns array with headers that may contain user IP
1524
- *
1525
- * @since 2.0.29
1526
- */
1527
- public function get_ip_number_header_keys()
1528
- {
1529
- $arr = array(
1530
- 'HTTP_CLIENT_IP',
1531
- 'HTTP_X_FORWARDED_FOR',
1532
- 'HTTP_X_FORWARDED',
1533
- 'HTTP_X_CLUSTER_CLIENT_IP',
1534
- 'HTTP_FORWARDED_FOR',
1535
- 'HTTP_FORWARDED'
1536
- );
1537
-
1538
- return $arr;
1539
- }
1540
-
1541
- /**
1542
- * Returns additional headers with ip number from context
1543
- *
1544
- * @since 2.0.29
1545
- * @param array $row Row with info.
1546
- * @return array Headers
1547
- */
 
 
 
 
1548
  public function get_event_ip_number_headers($row)
1549
- {
1550
- $ip_keys = $this->get_ip_number_header_keys();
1551
- $arr_found_additional_ip_headers = array();
1552
- $context = $row->context;
1553
-
1554
- foreach ($ip_keys as $one_ip_header_key) {
1555
- $one_ip_header_key_lower = strtolower($one_ip_header_key);
1556
-
1557
- foreach ($context as $context_key => $context_val) {
1558
- // $key_check_for = "_server_" . strtolower($one_ip_header_key) . "_0";
1559
- $match = preg_match(
1560
- "/^_server_{$one_ip_header_key_lower}_[\d+]/",
1561
- $context_key,
1562
- $matches
1563
- );
1564
- if ($match) {
1565
- $arr_found_additional_ip_headers[
1566
- $context_key
1567
- ] = $context_val;
1568
- }
1569
- }
1570
- } // End foreach().
1571
-
1572
- return $arr_found_additional_ip_headers;
1573
- }
1574
-
1575
- /**
1576
- * Ensures an ip address is both a valid IP and does not fall within
1577
- * a private network range.
1578
- *
1579
- * @param string $ip IP number.
1580
- * @return bool
1581
- */
1582
  public function validate_ip($ip)
1583
- {
1584
- if (filter_var(
1585
- $ip,
1586
- FILTER_VALIDATE_IP,
1587
- FILTER_FLAG_IPV4 |
1588
- FILTER_FLAG_NO_PRIV_RANGE |
1589
- FILTER_FLAG_NO_RES_RANGE
1590
- ) === false
1591
- ) {
1592
- return false;
1593
- }
1594
-
1595
- return true;
1596
- }
1597
-
1598
- /**
1599
- * Override this to add CSS in <head> for your logger.
1600
- * The CSS that you output will only be outputed
1601
- * on pages where Simple History is used.
1602
- */
 
1603
  public function adminCSS()
1604
- {
1605
- /*
1606
- ?>
1607
- <style>
1608
- body {
1609
- border: 2px solid red;
1610
- }
1611
- </style>
1612
- <?php
1613
- */
1614
- }
1615
-
1616
- /**
1617
- * Override this to add JavaScript in the footer for your logger.
1618
- * The JS that you output will only be outputed
1619
- * on pages where Simple History is used.
1620
- */
1621
  public function adminJS()
1622
- {
1623
- /*
1624
- ?>
1625
- <script>
1626
- console.log("This is outputed in the footer");
1627
- </script>
1628
- <?php
1629
- */
1630
- }
1631
  }
1632
 
1633
  /**
@@ -1635,23 +1643,23 @@ class SimpleLogger
1635
  */
1636
  class SimpleLoggerLogInitiators
1637
  {
1638
- // A wordpress user that at the log event created did exist in the wp database
1639
- // May have been deleted when the log is viewed.
1640
- const WP_USER = 'wp_user';
1641
 
1642
- // Cron job run = wordpress initiated
1643
- // Email sent to customer on webshop = system/wordpress/anonymous web user
1644
- // Javascript error occured on website = anonymous web user.
1645
- const WEB_USER = 'web_user';
1646
 
1647
- // WordPress core or plugins updated automatically via wp-cron.
1648
- const WORDPRESS = 'wp';
1649
 
1650
- // WP CLI / terminal.
1651
- const WP_CLI = 'wp_cli';
1652
 
1653
- // I dunno.
1654
- const OTHER = 'other';
1655
  }
1656
 
1657
  /**
@@ -1663,11 +1671,11 @@ class SimpleLoggerLogInitiators
1663
  */
1664
  class SimpleLoggerLogTypes
1665
  {
1666
- const CREATE = 'create';
1667
- const READ = 'read';
1668
- const UPDATE = 'update';
1669
- const DELETE = 'delete';
1670
- const OTHER = 'other';
1671
  }
1672
 
1673
  /**
@@ -1675,12 +1683,12 @@ class SimpleLoggerLogTypes
1675
  */
1676
  class SimpleLoggerLogLevels
1677
  {
1678
- const EMERGENCY = 'emergency';
1679
- const ALERT = 'alert';
1680
- const CRITICAL = 'critical';
1681
- const ERROR = 'error';
1682
- const WARNING = 'warning';
1683
- const NOTICE = 'notice';
1684
- const INFO = 'info';
1685
- const DEBUG = 'debug';
1686
  }
12
  */
13
  class SimpleLogger
14
  {
15
+ /**
16
+ * Unique slug for this logger
17
+ * Will be saved in DB and used to associate each log row with its logger
18
+ */
19
+ public $slug = __CLASS__;
20
+
21
+ /**
22
+ * Will contain the untranslated messages from getInfo()
23
+ *
24
+ * By adding your messages here they will be stored both translated and non-translated
25
+ * You then log something like this:
26
+ * <code>
27
+ * $this->info( $this->messages["POST_UPDATED"] );
28
+ * </code>
29
+ * or with the shortcut
30
+ * <code>
31
+ * $this->infoMessage("POST_UPDATED");
32
+ * </code>
33
+ * which results in the original, untranslated, string being added to the log and database
34
+ * the translated string are then only used when showing the log in the GUI
35
+ */
36
+ public $messages;
37
+
38
+ /**
39
+ * ID of last inserted row
40
+ *
41
+ * @var int $lastInsertID Database row primary key.
42
+ */
43
+ public $lastInsertID;
44
+
45
+ /**
46
+ * Context of last inserted row.
47
+ *
48
+ * @var int $lastInsertContext Context used for the last insert.
49
+ * @since 2.2x
50
+ */
51
+ public $lastInsertContext;
52
+
53
+ /**
54
+ * Simple History instance.
55
+ *
56
+ * @var object $simpleHistory Simple history instance.
57
+ */
58
+ public $simpleHistory;
59
+
60
+ /**
61
+ * Constructor. Remember to call this as parent constructor if making a childlogger
62
+ *
63
+ * @param $simpleHistory history class objectinstance
64
+ */
65
+ public function __construct($simpleHistory = null)
66
+ {
67
+ global $wpdb;
68
+
69
+ $this->db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
70
+ $this->db_table_contexts =
71
+ $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
72
+
73
+ $this->simpleHistory = $simpleHistory;
74
+ }
75
+
76
+ /**
77
+ * Method that is called automagically when logger is loaded by Simple History
78
+ * Add your init stuff here
79
+ */
80
+ public function loaded()
81
+ {
82
+ }
83
+
84
+ /**
85
+ * Get array with information about this logger
86
+ *
87
+ * @return array
88
+ */
89
  public function getInfo()
90
+ {
91
+ $arr_info = array(
92
+ // The logger slug. Defaulting to the class name is nice and logical I think
93
+ 'slug' => __CLASS__,
94
+
95
+ // Shown on the info-tab in settings, use these fields to tell
96
+ // an admin what your logger is used for
97
+ 'name' => 'SimpleLogger',
98
+ 'description' => 'The built in logger for Simple History',
99
+
100
+ // Capability required to view log entries from this logger
101
+ 'capability' => 'edit_pages',
102
+ 'messages' => array(
103
+ // No pre-defined variants
104
+ // when adding messages __() or _x() must be used
105
+ )
106
+ );
107
+
108
+ return $arr_info;
109
+ }
110
+
111
+ /**
112
+ * Return single array entry from the array in getInfo()
113
+ * Returns the value of the key if value exists, or null
114
+ *
115
+ * @since 2.5.4
116
+ * @return Mixed
117
+ */
118
  public function getInfoValueByKey($key)
119
+ {
120
+ $arr_info = $this->getInfo();
121
+
122
+ return isset($arr_info[$key]) ? $arr_info[$key] : null;
123
+ }
124
+
125
+ /**
126
+ * Returns the capability required to read log rows from this logger
127
+ *
128
+ * @return $string capability
129
+ */
130
+ public function getCapability()
131
+ {
132
+ $arr_info = $this->getInfo();
133
+
134
+ $capability = 'manage_options';
135
+
136
+ if (!empty($arr_info['capability'])) {
137
+ $capability = $arr_info['capability'];
138
+ }
139
+
140
+ return $capability;
141
+ }
142
+
143
+ /**
144
+ * Interpolates context values into the message placeholders.
145
+ *
146
+ * @param string $message
147
+ * @param array $context
148
+ * @param array $row Currently not always passed, because loggers need to be updated to support this...
149
+ */
150
  public function interpolate($message, $context = array(), $row = null)
151
+ {
152
+ if (!is_array($context)) {
153
+ return $message;
154
+ }
155
+
156
+ /**
157
+ * Filter the context used to create the message from the message template
158
+ *
159
+ * @since 2.2.4
160
+ */
161
+ $context = apply_filters(
162
+ 'simple_history/logger/interpolate/context',
163
+ $context,
164
+ $message,
165
+ $row
166
+ );
167
+
168
+ // Build a replacement array with braces around the context keys
169
+ $replace = array();
170
+ foreach ($context as $key => $val) {
171
+ // Both key and val must be strings or number (for vals)
172
+ if (is_string($key) || is_numeric($key)) {
173
+ // key ok
174
+ }
175
+
176
+ if (is_string($val) || is_numeric($val)) {
177
+ // val ok
178
+ } else {
179
+ // not a value we can replace
180
+ continue;
181
+ }
182
+
183
+ $replace['{' . $key . '}'] = $val;
184
+ }
185
+
186
+ // Interpolate replacement values into the message and return
187
+ /*
188
+ if ( ! is_string( $message )) {
189
+ echo "message:";
190
+ var_dump($message);exit;
191
+ }
192
+ //*/
193
+ /*
194
+ if ( ! is_string( $replace )) {
195
+ echo "replace: \n";
196
+ var_dump($replace);
197
+ }
198
+ // */
199
+
200
+ return strtr($message, $replace);
201
+ }
202
+
203
+ /**
204
+ * Returns header output for a log row
205
+ * Format should be common for all log rows and should be like:
206
+ * Username (user role) · Date
207
+ *
208
+ * @return string HTML
209
+ */
210
  public function getLogRowHeaderOutput($row)
211
+ {
212
+ // HTML for initiator
213
+ $initiator_html = '';
214
+
215
+ $initiator = $row->initiator;
216
+ $context = $row->context;
217
+
218
+ switch ($initiator) {
219
+ case 'wp':
220
+ $initiator_html .=
221
+ '<strong class="SimpleHistoryLogitem__inlineDivided">WordPress</strong> ';
222
+ break;
223
+
224
+ case 'wp_cli':
225
+ $initiator_html .=
226
+ '<strong class="SimpleHistoryLogitem__inlineDivided">WP-CLI</strong> ';
227
+ break;
228
+
229
+ // wp_user = wordpress uses, but user may have been deleted since log entry was added
230
+ case 'wp_user':
231
+ $user_id = isset($row->context['_user_id'])
232
+ ? $row->context['_user_id']
233
+ : null;
234
+
235
+ if ($user_id > 0 && ($user = get_user_by('id', $user_id))) {
236
+ // Sender is user and user still exists
237
+ $is_current_user =
238
+ get_current_user_id() == $user_id ? true : false;
239
+
240
+ // get user role, as done in user-edit.php
241
+ $wp_roles = $GLOBALS['wp_roles'];
242
+ $all_roles = (array) $wp_roles->roles;
243
+ $user_roles = array_intersect(
244
+ array_values((array) $user->roles),
245
+ array_keys((array) $wp_roles->roles)
246
+ );
247
+ $user_role = array_shift($user_roles);
248
+
249
+ $user_display_name = $user->display_name;
250
+
251
+ /*
252
+ * If user who logged this is the currently logged in user
253
+ * skip name and email and use just "You"
254
+ *
255
+ * @param bool If you should be used
256
+ * @since 2.1
257
+ */
258
+ $use_you = apply_filters(
259
+ 'simple_history/header_initiator_use_you',
260
+ true
261
+ );
262
+
263
+ if ($use_you && $is_current_user) {
264
+ $tmpl_initiator_html = '
265
  <a href="%6$s" class="SimpleHistoryLogitem__headerUserProfileLink">
266
  <strong class="SimpleHistoryLogitem__inlineDivided">%5$s</strong>
267
  </a>
268
  ';
269
+ } else {
270
+ $tmpl_initiator_html = '
271
  <a href="%6$s" class="SimpleHistoryLogitem__headerUserProfileLink">
272
  <strong class="SimpleHistoryLogitem__inlineDivided">%3$s</strong>
273
  <span class="SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__headerEmail">%2$s</span>
274
  </a>
275
  ';
276
+ }
277
+
278
+ /**
279
+ * Filter the format for the user output
280
+ *
281
+ * @since 2.0
282
+ *
283
+ * @param string $format.
284
+ */
285
+ $tmpl_initiator_html = apply_filters(
286
+ 'simple_history/header_initiator_html_existing_user',
287
+ $tmpl_initiator_html
288
+ );
289
+
290
+ $initiator_html .= sprintf(
291
+ $tmpl_initiator_html,
292
+ esc_html($user->user_login), // 1
293
+ esc_html($user->user_email), // 2
294
+ esc_html($user_display_name), // 3
295
+ $user_role, // 4
296
+ _x(
297
+ 'You',
298
+ 'header output when initiator is the currently logged in user',
299
+ 'simple-history'
300
+ ), // 5
301
+ get_edit_user_link($user_id) // 6
302
+ );
303
+ } elseif ($user_id > 0) {
304
+ // Sender was a user, but user is deleted now
305
+ // output all info we have
306
+ // _user_id
307
+ // _username
308
+ // _user_login
309
+ // _user_email
310
+ $initiator_html .= sprintf(
311
+ '<strong class="SimpleHistoryLogitem__inlineDivided">' .
312
+ __(
313
+ 'Deleted user (had id %1$s, email %2$s, login %3$s)',
314
+ 'simple-history'
315
+ ) .
316
+ '</strong>',
317
+ esc_html($context['_user_id']), // 1
318
+ esc_html($context['_user_email']), // 2
319
+ esc_html($context['_user_login']) // 3
320
+ );
321
+ } // End if().
322
+
323
+ break;
324
+
325
+ case 'web_user':
326
+ /*
327
+ Note: server_remote_addr may not show visiting/attacking ip, if server is behind...stuff..
328
+ Can be behind varnish cashe, or browser can for example use compression in chrome mobile
329
+ then the real ip is behind _server_http_x_forwarded_for_0 or similar
330
+ _server_remote_addr 66.249.81.222
331
+ _server_http_x_forwarded_for_0 5.35.187.212
332
+ */
333
+
334
+ // Check if additional IP addresses are stored, from http_x_forwarded_for and so on
335
+ $arr_found_additional_ip_headers = $this->get_event_ip_number_headers(
336
+ $row
337
+ );
338
+
339
+ if (empty($context['_server_remote_addr'])) {
340
+ $initiator_html .=
341
+ "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
342
+ __('Anonymous web user', 'simple-history') .
343
+ '</strong> ';
344
+ } else {
345
+ $initiator_html .=
346
+ "<strong class='SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__anonUserWithIp'>";
347
+
348
+ // if ( sizeof( $arr_found_additional_ip_headers ) ) {
349
+ // $iplookup_link = sprintf('https://ipinfo.io/%1$s', esc_attr($context["_server_remote_addr"]));
350
+ // $ip_numbers_joined = wp_sprintf_l('%l', array("_server_remote_addr" => $context["_server_remote_addr"]) + $arr_found_additional_ip_headers);
351
+ /*
352
+ $initiator_html .= sprintf(
353
+ __('Anonymous user with multiple IP addresses detected: %1$s', "simple-history"),
354
+ "<a target='_blank' href={$iplookup_link} class='SimpleHistoryLogitem__anonUserWithIp__theIp'>" . esc_html( $ip_numbers_joined ) . "</a>"
355
+ );*/
356
+
357
+ /*
358
+ print_r($arr_found_additional_ip_headers);
359
+ Array
360
+ (
361
+ [_server_http_x_forwarded_for_0] => 5.35.187.212
362
+ [_server_http_x_forwarded_for_1] => 83.251.97.21
363
+ )
364
+ */
365
+
366
+ // } else {
367
+ // single ip address
368
+ $iplookup_link = sprintf(
369
+ 'https://ipinfo.io/%1$s',
370
+ esc_attr($context['_server_remote_addr'])
371
+ );
372
+
373
+ $initiator_html .= sprintf(
374
+ __('Anonymous user from %1$s', 'simple-history'),
375
+ "<a target='_blank' href={$iplookup_link} class='SimpleHistoryLogitem__anonUserWithIp__theIp'>" .
376
+ esc_html($context['_server_remote_addr']) .
377
+ '</a>'
378
+ );
379
+
380
+ // } // multiple ip
381
+ $initiator_html .= '</strong> ';
382
+
383
+ // $initiator_html .= "<strong>" . __("<br><br>Unknown user from {$context["_server_remote_addr"]}") . "</strong>";
384
+ // $initiator_html .= "<strong>" . __("<br><br>{$context["_server_remote_addr"]}") . "</strong>";
385
+ // $initiator_html .= "<strong>" . __("<br><br>User from IP {$context["_server_remote_addr"]}") . "</strong>";
386
+ // $initiator_html .= "<strong>" . __("<br><br>Non-logged in user from IP {$context["_server_remote_addr"]}") . "</strong>";
387
+ } // End if().
388
+
389
+ break;
390
+
391
+ case 'other':
392
+ $initiator_html .=
393
+ "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
394
+ _x(
395
+ 'Other',
396
+ 'Event header output, when initiator is unknown',
397
+ 'simple-history'
398
+ ) .
399
+ '</strong>';
400
+ break;
401
+
402
+ // no initiator
403
+ case null:
404
+ // $initiator_html .= "<strong class='SimpleHistoryLogitem__inlineDivided'>Null</strong>";
405
+ break;
406
+
407
+ default:
408
+ $initiator_html .=
409
+ "<strong class='SimpleHistoryLogitem__inlineDivided'>" .
410
+ esc_html($initiator) .
411
+ '</strong>';
412
+ } // End switch().
413
+
414
+ /**
415
+ * Filter generated html for the initiator row header html
416
+ *
417
+ * @since 2.0
418
+ *
419
+ * @param string $initiator_html
420
+ * @param object $row Log row
421
+ */
422
+ $initiator_html = apply_filters(
423
+ 'simple_history/row_header_initiator_output',
424
+ $initiator_html,
425
+ $row
426
+ );
427
+
428
+ // HTML for date
429
+ // Date (should...) always exist
430
+ // http://developers.whatwg.org/text-level-semantics.html#the-time-element
431
+ $date_html = '';
432
+ $str_when = '';
433
+
434
+ // $row->date is in GMT
435
+ $date_datetime = new DateTime($row->date, new DateTimeZone('GMT'));
436
+
437
+ // Current datetime in GMT
438
+ $time_current = strtotime(current_time('mysql', 1));
439
+
440
+ /**
441
+ * Filter how many seconds as most that can pass since an
442
+ * event occured to show "nn minutes ago" (human diff time-format) instead of exact date
443
+ *
444
+ * @since 2.0
445
+ *
446
+ * @param int $time_ago_max_time Seconds
447
+ */
448
+ $time_ago_max_time = DAY_IN_SECONDS * 2;
449
+ $time_ago_max_time = apply_filters(
450
+ 'simple_history/header_time_ago_max_time',
451
+ $time_ago_max_time
452
+ );
453
+
454
+ /**
455
+ * Filter how many seconds as most that can pass since an
456
+ * event occured to show "just now" instead of exact date
457
+ *
458
+ * @since 2.0
459
+ *
460
+ * @param int $time_ago_max_time Seconds
461
+ */
462
+ $time_ago_just_now_max_time = 30;
463
+ $time_ago_just_now_max_time = apply_filters(
464
+ 'simple_history/header_just_now_max_time',
465
+ $time_ago_just_now_max_time
466
+ );
467
+
468
+ $date_format = get_option('date_format');
469
+ $time_format = get_option('time_format');
470
+ $date_and_time_format = $date_format . ' - ' . $time_format;
471
+
472
+ // Show local time as hours an minutes when event is recent.
473
+ $local_date_format = $time_format;
474
+
475
+ // Show local time as date and hours when event is a bit older.
476
+ if (
477
+ $time_current - HOUR_IN_SECONDS * 6 >
478
+ $date_datetime->getTimestamp()
479
+ ) {
480
+ $local_date_format = $date_and_time_format;
481
+ }
482
+
483
+ if (
484
+ $time_current - $date_datetime->getTimestamp() <=
485
+ $time_ago_just_now_max_time
486
+ ) {
487
+ // Show "just now" if event is very recent.
488
+ $str_when = __('Just now', 'simple-history');
489
+ } elseif (
490
+ $time_current - $date_datetime->getTimestamp() >
491
+ $time_ago_max_time
492
+ ) {
493
+ /* Translators: Date format for log row header, see http://php.net/date */
494
+ $datef = __('M j, Y \a\t G:i', 'simple-history');
495
+ $str_when = date_i18n(
496
+ $datef,
497
+ strtotime(get_date_from_gmt($row->date))
498
+ );
499
+ } else {
500
+ // Show "nn minutes ago" when event is xx seconds ago or earlier
501
+ $date_human_time_diff = human_time_diff(
502
+ $date_datetime->getTimestamp(),
503
+ $time_current
504
+ );
505
+ /* Translators: 1: last modified date and time in human time diff-format */
506
+ $str_when = sprintf(
507
+ __('%1$s ago', 'simple-history'),
508
+ $date_human_time_diff
509
+ );
510
+ }
511
+
512
+ $item_permalink = admin_url(apply_filters('simple_history/admin_location', 'index') . '.php?page=simple_history_page');
513
+ if (! empty($row->id)) {
514
+ $item_permalink .= "#item/{$row->id}";
515
+ }
516
+
517
+ // Datetime attribute on <time> element.
518
+ $str_datetime_title = sprintf(
519
+ __('%1$s local time %3$s (%2$s GMT time)', 'simple-history'),
520
+ get_date_from_gmt(
521
+ $date_datetime->format('Y-m-d H:i:s'),
522
+ $date_and_time_format
523
+ ), // 1 local time
524
+ $date_datetime->format($date_and_time_format), // GMT time
525
+ PHP_EOL // 3, new line
526
+ );
527
+
528
+ // Time and date before live updated relative date.
529
+ $str_datetime_local = sprintf(
530
+ '%1$s',
531
+ get_date_from_gmt(
532
+ $date_datetime->format('Y-m-d H:i:s'),
533
+ $local_date_format
534
+ ) // 1 local time
535
+ );
536
+
537
+ // HTML for whole span with date info.
538
+ $date_html =
539
+ "<span class='SimpleHistoryLogitem__permalink SimpleHistoryLogitem__when SimpleHistoryLogitem__inlineDivided'>";
540
+ $date_html .= "<a class='' href='{$item_permalink}'>";
541
+ $date_html .= sprintf(
542
+ '<span title="%1$s">%4$s (<time datetime="%3$s" class="SimpleHistoryLogitem__when__liveRelative">%2$s</time>)</span>',
543
+ esc_attr($str_datetime_title), // 1 datetime attribute
544
+ esc_html($str_when), // 2 date text, visible in log, but overridden by JS relative date script.
545
+ $date_datetime->format(DateTime::RFC3339), // 3
546
+ esc_html($str_datetime_local) // 4
547
+ );
548
+ $date_html .= '</a>';
549
+ $date_html .= '</span>';
550
+
551
+ /**
552
+ * Filter the output of the date section of the header.
553
+ *
554
+ * @since 2.5.1
555
+ *
556
+ * @param String $date_html
557
+ * @param array $row
558
+ */
559
+ $date_html = apply_filters(
560
+ 'simple_history/row_header_date_output',
561
+ $date_html,
562
+ $row
563
+ );
564
+
565
+ // Logger "via" info in header, i.e. output some extra
566
+ // info next to the time to make it more clear what plugin etc.
567
+ // that "caused" this event
568
+ $via_html = '';
569
+ $logger_name_via = $this->getInfoValueByKey('name_via');
570
+
571
+ if ($logger_name_via) {
572
+ $via_html =
573
+ "<span class='SimpleHistoryLogitem__inlineDivided SimpleHistoryLogitem__via'>";
574
+ $via_html .= $logger_name_via;
575
+ $via_html .= '</span>';
576
+ }
577
+
578
+ // Loglevel
579
+ // SimpleHistoryLogitem--loglevel-warning
580
+ /*
581
+ $level_html = sprintf(
582
+ '<span class="SimpleHistoryLogitem--logleveltag SimpleHistoryLogitem--logleveltag-%1$s">%1$s</span>',
583
+ $row->level
584
+ );
585
+ */
586
+
587
+ // Glue together final result
588
+ $template = '
589
  %1$s
590
  %2$s
591
  %3$s
592
  ';
593
+ // if ( ! $initiator_html ) {
594
+ // $template = '%2$s';
595
+ // }
596
+ $html = sprintf(
597
+ $template,
598
+ $initiator_html, // 1
599
+ $date_html, // 2
600
+ $via_html // 3
601
+ );
602
+
603
+ /**
604
+ * Filter generated html for the log row header
605
+ *
606
+ * @since 2.0
607
+ *
608
+ * @param string $html
609
+ * @param object $row Log row
610
+ */
611
+ $html = apply_filters('simple_history/row_header_output', $html, $row);
612
+
613
+ return $html;
614
+ }
615
+
616
+ /**
617
+ * Returns the plain text version of this entry
618
+ * Used in for example CSV-exports.
619
+ * Defaults to log message with context interpolated.
620
+ * Keep format as plain and simple as possible.
621
+ * Links are ok, for example to link to users or posts.
622
+ * Tags will be stripped when text is used for CSV-exports and so on.
623
+ * Keep it on a single line. No <p> or <br> and so on.
624
+ *
625
+ * Example output:
626
+ * Edited post "About the company"
627
+ *
628
+ * Message should sound like it's coming from the user.
629
+ * Image that the name of the user is added in front of the text:
630
+ * Jessie James: Edited post "About the company"
631
+ */
632
+ public function getLogRowPlainTextOutput($row)
633
+ {
634
+ $message = $row->message;
635
+ $message_key = isset($row->context['_message_key'])
636
+ ? $row->context['_message_key']
637
+ : null;
638
+
639
+ // Message is translated here, but translation must be added in
640
+ // plain text before
641
+ if (empty($message_key)) {
642
+ // Message key did not exist, so check if we should translate using textdomain
643
+ if (!empty($row->context['_gettext_domain'])) {
644
+ $message = __($message, $row->context['_gettext_domain']);
645
+ }
646
+ } else {
647
+ // Check that messages does exist
648
+ // If we for example disable a Logger we may have references
649
+ // to message keys that are unavailable. If so then fallback to message.
650
+ if (isset($this->messages[$message_key]['translated_text'])) {
651
+ $message = $this->messages[$message_key]['translated_text'];
652
+ } else {
653
+ // Not message exists for message key. Just keep using message.
654
+ }
655
+ }
656
+
657
+ $html = $this->interpolate($message, $row->context, $row);
658
+
659
+ // All messages are escaped by default.
660
+ // If you need unescaped output override this method
661
+ // in your own logger
662
+ $html = esc_html($html);
663
+
664
+ /**
665
+ * Filter generated output for plain text output
666
+ *
667
+ * @since 2.0
668
+ *
669
+ * @param string $html
670
+ * @param object $row Log row
671
+ */
672
+ $html = apply_filters(
673
+ 'simple_history/row_plain_text_output',
674
+ $html,
675
+ $row
676
+ );
677
+
678
+ return $html;
679
+ }
680
+
681
+ /**
682
+ * Get output for image
683
+ * Image can be for example gravar if sender is user,
684
+ * or other images if sender i system, wordpress, and so on
685
+ */
686
+ public function getLogRowSenderImageOutput($row)
687
+ {
688
+ $sender_image_html = '';
689
+ $sender_image_size = 32;
690
+
691
+ $initiator = $row->initiator;
692
+
693
+ switch ($initiator) {
694
+ // wp_user = wordpress uses, but user may have been deleted since log entry was added
695
+ case 'wp_user':
696
+ $user_id = isset($row->context['_user_id'])
697
+ ? $row->context['_user_id']
698
+ : null;
699
+
700
+ if ($user_id > 0 && ($user = get_user_by('id', $user_id))) {
701
+ // Sender was user
702
+ $sender_image_html = $this->simpleHistory->get_avatar(
703
+ $user->user_email,
704
+ $sender_image_size
705
+ );
706
+ } elseif ($user_id > 0) {
707
+ // Sender was a user, but user is deleted now
708
+ $sender_image_html = $this->simpleHistory->get_avatar(
709
+ '',
710
+ $sender_image_size
711
+ );
712
+ } else {
713
+ $sender_image_html = $this->simpleHistory->get_avatar(
714
+ '',
715
+ $sender_image_size
716
+ );
717
+ }
718
+
719
+ break;
720
+ }
721
+
722
+ /**
723
+ * Filter generated output for row image (sender image)
724
+ *
725
+ * @since 2.0
726
+ *
727
+ * @param string $sender_image_html
728
+ * @param object $row Log row
729
+ */
730
+ $sender_image_html = apply_filters(
731
+ 'simple_history/row_sender_image_output',
732
+ $sender_image_html,
733
+ $row
734
+ );
735
+
736
+ return $sender_image_html;
737
+ }
738
+
739
+ /**
740
+ * Use this method to output detailed output for a log row
741
+ * Example usage is if a user has uploaded an image then a
742
+ * thumbnail of that image can bo outputed here
743
+ *
744
+ * @param object $row
745
+ * @return string HTML-formatted output
746
+ */
747
+ public function getLogRowDetailsOutput($row)
748
+ {
749
+ $html = '';
750
+
751
+ /**
752
+ * Filter generated output for details
753
+ *
754
+ * @since 2.0
755
+ *
756
+ * @param string $html
757
+ * @param object $row Log row
758
+ */
759
+ $html = apply_filters('simple_history/row_details_output', $html, $row);
760
+
761
+ return $html;
762
+ }
763
+
764
+ /**
765
+ * System is unusable.
766
+ *
767
+ * @param string $message
768
+ * @param array $context
769
+ * @return null
770
+ */
771
+ public function emergency($message, array $context = array())
772
+ {
773
+ return $this->log(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
774
+ }
775
+
776
+ /**
777
+ * System is unusable.
778
+ *
779
+ * @param string $message key from getInfo messages array
780
+ * @param array $context
781
+ * @return null
782
+ */
783
+ public function emergencyMessage($message, array $context = array())
784
+ {
785
+ return $this->logByMessageKey(
786
+ SimpleLoggerLogLevels::EMERGENCY,
787
+ $message,
788
+ $context
789
+ );
790
+ }
791
+
792
+ /**
793
+ * Log with message
794
+ * Called from infoMessage(), errorMessage(), and so on
795
+ *
796
+ * Call like this:
797
+ *
798
+ * return $this->logByMessageKey(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
799
+ */
800
+ private function logByMessageKey(
801
+ $SimpleLoggerLogLevelsLevel,
802
+ $messageKey,
803
+ $context
804
+ ) {
805
+ // When logging by message then the key must exist
806
+ if (!isset($this->messages[$messageKey]['untranslated_text'])) {
807
+ return;
808
+ }
809
+
810
+ /**
811
+ * Filter so plugins etc. can shortut logging
812
+ *
813
+ * @since 2.0.20
814
+ *
815
+ * @param true yes, we default to do the logging
816
+ * @param string logger slug
817
+ * @param string messageKey
818
+ * @param string log level
819
+ * @param array context
820
+ * @return bool false to abort logging
821
+ */
822
+ $doLog = apply_filters(
823
+ 'simple_history/simple_logger/log_message_key',
824
+ true,
825
+ $this->slug,
826
+ $messageKey,
827
+ $SimpleLoggerLogLevelsLevel,
828
+ $context
829
+ );
830
+
831
+ if (!$doLog) {
832
+ return;
833
+ }
834
+
835
+ $context['_message_key'] = $messageKey;
836
+ $message = $this->messages[$messageKey]['untranslated_text'];
837
+
838
+ $this->log($SimpleLoggerLogLevelsLevel, $message, $context);
839
+ }
840
+
841
+ /**
842
+ * Action must be taken immediately.
843
+ *
844
+ * @param string $message
845
+ * @param array $context
846
+ * @return null
847
+ */
848
+ public function alert($message, array $context = array())
849
+ {
850
+ return $this->log(SimpleLoggerLogLevels::ALERT, $message, $context);
851
+ }
852
+
853
+ /**
854
+ * Action must be taken immediately.
855
+ *
856
+ * @param string $message key from getInfo messages array
857
+ * @param array $context
858
+ * @return null
859
+ */
860
+ public function alertMessage($message, array $context = array())
861
+ {
862
+ return $this->logByMessageKey(
863
+ SimpleLoggerLogLevels::ALERT,
864
+ $message,
865
+ $context
866
+ );
867
+ }
868
+
869
+ /**
870
+ * Critical conditions.
871
+ *
872
+ * Example: Application component unavailable, unexpected exception.
873
+ *
874
+ * @param string $message
875
+ * @param array $context
876
+ * @return null
877
+ */
878
+ public function critical($message, array $context = array())
879
+ {
880
+ return $this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
881
+ }
882
+
883
+ /**
884
+ * Critical conditions.
885
+ *
886
+ * @param string $message key from getInfo messages array
887
+ * @param array $context
888
+ * @return null
889
+ */
890
+ public function criticalMessage($message, array $context = array())
891
+ {
892
+ if (!isset($this->messages[$message]['untranslated_text'])) {
893
+ return;
894
+ }
895
+
896
+ $context['_message_key'] = $message;
897
+ $message = $this->messages[$message]['untranslated_text'];
898
+
899
+ $this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
900
+ }
901
+
902
+ /**
903
+ * Runtime errors that do not require immediate action but should typically
904
+ * be logged and monitored.
905
+ *
906
+ * @param string $message
907
+ * @param array $context
908
+ * @return null
909
+ */
910
+ public function error($message, array $context = array())
911
+ {
912
+ return $this->log(SimpleLoggerLogLevels::ERROR, $message, $context);
913
+ }
914
+
915
+ /**
916
+ * Runtime errors that do not require immediate action but should typically
917
+ * be logged and monitored.
918
+ *
919
+ * @param string $message key from getInfo messages array
920
+ * @param array $context
921
+ * @return null
922
+ */
923
+ public function errorMessage($message, array $context = array())
924
+ {
925
+ return $this->logByMessageKey(
926
+ SimpleLoggerLogLevels::ERROR,
927
+ $message,
928
+ $context
929
+ );
930
+ }
931
+
932
+ /**
933
+ * Exceptional occurrences that are not errors.
934
+ *
935
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
936
+ * that are not necessarily wrong.
937
+ *
938
+ * @param string $message
939
+ * @param array $context
940
+ * @return null
941
+ */
942
+ public function warning($message, array $context = array())
943
+ {
944
+ return $this->log(SimpleLoggerLogLevels::WARNING, $message, $context);
945
+ }
946
+
947
+ /**
948
+ * Exceptional occurrences that are not errors.
949
+ *
950
+ * @param string $message key from getInfo messages array
951
+ * @param array $context
952
+ * @return null
953
+ */
954
+ public function warningMessage($message, array $context = array())
955
+ {
956
+ return $this->logByMessageKey(
957
+ SimpleLoggerLogLevels::WARNING,
958
+ $message,
959
+ $context
960
+ );
961
+ }
962
+
963
+ /**
964
+ * Normal but significant events.
965
+ *
966
+ * @param string $message
967
+ * @param array $context
968
+ * @return null
969
+ */
970
+ public function notice($message, array $context = array())
971
+ {
972
+ return $this->log(SimpleLoggerLogLevels::NOTICE, $message, $context);
973
+ }
974
+
975
+ /**
976
+ * Normal but significant events.
977
+ *
978
+ * @param string $message key from getInfo messages array
979
+ * @param array $context
980
+ * @return null
981
+ */
982
+ public function noticeMessage($message, array $context = array())
983
+ {
984
+ return $this->logByMessageKey(
985
+ SimpleLoggerLogLevels::NOTICE,
986
+ $message,
987
+ $context
988
+ );
989
+ }
990
+
991
+ /**
992
+ * Interesting events.
993
+ *
994
+ * Example: User logs in, SQL logs.
995
+ *
996
+ * @param string $message
997
+ * @param array $context
998
+ * @return null
999
+ */
1000
+ public function info($message, array $context = array())
1001
+ {
1002
+ return $this->log(SimpleLoggerLogLevels::INFO, $message, $context);
1003
+ }
1004
+
1005
+ /**
1006
+ * Interesting events.
1007
+ *
1008
+ * Example: User logs in, SQL logs.
1009
+ *
1010
+ * @param string $message key from getInfo messages array
1011
+ * @param array $context
1012
+ * @return null
1013
+ */
1014
+ public function infoMessage($message, array $context = array())
1015
+ {
1016
+ return $this->logByMessageKey(
1017
+ SimpleLoggerLogLevels::INFO,
1018
+ $message,
1019
+ $context
1020
+ );
1021
+ }
1022
+
1023
+ /**
1024
+ * Detailed debug information.
1025
+ *
1026
+ * @param string $message
1027
+ * @param array $context
1028
+ * @return null
1029
+ */
1030
+ public function debug($message, array $context = array())
1031
+ {
1032
+ return $this->log(SimpleLoggerLogLevels::DEBUG, $message, $context);
1033
+ }
1034
+
1035
+ /**
1036
+ * Detailed debug information.
1037
+ *
1038
+ * @param string $message key from getInfo messages array
1039
+ * @param array $context
1040
+ * @return null
1041
+ */
1042
+ public function debugMessage($message, array $context = array())
1043
+ {
1044
+ return $this->logByMessageKey(
1045
+ SimpleLoggerLogLevels::DEBUG,
1046
+ $message,
1047
+ $context
1048
+ );
1049
+ }
1050
+
1051
+ /**
1052
+ * Logs with an arbitrary level.
1053
+ *
1054
+ * @param mixed $level The log level. Default "info".
1055
+ * @param string $message The log message. Default "".
1056
+ * @param array $context The log context. Default empty array.
1057
+ * @return class SimpleLogger instance
1058
+ */
1059
+ public function log($level = 'info', $message = '', $context = array())
1060
+ {
1061
+ global $wpdb;
1062
+
1063
+ // Check that passed args are of correct types.
1064
+ if (!is_string($level) || !is_string($message)) {
1065
+ return $this;
1066
+ }
1067
+
1068
+ // Context must be array, but can be passed as null and so on.
1069
+ if (!is_array($context)) {
1070
+ $context = array();
1071
+ }
1072
+
1073
+ // Don't go on if message is empty.
1074
+ if (empty($message)) {
1075
+ return $this;
1076
+ }
1077
+
1078
+ /**
1079
+ * Filter that makes it possible to shortcut this log.
1080
+ * Return bool false to cancel.
1081
+ *
1082
+ * @since 2.3.1
1083
+ */
1084
+ $do_log = apply_filters(
1085
+ 'simple_history/log/do_log',
1086
+ true,
1087
+ $level,
1088
+ $message,
1089
+ $context,
1090
+ $this
1091
+ );
1092
+ if (false === $do_log) {
1093
+ return $this;
1094
+ }
1095
+
1096
+ /**
1097
+ * Easy shortcut method to disable logging of messages from
1098
+ * a specific logger.
1099
+ *
1100
+ * Example filter name:
1101
+ * simple_history/log/do_log/SimpleUserLogger
1102
+ * simple_history/log/do_log/SimplePostLogger
1103
+ *
1104
+ * Example to disable logging of any user login/logout/failed login activity:
1105
+ * add_filter('simple_history/log/do_log/SimpleUserLogger', '__return_false')
1106
+ *
1107
+ * @since 2.nn
1108
+ */
1109
+ $do_log = apply_filters(
1110
+ "simple_history/log/do_log/{$this->slug}",
1111
+ true
1112
+ );
1113
+ if (false === $do_log) {
1114
+ return $this;
1115
+ }
1116
+
1117
+ /**
1118
+ * Easy shortcut method to disable logging of messages from
1119
+ * a specific logger and message.
1120
+ *
1121
+ * Example filter name:
1122
+ * simple_history/log/do_log/SimpleUserLogger/user_logged_in
1123
+ * simple_history/log/do_log/SimplePostLogger/post_updated
1124
+ *
1125
+ * @since 2.nn
1126
+ */
1127
+ $message_key = isset($context['_message_key'])
1128
+ ? $context['_message_key']
1129
+ : null;
1130
+ $do_log = apply_filters(
1131
+ "simple_history/log/do_log/{$this->slug}/{$message_key}",
1132
+ true
1133
+ );
1134
+ if (false === $do_log) {
1135
+ return $this;
1136
+ }
1137
+
1138
+ // Check if $message is a translated message, and if so then fetch original
1139
+ $sh_latest_translations =
1140
+ $this->simpleHistory->gettextLatestTranslations;
1141
+
1142
+ if (!empty($sh_latest_translations)) {
1143
+ if (isset($sh_latest_translations[$message])) {
1144
+ // Translation of this phrase was found, so use original phrase instead of translated one
1145
+ // Store textdomain since it's required to translate
1146
+ $context['_gettext_domain'] =
1147
+ $sh_latest_translations[$message]['domain'];
1148
+
1149
+ // These are good to keep when debugging
1150
+ // $context["_gettext_org_message"] = $sh_latest_translations[$message]["text"];
1151
+ // $context["_gettext_translated_message"] = $sh_latest_translations[$message]["translation"];
1152
+ $message = $sh_latest_translations[$message]['text'];
1153
+ }
1154
+ }
1155
+
1156
+ /**
1157
+ * Filter arguments passed to log funtion
1158
+ *
1159
+ * @since 2.0
1160
+ *
1161
+ * @param string $level
1162
+ * @param string $message
1163
+ * @param array $context
1164
+ * @param object SimpleLogger object
1165
+ */
1166
+ apply_filters(
1167
+ 'simple_history/log_arguments',
1168
+ $level,
1169
+ $message,
1170
+ $context,
1171
+ $this
1172
+ );
1173
+ $context = apply_filters(
1174
+ 'simple_history/log_argument/context',
1175
+ $context,
1176
+ $level,
1177
+ $message,
1178
+ $this
1179
+ );
1180
+ $level = apply_filters(
1181
+ 'simple_history/log_argument/level',
1182
+ $level,
1183
+ $context,
1184
+ $message,
1185
+ $this
1186
+ );
1187
+ $message = apply_filters(
1188
+ 'simple_history/log_argument/message',
1189
+ $message,
1190
+ $level,
1191
+ $context,
1192
+ $this
1193
+ );
1194
+
1195
+ /*
1196
+ Store date as GMT date, i.e. not local date/time
1197
+ * Some info here:
1198
+ * http://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
1199
+ */
1200
+ $localtime = current_time('mysql', 1);
1201
+
1202
+ $db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
1203
+
1204
+ /**
1205
+ * Filter db table used for simple history events
1206
+ *
1207
+ * @since 2.0
1208
+ *
1209
+ * @param string $db_table
1210
+ */
1211
+ $db_table = apply_filters('simple_history/db_table', $db_table);
1212
+
1213
+ $data = array(
1214
+ 'logger' => $this->slug,
1215
+ 'level' => $level,
1216
+ 'date' => $localtime,
1217
+ 'message' => $message
1218
+ );
1219
+
1220
+ // Allow date to be overriden.
1221
+ // Date must be in format 'Y-m-d H:i:s'.
1222
+ if (isset($context['_date'])) {
1223
+ $data['date'] = $context['_date'];
1224
+ unset($context['_date']);
1225
+ }
1226
+
1227
+ // Add occasions id.
1228
+ $occasions_id = null;
1229
+ if (isset($context['_occasionsID'])) {
1230
+ // Minimize risk of similar loggers logging same messages and such and resulting in same occasions id
1231
+ // by always adding logger slug.
1232
+ $occasions_data = array(
1233
+ '_occasionsID' => $context['_occasionsID'],
1234
+ '_loggerSlug' => $this->slug
1235
+ );
1236
+ $occasions_id = md5(json_encode($occasions_data));
1237
+ unset($context['_occasionsID']);
1238
+ } else {
1239
+ // No occasions id specified, create one bases on the data array.
1240
+ $occasions_data = $data + $context;
1241
+
1242
+ // Don't include date in context data.
1243
+ unset($occasions_data['date']);
1244
+
1245
+ $occasions_id = md5(json_encode($occasions_data));
1246
+ }
1247
+
1248
+ $data['occasionsID'] = $occasions_id;
1249
+
1250
+ // Log initiator, defaults to current user if exists, or other if not user exist
1251
+ if (isset($context['_initiator'])) {
1252
+ // Manually set in context
1253
+ $data['initiator'] = $context['_initiator'];
1254
+ unset($context['_initiator']);
1255
+ } else {
1256
+ // No initiator set, try to determine
1257
+ // Default to other
1258
+ $data['initiator'] = SimpleLoggerLogInitiators::OTHER;
1259
+
1260
+ // Check if user is responsible.
1261
+ if (function_exists('wp_get_current_user')) {
1262
+ $current_user = wp_get_current_user();
1263
+
1264
+ if (isset($current_user->ID) && $current_user->ID) {
1265
+ $data['initiator'] = SimpleLoggerLogInitiators::WP_USER;
1266
+ $context['_user_id'] = $current_user->ID;
1267
+ $context['_user_login'] = $current_user->user_login;
1268
+ $context['_user_email'] = $current_user->user_email;
1269
+ }
1270
+ }
1271
+
1272
+ // If cron then set WordPress as responsible
1273
+ if (defined('DOING_CRON') && DOING_CRON) {
1274
+ // Seems to be wp cron running and doing this.
1275
+ $data['initiator'] = SimpleLoggerLogInitiators::WORDPRESS;
1276
+ $context['_wp_cron_running'] = true;
1277
+
1278
+ // To aid debugging we log the current filter and a list of all filters.
1279
+ global $wp_current_filter;
1280
+ $context['_wp_cron_current_filter'] = current_filter();
1281
+ }
1282
+
1283
+ // If running as CLI and WP_CLI_PHP_USED is set then it is WP CLI that is doing it
1284
+ // How to log this? Is this a user, is it WordPress, or what?
1285
+ // I'm thinking:
1286
+ // - it is a user that is manually doing this, on purpose, with intent, so not auto wordpress
1287
+ // - it is a specific user, but we don't know who
1288
+ // - sounds like a special case, set initiator to wp_cli
1289
+ // Can be used by plugins/themes to check if WP-CLI is running or not
1290
+ if (defined('WP_CLI') && WP_CLI) {
1291
+ $data['initiator'] = SimpleLoggerLogInitiators::WP_CLI;
1292
+ }
1293
+ } // End if().
1294
+
1295
+ // Detect XML-RPC calls and append to context, if not already there.
1296
+ if (
1297
+ defined('XMLRPC_REQUEST') &&
1298
+ XMLRPC_REQUEST &&
1299
+ !isset($context['_xmlrpc_request'])
1300
+ ) {
1301
+ $context['_xmlrpc_request'] = true;
1302
+ }
1303
+
1304
+ // Detect REST calls and append to context, if not already there.
1305
+ $isRestApiRequest =
1306
+ (defined('REST_API_REQUEST') && REST_API_REQUEST) ||
1307
+ (defined('REST_REQUEST') && REST_REQUEST);
1308
+ if ($isRestApiRequest) {
1309
+ $context['_rest_api_request'] = true;
1310
+ }
1311
+
1312
+ // Trim message.
1313
+ $data['message'] = trim($data['message']);
1314
+
1315
+ /**
1316
+ * Filter data to be saved to db.
1317
+ *
1318
+ * @since 2.0
1319
+ *
1320
+ * @param array $data
1321
+ */
1322
+ $data = apply_filters('simple_history/log_insert_data', $data);
1323
+
1324
+ // Insert data into db.
1325
+ $result = $wpdb->insert($db_table, $data);
1326
+
1327
+ $data_parent_row = null;
1328
+
1329
+ // Only save context if able to store row.
1330
+ if (false === $result) {
1331
+ $history_inserted_id = null;
1332
+ } else {
1333
+ $history_inserted_id = $wpdb->insert_id;
1334
+
1335
+ $db_table_contexts =
1336
+ $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
1337
+
1338
+ /**
1339
+ * Filter table name for contexts.
1340
+ *
1341
+ * @since 2.0
1342
+ *
1343
+ * @param string $db_table_contexts
1344
+ */
1345
+ $db_table_contexts = apply_filters(
1346
+ 'simple_history/logger_db_table_contexts',
1347
+ $db_table_contexts
1348
+ );
1349
+
1350
+ if (!is_array($context)) {
1351
+ $context = array();
1352
+ }
1353
+
1354
+ // Append user id to context, if not already added.
1355
+ if (!isset($context['_user_id'])) {
1356
+ // wp_get_current_user is not available early.
1357
+ // http://codex.wordpress.org/Function_Reference/wp_get_current_user
1358
+ // https://core.trac.wordpress.org/ticket/14024
1359
+ if (function_exists('wp_get_current_user')) {
1360
+ $current_user = wp_get_current_user();
1361
+
1362
+ if (isset($current_user->ID) && $current_user->ID) {
1363
+ $context['_user_id'] = $current_user->ID;
1364
+ $context['_user_login'] = $current_user->user_login;
1365
+ $context['_user_email'] = $current_user->user_email;
1366
+ }
1367
+ }
1368
+ }
1369
+
1370
+ // Add remote addr to context.
1371
+ if (!isset($context['_server_remote_addr'])) {
1372
+ $remote_addr = empty($_SERVER['REMOTE_ADDR'])
1373
+ ? ''
1374
+ : wp_unslash($_SERVER['REMOTE_ADDR']);
1375
+
1376
+ /**
1377
+ * Filter to control if ip addresses should be anonymized or not.
1378
+ *
1379
+ * @since 2.22
1380
+ *
1381
+ * @param bool true to anonymize ip address, false to keep original ip address.
1382
+ * @return bool
1383
+ */
1384
+ $anonymize_ip_address = apply_filters(
1385
+ 'simple_history/privacy/anonymize_ip_address',
1386
+ true
1387
+ );
1388
+
1389
+ if (
1390
+ $anonymize_ip_address &&
1391
+ function_exists('wp_privacy_anonymize_ip')
1392
+ ) {
1393
+ $remote_addr = wp_privacy_anonymize_ip($remote_addr);
1394
+ }
1395
+
1396
+ $context['_server_remote_addr'] = $remote_addr;
1397
+
1398
+ // If web server is behind a load balancer then the ip address will always be the same
1399
+ // See bug report: https://wordpress.org/support/topic/use-x-forwarded-for-http-header-when-logging-remote_addr?replies=1#post-6422981
1400
+ // Note that the x-forwarded-for header can contain multiple ips, comma separated
1401
+ // Also note that the header can be faked
1402
+ // Ref: http://stackoverflow.com/questions/753645/how-do-i-get-the-correct-ip-from-http-x-forwarded-for-if-it-contains-multiple-ip
1403
+ // Ref: http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
1404
+ // Check for IP in lots of headers
1405
+ // Based on code found here:
1406
+ // http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
1407
+ $ip_keys = $this->get_ip_number_header_keys();
1408
+
1409
+ foreach ($ip_keys as $key) {
1410
+ if (array_key_exists($key, $_SERVER) === true) {
1411
+ // Loop through all IPs.
1412
+ $ip_loop_num = 0;
1413
+ foreach (explode(',', $_SERVER[$key]) as $ip) {
1414
+ // trim for safety measures.
1415
+ $ip = trim($ip);
1416
+
1417
+ // attempt to validate IP.
1418
+ if ($this->validate_ip($ip)) {
1419
+ // valid, add to context, with loop index appended so we can store many IPs.
1420
+ $key_lower = strtolower($key);
1421
+
1422
+ if (
1423
+ $anonymize_ip_address &&
1424
+ function_exists('wp_privacy_anonymize_ip')
1425
+ ) {
1426
+ $ip = wp_privacy_anonymize_ip($ip);
1427
+ }
1428
+
1429
+ $context[
1430
+ "_server_{$key_lower}_{$ip_loop_num}"
1431
+ ] = $ip;
1432
+ }
1433
+
1434
+ $ip_loop_num++;
1435
+ }
1436
+ }
1437
+ }
1438
+ } // End if().
1439
+
1440
+ // Append http referer.
1441
+ if (
1442
+ !isset($context['_server_http_referer']) &&
1443
+ isset($_SERVER['HTTP_REFERER'])
1444
+ ) {
1445
+ $context['_server_http_referer'] = $_SERVER['HTTP_REFERER'];
1446
+ }
1447
+
1448
+ /**
1449
+ * Filter the context to store for this event/row
1450
+ *
1451
+ * @since 2.0.29
1452
+ *
1453
+ * @param array $context Array with all context data to store. Modify and return this.
1454
+ * @param array $data Array with data used for parent row.
1455
+ * @param array $this Reference to this logger instance.
1456
+ */
1457
+ $context = apply_filters(
1458
+ 'simple_history/log_insert_context',
1459
+ $context,
1460
+ $data,
1461
+ $this
1462
+ );
1463
+ $data_parent_row = $data;
1464
+
1465
+ // Insert all context values into db.
1466
+ $this->append_context($history_inserted_id, $context);
1467
+ } // End if().
1468
+
1469
+ $this->lastInsertID = $history_inserted_id;
1470
+ $this->lastInsertContext = $context;
1471
+
1472
+ $this->simpleHistory->get_cache_incrementor(true);
1473
+
1474
+ /**
1475
+ * Action that is called after an event has been logged
1476
+ *
1477
+ * @since 2.5.1
1478
+ *
1479
+ * @param array $context Array with all context data that was used to log event.
1480
+ * @param array $data Array with data used for parent row.
1481
+ * @param array $this Reference to this logger instance
1482
+ */
1483
+ do_action(
1484
+ 'simple_history/log/inserted',
1485
+ $context,
1486
+ $data_parent_row,
1487
+ $this
1488
+ );
1489
+
1490
+ // Return $this so we can chain methods.
1491
+ return $this;
1492
+ } // log
1493
+
1494
+ /**
1495
+ * Append new info to the context of history item with id $post_logger->lastInsertID.
1496
+ *
1497
+ * @param int $history_id The id of the history row to add context to.
1498
+ * @param array $context Context to append to existing context for the row.
1499
+ * @return bool True if context was added, false if not (beacuse row_id or context is empty).
1500
+ */
1501
+ public function append_context($history_id, $context)
1502
+ {
1503
+ if (empty($history_id) || empty($context)) {
1504
+ return false;
1505
+ }
1506
+
1507
+ global $wpdb;
1508
+
1509
+ $db_table_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
1510
+
1511
+ foreach ($context as $key => $value) {
1512
+ // Everything except strings should be json_encoded, ie. arrays and objects.
1513
+ if (!is_string($value)) {
1514
+ $value = simpleHistory::json_encode($value);
1515
+ }
1516
+
1517
+ $data = array(
1518
+ 'history_id' => $history_id,
1519
+ 'key' => $key,
1520
+ 'value' => $value
1521
+ );
1522
+
1523
+ $result = $wpdb->insert($db_table_contexts, $data);
1524
+ }
1525
+
1526
+ return true;
1527
+ }
1528
+
1529
+ /**
1530
+ * Returns array with headers that may contain user IP
1531
+ *
1532
+ * @since 2.0.29
1533
+ */
1534
+ public function get_ip_number_header_keys()
1535
+ {
1536
+ $arr = array(
1537
+ 'HTTP_CLIENT_IP',
1538
+ 'HTTP_X_FORWARDED_FOR',
1539
+ 'HTTP_X_FORWARDED',
1540
+ 'HTTP_X_CLUSTER_CLIENT_IP',
1541
+ 'HTTP_FORWARDED_FOR',
1542
+ 'HTTP_FORWARDED'
1543
+ );
1544
+
1545
+ return $arr;
1546
+ }
1547
+
1548
+ /**
1549
+ * Returns additional headers with ip number from context
1550
+ *
1551
+ * @since 2.0.29
1552
+ * @param array $row Row with info.
1553
+ * @return array Headers
1554
+ */
1555
  public function get_event_ip_number_headers($row)
1556
+ {
1557
+ $ip_keys = $this->get_ip_number_header_keys();
1558
+ $arr_found_additional_ip_headers = array();
1559
+ $context = $row->context;
1560
+
1561
+ foreach ($ip_keys as $one_ip_header_key) {
1562
+ $one_ip_header_key_lower = strtolower($one_ip_header_key);
1563
+
1564
+ foreach ($context as $context_key => $context_val) {
1565
+ // $key_check_for = "_server_" . strtolower($one_ip_header_key) . "_0";
1566
+ $match = preg_match(
1567
+ "/^_server_{$one_ip_header_key_lower}_[\d+]/",
1568
+ $context_key,
1569
+ $matches
1570
+ );
1571
+ if ($match) {
1572
+ $arr_found_additional_ip_headers[
1573
+ $context_key
1574
+ ] = $context_val;
1575
+ }
1576
+ }
1577
+ } // End foreach().
1578
+
1579
+ return $arr_found_additional_ip_headers;
1580
+ }
1581
+
1582
+ /**
1583
+ * Ensures an ip address is both a valid IP and does not fall within
1584
+ * a private network range.
1585
+ *
1586
+ * @param string $ip IP number.
1587
+ * @return bool
1588
+ */
1589
  public function validate_ip($ip)
1590
+ {
1591
+ if (
1592
+ filter_var(
1593
+ $ip,
1594
+ FILTER_VALIDATE_IP,
1595
+ FILTER_FLAG_IPV4 |
1596
+ FILTER_FLAG_NO_PRIV_RANGE |
1597
+ FILTER_FLAG_NO_RES_RANGE
1598
+ ) === false
1599
+ ) {
1600
+ return false;
1601
+ }
1602
+
1603
+ return true;
1604
+ }
1605
+
1606
+ /**
1607
+ * Override this to add CSS in <head> for your logger.
1608
+ * The CSS that you output will only be outputed
1609
+ * on pages where Simple History is used.
1610
+ */
1611
  public function adminCSS()
1612
+ {
1613
+ /*
1614
+ ?>
1615
+ <style>
1616
+ body {
1617
+ border: 2px solid red;
1618
+ }
1619
+ </style>
1620
+ <?php
1621
+ */
1622
+ }
1623
+
1624
+ /**
1625
+ * Override this to add JavaScript in the footer for your logger.
1626
+ * The JS that you output will only be outputed
1627
+ * on pages where Simple History is used.
1628
+ */
1629
  public function adminJS()
1630
+ {
1631
+ /*
1632
+ ?>
1633
+ <script>
1634
+ console.log("This is outputed in the footer");
1635
+ </script>
1636
+ <?php
1637
+ */
1638
+ }
1639
  }
1640
 
1641
  /**
1643
  */
1644
  class SimpleLoggerLogInitiators
1645
  {
1646
+ // A wordpress user that at the log event created did exist in the wp database
1647
+ // May have been deleted when the log is viewed.
1648
+ const WP_USER = 'wp_user';
1649
 
1650
+ // Cron job run = wordpress initiated
1651
+ // Email sent to customer on webshop = system/wordpress/anonymous web user
1652
+ // Javascript error occured on website = anonymous web user.
1653
+ const WEB_USER = 'web_user';
1654
 
1655
+ // WordPress core or plugins updated automatically via wp-cron.
1656
+ const WORDPRESS = 'wp';
1657
 
1658
+ // WP CLI / terminal.
1659
+ const WP_CLI = 'wp_cli';
1660
 
1661
+ // I dunno.
1662
+ const OTHER = 'other';
1663
  }
1664
 
1665
  /**
1671
  */
1672
  class SimpleLoggerLogTypes
1673
  {
1674
+ const CREATE = 'create';
1675
+ const READ = 'read';
1676
+ const UPDATE = 'update';
1677
+ const DELETE = 'delete';
1678
+ const OTHER = 'other';
1679
  }
1680
 
1681
  /**
1683
  */
1684
  class SimpleLoggerLogLevels
1685
  {
1686
+ const EMERGENCY = 'emergency';
1687
+ const ALERT = 'alert';
1688
+ const CRITICAL = 'critical';
1689
+ const ERROR = 'error';
1690
+ const WARNING = 'warning';
1691
+ const NOTICE = 'notice';
1692
+ const INFO = 'info';
1693
+ const DEBUG = 'debug';
1694
  }
loggers/SimplePluginLogger.php CHANGED
@@ -464,11 +464,12 @@ class SimplePluginLogger extends SimpleLogger
464
  }
465
 
466
  // We found the transient we were looking for
467
- if (isset($_POST['action'])
 
468
  && 'delete-selected' == $_POST['action']
469
  && isset($_POST['checked'])
470
  && is_array($_POST['checked'])
471
- ) {
472
  /*
473
  [checked] => Array
474
  (
464
  }
465
 
466
  // We found the transient we were looking for
467
+ if (
468
+ isset($_POST['action'])
469
  && 'delete-selected' == $_POST['action']
470
  && isset($_POST['checked'])
471
  && is_array($_POST['checked'])
472
+ ) {
473
  /*
474
  [checked] => Array
475
  (
loggers/SimplePostLogger.php CHANGED
@@ -872,7 +872,20 @@ class SimplePostLogger extends SimpleLogger
872
  // Sticky is stored in option:
873
  // $sticky_posts = get_option('sticky_posts');
874
 
875
- return $context;
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  }
877
 
878
  /**
@@ -1440,29 +1453,4 @@ class SimplePostLogger extends SimpleLogger
1440
 
1441
  return $out;
1442
  }
1443
-
1444
- /**
1445
- * Output CSS for diff output
1446
- */
1447
- public function adminCSS()
1448
- {
1449
- ?>
1450
- <style>
1451
- .SimpleHistory__diff.SimpleHistory__diff {
1452
- border-spacing: 1px;
1453
- }
1454
-
1455
- .SimpleHistory__diff.SimpleHistory__diff td,
1456
- .SimpleHistory__diff.SimpleHistory__diff td:first-child {
1457
- text-align: left;
1458
- white-space: normal;
1459
- font-size: 13px;
1460
- line-height: 1.3;
1461
- padding: 0.25em 0.5em;
1462
- color: rgb(75, 75, 75);
1463
- font-family: "Open Sans", sans-serif;
1464
- }
1465
- </style>
1466
- <?php
1467
- }
1468
  }
872
  // Sticky is stored in option:
873
  // $sticky_posts = get_option('sticky_posts');
874
 
875
+ /**
876
+ * Filter to control context sent to the diff output.
877
+ *
878
+ * @param array $context Array with context.
879
+ * @param array $old_data Old/prev post data.
880
+ * @param array $new_data New post data.
881
+ * @param array $old_meta Old/prev post meta data.
882
+ * @param array $new_meta New post meta data.
883
+ *
884
+ * @return array $context Array with diff data added.
885
+ *
886
+ * @since 2.36.0
887
+ */
888
+ return apply_filters('simple_history/post_logger/context', $context, $old_data, $new_data, $old_meta, $new_meta);
889
  }
890
 
891
  /**
1453
 
1454
  return $out;
1455
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1456
  }
loggers/class-sh-jetpack-logger.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Logger for Automattic Jetpack plugin.
4
  *
1
  <?php
2
+
3
  /**
4
  * Logger for Automattic Jetpack plugin.
5
  *
loggers/class-sh-privacy-logger.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Logger for privacy/GDPR related things.
4
  *
1
  <?php
2
+
3
  /**
4
  * Logger for privacy/GDPR related things.
5
  *
loggers/class-sh-translations-logger.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Logger for translation related things, like translation updated.
4
  *
1
  <?php
2
+
3
  /**
4
  * Logger for translation related things, like translation updated.
5
  *
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: history, log, changes, changelog, audit, audit log, event log, user tracki
5
  Requires at least: 5.2
6
  Tested up to: 5.5
7
  Requires PHP: 5.6
8
- Stable tag: 2.35.1
9
 
10
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
11
 
@@ -189,6 +189,13 @@ Events in the log are stored for 60 days by default. Events older than this will
189
 
190
  == Changelog ==
191
 
 
 
 
 
 
 
 
192
  = 2.35.1 (August 2020) =
193
 
194
  Minor update to correct readme.
5
  Requires at least: 5.2
6
  Tested up to: 5.5
7
  Requires PHP: 5.6
8
+ Stable tag: 2.36.0
9
 
10
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
11
 
189
 
190
  == Changelog ==
191
 
192
+ = 2.36 (August 2020) =
193
+
194
+ - Fix plus and minus icons in quick diff.
195
+ - Add filter for Post Logger context. (https://github.com/bonny/WordPress-Simple-History/pull/216)
196
+ - Add link to my [GitHub sponsors page](https://github.com/sponsors/bonny/) in the sidebar.
197
+ - Misc code cleanups and smaller fixes.
198
+
199
  = 2.35.1 (August 2020) =
200
 
201
  Minor update to correct readme.
templates/settings-style-example.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Style example.
4
  *
1
  <?php
2
+
3
  /**
4
  * Style example.
5
  *
templates/template-settings-tab-debug.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Undocumented class
4
  *
1
  <?php
2
+
3
  /**
4
  * Undocumented class
5
  *
uninstall.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * File that is run during plugin uninstall (not just de-activate)
4
  *
1
  <?php
2
+
3
  /**
4
  * File that is run during plugin uninstall (not just de-activate)
5
  *