Passed
Push — master ( a1de92...1a465b )
by Robbie
03:52
created

src/Tasks/MigrateContentToElement.php (4 issues)

1
<?php
2
3
namespace DNADesign\Elemental\Tasks;
4
5
use DNADesign\Elemental\Extensions\ElementalAreasExtension;
6
use DNADesign\Elemental\Extensions\ElementalPageExtension;
7
use DNADesign\Elemental\Models\BaseElement;
8
use DNADesign\Elemental\Models\ElementalArea;
9
use DNADesign\Elemental\Models\ElementContent;
10
use Exception;
11
use SilverStripe\CMS\Model\SiteTree;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\Dev\BuildTask;
14
use SilverStripe\Versioned\Versioned;
15
16
class MigrateContentToElement extends BuildTask
17
{
18
    /**
19
     * Configures if the existing content should be cleared once the migration task has completed.
20
     *
21
     * @config
22
     * @var bool
23
     */
24
    private static $clear_content = true;
0 ignored issues
show
The private property $clear_content is not used, and could be removed.
Loading history...
25
26
    /**
27
     * The FQN of an element that will be the target of the content
28
     *
29
     * @config
30
     * @var string
31
     */
32
    private static $target_element = ElementContent::class;
0 ignored issues
show
The private property $target_element is not used, and could be removed.
Loading history...
33
34
    /**
35
     * The name of the field on the `target_element` where the content should be placed
36
     *
37
     * @config
38
     * @var string
39
     */
40
    private static $target_element_field = 'HTML';
0 ignored issues
show
The private property $target_element_field is not used, and could be removed.
Loading history...
41
42
    /**
43
     * Indicates that the updated page and elements should be immediately published (provided the Versioned extension
44
     * is present, and the page was previously in a published state)
45
     *
46
     * @config
47
     * @var bool
48
     */
49
    private static $publish_changes = true;
0 ignored issues
show
The private property $publish_changes is not used, and could be removed.
Loading history...
50
51
    protected $title = 'MigrateContentToElement';
52
53
    protected $description = 'When installing Elemental this task converts content in the $Content '
54
        . 'field to an ElementContent';
55
56
    public function run($request)
57
    {
58
        $pageTypes = singleton(ElementalArea::class)->supportedPageTypes();
59
        $count = 0;
60
        foreach ($pageTypes as $pageType) {
61
            // Only pages that have the ElementalPageExtension have a known ElementalArea relation
62
            if (!$this->isMigratable($pageType)) {
63
                continue;
64
            }
65
66
            $pages = $pageType::get()->filter('Content:not', ['', null]);
67
            $clearContent = $this->config()->get('clear_content');
68
69
            $this->extend('updatePageFilter', $pages, $pageType);
70
            $pageTypeCount = 0;
71
72
            /** @var SiteTree&ElementalAreasExtension $page */
73
            foreach ($pages as $page) {
74
                if ($this->shouldSkipMigration($page)) {
75
                    continue;
76
                }
77
                // Fetch and clear existing content (if configured)
78
                $content = $page->Content;
79
                $pageIsLive = $page->isPublished();
80
                if ($clearContent) {
81
                    $page->Content = '';
82
                }
83
84
                // Get the area
85
                $area = $this->getAreaRelationFromPage($page);
86
87
                // Write the page if we're clearing content or if the area doesn't exist - we write to trigger a
88
                // relationship update
89
                if ($clearContent || !$area->exists()) {
90
                    try {
91
                        $page->write();
92
                    } catch (Exception $e) {
93
                        echo sprintf(
94
                            'Could not clear content on page %s: %s',
95
                            $page->ID,
96
                            $e->getMessage()
97
                        );
98
                    }
99
100
                    if (!$area->exists()) {
101
                        $area = $this->getAreaRelationFromPage($page);
102
                    }
103
                }
104
105
                // Create a new element
106
                /** @var BaseElement $element */
107
                $element = Injector::inst()->create($this->config()->get('target_element'));
108
                $element->Title = 'Auto migrated content';
109
110
                // Set the configured field
111
                $element->setField($this->config()->get('target_element_field'), $content);
112
113
                // Provide an extension hook for further updates to the new element
114
                $this->extend('updateMigratedElement', $element, $content, $page);
115
116
                // Add and write to the area
117
                $area->Elements()->add($element);
118
119
                // Publish the record if configured
120
                if ($this->config()->get('publish_changes') && $pageIsLive) {
121
                    $page->publishRecursive();
122
                }
123
124
                $pageTypeCount++;
125
            }
126
            $count += $pageTypeCount;
127
            echo 'Migrated ' . $pageTypeCount . ' ' . $pageType . ' pages\' content<br>';
128
        }
129
        echo 'Finished migrating ' . $count . ' pages\' content<br>';
130
    }
131
132
    /**
133
     * Indicates if the given page type is migratable
134
     *
135
     * @param string|SiteTree $pageType
136
     * @return bool
137
     */
138
    protected function isMigratable($pageType)
139
    {
140
        $migratable = SiteTree::has_extension($pageType, ElementalPageExtension::class);
141
142
        $this->extend('updateIsMigratable', $migratable, $pageType);
143
144
        return $migratable;
145
    }
146
147
    /**
148
     * Extracts the relevant ElementalArea from the given page. This can be overloaded for custom page types that might
149
     * prefer an alternate area to hold the migrated content
150
     *
151
     * @param SiteTree&ElementalPageExtension $page
152
     * @return ElementalArea
153
     */
154
    protected function getAreaRelationFromPage(SiteTree $page)
155
    {
156
        return $page->ElementalArea;
157
    }
158
159
    /**
160
     * Assert that the given page should actually have content migrated. By default this asserts that no elements
161
     * currently exist IFF the "clear_content" config is on
162
     *
163
     * @param SiteTree $page
164
     * @return bool
165
     */
166
    protected function shouldSkipMigration(SiteTree $page)
167
    {
168
        $skip = !$this->config()->get('clear_content')
169
            && $this->getAreaRelationFromPage($page)->Elements()->count() > 0;
170
171
        $this->extend('updatePageShouldSkip', $skip, $page);
172
173
        return $skip;
174
    }
175
}
176