Issues (55)

src/components/DocumentApiComponent.php (1 issue)

Severity
1
<?php
2
/**
3
 * Created by jensk on 26-3-2018.
4
 */
5
6
namespace CloudControl\Cms\components;
7
8
9
use CloudControl\Cms\cc\Application;
10
use CloudControl\Cms\cc\Request;
11
use CloudControl\Cms\cc\ResponseHeaders;
12
use CloudControl\Cms\components\api\Response;
13
use CloudControl\Cms\search\CharacterFilter;
14
use CloudControl\Cms\search\results\SearchSuggestion;
15
use CloudControl\Cms\search\Search;
16
use CloudControl\Cms\search\Tokenizer;
17
use CloudControl\Cms\storage\entities\Document;
18
use CloudControl\Cms\storage\Storage;
19
use CloudControl\Cms\storage\storage\DocumentStorage;
20
21
class DocumentApiComponent extends CachableBaseComponent
22
{
23
    const GET_PARAMETER_PATH = 'path';
24
    const GET_PARAMETER_Q = 'q';
25
    /**
26
     * @var Response|string
27
     */
28
    protected $response;
29
30
    /**
31
     * @param Storage $storage
32
     */
33
    public function run(Storage $storage)
34
    {
35
        parent::run($storage);
36
        ResponseHeaders::add(ResponseHeaders::HEADER_CONTENT_TYPE, ResponseHeaders::HEADER_CONTENT_TYPE_CONTENT_APPLICATION_JSON);
37
        $this->setResponse();
38
    }
39
40
    private function setResponse()
41
    {
42
        try {
43
44
            if (isset($_GET[self::GET_PARAMETER_Q])) {
45
                $this->response = $this->getSearchDocumentsResponse();
46
                return;
47
            }
48
49
            if (isset($_GET[self::GET_PARAMETER_PATH])) {
50
                $this->response = $this->getDocumentsByPathResponse();
51
                return;
52
            }
53
            $this->response = '{"swagger":"2.0","info":{"description":"This is documentation for the DocumentApiComponent of the Cloud Control Framework & CMS. See https://getcloudcontrol.org. Additional documentation regarding the DocumentApiComponent can be found here: https://github.com/jenskooij/cloudcontrol/wiki/DocumentApiComponent","title":"DocumentApiComponent for Cloud Control - Framework & CMS","contact":{"name":"Cloud Control - Framework & CMS","url":"https://getcloudcontrol.org"},"license":{"name":"MIT","url":"https://github.com/jenskooij/cloudcontrol/blob/master/LICENSE"},"version":"1.0.0"},"basePath":"' . Request::$subfolders . '","paths":{"' . Request::$subfolders . Request::$relativeUri . '":{"get":{"summary":"Retrieve documents","produces":["application/json"],"parameters":[{"name":"q","in":"query","description":"Search query","required":false,"type":"string"},{"name":"path","in":"query","description":"The (folder) path of which you want to see the contents","required":false,"type":"string"}],"responses":{"200":{"description":"Successful operation","schema":{"$ref":"#/definitions/ApiResponse"}},"default":{"schema":{"type":"string","example":"{}"},"description":"When no parameters present, shows the swagger definition"}}}}},"definitions":{"ApiResponse":{"type":"object","properties":{"success":{"type":"boolean","title":"Wheter or not the call was processed succesful"},"results":{"type":"array","title":"The array of Documents that were found","items":{"$ref":"#/definitions/Document"}},"error":{"title":"If an error occured, it will be displayed, empty if not.","type":"string"},"folder":{"type":"string","title":"Path of the currently selected folder, by using the id parameter","example":"/folder"},"searchSuggestions":{"type":"array","items":{"type":"array","items":{"$ref":"#/definitions/SearchSuggestion"}}}}},"Document":{"type":"object","properties":{"id":{"type":"string","title":"The Document identifier","example":"1"},"path":{"type":"string","title":"The Document path"},"title":{"type":"string","title":"The Document title"},"slug":{"type":"string","title":"The Document slug"},"type":{"type":"string","title":"The Document type","enum":["document","folder"]},"documentType":{"type":"string","title":"The Document DocumentType, as defined in the CMS"},"documentTypeSlug":{"type":"string","title":"The Document DocumentType slug"},"state":{"type":"string","title":"The publication state for this document","enum":["published","unpublished"]},"lastModificationDate":{"type":"string","title":"The Document\'s last modification timestamp","example":"0"},"publicationDate":{"type":"string","title":"The Document\'s publication timestamp","example":"0"}}},"SearchSuggestion":{"type":"object","properties":{"original":{"type":"string","title":"The query that was retrieved from parameter q","example":"kyeword"},"term":{"type":"string","title":"An existing term that is closest to the original","example":"keyword"},"editDistance":{"type":"string","title":"The amount of changes were made to get from the term to the original","example":"2"}}}}}';
54
            return;
55
        } catch (\Exception $e) {
56
            $error = $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage();
57
            $this->response = new Response(array(), false, $error);
58
            return;
59
        }
60
    }
61
62
    /**
63
     * @return Document
64
     * @throws \RuntimeException
65
     */
66
    private function getDococumentPathPath()
67
    {
68
        $path = $_GET[self::GET_PARAMETER_PATH];
69
        $db = $this->storage->getRepository()->getContentRepository()->getContentDbHandle();
70
        $stmt = $this->getPDOStatement($db, $this->getDocumentByPathSql($db, $path));
71
        return $stmt->fetchObject(Document::class);
72
    }
73
74
    /**
75
     * @param \PDO $db
76
     * @param int $path
77
     * @return string
78
     */
79
    private function getDocumentByPathSql($db, $path)
80
    {
81
        return 'SELECT *
82
              FROM documents_published
83
             WHERE path = ' . $db->quote($path) . '
84
        ';
85
    }
86
87
    /**
88
     * @param \PDO $db
89
     * @param string $sql
90
     * @return \PDOStatement
91
     * @throws \RuntimeException
92
     */
93
    private function getPDOStatement($db, $sql)
94
    {
95
        $stmt = $db->query($sql);
96
        if ($stmt === false) {
97
            $errorInfo = $db->errorInfo();
98
            $errorMsg = $errorInfo[2];
99
            throw new \RuntimeException('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
100
        }
101
        return $stmt;
102
    }
103
104
    /**
105
     * @return Response
106
     * @throws \RuntimeException
107
     */
108
    private function getSingleDocumentResponse()
109
    {
110
        $document = $this->getDococumentPathPath();
111
112
        if ($document === false) {
0 ignored issues
show
The condition $document === false is always false.
Loading history...
113
            return new Response();
114
        }
115
116
        if ($document->type === 'folder') {
117
            return $this->getFolderResponseByDocument($document);
118
        }
119
120
        $documentContent = $this->getDocumentContent($document);
121
        $document->documentContent = $documentContent;
122
123
        return new Response($document);
124
    }
125
126
    /**
127
     * @param Application $application
128
     */
129
    public function render($application = null)
130
    {
131
        $this->renderedContent = $this->response;
132
    }
133
134
    /**
135
     * @return Response
136
     * @throws \Exception
137
     */
138
    private function getDocumentsByPathResponse()
139
    {
140
        $slug = $this->getSlugFromPath();
141
142
        if (($response = $this->getFolderWithState($slug)) !== false) {
143
            return $response;
144
        }
145
146
        if (($response = $this->getFolderWithoutState($slug)) !== false) {
147
            return $response;
148
        }
149
150
        return $this->getSingleDocumentResponse();
151
    }
152
153
    /**
154
     * @return Response
155
     * @throws \Exception
156
     */
157
    private function getSearchDocumentsResponse()
158
    {
159
        $rawResults = $this->getRawResults();
160
        $results = array();
161
        $suggestions = array();
162
        foreach ($rawResults as $rawResult) {
163
            if ($rawResult instanceof SearchSuggestion) {
164
                $suggestions[] = $rawResults;
165
                continue;
166
            }
167
            $result = $rawResult->getDocument();
168
            $result->searchInfo = $rawResult;
169
            $results[] = $result;
170
        }
171
        $response = new Response($results);
172
        $response->searchSuggestions = $suggestions;
173
        return $response;
174
    }
175
176
    /**
177
     * @param Document $document
178
     * @return \stdClass
179
     */
180
    private function getDocumentContent($document)
181
    {
182
        $documentContent = new \stdClass();
183
        $documentContent->fields = $document->fields;
184
        $documentContent->bricks = $document->bricks;
185
        $documentContent->dynamicBricks = $document->dynamicBricks;
186
        return $documentContent;
187
    }
188
189
    /**
190
     * @return array
191
     * @throws \Exception
192
     */
193
    private function getRawResults()
194
    {
195
        $filteredQuery = new CharacterFilter($_GET[self::GET_PARAMETER_Q]);
196
        $tokenizer = new Tokenizer($filteredQuery);
197
        $search = new Search($this->storage);
198
        $rawResults = $search->getDocumentsForTokenizer($tokenizer);
199
        return $rawResults;
200
    }
201
202
    /**
203
     * @param Document $document
204
     * @return Response
205
     * @throws \RuntimeException
206
     */
207
    private function getFolderResponseByDocument($document)
208
    {
209
        if ($document->type !== 'folder') {
210
            return new Response();
211
        }
212
        $document->dbHandle = $this->storage->getContentDbHandle();
213
        $document->documentStorage = new DocumentStorage($this->storage->getRepository());
214
        $response = new Response($document->getContent());
215
        $response->folder = $document->path;
216
        return $response;
217
    }
218
219
    /**
220
     * @return bool|string
221
     */
222
    private function getSlugFromPath()
223
    {
224
        $path = $_GET[self::GET_PARAMETER_PATH];
225
        if ($path[0] === '/') {
226
            $path = substr($path, 1);
227
        }
228
        return $path;
229
    }
230
231
    /**
232
     * @param string $slug
233
     * @return bool|Response
234
     */
235
    private function getFolderWithState($slug)
236
    {
237
        if (!CmsComponent::isCmsLoggedIn()) {
238
            return false;
239
        }
240
        $documents = $this->storage->getDocuments()->getDocumentsWithState('/' . $slug);
241
242
        if (!empty($documents)) {
243
            $response = new Response($documents);
244
            $response->folder = $_GET[self::GET_PARAMETER_PATH];
245
            return $response;
246
        }
247
        return false;
248
    }
249
250
    /**
251
     * @param $slug
252
     * @return bool|Response
253
     * @throws \Exception
254
     */
255
    private function getFolderWithoutState($slug)
256
    {
257
        $folderDocument = $this->storage->getDocuments()->getDocumentFolderBySlug($slug);
258
259
        if ($folderDocument instanceof Document && $folderDocument->type === 'folder') {
260
            return $this->getFolderResponseByDocument($folderDocument);
261
        }
262
        return false;
263
    }
264
265
266
}