Custom Twitter Feeds - Version 1.6.1

Version Description

  • Tweak: Added support for improved dashboard notices on the plugin settings page.
  • Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
  • Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
Download this release

Release Info

Developer smashballoon
Plugin Icon 128x128 Custom Twitter Feeds
Version 1.6.1
Comparing to
See all releases

Code changes from version 1.6 to 1.6.1

README.txt CHANGED
@@ -4,8 +4,8 @@ Contributors: smashballoon, craig-at-smash-balloon
4
  Support Website: http://smashballoon/custom-twitter-feeds/
5
  Tags: Twitter, Twitter feed, Twitter Tweets, Twitter widget, Custom Twitter Feed
6
  Requires at least: 3.4
7
- Tested up to: 5.5
8
- Stable tag: 1.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -233,6 +233,11 @@ If you're still having trouble displaying your Tweets after trying the common is
233
  7. To display a Twitter feed just copy and paste the shortcode into a Twitter widget or page
234
 
235
  == Changelog ==
 
 
 
 
 
236
  = 1.6 =
237
  * New: Updated icons in the feed to match Twitter.
238
  * Tweak: Several CSS changes to make the Twitter feed text responsive with more themes.
4
  Support Website: http://smashballoon/custom-twitter-feeds/
5
  Tags: Twitter, Twitter feed, Twitter Tweets, Twitter widget, Custom Twitter Feed
6
  Requires at least: 3.4
7
+ Tested up to: 5.6
8
+ Stable tag: 1.6.1
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
233
  7. To display a Twitter feed just copy and paste the shortcode into a Twitter widget or page
234
 
235
  == Changelog ==
236
+ = 1.6.1 =
237
+ * Tweak: Added support for improved dashboard notices on the plugin settings page.
238
+ * Tweak: HTML attribute rel="noopener noreferrer" added to all outbound links for extra security.
239
+ * Fix: Fixed PHP warning "non-numeric value encountered" when setting the tweet multiplier option to something that wasn't a number.
240
+
241
  = 1.6 =
242
  * New: Updated icons in the feed to match Twitter.
243
  * Tweak: Several CSS changes to make the Twitter feed text responsive with more themes.
