aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/updater/Updater.php
blob: 4f557d0f58fabca7429684eadbf3dfcc82bb1f7c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<?php

namespace Shaarli\Updater;

use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\Updater\Exception\UpdaterException;

/**
 * Class Updater.
 * Used to update stuff when a new Shaarli's version is reached.
 * Update methods are ran only once, and the stored in a TXT file.
 */
class Updater
{
    /**
     * @var array Updates which are already done.
     */
    protected $doneUpdates;

    /**
     * @var BookmarkServiceInterface instance.
     */
    protected $bookmarkService;

    /**
     * @var ConfigManager $conf Configuration Manager instance.
     */
    protected $conf;

    /**
     * @var bool True if the user is logged in, false otherwise.
     */
    protected $isLoggedIn;

    /**
     * @var \ReflectionMethod[] List of current class methods.
     */
    protected $methods;

    /**
     * @var string $basePath Shaarli root directory (from HTTP Request)
     */
    protected $basePath = null;

    /**
     * Object constructor.
     *
     * @param array                    $doneUpdates Updates which are already done.
     * @param BookmarkServiceInterface $linkDB      LinksService instance.
     * @param ConfigManager            $conf        Configuration Manager instance.
     * @param boolean                  $isLoggedIn  True if the user is logged in.
     */
    public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
    {
        $this->doneUpdates = $doneUpdates;
        $this->bookmarkService = $linkDB;
        $this->conf = $conf;
        $this->isLoggedIn = $isLoggedIn;

        // Retrieve all update methods.
        $class = new \ReflectionClass($this);
        $this->methods = $class->getMethods();
    }

    /**
     * Run all new updates.
     * Update methods have to start with 'updateMethod' and return true (on success).
     *
     * @param string $basePath Shaarli root directory (from HTTP Request)
     *
     * @return array An array containing ran updates.
     *
     * @throws UpdaterException If something went wrong.
     */
    public function update(string $basePath = null)
    {
        $updatesRan = [];

        // If the user isn't logged in, exit without updating.
        if ($this->isLoggedIn !== true) {
            return $updatesRan;
        }

        if ($this->methods === null) {
            throw new UpdaterException('Couldn\'t retrieve LegacyUpdater class methods.');
        }

        foreach ($this->methods as $method) {
            // Not an update method or already done, pass.
            if (
                ! startsWith($method->getName(), 'updateMethod')
                || in_array($method->getName(), $this->doneUpdates)
            ) {
                continue;
            }

            try {
                $method->setAccessible(true);
                $res = $method->invoke($this);
                // Update method must return true to be considered processed.
                if ($res === true) {
                    $updatesRan[] = $method->getName();
                }
            } catch (\Exception $e) {
                throw new UpdaterException($method, $e);
            }
        }

        $this->doneUpdates = array_merge($this->doneUpdates, $updatesRan);

        return $updatesRan;
    }

    /**
     * @return array Updates methods already processed.
     */
    public function getDoneUpdates()
    {
        return $this->doneUpdates;
    }

    public function readUpdates(string $updatesFilepath): array
    {
        return UpdaterUtils::readUpdatesFile($updatesFilepath);
    }

    public function writeUpdates(string $updatesFilepath, array $updates): void
    {
        UpdaterUtils::writeUpdatesFile($updatesFilepath, $updates);
    }

    /**
     * With the Slim routing system, default header link should be `/subfolder/` instead of `?`.
     * Otherwise you can not go back to the home page.
     * Example: `/subfolder/picture-wall` -> `/subfolder/picture-wall?` instead of `/subfolder/`.
     */
    public function updateMethodRelativeHomeLink(): bool
    {
        if ('?' === trim($this->conf->get('general.header_link'))) {
            $this->conf->set('general.header_link', $this->basePath . '/', true, true);
        }

        return true;
    }

    /**
     * With the Slim routing system, note bookmarks URL formatted `?abcdef`
     * should be replaced with `/shaare/abcdef`
     */
    public function updateMethodMigrateExistingNotesUrl(): bool
    {
        $updated = false;

        foreach ($this->bookmarkService->search() as $bookmark) {
            if (
                $bookmark->isNote()
                && startsWith($bookmark->getUrl(), '?')
                && 1 === preg_match('/^\?([a-zA-Z0-9-_@]{6})($|&|#)/', $bookmark->getUrl(), $match)
            ) {
                $updated = true;
                $bookmark = $bookmark->setUrl('/shaare/' . $match[1]);

                $this->bookmarkService->set($bookmark, false);
            }
        }

        if ($updated) {
            $this->bookmarkService->save();
        }

        return true;
    }

    public function setBasePath(string $basePath): self
    {
        $this->basePath = $basePath;

        return $this;
    }
}