1<?php
2/**
3 * Copyright © 2019-2026 Rhubarb Tech Inc. All Rights Reserved.
4 *
5 * The Object Cache Pro Software and its related materials are property and confidential
6 * information of Rhubarb Tech Inc. Any reproduction, use, distribution, or exploitation
7 * of the Object Cache Pro Software and its related materials, in whole or in part,
8 * is strictly forbidden unless prior permission is obtained from Rhubarb Tech Inc.
9 *
10 * In addition, any reproduction, use, distribution, or exploitation of the Object Cache Pro
11 * Software and its related materials, in whole or in part, is subject to the End-User License
12 * Agreement accessible in the included `LICENSE` file, or at: https://objectcache.pro/eula
13 */
14
15declare(strict_types=1);
16
17namespace RedisCachePro\Plugin;
18
19use RedisCachePro\Plugin;
20use RedisCachePro\Diagnostics\Diagnostics;
21
22/**
23 * @mixin \RedisCachePro\Plugin
24 */
25trait Meta
26{
27 /**
28 * Boot Meta component and register hooks.
29 *
30 * @return void
31 */
32 public function bootMeta()
33 {
34 add_filter('plugins_api', [$this, 'pluginInformation'], 1000, 3);
35
36 add_filter('plugin_row_meta', [$this, 'pluginRowMeta'], 10, 4);
37
38 add_filter('plugin_action_links_object-cache.php', [$this, 'actionLinks']);
39 add_filter("plugin_action_links_{$this->basename}", [$this, 'actionLinks']);
40 add_filter("network_admin_plugin_action_links_{$this->basename}", [$this, 'actionLinks']);
41
42 add_filter('manage_sites_action_links', [$this, 'siteActionLinks'], 10, 2);
43
44 // allow plugin information to be viewed in version controlled environments
45 if ($this->isPluginInformationRequest()) {
46 add_filter('file_mod_allowed', '__return_true');
47 }
48 }
49
50 /**
51 * Adds useful links to the meta row of the plugin, must-use plugin and drop-in.
52 *
53 * @param array<string> $links
54 * @param string $file
55 * @param array<string, string> $data
56 * @param string $status
57 * @return array<string>
58 */
59 public function pluginRowMeta($links, $file, $data, $status)
60 {
61 if (! preg_match('/(Object|Redis) Cache Pro/', $data['Name'])) {
62 return $links;
63 }
64
65 if ($file !== $this->basename && $file !== 'object-cache.php') {
66 return $links;
67 }
68
69 $append = [];
70
71 if ($file === 'object-cache.php' || $status === 'mustuse') {
72 $append[] = sprintf(
73 '<a href="%s" class="thickbox open-plugin-details-modal">View details</a>',
74 self_admin_url('plugin-install.php?' . http_build_query([
75 'tab' => 'plugin-information',
76 'plugin' => $this->slug(),
77 'section' => 'changelog',
78 'TB_iframe' => 'true',
79 'width' => '600',
80 'height' => '800',
81 ]))
82 );
83 }
84
85 $append[] = sprintf('<a href="%s" target="_blank">Docs</a>', 'https://objectcache.pro/docs/');
86
87 return array_merge($links, $append);
88 }
89
90 /**
91 * Adds useful links to the plugin action link list.
92 *
93 * @param array<string> $links
94 * @return array<string>
95 */
96 public function actionLinks($links)
97 {
98 global $wp_version;
99
100 if (version_compare($wp_version, '5.2', '>=') && ! is_network_admin()) {
101 $links = array_merge([
102 sprintf('<a href="%s">Health</a>', admin_url('site-health.php')),
103 ], $links);
104 }
105
106 if (current_user_can(Plugin::Capability)) {
107 $links = array_merge([
108 sprintf('<a href="%s">Settings</a>', network_admin_url($this->baseurl)),
109 ], $links);
110 }
111
112 return $links;
113 }
114
115 /**
116 * Adds a "Flush" link to sites in "Network Admin -> Sites".
117 *
118 * @param array<string> $actions
119 * @param int $blog_id
120 * @return array<string>
121 */
122 public function siteActionLinks($actions, $blog_id)
123 {
124 if (! $this->blogFlushingEnabled()) {
125 return $actions;
126 }
127
128 if (! current_user_can(Plugin::Capability) || ! current_user_can('manage_sites')) {
129 return $actions;
130 }
131
132 if (! $this->diagnostics()->ping()) {
133 return $actions;
134 }
135
136 array_splice($actions, 1, 0, [
137 sprintf(
138 '<a href="%s" title="Flush Object Cache">Flush</a>',
139 esc_url(wp_nonce_url(
140 network_admin_url("sites.php?action=flush-blog-object-cache&id={$blog_id}"),
141 "flushblog_{$blog_id}"
142 ))
143 ),
144 ]);
145
146 return $actions;
147 }
148
149 /**
150 * Fetch plugin information for update modal.
151 *
152 * @param object|array<mixed>|false $result
153 * @param string $action
154 * @param object $args
155 * @return object|array<mixed>|false
156 */
157 public function pluginInformation($result, $action = null, $args = null)
158 {
159 if ($action !== 'plugin_information') {
160 return $result;
161 }
162
163 if ($this->slug() === ($args->slug ?? null)) {
164 $info = $this->pluginInfoRequest();
165
166 if (is_wp_error($info)) {
167 return false;
168 }
169
170 if (Diagnostics::isMustUse()) {
171 $info->download_link = null;
172 }
173
174 $info->icons = (array) $info->icons; // @phpstan-ignore-line
175 $info->banners = (array) $info->banners; // @phpstan-ignore-line
176 $info->sections = (array) $info->sections; // @phpstan-ignore-line
177 $info->contributors = array_map('get_object_vars', (array) $info->contributors); // @phpstan-ignore-line
178
179 return $info;
180 }
181
182 return $result;
183 }
184
185 /**
186 * Determines whether the request is for this plugin's information.
187 *
188 * @return bool
189 */
190 public function isPluginInformationRequest()
191 {
192 global $pagenow;
193
194 if ($pagenow !== 'plugin-install.php') {
195 return false;
196 }
197
198 if (($_GET['tab'] ?? '') !== 'plugin-information') {
199 return false;
200 }
201
202 if (($_GET['plugin'] ?? '') !== $this->slug()) {
203 return false;
204 }
205
206 return true;
207 }
208}
209