css/admin-notifications.css ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ctf-notifications {
2
+ position: relative;
3
+ background: #FFFFFF 0 0 no-repeat padding-box;
4
+ box-shadow: 0px 5px 15px #0000000D;
5
+ border-radius: 6px;
6
+ opacity: 1;
7
+ min-height: 48px;
8
+ padding: 15px 102px 15px 72px;
9
+ margin: 0 0 14px 0;
10
+ }
11
+
12
+ #ctf-notifications * {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ #ctf-notifications .bell,
17
+ #ctf-notifications .thumb{
18
+ position: absolute;
19
+ top: 15px;
20
+ left: 15px;
21
+ width: 42px;
22
+ height: 48px;
23
+ }
24
+ #ctf-notifications .thumb img {
25
+ max-width: 100%;
26
+ }
27
+ #ctf-notifications .thumb .img-overlay {
28
+ top: 42px;
29
+ left: -6px;
30
+ width: 54px;
31
+ position: absolute;
32
+ background: #ca4a1f;
33
+ color: #fff;
34
+ padding: 2px 4px;
35
+ border-radius: 3px;
36
+ line-height: 1;
37
+ font-size: 11px;
38
+ font-weight: bold;
39
+ text-align: center;
40
+ }
41
+
42
+ #ctf-notifications .messages .message {
43
+ display: none;
44
+ }
45
+
46
+ #ctf-notifications .messages .message.current {
47
+ display: block;
48
+ }
49
+
50
+ #ctf-notifications .messages .message .title {
51
+ font-weight: bold;
52
+ font-size: 17px;
53
+ line-height: 20px;
54
+ margin: 0;
55
+ color: #444;
56
+ }
57
+
58
+ #ctf-notifications .messages .message .content {
59
+ font-weight: normal;
60
+ font-size: 13px;
61
+ line-height: 20px;
62
+ margin: 6px 0 40px 0;
63
+ }
64
+
65
+ #ctf-notifications .messages .message .buttons {
66
+ margin: -30px 80px 0 0;
67
+ }
68
+
69
+ #ctf-notifications .messages .message .buttons a {
70
+ margin: 0 6px 0 0;
71
+ padding: 8px 10px;
72
+ line-height: 13px;
73
+ font-size: 13px;
74
+ min-height: unset;
75
+ }
76
+
77
+ #ctf-notifications .messages .message .buttons .button-secondary {
78
+ border: 1px solid #0071A1;
79
+ }
80
+
81
+ #ctf-notifications .dismiss {
82
+ position: absolute;
83
+ top: 15px;
84
+ right: 15px;
85
+ width: 16px;
86
+ height: 16px;
87
+ color: #72777C;
88
+ font-size: 16px;
89
+ cursor: pointer;
90
+ text-align: center;
91
+ vertical-align: middle;
92
+ line-height: 16px;
93
+ }
94
+
95
+ #ctf-notifications .dismiss:hover {
96
+ color: #dc3232;
97
+ }
98
+
99
+ #ctf-notifications .navigation {
100
+ position: absolute;
101
+ bottom: 15px;
102
+ right: 15px;
103
+ width: 63px;
104
+ height: 30px;
105
+ }
106
+
107
+ #ctf-notifications .navigation a {
108
+ display: block;
109
+ width: 30px;
110
+ height: 30px;
111
+ border: 1px solid #7E8993;
112
+ border-radius: 3px;
113
+ font-size: 8px;
114
+ text-align: center;
115
+ vertical-align: middle;
116
+ line-height: 30px;
117
+ cursor: pointer;
118
+ background-color: #ffffff;
119
+ color: #41454A;
120
+ }
121
+ #ctf-notifications .navigation svg {
122
+ width: 8px;
123
+ height: 8px;
124
+ }
125
+
126
+ #ctf-notifications .navigation a:hover {
127
+ background-color: #f1f1f1;
128
+ }
129
+
130
+ #ctf-notifications .navigation .prev {
131
+ float: left;
132
+ }
133
+
134
+ #ctf-notifications .navigation .next {
135
+ float: right;
136
+ }
137
+
138
+ #ctf-notifications .navigation .disabled {
139
+ border-color: #dddddd;
140
+ color: #A0A5AA;
141
+ cursor: default;
142
+ }
143
+
144
+ #ctf-notifications .navigation .disabled:hover {
145
+ background-color: #ffffff;
146
+ }
147
+
148
+ @media screen and (max-width: 768px) {
149
+ #ctf-notifications {
150
+ padding: 15px 15px 15px 72px;
151
+ }
152
+ #ctf-notifications .messages .message .title {
153
+ margin: 0 30px 0 0;
154
+ }
155
+ #ctf-notifications .messages .message .content {
156
+ font-size: 16px;
157
+ line-height: 24px;
158
+ }
159
+ #ctf-notifications .messages .message .buttons {
160
+ margin: -30px 80px 0 0;
161
+ }
162
+ #ctf-notifications .messages .message .buttons a {
163
+ margin: 0;
164
+ display: table;
165
+ }
166
+ #ctf-notifications .messages .message .buttons .button-secondary {
167
+ margin-top: 6px;
168
+ }
169
+ }
170
+
171
+ /*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXRzL2Nzcy9hZG1pbi1ub3RpZmljYXRpb25zLmNzcyIsInNvdXJjZXMiOlsiYXNzZXRzL3Njc3MvYWRtaW4tbm90aWZpY2F0aW9ucy5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIEFkbWluIG5vdGlmaWNhdGlvbiBzdHlsZXMuXG5cbiN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXG5cdHBvc2l0aW9uOiByZWxhdGl2ZTtcblx0YmFja2dyb3VuZDogI0ZGRkZGRiAwIDAgbm8tcmVwZWF0IHBhZGRpbmctYm94O1xuXHRib3gtc2hhZG93OiAwcHggNXB4IDE1cHggIzAwMDAwMDBEO1xuXHRib3JkZXItcmFkaXVzOiA2cHg7XG5cdG9wYWNpdHk6IDE7XG5cdG1pbi1oZWlnaHQ6IDQ4cHg7XG5cdHBhZGRpbmc6IDE1cHggMTAycHggMTVweCA3MnB4O1xuXHRtYXJnaW46IDAgMCAxNHB4IDA7XG5cblx0KiB7XG5cdFx0Ym94LXNpemluZzogYm9yZGVyLWJveDtcblx0fVxuXG5cdC5iZWxsIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdGxlZnQ6IDE1cHg7XG5cdFx0d2lkdGg6IDQycHg7XG5cdFx0aGVpZ2h0OiA0OHB4O1xuXHR9XG5cblx0Lm1lc3NhZ2VzIHtcblx0XHQubWVzc2FnZSB7XG5cdFx0XHRkaXNwbGF5OiBub25lO1xuXG5cdFx0XHQmLmN1cnJlbnQge1xuXHRcdFx0XHRkaXNwbGF5OiBibG9jaztcblx0XHRcdH1cblxuXHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IGJvbGQ7XG5cdFx0XHRcdGZvbnQtc2l6ZTogMTdweDtcblx0XHRcdFx0bGluZS1oZWlnaHQ6IDIwcHg7XG5cdFx0XHRcdG1hcmdpbjogMDtcblx0XHRcdFx0Y29sb3I6ICM0NDQ7XG5cdFx0XHR9XG5cblx0XHRcdC5jb250ZW50IHtcblx0XHRcdFx0Zm9udC13ZWlnaHQ6IG5vcm1hbDtcblx0XHRcdFx0Zm9udC1zaXplOiAxM3B4O1xuXHRcdFx0XHRsaW5lLWhlaWdodDogMjBweDtcblx0XHRcdFx0bWFyZ2luOiA2cHggMCA0MHB4IDA7XG5cdFx0XHR9XG5cblx0XHRcdC5idXR0b25zIHtcblx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRhIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgNnB4IDAgMDtcblx0XHRcdFx0XHRwYWRkaW5nOiA4cHggMTBweDtcblx0XHRcdFx0XHRsaW5lLWhlaWdodDogMTNweDtcblx0XHRcdFx0XHRmb250LXNpemU6IDEzcHg7XG5cdFx0XHRcdFx0bWluLWhlaWdodDogdW5zZXQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9uLXNlY29uZGFyeSB7XG5cdFx0XHRcdFx0Ym9yZGVyOiAxcHggc29saWQgIzAwNzFBMTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC5kaXNtaXNzIHtcblx0XHRwb3NpdGlvbjogYWJzb2x1dGU7XG5cdFx0dG9wOiAxNXB4O1xuXHRcdHJpZ2h0OiAxNXB4O1xuXHRcdHdpZHRoOiAxNnB4O1xuXHRcdGhlaWdodDogMTZweDtcblx0XHRjb2xvcjogIzcyNzc3Qztcblx0XHRmb250LXNpemU6IDE2cHg7XG5cdFx0Y3Vyc29yOiBwb2ludGVyO1xuXHRcdHRleHQtYWxpZ246IGNlbnRlcjtcblx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdGxpbmUtaGVpZ2h0OiAxNnB4O1xuXG5cdFx0Jjpob3ZlciB7XG5cdFx0XHRjb2xvcjogI2RjMzIzMjtcblx0XHR9XG5cdH1cblxuXHQubmF2aWdhdGlvbiB7XG5cdFx0cG9zaXRpb246IGFic29sdXRlO1xuXHRcdGJvdHRvbTogMTVweDtcblx0XHRyaWdodDogMTVweDtcblx0XHR3aWR0aDogNjNweDtcblx0XHRoZWlnaHQ6IDMwcHg7XG5cblx0XHRhIHtcblx0XHRcdGRpc3BsYXk6IGJsb2NrO1xuXHRcdFx0d2lkdGg6IDMwcHg7XG5cdFx0XHRoZWlnaHQ6IDMwcHg7XG5cdFx0XHRib3JkZXI6IDFweCBzb2xpZCAjN0U4OTkzO1xuXHRcdFx0Ym9yZGVyLXJhZGl1czogM3B4O1xuXHRcdFx0Zm9udC1zaXplOiA4cHg7XG5cdFx0XHR0ZXh0LWFsaWduOiBjZW50ZXI7XG5cdFx0XHR2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuXHRcdFx0bGluZS1oZWlnaHQ6IDMwcHg7XG5cdFx0XHRjdXJzb3I6IHBvaW50ZXI7XG5cdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0Y29sb3I6ICM0MTQ1NEE7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZjFmMWYxO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC5wcmV2IHtcblx0XHRcdGZsb2F0OiBsZWZ0O1xuXHRcdH1cblxuXHRcdC5uZXh0IHtcblx0XHRcdGZsb2F0OiByaWdodDtcblx0XHR9XG5cblx0XHQuZGlzYWJsZWQge1xuXHRcdFx0Ym9yZGVyLWNvbG9yOiAjZGRkZGRkO1xuXHRcdFx0Y29sb3I6ICNBMEE1QUE7XG5cdFx0XHRjdXJzb3I6IGRlZmF1bHQ7XG5cblx0XHRcdCY6aG92ZXIge1xuXHRcdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufVxuXG5AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOiA3NjhweCkge1xuXG5cdCN3cGZvcm1zLW5vdGlmaWNhdGlvbnMge1xuXHRcdHBhZGRpbmc6IDE1cHggMTVweCAxNXB4IDcycHg7XG5cblx0XHQubWVzc2FnZXMge1xuXG5cdFx0XHQubWVzc2FnZSB7XG5cblx0XHRcdFx0LnRpdGxlIHtcblx0XHRcdFx0XHRtYXJnaW46IDAgMzBweCAwIDA7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuY29udGVudCB7XG5cdFx0XHRcdFx0Zm9udC1zaXplOiAxNnB4O1xuXHRcdFx0XHRcdGxpbmUtaGVpZ2h0OiAyNHB4XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQuYnV0dG9ucyB7XG5cdFx0XHRcdFx0bWFyZ2luOiAtMzBweCA4MHB4IDAgMDtcblxuXHRcdFx0XHRcdGEge1xuXHRcdFx0XHRcdFx0bWFyZ2luOiAwO1xuXHRcdFx0XHRcdFx0ZGlzcGxheTogdGFibGU7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0LmJ1dHRvbi1zZWNvbmRhcnkge1xuXHRcdFx0XHRcdFx0bWFyZ2luLXRvcDogNnB4O1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxufSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxBQUFBLHNCQUFzQixDQUFDO0VBRXRCLFFBQVEsRUFBRSxRQUFRO0VBQ2xCLFVBQVUsRUFBRSxpQ0FBaUM7RUFDN0MsVUFBVSxFQUFFLHNCQUFzQjtFQUNsQyxhQUFhLEVBQUUsR0FBRztFQUNsQixPQUFPLEVBQUUsQ0FBQztFQUNWLFVBQVUsRUFBRSxJQUFJO0VBQ2hCLE9BQU8sRUFBRSxvQkFBb0I7RUFDN0IsTUFBTSxFQUFFLFVBQVU7Q0FxSGxCOztBQTlIRCxBQVdDLHNCQVhxQixDQVdyQixDQUFDLENBQUM7RUFDRCxVQUFVLEVBQUUsVUFBVTtDQUN0Qjs7QUFiRixBQWVDLHNCQWZxQixDQWVyQixLQUFLLENBQUM7RUFDTCxRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULElBQUksRUFBRSxJQUFJO0VBQ1YsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtDQUNaOztBQXJCRixBQXdCRSxzQkF4Qm9CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQUFDO0VBQ1IsT0FBTyxFQUFFLElBQUk7Q0FvQ2I7O0FBN0RILEFBMkJHLHNCQTNCbUIsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLEFBR04sUUFBUSxDQUFDO0VBQ1QsT0FBTyxFQUFFLEtBQUs7Q0FDZDs7QUE3QkosQUErQkcsc0JBL0JtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FPUCxNQUFNLENBQUM7RUFDTixXQUFXLEVBQUUsSUFBSTtFQUNqQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxDQUFDO0VBQ1QsS0FBSyxFQUFFLElBQUk7Q0FDWDs7QUFyQ0osQUF1Q0csc0JBdkNtQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FlUCxRQUFRLENBQUM7RUFDUixXQUFXLEVBQUUsTUFBTTtFQUNuQixTQUFTLEVBQUUsSUFBSTtFQUNmLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLE1BQU0sRUFBRSxZQUFZO0NBQ3BCOztBQTVDSixBQThDRyxzQkE5Q21CLENBdUJyQixTQUFTLENBQ1IsUUFBUSxDQXNCUCxRQUFRLENBQUM7RUFDUixNQUFNLEVBQUUsY0FBYztDQWF0Qjs7QUE1REosQUFpREksc0JBakRrQixDQXVCckIsU0FBUyxDQUNSLFFBQVEsQ0FzQlAsUUFBUSxDQUdQLENBQUMsQ0FBQztFQUNELE1BQU0sRUFBRSxTQUFTO0VBQ2pCLE9BQU8sRUFBRSxRQUFRO0VBQ2pCLFdBQVcsRUFBRSxJQUFJO0VBQ2pCLFNBQVMsRUFBRSxJQUFJO0VBQ2YsVUFBVSxFQUFFLEtBQUs7Q0FDakI7O0FBdkRMLEFBeURJLHNCQXpEa0IsQ0F1QnJCLFNBQVMsQ0FDUixRQUFRLENBc0JQLFFBQVEsQ0FXUCxpQkFBaUIsQ0FBQztFQUNqQixNQUFNLEVBQUUsaUJBQWlCO0NBQ3pCOztBQTNETCxBQWdFQyxzQkFoRXFCLENBZ0VyQixRQUFRLENBQUM7RUFDUixRQUFRLEVBQUUsUUFBUTtFQUNsQixHQUFHLEVBQUUsSUFBSTtFQUNULEtBQUssRUFBRSxJQUFJO0VBQ1gsS0FBSyxFQUFFLElBQUk7RUFDWCxNQUFNLEVBQUUsSUFBSTtFQUNaLEtBQUssRUFBRSxPQUFPO0VBQ2QsU0FBUyxFQUFFLElBQUk7RUFDZixNQUFNLEVBQUUsT0FBTztFQUNmLFVBQVUsRUFBRSxNQUFNO0VBQ2xCLGNBQWMsRUFBRSxNQUFNO0VBQ3RCLFdBQVcsRUFBRSxJQUFJO0NBS2pCOztBQWhGRixBQTZFRSxzQkE3RW9CLENBZ0VyQixRQUFRLEFBYU4sTUFBTSxDQUFDO0VBQ1AsS0FBSyxFQUFFLE9BQU87Q0FDZDs7QUEvRUgsQUFrRkMsc0JBbEZxQixDQWtGckIsV0FBVyxDQUFDO0VBQ1gsUUFBUSxFQUFFLFFBQVE7RUFDbEIsTUFBTSxFQUFFLElBQUk7RUFDWixLQUFLLEVBQUUsSUFBSTtFQUNYLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7Q0FzQ1o7O0FBN0hGLEFBeUZFLHNCQXpGb0IsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLENBQUM7RUFDRCxPQUFPLEVBQUUsS0FBSztFQUNkLEtBQUssRUFBRSxJQUFJO0VBQ1gsTUFBTSxFQUFFLElBQUk7RUFDWixNQUFNLEVBQUUsaUJBQWlCO0VBQ3pCLGFBQWEsRUFBRSxHQUFHO0VBQ2xCLFNBQVMsRUFBRSxHQUFHO0VBQ2QsVUFBVSxFQUFFLE1BQU07RUFDbEIsY0FBYyxFQUFFLE1BQU07RUFDdEIsV0FBVyxFQUFFLElBQUk7RUFDakIsTUFBTSxFQUFFLE9BQU87RUFDZixnQkFBZ0IsRUFBRSxPQUFPO0VBQ3pCLEtBQUssRUFBRSxPQUFPO0NBS2Q7O0FBMUdILEFBdUdHLHNCQXZHbUIsQ0FrRnJCLFdBQVcsQ0FPVixDQUFDLEFBY0MsTUFBTSxDQUFDO0VBQ1AsZ0JBQWdCLEVBQUUsT0FBTztDQUN6Qjs7QUF6R0osQUE0R0Usc0JBNUdvQixDQWtGckIsV0FBVyxDQTBCVixLQUFLLENBQUM7RUFDTCxLQUFLLEVBQUUsSUFBSTtDQUNYOztBQTlHSCxBQWdIRSxzQkFoSG9CLENBa0ZyQixXQUFXLENBOEJWLEtBQUssQ0FBQztFQUNMLEtBQUssRUFBRSxLQUFLO0NBQ1o7O0FBbEhILEFBb0hFLHNCQXBIb0IsQ0FrRnJCLFdBQVcsQ0FrQ1YsU0FBUyxDQUFDO0VBQ1QsWUFBWSxFQUFFLE9BQU87RUFDckIsS0FBSyxFQUFFLE9BQU87RUFDZCxNQUFNLEVBQUUsT0FBTztDQUtmOztBQTVISCxBQXlIRyxzQkF6SG1CLENBa0ZyQixXQUFXLENBa0NWLFNBQVMsQUFLUCxNQUFNLENBQUM7RUFDUCxnQkFBZ0IsRUFBRSxPQUFPO0NBQ3pCOztBQUtKLE1BQU0sQ0FBQyxNQUFNLE1BQU0sU0FBUyxFQUFFLEtBQUs7RUFFbEMsQUFBQSxzQkFBc0IsQ0FBQztJQUN0QixPQUFPLEVBQUUsbUJBQW1CO0dBNkI1QjtFQTlCRCxBQU9HLHNCQVBtQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQUVQLE1BQU0sQ0FBQztJQUNOLE1BQU0sRUFBRSxVQUFVO0dBQ2xCO0VBVEosQUFXRyxzQkFYbUIsQ0FHckIsU0FBUyxDQUVSLFFBQVEsQ0FNUCxRQUFRLENBQUM7SUFDUixTQUFTLEVBQUUsSUFBSTtJQUNmLFdBQVcsRUFBRSxJQUNkO0dBQUM7RUFkSixBQWdCRyxzQkFoQm1CLENBR3JCLFNBQVMsQ0FFUixRQUFRLENBV1AsUUFBUSxDQUFDO0lBQ1IsTUFBTSxFQUFFLGNBQWM7R0FVdEI7RUEzQkosQUFtQkksc0JBbkJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FHUCxDQUFDLENBQUM7SUFDRCxNQUFNLEVBQUUsQ0FBQztJQUNULE9BQU8sRUFBRSxLQUFLO0dBQ2Q7RUF0QkwsQUF3Qkksc0JBeEJrQixDQUdyQixTQUFTLENBRVIsUUFBUSxDQVdQLFFBQVEsQ0FRUCxpQkFBaUIsQ0FBQztJQUNqQixVQUFVLEVBQUUsR0FBRztHQUNmIn0= */
css/ctf-admin-styles.css CHANGED
@@ -198,7 +198,7 @@
198
  }
199
 
200
  /* Error notice */
201
- #ctf-admin .ctf_notice, .ctf_notice{
202
  margin-top: 5px;
203
  background: #f9ecda;
204
  padding: 5px 10px;
@@ -209,14 +209,14 @@
209
  -webkit-border-radius: 3px;
210
  border-radius: 3px;
211
  }
212
- #ctf-admin .ctf_notice a, .ctf_notice a{
213
  color: #d85600;
214
  }
215
- #ctf-admin .ctf_notice a:hover, .ctf_notice a:hover,
216
- #ctf-admin .ctf_notice a:focus, .ctf_notice a:focus{
217
  color: #a34100;
218
  }
219
- #ctf-admin .ctf_notice p, .ctf_notice p{
220
  margin: 0;
221
  padding: 5px 0;
222
  font-size: 13px;
@@ -236,7 +236,7 @@
236
  }
237
 
238
  /* Review notice */
239
- .ctf_review_notice{
240
  position: relative;
241
  overflow: hidden;
242
  max-width: 870px;
@@ -247,11 +247,9 @@
247
  border: 1px solid #6AB074;
248
  color: #214F28;
249
  }
250
- .ctf_bfcm_sale_notice,
251
- .ctf_new_user_sale_notice{
252
- max-width: 672px;
253
- }
254
- .ctf_review_notice img{
255
  width: 74px;
256
  margin: 0 0 0 -100% !important;
257
 
@@ -259,13 +257,31 @@
259
  -webkit-border-radius: 4px;
260
  border-radius: 4px;
261
  }
