source: trunk/server/www/app/models/gallery.php @ 358

Last change on this file since 358 was 358, checked in by sander, 9 years ago

Kill the SQL query cache. It does not work with the background daemon

File size: 14.0 KB
Line 
1<?php
2/**
3 * Officeshots.org - Test your office documents in different applications
4 * Copyright (C) 2009 Stichting Lone Wolves
5 * Written by Sander Marechal <s.marechal@jejik.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * The Gallery model
23 *
24 * Galleries can be used to publicly display requests
25 */
26class Gallery extends AppModel
27{
28        /** @var array Galleries have an owner and optionally a group who can edit the gallery */
29        public $belongsTo = array('Group', 'User');
30
31        /** @var string Every application supports a certain ODF doctype */
32        public $hasAndBelongsToMany = array('Request' => array('unique' => false));
33
34        /** Some galleries have a Testsuite */
35        public $hasOne = 'Testsuite';
36
37        /** @var array The model behaviors */
38        public $actsAs = array(
39                'Containable',
40                'Sluggable' => array('label' => 'name', 'overwrite' => true),
41                'Tree',
42        );
43
44        /**
45         * Check access control for this gallery
46         * @param string $user_id The user ID
47         * @param string $id The gallery ID
48         * @return boolean True or False
49         */
50        public function checkAccess($user_id, $id = false)
51        {
52                if (!$id) {
53                        $id = $this->id;
54                }
55
56                if (!$id) {
57                        return false;
58                }
59
60                $gallery = $this->find('first', array(
61                        'conditions' => array('Gallery.id' => $id),
62                        'recursive' => -1,
63                ));
64
65                if (!$gallery) {
66                        return false;
67                }
68
69                if ($gallery['Gallery']['user_id'] == $user_id) {
70                        return true;
71                }
72
73                if ($this->User->Group->has_member($user_id, $gallery['Gallery']['group_id'])) {
74                        return true;
75                }
76
77                return false;
78        }
79
80        /**
81         * Determine if this gallery is a test suite gallery
82         * @param string $id The gallery ID
83         * @return boolean True or False
84         */
85        public function isTestsuite($id = null)
86        {
87                if (!$id) {
88                        $id = $this->id;
89                }
90
91                if (!$id) {
92                        return false;
93                }
94
95                // First, find the root of the gallery tree
96                $gallery = $this->find('first', array(
97                        'conditions' => array('Gallery.id' => $id),
98                        'recursive' => -1,
99                ));
100
101                if ($gallery['Gallery']['parent_id']) {
102                        $root = $this->find('first', array(
103                                'conditions' => array(
104                                        'Gallery.parent_id IS NULL',
105                                        'Gallery.lft <=' => $gallery['Gallery']['lft'],
106                                        'Gallery.rght >=' => $gallery['Gallery']['rght'],
107                                ),
108                                'recursive' => -1,
109                        ));
110                } else {
111                        $root = $gallery;
112                }
113
114                $count = $this->Testsuite->find('count', array(
115                        'conditions' => array('Testsuite.gallery_id' => $root['Gallery']['id']),
116                        'recursive' => -1,
117                ));
118
119                return ($count > 0);
120        }
121
122        /**
123         * Add a request to a gallery
124         * @param string $request_id The request to add
125         * @param string $id The gallery ID, or $this->id
126         */
127        public function addRequest($request_id, $id = null)
128        {
129                if (!$id) {
130                        $id = $this->id;
131                }
132
133                $relation = $this->GalleriesRequest->find('first', array('conditions' => array(
134                        'gallery_id' => $id,
135                        'request_id' => $request_id,
136                )));
137
138                if (!empty($relation)) {
139                        return true;
140                }
141
142                $this->GalleriesRequest->create();
143                return $this->GalleriesRequest->save(array('GalleriesRequest' => array(
144                        'gallery_id' => $id,
145                        'request_id' => $request_id,
146                )));
147        }
148
149        /**
150         * Remove a request from a gallery
151         * @param string $request_id The request to add
152         * @param string $id The gallery ID, or $this->id
153         */
154        public function removeRequest($request_id, $id = null)
155        {
156                if (!$id) {
157                        $id = $this->id;
158                }
159
160                $relation = $this->GalleriesRequest->find('first', array('conditions' => array(
161                        'gallery_id' => $id,
162                        'request_id' => $request_id,
163                )));
164
165                if (!empty($relation)) {
166                        return $this->GalleriesRequest->del($relation['GalleriesRequest']['id']);
167                }
168
169                return true; // The relation didn't exist to begin with
170        }
171
172        /**
173         * Count the number of requests in this gallery
174         * @param int $id The Gallery ID
175         * @param bool $recursive True to count requests in subgalleries as well
176         */
177        public function requestCount($id = null, $recursive = false)
178        {
179                if (!$id) {
180                        $id = $this->id;
181                }
182
183                if (!$id) {
184                        return 0;
185                }
186
187                $num_requests = $this->GalleriesRequest->find('count', array(
188                        'conditions' => array('gallery_id' => $id),
189                ));
190
191                if ($recursive) {
192                        $gallery = $this->read(null, $id);
193
194                        if (!$gallery['Gallery']['lft'] && !$gallery['Gallery']['rght']) {
195                                return $num_requests;
196                        }
197
198                        $num_documents = $this->Request->query('SELECT COUNT(*) as `count`
199                                FROM `requests` AS `Request`
200                                LEFT JOIN `galleries_requests` AS `GalleriesRequest`
201                                        ON `Request`.`id` = `GalleriesRequest`.`request_id`
202                                LEFT JOIN `galleries` AS `Gallery`
203                                        ON `GalleriesRequest`.`gallery_id` = `Gallery`.`id`
204                                WHERE
205                                        `Gallery`.`lft` > ' . $gallery['Gallery']['lft'] . '
206                                        AND `Gallery`.`rght` < ' . $gallery['Gallery']['rght']);
207                        $num_requests = $num_requests + $num_documents[0][0]['count'];
208                }
209
210                return $num_requests;
211        }
212
213        /**
214         * Return all requests and jobs associated with a gallery and all subgalleries
215         * @param int $id The Gallery ID
216         * @return array
217         */
218        public function getRequests($id = null, $contain = array())
219        {
220                if (!$id) {
221                        $id = $this->id;
222                }
223
224                if (!$id) {
225                        return array();
226                }
227
228                $gallery = $this->find('first', array(
229                        'conditions' => array('Gallery.id' => $id),
230                        'recursive' => -1,
231                ));
232
233                if (empty($gallery)) {
234                        return array();
235                }
236
237                $result = $this->GalleriesRequest->find('all', array(
238                        'conditions' => array('GalleriesRequest.gallery_id' => $id),
239                        'recursive' => -1,
240                ));
241                $request_ids = Set::extract('/GalleriesRequest/request_id', $result);
242
243                $result = $this->query('SELECT DISTINCT `GalleriesRequest`.`request_id`
244                        FROM `galleries_requests` AS `GalleriesRequest`
245                        LEFT JOIN `galleries` AS `Gallery`
246                                ON `GalleriesRequest`.`gallery_id` = `Gallery`.`id`
247                        WHERE
248                                `Gallery`.`lft` > ' . $gallery['Gallery']['lft'] . '
249                                AND `Gallery`.`rght` < ' . $gallery['Gallery']['rght']);
250                $subgallery_request_ids = Set::extract('/GalleriesRequest/request_id', $result);
251                $request_ids = Set::merge($request_ids, $subgallery_request_ids);
252
253                $requests = $this->Request->find('all', array(
254                        'contain' => $contain,
255                        'conditions' => array('Request.id' => $request_ids),
256                ));
257
258                return $requests;
259        }
260
261        /**
262         * Get the view data for a gallery
263         * This is implemented in the model so we can call it in the background and cache the result
264         */
265        public function viewData($id)
266        {
267                // Get the gallery
268                $prof_start = microtime(true);
269                $gallery = $this->find('first', array(
270                        'conditions' => array('Gallery.id' => $id),
271                        'contain' => array(
272                                'Request' => array('order' => 'Request.filename ASC'),
273                                'Request.Validator' => array(
274                                        'fields' => array('id', 'parent_id', 'name', 'state'),
275                                        'order' => 'Validator.name ASC',
276                                ),
277                                'Request.Job' => array('order' => array(
278                                        'Application.name ASC',
279                                        'Job.version ASC',
280                                        'Platform.name ASC',
281                                        'Format.name ASC',
282                                )),
283                                'Request.Job.Application',
284                                'Request.Job.Format',
285                                'Request.Job.Platform',
286                                'Request.Job.Result',
287                                'Request.Job.Result.Validator' => array(
288                                        'fields' => array('id', 'parent_id', 'name', 'state'),
289                                        'order' => 'Validator.name ASC',
290                                ),
291                        ),
292                ));
293
294                if (empty($gallery)) {
295                        return array(
296                                'gallery' => array(),
297                                'applications' => array(),
298                                'path' => array(),
299                                'abbreviate' => false,
300                                'isTestsuite' => false,
301                        );
302                }
303
304                // Find out if this test suite needs to be abbreviated
305                $requestCount = $this->requestCount($id, true);
306                $isTestsuite = $this->isTestsuite($id);
307                $abbreviate = ($requestCount >= Configure::read('Gallery.abbreviate') && !$isTestsuite);
308
309                // Get the children of this gallery
310                $children = $this->find('all', array(
311                        'conditions' => array(
312                                'Gallery.lft >' => $gallery['Gallery']['lft'],
313                                'Gallery.rght <' => $gallery['Gallery']['rght'],
314                        ),
315                        'contain' => array('Request' => array('order' => 'Request.filename ASC')),
316                        'order' => array('Gallery.name ASC'),
317                ));
318
319                foreach ($children as &$child) {
320                        foreach ($child['Request'] as &$request) {
321
322                                $request['Validator'] = $this->Request->Validator->find('all', array(
323                                        'fields'     => array('id', 'parent_id', 'name', 'state'),
324                                        'conditions' => array('Validator.parent_id' => $request['id']),
325                                        'order'      => 'Validator.name ASC',
326                                        'recursive'  => -1,
327                                ));
328
329                                if (!$abbreviate) {
330                                        $request['Job'] = $this->Request->Job->query("SELECT
331                                                        `Job`.`id`,
332                                                        `Job`.`request_id`,
333                                                        `Job`.`platform_id`,
334                                                        `Job`.`application_id`,
335                                                        `Job`.`version`,
336                                                        `Job`.`format_id`,
337                                                        `Job`.`result_id`,
338                                                        `Job`.`locked`,
339                                                        `Platform`.`id`,
340                                                        `Platform`.`name`,
341                                                        `Application`.`id`,
342                                                        `Application`.`name`,
343                                                        `Result`.`id`,
344                                                        `Result`.`state`,
345                                                        `Result`.`state_info`,
346                                                        `Result`.`verified`,
347                                                        `Result`.`created`,
348                                                        `Format`.`id`,
349                                                        `Format`.`name`,
350                                                        `Format`.`icon`,
351                                                        `Format`.`code`
352                                                FROM `jobs` AS `Job`
353                                                        LEFT JOIN `platforms` AS `Platform` ON (`Job`.`platform_id` = `Platform`.`id`)
354                                                        LEFT JOIN `applications` AS `Application` ON (`Job`.`application_id` = `Application`.`id`)
355                                                        LEFT JOIN `results` AS `Result` ON (`Job`.`result_id` = `Result`.`id`)
356                                                        LEFT JOIN `formats` AS `Format` ON (`Job`.`format_id` = `Format`.`id`)
357                                                WHERE `Job`.`request_id` = '" . $request['id'] . "'
358                                                ORDER BY
359                                                        `Application`.`name` ASC,
360                                                        `Job`.`version` ASC,
361                                                        `Platform`.`name` ASC,
362                                                        `Format`.`name` ASC");
363
364                                        // Fix array layout to match a regular query layout
365                                        foreach ($request['Job'] as &$job) {
366                                                foreach ($job['Job'] as $attr => $value) {
367                                                        $job[$attr] = $value;
368                                                }
369                                                unset($job['Job']);
370
371                                                if (!isset($job['Result']['id']) || !$job['Result']['id']) {
372                                                        continue;
373                                                }
374
375                                                $job['Result']['Validator'] = $this->Request->Validator->find('all', array(
376                                                        'fields'     => array('id', 'parent_id', 'name', 'state'),
377                                                        'conditions' => array('Validator.parent_id' => $job['Result']['id']),
378                                                        'order'      => 'Validator.name ASC',
379                                                        'recursive'  => -1,
380                                                ));
381                                        }
382                                        unset($job);
383                                }
384                        }
385                }
386
387                // If this is a Testsuite then we need some extra information
388                $applications = array();
389                if ($isTestsuite) {
390                        $versions = array();
391                        $jobs = Set::extract('/Request/Job', $gallery);
392                        $jobs = array_merge(Set::extract('/Request/Job', $children), $jobs);
393
394                        foreach ($jobs as $job) {
395                                if (empty($job['Job'])) { // When a testsuite request has no jobs yet
396                                        continue;
397                                }
398
399                                if ($job['Job']['Format']['code'] != 'odf') {
400                                        continue;
401                                }
402
403                                $application = $job['Job']['application_id'];
404                                if (!isset($versions[$application])) {
405                                        $versions[$application] = $job['Job']['version'];
406                                        continue;
407                                }
408
409                                if ($job['Job']['version'] > $versions[$application]) {
410                                        $versions[$application] = $job['Job']['version'];
411                                }
412                        }
413                        unset($application); // Because we're referencing below
414
415                        $applications = $this->Request->Job->Application->find('all', array(
416                                'conditions' => array('Application.id' => array_keys($versions)),
417                                'order' => array('Application.name'),
418                                'recursive' => -1,
419                        ));
420
421                        foreach ($applications as &$application) {
422                                $application['Application']['version'] = $versions[$application['Application']['id']];
423                        }
424                        unset($application); // Drop the reference
425                }
426
427                // Sort the subgalleries for display
428                $gallery['children'] = $this->_sortThreaded($children);
429
430                // Get the path to this gallery
431                $path = $this->getpath($gallery['Gallery']['id']);
432                array_pop($path);
433
434                $this->log(sprintf('Gallery %s: generated viewData in %f seconds', $id, microtime(true) - $prof_start), LOG_DEBUG);
435                return compact('gallery', 'applications', 'path', 'abbreviate', 'isTestsuite');
436        }
437
438        /**
439         * Sort an array of Galleries threaded, like find('threaded') would do
440         */
441        private function _sortThreaded($results)
442        {
443                $return = $idMap = array();
444                $ids = Set::extract($results, '{n}.Gallery.id');
445
446                foreach ($results as $result) {
447                        $result['children'] = array();
448                        $id = $result['Gallery']['id'];
449                        $parentId = $result['Gallery']['parent_id'];
450                        if (isset($idMap[$id]['children'])) {
451                                $idMap[$id] = array_merge($result, (array)$idMap[$id]);
452                        } else {
453                                $idMap[$id] = array_merge($result, array('children' => array()));
454                        }
455                        if (!$parentId || !in_array($parentId, $ids)) {
456                                $return[] =& $idMap[$id];
457                        } else {
458                                $idMap[$parentId]['children'][] =& $idMap[$id];
459                        }
460                }
461                if (count($return) > 1) {
462                        $ids = array_unique(Set::extract('/Gallery/parent_id', $return));
463                        if (count($ids) > 1) {
464                                $root = $return[0]['Gallery']['parent_id'];
465                                foreach ($return as $key => $value) {
466                                        if ($value['Gallery']['parent_id'] != $root) {
467                                                unset($return[$key]);
468                                        }
469                                }
470                        }
471                }
472                return $return;
473        }
474
475        /**
476         * Convert the Markdown description to HTML before saving
477         * @return boolean True to continue saving
478         */
479        public function beforeSave()
480        {
481                if (isset($this->data['Gallery']['description'])) {
482                        // Convert the markdown description to HTML
483                        App::import('Vendor', 'markdown');
484                        App::import('Vendor', 'HTMLPurifier', array('file' => 'htmlpurifier/HTMLPurifier.standalone.php'));
485
486                        $config = HTMLPurifier_Config::createDefault();
487                        $config->set('Cache.SerializerPath', CACHE . DS . 'htmlpurifier');
488                        $purifier = new HTMLPurifier($config);
489
490                        $desc_html = Markdown($this->data['Gallery']['description']);
491                        $this->data['Gallery']['description_html'] = $purifier->purify($desc_html);
492
493                        // Save the markdown version to file
494                        $suite = $this->Testsuite->find('first', array(
495                                'conditions' => array('Testsuite.gallery_id' => $this->id),
496                                'recursive' => -1,
497                        ));
498
499                        if ($suite) {
500                                $path = FILES . $suite['Testsuite']['root'] . DS . 'description.txt';
501                                file_put_contents($path, $this->data['Gallery']['description']);
502                        }
503                }
504
505                return true;
506        }
507}
508
509?>
Note: See TracBrowser for help on using the repository browser.