Passed
Pull Request — 4.6 (#9600)
by Steve
07:52
created

EmbedShortcodeProvider   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 82
c 1
b 0
f 0
dl 0
loc 207
rs 10
wmc 30

7 Methods

Rating   Name   Duplication   Size   Complexity  
A buildAttributeListFromArguments() 0 15 3
A photoEmbed() 0 9 1
B embedForTemplate() 0 16 7
A get_shortcodes() 0 3 1
A linkEmbed() 0 10 2
A videoEmbed() 0 14 2
F handle_shortcode() 0 72 14
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
        $data = [
164
            'Arguments' => $arguments,
165
            '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

165
            '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...
166
            'Content' => DBField::create_field('HTMLFragment', $content)
167
        ];
168
169
        return ArrayData::create($data)->renderWith(self::class . '_video')->forTemplate();
170
    }
171
172
    /**
173
     * Build <a> embed tag
174
     *
175
     * @param array $arguments
176
     * @param string $href
177
     * @param string $title Default title
178
     * @return string
179
     */
180
    protected static function linkEmbed($arguments, $href, $title)
181
    {
182
        $data = [
183
            'Arguments' => $arguments,
184
            '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

184
            '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...
185
            'Href' => $href,
186
            'Title' => !empty($arguments['caption']) ? ($arguments['caption']) : $title
187
        ];
188
189
        return ArrayData::create($data)->renderWith(self::class . '_link')->forTemplate();
190
    }
191
192
    /**
193
     * Build img embed tag
194
     *
195
     * @param array $arguments
196
     * @param string $src
197
     * @return string
198
     */
199
    protected static function photoEmbed($arguments, $src)
200
    {
201
        $data = [
202
            'Arguments' => $arguments,
203
            '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

203
            '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...
204
            'Src' => $src
205
        ];
206
207
        return ArrayData::create($data)->renderWith(self::class . '_photo')->forTemplate();
208
    }
209
210
    /**
211
     * Build a list of HTML attributes from embed arguments - used to preserve backward compatibility
212
     *
213
     * @deprecated 4.5.0 Use {$Arguments.name} directly in shortcode templates to access argument values
214
     * @param array $arguments List of embed arguments
215
     * @param array $exclude List of attribute names to exclude from the resulting list
216
     * @return ArrayList
217
     */
218
    private static function buildAttributeListFromArguments(array $arguments, array $exclude = []): ArrayList
219
    {
220
        $attributes = ArrayList::create();
221
        foreach ($arguments as $key => $value) {
222
            if (in_array($key, $exclude)) {
223
                continue;
224
            }
225
226
            $attributes->push(ArrayData::create([
227
                'Name' => $key,
228
                'Value' => Convert::raw2att($value)
229
            ]));
230
        }
231
232
        return $attributes;
233
    }
234
}
235