262
- .ctf_review_notice .ctf-notice-text{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  float: left;
264
  clear: none;
265
  width: 100%;
266
  padding: 0;
267
  }
268
- .ctf_review_notice p{
269
  float: left;
270
  clear: both;
271
  width: auto;
@@ -273,37 +289,37 @@
273
  padding: 2px 40px 2px 0;
274
  line-height: 1.4;
275
  }
276
- .ctf_review_notice .ctf-links{
277
  margin-top: 4px !important;
278
  }
279
- .ctf_review_notice a{
280
  display: inline-block;
281
  padding: 0 8px;
282
  color: #178529;
283
  }
284
- .ctf_review_notice a:hover,
285
- .ctf_review_notice a:focus{
286
  color: #0c7abf;
287
  }
288
- .ctf_review_notice .links{
289
  margin: 0 0 0 82px !important;
290
  padding: 4px 0 0 0;
291
  margin-top: 6px !important;
292
  }
293
- .ctf_review_notice .ctf_notice_close,
294
- .ctf_review_notice .ctf_bfcm_sale_notice_close,
295
- .ctf_review_notice .ctf_new_user_sale_notice_close {
296
  position: absolute;
297
  top: 0;
298
  right: 0;
299
  padding: 10px;
300
  line-height: 1;
301
  }
302
- .ctf_review_notice .ctf_notice_close:hover,
303
- .ctf_review_notice .ctf_notice_close:focus{
304
  color: #a34100;
305
  }
306
- .ctf_review_notice .ctf_offer_btn {
307
  padding: 4px 12px 6px 12px;
308
  background: green;
309
  color: #fff;
@@ -312,16 +328,16 @@
312
  text-decoration: none;
313
  margin-left: 0;
314
  }
315
- .ctf_review_notice .ctf_offer_btn:hover, .ctf_review_notice .ctf_offer_btn:focus {
316
  background: #049404;
317
  color: #fff;
318
  }
319
- .ctf_review_notice .ctf_other_notice{
320
  padding-top: 10px;
321
  font-style: italic;
322
  font-size: 12px;
323
  }
324
- .ctf_review_notice .ctf_other_notice a{
325
  padding: 0;
326
  }
327
 
198
  }
199
 
200
  /* Error notice */
201
+ #ctf-admin .ctf_error_notice, .ctf_error_notice{
202
  margin-top: 5px;
203
  background: #f9ecda;
204
  padding: 5px 10px;
209
  -webkit-border-radius: 3px;
210
  border-radius: 3px;
211
  }
212
+ #ctf-admin .ctf_error_notice a, .ctf_error_notice a{
213
  color: #d85600;
214
  }
215
+ #ctf-admin .ctf_error_notice a:hover, .ctf_error_notice a:hover,
216
+ #ctf-admin .ctf_error_notice a:focus, .ctf_error_notice a:focus{
217
  color: #a34100;
218
  }
219
+ #ctf-admin .ctf_error_notice p, .ctf_error_notice p{
220
  margin: 0;
221
  padding: 5px 0;
222
  font-size: 13px;
236
  }
237
 
238
  /* Review notice */
239
+ .ctf_notice {
240
  position: relative;
241
  overflow: hidden;
242
  max-width: 870px;
247
  border: 1px solid #6AB074;
248
  color: #214F28;
249
  }
250
+ .ctf_notice .ctf_thumb{
251
+ position: relative;
252
+ display: inline-block;
 
 
253
  width: 74px;
254
  margin: 0 0 0 -100% !important;
255
 
257
  -webkit-border-radius: 4px;
258
  border-radius: 4px;
259
  }
260
+ .ctf_notice .ctf_thumb .img-overlay {
261
+ position: absolute;
262
+ top: 3px;
263
+ padding: 6px 5px;
264
+ font-size: 12px;
265
+ font-weight: bold;
266
+ background: #fff;
267
+ line-height: 1;
268
+ color: #000;
269
+ opacity: .8;
270
+ }
271
+ .ctf_notice img{
272
+ width: 74px;
273
+
274
+ -moz-border-radius: 4px;
275
+ -webkit-border-radius: 4px;
276
+ border-radius: 4px;
277
+ }
278
+ .ctf_notice .ctf-notice-text{
279
  float: left;
280
  clear: none;
281
  width: 100%;
282
  padding: 0;
283
  }
284
+ .ctf_notice p{
285
  float: left;
286
  clear: both;
287
  width: auto;
289
  padding: 2px 40px 2px 0;
290
  line-height: 1.4;
291
  }
292
+ .ctf_notice .ctf-links{
293
  margin-top: 4px !important;
294
  }
295
+ .ctf_notice a{
296
  display: inline-block;
297
  padding: 0 8px;
298
  color: #178529;
299
  }
300
+ .ctf_notice a:hover,
301
+ .ctf_notice a:focus{
302
  color: #0c7abf;
303
  }
304
+ .ctf_notice .links{
305
  margin: 0 0 0 82px !important;
306
  padding: 4px 0 0 0;
307
  margin-top: 6px !important;
308
  }
309
+ .ctf_notice .ctf_notice_close,
310
+ .ctf_notice .ctf_bfcm_sale_notice_close,
311
+ .ctf_notice .ctf_new_user_sale_notice_close {
312
  position: absolute;
313
  top: 0;
314
  right: 0;
315
  padding: 10px;
316
  line-height: 1;
317
  }
318
+ .ctf_notice .ctf_notice_close:hover,
319
+ .ctf_notice .ctf_notice_close:focus{
320
  color: #a34100;
321
  }
322
+ .ctf_notice .ctf_offer_btn {
323
  padding: 4px 12px 6px 12px;
324
  background: green;
325
  color: #fff;
328
  text-decoration: none;
329
  margin-left: 0;
330
  }
331
+ .ctf_notice .ctf_offer_btn:hover, .ctf_notice .ctf_offer_btn:focus {
332
  background: #049404;
333
  color: #fff;
334
  }
335
+ .ctf_notice .ctf_other_notice{
336
  padding-top: 10px;
337
  font-style: italic;
338
  font-size: 12px;
339
  }
340
+ .ctf_notice .ctf_other_notice a{
341
  padding: 0;
342
  }
343
 
custom-twitter-feed.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Custom Twitter Feeds
4
  Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
  Description: Customizable Twitter feeds for your website
6
- Version: 1.6
7
  Author: Smash Balloon
8
  Author URI: http://smashballoon.com/
9
  Text Domain: custom-twitter-feeds
@@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
  */
25
 
26
  define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
- define( 'CTF_VERSION', '1.6' );
28
  define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
  define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
  define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
@@ -57,6 +57,19 @@ function ctf_plugin_init() {
57
  }
58
 
59
  include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-tracking.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
  add_action( 'plugins_loaded', 'ctf_plugin_init' );
3
  Plugin Name: Custom Twitter Feeds
4
  Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
  Description: Customizable Twitter feeds for your website
6
+ Version: 1.6.1
7
  Author: Smash Balloon
8
  Author URI: http://smashballoon.com/
9
  Text Domain: custom-twitter-feeds
24
  */
25
 
26
  define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
+ define( 'CTF_VERSION', '1.6.1' );
28
  define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
  define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
  define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
57
  }
58
 
59
  include_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/class-ctf-tracking.php';
60
+
61
+ if ( is_admin() ) {
62
+ if ( version_compare( PHP_VERSION, '5.3.0' ) >= 0
63
+ && version_compare( get_bloginfo('version'), '4.6' , '>' ) ) {
64
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-notifications.php';
65
+ $ctf_notifications = new CTF_Notifications();
66
+ $ctf_notifications->init();
67
+
68
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/admin/class-ctf-new-user.php';
69
+ $ctf_new_user = new CTF_New_User();
70
+ $ctf_new_user->init();
71
+ }
72
+ }
73
  }
74
 
75
  add_action( 'plugins_loaded', 'ctf_plugin_init' );
img/ctf-icon.png ADDED
Binary file
inc/CtfAdmin.php CHANGED
@@ -124,7 +124,7 @@ class CtfAdmin
124
 
125
  <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
126
 
127
- <p class="ctf_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
128
  <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
129
  <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
130
 
124
 
125
  <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
126
 
127
+ <p class="ctf_error_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
128
  <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
129
  <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/?utm_campaign=twitter-free&utm_source=settings&utm_medium=errorconnecting" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
130
 
inc/CtfFeed.php CHANGED
@@ -408,11 +408,11 @@ class CtfFeed
408
  $this->feed_options['count'] = $this->feed_options['num'];
409
  } else {
410
  if ( $this->feed_options['num'] < 10 ) {
411
- $this->feed_options['count'] = max( round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
412
  } elseif ( $this->feed_options['num'] < 30 ) {
413
- $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.2 );
414
  } else {
415
- $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] );
416
  }
417
  }
418
  } else {
@@ -1378,9 +1378,9 @@ class CtfFeed
1378
  $html .= '<div class="ctf-out-of-tweets">';
1379
  $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1380
  $html .= '<p>';
1381
- $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" data-size="large" data-url="'.get_home_url().'">Share</a>';
1382
  if ( !empty( $feed_options['screenname'] ) ) {
1383
- $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1384
  }
1385
  $html .= '</p>';
1386
  if ( !$feed_options['disableintents'] ) {
@@ -1442,7 +1442,7 @@ class CtfFeed
1442
  }
1443
 
1444
  if ( $feed_options['creditctf'] ) {
1445
- $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1446
  }
1447
 
1448
  $ctf_feed_html .= '</div>'; // closing div tag for #ctf
@@ -1472,7 +1472,7 @@ class CtfFeed
1472
 
1473
  if ( $feed_options['type'] === 'usertimeline' ) {
1474
  $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1475
- $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1476
  $ctf_header_html .= '<div class="ctf-header-text">';
1477
  $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1478
  $ctf_header_html .= '<span class="ctf-header-name">';
@@ -1516,7 +1516,7 @@ class CtfFeed
1516
  $default_header_text = str_replace( ' -filter:retweets', '', $default_header_text );
1517
 
1518
  $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1519
- $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" class="ctf-header-link">';
1520
  $ctf_header_html .= '<div class="ctf-header-text">';
1521
  $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1522
  $ctf_header_html .= '</div>';
@@ -1642,8 +1642,8 @@ class CtfFeed
1642
 
1643
  if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1644
  $tweet_html .= '<div class="ctf-context">';
1645
- $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1646
- $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . $feed_options['retweetedtext'] . '</a>';
1647
  $tweet_html .= '</div>';
1648
  }
1649
 
@@ -1652,17 +1652,17 @@ class CtfFeed
1652
  $tweet_html .= '<div class="ctf-author-box">';
1653
  $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1654
  if ( ctf_show( 'avatar', $feed_options ) ) {
1655
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1656
  $tweet_html .= '<img src="' . $post['user']['profile_image_url_https'] . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1657
  $tweet_html .= '</a>';
1658
  }
1659
 
1660
  if ( ctf_show( 'author', $feed_options ) ) {
1661
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1662
  if ( $post['user']['verified'] == 1 ) {
1663
  $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1664
  }
1665
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1666
  $sep_style_att = ! empty( $feed_options['authortextsize'] ) ? ' style="' . $feed_options['authortextsize'] . '"' : '';
1667
  $tweet_html .= '<span class="ctf-screename-sep"' . $sep_style_att . '>&middot;</span>';
1668
  }
@@ -1670,7 +1670,7 @@ class CtfFeed
1670
  if ( ctf_show( 'date', $feed_options ) ) {
1671
  $tweet_html .= '<div class="ctf-tweet-meta">';
1672
  //https://twitter.com/EnterLaw/status/869452491041243137
1673
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1674
  $tweet_html .= '</div>';
1675
  } // show date
1676
  $tweet_html .= '</div>';
@@ -1688,7 +1688,7 @@ class CtfFeed
1688
  $tweet_html .= '<div class="ctf-tweet-content">';
1689
 
1690
  if ( $feed_options['linktexttotwitter'] ) {
1691
- $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank">';
1692
  $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1693
  $tweet_html .= '</a>';
1694
  } else {
@@ -1702,7 +1702,7 @@ class CtfFeed
1702
  if ( $feed_options['disablelinks'] ) {
1703
  $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1704
  } else {
1705
- $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1706
  }
1707
  }
1708
  } // link text to twitter option is selected
