| @@ -143,75 +143,39 @@ | ||
| 143 | 143 | |
| 144 | 144 | if ($transformer && is_string($transformer) && $value) { |
| 145 | 145 | switch ($transformer) { |
| 146 | 146 | case 'trim': |
| 147 | - $value = trim($value); | |
| 148 | - break; | |
| 147 | + return trim($value); | |
| 149 | 148 | case 'ucfirst': |
| 150 | - $value = ucfirst($value); | |
| 151 | - break; | |
| 149 | + return ucfirst($value); | |
| 152 | 150 | case 'strtolower': |
| 153 | - $value = strtolower($value); | |
| 154 | - break; | |
| 151 | + return strtolower($value); | |
| 155 | 152 | case 'strtoupper': |
| 156 | - $value = strtoupper($value); | |
| 157 | - break; | |
| 153 | + return strtoupper($value); | |
| 158 | 154 | case 'ucwords': |
| 159 | - $value = ucwords($value); | |
| 160 | - break; | |
| 155 | + return ucwords($value); | |
| 161 | 156 | case 'concat_first': // usage: {{contact.first_name||concat_first|Hi |
| 162 | 157 | if (isset($valueKeys[3])) { |
| 163 | 158 | $value = trim($valueKeys[3] . ' ' . $value); |
| 164 | 159 | } |
| 165 | - break; | |
| 160 | + return $value; | |
| 166 | 161 | case 'concat_last': // usage: {{contact.first_name||concat_last|, => FIRST_NAME, |
| 167 | 162 | if (isset($valueKeys[3])) { |
| 168 | 163 | $value = trim($value . '' . $valueKeys[3]); |
| 169 | 164 | } |
| 170 | - break; | |
| 165 | + return $value; | |
| 171 | 166 | case 'show_if': // usage {{contact.first_name||show_if|First name exist |
| 172 | 167 | if (isset($valueKeys[3])) { |
| 173 | 168 | $value = $valueKeys[3]; |
| 174 | 169 | } |
| 175 | - break; | |
| 170 | + return $value; | |
| 171 | + default: | |
| 172 | + return $value; | |
| 176 | 173 | } |
| 177 | 174 | } |
| 178 | 175 | |
| 179 | - return $this->escapeValueForContext($value, $dataKey, $valueKey); | |
| 180 | - } | |
| 176 | + return $value; | |
| 181 | 177 | |
| 182 | - /** | |
| 183 | - * Smartcode values are substituted into lockscreen/lesson/email HTML *after* | |
| 184 | - * that content has passed through wp_kses / do_blocks, so a resolved scalar | |
| 185 | - * carrying markup would otherwise bypass sanitisation. Escape ordinary | |
| 186 | - * values for the HTML context. The few branches that intentionally build a | |
| 187 | - * trusted fragment (photo_html, name_with_url, section url) already escape | |
| 188 | - * their own interpolated parts, so they are left untouched, and values from | |
| 189 | - * third-party group callbacks are the extension's responsibility. | |
| 190 | - */ | |
| 191 | - protected function escapeValueForContext($value, $dataKey, $valueKey) | |
| 192 | - { | |
| 193 | - if (!static::$isHtml || !is_string($value) || $value === '') { | |
| 194 | - return $value; | |
| 195 | - } | |
| 196 | - | |
| 197 | - $knownGroups = ['site', 'user', 'community', 'section', 'course']; | |
| 198 | - if (!in_array($dataKey, $knownGroups, true)) { | |
| 199 | - return $value; | |
| 200 | - } | |
| 201 | - | |
| 202 | - $trustedHtml = [ | |
| 203 | - 'user' => ['photo_html'], | |
| 204 | - 'community' => ['name_with_url'], | |
| 205 | - 'section' => ['url'], | |
| 206 | - ]; | |
| 207 | - | |
| 208 | - $baseKey = strtok($valueKey, '.'); // "photo_html.50px" -> "photo_html" | |
| 209 | - if (in_array($baseKey, Arr::get($trustedHtml, $dataKey, []), true)) { | |
| 210 | - return $value; | |
| 211 | - } | |
| 212 | - | |
| 213 | - return esc_html($value); | |
| 214 | 178 | } |
| 215 | 179 | |
| 216 | 180 | protected function getWpValue($valueKey, $defaultValue) |
| 217 | 181 | { |
| @@ -262,22 +226,8 @@ | ||
| 262 | 226 | |
| 263 | 227 | $wpUser = $userModel->getWpUser(); |
| 264 | 228 | $valueKeys = explode('.', $valueKey); |
| 265 | 229 | if (count($valueKeys) == 1) { |
| 266 | - // Smartcodes are resolved against the *viewer* and can be authored by | |
| 267 | - // space/course/page admins, so only a fixed set of non-sensitive | |
| 268 | - // profile fields may be read. Never expose user_pass, | |
| 269 | - // user_activation_key, session tokens or capability meta. | |
| 270 | - $allowedFields = apply_filters('fluent_community/smartcode/user_fields', [ | |
| 271 | - 'ID', 'first_name', 'last_name', 'nickname', 'display_name', | |
| 272 | - 'user_email', 'user_login', 'user_nicename', 'user_url', | |
| 273 | - 'description', 'user_registered' | |
| 274 | - ]); | |
| 275 | - | |
| 276 | - if (!in_array($valueKey, $allowedFields, true)) { | |
| 277 | - return $defaultValue; | |
| 278 | - } | |
| 279 | - | |
| 280 | 230 | $value = $wpUser->get($valueKey); |
| 281 | 231 | if (!$value) { |
| 282 | 232 | return $defaultValue; |
| 283 | 233 | } |
| @@ -301,18 +251,8 @@ | ||
| 301 | 251 | return '<img ' . $style . ' src="' . esc_url($userModel->photo) . '" alt="' . esc_attr($userModel->display_name) . '" class="fcom_user_dynamic_photo" />'; |
| 302 | 252 | } |
| 303 | 253 | |
| 304 | 254 | if ($customKey == 'meta') { |
| 305 | - // No first-party template reads user meta through smartcodes, and the | |
| 306 | - // viewer's own meta (session_tokens, capability keys, reset keys, | |
| 307 | - // any _-prefixed value) must never leak into author-controlled | |
| 308 | - // content. Resolve only meta keys a site has explicitly allowed. | |
| 309 | - $allowedMetaKeys = apply_filters('fluent_community/smartcode/user_meta_keys', []); | |
| 310 | - | |
| 311 | - if (strpos($customProperty, '_') === 0 || !in_array($customProperty, $allowedMetaKeys, true)) { | |
| 312 | - return $defaultValue; | |
| 313 | - } | |
| 314 | - | |
| 315 | 255 | $metaValue = get_user_meta($wpUser->ID, $customProperty, true); |
| 316 | 256 | if (!$metaValue) { |
| 317 | 257 | return $defaultValue; |
| 318 | 258 | } |