Completed
Pull Request — master (#4416)
by Craig
20:52
created

checkSerializedBlockContent()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
nc 3
nop 0
dl 0
loc 23
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\BlocksModule;
15
16
use Doctrine\Persistence\ManagerRegistry;
17
use Symfony\Component\Filesystem\Filesystem;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
use Symfony\Contracts\Translation\TranslatorInterface;
20
use Zikula\BlocksModule\Block\HtmlBlock;
21
use Zikula\BlocksModule\Entity\BlockEntity;
22
use Zikula\BlocksModule\Entity\BlockPlacementEntity;
23
use Zikula\BlocksModule\Entity\BlockPositionEntity;
24
use Zikula\Bundle\CoreBundle\Doctrine\Helper\SchemaHelper;
25
use Zikula\ExtensionsModule\AbstractExtension;
26
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
27
use Zikula\ExtensionsModule\Entity\ExtensionEntity;
28
use Zikula\ExtensionsModule\Installer\AbstractExtensionInstaller;
29
use Zikula\SearchModule\Block\SearchBlock;
30
use Zikula\UsersModule\Block\LoginBlock;
31
32
class BlocksModuleInstaller extends AbstractExtensionInstaller
33
{
34
    private $entities = [
35
        BlockEntity::class,
36
        BlockPositionEntity::class,
37
        BlockPlacementEntity::class
38
    ];
39
40
    /**
41
     * @var string
42
     */
43
    private $projectDir;
44
45
    public function __construct(
46
        AbstractExtension $extension,
47
        ManagerRegistry $managerRegistry,
48
        SchemaHelper $schemaTool,
49
        RequestStack $requestStack,
50
        TranslatorInterface $translator,
51
        VariableApiInterface $variableApi,
52
        string $projectDir
53
    ) {
54
        parent::__construct($extension, $managerRegistry, $schemaTool, $requestStack, $translator, $variableApi);
55
        $this->projectDir = $projectDir;
56
    }
57
58
    public function install(): bool
59
    {
60
        $this->schemaTool->create($this->entities);
61
        $this->setVar('collapseable', false);
62
63
        return true;
64
    }
65
66
    public function upgrade(string $oldVersion): bool
67
    {
68
        switch ($oldVersion) {
69
            // 3.9.6 shipped with Core-1.4.3
70
            // 3.9.8 shipped with Core-2.0.15
71
            // version number reset to 3.0.0 at Core 3.0.0
72
            case '2.9.9':
73
                $this->updateLangToLocaleBlock();
74
                $this->removeSlashFromBKey();
75
                $this->convertTemplatePaths();
76
                $this->checkSerializedBlockContent();
77
        }
78
79
        return true;
80
    }
81
82
    private function updateLangToLocaleBlock(): void
83
    {
84
        // for Core-1.4.4
85
        $statement = $this->entityManager->getConnection()->executeQuery("SELECT * FROM blocks WHERE blocktype = 'Lang'");
0 ignored issues
show
Bug introduced by
The method getConnection() does not exist on Doctrine\Persistence\ObjectManager. It seems like you code against a sub-type of Doctrine\Persistence\ObjectManager such as Doctrine\ORM\EntityManagerInterface or Doctrine\ORM\Decorator\EntityManagerDecorator. ( Ignorable by Annotation )

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

85
        $statement = $this->entityManager->/** @scrutinizer ignore-call */ getConnection()->executeQuery("SELECT * FROM blocks WHERE blocktype = 'Lang'");
Loading history...
86
        $blocks = $statement->fetchAll(\PDO::FETCH_ASSOC);
87
        if (count($blocks) > 0) {
88
            $this->entityManager->getConnection()->executeQuery("UPDATE blocks set bkey=?, blocktype=?, properties=? WHERE blocktype = 'Lang'", [
89
                'ZikulaSettingsModule:Zikula\SettingsModule\Block\LocaleBlock',
90
                'Locale',
91
                'a:0:{}'
92
            ]);
93
            $this->addFlash('success', 'All instances of LangBlock have been converted to LocaleBlock.');
94
        }
95
        $this->entityManager->getConnection()->executeQuery(
96
            "UPDATE group_perms SET component = REPLACE(component, 'Languageblock', 'LocaleBlock') WHERE component LIKE 'Languageblock%'"
97
        );
98
    }
99
100
    private function removeSlashFromBKey(): void
101
    {
102
        // for Core-3.0.0
103
        $statement = $this->entityManager->getConnection()->executeQuery("SELECT * FROM blocks");
104
        $blocks = $statement->fetchAll(\PDO::FETCH_ASSOC);
105
        foreach ($blocks as $block) {
106
            $bKey = $block['bkey'];
107
            if (mb_strpos($bKey, ':')) {
108
                [/*$moduleName*/, $bKey] = explode(':', $bKey);
109
            }
110
            $this->entityManager->getConnection()->executeUpdate('UPDATE blocks SET bKey=? WHERE bid=?', [trim($bKey, '\\'), $block['bid']]);
111
        }
112
    }
113
114
    private function convertTemplatePaths(): void
115
    {
116
        // for Core-3.0.0
117
        $statement = $this->entityManager->getConnection()->executeQuery("SELECT * FROM blocks WHERE blocktype = 'Menu'");
118
        $blocks = $statement->fetchAll(\PDO::FETCH_ASSOC);
119
        foreach ($blocks as $block) {
120
            $properties = unserialize($block['properties']);
121
            if (isset($properties['template'])) {
122
                $properties['template'] = '@' . str_replace(':', '/', $properties['template']);
123
                $this->entityManager->getConnection()->executeUpdate('UPDATE blocks SET properties=? WHERE bid=?', [serialize($properties), $block['bid']]);
124
            }
125
        }
126
    }
127
128
    /**
129
     * Block content that cannot be unserialized must be removed because it causes problems later.
130
     * In order to not lose the data, the content is dumped to individual files in /var/log
131
     */
132
    private function checkSerializedBlockContent(): void
133
    {
134
        $statement = $this->entityManager->getConnection()->executeQuery("SELECT * FROM blocks");
135
        $blocks = $statement->fetchAll(\PDO::FETCH_ASSOC);
136
        // temporarily suppress E_NOTICE to avoid using @unserialize
137
        $errorReporting = error_reporting(error_reporting() ^ E_NOTICE);
138
        $fs = new Filesystem();
139
        foreach ($blocks as $block) {
140
            if (false === unserialize($block['properties'])) {
141
                // failure to unserialize returns FALSE
142
                $this->entityManager->getConnection()->executeQuery(
143
                    "DELETE FROM block_placements WHERE bid=?",
144
                    [$block['bid']]
145
                );
146
                $this->entityManager->getConnection()->executeQuery(
147
                    "DELETE FROM blocks WHERE bid=?",
148
                    [$block['bid']]
149
                );
150
                $name = '/var/log/removedBlock' . $block['bid'] . '.txt';
151
                $fs->dumpFile($this->projectDir . $name, $block['properties']);
152
            }
153
        }
154
        error_reporting($errorReporting);
155
    }
156
157
    public function uninstall(): bool
158
    {
159
        // Deletion not allowed
160
        return false;
161
    }
162
163
    /**
164
     * Add default block data for new installations.
165
     * This is called after a complete installation since the blocks
166
     * need to be populated with module id's which are only available
167
     * once the installation has been completed.
168
     */
169
    public function createDefaultData(): void
170
    {
171
        // create the default block positions - left, right and center for the traditional 3 column layout
172
        $positions = [
173
            'left' => $this->trans('Left blocks'),
174
            'right' => $this->trans('Right blocks'),
175
            'center' => $this->trans('Center blocks'),
176
            'search' => $this->trans('Search block'),
177
            'header' => $this->trans('Header block'),
178
            'footer' => $this->trans('Footer block'),
179
            'topnav' => $this->trans('Top navigation block'),
180
            'bottomnav' => $this->trans('Bottom navigation block')
181
        ];
182
        foreach ($positions as $name => $description) {
183
            $positions[$name] = new BlockPositionEntity();
184
            $positions[$name]->setName($name);
185
            $positions[$name]->setDescription($description);
186
            $this->entityManager->persist($positions[$name]);
0 ignored issues
show
Bug introduced by
$positions[$name] of type string is incompatible with the type object expected by parameter $object of Doctrine\Persistence\ObjectManager::persist(). ( Ignorable by Annotation )

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

186
            $this->entityManager->persist(/** @scrutinizer ignore-type */ $positions[$name]);
Loading history...
187
        }
188
        $this->entityManager->flush();
189
190
        $hellomessage = $this->trans('<p><a href="https://ziku.la">Zikula</a> is an Open Source Content Application Framework built on top of Symfony.</p><p>With Zikula you get:</p><ul><li><strong>Power:</strong> You get the all the features of <a href="https://symfony.com">Symfony</a> PLUS: </li><li><strong>User Management:</strong> Built in User and Group management with Rights/Roles control</li><li><strong>Front end control:</strong> You can customise all aspects of the site\'s appearance through themes, with support for <a href="http://jquery.com">jQuery</a>, <a href="http://getbootstrap.com">Bootstrap</a> and many other modern technologies</li><li><strong>Internationalization (i18n):</strong> You can mark content as being suitable for either a single language or for all languages, and can control all aspects of localisation of your site</li><li><strong>Extensibility:</strong> you get a standard application-programming interface (API) that lets you easily extend your site\'s functionality through modules</li><li><strong>More:</strong> Admin UI, global categories, site-wide search, content blocks, menu creation, and more!</li><li><strong>Support:</strong> you can get help and support from the Zikula community of webmasters and developers at <a href="https://ziku.la">ziku.la</a>, <a href="https://github.com/zikula/core">Github</a> and <a href="https://zikula.slack.com/">Slack</a>.</li></ul><p>Enjoy using Zikula!</p><p><strong>The Zikula team</strong></p><p><em>Note: Zikula is Free Open Source Software (FOSS) licensed under the GNU General Public License.</em></p>');
191
192
        $blocks = [];
193
        $extensionRepo = $this->entityManager->getRepository(ExtensionEntity::class);
194
        $blocksModuleEntity = $extensionRepo->findOneBy(['name' => 'ZikulaBlocksModule']);
195
        $searchModuleEntity = $extensionRepo->findOneBy(['name' => 'ZikulaSearchModule']);
196
        $usersModuleEntity = $extensionRepo->findOneBy(['name' => 'ZikulaUsersModule']);
197
        $blocks[] = [
198
            'bkey' => SearchBlock::class,
199
            'blocktype' => 'Search',
200
            'language' => '',
201
            'module' => $searchModuleEntity,
202
            'title' => $this->trans('Search box'),
203
            'description' => $this->trans('Search block'),
204
            'properties' => [
205
                'displaySearchBtn' => true,
206
                'active' => ['ZikulaUsersModule' => 1]
207
            ],
208
            'position' => $positions['left']
209
        ];
210
        $blocks[] = [
211
            'bkey' => HtmlBlock::class,
212
            'blocktype' => 'Html',
213
            'language' => '',
214
            'module' => $blocksModuleEntity,
215
            'title' => $this->trans('This site is powered by Zikula!'),
216
            'description' => $this->trans('HTML block'),
217
            'properties' => ['content' => $hellomessage],
218
            'position' => $positions['center']
219
        ];
220
        $blocks[] = [
221
            'bkey' => LoginBlock::class,
222
            'blocktype' => 'Login',
223
            'language' => '',
224
            'module' => $usersModuleEntity,
225
            'title' => $this->trans('User log-in'),
226
            'description' => $this->trans('Login block'),
227
            'position' => $positions['topnav'],
228
            'order' => 1,
229
            'filters' => [[
230
                'attribute' => '_route',
231
                'queryParameter' => null,
232
                'comparator' => '!=',
233
                'value' => 'zikulausersmodule_access_login'
234
            ]]
235
        ];
236
237
        foreach ($blocks as $block) {
238
            $blockEntity = new BlockEntity();
239
            $position = $block['position'];
240
            $sortOrder = !empty($block['order']) ? $block['order'] : 0;
241
            unset($block['position'], $block['order']);
242
            $blockEntity->merge($block);
243
            $this->entityManager->persist($blockEntity);
244
            $placement = new BlockPlacementEntity();
245
            $placement->setBlock($blockEntity);
246
            $placement->setPosition($position);
247
            $placement->setSortorder($sortOrder);
248
            $this->entityManager->persist($placement);
249
        }
250
        $this->entityManager->flush();
251
    }
252
}
253