@@ -1711,7 +1711,7 @@ class CtfFeed
1711
  } // show tweet text
1712
 
1713
  if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1714
- $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank">';
1715
  $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1716
 
1717
  if ($quoted['user']['verified'] == 1) {
@@ -1727,20 +1727,20 @@ class CtfFeed
1727
 
1728
  $tweet_html .= '<div class="ctf-tweet-actions">';
1729
  if ( ctf_show( 'actions', $feed_options ) ) {
1730
- $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1731
- $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1732
  if ( $post['retweet_count'] > 0 ) {
1733
  $tweet_html .= $post['retweet_count'];
1734
  }
1735
  $tweet_html .= '</span></a>';
1736
- $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1737
  if ( $post['favorite_count'] > 0 ) {
1738
  $tweet_html .= $post['favorite_count'];
1739
  }
1740
  $tweet_html .= '</span></a>';
1741
  }
1742
  if ( ctf_show( 'twitterlink', $feed_options ) ) {
1743
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1744
  } // show twitter link or actions
1745
  $tweet_html .= '</div>';
1746
  $tweet_html .= '</div>';
@@ -1797,7 +1797,7 @@ class CtfFeed
1797
  $error_html .= 'The error response from the Twitter API is the following:<br />';
1798
  $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1799
  $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1800
- $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/?utm_campaign=twitter-free&utm_source=frontend&utm_medium=errormessage" target="_blank">Click here to troubleshoot</a></p>';
1801
 
1802
 
1803
  }
408
  $this->feed_options['count'] = $this->feed_options['num'];
409
  } else {
410
  if ( $this->feed_options['num'] < 10 ) {
411
+ $this->feed_options['count'] = max( round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
412
  } elseif ( $this->feed_options['num'] < 30 ) {
413
+ $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.2 );
414
  } else {
415
+ $this->feed_options['count'] = round( $this->feed_options['num'] * (float)$this->feed_options['multiplier'] );
416
  }
417
  }
418
  } else {
1378
  $html .= '<div class="ctf-out-of-tweets">';
1379
  $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1380
  $html .= '<p>';
1381
+ $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" rel="noopener noreferrer" data-size="large" data-url="'.get_home_url().'">Share</a>';
1382
  if ( !empty( $feed_options['screenname'] ) ) {
1383
+ $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" rel="noopener noreferrer" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1384
  }
1385
  $html .= '</p>';
1386
  if ( !$feed_options['disableintents'] ) {
1442
  }
1443
 
1444
  if ( $feed_options['creditctf'] ) {
1445
+ $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank" rel="noopener noreferrer">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1446
  }
1447
 
1448
  $ctf_feed_html .= '</div>'; // closing div tag for #ctf
1472
 
1473
  if ( $feed_options['type'] === 'usertimeline' ) {
1474
  $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1475
+ $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1476
  $ctf_header_html .= '<div class="ctf-header-text">';
1477
  $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1478
  $ctf_header_html .= '<span class="ctf-header-name">';
1516
  $default_header_text = str_replace( ' -filter:retweets', '', $default_header_text );
1517
 
1518
  $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1519
+ $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" rel="noopener noreferrer" class="ctf-header-link">';
1520
  $ctf_header_html .= '<div class="ctf-header-text">';
1521
  $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1522
  $ctf_header_html .= '</div>';
1642
 
1643
  if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1644
  $tweet_html .= '<div class="ctf-context">';
1645
+ $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1646
+ $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . $feed_options['retweetedtext'] . '</a>';
1647
  $tweet_html .= '</div>';
1648
  }
1649
 
1652
  $tweet_html .= '<div class="ctf-author-box">';
1653
  $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1654
  if ( ctf_show( 'avatar', $feed_options ) ) {
1655
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1656
  $tweet_html .= '<img src="' . $post['user']['profile_image_url_https'] . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1657
  $tweet_html .= '</a>';
1658
  }
1659
 
1660
  if ( ctf_show( 'author', $feed_options ) ) {
1661
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" rel="noopener noreferrer" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1662
  if ( $post['user']['verified'] == 1 ) {
1663
  $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1664
  }
1665
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" rel="noopener noreferrer" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1666
  $sep_style_att = ! empty( $feed_options['authortextsize'] ) ? ' style="' . $feed_options['authortextsize'] . '"' : '';
1667
  $tweet_html .= '<span class="ctf-screename-sep"' . $sep_style_att . '>&middot;</span>';
1668
  }
1670
  if ( ctf_show( 'date', $feed_options ) ) {
1671
  $tweet_html .= '<div class="ctf-tweet-meta">';
1672
  //https://twitter.com/EnterLaw/status/869452491041243137
1673
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" rel="noopener noreferrer" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1674
  $tweet_html .= '</div>';
1675
  } // show date
1676
  $tweet_html .= '</div>';
1688
  $tweet_html .= '<div class="ctf-tweet-content">';
1689
 
1690
  if ( $feed_options['linktexttotwitter'] ) {
1691
+ $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer">';
1692
  $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1693
  $tweet_html .= '</a>';
1694
  } else {
1702
  if ( $feed_options['disablelinks'] ) {
1703
  $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1704
  } else {
1705
+ $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" rel="noopener noreferrer" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1706
  }
1707
  }
1708
  } // link text to twitter option is selected
1711
  } // show tweet text
1712
 
1713
  if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1714
+ $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">';
1715
  $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1716
 
1717
  if ($quoted['user']['verified'] == 1) {
1727
 
1728
  $tweet_html .= '<div class="ctf-tweet-actions">';
1729
  if ( ctf_show( 'actions', $feed_options ) ) {
1730
+ $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1731
+ $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1732
  if ( $post['retweet_count'] > 0 ) {
1733
  $tweet_html .= $post['retweet_count'];
1734
  }
1735
  $tweet_html .= '</span></a>';
1736
+ $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" rel="noopener noreferrer" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1737
  if ( $post['favorite_count'] > 0 ) {
1738
  $tweet_html .= $post['favorite_count'];
1739
  }
1740
  $tweet_html .= '</span></a>';
1741
  }
1742
  if ( ctf_show( 'twitterlink', $feed_options ) ) {
1743
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank" rel="noopener noreferrer">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1744
  } // show twitter link or actions
1745
  $tweet_html .= '</div>';
1746
  $tweet_html .= '</div>';
1797
  $error_html .= 'The error response from the Twitter API is the following:<br />';
1798
  $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1799
  $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1800
+ $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/?utm_campaign=twitter-free&utm_source=frontend&utm_medium=errormessage" target="_blank" rel="noopener noreferrer">Click here to troubleshoot</a></p>';
1801
 
1802
 
1803
  }
