-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjcr_section_custom.php
606 lines (499 loc) · 22.2 KB
/
jcr_section_custom.php
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
<?php
// This is a PLUGIN TEMPLATE for Textpattern CMS.
// Copy this file to a new name like abc_myplugin.php. Edit the code, then
// run this file at the command line to produce a plugin for distribution:
// $ php abc_myplugin.php > abc_myplugin-0.1.txt
// Plugin name is optional. If unset, it will be extracted from the current
// file name. Plugin names should start with a three letter prefix which is
// unique and reserved for each plugin author ("abc" is just an example).
// Uncomment and edit this line to override:
$plugin["name"] = "jcr_section_custom";
// Allow raw HTML help, as opposed to Textile.
// 0 = Plugin help is in Textile format, no raw HTML allowed (default).
// 1 = Plugin help is in raw HTML. Not recommended.
# $plugin['allow_html_help'] = 1;
$plugin["version"] = "0.2.3";
$plugin["author"] = "jcr / txpbuilders";
$plugin["author_uri"] = "http://txp.builders";
$plugin["description"] = "Adds multiple custom fields to the sections panel";
// Plugin load order:
// The default value of 5 would fit most plugins, while for instance comment
// spam evaluators or URL redirectors would probably want to run earlier
// (1...4) to prepare the environment for everything else that follows.
// Values 6...9 should be considered for plugins which would work late.
// This order is user-overrideable.
$plugin["order"] = "5";
// Plugin 'type' defines where the plugin is loaded
// 0 = public : only on the public side of the website (default)
// 1 = public+admin : on both the public and admin side
// 2 = library : only when include_plugin() or require_plugin() is called
// 3 = admin : only on the admin side (no AJAX)
// 4 = admin+ajax : only on the admin side (AJAX supported)
// 5 = public+admin+ajax : on both the public and admin side (AJAX supported)
$plugin["type"] = "1";
// Plugin "flags" signal the presence of optional capabilities to the core plugin loader.
// Use an appropriately OR-ed combination of these flags.
// The four high-order bits 0xf000 are available for this plugin's private use
if (!defined('PLUGIN_HAS_PREFS')) define('PLUGIN_HAS_PREFS', 0x0001); // This plugin wants to receive "plugin_prefs.{$plugin['name']}" events
if (!defined('PLUGIN_LIFECYCLE_NOTIFY')) define('PLUGIN_LIFECYCLE_NOTIFY', 0x0002); // This plugin wants to receive "plugin_lifecycle.{$plugin['name']}" events
$plugin["flags"] = "3";
// Plugin 'textpack' is optional. It provides i18n strings to be used in conjunction with gTxt().
// Syntax:
// ## arbitrary comment
// #@event
// #@language ISO-LANGUAGE-CODE
// abc_string_name => Localized String
// Customise the display of the custom field form labels by pasting the following into the Textpack box
// in Settings › Languages replacing the language code and field label names:
// #@owner jcr_section_custom
// #@language en, en-gb, en-us
// #@section
// jcr_sec_custom_1 => Hero image
// jcr_sec_custom_2 => Menu title
// jcr_sec_custom_3 => Page title
// jcr_sec_custom_4 => Accent color
// jcr_sec_custom_5 => Background color
$plugin["textpack"] = <<<EOT
#@owner jcr_section_custom
#@language en, en-gb, en-us
#@prefs
jcr_section_custom => Section custom fields
section_custom_1_set => Section custom field 1 name
section_custom_2_set => Section custom field 2 name
section_custom_3_set => Section custom field 3 name
section_custom_4_set => Section custom field 4 name
section_custom_5_set => Section custom field 5 name
#@language de
#@prefs
jcr_link_custom => Sektion Custom-Felder
section_custom_1_set => Name des 1. Sektion-Custom Feldes
section_custom_2_set => Name des 2. Sektion-Custom Feldes
section_custom_3_set => Name des 3. Sektion-Custom Feldes
section_custom_4_set => Name des 4. Sektion-Custom Feldes
section_custom_5_set => Name des 5. Sektion-Custom Feldes
EOT;
// End of textpack
if (!defined("txpinterface")) {
@include_once("zem_tpl.php");
}
# --- BEGIN PLUGIN CODE ---
class jcr_section_custom
{
/**
* Initialise.
*/
function __construct()
{
// Hook into the system's callbacks
register_callback(array(__CLASS__, "lifecycle"), "plugin_lifecycle.jcr_section_custom");
register_callback(array(__CLASS__, "ui"), "section_ui", "extend_detail_form");
register_callback(array(__CLASS__, "save"), "section", "section_save");
// Prefs pane for custom fields
add_privs("prefs.jcr_section_custom", "1");
// Redirect 'Options' link on plugins panel to preferences pane
add_privs("plugin_prefs.jcr_section_custom", "1");
register_callback(array(__CLASS__, "options_prefs_redirect"), "plugin_prefs.jcr_section_custom");
}
/**
* Add and remove custom fields from txp_section table.
*
* @param $event string
* @param $step string The lifecycle phase of this plugin
*/
public static function lifecycle($event, $step)
{
switch ($step) {
case "enabled":
add_privs("prefs.jcr_section_custom", "1");
break;
case "disabled":
break;
case "installed":
// Add section custom fields to txp_section table
$cols_exist = safe_query("SHOW COLUMNS FROM " . safe_pfx("txp_section") . " LIKE 'jcr_sec_custom_1'");
if (@numRows($cols_exist) == 0) {
safe_alter(
"txp_section",
"ADD COLUMN jcr_sec_custom_1 VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN jcr_sec_custom_2 VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN jcr_sec_custom_3 VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN jcr_sec_custom_4 VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN jcr_sec_custom_5 VARCHAR(255) NOT NULL DEFAULT ''"
);
}
// Add prefs for section custom field names
create_pref("section_custom_1_set", "", "jcr_section_custom", "0", "section_custom_set", "1");
create_pref("section_custom_2_set", "", "jcr_section_custom", "0", "section_custom_set", "2");
create_pref("section_custom_3_set", "", "jcr_section_custom", "0", "section_custom_set", "3");
create_pref("section_custom_4_set", "", "jcr_section_custom", "0", "section_custom_set", "4");
create_pref("section_custom_5_set", "", "jcr_section_custom", "0", "section_custom_set", "5");
// Insert initial value for cf1 if none already exists (so that upgrade works)
$cf_pref = get_pref("section_custom_1_set");
if ($cf_pref === "") {
set_pref("section_custom_1_set", "custom1");
}
// Upgrade: Migrate v1 plugin legacy column
$legacy = safe_query("SHOW COLUMNS FROM " . safe_pfx("txp_section") . " LIKE 'jcr_section_custom'");
if (@numRows($legacy) > 0) {
// Copy contents of jcr_section_custom to jcr_sec_custom_1
safe_update("txp_section", "`jcr_sec_custom_1` = `jcr_section_custom`", "jcr_section_custom IS NOT NULL");
// Delete jcr_section_custom column
safe_alter("txp_section", "DROP COLUMN `jcr_section_custom`");
// Update language string (is seemingly not replaced by textpack)
safe_update("txp_lang", "data = 'Section custom fields', owner = 'jcr_section_custom'", "name = 'jcr_section_custom' AND lang = 'en'");
safe_update("txp_lang", "data = 'Sektion Custom-Felder', owner = 'jcr_section_custom'", "name = 'jcr_section_custom' AND lang = 'de'");
}
// Upgrade: Migrate from NULL to '' default value
$has_nulls = safe_rows_start("*", "txp_section", "`jcr_sec_custom_1` IS NULL OR `jcr_sec_custom_2` IS NULL OR `jcr_sec_custom_3` IS NULL OR `jcr_sec_custom_4` IS NULL OR `jcr_sec_custom_5` IS NULL");
if (@numRows($has_nulls) > 0) {
safe_update("txp_section", "jcr_sec_custom_1 = ''", "jcr_sec_custom_1 IS NULL");
safe_update("txp_section", "jcr_sec_custom_2 = ''", "jcr_sec_custom_2 IS NULL");
safe_update("txp_section", "jcr_sec_custom_3 = ''", "jcr_sec_custom_3 IS NULL");
safe_update("txp_section", "jcr_sec_custom_4 = ''", "jcr_sec_custom_4 IS NULL");
safe_update("txp_section", "jcr_sec_custom_5 = ''", "jcr_sec_custom_5 IS NULL");
safe_alter(
"txp_section",
"MODIFY jcr_sec_custom_1 VARCHAR(255) NOT NULL DEFAULT '',
MODIFY jcr_sec_custom_2 VARCHAR(255) NOT NULL DEFAULT '',
MODIFY jcr_sec_custom_3 VARCHAR(255) NOT NULL DEFAULT '',
MODIFY jcr_sec_custom_4 VARCHAR(255) NOT NULL DEFAULT '',
MODIFY jcr_sec_custom_5 VARCHAR(255) NOT NULL DEFAULT ''"
);
}
break;
case "deleted":
// Remove columns from section table
safe_alter(
"txp_section",
"DROP COLUMN jcr_sec_custom_1,
DROP COLUMN jcr_sec_custom_2,
DROP COLUMN jcr_sec_custom_3,
DROP COLUMN jcr_sec_custom_4,
DROP COLUMN jcr_sec_custom_5"
);
// Remove all prefs from event 'jcr_section_custom'.
remove_pref(null,"jcr_section_custom");
// Remove all associated lang strings
safe_delete(
"txp_lang",
"owner = 'jcr_section_custom'"
);
break;
}
return;
}
/**
* Paint additional fields for section custom field
*
* @param $event string
* @param $step string
* @param $dummy string
* @param $rs array The current section's data
* @return string
*/
public static function ui($event, $step, $dummy, $rs)
{
global $prefs;
extract(lAtts(array(
"jcr_sec_custom_1" => "",
"jcr_sec_custom_2" => "",
"jcr_sec_custom_3" => "",
"jcr_sec_custom_4" => "",
"jcr_sec_custom_5" => ""
), $rs, 0));
$out = "";
$cfs = preg_grep("/^section_custom_\d+_set/", array_keys($prefs));
asort($cfs);
foreach ($cfs as $name) {
preg_match("/(\d+)/", $name, $match);
if ($prefs[$name] !== "") {
$out .= inputLabel("jcr_sec_custom_".$match[1], fInput("text", "jcr_sec_custom_".$match[1], ${"jcr_sec_custom_".$match[1]}, "", "", "", INPUT_REGULAR, "", "jcr_sec_custom_".$match[1]), "jcr_sec_custom_".$match[1]).n;
}
}
return $out;
}
/**
* Save additional section custom fields
*
* @param $event string
* @param $step string
*/
public static function save($event, $step)
{
extract(doSlash(psa(array("jcr_sec_custom_1", "jcr_sec_custom_2", "jcr_sec_custom_3", "jcr_sec_custom_4", "jcr_sec_custom_5", "name"))));
$name = assert_string($name);
safe_update(
"txp_section",
"jcr_sec_custom_1 = '$jcr_sec_custom_1',
jcr_sec_custom_2 = '$jcr_sec_custom_2',
jcr_sec_custom_3 = '$jcr_sec_custom_3',
jcr_sec_custom_4 = '$jcr_sec_custom_4',
jcr_sec_custom_5 = '$jcr_sec_custom_5'",
"name = '$name'"
);
}
/**
* Renders a HTML section custom field.
*
* Can be altered by plugins via the 'prefs_ui > section_custom_set'
* pluggable UI callback event.
*
* @param string $name HTML name of the widget
* @param string $val Initial (or current) content
* @return string HTML
* @todo deprecate or move this when CFs are migrated to the meta store
*/
public static function section_custom_set($name, $val)
{
return pluggable_ui("prefs_ui", "section_custom_set", text_input($name, $val, INPUT_REGULAR), $name, $val);
}
/**
* Re-route 'Options' link on Plugins panel to Admin › Preferences panel
*
*/
public static function options_prefs_redirect()
{
header("Location: index.php?event=prefs#prefs_group_jcr_section_custom");
}
}
if (txpinterface === "admin") {
new jcr_section_custom();
}
// Register public tags (not restricted to public so that usable on dashboards)
if (class_exists("\Textpattern\Tag\Registry")) {
Txp::get("\Textpattern\Tag\Registry")
->register("jcr_section_custom")
->register("jcr_if_section_custom");
}
/**
* Gets a list of section custom fields.
*
* @return array
*/
function jcr_get_section_custom_fields()
{
global $prefs;
static $out = null;
// Have cache?
if (!is_array($out)) {
$cfs = preg_grep("/^section_custom_\d+_set/", array_keys($prefs));
$out = array();
foreach ($cfs as $name) {
preg_match("/(\d+)/", $name, $match);
if ($prefs[$name] !== "") {
$out[$match[1]] = strtolower($prefs[$name]);
}
}
}
return $out;
}
/**
* Maps 'txp_section' table's columns to article data values.
*
* This function returns an array of 'data-value' => 'column' pairs.
*
* @return array
*/
function jcr_section_column_map()
{
$section_custom = jcr_get_section_custom_fields();
$section_custom_map = array();
if ($section_custom) {
foreach ($section_custom as $i => $name) {
$section_custom_map[$name] = "jcr_sec_custom_".$i;
}
}
return $section_custom_map;
}
/**
* Public tag: Output custom section field
* @param string $atts[name] Name of custom field.
* @param string $atts[escape] Convert special characters to HTML entities.
* @param string $atts[default] Default output if field is empty.
* @return string custom field output
* <code>
* <txp:jcr_section_custom name="title_image" escape="html" />
* </code>
*/
function jcr_section_custom($atts, $thing = null)
{
global $thissection, $pretext;
// If not currently in section context, get current section from pretext
$current_section = empty($thissection) ? $pretext["s"] : $thissection["name"];
extract(lAtts(array(
"class" => "",
"name" => get_pref("section_custom_1_set"),
"escape" => null,
"default" => "",
"wraptag" => "",
), $atts));
$name = strtolower($name);
$rs = safe_rows_start(
"*",
"txp_section",
"name = '".$current_section."'"
);
if ($rs) {
while ($row = nextRow($rs)) {
// Populate section custom field data;
foreach (jcr_section_column_map() as $key => $column) {
$currentsection[$key] = isset($row[$column]) ? $row[$column] : null;
}
}
}
if (!isset($currentsection[$name])) {
trigger_error(gTxt("field_not_found", array("{name}" => $name)), E_USER_NOTICE);
return "";
}
if (!isset($thing)) {
$thing = $currentsection[$name] !== "" ? $currentsection[$name] : $default;
}
$thing = ($escape === null ? txpspecialchars($thing) : parse($thing));
return !empty($thing) ? doTag($thing, $wraptag, $class) : "";
}
/**
* Public tag: Check if section custom field exists
* @param string $atts[name] Name of custom field.
* @param string $atts[value] Value to test against (optional).
* @param string $atts[match] Match testing: exact, any, all, pattern.
* @param string $atts[separator] Item separator for match="any" or "all". Otherwise ignored.
* @return string custom field output
* <code>
* <txp:jcr_if_section_custom name="menu_title" /> … <txp:else /> … </txp:jcr_if_section_custom>
* </code>
*/
function jcr_if_section_custom($atts, $thing = null)
{
global $thissection, $pretext;
// If not currently in section context, get current section from pretext
$current_section = empty($thissection) ? $pretext["s"] : $thissection["name"];
extract($atts = lAtts(array(
"name" => get_pref("section_custom_1_set"),
"value" => null,
"match" => "exact",
"separator" => "",
), $atts));
$name = strtolower($name);
$rs = safe_rows_start(
"*",
"txp_section",
"name = '".$current_section."'"
);
if ($rs) {
while ($row = nextRow($rs)) {
// Populate section custom field data;
foreach (jcr_section_column_map() as $key => $column) {
$currentsection[$key] = isset($row[$column]) ? $row[$column] : null;
}
}
}
if (!isset($currentsection[$name])) {
trigger_error(gTxt("field_not_found", array("{name}" => $name)), E_USER_NOTICE);
return "";
}
if ($value !== null) {
$cond = txp_match($atts, $currentsection[$name]);
} else {
$cond = ($currentsection[$name] !== "");
}
return isset($thing) ? parse($thing, !empty($cond)) : !empty($cond);
}
# --- END PLUGIN CODE ---
if (0) {
?>
<!--
# --- BEGIN PLUGIN CSS ---
# --- END PLUGIN CSS ---
-->
<!--
# --- BEGIN PLUGIN HELP ---
h1. jcr_section_custom
Adds up to five extra custom fields of up to 255 characters to the "Presentation › Sections":http://docs.textpattern.io/administration/sections-panel panel along with corresponding tags to output the custom field and to test if it contains a value or matches a specific value.
h3. Use cases
Use whenever extra information needs to be stored with a section. For example:
* Store a txp image ID number and use it to associate a cover image with the section.
* Store associated details, for example the background colour or key colour of a section.
* To use a different title for the menu than the section title.
* To create links between parallel sections in different languages.
* …
h2. Installation / Deinstallation
h3. Installation
Paste the @.txt@ installer code into the _Admin › Plugins_ panel, or upload the plugin's @.php@ file via the _Upload plugin_ button, then install and enable the plugin.
h3. De-installation
The plugin cleans up after itself: deinstalling (deleting) the plugin removes the extra columns from the database as well as custom field names and labels. To stop using the plugin but keep the custom field data in the database, just disable (deactivate) the plugin but don't delete it.
h2. Plugin tags
h3. jcr_section_custom
Outputs the content of the section custom field.
h4. Tag attributes
@name@
Specifies the name of the section custom field.
Example: Use @name="title_image"@ to output the title_image custom field. Default: jcr_sec_custom_1.
@escape@
Escape HTML entities such as @<@, @>@ and @&@ prior to echoing the field contents.
Supports extended escape values in txp 4.8
Example: Use @escape="textile"@ to convert textile in the value. Default: none.
@default@
Specifies the default output if the custom field is empty
Example: Use @default="123"@ to output "123", e.g. for use as the default image ID number. Default: empty.
@wraptag@
Wrap the custom field contents in an HTML tag
Example: Use @wraptag="h2"@ to output @<h2>Custom field value</h2>@. Default: empty.
@class@
Specifies a class to be added to the @wraptag@ attribute
Example: Use @wraptag="p" class="intro"@ to output @<p class="intro">Custom field value</p>@. Default: empty
h3. jcr_if_section_custom
Tests for existence of a section custom field, or whether one or several matches a value or pattern.
h4. Tag attributes
@name@
Specifies the name of the section custom field.
Example: Use @name="title_image"@ to output the title_image custom field. Default: jcr_sec_custom_1.
@value@
Value to test against (optional).
If not specified, the tag tests for the existence of any value in the specified section custom field.
Example: Use @value="english"@ to output only those sections whose “language” section custom field is english. Default: none.
@match@
Match testing: exact, any, all, pattern. See the docs for "if_custom_field":https://docs.textpattern.com/tags/if_custom_field.
Default: exact.
@separator@
Item separator for match="any" or "all". Otherwise ignored.
Default: empty.
h2. Examples
h3. Example 1
Outputs the specified title image from the image ID number. If no image is specified a default image with image ID# 123 is output:
bc. <txp:section_list>
<txp:images id='<txp:jcr_section_custom name="title_image" escape="" default="123" />'>
<txp:image />
</txp:images>
</txp:section_list>
p. where the section custom field is used to store the Image ID# of the title image.
h3. Example 2
Outputs a menu of only those sections whose "language" custom field equals "english":
bc. <txp:section_list wraptag="ul" break="" class="nav en">
<txp:jcr_if_section_custom name="language" value="english">
<li><txp:section title link /></li>
</txp:jcr_if_section_custom>
</txp:section_list>
h2. Custom field labels
The label displayed alongside the custom field in the edit image panel can be changed by specifying a new label using the _Install from Textpack_ field in the "Admin › Languages":http://docs.textpattern.io/administration/languages-panel.html panel. Enter your own information in the following pattern and click *Upload*:
bc. #@owner jcr_section_custom
#@language en, en-gb, en-us
#@section
jcr_sec_custom_1 => Your label
jcr_sec_custom_2 => Your other label
…
p. replacing @en@ with your own language and @Your label@ with your own desired label.
h2. Changelog and credits
h3. Changelog
* Version 0.2.3 – 2020/12/18 – No new functionality. Textpack fixes and align with other custom field plugins
* Version 0.2.2 – 2020/06/27 – Handle migration from previous versions of the plugin on install
* Version 0.2.1 – 2020/06/27 – Fix for missing custom_field name vs. missing value for cf
* Version 0.2 – 2020/03/04 – Expand to handle multiple custom fields
* Version 0.1 – 2018/07/18 – Remedy table not being created on install
* Version 0.1 – 2016/03/04 – First release
h3. Credits
Robert Wetzlmayr’s "wet_profile":https://github.com/rwetzlmayr/wet_profile plugin for the starting point, and further examples by "Stef Dawson":http://www.stefdawson.com and "Jukka Svahn":https://github.com/gocom.
# --- END PLUGIN HELP ---
-->
<?php
}
?>