Attachment   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Importance

Changes 11
Bugs 2 Features 0
Metric Value
wmc 22
eloc 82
c 11
b 2
f 0
dl 0
loc 256
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A ajaxSendAttachmentToEditor() 0 20 2
A ajaxGetAttachment() 0 19 2
A mediaSendToEditor() 0 9 1
A mediaStrings() 0 5 1
A prepareAttachmentForJs() 0 11 1
A attachmentImageSrc() 0 17 2
B makeContentImagesResponsive() 0 51 9
A ajaxQueryAttachments() 0 15 3
1
<?php # -*- coding: utf-8 -*-
2
declare(strict_types=1);
3
4
namespace MultisiteGlobalMedia;
5
6
/**
7
 * Class Attachment
8
 */
9
class Attachment
10
{
11
12
    use Helper;
13
14
    /**
15
     * @var Site
16
     */
17
    private $site;
18
19
    /**
20
     * @var SiteSwitcher
21
     */
22
    private $siteSwitcher;
23
24
    /**
25
     * Attachment constructor.
26
     *
27
     * @param Site $site
28
     * @param SiteSwitcher $siteSwitcher
29
     */
30
    public function __construct(Site $site, SiteSwitcher $siteSwitcher)
31
    {
32
        $this->site = $site;
33
        $this->siteSwitcher = $siteSwitcher;
34
    }
35
36
    /**
37
     * Prepare media for javascript
38
     *
39
     * @param array $response Array of prepared attachment data.
40
     *
41
     * @return array Array of prepared attachment data.
42
     * @since   2015-01-26
43
     * @version 2018-08-29
44
     *
45
     */
46
    public function prepareAttachmentForJs(array $response): array
47
    {
48
        $idPrefix = $this->site->idSitePrefix();
49
50
        $response['id'] = (int) ($idPrefix.$response['id']); // Unique ID, must be a number.
51
        $response['nonces']['update'] = false;
52
        $response['nonces']['edit'] = false;
53
        $response['nonces']['delete'] = false;
54
        $response['editLink'] = false;
55
56
        return $response;
57
    }
58
59
    /**
60
     * Same as wp_ajax_query_attachments() but with switch_to_blog support.
61
     *
62
     * @return void
63
     * @since   2015-01-26
64
     */
65
    public function ajaxQueryAttachments()
66
    {
67
        // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
68
        // phpcs:disable
69
        $query = isset($_REQUEST['query']) // csrf ok
70
            ? (array) wp_unslash($_REQUEST['query']) // csrf ok
71
            : [];
72
        // phpcs:enable
73
74
        if (!empty($query['global_media'])) {
75
            switch_to_blog($this->site->id());
76
            add_filter('wp_prepare_attachment_for_js', [$this, 'prepareAttachmentForJs'], 0);
77
        }
78
79
        wp_ajax_query_attachments();
80
    }
81
82
    /**
83
     * Get attachment
84
     *
85
     * @return  void
86
     * @since   2015-01-26
87
     */
88
    public function ajaxGetAttachment()
89
    {
90
        // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
91
        // phpcs:disable WordPress.VIP.ValidatedSanitizedInput.InputNotSanitized
92
        // phpcs:disable WordPress.VIP.ValidatedSanitizedInput.InputNotValidated
93
        $attachmentId = (int) wp_unslash($_REQUEST['id']); // csrf ok
94
        // phpcs:enable
95
        $idPrefix = $this->site->idSitePrefix();
96
97
        if ($this->idPrefixIncludedInAttachmentId($attachmentId, $idPrefix)) {
98
            $attachmentId = $this->stripSiteIdPrefixFromAttachmentId($idPrefix, $attachmentId);
99
            $_REQUEST['id'] = $attachmentId;
100
101
            $this->siteSwitcher->switchToBlog($this->site->id());
102
            add_filter('wp_prepare_attachment_for_js', [$this, 'prepareAttachmentForJs'], 0);
103
            $this->siteSwitcher->restoreBlog();
104
        }
105
106
        wp_ajax_get_attachment();
107
    }
108
109
    /**
110
     * Send media via AJAX call to editor
111
     *
112
     * @return  void
113
     * @since   2015-01-26
114
     */
115
    public function ajaxSendAttachmentToEditor()
116
    {
117
        // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
118
        // phpcs:disable WordPress.VIP.ValidatedSanitizedInput.InputNotSanitized
119
        // phpcs:disable WordPress.VIP.ValidatedSanitizedInput.InputNotValidated
120
        $attachment = wp_unslash($_POST['attachment']); // csrf ok
121
        $attachmentId = (int) $attachment['id'];
122
        $idPrefix = $this->site->idSitePrefix();
123
124
        if ($this->idPrefixIncludedInAttachmentId($attachmentId, $idPrefix)) {
125
            $attachment['id'] = $this->stripSiteIdPrefixFromAttachmentId($idPrefix, $attachmentId);
126
            $_POST['attachment'] = wp_slash($attachment);
127
128
            // TODO Which is the reason why we don't restore the blog?
129
            switch_to_blog($this->site->id());
130
131
            add_filter('mediaSendToEditor', [$this, 'mediaSendToEditor'], 10, 2);
132
        }
133
134
        wp_ajax_send_attachment_to_editor();
135
    }
136
137
    /**
138
     * Send media to editor
139
     *
140
     * @param string $html
141
     * @param int $id
142
     *
143
     * @return string $html
144
     * @since   2015-01-26
145
     *
146
     */
147
    public function mediaSendToEditor(string $html, int $id): string
148
    {
149
        $idPrefix = $this->site->idSitePrefix();
150
        $newId = $idPrefix.$id; // Unique ID, must be a number.
151
152
        $search = 'wp-image-'.$id;
153
        $replace = 'wp-image-'.$newId;
154
155
        return str_replace($search, $replace, $html);
156
    }
157
158
    /**
159
     * @param array|false $image
160
     * @param int|string $attachmentId
161
     * @param array|string $size
162
     * @param bool $icon
163
     *
164
     * @return array|false
165
     *
166
     * @wp-hook wp_get_attachment_image_src
167
     */
168
    public function attachmentImageSrc($image, $attachmentId, $size, bool $icon)
169
    {
170
        // phpcs:enable
171
172
        $attachmentId = (int) $attachmentId;
173
        $idPrefix = $this->site->idSitePrefix();
174
175
        if (!$this->idPrefixIncludedInAttachmentId($attachmentId, $idPrefix)) {
176
            return $image;
177
        }
178
179
        $attachmentId = $this->stripSiteIdPrefixFromAttachmentId($idPrefix, $attachmentId);
180
        $this->siteSwitcher->switchToBlog($this->site->id());
181
        $image = wp_get_attachment_image_src($attachmentId, $size, $icon);
182
        $this->siteSwitcher->restoreBlog();
183
184
        return $image;
185
    }
186
187
    /**
188
     * Define Strings for translation
189
     *
190
     * @param array $strings
191
     *
192
     * @return array
193
     * @since   2015-01-26
194
     *
195
     */
196
    public function mediaStrings(array $strings): array
197
    {
198
        $strings['globalMediaTitle'] = esc_html__('Global Media', 'multisite-global-media');
199
200
        return $strings;
201
    }
202
203
    /**
204
     * Add srcset to images in content.
205
     *
206
     * @param string $content
207
     *
208
     * @return string
209
     *
210
     * @wp-hook the_content
211
     * @see wp_make_content_images_responsive
212
     *
213
     */
214
    public function makeContentImagesResponsive(string $content): string
215
    {
216
        if (!preg_match_all('/<img [^>]+>/', $content, $matches)) {
217
            return $content;
218
        }
219
220
        $selectedImages = $attachmentIds = [];
221
222
        foreach ($matches[0] as $image) {
223
            $hasSrcset = strpos($image, ' srcset=') !== false;
224
            $hasClassId = preg_match('/wp-image-(\d+)/i', $image, $classId);
225
            $attachmentId = !$hasSrcset && $hasClassId
226
                ? absint($classId[1])
227
                : null;
228
            if ($attachmentId) {
229
                // If exactly the same image tag is used more than once, overwrite it.
230
                // All identical tags will be replaced later with 'str_replace()'.
231
                $selectedImages[$image] = $attachmentId;
232
                // Overwrite the ID when the same image is included more than once.
233
                $attachmentIds[$attachmentId] = true;
234
            }
235
        }
236
237
        if (count($attachmentIds) > 1) {
238
            // Warm the object cache with post and meta information for all found
239
            // images to avoid making individual database calls.
240
            _prime_post_caches(array_keys($attachmentIds), false);
241
        }
242
243
        $idPrefix = $this->site->idSitePrefix();
244
245
        foreach ($selectedImages as $image => $attachmentId) {
246
            if (!$this->idPrefixIncludedInAttachmentId($attachmentId, $idPrefix)) {
247
                $imageMeta = wp_get_attachment_metadata($attachmentId);
248
                $content = str_replace(
249
                    $image,
250
                    wp_image_add_srcset_and_sizes($image, $imageMeta, $attachmentId),
251
                    $content
252
                );
253
                continue;
254
            }
255
256
            $globalAttachmentId = $this->stripSiteIdPrefixFromAttachmentId($idPrefix, $attachmentId);
257
258
            $this->siteSwitcher->switchToBlog($this->site->id());
259
            $imageMeta = wp_get_attachment_metadata($globalAttachmentId);
260
            $content = str_replace($image, wp_image_add_srcset_and_sizes($image, $imageMeta, $attachmentId), $content);
261
            $this->siteSwitcher->restoreBlog();
262
        }
263
264
        return $content;
265
    }
266
}
267