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

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

Do not regenerate viewData for empty or non-existant galleries

File size: 13.8 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                $result = $this->GalleriesRequest->find('all', array(
234                        'conditions' => array('GalleriesRequest.gallery_id' => $id),
235                        'recursive' => -1,
236                ));
237                $request_ids = Set::extract('/GalleriesRequest/request_id', $result);
238
239                if (empty($result)) {
240                        return array();
241                }
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                // Find out if this test suite needs to be abbreviated
295                $requestCount = $this->requestCount($id, true);
296                $isTestsuite = $this->isTestsuite($id);
297                $abbreviate = ($requestCount >= Configure::read('Gallery.abbreviate') && !$isTestsuite);
298
299                // Get the children of this gallery
300                $children = $this->find('all', array(
301                        'conditions' => array(
302                                'Gallery.lft >' => $gallery['Gallery']['lft'],
303                                'Gallery.rght <' => $gallery['Gallery']['rght'],
304                        ),
305                        'contain' => array('Request' => array('order' => 'Request.filename ASC')),
306                        'order' => array('Gallery.name ASC'),
307                ));
308
309                foreach ($children as &$child) {
310                        foreach ($child['Request'] as &$request) {
311
312                                $request['Validator'] = $this->Request->Validator->find('all', array(
313                                        'fields'     => array('id', 'parent_id', 'name', 'state'),
314                                        'conditions' => array('Validator.parent_id' => $request['id']),
315                                        'order'      => 'Validator.name ASC',
316                                        'recursive'  => -1,
317                                ));
318
319                                if (!$abbreviate) {
320                                        $request['Job'] = $this->Request->Job->query("SELECT
321                                                        `Job`.`id`,
322                                                        `Job`.`request_id`,
323                                                        `Job`.`platform_id`,
324                                                        `Job`.`application_id`,
325                                                        `Job`.`version`,
326                                                        `Job`.`format_id`,
327                                                        `Job`.`result_id`,
328                                                        `Job`.`locked`,
329                                                        `Platform`.`id`,
330                                                        `Platform`.`name`,
331                                                        `Application`.`id`,
332                                                        `Application`.`name`,
333                                                        `Result`.`id`,
334                                                        `Result`.`state`,
335                                                        `Result`.`state_info`,
336                                                        `Result`.`verified`,
337                                                        `Result`.`created`,
338                                                        `Format`.`id`,
339                                                        `Format`.`name`,
340                                                        `Format`.`icon`,
341                                                        `Format`.`code`
342                                                FROM `jobs` AS `Job`
343                                                        LEFT JOIN `platforms` AS `Platform` ON (`Job`.`platform_id` = `Platform`.`id`)
344                                                        LEFT JOIN `applications` AS `Application` ON (`Job`.`application_id` = `Application`.`id`)
345                                                        LEFT JOIN `results` AS `Result` ON (`Job`.`result_id` = `Result`.`id`)
346                                                        LEFT JOIN `formats` AS `Format` ON (`Job`.`format_id` = `Format`.`id`)
347                                                WHERE `Job`.`request_id` = '" . $request['id'] . "'
348                                                ORDER BY
349                                                        `Application`.`name` ASC,
350                                                        `Job`.`version` ASC,
351                                                        `Platform`.`name` ASC,
352                                                        `Format`.`name` ASC");
353
354                                        // Fix array layout to match a regular query layout
355                                        foreach ($request['Job'] as &$job) {
356                                                foreach ($job['Job'] as $attr => $value) {
357                                                        $job[$attr] = $value;
358                                                }
359                                                unset($job['Job']);
360
361                                                if (!isset($job['Result']['id']) || !$job['Result']['id']) {
362                                                        continue;
363                                                }
364
365                                                $job['Result']['Validator'] = $this->Request->Validator->find('all', array(
366                                                        'fields'     => array('id', 'parent_id', 'name', 'state'),
367                                                        'conditions' => array('Validator.parent_id' => $job['Result']['id']),
368                                                        'order'      => 'Validator.name ASC',
369                                                        'recursive'  => -1,
370                                                ));
371                                        }
372                                        unset($job);
373                                }
374                        }
375                }
376
377                // If this is a Testsuite then we need some extra information
378                $applications = array();
379                if ($isTestsuite) {
380                        $versions = array();
381                        $jobs = Set::extract('/Request/Job', $gallery);
382                        $jobs = array_merge(Set::extract('/Request/Job', $children), $jobs);
383
384                        foreach ($jobs as $job) {
385                                if (empty($job['Job'])) { // When a testsuite request has no jobs yet
386                                        continue;
387                                }
388
389                                if ($job['Job']['Format']['code'] != 'odf') {
390                                        continue;
391                                }
392
393                                $application = $job['Job']['application_id'];
394                                if (!isset($versions[$application])) {
395                                        $versions[$application] = $job['Job']['version'];
396                                        continue;
397                                }
398
399                                if ($job['Job']['version'] > $versions[$application]) {
400                                        $versions[$application] = $job['Job']['version'];
401                                }
402                        }
403                        unset($application); // Because we're referencing below
404
405                        $applications = $this->Request->Job->Application->find('all', array(
406                                'conditions' => array('Application.id' => array_keys($versions)),
407                                'order' => array('Application.name'),
408                                'recursive' => -1,
409                        ));
410
411                        foreach ($applications as &$application) {
412                                $application['Application']['version'] = $versions[$application['Application']['id']];
413                        }
414                        unset($application); // Drop the reference
415                }
416
417                // Sort the subgalleries for display
418                $gallery['children'] = $this->_sortThreaded($children);
419
420                // Get the path to this gallery
421                $path = $this->getpath($gallery['Gallery']['id']);
422                array_pop($path);
423
424                $this->log(sprintf('Gallery %s: generated viewData in %f seconds', $id, microtime(true) - $prof_start), LOG_DEBUG);
425                return compact('gallery', 'applications', 'path', 'abbreviate', 'isTestsuite');
426        }
427
428        /**
429         * Sort an array of Galleries threaded, like find('threaded') would do
430         */
431        private function _sortThreaded($results)
432        {
433                $return = $idMap = array();
434                $ids = Set::extract($results, '{n}.Gallery.id');
435
436                foreach ($results as $result) {
437                        $result['children'] = array();
438                        $id = $result['Gallery']['id'];
439                        $parentId = $result['Gallery']['parent_id'];
440                        if (isset($idMap[$id]['children'])) {
441                                $idMap[$id] = array_merge($result, (array)$idMap[$id]);
442                        } else {
443                                $idMap[$id] = array_merge($result, array('children' => array()));
444                        }
445                        if (!$parentId || !in_array($parentId, $ids)) {
446                                $return[] =& $idMap[$id];
447                        } else {
448                                $idMap[$parentId]['children'][] =& $idMap[$id];
449                        }
450                }
451                if (count($return) > 1) {
452                        $ids = array_unique(Set::extract('/Gallery/parent_id', $return));
453                        if (count($ids) > 1) {
454                                $root = $return[0]['Gallery']['parent_id'];
455                                foreach ($return as $key => $value) {
456                                        if ($value['Gallery']['parent_id'] != $root) {
457                                                unset($return[$key]);
458                                        }
459                                }
460                        }
461                }
462                return $return;
463        }
464
465        /**
466         * Convert the Markdown description to HTML before saving
467         * @return boolean True to continue saving
468         */
469        public function beforeSave()
470        {
471                if (isset($this->data['Gallery']['description'])) {
472                        // Convert the markdown description to HTML
473                        App::import('Vendor', 'markdown');
474                        App::import('Vendor', 'HTMLPurifier', array('file' => 'htmlpurifier/HTMLPurifier.standalone.php'));
475
476                        $config = HTMLPurifier_Config::createDefault();
477                        $config->set('Cache.SerializerPath', CACHE . DS . 'htmlpurifier');
478                        $purifier = new HTMLPurifier($config);
479
480                        $desc_html = Markdown($this->data['Gallery']['description']);
481                        $this->data['Gallery']['description_html'] = $purifier->purify($desc_html);
482
483                        // Save the markdown version to file
484                        $suite = $this->Testsuite->find('first', array(
485                                'conditions' => array('Testsuite.gallery_id' => $this->id),
486                                'recursive' => -1,
487                        ));
488
489                        if ($suite) {
490                                $path = FILES . $suite['Testsuite']['root'] . DS . 'description.txt';
491                                file_put_contents($path, $this->data['Gallery']['description']);
492                        }
493                }
494
495                return true;
496        }
497}
498
499?>
Note: See TracBrowser for help on using the repository browser.