$data
98 | */
99 | public function render(string $tpl, array $data = [], bool $string = false): string {
100 | $twig = $this->initTwig();
101 |
102 | try {
103 | if ($string) {
104 | return $twig->createTemplate($tpl)->render($data);
105 | }
106 |
107 | return $twig->render($tpl.'.twig', $data);
108 | } catch (Exception $e) {
109 | return $e->getMessage().' in '.$e->getFile().' at line: '.$e->getLine();
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Value.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | public static function format(string $value): array {
20 | // It's only used to display the name in the UI
21 | $encoder = null;
22 | $is_formatted = false;
23 |
24 | if (!json_validate($value)) {
25 | // Decoding must be done first, in case there is data that can also be formatted
26 | $value = self::decoded($value, $encoder);
27 | $value = self::formatted($value, $is_formatted);
28 | }
29 |
30 | // Always pretty print the JSON because some formatters may return the value as JSON
31 | $value = self::prettyPrintJson($value);
32 |
33 | return [$value, $encoder, $is_formatted];
34 | }
35 |
36 | public static function decoded(string $value, ?string &$encoder = null): string {
37 | foreach (Config::get('converters', []) as $name => $decoder) {
38 | if (is_callable($decoder['view']) && ($decoded = $decoder['view']($value)) !== null) {
39 | $encoder = (string) $name;
40 |
41 | return $decoded;
42 | }
43 | }
44 |
45 | return $value;
46 | }
47 |
48 | public static function formatted(string $value, bool &$is_formatted = false): string {
49 | foreach (Config::get('formatters', []) as $formatter) {
50 | if (is_callable($formatter) && $formatter($value) !== null) {
51 | $is_formatted = true;
52 |
53 | return $formatter($value);
54 | }
55 | }
56 |
57 | return $value;
58 | }
59 |
60 | public static function prettyPrintJson(string $value): string {
61 | if (!is_numeric($value) && json_validate($value)) {
62 | try {
63 | $json_array = json_decode($value, false, 512, JSON_THROW_ON_ERROR);
64 | $value = json_encode($json_array, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
65 |
66 | return ''.htmlspecialchars($value).'
';
67 | } catch (JsonException) {
68 | return htmlspecialchars($value);
69 | }
70 | }
71 |
72 | return htmlspecialchars($value);
73 | }
74 |
75 | /**
76 | * Decode/Encode value.
77 | *
78 | * Used in forms.
79 | */
80 | public static function converter(string $value, string $converter, string $type): string {
81 | if ($converter === 'none') {
82 | return $value;
83 | }
84 |
85 | $converters = (array) Config::get('converters', []);
86 |
87 | // $type can be view (decode) or save (encode)
88 | if (
89 | is_callable($converters[$converter][$type]) &&
90 | ($converted = $converters[$converter][$type]($value)) !== null
91 | ) {
92 | return $converted;
93 | }
94 |
95 | return $value;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 | $JSON_MAX_DEPTH) {
24 | throw new ValueError(sprintf('json_validate(): Argument #2 ($depth) must be less than %d', $JSON_MAX_DEPTH));
25 | }
26 |
27 | try {
28 | json_decode($json, null, $depth, JSON_THROW_ON_ERROR | $flags);
29 | } catch (JsonException) {
30 | return false;
31 | }
32 |
33 | return true;
34 | }
35 | }
36 |
37 | function autoload(string $path): void {
38 | if (is_file($path.'twig.phar')) {
39 | require_once 'phar://'.$path.'twig.phar/vendor/autoload.php';
40 | }
41 |
42 | if (!extension_loaded('redis') && is_file($path.'predis.phar')) {
43 | require_once 'phar://'.$path.'predis.phar/vendor/autoload.php';
44 | }
45 |
46 | spl_autoload_register(static function (string $class_name) use ($path): void {
47 | $class_name = str_replace("RobiNN\\Pca\\", '', $class_name);
48 | $filename = str_replace("\\", DIRECTORY_SEPARATOR, $class_name);
49 | $fullpath = $path.'src/'.$filename.'.php';
50 |
51 | if (is_file($fullpath)) {
52 | require_once $fullpath;
53 | }
54 | });
55 | }
56 |
57 | if (!extension_loaded('xdebug')) {
58 | set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline): bool {
59 | if ((error_reporting() & $errno) === 0) {
60 | return false;
61 | }
62 |
63 | $type = static function (int $errno): string {
64 | $constants = get_defined_constants(true);
65 | if (array_key_exists('Core', $constants)) {
66 | foreach ($constants['Core'] as $constant => $value) {
67 | if ($value === $errno && str_starts_with($constant, 'E_')) {
68 | return $constant;
69 | }
70 | }
71 | }
72 |
73 | return 'E_UNKNOWN';
74 | };
75 |
76 | $errstr = htmlspecialchars($errstr);
77 |
78 | echo '';
79 | echo $type($errno).': '.$errstr.' in '.$errfile.' on line '.$errline;
80 | echo '
';
81 |
82 | return true;
83 | });
84 | }
85 |
--------------------------------------------------------------------------------
/templates/components/alert.twig:
--------------------------------------------------------------------------------
1 | {% if alert_color == 'success' %}
2 | {% set alert_color = 'bg-green-500' %}
3 | {% elseif alert_color == 'error' %}
4 | {% set alert_color = 'bg-red-500' %}
5 | {% endif %}
6 |
7 |
8 |
{{ message|raw }}
9 |
12 |
13 |
--------------------------------------------------------------------------------
/templates/components/badge.twig:
--------------------------------------------------------------------------------
1 | {{ text|raw }}
2 |
--------------------------------------------------------------------------------
/templates/components/button.twig:
--------------------------------------------------------------------------------
1 | {% if btn_green %}
2 | {% set btn_color = 'bg-green-500 hover:bg-green-600 focus:ring-green-200 disabled:bg-green-300 dark:bg-green-700 dark:hover:bg-green-800 dark:focus:ring-green-400 dark:disabled:bg-green-400' %}
3 | {% elseif btn_red %}
4 | {% set btn_color = 'bg-red-500 hover:bg-red-600 focus:ring-red-200 disabled:bg-red-300 dark:bg-red-700 dark:hover:bg-red-800 dark:focus:ring-red-400 dark:disabled:bg-red-400' %}
5 | {% else %}
6 | {% set btn_color = 'bg-primary-500 hover:bg-primary-600 focus:ring-primary-200 disabled:bg-primary-300 dark:bg-primary-700 dark:hover:bg-primary-800 dark:focus:ring-primary-400 dark:disabled:bg-primary-400' %}
7 | {% endif %}
8 |
9 | {% set classes = 'text-sm inline-flex items-center gap-1 border border-transparent rounded-sm py-1.5 px-3 font-semibold text-white align-middle shadow-sm focus:ring-3 dark:text-white ' ~ btn_color %}
10 |
11 | {% if link %}
12 | {{ icon ? svg(icon, 12) : '' }}{{ text }}
13 | {% else %}
14 | {% macro attr(attributes) %}
15 | {% for name, value in attributes %}
16 | {% if value is not empty %}
17 | {{ name }}="{{ value|e }}"
18 | {% else %}
19 | {{ name }}
20 | {% endif %}
21 | {% endfor %}
22 | {% endmacro %}
23 |
24 | {% set id = id is not empty ? 'id="' ~ id ~ '" name="' ~ id ~ '" ' : '' %}
25 |
26 |
29 | {% endif %}
30 |
--------------------------------------------------------------------------------
/templates/components/checkbox.twig:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/templates/components/input.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | {% if help %}
7 | {{ help }}
8 | {% endif %}
9 |
10 |
--------------------------------------------------------------------------------
/templates/components/modal.twig:
--------------------------------------------------------------------------------
1 | {{ include('components/button.twig', open_btn|merge({attributes: {'data-modal-target': '#' ~ modal_id}})) }}
2 |
3 | {% macro modal(modal_id, title, content) %}
4 |
5 |
6 |
7 |
8 |
9 |
{{ title }}
10 |
11 |
14 |
15 |
16 |
{{ content|raw }}
17 |
18 |
19 | {% endmacro %}
20 |
21 | {{ add_global('modals', _self.modal(modal_id, title, content)) }}
22 |
--------------------------------------------------------------------------------
/templates/components/paginator.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 | Showing
4 | {{ first_on_page }} to
5 | {{ last_on_page }} of
6 | {{ total }} items
7 |
8 |
9 |
10 | {{ include('components/select.twig', {id: 'per_page', options: select, selected: per_page, width: 'w-24', wrapper_class: false}) }}
11 |
12 |
13 |
14 | {% if pages and pages|length > 1 %}
15 |
33 | {% endif %}
34 |
35 |
--------------------------------------------------------------------------------
/templates/components/select.twig:
--------------------------------------------------------------------------------
1 |
2 | {% if label %}
3 |
4 | {% endif %}
5 |
12 |
13 |
--------------------------------------------------------------------------------
/templates/components/tabs.twig:
--------------------------------------------------------------------------------
1 |
2 | {% for link, title in links %}
3 | {% set active = get('tab', links|keys|first) == link ? ' shadow-sm bg-white dark:bg-gray-700' : ' hover:text-gray-600 dark:hover:text-gray-300' %}
4 | -
5 |
6 | {{ title }}
7 |
8 |
9 | {% endfor %}
10 |
11 |
--------------------------------------------------------------------------------
/templates/components/textarea.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/templates/dashboards/apcu.twig:
--------------------------------------------------------------------------------
1 | {{ include('partials/keys_list.twig', {
2 | treeview: true,
3 | buttons: {
4 | import_btn: true,
5 | export_btn: true,
6 | add_new_btn: true,
7 | },
8 | head_items: [
9 | {'title': 'Key', 'sort': 'link_title'},
10 | {'title': 'Size', 'class': 'w-24', 'sort': 'bytes_size'},
11 | {'title': 'Hits', 'class': 'w-16', 'sort': 'number_hits'},
12 | {'title': 'Last used', 'class': 'w-32', 'sort': 'timediff_last_used'},
13 | {'title': 'Created', 'class': 'w-44 hidden md:table-cell', 'sort': 'time_created'},
14 | {'title': 'TTL', 'class': 'w-32', 'sort': 'ttl'},
15 | ],
16 | classes: {
17 | 4: 'hidden md:table-cell',
18 | },
19 | }) }}
20 |
--------------------------------------------------------------------------------
/templates/dashboards/memcached.twig:
--------------------------------------------------------------------------------
1 | {{ include('components/tabs.twig', {
2 | links: {
3 | 'keys': 'Keys',
4 | 'commands_stats': 'Commands Stats',
5 | },
6 | }) }}
7 |
8 | {% if get('tab', 'keys') == 'keys' %}
9 | {{ include('partials/keys_list.twig', {
10 | treeview: true,
11 | buttons: {
12 | import_btn: true,
13 | export_btn: true,
14 | add_new_btn: true,
15 | },
16 | head_items: [
17 | {'title': 'Key', 'sort': 'link_title'},
18 | {'title': 'Size', 'class': 'w-24', 'sort': 'bytes_size'},
19 | {'title': 'Last used', 'class': 'w-32', 'sort': 'timediff_last_access'},
20 | {'title': 'TTL', 'class': 'w-32', 'sort': 'ttl'},
21 | ],
22 | }) }}
23 | {% endif %}
24 |
25 | {% if get('tab') == 'commands_stats' %}
26 |
27 |
28 | {% for command in commands %}
29 | {{ include('partials/panel.twig', {
30 | panel_title: command.title,
31 | array: command.data,
32 | }) }}
33 | {% endfor %}
34 |
35 |
36 | {% endif %}
37 |
--------------------------------------------------------------------------------
/templates/dashboards/opcache.twig:
--------------------------------------------------------------------------------
1 | {{ include('partials/keys_list.twig', {
2 | custom_buttons: [
3 | include('components/button.twig', {
4 | text: (is_ignored ? 'Show' : 'Ignore') ~ ' scripts from this tool',
5 | link: link(['pp', 'p'], {'ignore': (is_ignored ? 'no' : 'yes')}),
6 | })
7 | ],
8 | head_items: [
9 | {'title': 'Filename', 'sort': 'title'},
10 | {'title': 'Hits', 'class': 'w-16', 'sort': 'number_hits'},
11 | {'title': 'Memory', 'class': 'w-24', 'sort': 'bytes_memory'},
12 | {'title': 'Last used', 'class': 'w-32', 'sort': 'timediff_last_used'},
13 | {'title': 'Created', 'class': 'w-44 hidden md:table-cell', 'sort': 'time_created'},
14 | ],
15 | classes: {
16 | 5: 'hidden md:table-cell',
17 | },
18 | keys: cached_scripts,
19 | }) }}
20 |
--------------------------------------------------------------------------------
/templates/dashboards/realpath.twig:
--------------------------------------------------------------------------------
1 | {{ include('partials/keys_list.twig', {
2 | buttons: {
3 | delete_selected_btn: false,
4 | },
5 | head_items: [
6 | {'title': 'Path', 'sort': 'title'},
7 | {'title': 'Realpath', 'sort': 'realpath'},
8 | {'title': 'Is dir', 'class': 'w-16', 'sort': 'is_dir'},
9 | {'title': 'TTL', 'class': 'w-16', 'sort': 'ttl'},
10 | ],
11 | hide_actions: true,
12 | }) }}
13 |
--------------------------------------------------------------------------------
/templates/dashboards/redis/form.twig:
--------------------------------------------------------------------------------
1 |
79 |
80 |
89 |
--------------------------------------------------------------------------------
/templates/dashboards/redis/redis.twig:
--------------------------------------------------------------------------------
1 | {{ include('components/tabs.twig', {
2 | links: {
3 | 'keys': 'Keys',
4 | 'slowlog': 'Slow Log',
5 | },
6 | }) }}
7 |
8 | {% if get('tab', 'keys') == 'keys' %}
9 | {{ include('partials/keys_list.twig', {
10 | treeview: true,
11 | buttons: {
12 | import_btn: true,
13 | export_btn: true,
14 | add_new_btn: true,
15 | },
16 | head_items: [
17 | {'title': 'Key', 'sort': 'link_title'},
18 | {'title': 'Size', 'class': 'w-24', 'sort': 'bytes_size'},
19 | {'title': 'Type', 'class': 'w-24 hidden md:table-cell', 'sort': 'type'},
20 | {'title': 'TTL', 'class': 'w-32', 'sort': 'ttl'},
21 | ],
22 | classes: {
23 | 2: 'hidden md:table-cell',
24 | },
25 | }) }}
26 | {% endif %}
27 |
28 | {% if get('tab') == 'slowlog' %}
29 |
30 |
31 |
53 |
54 |
55 | {{ include('components/button.twig', {
56 | text: 'Reset',
57 | link: link(['tab'], {'resetlog': 1}),
58 | btn_green: true,
59 | }) }}
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | ID |
68 | Timestamp |
69 | Duration |
70 | Command |
71 |
72 |
73 |
74 | {% for item in slowlog.items %}
75 |
76 | {{ item[0] }} |
77 | {{ item[1]|time }} |
78 | {{ item[2] }}μs |
79 | {{ item[3]|join(' ') }} |
80 |
81 | {% else %}
82 |
83 | No items. |
84 |
85 | {% endfor %}
86 |
87 |
88 |
89 |
90 | {% endif %}
91 |
--------------------------------------------------------------------------------
/templates/dashboards/server.twig:
--------------------------------------------------------------------------------
1 |
2 | {% for panel in panels %}
3 | {{ include('partials/info_table.twig', {
4 | panel_title: panel.title,
5 | array: panel.data,
6 | }) }}
7 | {% endfor %}
8 |
9 |
10 | {{ include('components/button.twig', {
11 | text: 'PHP Info',
12 | link: link([], {'moreinfo': 1}),
13 | class: 'mb-4',
14 | }) }}
15 |
16 | Loaded PHP extensions ({{ extensions|length }})
17 |
18 |
30 |
31 | You can click on these badges, and it will redirect you to the appropriate section in phpinfo().
32 |
--------------------------------------------------------------------------------
/templates/layout.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ site_title }} - phpCacheAdmin
7 |
8 |
9 |
10 |
11 | {%- if colors -%}
12 |
17 | {%- endif %}
18 |
31 |
32 |
33 |
34 |
38 |
39 |
40 | {% for link, item in nav %}
41 | {% set link_bg = item.colors ? ' style="--link-bg:' ~ item.colors.500 ~ ';--link-bg-hover:' ~ item.colors.700 ~ ';--link-active:' ~ item.colors.300 ~ ';--link-active-dark:' ~ item.colors.900 ~ ';"' : '' %}
42 | {% set server = get('server') and get('dashboard') == link ? '&server=' ~ get('server') : '' %}
43 |
44 | {{ svg(item.icon ?? ('dashboards/' ~ item.key), 16) }}
45 |
46 | {% endfor %}
47 |
48 |
49 |
50 | {% if logout_url %}
51 |
{{ svg('logout', 32) }}
52 | {% endif %}
53 |
54 |
55 |
58 |
61 |
64 |
65 |
66 |
{{ svg('github', 28) }}
67 |
68 |
69 |
70 | {{ alerts|raw }}
71 |
72 | {% if side %}
73 |
74 |
{{ side|raw }}
75 |
76 |
{{ dashboard|raw }}
77 |
78 | {% else %}
79 | {{ dashboard|raw }}
80 | {% endif %}
81 |
82 |
83 |
94 | {{ modals|raw }}
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/templates/partials/form.twig:
--------------------------------------------------------------------------------
1 |
45 |
--------------------------------------------------------------------------------
/templates/partials/import_form.twig:
--------------------------------------------------------------------------------
1 | {% macro form() %}
2 |
22 | {% endmacro %}
23 |
24 | {{ include('components/modal.twig', {
25 | modal_id: 'importkeys',
26 | title: 'Import keys',
27 | content: _self.form(),
28 | open_btn: {
29 | text: 'Import',
30 | icon: 'import',
31 | btn_green: true,
32 | },
33 | }) }}
34 |
--------------------------------------------------------------------------------
/templates/partials/info.twig:
--------------------------------------------------------------------------------
1 | {% for panel in panels %}
2 | {% if panel %}
3 | {% if not panel.error %}
4 | {{ include('partials/panel.twig', {
5 | panel_title: panel.title,
6 | array: panel.data,
7 | moreinfo: panel.moreinfo,
8 | }) }}
9 | {% else %}
10 | {{ panel.error }}
11 | {% endif %}
12 | {% endif %}
13 | {% endfor %}
14 |
--------------------------------------------------------------------------------
/templates/partials/info_table.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ panel_title }}
4 |
5 |
6 |
7 |
8 |
9 | {% for name, value in array %}
10 | {% if value is iterable and value|length > 0 %}
11 |
12 |
13 | {{ name|replace({'_': ' '}) }}
14 | |
15 |
16 | {% for sub_name, sub_value in value %}
17 |
18 | {{ sub_name }} |
19 |
20 | {% if sub_value is not iterable %}
21 | {{ sub_value|raw }}
22 | {% else %}
23 | {% for node, node_value in sub_value %}
24 | Node {{ node }}: {{ node_value|raw }}
25 | {% endfor %}
26 | {% endif %}
27 | |
28 |
29 | {% endfor %}
30 | {% elseif not value is iterable %}
31 |
32 | {{ name }} |
33 | {{ value|raw }} |
34 |
35 | {% endif %}
36 | {% endfor %}
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/templates/partials/key_type_badge.twig:
--------------------------------------------------------------------------------
1 | {% macro key_type(type) %}
2 | {% if type == 'string' %}
3 | {% set type_color = 'bg-green-500' %}
4 | {% elseif type == 'set' %}
5 | {% set type_color = 'bg-sky-500' %}
6 | {% elseif type == 'list' %}
7 | {% set type_color = 'bg-orange-500' %}
8 | {% elseif type == 'zset' %}
9 | {% set type_color = 'bg-red-500' %}
10 | {% elseif type == 'hash' %}
11 | {% set type_color = 'bg-purple-500' %}
12 | {% elseif type == 'stream' %}
13 | {% set type_color = 'bg-slate-500' %}
14 | {% else %}
15 | {% set type_color = 'bg-blue-500' %}
16 | {% endif %}
17 |
18 | {{ include('components/badge.twig', {text: type, class: 'uppercase font-bold', bg: type_color}) }}
19 | {% endmacro %}
20 |
--------------------------------------------------------------------------------
/templates/partials/keys_list.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% if treeview %}
5 |
13 | {% endif %}
14 |
15 | {% if all_keys != 0 %}
16 |
17 |
22 | {{ include('components/button.twig', {
23 | text: '',
24 | icon: 'search',
25 | id: 'submit_search',
26 | class: 'rounded-l-none',
27 | }) }}
28 |
29 | {% endif %}
30 |
31 |
32 |
33 | {% for button in custom_buttons %}
34 | {{ button|raw }}
35 | {% endfor %}
36 |
37 | {% if buttons.import_btn %}
38 | {{ include('partials/import_form.twig') }}
39 | {% endif %}
40 |
41 | {% if buttons.export_btn and all_keys != 0 %}
42 | {{ include('components/button.twig', {
43 | text: 'Export',
44 | icon: 'export',
45 | link: link([], {'export_btn': 1}),
46 | btn_green: true,
47 | }) }}
48 | {% endif %}
49 |
50 | {% if buttons.add_new_btn %}
51 | {{ include('components/button.twig', {
52 | text: 'Add new',
53 | icon: 'plus',
54 | link: link([], {'form': 'new'}),
55 | btn_green: true,
56 | }) }}
57 | {% endif %}
58 |
59 | {% if all_keys != 0 %}
60 | {{ include('components/button.twig', {
61 | text: 'Delete all',
62 | icon: 'trash',
63 | id: 'delete_all',
64 | btn_red: true,
65 | }) }}
66 |
67 | {% if buttons.delete_selected_btn is not defined or buttons.delete_selected_btn == true %}
68 | {{ include('components/button.twig', {
69 | text: 'Delete selected',
70 | icon: 'trash',
71 | id: 'delete_selected',
72 | btn_red: true,
73 | }) }}
74 | {% endif %}
75 | {% endif %}
76 |
77 |
78 |
79 | {% if treeview and get('view', config('list-view', 'table')) == 'tree' %}
80 | {{ include('partials/tree_view.twig') }}
81 | {% else %}
82 | {{ include('partials/table_view.twig') }}
83 | {% endif %}
84 |
85 |
86 | No keys.
87 |
88 |
{{ paginator|raw }}
89 |
90 |
--------------------------------------------------------------------------------
/templates/partials/panel.twig:
--------------------------------------------------------------------------------
1 | {% macro progress(percentage, type='lower') %}
2 | {% if type == 'higher' %}{# higher is better #}
3 | {% if percentage >= 80 %}
4 | {% set color = 'bg-green-600' %}
5 | {% elseif percentage >= 50 %}
6 | {% set color = 'bg-orange-600' %}
7 | {% else %}
8 | {% set color = 'bg-red-600' %}
9 | {% endif %}
10 | {% else %}{# lower is better #}
11 | {% if percentage <= 50 %}
12 | {% set color = 'bg-green-600' %}
13 | {% elseif percentage <= 80 %}
14 | {% set color = 'bg-orange-600' %}
15 | {% else %}
16 | {% set color = 'bg-red-600' %}
17 | {% endif %}
18 | {% endif %}
19 |
20 |
23 | {% endmacro %}
24 |
25 |
26 |
27 |
28 |
{{ panel_title }}
29 | {% if moreinfo %}
30 |
More info
31 | {% endif %}
32 |
33 |
34 |
35 |
36 | {% for name, value in array %}
37 | {% if name %}
38 |
39 |
40 |
{{ value is iterable ? value[0] : name }}
41 |
{{ (value is iterable ? value[1] : value)|raw }}
42 |
43 |
44 | {% if value[2] is not null %}
45 | {{ _self.progress(value[2], (value[3] ?? 'lower')) }}
46 | {% endif %}
47 |
48 | {% endif %}
49 | {% endfor %}
50 |
51 |
52 |
--------------------------------------------------------------------------------
/templates/partials/table_view.twig:
--------------------------------------------------------------------------------
1 | {% set show_actions = hide_actions is not defined or hide_actions == false %}
2 |
3 |
4 |
5 |
6 | {% if show_actions %}
7 | {{ include('components/checkbox.twig', {disabled: keys|length == 0, class: 'check-all'}) }} |
8 | {% endif %}
9 | {% for item in head_items %}
10 | {% set is_active = sortcol == item.sort %}
11 | {% set data_sort = item.sort ? ' data-sortdir="' ~ sortdir ~ '" data-sortcol="' ~ item.sort ~ '"' : '' %}
12 |
13 |
14 |
15 | {{ item.title }}
16 | {% if item.sort %}
17 | {{ svg('down', 10, (is_active ? '' : 'opacity-30') ~ (is_active and sortdir == 'asc' ? ' rotate-180' : '')) }}
18 | {% endif %}
19 |
20 | |
21 | {% endfor %}
22 | {% if show_actions %}
23 | Actions |
24 | {% endif %}
25 |
26 |
27 |
28 | {% for key in keys %}
29 |
30 | {% if show_actions %}
31 | {{ include('components/checkbox.twig', {class: 'check-key'}) }} |
32 | {% endif %}
33 | {% for item_key, item in key.info %}
34 | {% set is_title = item_key == 'title' or (item_key == 'link_title' and view_key) %}
35 | {% set td_class = is_title ? ' max-w-xs md:max-w-md truncate hover:text-clip hover:break-all hover:whitespace-normal' : '' %}
36 |
37 | {% if item_key == 'link_title' and view_key %}
38 | {% set link = view_key|replace({'__key__': key.key}) %}
39 | {{ item }}
40 | {% elseif item_key == 'type' %}
41 | {% import 'partials/key_type_badge.twig' as key_badge %}
42 | {{- key_badge.key_type(item) -}}
43 | {% elseif item_key starts with 'number_' %}
44 | {{- item|number -}}
45 | {% elseif item_key starts with 'time_' %}
46 | {{- item|time -}}
47 | {% elseif item_key starts with 'timediff_' and item is not empty %}
48 | {{- item|timediff -}}
49 | {% elseif item_key starts with 'bytes_' %}
50 | {{- item|bytes -}}
51 | {% else %}
52 | {{- item -}}
53 | {% endif %}
54 | |
55 | {% endfor %}
56 | {% if show_actions %}
57 |
58 |
61 | |
62 | {% endif %}
63 |
64 | {% endfor %}
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/templates/partials/tree_view.twig:
--------------------------------------------------------------------------------
1 | {% set show_actions = hide_actions is not defined or hide_actions == false %}
2 | {% macro render_tree(items, level=0, show_actions, view_key) %}
3 | {% for key, item in items %}
4 | {% if item.type == 'folder' %}
5 |
6 |
7 |
11 | ({{ item.count }})
12 |
13 |
14 | {{ _self.render_tree(item.children, level + 1, show_actions, view_key) }}
15 |
16 |
17 | {% else %}
18 |
19 |
20 |
21 | {% if show_actions %}
22 | {{ include('components/checkbox.twig', {class: 'check-key mt-0'}) }}
23 | {% endif %}
24 |
25 |
26 | {% set link = view_key|replace({'__key__': item.key}) %}
27 |
28 | {{ item.name }}
29 |
30 |
31 |
32 |
33 |
34 | {% for item_key, kitem in item.info %}
35 | {% if item_key == 'type' %}
36 | {% import 'partials/key_type_badge.twig' as key_badge %}
37 | {{- key_badge.key_type(kitem) -}}
38 | {% elseif item_key starts with 'number_' %}
39 | {{- kitem|number -}}
40 | {% elseif item_key starts with 'time_' %}
41 | {{- kitem|time -}}
42 | {% elseif item_key starts with 'timediff_' and kitem is not empty %}
43 | {{- kitem|timediff -}}
44 | {% elseif item_key starts with 'bytes_' %}
45 | {{- kitem|bytes -}}
46 | {% else %}
47 | {{- kitem -}}
48 | {% endif %}
49 | {% endfor %}
50 |
51 | {% if show_actions %}
52 |
55 | {% endif %}
56 |
57 |
58 |
59 | {% endif %}
60 | {% endfor %}
61 | {% endmacro %}
62 |
63 |
64 |
65 |
{{ include('components/checkbox.twig', {disabled: keys|length == 0, class: 'check-all'}) }}
66 |
69 |
70 |
71 | {{ _self.render_tree(keys, 0, show_actions, view_key) }}
72 |
73 |
74 |
--------------------------------------------------------------------------------
/templates/partials/view_key.twig:
--------------------------------------------------------------------------------
1 | {% import 'partials/key_type_badge.twig' as key_badge %}
2 | {{ key }}
3 |
4 | {% if type %}
5 |
Type {{ key_badge.key_type(type) }}
6 | {% endif %}
7 |
8 | {% if ttl %}
9 |
10 | TTL
11 | {{ include('components/badge.twig', {
12 | text: ttl == -1 ? 'Doesn\'t expire' : ttl,
13 | class: 'uppercase font-bold',
14 | bg: 'bg-slate-500 dark:bg-slate-700',
15 | }) }}
16 |
17 | {% endif %}
18 |
19 | {% if size %}
20 |
21 | Size
22 | {{ include('components/badge.twig', {
23 | text: size,
24 | class: 'font-bold',
25 | bg: 'bg-sky-500 dark:bg-sky-700',
26 | }) }}
27 |
28 | {% endif %}
29 |
30 | {% if encode_fn %}
31 |
Encoded with {{ include('components/badge.twig', {text: encode_fn, class: 'uppercase font-bold', bg: 'bg-blue-500 dark:bg-blue-700'}) }}
32 | {% set encode_url = '&encoder=' ~ encode_fn %}
33 | {% endif %}
34 |
35 | {% if formatted == true %}
36 | {{ include('components/badge.twig', {text: 'Formatted', class: 'uppercase font-bold', bg: 'bg-blue-500 dark:bg-blue-700'}) }}
37 | {% endif %}
38 |
39 |
40 | {% if export_url %}
41 |
42 | {{ svg('export', 16) }} Export
43 |
44 | {% endif %}
45 |
46 | {% if delete_url %}
47 |
49 | {{ svg('trash', 16) }} Delete
50 |
51 | {% endif %}
52 |
53 |
54 |
55 | {% if value is iterable %}
56 | {{ include('partials/view_key_array.twig') }}
57 | {% else %}
58 | {% if edit_url %}
59 |
60 | {{ svg('edit', 16) }} Edit
61 |
62 | {% endif %}
63 |
64 |
65 | {{ value|raw }}
66 |
67 |
68 | {% endif %}
69 |
--------------------------------------------------------------------------------
/templates/partials/view_key_array.twig:
--------------------------------------------------------------------------------
1 | {% if type not in types.extra.hide_edit %}
2 | {{ include('components/button.twig', {
3 | text: 'Add another value',
4 | icon: 'plus',
5 | link: add_subkey_url,
6 | btn_green: true,
7 | class: 'mb-4',
8 | }) }}
9 | {% endif %}
10 |
11 | {% for item in value %}
12 |
13 |
14 |
15 | {% if type not in types.extra.hide_title %}
16 | {{ types[type].title }} {{ include('components/badge.twig', {text: item.sub_key, class: 'font-bold', bg: 'bg-slate-400 dark:bg-slate-600'}) }}
17 | {% endif %}
18 |
19 | {% if item.encode_fn %}
20 |
21 | Encoded with
22 | {{ include('components/badge.twig', {text: item.encode_fn, class: 'uppercase font-bold', bg: 'bg-blue-500 dark:bg-blue-600'}) }}
23 |
24 |
25 | {% set item_encode_url = '&encoder=' ~ item.encode_fn %}
26 | {% endif %}
27 |
28 | {% if item.formatted == true %}
29 | {{ include('components/badge.twig', {text: 'Formatted', class: 'uppercase font-bold', bg: 'bg-blue-500 dark:bg-blue-600'}) }}
30 | {% endif %}
31 |
32 |
33 |
44 |
45 |
46 |
47 | {{ item.value|raw }}
48 |
49 |
50 | {% endfor %}
51 |
52 | {{ paginator|raw }}
53 |
--------------------------------------------------------------------------------
/twig.phar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobiNN1/phpCacheAdmin/f68bc366231ed6665fe94aa6d8f0d70dab974633/twig.phar
--------------------------------------------------------------------------------