inc/admin-hooks.php CHANGED
@@ -59,7 +59,7 @@ add_action( 'ctf_admin_feed_settings_radio_extra', 'ctf_usertimeline_error_messa
59
  function ctf_usertimeline_error_message( $args )
60
  { //sbi_notice sbi_user_id_error
61
  if ( $args['name'] == 'usertimeline') : ?>
62
- <div class="ctf_notice ctf_usertimeline_error">
63
  <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multiuser' target='_blank'>Pro version</a>.</p>" ); ?>
64
  </div>
65
  <?php endif;
@@ -68,7 +68,7 @@ function ctf_usertimeline_error_message( $args )
68
  add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
  function ctf_hashtag_error_message() {
70
  ?>
71
- <div class="ctf_notice ctf_search_error">
72
  <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multisearch' target='_blank'>Pro version</a>.</p>" ); ?>
73
  </div>
74
  <?php
59
  function ctf_usertimeline_error_message( $args )
60
  { //sbi_notice sbi_user_id_error
61
  if ( $args['name'] == 'usertimeline') : ?>
62
+ <div class="ctf_error_notice ctf_usertimeline_error">
63
  <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multiuser' target='_blank'>Pro version</a>.</p>" ); ?>
64
  </div>
65
  <?php endif;
68
  add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
  function ctf_hashtag_error_message() {
70
  ?>
71
+ <div class="ctf_error_notice ctf_search_error">
72
  <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=settings&utm_medium=multisearch' target='_blank'>Pro version</a>.</p>" ); ?>
73
  </div>
74
  <?php
inc/admin/class-ctf-new-user.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CTF_New_User.
4
+ *
5
+ * @since 2.18
6
+ */
7
+
8
+ // Exit if accessed directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class CTF_New_User extends CTF_Notifications {
14
+
15
+ /**
16
+ * Source of notifications content.
17
+ *
18
+ * @since 2.18
19
+ *
20
+ * @var string
21
+ */
22
+ const SOURCE_URL = 'http://plugin.smashballoon.com/newuser.json';
23
+
24
+ /**
25
+ * @var string
26
+ */
27
+ const OPTION_NAME = 'ctf_newuser_notifications';
28
+
29
+ /**
30
+ * Register hooks.
31
+ *
32
+ * @since 2.18
33
+ */
34
+ public function hooks() {
35
+ add_action( 'admin_notices', array( $this, 'output' ), 8 );
36
+
37
+ add_action( 'admin_init', array( $this, 'dismiss' ) );
38
+ }
39
+
40
+ public function option_name() {
41
+ return self::OPTION_NAME;
42
+ }
43
+
44
+ public function source_url() {
45
+ return self::SOURCE_URL;
46
+ }
47
+
48
+ /**
49
+ * Verify notification data before it is saved.
50
+ *
51
+ * @param array $notifications Array of notifications items to verify.
52
+ *
53
+ * @return array
54
+ *
55
+ * @since 2.18
56
+ */
57
+ public function verify( $notifications ) {
58
+ $data = array();
59
+
60
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
61
+ return $data;
62
+ }
63
+
64
+ $option = $this->get_option();
65
+
66
+ foreach ( $notifications as $key => $notification ) {
67
+
68
+ // The message should never be empty, if they are, ignore.
69
+ if ( empty( $notification['content'] ) ) {
70
+ continue;
71
+ }
72
+
73
+ // Ignore if notification has already been dismissed.
74
+ if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
75
+ continue;
76
+ }
77
+
78
+ $data[ $key ] = $notification;
79
+ }
80
+
81
+ return $data;
82
+ }
83
+
84
+ /**
85
+ * Verify saved notification data for active notifications.
86
+ *
87
+ * @since 2.18
88
+ *
89
+ * @param array $notifications Array of notifications items to verify.
90
+ *
91
+ * @return array
92
+ */
93
+ public function verify_active( $notifications ) {
94
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
95
+ return array();
96
+ }
97
+
98
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
99
+ $current_time = ctf_get_current_time();
100
+
101
+ // rating notice logic
102
+ $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
103
+ $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
104
+ $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
105
+
106
+ // new user discount logic
107
+ $in_new_user_month_range = true;
108
+ $should_show_new_user_discount = false;
109
+ $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + ((int)$notifications['review']['wait'] * DAY_IN_SECONDS)) < $current_time + 1: true;
110
+
111
+ if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
112
+ global $current_user;
113
+ $user_id = $current_user->ID;
114
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
115
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
116
+ if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
117
+ $should_show_new_user_discount = true;
118
+ }
119
+ } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal && $ctf_rating_notice_waiting !== 'waiting' ) {
120
+ global $current_user;
121
+ $user_id = $current_user->ID;
122
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
123
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
124
+
125
+ if ( $ignore_new_user_sale_notice_meta !== 'always'
126
+ && isset( $ctf_statuses_option['first_install'] )
127
+ && $current_time > (int)$ctf_statuses_option['first_install'] + ((int)$notifications['discount']['wait'] * DAY_IN_SECONDS) ) {
128
+ $should_show_new_user_discount = true;
129
+ }
130
+ }
131
+
132
+ if ( isset( $notifications['review'] ) && $should_show_rating_notice ) {
133
+ return array( $notifications['review'] );
134
+ } elseif ( isset( $notifications['discount'] ) && $should_show_new_user_discount ) {
135
+ return array( $notifications['discount'] );
136
+ }
137
+
138
+ return array();
139
+ }
140
+
141
+ /**
142
+ * Get notification data.
143
+ *
144
+ * @since 2.18
145
+ *
146
+ * @return array
147
+ */
148
+ public function get() {
149
+ if ( ! $this->has_access() ) {
150
+ return array();
151
+ }
152
+
153
+ $option = $this->get_option();
154
+
155
+ // Only update if does not exist.
156
+ if ( empty( $option['update'] ) ) {
157
+ $this->update();
158
+ }
159
+
160
+ $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
161
+ $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
162
+
163
+ return array_merge( $events, $feed );
164
+ }
165
+
166
+ /**
167
+ * Add a manual notification event.
168
+ *
169
+ * @since 2.18
170
+ *
171
+ * @param array $notification Notification data.
172
+ */
173
+ public function add( $notification ) {
174
+ if ( empty( $notification['id'] ) ) {
175
+ return;
176
+ }
177
+
178
+ $option = $this->get_option();
179
+
180
+ if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
181
+ return;
182
+ }
183
+
184
+ foreach ( $option['events'] as $item ) {
185
+ if ( $item['id'] === $notification['id'] ) {
186
+ return;
187
+ }
188
+ }
189
+
190
+ $notification = $this->verify( array( $notification ) );
191
+
192
+ update_option(
193
+ $this->option_name(),
194
+ array(
195
+ 'update' => $option['update'],
196
+ 'feed' => $option['feed'],
197
+ 'events' => array_merge( $notification, $option['events'] ),
198
+ 'dismissed' => $option['dismissed'],
199
+ )
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Update notification data from feed.
205
+ *
206
+ * @since 2.18
207
+ */
208
+ public function update() {
209
+ $feed = $this->fetch_feed();
210
+ $option = $this->get_option();
211
+
212
+ update_option(
213
+ $this->option_name(),
214
+ array(
215
+ 'update' => time(),
216
+ 'feed' => $feed,
217
+ 'events' => $option['events'],
218
+ 'dismissed' => $option['dismissed'],
219
+ )
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Do not enqueue anything extra.
225
+ *
226
+ * @since 2.18
227
+ */
228
+ public function enqueues() {
229
+
230
+ }
231
+
232
+ /**
233
+ * Output notifications on Form Overview admin area.
234
+ *
235
+ * @since 2.18
236
+ */
237
+ public function output() {
238
+ // If the Instagram Feed plugin is active, notices only shown on CTF Settings pages
239
+ if ( function_exists( 'sb_instagram_activate' )
240
+ && ! function_exists( 'sb_instagram_feed_pro_init' ) ) {
241
+ return;
242
+ }
243
+
244
+ if ( function_exists( 'cff_check_for_db_updates' ) ) {
245
+ return;
246
+ }
247
+
248
+ $notifications = $this->get();
249
+
250
+ if ( empty( $notifications ) ) {
251
+ return;
252
+ }
253
+
254
+ // new user notices included in regular settings page notifications so this
255
+ // checks to see if user is one of those pages
256
+ if ( ! empty( $_GET['page'] )
257
+ && strpos( $_GET['page'], 'custom-twitter-feeds' ) !== false ) {
258
+ return;
259
+ }
260
+
261
+ $content_allowed_tags = array(
262
+ 'em' => array(),
263
+ 'strong' => array(),
264
+ 'span' => array(
265
+ 'style' => array(),
266
+ ),
267
+ 'a' => array(
268
+ 'href' => array(),
269
+ 'target' => array(),
270
+ 'rel' => array(),
271
+ ),
272
+ );
273
+ $image_overlay = '';
274
+
275
+ foreach ( $notifications as $notification ) {
276
+ $type = sanitize_text_field( $notification['id'] );
277
+ $img_src = CTF_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
278
+ $content = '';
279
+ if ( ! empty( $notification['content'] ) ) {
280
+ $content = wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags );
281
+ }
282
+ $buttons = array();
283
+ if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
284
+ foreach ( $notification['btns'] as $btn_type => $btn ) {
285
+ if ( ! is_array( $btn['url'] ) ) {
286
+ $buttons[ $btn_type ]['url'] = $this->replace_merge_fields( $btn['url'], $notification );
287
+ } elseif ( is_array( $btn['url'] ) ) {
288
+ $buttons[ $btn_type ]['url'] = add_query_arg( $btn['url'] );
289
+ }
290
+
291
+ $buttons[ $btn_type ]['attr'] = '';
292
+ if ( ! empty( $btn['attr'] ) ) {
293
+ $buttons[ $btn_type ]['attr'] = ' target="_blank" rel="noopener noreferrer"';
294
+ }
295
+
296
+ $buttons[ $btn_type ]['class'] = '';
297
+ if ( ! empty( $btn['class'] ) ) {
298
+ $buttons[ $btn_type ]['class'] = ' ' . $btn['class'];
299
+ }
300
+
301
+ $buttons[ $btn_type ]['text'] = '';
302
+ if ( ! empty( $btn['text'] ) ) {
303
+ $buttons[ $btn_type ]['text'] = wp_kses( $btn['text'], $content_allowed_tags );
304
+ }
305
+ }
306
+ }
307
+ if ( isset( $notification['image_overlay'] ) ) {
308
+ $image_overlay = '<div class="img-overlay">'. esc_html( $notification['image_overlay'] ).'</div>';
309
+ }
310
+ }
311
+ ?>
312
+
313
+ <div class="ctf_notice ctf_<?php echo esc_attr( $type ); ?>_notice">
314
+ <div class="ctf_thumb">
315
+ <img src="<?php echo esc_url( $img_src ); ?>" alt="notice">
316
+ <?php echo $image_overlay; ?>
317
+ </div>
318
+ <div class="ctf-notice-text">
319
+ <p style="padding-top: 4px;"><?php echo $content; ?></p>
320
+ <p class="links">
321
+ <?php foreach ( $buttons as $button ) : ?>
322
+ <a class="<?php echo esc_attr( $button['class'] ); ?>" href="<?php echo esc_attr( $button['url'] ); ?>"<?php echo $button['attr']; ?>><?php echo $button['text']; ?></a>
323
+ <?php endforeach; ?>
324
+ </p>
325
+ </div>
326
+ <a class="ctf_notice_close" href="<?php echo add_query_arg( array( 'ctf_dismiss' => $type ) ); ?>"><i class="fa fa-close"></i></a>
327
+ </div>
328
+ <?php
329
+ }
330
+
331
+ /**
332
+ * Hide messages permanently or some can be dismissed temporarily
333
+ *
334
+ * @since 2.18
335
+ */
336
+ public function dismiss() {
337
+ global $current_user;
338
+ $user_id = $current_user->ID;
339
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
340
+
341
+ if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
342
+ if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
343
+ update_option( 'ctf_rating_notice', 'dismissed', false );
344
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
345
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
346
+
347
+ } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
348
+ set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
349
+ update_option( 'ctf_rating_notice', 'pending', false );
350
+ }
351
+ }
352
+
353
+ if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
354
+ $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
355
+ if ( $response === 'always' ) {
356
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
357
+
358
+ $current_month_number = (int)date('n', ctf_get_current_time() );
359
+ $not_early_in_the_year = ($current_month_number > 5);
360
+
361
+ if ( $not_early_in_the_year ) {
362
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
363
+ }
364
+
365
+ }
366
+ }
367
+
368
+ if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
369
+ $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
370
+ if ( $response === 'always' ) {
371
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
372
+ } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
373
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
374
+ }
375
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
376
+ }
377
+
378
+ if ( isset( $_GET['ctf_dismiss'] ) ) {
379
+ if ( $_GET['ctf_dismiss'] === 'review' ) {
380
+ update_option( 'ctf_rating_notice', 'dismissed', false );
381
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
382
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
383
+ } elseif ( $_GET['ctf_dismiss'] === 'discount' ) {
384
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
385
+
386
+ $current_month_number = (int)date('n', ctf_get_current_time() );
387
+ $not_early_in_the_year = ($current_month_number > 5);
388
+
389
+ if ( $not_early_in_the_year ) {
390
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
391
+ }
392
+ }
393
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
394
+ }
395
+ }
396
+ }
inc/admin/class-ctf-notifications.php ADDED
@@ -0,0 +1,581 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CTF_Notifications.
4
+ *
5
+ * @since 1.7/1.11
6
+ */
7
+
8
+ // Exit if accessed directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ class CTF_Notifications {
14
+
15
+ /**
16
+ * Source of notifications content.
17
+ *
18
+ * @var string
19
+ */
20
+ const SOURCE_URL = 'http://plugin.smashballoon.com/notifications.json';
21
+
22
+ /**
23
+ * @var string
24
+ */
25
+ const OPTION_NAME = 'ctf_notifications';
26
+
27
+ /**
28
+ * JSON data contains notices for all plugins. This is used
29
+ * to select messages only meant for this plugin
30
+ *
31
+ * @var string
32
+ */
33
+ const PLUGIN = 'twitter';
34
+
35
+ /**
36
+ * Option value.
37
+ *
38
+ * @since 1.7/1.11
39
+ *
40
+ * @var bool|array
41
+ */
42
+ public $option = false;
43
+
44
+ /**
45
+ * Initialize class.
46
+ *
47
+ * @since 1.7/1.11
48
+ */
49
+ public function init() {
50
+ $this->hooks();
51
+ }
52
+
53
+ /**
54
+ * Use this function to get the option name to allow
55
+ * inheritance for the New_User class
56
+ *
57
+ * @return string
58
+ */
59
+ public function option_name() {
60
+ return self::OPTION_NAME;
61
+ }
62
+
63
+ /**
64
+ * Use this function to get the source URL to allow
65
+ * inheritance for the New_User class
66
+ *
67
+ * @return string
68
+ */
69
+ public function source_url() {
70
+ return self::SOURCE_URL;
71
+ }
72
+
73
+ /**
74
+ * Register hooks.
75
+ *
76
+ * @since 1.7/1.11
77
+ */
78
+ public function hooks() {
79
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
80
+
81
+ add_action( 'ctf_admin_overview_before_title', array( $this, 'output' ) );
82
+
83
+ // on cron. Once a week?
84
+ add_action( 'ctf_notification_update', array( $this, 'update' ) );
85
+
86
+ add_action( 'wp_ajax_ctf_dashboard_notification_dismiss', array( $this, 'dismiss' ) );
87
+ }
88
+
89
+
90
+ /**
91
+ * Check if user has access and is enabled.
92
+ *
93
+ * @since 1.7/1.11
94
+ *
95
+ * @return bool
96
+ */
97
+ public function has_access() {
98
+ $access = false;
99
+
100
+ if ( current_user_can( 'manage_options' ) ) {
101
+ $access = true;
102
+ }
103
+
104
+ return apply_filters( 'ctf_admin_notifications_has_access', $access );
105
+ }
106
+
107
+ /**
108
+ * Get option value.
109
+ *
110
+ * @since 1.7/1.11
111
+ *
112
+ * @param bool $cache Reference property cache if available.
113
+ *
114
+ * @return array
115
+ */
116
+ public function get_option( $cache = true ) {
117
+ if ( $this->option && $cache ) {
118
+ return $this->option;
119
+ }
120
+
121
+ $option = get_option( $this->option_name(), array() );
122
+
123
+ $this->option = array(
124
+ 'update' => ! empty( $option['update'] ) ? $option['update'] : 0,
125
+ 'events' => ! empty( $option['events'] ) ? $option['events'] : array(),
126
+ 'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(),
127
+ 'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
128
+ );
129
+
130
+ return $this->option;
131
+ }
132
+
133
+ /**
134
+ * Fetch notifications from feed.
135
+ *
136
+ * @since 1.7/1.11
137
+ *
138
+ * @return array
139
+ */
140
+ public function fetch_feed() {
141
+ $res = wp_remote_get( $this->source_url() );
142
+
143
+ if ( is_wp_error( $res ) ) {
144
+ return array();
145
+ }
146
+
147
+ $body = wp_remote_retrieve_body( $res );
148
+
149
+ if ( empty( $body ) ) {
150
+ return array();
151
+ }
152
+
153
+ $body = str_replace( array( 'sbi_', 'sbi-' ), array( 'ctf_', 'ctf-' ), $body );
154
+
155
+ return $this->verify( json_decode( $body, true ) );
156
+ }
157
+
158
+ /**
159
+ * Verify notification data before it is saved.
160
+ *
161
+ * @since 1.7/1.11
162
+ *
163
+ * @param array $notifications Array of notifications items to verify.
164
+ *
165
+ * @return array
166
+ */
167
+ public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
168
+ $data = array();
169
+
170
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
171
+ return $data;
172
+ }
173
+
174
+ $option = $this->get_option();
175
+
176
+ foreach ( $notifications as $notification ) {
177
+
178
+ // The message and license should never be empty, if they are, ignore.
179
+ if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) {
180
+ continue;
181
+ }
182
+
183
+ // Ignore if license type does not match.
184
+ $license = ctf_is_pro_version() ? 'pro' : 'free';
185
+
186
+ if ( ! in_array( $license, $notification['type'], true ) ) {
187
+ continue;
188
+ }
189
+
190
+ // Ignore if expired.
191
+ if ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) {
192
+ continue;
193
+ }
194
+
195
+ // Ignore if notification has already been dismissed.
196
+ if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
197
+ continue;
198
+ }
199
+
200
+ // TODO: Ignore if notification existed before installing CTF.
201
+ // Prevents bombarding the user with notifications after activation.
202
+ $activated = false;
203
+ if ( ! empty( $activated )
204
+ && ! empty( $notification['start'] )
205
+ && $activated > strtotime( $notification['start'] ) ) {
206
+ continue;
207
+ }
208
+
209
+ $data[] = $notification;
210
+ }
211
+
212
+ return $data;
213
+ }
214
+
215
+ /**
216
+ * Verify saved notification data for active notifications.
217
+ *
218
+ * @since 1.7/1.11
219
+ *
220
+ * @param array $notifications Array of notifications items to verify.
221
+ *
222
+ * @return array
223
+ */
224
+ public function verify_active( $notifications ) {
225
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
226
+ return array();
227
+ }
228
+
229
+ // Remove notfications that are not active.
230
+ foreach ( $notifications as $key => $notification ) {
231
+ if ( ( ! empty( $notification['start'] ) && ctf_get_current_time() < strtotime( $notification['start'] ) )
232
+ || ( ! empty( $notification['end'] ) && ctf_get_current_time() > strtotime( $notification['end'] ) ) ) {
233
+ unset( $notifications[ $key ] );
234
+ }
235
+ }
236
+
237
+ return $notifications;
238
+ }
239
+
240
+ /**
241
+ * Get notification data.
242
+ *
243
+ * @since 1.7/1.11
244
+ *
245
+ * @return array
246
+ */
247
+ public function get() {
248
+ if ( ! $this->has_access() ) {
249
+ return array();
250
+ }
251
+
252
+ $option = $this->get_option();
253
+
254
+ // Update notifications using async task.
255
+ if ( empty( $option['update'] ) || ctf_get_current_time() > $option['update'] + DAY_IN_SECONDS ) {
256
+ $this->update();
257
+ }
258
+
259
+ $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
260
+ $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
261
+
262
+ // If there is a new user notification, add it to the beginning of the notification list
263
+ $ctf_newuser = new CTF_New_User();
264
+ $newuser_notifications = $ctf_newuser->get();
265
+
266
+ if ( ! empty( $newuser_notifications ) ) {
267
+ $events = array_merge( $newuser_notifications, $events );
268
+ }
269
+
270
+ return array_merge( $events, $feed );
271
+ }
272
+
273
+ /**
274
+ * Get notification count.
275
+ *
276
+ * @since 1.7/1.11
277
+ *
278
+ * @return int
279
+ */
280
+ public function get_count() {
281
+ return count( $this->get() );
282
+ }
283
+
284
+ /**
285
+ * Add a manual notification event.
286
+ *
287
+ * @since 1.7/1.11
288
+ *
289
+ * @param array $notification Notification data.
290
+ */
291
+ public function add( $notification ) {
292
+ if ( empty( $notification['id'] ) ) {
293
+ return;
294
+ }
295
+
296
+ $option = $this->get_option();
297
+
298
+ if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
299
+ return;
300
+ }
301
+
302
+ foreach ( $option['events'] as $item ) {
303
+ if ( $item['id'] === $notification['id'] ) {
304
+ return;
305
+ }
306
+ }
307
+
308
+ $notification = $this->verify( array( $notification ) );
309
+
310
+ update_option(
311
+ 'ctf_notifications',
312
+ array(
313
+ 'update' => $option['update'],
314
+ 'feed' => $option['feed'],
315
+ 'events' => array_merge( $notification, $option['events'] ),
316
+ 'dismissed' => $option['dismissed'],
317
+ )
318
+ );
319
+ }
320
+
321
+ /**
322
+ * Update notification data from feed.
323
+ *
324
+ * @since 1.7/1.11
325
+ */
326
+ public function update() {
327
+ $feed = $this->fetch_feed();
328
+ $option = $this->get_option();
329
+
330
+ update_option(
331
+ 'ctf_notifications',
332
+ array(
333
+ 'update' => ctf_get_current_time(),
334
+ 'feed' => $feed,
335
+ 'events' => $option['events'],
336
+ 'dismissed' => $option['dismissed'],
337
+ )
338
+ );
339
+ }
340
+
341
+ /**
342
+ * Admin area Form Overview enqueues.
343
+ *
344
+ * @since 1.7/1.11
345
+ */
346
+ public function enqueues() {
347
+ if ( ! $this->has_access() ) {
348
+ return;
349
+ }
350
+
351
+ $notifications = $this->get();
352
+
353
+ if ( empty( $notifications ) ) {
354
+ return;
355
+ }
356
+
357
+ $min = '';
358
+
359
+ wp_enqueue_style(
360
+ 'ctf-admin-notifications',
361
+ CTF_PLUGIN_URL . "css/admin-notifications{$min}.css",
362
+ array(),
363
+ CTF_VERSION
364
+ );
365
+
366
+ wp_enqueue_script(
367
+ 'ctf-admin-notifications',
368
+ CTF_PLUGIN_URL . "js/admin-notifications{$min}.js",
369
+ array( 'jquery' ),
370
+ CTF_VERSION,
371
+ true
372
+ );
373
+
374
+ wp_localize_script( 'ctf-admin-notifications', 'ctf_admin', array(
375
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
376
+ 'nonce' => wp_create_nonce( 'ctf-admin' )
377
+ )
378
+ );
379
+ }
380
+
381
+ /**
382
+ * Fields from the remote source contain placeholders to allow
383
+ * some messages to be used for multiple plugins.
384
+ *
385
+ * @param $content string
386
+ * @param $notification array
387
+ *
388
+ * @return string
389
+ *
390
+ * @since 1.7/1.11
391
+ */
392
+ public function replace_merge_fields( $content, $notification ) {
393
+ $merge_fields = array(
394
+ '{plugin}' => 'Custom Twitter Feeds',
395
+ '{amount}' => isset( $notification['amount'] ) ? $notification['amount'] : '',
396
+ '{platform}' => 'Twitter',
397
+ '{lowerplatform}' => 'twitter',
398
+ '{review-url}' => 'https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/',
399
+ '{slug}' => 'custom-twitter-feed',
400
+ '{campaign}' => 'twitter-free'
401
+ );
402
+
403
+ if ( ctf_is_pro_version() ) {
404
+ $merge_fields['{campaign}'] = 'twitter-pro';
405
+ $merge_fields['{plugin}'] = 'Custom Twitter Feeds Pro';
406
+ }
407
+
408
+ foreach ( $merge_fields as $find => $replace ) {
409
+ $content = str_replace( $find, $replace, $content );
410
+ }
411
+
412
+ return $content;
413
+ }
414
+
415
+ /**
416
+ * Output notifications on Custom Twitter Feed admin area.
417
+ *
418
+ * @since 1.7/1.11
419
+ */
420
+ public function output() {
421
+ $notifications = $this->get();
422
+
423
+ if ( empty( $notifications ) ) {
424
+ return;
425
+ }
426
+
427
+ $notifications_html = '';
428
+ $current_class = ' current';
429
+ $content_allowed_tags = array(
430
+ 'em' => array(),
431
+ 'strong' => array(),
432
+ 'span' => array(
433
+ 'style' => array(),
434
+ ),
435
+ 'a' => array(
436
+ 'href' => array(),
437
+ 'target' => array(),
438
+ 'rel' => array(),
439
+ ),
440
+ );
441
+
442
+ foreach ( $notifications as $notification ) {
443
+
444
+ // Buttons HTML.
445
+ $buttons_html = '';
446
+ if ( ! empty( $notification['btns'] ) && is_array( $notification['btns'] ) ) {
447
+ foreach ( $notification['btns'] as $btn_type => $btn ) {
448
+ if ( is_array( $btn['url'] ) ) {
449
+ $btn['url'] = add_query_arg( $btn['url'] );
450
+ }
451
+ if ( ! empty( $btn['attr'] ) ) {
452
+ $btn['target'] = '_blank';
453
+ }
454
+ $buttons_html .= sprintf(
455
+ '<a href="%1$s" class="button button-%2$s"%3$s>%4$s</a>',
456
+ ! empty( $btn['url'] ) ? esc_url( $this->replace_merge_fields( $btn['url'], $notification ) ) : '',
457
+ $btn_type === 'primary' ? 'primary' : 'secondary',
458
+ ! empty( $btn['target'] ) && $btn['target'] === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '',
459
+ ! empty( $btn['text'] ) ? sanitize_text_field( $btn['text'] ) : ''
460
+ );
461
+ }
462
+ $buttons_html = ! empty( $buttons_html ) ? '<div class="buttons">' . $buttons_html . '</div>' : '';
463
+ }
464
+
465
+ if ( empty( $notification['image'] ) ) {
466
+ $image_html = '<div class="bell">';
467
+
468
+ $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" width="42" height="48" viewBox="0 0 42 48"><defs><style>.a{fill:#777;}.b{fill:#ca4a1f;}</style></defs><path class="a" d="M23-79a6.005,6.005,0,0,1-6-6h10.06a12.066,12.066,0,0,0,1.791,1.308,6.021,6.021,0,0,1-2.077,3.352A6.008,6.008,0,0,1,23-79Zm1.605-9H5.009a2.955,2.955,0,0,1-2.173-.923A3.088,3.088,0,0,1,2-91a2.919,2.919,0,0,1,.807-2.036c.111-.12.229-.243.351-.371a14.936,14.936,0,0,0,3.126-4.409A23.283,23.283,0,0,0,8.007-107.5a14.846,14.846,0,0,1,.906-5.145,14.5,14.5,0,0,1,2.509-4.324A15.279,15.279,0,0,1,20-122.046V-124a3,3,0,0,1,3-3,3,3,0,0,1,3,3v1.954a15.28,15.28,0,0,1,8.58,5.078,14.5,14.5,0,0,1,2.509,4.324,14.846,14.846,0,0,1,.906,5.145c0,.645.016,1.281.047,1.888A12.036,12.036,0,0,0,35-106a11.921,11.921,0,0,0-8.485,3.515A11.923,11.923,0,0,0,23-94a12,12,0,0,0,1.6,6Z" transform="translate(-2 127)"/><circle class="b" cx="9" cy="9" r="9" transform="translate(24 24)"/></svg>';
469
+ $image_html .= '</div>';
470
+ } else {
471
+ if ( $notification['image'] === 'balloon'
472
+ || $notification['id'] === 'review'
473
+ || $notification['id'] === 'discount') {
474
+ $image_html = '<div class="bell">';
475
+
476
+ $image_html .= '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1438 1878" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2">';
477
+ $image_html .= ' <path d="M671.51004 492.9884C539.9423 433.8663 402.90125 345.5722 274.97656 304.47286c45.45163 108.39592 83.81332 223.88017 123.51 338.03105C319.308 702.00293 226.8217 748.19258 138.46278 798.51607c75.1914 74.32371 181.67968 117.34651 266.52444 182.01607-67.96124 83.86195-201.48527 171.01801-234.02107 247.01998 140.6922-17.6268 304.63688-46.21031 435.53794-52.00418 28.76427 144.58328 43.5987 303.09763 84.50756 435.53713 60.92033-175.26574 116.0014-356.37317 188.51594-520.0451 111.90644 46.2857 248.29012 102.72607 357.52902 130.01188-76.64636-107.5347-146.59346-221.76948-214.5166-338.02903 100.51162-72.83876 202.1718-144.52451 299.02538-221.02092-136.89514-12.61229-278.73428-20.28827-422.53618-25.99865-22.85288-148.33212-16.84826-325.51604-52.005-461.53983-53.19327 111.4882-115.96694 213.39155-175.51418 318.52497m65.00513 1228.60735c-18.0795 77.37586 41.4876 109.11326 32.50298 156.01215-58.8141-20.268-103.0576-30.67962-182.01567-19.50203 2.47018-60.37036 56.76662-68.90959 45.50432-143.0108C-208.90184 1619.4318-210.59186 99.02478 626.00572 5.44992c1046.0409-117.00405 1078.86445 1689.2596 110.50945 1716.14582" fill="#e34f0e"/>';
478
+ $image_html .= ' <path d="M847.02422 174.46342c35.15674 136.02379 29.15212 313.20771 52.0046 461.53578 143.8023 5.71443 285.63982 13.38636 422.53658 26.0027-96.85317 76.4964-198.51497 148.18216-299.02579 221.0189 67.92355 116.26239 137.87024 230.49432 214.51864 338.03024-109.24093-27.28662-245.62461-83.72577-357.53106-130.01269-72.51454 163.67274-127.5956 344.78017-188.51553 520.0459-40.90926-132.4395-55.74329-290.95384-84.50796-435.53712-130.90066 5.79549-294.84493 34.37738-435.53754 52.00418 32.5358-76.00075 166.05902-163.156 234.02026-247.02038-84.84516-64.67037-191.33222-107.69074-266.52363-182.01486 88.35892-50.32349 180.8436-96.51314 260.02295-156.0162-39.69708-114.14683-78.05674-229.63108-123.50878-338.027C402.89923 345.5722 539.9423 433.86629 671.51004 492.98839c59.54684-105.13342 122.3209-207.03677 175.51418-318.52497" fill="#fff"/>';
479
+ $image_html .= '</svg>';
480
+ } else {
481
+ $image_html = '<div class="thumb">';
482
+ $img_src = SBY_PLUGIN_URL . 'img/' . sanitize_text_field( $notification['image'] );
483
+ $image_html .= '<img src="'.esc_url( $img_src ).'" alt="notice">';
484
+
485
+ if ( isset( $notification['image_overlay'] ) ) {
486
+ $image_html .= '<div class="img-overlay">'. esc_html( str_replace( '%', '%%', $notification['image_overlay'] ) ).'</div>';
487
+ }
488
+ }
489
+ $image_html .= '</div>';
490
+
491
+ }
492
+
493
+ // Notification HTML.
494
+ $notifications_html .= sprintf(
495
+ '<div class="message%5$s" data-message-id="%4$s">' . $image_html . '
496
+ <h3 class="title">%1$s</h3>
497
+ <p class="content">%2$s</p>
498
+ %3$s
499
+ </div>',
500
+ ! empty( $notification['title'] ) ? $this->replace_merge_fields( sanitize_text_field( $notification['title'] ), $notification ) : '',
501
+ ! empty( $notification['content'] ) ? wp_kses( $this->replace_merge_fields( $notification['content'], $notification ), $content_allowed_tags ) : '',
502
+ $buttons_html,
503
+ ! empty( $notification['id'] ) ? esc_attr( sanitize_text_field( $notification['id'] ) ) : 0,
504
+ $current_class
505
+ );
506
+
507
+ // Only first notification is current.
508
+ $current_class = '';
509
+ }
510
+ ?>
511
+
512
+ <div id="ctf-notifications">
513
+ <a class="dismiss" title="<?php echo esc_attr__( 'Dismiss this message', 'custom-twitter-feeds' ); ?>"><i class="fa fa-times-circle" aria-hidden="true"></i></a>
514
+
515
+ <div class="navigation">
516
+ <a class="prev disabled" title="<?php echo esc_attr__( 'Previous message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-left fa-w-10"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z" class=""></path></svg></a>
517
+ <a class="next disabled" title="<?php echo esc_attr__( 'Next message', 'custom-twitter-feeds' ); ?>"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-right fa-w-10"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z" class=""></path></svg></a>
518
+ </div>
519
+
520
+ <div class="messages">
521
+ <?php echo $notifications_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
522
+ </div>
523
+ </div>
524
+ <?php
525
+ }
526
+
527
+ /**
528
+ * Dismiss notification via AJAX. If it's a new user message, also dismiss it
529
+ * on all admin pages.
530
+ *
531
+ * @since 1.7/1.11
532
+ */
533
+ public function dismiss() {
534
+ // Run a security check.
535
+ check_ajax_referer( 'ctf-admin', 'nonce' );
536
+
537
+ // Check for access and required param.
538
+ if ( ! $this->has_access() || empty( $_POST['id'] ) ) {
539
+ wp_send_json_error();
540
+ }
541
+
542
+ $id = sanitize_text_field( wp_unslash( $_POST['id'] ) );
543
+
544
+ if ( $id === 'review' ) {
545
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
546
+
547
+ update_option( 'ctf_rating_notice', 'dismissed', false );
548
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
549
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
550
+ } elseif ( $id === 'discount' ) {
551
+ update_user_meta( get_current_user_id(), 'ctf_ignore_new_user_sale_notice', 'always' );
552
+
553
+ $current_month_number = (int)date('n', ctf_get_current_time() );
554
+ $not_early_in_the_year = ($current_month_number > 5);
555
+
556
+ if ( $not_early_in_the_year ) {
557
+ update_user_meta( get_current_user_id(), 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
558
+ }
559
+ }
560
+
561
+ $option = $this->get_option();
562
+ $type = is_numeric( $id ) ? 'feed' : 'events';
563
+
564
+ $option['dismissed'][] = $id;
565
+ $option['dismissed'] = array_unique( $option['dismissed'] );
566
+
567
+ // Remove notification.
568
+ if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
569
+ foreach ( $option[ $type ] as $key => $notification ) {
570
+ if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons
571
+ unset( $option[ $type ][ $key ] );
572
+ break;
573
+ }
574
+ }
575
+ }
576
+
577
+ update_option( 'ctf_notifications', $option );
578
+
579
+ wp_send_json_success();
580
+ }
581
+ }
inc/notices.php CHANGED
@@ -10,250 +10,10 @@ function ctf_get_current_time() {
10
 
11
  // generates the html for the admin notices
12
  function ctf_notices_html() {
13
-
14
- if ( function_exists( 'sbi_notices_html' ) || function_exists( 'cff_notices_html' ) ) {
15
- return;
16
- }
17
-
18
- $current_screen = get_current_screen();
19
- $is_plugins_page = isset( $current_screen->id ) && $current_screen->id === 'plugins';
20
- $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
21
- //Only show to admins
22
- if ( ! current_user_can( 'manage_options' ) ) {
23
- return;
24
- }
25
-
26
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
27
- $current_time = ctf_get_current_time();
28
- $ctf_bfcm_discount_code = 'happysmashgiving' . date('Y', $current_time );
29
-
30
- // reset everything for testing
31
- if ( false ) {
32
- global $current_user;
33
- $user_id = $current_user->ID;
34
- delete_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
35
- //delete_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
36
- //$ctf_statuses_option = array( 'first_install' => strtotime( 'December 8, 2019' ) );
37
- //$ctf_statuses_option = array( 'first_install' => time() );
38
-
39
- //update_option( 'ctf_statuses', $ctf_statuses_option, false );
40
  //delete_option( 'ctf_rating_notice');
41
  //delete_transient( 'instagram_feed_rating_notice_waiting' );
42
-
43
- //set_transient( 'instagram_feed_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
44
- //update_option( 'ctf_rating_notice', 'pending', false );
45
- }
46
-
47
- //$ctf_statuses_option['rating_notice_dismissed'] = time();
48
- //update_option( 'ctf_statuses', $ctf_statuses_option, false );
49
- // rating notice logic
50
- $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
51
- $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
52
- $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
53
-
54
- // black friday cyber monday logic
55
- $thanksgiving_this_year = ctf_get_future_date( 11, date('Y', $current_time ), 4, 4, 1 );
56
- $one_week_before_black_friday_this_year = $thanksgiving_this_year - 7*24*60*60;
57
- $one_day_after_cyber_monday_this_year = $thanksgiving_this_year + 5*24*60*60;
58
- $has_been_two_days_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 2*24*60*60) < $current_time : true;
59
-
60
- $could_show_bfcm_discount = ($current_time > $one_week_before_black_friday_this_year && $current_time < $one_day_after_cyber_monday_this_year);
61
- $should_show_bfcm_discount = false;
62
- if ( $could_show_bfcm_discount && $has_been_two_days_since_rating_dismissal ) {
63
- global $current_user;
64
- $user_id = $current_user->ID;
65
-
66
- $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
67
- $ignore_bfcm_sale_notice_meta = isset( $ignore_bfcm_sale_notice_meta[0] ) ? $ignore_bfcm_sale_notice_meta[0] : '';
68
-
69
- /* Check that the user hasn't already clicked to ignore the message */
70
- $should_show_bfcm_discount = ($ignore_bfcm_sale_notice_meta !== 'always' && $ignore_bfcm_sale_notice_meta !== date( 'Y', $current_time ));
71
- }
72
-
73
- // new user discount logic
74
- $in_new_user_month_range = true;
75
- $should_show_new_user_discount = false;
76
- $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 30*24*60*60) < $current_time + 1: true;
77
-
78
- if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
79
- global $current_user;
80
- $user_id = $current_user->ID;
81
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
82
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
83
-
84
- if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
85
- $should_show_new_user_discount = true;
86
- }
87
- } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal ) {
88
- global $current_user;
89
- $user_id = $current_user->ID;
90
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
91
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
92
-
93
- if ( $ignore_new_user_sale_notice_meta !== 'always'
94
- && isset( $ctf_statuses_option['first_install'] )
95
- && $current_time > (int)$ctf_statuses_option['first_install'] + 60*60*24*30 ) {
96
- $should_show_new_user_discount = true;
97
- }
98
- }
99
-
100
- // for debugging
101
- if ( false ) {
102
- global $current_user;
103
- $user_id = $current_user->ID;
104
- $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
105
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
106
-
107
- var_dump( 'new user rating option', $ctf_rating_notice_option );
108
- var_dump( 'new user rating transient', $ctf_rating_notice_waiting );
109
-
110
- var_dump( 'should show new user rating notice?', $should_show_rating_notice );
111
-
112
- var_dump( 'new user discount month range?', $in_new_user_month_range );
113
- var_dump( 'should show new user discount?', $should_show_new_user_discount );
114
-
115
- var_dump( 'Thanksgiving this year?', date('m/d/Y', $thanksgiving_this_year ) );
116
-
117
- var_dump( 'could show bfcm discount?', $could_show_bfcm_discount );
118
- var_dump( 'rating was dismissed?', date('m/d/Y', $ctf_statuses_option['rating_notice_dismissed'] ) );
119
-
120
- var_dump( 'should show bfcm discount?', $should_show_bfcm_discount );
121
-
122
- var_dump( 'ignore_bfcm_sale_notice_meta', $ignore_bfcm_sale_notice_meta );
123
- var_dump( 'ignore_new_user_sale_notice_meta', $ignore_new_user_sale_notice_meta );
124
-
125
- var_dump( $ctf_statuses_option );
126
- }
127
-
128
-
129
- if ( $should_show_rating_notice ) {
130
- $other_notice_html = '';
131
- $dismiss_url = add_query_arg( 'ctf_ignore_rating_notice_nag', '1' );
132
- $later_url = add_query_arg( 'ctf_ignore_rating_notice_nag', 'later' );
133
- if ( $should_show_bfcm_discount ) {
134
- $other_notice_html = '<p class="ctf_other_notice">' . __( 'PS. We currently have a <a href="https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=adminnotice&utm_medium=bfcm&discount='.$ctf_bfcm_discount_code.'" target="_blank"><b style="font-weight: 700;">Black Friday deal</b></a> for 60% off the Pro version!', 'custom-twitter-feed' ) . '</p>';
135
-
136
- $dismiss_url = add_query_arg( array(
137
- 'ctf_ignore_rating_notice_nag' => '1',
138
- 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
139
- )
140
- );
141
- $later_url = add_query_arg( array(
142
- 'ctf_ignore_rating_notice_nag' => 'later',
143
- 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
144
- )
145
- );
146
- }
147
-
148
- echo "
149
- <div class='ctf_notice ctf_review_notice'>
150
- <img src='". CTF_PLUGIN_URL . 'img/ctf-icon.jpg' ."' alt='" . __( 'Custom Twitter Feed', 'custom-twitter-feed' ) . "'>
151
- <div class='ctf-notice-text'>
152
- <p style='padding-top: 4px;'>" . __( "It's great to see that you've been using the <strong style='font-weight: 700;'>Custom Twitter Feeds</strong> plugin for a while now. Hopefully you're happy with it!&nbsp; If so, would you consider leaving a positive review? It really helps to support the plugin and helps others to discover it too!", 'custom-twitter-feed' ) . "</p>
153
- <p class='links'";
154
- if( $should_show_bfcm_discount ) echo " style='margin-top: 0 !important;'";
155
- echo ">
156
- <a class='ctf_notice_dismiss' href='https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/' target='_blank'>" . __( 'Sure, I\'d love to!', 'custom-twitter-feed' ) . "</a>
157
- &middot;
158
- <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'No thanks', 'custom-twitter-feed' ) . "</a>
159
- &middot;
160
- <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'I\'ve already given a review', 'custom-twitter-feed' ) . "</a>
161
- &middot;
162
- <a class='ctf_notice_dismiss' href='" .esc_url( $later_url ). "'>" . __( 'Ask Me Later', 'custom-twitter-feed' ) . "</a>
163
- </p>"
164
- . $other_notice_html .
165
- "</div>
166
- <a class='ctf_notice_close' href='" .esc_url( $dismiss_url ). "'><i class='fa fa-close'></i></a>
167
- </div>";
168
-
169
- } elseif ( $should_show_new_user_discount ) {
170
- global $current_user;
171
- $user_id = $current_user->ID;
172
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
173
- if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
174
-
175
- echo "
176
- <div class='ctf_notice ctf_review_notice ctf_new_user_sale_notice'>
177
- <img src='" . CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' . "' alt='Custom Twitter Feed'>
178
- <div class='ctf-notice-text'>
179
- <p>" . __( '<b style="font-weight: 700;">Exclusive offer!</b> We don\'t run promotions very often, but for a limited time we\'re offering <b style="font-weight: 700;">60% off</b> our Pro version to all users of our free Custom Twitter Feeds plugin.', 'custom-twitter-feed' ) . "</p>
180
- <p class='ctf-links'>
181
- <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=adminnotice&utm_medium=newuser&discount=twitterthankyou' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
182
- <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
183
-
184
- </p>
185
- </div>
186
- <a class='ctf_new_user_sale_notice_close' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'><i class='fa fa-close'></i></a>
187
- </div>
188
- ";
189
- }
190
-
191
- } elseif ( $should_show_bfcm_discount ) {
192
-
193
- echo "
194
- <div class='ctf_notice ctf_review_notice ctf_bfcm_sale_notice'>
195
- <img src='". CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' ."' alt='Custom Twitter Feed'>
196
- <div class='ctf-notice-text'>
197
- <p>" . __( '<b style="font-weight: 700;">Black Friday/Cyber Monday Deal!</b> Thank you for using our free Custom Twitter Feeds plugin. For a limited time, we\'re offering <b style="font-weight: 700;">60% off</b> the Pro version for all of our users.', 'custom-twitter-feed' ) . "</p>
198
- <p class='ctf-links'>
199
- <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_campaign=twitter-free&utm_source=adminnotice&utm_medium=bfcmmain&discount=".$ctf_bfcm_discount_code."' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
200
- <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
201
- </p>
202
- </div>
203
- <a class='ctf_bfcm_sale_notice_close' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'><i class='fa fa-close'></i></a>
204
- </div>
205
- ";
206
-
207
- }
208
-
209
- }
210
- add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
211
-
212
- function ctf_process_nags() {
213
-
214
- global $current_user;
215
- $user_id = $current_user->ID;
216
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
217
-
218
- if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
219
- if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
220
- update_option( 'ctf_rating_notice', 'dismissed', false );
221
- $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
222
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
223
-
224
- } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
225
- set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
226
- update_option( 'ctf_rating_notice', 'pending', false );
227
- }
228
- }
229
-
230
- if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
231
- $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
232
- if ( $response === 'always' ) {
233
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
234
-
235
- $current_month_number = (int)date('n', ctf_get_current_time() );
236
- $not_early_in_the_year = ($current_month_number > 5);
237
-
238
- if ( $not_early_in_the_year ) {
239
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
240
- }
241
-
242
- }
243
- }
244
-
245
- if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
246
- $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
247
- if ( $response === 'always' ) {
248
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
249
- } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
250
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
251
- }
252
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
253
- }
254
-
255
  }
