Passed
Push — pulls/manymanylist-add-callbac... ( 77b6c5...aed064 )
by Sam
07:52
created

EmbedShortcodeProvider::photoEmbed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 9
rs 10
1
<?php
2
3
namespace SilverStripe\View\Shortcodes;
4
5
use Embed\Http\DispatcherInterface;
6
use SilverStripe\Core\Convert;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\ORM\ArrayList;
9
use SilverStripe\ORM\FieldType\DBField;
10
use SilverStripe\View\ArrayData;
11
use SilverStripe\View\Embed\Embeddable;
12
use SilverStripe\View\Embed\EmbedResource;
13
use SilverStripe\View\HTML;
14
use SilverStripe\View\Parsers\ShortcodeHandler;
15
use Embed\Adapters\Adapter;
16
use Embed\Embed;
17
use Embed\Exceptions\InvalidUrlException;
18
use SilverStripe\View\Parsers\ShortcodeParser;
19
use SilverStripe\Control\Director;
20
21
/**
22
 * Provider for the [embed] shortcode tag used by the embedding service
23
 * in the HTML Editor field.
24
 * Provides the html needed for the frontend and the editor field itself.
25
 */
26
class EmbedShortcodeProvider implements ShortcodeHandler
27
{
28
29
    /**
30
     * Gets the list of shortcodes provided by this handler
31
     *
32
     * @return mixed
33
     */
34
    public static function get_shortcodes()
35
    {
36
        return ['embed'];
37
    }
38
39
    /**
40
     * Embed shortcode parser from Oembed. This is a temporary workaround.
41
     * Oembed class has been replaced with the Embed external service.
42
     *
43
     * @param array $arguments
44
     * @param string $content
45
     * @param ShortcodeParser $parser
46
     * @param string $shortcode
47
     * @param array $extra
48
     *
49
     * @return string
50
     */
51
    public static function handle_shortcode($arguments, $content, $parser, $shortcode, $extra = [])
52
    {
53
        // Get service URL
54
        if (!empty($content)) {
55
            $serviceURL = $content;
56
        } elseif (!empty($arguments['url'])) {
57
            $serviceURL = $arguments['url'];
58
        } else {
59
            return '';
60
        }
61
62
        // See https://github.com/oscarotero/Embed#example-with-all-options for service arguments
63
        $serviceArguments = [];
64
        if (!empty($arguments['width'])) {
65
            $serviceArguments['min_image_width'] = $arguments['width'];
66
        }
67
        if (!empty($arguments['height'])) {
68
            $serviceArguments['min_image_height'] = $arguments['height'];
69
        }
70
71
        /** @var EmbedResource $embed */
72
        $embed = Injector::inst()->create(Embeddable::class, $serviceURL);
73
        if (!empty($serviceArguments)) {
74
            $embed->setOptions(array_merge($serviceArguments, (array) $embed->getOptions()));
75
        }
76
77
        // Allow resolver to be mocked
78
        $dispatcher = null;
79
        if (isset($extra['resolver'])) {
80
            $dispatcher = Injector::inst()->create(
81
                $extra['resolver']['class'],
82
                $serviceURL,
83
                $extra['resolver']['config']
84
            );
85
        } elseif (Injector::inst()->has(DispatcherInterface::class)) {
86
            $dispatcher = Injector::inst()->get(DispatcherInterface::class);
87
        }
88
89
        if ($dispatcher) {
0 ignored issues
show
introduced by
$dispatcher is of type null, thus it always evaluated to false.
Loading history...
90
            $embed->setDispatcher($dispatcher);
91
        }
92
93
        // Process embed
94
        try {
95
            $embed = $embed->getEmbed();
96
        } catch (InvalidUrlException $e) {
97
            $message = (Director::isDev())
98
                ? $e->getMessage()
99
                : _t(__CLASS__ . '.INVALID_URL', 'There was a problem loading the media.');
100
101
            $attr = [
102
                'class' => 'ss-media-exception embed'
103
            ];
104
105
            $result = HTML::createTag(
106
                'div',
107
                $attr,
108
                HTML::createTag('p', [], $message)
109
            );
110
            return $result;
111
        }
112
113
        // Convert embed object into HTML
114
        if ($embed && $embed instanceof Adapter) {
0 ignored issues
show
introduced by
$embed is always a sub-type of Embed\Adapters\Adapter.
Loading history...
115
            $result = static::embedForTemplate($embed, $arguments);
116
            if ($result) {
117
                return $result;
118
            }
119
        }
120
121
        // Fallback to link to service
122
        return static::linkEmbed($arguments, $serviceURL, $serviceURL);
123
    }
124
125
    /**
126
     * @param Adapter $embed
127
     * @param array $arguments Additional shortcode params
128
     * @return string
129
     */
130
    public static function embedForTemplate($embed, $arguments)
131
    {
132
        switch ($embed->getType()) {
133
            case 'video':
134
            case 'rich':
135
                // Attempt to inherit width (but leave height auto)
136
                if (empty($arguments['width']) && $embed->getWidth()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $embed->getWidth() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
137
                    $arguments['width'] = $embed->getWidth();
138
                }
139
                return static::videoEmbed($arguments, $embed->getCode());
140
            case 'link':
141
                return static::linkEmbed($arguments, $embed->getUrl(), $embed->getTitle());
142
            case 'photo':
143
                return static::photoEmbed($arguments, $embed->getUrl());
144
            default:
145
                return null;
146
        }
147
    }
148
149
    /**
150
     * Build video embed tag
151
     *
152
     * @param array $arguments
153
     * @param string $content Raw HTML content
154
     * @return string
155
     */
156
    protected static function videoEmbed($arguments, $content)
157
    {
158
        // Ensure outer div has given width (but leave height auto)
159
        if (!empty($arguments['width'])) {
160
            $arguments['style'] = 'width: ' . intval($arguments['width']) . 'px;';
161
        }
162
163
        // override iframe dimension attributes provided by webservice with ones specified in shortcode arguments
164
        foreach (['width', 'height'] as $attr) {
165
            if (!($value = $arguments[$attr] ?? false)) {
166
                continue;
167
            }
168
            foreach (['"', "'"] as $quote) {
169
                $rx = "/(<iframe .*?)$attr=$quote([0-9]+)$quote([^>]+>)/";
170
                $content = preg_replace($rx, "$1{$attr}={$quote}{$value}{$quote}$3", $content);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $1 seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $3 seems to be never defined.
Loading history...
171
            }
172
        }
173
174
        $data = [
175
            'Arguments' => $arguments,
176
            'Attributes' => static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\View\Shortc...buteListFromArguments() has been deprecated: 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

176
            'Attributes' => /** @scrutinizer ignore-deprecated */ static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
177
            'Content' => DBField::create_field('HTMLFragment', $content)
178
        ];
179
180
        return ArrayData::create($data)->renderWith(self::class . '_video')->forTemplate();
181
    }
182
183
    /**
184
     * Build <a> embed tag
185
     *
186
     * @param array $arguments
187
     * @param string $href
188
     * @param string $title Default title
189
     * @return string
190
     */
191
    protected static function linkEmbed($arguments, $href, $title)
192
    {
193
        $data = [
194
            'Arguments' => $arguments,
195
            'Attributes' => static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\View\Shortc...buteListFromArguments() has been deprecated: 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

195
            'Attributes' => /** @scrutinizer ignore-deprecated */ static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
196
            'Href' => $href,
197
            'Title' => !empty($arguments['caption']) ? ($arguments['caption']) : $title
198
        ];
199
200
        return ArrayData::create($data)->renderWith(self::class . '_link')->forTemplate();
201
    }
202
203
    /**
204
     * Build img embed tag
205
     *
206
     * @param array $arguments
207
     * @param string $src
208
     * @return string
209
     */
210
    protected static function photoEmbed($arguments, $src)
211
    {
212
        $data = [
213
            'Arguments' => $arguments,
214
            'Attributes' => static::buildAttributeListFromArguments($arguments, ['url']),
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\View\Shortc...buteListFromArguments() has been deprecated: 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

214
            'Attributes' => /** @scrutinizer ignore-deprecated */ static::buildAttributeListFromArguments($arguments, ['url']),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
215
            'Src' => $src
216
        ];
217
218
        return ArrayData::create($data)->renderWith(self::class . '_photo')->forTemplate();
219
    }
220
221
    /**
222
     * Build a list of HTML attributes from embed arguments - used to preserve backward compatibility
223
     *
224
     * @deprecated 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values
225
     * @param array $arguments List of embed arguments
226
     * @param array $exclude List of attribute names to exclude from the resulting list
227
     * @return ArrayList
228
     */
229
    private static function buildAttributeListFromArguments(array $arguments, array $exclude = []): ArrayList
230
    {
231
        $attributes = ArrayList::create();
232
        foreach ($arguments as $key => $value) {
233
            if (in_array($key, $exclude)) {
234
                continue;
235
            }
236
237
            $attributes->push(ArrayData::create([
238
                'Name' => $key,
239
                'Value' => Convert::raw2att($value)
240
            ]));
241
        }
242
243
        return $attributes;
244
    }
245
}
246