PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / trunk
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback vtrunk
5.1.3 5.1.2 5.1.1 5.1 5.0 trunk 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2.0 3.2.1 3.22 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.3.0 3.3.1 3.3.2 3.3.2.1 3.3.2.2 3.3.3 3.30 3.31 3.32 3.4 3.4.1 3.4.3 3.4.4 3.5 3.5.1 3.6 3.6.1 3.7 3.8 3.9 3.9.1 3.9.2 3.9.3 3.9.4 3.9.6 3.9.6.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.4
atarim-visual-collaboration / third-party / backup / jetbackup / class-avcf-abilities-jetbackup-destinations.php
atarim-visual-collaboration / third-party / backup / jetbackup Last commit date
class-avcf-abilities-jetbackup-backups.php 2 days ago class-avcf-abilities-jetbackup-destinations.php 2 weeks ago class-avcf-abilities-jetbackup-jobs.php 2 weeks ago class-avcf-abilities-jetbackup-queue.php 2 days ago class-avcf-abilities-jetbackup-restore-point.php 2 days ago class-avcf-abilities-jetbackup-restore.php 2 days ago class-avcf-abilities-jetbackup-schedules.php 2 weeks ago class-avcf-abilities-jetbackup-settings.php 2 weeks ago class-avcf-abilities-jetbackup-system.php 2 weeks ago class-avcf-jetbackup-detector.php 2 weeks ago class-avcf-jetbackup-helpers.php 2 days ago
class-avcf-abilities-jetbackup-destinations.php
292 lines
1 <?php
2 /**
3 * JetBackup — Destinations ability cluster.
4 *
5 * Namespaced atarim/jetbackup-* over JetBackup's destination AJAX Calls:
6 * list/get destinations, save (create/edit), delete, toggle enabled,
7 * validate (test connection), and toggle export-config membership.
8 *
9 * ManageDestination is isset-gated for most fields (so partial edits are safe
10 * and omitted options/credentials are left untouched), BUT it applies
11 * export_config unconditionally — so on edit we read and re-send the current
12 * value unless the caller overrides it, to avoid silently clearing it. Saving a
13 * destination also registers + connects it (a live connection test), and
14 * non-local destination types require a valid JetBackup license.
15 *
16 * The options object is provider-specific (S3/FTP/SFTP/Google Drive/OneDrive/
17 * Dropbox/Box/pCloud/JetBackup Storage/Local): read an existing destination with
18 * jetbackup-get-destination to see the exact type + options shape before saving.
19 *
20 * Gated by manage_options; delete requires confirm:true. Built on JetBackup
21 * 3.1.23.x; not runtime-tested here.
22 *
23 * @package atarim-visual-collaboration
24 */
25
26 if ( ! defined( 'ABSPATH' ) ) {
27 exit;
28 }
29
30 class AVCF_Abilities_JetBackup_Destinations extends AVCF_Abilities_Base {
31
32 /** @var AVCF_JetBackup_Detector */
33 private $detector;
34
35 public function __construct() {
36 $this->detector = new AVCF_JetBackup_Detector();
37 }
38
39 public function register() {
40 if ( ! $this->detector->avcf_jetbackup_is_available() ) {
41 return;
42 }
43 $this->register_list_destinations();
44 $this->register_get_destination();
45 $this->register_save_destination();
46 $this->register_delete_destination();
47 $this->register_toggle_destination();
48 $this->register_validate_destination();
49 $this->register_toggle_destination_export_config();
50 }
51
52 /* ------------------------------ shared ----------------------------- */
53
54 private function ro_meta() {
55 return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => true, 'destructive' => false, 'idempotent' => true ] ];
56 }
57 private function write_meta( $d = false ) {
58 return [ 'mcp' => [ 'public' => true, 'type' => 'tool' ], 'annotations' => [ 'readonly' => false, 'destructive' => (bool) $d, 'idempotent' => false ] ];
59 }
60 public function can() {
61 return AVCF_JetBackup_Helpers::can_manage();
62 }
63 private function out_schema() {
64 return [
65 'type' => 'object',
66 'properties' => [
67 'success' => [ 'type' => 'boolean' ],
68 'message' => [ 'type' => 'string' ],
69 'data' => [ 'type' => 'object' ],
70 ],
71 'required' => [ 'success', 'message' ],
72 ];
73 }
74 private function id_schema( $desc ) {
75 return [
76 'type' => 'object',
77 'properties' => [ 'id' => [ 'type' => 'integer', 'minimum' => 1, 'description' => $desc ] ],
78 'required' => [ 'id' ],
79 'additionalProperties' => false,
80 ];
81 }
82 private function missing_id( $what ) {
83 return [ 'success' => false, 'message' => sprintf( 'Missing id (%s).', $what ), 'data' => [] ];
84 }
85
86 /* ------------------------- list-destinations ------------------------ */
87
88 private function register_list_destinations() {
89 $self = $this;
90 wp_register_ability( 'atarim/jetbackup-list-destinations', [
91 'label' => 'List JetBackup Destinations',
92 'category' => 'atarim',
93 'description' => 'List configured JetBackup backup destinations (paginated). Optional skip, limit (1-100, default 50).',
94 'input_schema' => [
95 'type' => 'object',
96 'properties' => [
97 'skip' => [ 'type' => 'integer', 'minimum' => 0, 'default' => 0 ],
98 'limit' => [ 'type' => 'integer', 'minimum' => 1, 'maximum' => 100, 'default' => 50 ],
99 ],
100 'additionalProperties' => false,
101 ],
102 'output_schema' => $this->out_schema(),
103 'execute_callback' => function( $input = [] ) {
104 return AVCF_JetBackup_Helpers::invoke( 'ListDestinations', AVCF_JetBackup_Helpers::paginate( (array) $input ) );
105 },
106 'permission_callback' => function() use ( $self ) { return $self->can(); },
107 'meta' => $this->ro_meta(),
108 ] );
109 }
110
111 /* -------------------------- get-destination ------------------------- */
112
113 private function register_get_destination() {
114 $self = $this;
115 wp_register_ability( 'atarim/jetbackup-get-destination', [
116 'label' => 'Get JetBackup Destination',
117 'category' => 'atarim',
118 'description' => 'Get a single JetBackup destination by id (shows its type and options shape — read this before editing with jetbackup-save-destination). Sensitive credential fields are masked by JetBackup.',
119 'input_schema' => $this->id_schema( 'Destination id.' ),
120 'output_schema' => $this->out_schema(),
121 'execute_callback' => function( $input = [] ) {
122 $input = (array) $input;
123 if ( empty( $input['id'] ) ) { return $this->missing_id( 'destination id' ); }
124 return AVCF_JetBackup_Helpers::invoke( 'GetDestination', AVCF_JetBackup_Helpers::id_payload( $input ) );
125 },
126 'permission_callback' => function() use ( $self ) { return $self->can(); },
127 'meta' => $this->ro_meta(),
128 ] );
129 }
130
131 /* -------------------------- save-destination ------------------------ */
132
133 private function register_save_destination() {
134 $self = $this;
135 wp_register_ability( 'atarim/jetbackup-save-destination', [
136 'label' => 'Save JetBackup Destination',
137 'category' => 'atarim',
138 'description' => 'Create or update a backup destination. Omit id (or 0) to create; pass a real id to edit. Only fields provided are changed (omit options to leave credentials untouched). Saving performs a live connection test; non-local destination types require a valid JetBackup license. options is provider-specific — read an existing destination with jetbackup-get-destination for the correct type and options keys.',
139 'input_schema' => [
140 'type' => 'object',
141 'properties' => [
142 'id' => [ 'type' => 'integer', 'minimum' => 0, 'description' => 'Destination id to edit; omit or 0 to create.' ],
143 'name' => [ 'type' => 'string' ],
144 'type' => [ 'type' => 'string', 'description' => 'Destination provider type (e.g. as shown by get-destination). Required when creating.' ],
145 'path' => [ 'type' => 'string', 'description' => 'Remote path/prefix on the destination.' ],
146 'notes' => [ 'type' => 'string' ],
147 'read_only' => [ 'type' => 'boolean' ],
148 'chunk_size' => [ 'type' => 'integer', 'description' => 'Upload chunk size.' ],
149 'free_disk' => [ 'type' => 'integer', 'description' => 'Reserved free-disk threshold.' ],
150 'options' => [ 'type' => 'object', 'description' => 'Provider-specific settings/credentials. Omit to leave unchanged on edit.' ],
151 'export_config' => [ 'type' => 'integer', 'description' => 'Advanced: config-export membership flag (normally toggled via jetbackup-toggle-destination-export-config).' ],
152 ],
153 'additionalProperties' => false,
154 ],
155 'output_schema' => $this->out_schema(),
156 'execute_callback' => function( $input = [] ) {
157 $input = (array) $input;
158 $payload = [];
159 $id = ! empty( $input['id'] ) ? (int) $input['id'] : 0;
160
161 if ( $id ) {
162 $payload[ AVCF_JetBackup_Helpers::ID_FIELD ] = $id;
163 }
164 if ( array_key_exists( 'name', $input ) ) { $payload['name'] = (string) $input['name']; }
165 if ( array_key_exists( 'type', $input ) ) { $payload['type'] = (string) $input['type']; }
166 if ( array_key_exists( 'path', $input ) ) { $payload['path'] = (string) $input['path']; }
167 if ( array_key_exists( 'notes', $input ) ) { $payload['notes'] = (string) $input['notes']; }
168 if ( array_key_exists( 'read_only', $input ) ) { $payload['read_only'] = filter_var( $input['read_only'], FILTER_VALIDATE_BOOLEAN ); }
169 if ( array_key_exists( 'chunk_size', $input ) ) { $payload['chunk_size'] = (int) $input['chunk_size']; }
170 if ( array_key_exists( 'free_disk', $input ) ) { $payload['free_disk'] = (int) $input['free_disk']; }
171 if ( array_key_exists( 'options', $input ) && is_array( $input['options'] ) ) {
172 $payload['options'] = (object) $input['options'];
173 } elseif ( array_key_exists( 'options', $input ) && is_object( $input['options'] ) ) {
174 $payload['options'] = $input['options'];
175 }
176
177 // export_config is applied unconditionally by ManageDestination; on edit,
178 // preserve the current value unless the caller overrides it, so a routine
179 // save doesn't silently clear it.
180 if ( array_key_exists( 'export_config', $input ) ) {
181 $payload['export_config'] = (int) $input['export_config'];
182 } elseif ( $id && class_exists( '\JetBackup\Destination\Destination' ) ) {
183 try {
184 $existing = new \JetBackup\Destination\Destination( $id );
185 if ( $existing->getId() ) {
186 $payload['export_config'] = (int) $existing->isExportConfig();
187 }
188 } catch ( \Throwable $e ) {
189 // Leave it out; JetBackup will default it.
190 }
191 }
192
193 if ( empty( $payload ) ) {
194 return [ 'success' => false, 'message' => 'No destination fields provided.', 'data' => [] ];
195 }
196 return AVCF_JetBackup_Helpers::invoke( 'ManageDestination', $payload );
197 },
198 'permission_callback' => function() use ( $self ) { return $self->can(); },
199 'meta' => $this->write_meta( false ),
200 ] );
201 }
202
203 /* ------------------------- delete-destination ----------------------- */
204
205 private function register_delete_destination() {
206 $self = $this;
207 wp_register_ability( 'atarim/jetbackup-delete-destination', [
208 'label' => 'Delete JetBackup Destination',
209 'category' => 'atarim',
210 'description' => 'Delete a backup destination by id. Destructive: requires confirm:true.',
211 'input_schema' => [
212 'type' => 'object',
213 'properties' => [
214 'id' => [ 'type' => 'integer', 'minimum' => 1, 'description' => 'Destination id to delete.' ],
215 'confirm' => [ 'type' => 'boolean', 'description' => 'Must be true to proceed.' ],
216 ],
217 'required' => [ 'id' ],
218 'additionalProperties' => false,
219 ],
220 'output_schema' => $this->out_schema(),
221 'execute_callback' => function( $input = [] ) {
222 $input = (array) $input;
223 if ( empty( $input['id'] ) ) { return $this->missing_id( 'destination id to delete' ); }
224 if ( ! AVCF_JetBackup_Helpers::confirmed( $input ) ) { return AVCF_JetBackup_Helpers::confirm_required_response( 'delete destination' ); }
225 return AVCF_JetBackup_Helpers::invoke( 'DeleteDestination', AVCF_JetBackup_Helpers::id_payload( $input ) );
226 },
227 'permission_callback' => function() use ( $self ) { return $self->can(); },
228 'meta' => $this->write_meta( true ),
229 ] );
230 }
231
232 /* ------------------------- toggle-destination ----------------------- */
233
234 private function register_toggle_destination() {
235 $self = $this;
236 wp_register_ability( 'atarim/jetbackup-toggle-destination', [
237 'label' => 'Toggle JetBackup Destination',
238 'category' => 'atarim',
239 'description' => 'Toggle a destination\'s enabled state (flips enabled <-> disabled) and returns the new state.',
240 'input_schema' => $this->id_schema( 'Destination id to enable/disable.' ),
241 'output_schema' => $this->out_schema(),
242 'execute_callback' => function( $input = [] ) {
243 $input = (array) $input;
244 if ( empty( $input['id'] ) ) { return $this->missing_id( 'destination id' ); }
245 return AVCF_JetBackup_Helpers::invoke( 'EnableDestination', AVCF_JetBackup_Helpers::id_payload( $input ) );
246 },
247 'permission_callback' => function() use ( $self ) { return $self->can(); },
248 'meta' => $this->write_meta( false ),
249 ] );
250 }
251
252 /* ------------------------ validate-destination ---------------------- */
253
254 private function register_validate_destination() {
255 $self = $this;
256 wp_register_ability( 'atarim/jetbackup-validate-destination', [
257 'label' => 'Validate JetBackup Destination',
258 'category' => 'atarim',
259 'description' => 'Test connectivity to an existing saved destination by id.',
260 'input_schema' => $this->id_schema( 'Destination id to validate.' ),
261 'output_schema' => $this->out_schema(),
262 'execute_callback' => function( $input = [] ) {
263 $input = (array) $input;
264 if ( empty( $input['id'] ) ) { return $this->missing_id( 'destination id to validate' ); }
265 return AVCF_JetBackup_Helpers::invoke( 'ValidateDestination', AVCF_JetBackup_Helpers::id_payload( $input ) );
266 },
267 'permission_callback' => function() use ( $self ) { return $self->can(); },
268 'meta' => $this->write_meta( false ),
269 ] );
270 }
271
272 /* ---------------- toggle-destination-export-config ------------------ */
273
274 private function register_toggle_destination_export_config() {
275 $self = $this;
276 wp_register_ability( 'atarim/jetbackup-toggle-destination-export-config', [
277 'label' => 'Toggle JetBackup Destination Config Export',
278 'category' => 'atarim',
279 'description' => 'Toggle whether a destination is included in the default configuration-backup job (flips on <-> off).',
280 'input_schema' => $this->id_schema( 'Destination id.' ),
281 'output_schema' => $this->out_schema(),
282 'execute_callback' => function( $input = [] ) {
283 $input = (array) $input;
284 if ( empty( $input['id'] ) ) { return $this->missing_id( 'destination id' ); }
285 return AVCF_JetBackup_Helpers::invoke( 'DestinationSetExportConfig', AVCF_JetBackup_Helpers::id_payload( $input ) );
286 },
287 'permission_callback' => function() use ( $self ) { return $self->can(); },
288 'meta' => $this->write_meta( false ),
289 ] );
290 }
291 }
292