PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.10.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.10.0
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / app / Services / SmartCodeParser.php

SmartCodeParser.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.10.0, at app/Services/SmartCodeParser.php

348 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Services;
4
5 use FluentCommunity\Modules\Course\Model\CourseTopic;
6 use FluentCommunity\Modules\Course\Model\Course;
7 use FluentCommunity\App\Services\ProfileHelper;
8 use FluentCommunity\App\Services\Helper;
9 use FluentCommunity\Framework\Support\Arr;
10
11 class SmartCodeParser
12 {
13 protected static $isHtml = true;
14
15 protected static $store = [
16 'user' => null,
17 'feed' => null,
18 'course' => null,
19 'community' => null
20 ];
21
22 public function parse($templateString, $user, $feed = null, $isHtml = true)
23 {
24 static::$isHtml = $isHtml;
25 static::setData($user, $feed);
26
27 $result = [];
28 $isSingle = false;
29
30 if (!is_array($templateString)) {
31 $isSingle = true;
32 }
33
34 foreach ((array)$templateString as $key => $string) {
35 $result[$key] = $this->parseShortcode($string);
36 }
37
38 if ($isSingle) {
39 return reset($result);
40 }
41
42 return $result;
43 }
44
45 protected static function setData($user, $feed = null)
46 {
47 static::$store['user'] = $user;
48 static::$store['feed'] = $feed;
49 static::$store['course'] = ($feed && $feed->course) ? $feed->course : null;
50 static::$store['community'] = Helper::generalSettings();
51 }
52
53 public function parseShortcode($string)
54 {
55 if (strpos($string, '{{') === false && strpos($string, '##') === false) {
56 return $string;
57 }
58
59 if (static::$isHtml) {
60 $string = $this->resolveHrefPlaceholders($string);
61 }
62
63 return preg_replace_callback('/({{|##)+(.*?)(}}|##)/', function ($matches) {
64 return $this->replace($matches);
65 }, $string);
66 }
67
68 /**
69 * Resolve placeholders inside href="..." attributes to raw URLs.
70 * Prevents nested anchors when URL smartcodes auto-wrap in HTML mode.
71 */
72 protected function resolveHrefPlaceholders($string)
73 {
74 return preg_replace_callback(
75 '/(?<![a-zA-Z-])href\s*=\s*(["\'])([^"\']*(?:\{\{|##)[^"\']*)\1/i',
76 function ($m) {
77 static::$isHtml = false;
78 $resolved = $this->parseShortcode($m[2]);
79 static::$isHtml = true;
80 return 'href=' . $m[1] . esc_url($resolved) . $m[1];
81 },
82 $string
83 );
84 }
85
86 protected function replace($matches)
87 {
88 if (empty($matches[2])) {
89 return apply_filters('fluent_community/smartcode_fallback', $matches[0], $this->store['user']);
90 }
91
92 $matches[2] = trim($matches[2]);
93
94 $matched = explode('.', $matches[2]);
95
96 if (count($matched) <= 1) {
97 return apply_filters('fluent_community/smartcode_fallback', $matches[0], $this->store['user']);
98 }
99
100 $dataKey = trim(array_shift($matched));
101
102 $valueKey = trim(implode('.', $matched));
103
104 if (!$valueKey) {
105 return apply_filters('fluent_community/smartcode_fallback', $matches[0], $this->store['user']);
106 }
107
108 $valueKeys = explode('|', $valueKey);
109
110 $valueKey = $valueKeys[0];
111 $defaultValue = '';
112 $transformer = '';
113
114 $valueCounts = count($valueKeys);
115
116 if ($valueCounts >= 3) {
117 $defaultValue = trim($valueKeys[1]);
118 $transformer = trim($valueKeys[2]);
119 } else if ($valueCounts === 2) {
120 $defaultValue = trim($valueKeys[1]);
121 }
122
123 $value = '';
124 switch ($dataKey) {
125 case 'site':
126 $value = $this->getWpValue($valueKey, $defaultValue);
127 break;
128 case 'user':
129 $value = static::$store['user'] ? $this->getUserValue($valueKey, $defaultValue) : $defaultValue;
130 break;
131 case 'community':
132 $value = $this->getCommunityValue($valueKey, $defaultValue);
133 break;
134 case 'section':
135 $value = $this->getSectionValue($valueKey, $defaultValue);
136 break;
137 case 'course':
138 $value = $this->getCourseValue($valueKey, $defaultValue);
139 break;
140 default:
141 $value = apply_filters('fluent_community/smartcode_group_callback_' . $dataKey, $matches[0], $valueKey, $defaultValue, static::$store['user']);
142 }
143
144 if ($transformer && is_string($transformer) && $value) {
145 switch ($transformer) {
146 case 'trim':
147 return trim($value);
148 case 'ucfirst':
149 return ucfirst($value);
150 case 'strtolower':
151 return strtolower($value);
152 case 'strtoupper':
153 return strtoupper($value);
154 case 'ucwords':
155 return ucwords($value);
156 case 'concat_first': // usage: {{contact.first_name||concat_first|Hi
157 if (isset($valueKeys[3])) {
158 $value = trim($valueKeys[3] . ' ' . $value);
159 }
160 return $value;
161 case 'concat_last': // usage: {{contact.first_name||concat_last|, => FIRST_NAME,
162 if (isset($valueKeys[3])) {
163 $value = trim($value . '' . $valueKeys[3]);
164 }
165 return $value;
166 case 'show_if': // usage {{contact.first_name||show_if|First name exist
167 if (isset($valueKeys[3])) {
168 $value = $valueKeys[3];
169 }
170 return $value;
171 default:
172 return $value;
173 }
174 }
175
176 return $value;
177
178 }
179
180 protected function getWpValue($valueKey, $defaultValue)
181 {
182 if ($valueKey == 'login_url') {
183 return network_site_url('wp-login.php', 'login');
184 }
185
186 if ($valueKey == 'name') {
187 return wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
188 }
189
190 $value = get_bloginfo($valueKey);
191 if (!$value) {
192 return $defaultValue;
193 }
194 return $value;
195 }
196
197
198 protected function getUserValue($valueKey, $defaultValue)
199 {
200 $userModel = static::$store['user'];
201 if (!$userModel || !$userModel instanceof \FluentCommunity\App\Models\User) {
202 return $defaultValue;
203 }
204
205 $xProfile = $userModel->xprofile;
206
207 if ($valueKey == 'profile_link') {
208 if ($xProfile) {
209 return $xProfile->getPermalink();
210 }
211 return $defaultValue;
212 }
213
214 if ($xProfile) {
215 if ($valueKey == 'display_name') {
216 return $xProfile->display_name;
217 }
218 }
219
220 if ($valueKey == 'photo_html') {
221 if ($xProfile) {
222 return '<img src="' . esc_url($xProfile->avatar) . '" alt="' . esc_attr($xProfile->display_name) . '" class="fcom_user_dynamic_photo" />';
223 }
224 return '<img src="' . esc_url($userModel->photo) . '" alt="' . esc_attr($userModel->display_name) . '" class="fcom_user_dynamic_photo" />';
225 }
226
227 $wpUser = $userModel->getWpUser();
228 $valueKeys = explode('.', $valueKey);
229 if (count($valueKeys) == 1) {
230 $value = $wpUser->get($valueKey);
231 if (!$value) {
232 return $defaultValue;
233 }
234
235 if (!is_array($value) || !is_object($value)) {
236 return $value;
237 }
238
239 return $defaultValue;
240 }
241
242 $customKey = $valueKeys[0];
243 $customProperty = $valueKeys[1];
244
245 if ($customKey === 'photo_html') {
246 $width = (string)esc_attr($customProperty);
247 $style = 'style="width: ' . $width . '; height: ' . $width . ';"';
248 if ($xProfile) {
249 return '<img ' . $style . ' src="' . esc_url($xProfile->avatar) . '" alt="' . esc_attr($xProfile->display_name) . '" class="fcom_user_dynamic_photo" />';
250 }
251 return '<img ' . $style . ' src="' . esc_url($userModel->photo) . '" alt="' . esc_attr($userModel->display_name) . '" class="fcom_user_dynamic_photo" />';
252 }
253
254 if ($customKey == 'meta') {
255 $metaValue = get_user_meta($wpUser->ID, $customProperty, true);
256 if (!$metaValue) {
257 return $defaultValue;
258 }
259
260 if (!is_array($metaValue) || !is_object($metaValue)) {
261 return $metaValue;
262 }
263
264 return $defaultValue;
265 }
266
267 return $defaultValue;
268 }
269
270 protected function getCommunityValue($valueKey, $defaultValue)
271 {
272 $communitySettings = static::$store['community'];
273
274 if ($valueKey == 'name') {
275 return Arr::get($communitySettings, 'site_title');
276 }
277
278 if ($valueKey == 'name_with_url') {
279 $siteTitle = Arr::get($communitySettings, 'site_title');
280 return static::$isHtml ? '<a target="_blank" href="' . Helper::baseUrl('/') . '">' . $siteTitle . '</a>' : $siteTitle;
281 }
282
283 if (isset($communitySettings[$valueKey])) {
284 return $communitySettings[$valueKey];
285 }
286
287 return $defaultValue;
288 }
289
290 protected function getSectionValue($valueKey, $defaultValue)
291 {
292 $sectionModel = static::$store['feed'];
293 if (!$sectionModel || !$sectionModel instanceof CourseTopic) {
294 return $defaultValue;
295 }
296
297 if ($valueKey === 'url') {
298 $user = static::$store['user'] ?? null;
299 $course = static::$store['course'] ?? null;
300
301 if (!$course instanceof Course) {
302 return $defaultValue;
303 }
304
305 $courseUrl = $course->getPermalink();
306
307 $userId = $user->ID ?? null;
308 $signedUrl = $userId ? ProfileHelper::signUserUrlWithAuthHash($courseUrl, $userId) : $courseUrl;
309
310 if (static::$isHtml) {
311 return sprintf('<a href="%s">%s</a>', esc_url($signedUrl), esc_html($courseUrl));
312 }
313
314 return $signedUrl;
315 }
316
317 $fillables = array_merge(
318 (new CourseTopic())->getFillable(),
319 ['id', 'created_at', 'updated_at']
320 );
321
322 if (in_array($valueKey, $fillables)) {
323 return $sectionModel->{$valueKey};
324 }
325
326 return $defaultValue;
327 }
328
329 protected function getCourseValue($valueKey, $defaultValue)
330 {
331 $courseModel = static::$store['course'];
332 if (!$courseModel || !$courseModel instanceof Course) {
333 return $defaultValue;
334 }
335
336 $fillables = array_merge(
337 (new Course())->getFillable(),
338 ['id', 'created_at', 'updated_at']
339 );
340
341 if (in_array($valueKey, $fillables)) {
342 return $courseModel->{$valueKey};
343 }
344
345 return $defaultValue;
346 }
347 }
348