1<?php
2
3/**
4 * @see       https://github.com/laminas/laminas-feed for the canonical source repository
5 * @copyright https://github.com/laminas/laminas-feed/blob/master/COPYRIGHT.md
6 * @license   https://github.com/laminas/laminas-feed/blob/master/LICENSE.md New BSD License
7 */
8
9namespace Laminas\Feed\Writer\Renderer\Feed\Atom;
10
11use DateTime;
12use DOMDocument;
13use DOMElement;
14use Laminas\Feed;
15use Laminas\Feed\Writer\Version;
16
17class AbstractAtom extends Feed\Writer\Renderer\AbstractRenderer
18{
19    /**
20     * @param Feed\Writer\Feed $container
21     */
22    public function __construct($container)
23    {
24        parent::__construct($container);
25    }
26
27    /**
28     * Set feed language
29     *
30     * @param  DOMDocument $dom
31     * @param  DOMElement $root
32     * @return void
33     */
34    // @codingStandardsIgnoreStart
35    protected function _setLanguage(DOMDocument $dom, DOMElement $root)
36    {
37        // @codingStandardsIgnoreEnd
38        if ($this->getDataContainer()->getLanguage()) {
39            $root->setAttribute('xml:lang', $this->getDataContainer()->getLanguage());
40        }
41    }
42
43    /**
44     * Set feed title
45     *
46     * @param  DOMDocument $dom
47     * @param  DOMElement $root
48     * @return void
49     * @throws Feed\Exception\InvalidArgumentException
50     */
51    // @codingStandardsIgnoreStart
52    protected function _setTitle(DOMDocument $dom, DOMElement $root)
53    {
54        // @codingStandardsIgnoreEnd
55        if (! $this->getDataContainer()->getTitle()) {
56            $message   = 'Atom 1.0 feed elements MUST contain exactly one'
57                . ' atom:title element but a title has not been set';
58            $exception = new Feed\Exception\InvalidArgumentException($message);
59            if (! $this->ignoreExceptions) {
60                throw $exception;
61            } else {
62                $this->exceptions[] = $exception;
63                return;
64            }
65        }
66
67        $title = $dom->createElement('title');
68        $root->appendChild($title);
69        $title->setAttribute('type', 'text');
70        $text = $dom->createTextNode($this->getDataContainer()->getTitle());
71        $title->appendChild($text);
72    }
73
74    /**
75     * Set feed description
76     *
77     * @param  DOMDocument $dom
78     * @param  DOMElement $root
79     * @return void
80     */
81    // @codingStandardsIgnoreStart
82    protected function _setDescription(DOMDocument $dom, DOMElement $root)
83    {
84        // @codingStandardsIgnoreEnd
85        if (! $this->getDataContainer()->getDescription()) {
86            return;
87        }
88        $subtitle = $dom->createElement('subtitle');
89        $root->appendChild($subtitle);
90        $subtitle->setAttribute('type', 'text');
91        $text = $dom->createTextNode($this->getDataContainer()->getDescription());
92        $subtitle->appendChild($text);
93    }
94
95    /**
96     * Set date feed was last modified
97     *
98     * @param  DOMDocument $dom
99     * @param  DOMElement $root
100     * @return void
101     * @throws Feed\Exception\InvalidArgumentException
102     */
103    // @codingStandardsIgnoreStart
104    protected function _setDateModified(DOMDocument $dom, DOMElement $root)
105    {
106        // @codingStandardsIgnoreEnd
107        if (! $this->getDataContainer()->getDateModified()) {
108            $message   = 'Atom 1.0 feed elements MUST contain exactly one'
109                . ' atom:updated element but a modification date has not been set';
110            $exception = new Feed\Exception\InvalidArgumentException($message);
111            if (! $this->ignoreExceptions) {
112                throw $exception;
113            } else {
114                $this->exceptions[] = $exception;
115                return;
116            }
117        }
118
119        $updated = $dom->createElement('updated');
120        $root->appendChild($updated);
121        $text = $dom->createTextNode(
122            $this->getDataContainer()->getDateModified()->format(DateTime::ATOM)
123        );
124        $updated->appendChild($text);
125    }
126
127    /**
128     * Set feed generator string
129     *
130     * @param  DOMDocument $dom
131     * @param  DOMElement $root
132     * @return void
133     */
134    // @codingStandardsIgnoreStart
135    protected function _setGenerator(DOMDocument $dom, DOMElement $root)
136    {
137        // @codingStandardsIgnoreEnd
138        if (! $this->getDataContainer()->getGenerator()) {
139            $this->getDataContainer()->setGenerator(
140                'Laminas_Feed_Writer',
141                Version::VERSION,
142                'https://getlaminas.org'
143            );
144        }
145
146        $gdata     = $this->getDataContainer()->getGenerator();
147        $generator = $dom->createElement('generator');
148        $root->appendChild($generator);
149        $text = $dom->createTextNode($gdata['name']);
150        $generator->appendChild($text);
151        if (array_key_exists('uri', $gdata)) {
152            $generator->setAttribute('uri', $gdata['uri']);
153        }
154        if (array_key_exists('version', $gdata)) {
155            $generator->setAttribute('version', $gdata['version']);
156        }
157    }
158
159    /**
160     * Set link to feed
161     *
162     * @param  DOMDocument $dom
163     * @param  DOMElement $root
164     * @return void
165     */
166    // @codingStandardsIgnoreStart
167    protected function _setLink(DOMDocument $dom, DOMElement $root)
168    {
169        // @codingStandardsIgnoreEnd
170        if (! $this->getDataContainer()->getLink()) {
171            return;
172        }
173        $link = $dom->createElement('link');
174        $root->appendChild($link);
175        $link->setAttribute('rel', 'alternate');
176        $link->setAttribute('type', 'text/html');
177        $link->setAttribute('href', $this->getDataContainer()->getLink());
178    }
179
180    /**
181     * Set feed links
182     *
183     * @param  DOMDocument $dom
184     * @param  DOMElement $root
185     * @return void
186     * @throws Feed\Exception\InvalidArgumentException
187     */
188    // @codingStandardsIgnoreStart
189    protected function _setFeedLinks(DOMDocument $dom, DOMElement $root)
190    {
191        // @codingStandardsIgnoreEnd
192        $flinks = $this->getDataContainer()->getFeedLinks();
193        if (! $flinks || ! array_key_exists('atom', $flinks)) {
194            $message   = 'Atom 1.0 feed elements SHOULD contain one atom:link '
195                . 'element with a rel attribute value of "self".  This is the '
196                . 'preferred URI for retrieving Atom Feed Documents representing '
197                . 'this Atom feed but a feed link has not been set';
198            $exception = new Feed\Exception\InvalidArgumentException($message);
199            if (! $this->ignoreExceptions) {
200                throw $exception;
201            } else {
202                $this->exceptions[] = $exception;
203                return;
204            }
205        }
206
207        foreach ($flinks as $type => $href) {
208            $mime  = 'application/' . strtolower($type) . '+xml';
209            $flink = $dom->createElement('link');
210            $root->appendChild($flink);
211            $flink->setAttribute('rel', 'self');
212            $flink->setAttribute('type', $mime);
213            $flink->setAttribute('href', $href);
214        }
215    }
216
217    /**
218     * Set feed authors
219     *
220     * @param  DOMDocument $dom
221     * @param  DOMElement $root
222     * @return void
223     */
224    // @codingStandardsIgnoreStart
225    protected function _setAuthors(DOMDocument $dom, DOMElement $root)
226    {
227        // @codingStandardsIgnoreEnd
228        $authors = $this->container->getAuthors();
229        if (! $authors || empty($authors)) {
230            /**
231             * Technically we should defer an exception until we can check
232             * that all entries contain an author. If any entry is missing
233             * an author, then a missing feed author element is invalid
234             */
235            return;
236        }
237        foreach ($authors as $data) {
238            $author = $this->dom->createElement('author');
239            $name   = $this->dom->createElement('name');
240            $author->appendChild($name);
241            $root->appendChild($author);
242            $text = $dom->createTextNode($data['name']);
243            $name->appendChild($text);
244            if (array_key_exists('email', $data)) {
245                $email = $this->dom->createElement('email');
246                $author->appendChild($email);
247                $text = $dom->createTextNode($data['email']);
248                $email->appendChild($text);
249            }
250            if (array_key_exists('uri', $data)) {
251                $uri = $this->dom->createElement('uri');
252                $author->appendChild($uri);
253                $text = $dom->createTextNode($data['uri']);
254                $uri->appendChild($text);
255            }
256        }
257    }
258
259    /**
260     * Set feed identifier
261     *
262     * @param  DOMDocument $dom
263     * @param  DOMElement $root
264     * @return void
265     * @throws Feed\Exception\InvalidArgumentException
266     */
267    // @codingStandardsIgnoreStart
268    protected function _setId(DOMDocument $dom, DOMElement $root)
269    {
270        // @codingStandardsIgnoreEnd
271        if (! $this->getDataContainer()->getId()
272            && ! $this->getDataContainer()->getLink()
273        ) {
274            $message   = 'Atom 1.0 feed elements MUST contain exactly one '
275                . 'atom:id element, or as an alternative, we can use the same '
276                . 'value as atom:link however neither a suitable link nor an '
277                . 'id have been set';
278            $exception = new Feed\Exception\InvalidArgumentException($message);
279            if (! $this->ignoreExceptions) {
280                throw $exception;
281            } else {
282                $this->exceptions[] = $exception;
283                return;
284            }
285        }
286
287        if (! $this->getDataContainer()->getId()) {
288            $this->getDataContainer()->setId(
289                $this->getDataContainer()->getLink()
290            );
291        }
292        $id = $dom->createElement('id');
293        $root->appendChild($id);
294        $text = $dom->createTextNode($this->getDataContainer()->getId());
295        $id->appendChild($text);
296    }
297
298    /**
299     * Set feed copyright
300     *
301     * @param  DOMDocument $dom
302     * @param  DOMElement $root
303     * @return void
304     */
305    // @codingStandardsIgnoreStart
306    protected function _setCopyright(DOMDocument $dom, DOMElement $root)
307    {
308        // @codingStandardsIgnoreEnd
309        $copyright = $this->getDataContainer()->getCopyright();
310        if (! $copyright) {
311            return;
312        }
313        $copy = $dom->createElement('rights');
314        $root->appendChild($copy);
315        $text = $dom->createTextNode($copyright);
316        $copy->appendChild($text);
317    }
318
319    /**
320     * Set feed level logo (image)
321     *
322     * @param  DOMDocument $dom
323     * @param  DOMElement $root
324     * @return void
325     */
326    // @codingStandardsIgnoreStart
327    protected function _setImage(DOMDocument $dom, DOMElement $root)
328    {
329        // @codingStandardsIgnoreEnd
330        $image = $this->getDataContainer()->getImage();
331        if (! $image) {
332            return;
333        }
334        $img = $dom->createElement('logo');
335        $root->appendChild($img);
336        $text = $dom->createTextNode($image['uri']);
337        $img->appendChild($text);
338    }
339
340    /**
341     * Set date feed was created
342     *
343     * @param  DOMDocument $dom
344     * @param  DOMElement $root
345     * @return void
346     */
347    // @codingStandardsIgnoreStart
348    protected function _setDateCreated(DOMDocument $dom, DOMElement $root)
349    {
350        // @codingStandardsIgnoreEnd
351        if (! $this->getDataContainer()->getDateCreated()) {
352            return;
353        }
354        if (! $this->getDataContainer()->getDateModified()) {
355            $this->getDataContainer()->setDateModified(
356                $this->getDataContainer()->getDateCreated()
357            );
358        }
359    }
360
361    /**
362     * Set base URL to feed links
363     *
364     * @param  DOMDocument $dom
365     * @param  DOMElement $root
366     * @return void
367     */
368    // @codingStandardsIgnoreStart
369    protected function _setBaseUrl(DOMDocument $dom, DOMElement $root)
370    {
371        // @codingStandardsIgnoreEnd
372        $baseUrl = $this->getDataContainer()->getBaseUrl();
373        if (! $baseUrl) {
374            return;
375        }
376        $root->setAttribute('xml:base', $baseUrl);
377    }
378
379    /**
380     * Set hubs to which this feed pushes
381     *
382     * @param  DOMDocument $dom
383     * @param  DOMElement $root
384     * @return void
385     */
386    // @codingStandardsIgnoreStart
387    protected function _setHubs(DOMDocument $dom, DOMElement $root)
388    {
389        // @codingStandardsIgnoreEnd
390        $hubs = $this->getDataContainer()->getHubs();
391        if (! $hubs) {
392            return;
393        }
394        foreach ($hubs as $hubUrl) {
395            $hub = $dom->createElement('link');
396            $hub->setAttribute('rel', 'hub');
397            $hub->setAttribute('href', $hubUrl);
398            $root->appendChild($hub);
399        }
400    }
401
402    /**
403     * Set feed categories
404     *
405     * @param  DOMDocument $dom
406     * @param  DOMElement $root
407     * @return void
408     */
409    // @codingStandardsIgnoreStart
410    protected function _setCategories(DOMDocument $dom, DOMElement $root)
411    {
412        // @codingStandardsIgnoreEnd
413        $categories = $this->getDataContainer()->getCategories();
414        if (! $categories) {
415            return;
416        }
417        foreach ($categories as $cat) {
418            $category = $dom->createElement('category');
419            $category->setAttribute('term', $cat['term']);
420            if (isset($cat['label'])) {
421                $category->setAttribute('label', $cat['label']);
422            } else {
423                $category->setAttribute('label', $cat['term']);
424            }
425            if (isset($cat['scheme'])) {
426                $category->setAttribute('scheme', $cat['scheme']);
427            }
428            $root->appendChild($category);
429        }
430    }
431}
432