256
- add_action( 'admin_init', 'ctf_process_nags' );
257
 
258
  function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
259
  if ( $direction > 0 ) {
10
 
11
  // generates the html for the admin notices
12
  function ctf_notices_html() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  //delete_option( 'ctf_rating_notice');
14
  //delete_transient( 'instagram_feed_rating_notice_waiting' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
+ //add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
17
 
18
  function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
19
  if ( $direction > 0 ) {
js/admin-notifications.js ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * CTF Admin Notifications.
3
+ *
4
+ * @since 2.18
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ var CTFAdminNotifications = window.CTFAdminNotifications || ( function( document, window, $ ) {
10
+
11
+ /**
12
+ * Elements holder.
13
+ *
14
+ * @since 2.18
15
+ *
16
+ * @type {object}
17
+ */
18
+ var el = {
19
+
20
+ $notifications: $( '#ctf-notifications' ),
21
+ $nextButton: $( '#ctf-notifications .navigation .next' ),
22
+ $prevButton: $( '#ctf-notifications .navigation .prev' ),
23
+ $adminBarCounter: $( '#wp-admin-bar-wpforms-menu .ctf-menu-notification-counter' ),
24
+ $adminBarMenuItem: $( '#wp-admin-bar-ctf-notifications' ),
25
+
26
+ };
27
+
28
+ /**
29
+ * Public functions and properties.
30
+ *
31
+ * @since 2.18
32
+ *
33
+ * @type {object}
34
+ */
35
+ var app = {
36
+
37
+ /**
38
+ * Start the engine.
39
+ *
40
+ * @since 2.18
41
+ */
42
+ init: function() {
43
+ el.$notifications.find( '.messages a').each(function() {
44
+ if ($(this).attr('href').indexOf('dismiss=') > -1 ) {
45
+ $(this).addClass('button-dismiss');
46
+ }
47
+ })
48
+
49
+ $( app.ready );
50
+ },
51
+
52
+ /**
53
+ * Document ready.
54
+ *
55
+ * @since 2.18
56
+ */
57
+ ready: function() {
58
+
59
+ app.updateNavigation();
60
+ app.events();
61
+ },
62
+
63
+ /**
64
+ * Register JS events.
65
+ *
66
+ * @since 2.18
67
+ */
68
+ events: function() {
69
+
70
+ el.$notifications
71
+ .on( 'click', '.dismiss', app.dismiss )
72
+ .on( 'click', '.button-dismiss', app.buttonDismiss )
73
+ .on( 'click', '.next', app.navNext )
74
+ .on( 'click', '.prev', app.navPrev );
75
+ },
76
+
77
+ /**
78
+ * Click on a dismiss button.
79
+ *
80
+ * @since 2.18
81
+ */
82
+ buttonDismiss: function( event ) {
83
+ event.preventDefault();
84
+ app.dismiss();
85
+ },
86
+
87
+ /**
88
+ * Click on the Dismiss notification button.
89
+ *
90
+ * @since 2.18
91
+ *
92
+ * @param {object} event Event object.
93
+ */
94
+ dismiss: function( event ) {
95
+
96
+ if ( el.$currentMessage.length === 0 ) {
97
+ return;
98
+ }
99
+
100
+ // Update counter.
101
+ var count = parseInt( el.$adminBarCounter.text(), 10 );
102
+ if ( count > 1 ) {
103
+ --count;
104
+ el.$adminBarCounter.html( '<span>' + count + '</span>' );
105
+ } else {
106
+ el.$adminBarCounter.remove();
107
+ el.$adminBarMenuItem.remove();
108
+ }
109
+
110
+ // Remove notification.
111
+ var $nextMessage = el.$nextMessage.length < 1 ? el.$prevMessage : el.$nextMessage,
112
+ messageId = el.$currentMessage.data( 'message-id' );
113
+
114
+ if ( $nextMessage.length === 0 ) {
115
+ el.$notifications.remove();
116
+ } else {
117
+ el.$currentMessage.remove();
118
+ $nextMessage.addClass( 'current' );
119
+ app.updateNavigation();
120
+ }
121
+
122
+ // AJAX call - update option.
123
+ var data = {
124
+ action: 'ctf_dashboard_notification_dismiss',
125
+ nonce: ctf_admin.nonce,
126
+ id: messageId,
127
+ };
128
+
129
+ $.post( ctf_admin.ajax_url, data, function( res ) {
130
+
131
+ if ( ! res.success ) {
132
+ //CTFAdmin.debug( res );
133
+ }
134
+ } ).fail( function( xhr, textStatus, e ) {
135
+
136
+ //CTFAdmin.debug( xhr.responseText );
137
+ } );
138
+ },
139
+
140
+ /**
141
+ * Click on the Next notification button.
142
+ *
143
+ * @since 2.18
144
+ *
145
+ * @param {object} event Event object.
146
+ */
147
+ navNext: function( event ) {
148
+
149
+ if ( el.$nextButton.hasClass( 'disabled' ) ) {
150
+ return;
151
+ }
152
+
153
+ el.$currentMessage.removeClass( 'current' );
154
+ el.$nextMessage.addClass( 'current' );
155
+
156
+ app.updateNavigation();
157
+ },
158
+
159
+ /**
160
+ * Click on the Previous notification button.
161
+ *
162
+ * @since 2.18
163
+ *
164
+ * @param {object} event Event object.
165
+ */
166
+ navPrev: function( event ) {
167
+
168
+ if ( el.$prevButton.hasClass( 'disabled' ) ) {
169
+ return;
170
+ }
171
+
172
+ el.$currentMessage.removeClass( 'current' );
173
+ el.$prevMessage.addClass( 'current' );
174
+
175
+ app.updateNavigation();
176
+ },
177
+
178
+ /**
179
+ * Update navigation buttons.
180
+ *
181
+ * @since 2.18
182
+ */
183
+ updateNavigation: function() {
184
+
185
+ el.$currentMessage = el.$notifications.find( '.message.current' );
186
+ el.$nextMessage = el.$currentMessage.next( '.message' );
187
+ el.$prevMessage = el.$currentMessage.prev( '.message' );
188
+
189
+ if ( el.$nextMessage.length === 0 ) {
190
+ el.$nextButton.addClass( 'disabled' );
191
+ } else {
192
+ el.$nextButton.removeClass( 'disabled' );
193
+ }
194
+
195
+ if ( el.$prevMessage.length === 0 ) {
196
+ el.$prevButton.addClass( 'disabled' );
197
+ } else {
198
+ el.$prevButton.removeClass( 'disabled' );
199
+ }
200
+ },
201
+ };
202
+
203
+ return app;
204
+
205
+ }( document, window, jQuery ) );
206
+
207
+ // Initialize.
208
+ CTFAdminNotifications.init();
uninstall.php CHANGED
@@ -17,6 +17,8 @@ if ( ! $ctf_preserve_settings ) {
17
  delete_option( 'ctf_version' );
18
  delete_option( 'ctf_rating_notice' );
19
  delete_transient( 'custom_twitter_feeds_rating_notice_waiting' );
 
 
20
 
21
  // delete tweet cache in transients
22
  global $wpdb;
17
  delete_option( 'ctf_version' );
18
  delete_option( 'ctf_rating_notice' );
19
  delete_transient( 'custom_twitter_feeds_rating_notice_waiting' );
20
+ delete_option( 'ctf_notifications' );
21
+ delete_option( 'ctf_newuser_notifications' );
22
 
23
  // delete tweet cache in transients
24
  global $wpdb;
views/admin/main.php CHANGED
@@ -1,5 +1,7 @@
1
  <div id="ctf-admin" class="wrap">
2
- <?php
 
 
3
  $lite_notice_dismissed = get_transient( 'twitter_feed_dismiss_lite' );
4
 
5
  if ( ! $lite_notice_dismissed ) :
1
  <div id="ctf-admin" class="wrap">
2
+ <?php do_action( 'ctf_admin_overview_before_title' ); ?>
3
+
4
+ <?php
5
  $lite_notice_dismissed = get_transient( 'twitter_feed_dismiss_lite' );
6
 
7
  if ( ! $lite_notice_dismissed ) :