-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclass-badges.php
More file actions
258 lines (225 loc) · 7.32 KB
/
class-badges.php
File metadata and controls
258 lines (225 loc) · 7.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<?php
/**
* Handle user badges.
*
* @package Progress_Planner
*/
namespace Progress_Planner;
use Progress_Planner\Badges\Monthly;
/**
* Badges class.
*/
class Badges {
/**
* Content badges.
*
* @var \Progress_Planner\Badges\Badge[]
*/
private $content = [];
/**
* Maintenance badges.
*
* @var \Progress_Planner\Badges\Badge[]
*/
private $maintenance = [];
/**
* Monthly badges.
*
* @var \Progress_Planner\Badges\Badge[]
*/
private $monthly = [];
/**
* Monthly badges flat.
*
* @var \Progress_Planner\Badges\Badge[]
*/
private $monthly_flat = [];
/**
* Latest completed badge.
*
* @var \Progress_Planner\Badges\Badge|null
*/
private $latest_completed_badge;
/**
* Constructor.
*/
public function __construct() {
$this->content = [
\progress_planner()->get_badges__content__content_curator(),
\progress_planner()->get_badges__content__revision_ranger(),
\progress_planner()->get_badges__content__purposeful_publisher(),
];
$this->maintenance = [
\progress_planner()->get_badges__maintenance__progress_padawan(),
\progress_planner()->get_badges__maintenance__maintenance_maniac(),
\progress_planner()->get_badges__maintenance__super_site_specialist(),
];
// Init monthly badges.
$this->monthly = Monthly::get_instances();
foreach ( $this->monthly as $monthly_year_badges ) {
$this->monthly_flat = \array_merge( $this->monthly_flat, $monthly_year_badges );
}
\add_action( 'progress_planner_suggested_task_completed', [ $this, 'clear_monthly_progress' ] );
\add_action( 'progress_planner_activity_content_publish_saved', [ $this, 'clear_content_progress' ] );
}
/**
* Get the badges for a context.
*
* @param string $context The badges context (content|maintenance|monthly).
*
* @return \Progress_Planner\Badges\Badge[]
*/
public function get_badges( $context ) {
// phpcs:ignore Generic.Commenting.DocComment.MissingShort -- Inline @var for PHPStan.
/** @var \Progress_Planner\Badges\Badge[] $badges */
$badges = isset( $this->$context ) ? $this->$context : [];
/**
* Filter the badges for a context.
*
* @param \Progress_Planner\Badges\Badge[] $badges The badges.
* @param string $context The badges context.
*
* @return \Progress_Planner\Badges\Badge[]
*/
return \apply_filters( 'progress_planner_badges', $badges, $context );
}
/**
* Get a single badge.
*
* @param string $badge_id The badge ID.
*
* @return \Progress_Planner\Badges\Badge|null
*/
public function get_badge( $badge_id ) {
foreach ( [ 'content', 'maintenance', 'monthly_flat' ] as $context ) {
foreach ( $this->$context as $badge ) {
if ( $badge->get_id() === $badge_id ) {
return $badge;
}
}
}
/**
* Filter for retrieving a single badge by ID.
*
* Allows external plugins to provide custom badges.
*
* @param \Progress_Planner\Badges\Badge|null $badge The badge object or null.
* @param string $badge_id The badge ID.
*
* @return \Progress_Planner\Badges\Badge|null
*/
return \apply_filters( 'progress_planner_get_badge', null, $badge_id );
}
/**
* Clear the progress of all monthly badges.
*
* @param string $activity_id The activity ID.
*
* @return void
*/
public function clear_monthly_progress( $activity_id ) {
$activities = \progress_planner()->get_activities__query()->query_activities(
[
'category' => 'suggested_task',
'type' => 'completed',
'data_id' => (string) $activity_id,
]
);
if ( empty( $activities ) ) {
return;
}
// Clear monthly saved progress.
$badge_id = Monthly::get_badge_id_from_date( $activities[0]->date );
$monthly_badge = $this->get_badge( $badge_id );
if ( $monthly_badge ) {
// Clear the progress.
$monthly_badge->clear_progress();
// Save the progress.
$monthly_badge->get_progress();
}
}
/**
* Clear the progress of all badges.
*
* @return void
*/
public function clear_content_progress() {
// Clear content saved progress.
foreach ( $this->content as $badge ) {
// If the badge is already complete, skip it.
if ( 100 <= $badge->progress_callback()['progress'] ) {
continue;
}
// Delete the badge value so it can be re-calculated.
$badge->clear_progress();
}
}
/**
* Get the latest completed badge across all badge types.
*
* Badge selection algorithm:
* 1. Iterates through all badge contexts (content, maintenance, monthly_flat)
* 2. For each badge, checks if it's 100% complete
* 3. Compares completion dates stored in settings to find the most recent
* 4. Returns the badge with the most recent completion date
*
* The completion date is stored in settings when a badge reaches 100% progress:
* - Format: 'Y-m-d H:i:s' (e.g., '2025-10-31 14:30:00')
* - Compared as Unix timestamps for accurate chronological ordering
* - Later completion dates take precedence (>= comparison ensures newer badges win)
*
* This is used to:
* - Trigger celebrations for newly completed badges
* - Track user progress momentum
*
* @return \Progress_Planner\Badges\Badge|null The most recently completed badge, or null if none completed.
*/
public function get_latest_completed_badge() {
if ( $this->latest_completed_badge ) {
return $this->latest_completed_badge;
}
// Get the settings for badges (stores completion dates).
$settings = \progress_planner()->get_settings()->get( 'badges', [] );
// phpcs:ignore Generic.Commenting.DocComment.MissingShort -- Inline @var for PHPStan.
/** @var string|null $latest_date */
$latest_date = null;
// Loop through all badge contexts to find the most recently completed badge.
foreach ( [ 'content', 'maintenance', 'monthly_flat' ] as $context ) {
foreach ( $this->$context as $badge ) {
// Skip badges that don't have a completion date recorded.
if ( ! isset( $settings[ $badge->get_id() ]['date'] ) ) {
continue;
}
$badge_progress = $badge->get_progress();
// Skip badges that aren't 100% complete.
if ( 100 > (int) $badge_progress['progress'] ) {
continue;
}
// Initialize with the first completed badge found.
if ( null === $latest_date ) {
$this->latest_completed_badge = $badge;
if ( isset( $settings[ $badge->get_id() ]['date'] ) ) {
$latest_date = (string) $settings[ $badge->get_id() ]['date'];
}
continue;
}
// Compare completion dates as Unix timestamps to find the most recent.
// Using >= ensures that if multiple badges complete simultaneously, the last one processed wins.
if ( \DateTime::createFromFormat( 'Y-m-d H:i:s', (string) $settings[ $badge->get_id() ]['date'] )->format( 'U' ) >= \DateTime::createFromFormat( 'Y-m-d H:i:s', $latest_date )->format( 'U' ) ) {
$latest_date = (string) $settings[ $badge->get_id() ]['date'];
$this->latest_completed_badge = $badge;
}
}
}
/**
* Filter the latest completed badge.
*
* @param \Progress_Planner\Badges\Badge|null $badge The latest completed badge.
* @param string|null $latest_date The latest completion date.
*
* @return \Progress_Planner\Badges\Badge|null
*/
$this->latest_completed_badge = \apply_filters( 'progress_planner_latest_completed_badge', $this->latest_completed_badge, $latest_date );
return $this->latest_completed_badge;
}
}