root/trunk/src/includes/editam/models/page.php

Revision 43, 16.2 kB (checked in by bermi, 3 years ago)

Added settings for currently enabled languages. Closes #7, #9, #5 and #2

Line 
1 <?php
2
3 // +----------------------------------------------------------------------+
4 // Editam is a content management platform developed by Akelos Media, S.L.|
5 // Copyright (C) 2006 - 2007 Akelos Media, S.L.                           |
6 //                                                                        |
7 // This program is free software; you can redistribute it and/or modify   |
8 // it under the terms of the GNU General Public License version 3 as      |
9 // published by the Free Software Foundation.                             |
10 //                                                                        |
11 // This program is distributed in the hope that it will be useful, but    |
12 // WITHOUT ANY WARRANTY; without even the implied warranty of             |
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   |
14 // See the GNU General Public License for more details.                   |
15 //                                                                        |
16 // You should have received a copy of the GNU General Public License      |
17 // along with this program; if not, see http://www.gnu.org/licenses or    |
18 // write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth |
19 // Floor, Boston, MA 02110-1301 USA.                                      |
20 //                                                                        |
21 // You can contact Akelos Media, S.L. headquarters at                     |
22 // C/ Pasodoble Amparito Roca, 6, 46240 - Carlet (Valencia) - Spain       |
23 // or at email address contact@akelos.com.                                |
24 //                                                                        |
25 // The interactive user interfaces in modified source and object code     |
26 // versions of this program must display Appropriate Legal Notices, as    |
27 // required under Section 5 of the GNU General Public License version 3.  |
28 //                                                                        |
29 // In accordance with Section 7(b) of the GNU General Public License      |
30 // version 3, these Appropriate Legal Notices must retain the display of  |
31 // the "Powered by Editam" logo. If the display of the logo is not        |
32 // reasonably feasible for technical reasons, the Appropriate Legal       |
33 // Notices must display the words "Powered by Editam".                    |
34 // +----------------------------------------------------------------------+
35
36 require(AK_APP_DIR.DS.'behaviours'.DS.'base_behaviour.php');
37
38 Ak::import('PagePart,ContentLayout,User');
39
40 class Page extends ActiveRecord
41 {
42     var $_Behaviour;
43
44     var $has_many = array(
45     'parts'=>array(
46     'dependent' => 'destroy',
47     'class_name' => 'PagePart',
48     'handler_name' => 'part'
49     ));
50
51     var $belongs_to = array(
52     'layout' => array('class_name' => 'ContentLayout', 'primary_key_name' => 'layout_id'),
53     'author' => array('class_name' => 'User', 'foreign_key' => 'created_by'),
54     'editor' => array('class_name' => 'User', 'foreign_key' => 'updated_by')
55     );
56
57     var $acts_as = array('nested_set' => array('order' => 'virtual DESC, title ASC','scope' => "locale = ?"));
58
59     function getLocale()
60     {
61         $default_lang = Ak::lang();
62         $lang = !isset($this->_original_locale) || empty($this->locale) ? $default_lang : $this->locale;
63         $locale = in_array($lang, Ak::langs()) ? $lang : $default_lang;
64         $this->_original_locale = empty($this->_original_locale) ? $locale : $this->_original_locale;
65         return $locale;
66     }
67     
68     function forceSetLocale($locale)
69     {
70         $this->_forced_locale = $locale;
71     }
72
73     function isHomepage()
74     {
75         return !$this->isNewRecord() && $this->nested_set->isRoot();
76     }
77
78     function saveWithParts($parts = array(), $position = null)
79     {
80         $this->transactionStart();
81         foreach ($parts as $part){
82             $this->part->create($part);
83         }
84         //$this->set('parent_id', null);
85         $success = $this->save() && !$this->transactionHasFailed();
86
87         if($success && !$this->isHomepage()){
88             $this->nested_set->moveToChildOf($position);
89         }
90
91         if(!$success){
92             $this->transactionFail();
93         }
94         $this->transactionComplete();
95         return $success;
96     }
97
98     function updateWithParts($parts = array(), $position = null)
99     {
100         $this->part->load();
101         //$this->layout->load();
102         $this->transactionStart();
103
104         foreach (array_keys($this->parts) as $k){
105             if(!isset($parts[$k+1])){
106                 $this->parts[$k]->destroy();
107             }
108         }
109         $success = true;
110         foreach ($parts as $k=>$part){
111             if(!$success){
112                 break;
113             }
114             if(empty($this->parts[$k-1])){
115                 if(!$this->part->create($part)){
116                     $success = false;
117                     $this->addErrorToBase($this->t('Unexpected errors on page part'));
118                 }
119             }else{
120                 $this->parts[$k-1]->setAttributes($part);
121             }
122         }
123         $success = $this->save() && !$this->transactionHasFailed();
124
125         if(!empty($position) && $success && $this->parent_id != $position){
126             $this->nested_set->moveToChildOf($position);
127         }
128
129         if(!$success){
130             $this->transactionFail();
131         }
132         $this->transactionComplete();
133         return $success;
134     }
135
136
137     function beforeSave()
138     {
139         $this->loadBehaviour();
140         $this->_updatePublishedAt();
141         $this->_updateVirtual();
142         return true;
143     }
144     
145     function beforeSaveOnCreate()
146     {
147         $this->_updateLocale();
148         return true;
149     }
150
151
152     function validate()
153     {
154         $this->validatesPresenceOf(array('title','slug','breadcrumb','status','created_by'));
155
156         $this->_validateSlug();
157
158         $this->validatesInclusionOf('behaviour',array_keys($this->getAvailableBehaviours()), 'inclusion', true);
159         $this->validatesInclusionOf('status',array_keys($this->getAvailableStatuses()));
160
161         $this->validatesInclusionOf('locale', Ak::langs());
162         
163         if(isset($this->_original_locale) && $this->_original_locale != $this->get('locale')){
164             $this->addError('locale', $this->t('Page locale can\'t be changed once it has been set.'));
165         }
166     }
167
168     function _validateSlug($parent_id = null)
169     {
170         $original_parent_id = $this->parent_id;
171         $this->parent_id = !empty($parent_id) ? $parent_id : $this->parent_id;
172
173         if(!$this->getErrorsOn('slug')){
174             $this->validatesFormatOf('slug','/^([\w\d]+|[\w\d](?!.*(\-|\.){2,}.*)[\w\d\-_\.]*[\w\d]|\/)$/');
175             if(!empty($this->parent_id)){
176                 $this->validatesUniquenessOf('slug', array('scope' => array('locale','parent_id'), 'message' => $this->t('Slug is already used in a page that shares the parent with this one')));
177             }
178         }
179         $this->parent_id = $original_parent_id;
180     }
181
182     function validateOnCreate()
183     {
184         $this->validatesAssociated('parts');
185     }
186
187     function isPublished()
188     {
189         return !empty($this->status) && $this->status == 'published';
190     }
191
192     function &getPart($name)
193     {
194         if(empty($this->parts)){
195             $this->part->load();
196         }
197         $IndexedPart =& $this->_getPartIndexedAs($name);
198         return $IndexedPart;
199     }
200
201     function &getInheritedPart($name)
202     {
203         if(!$Part =& $this->getPart($name)){
204             if(!$ParentPages =& $this->getParentPages()){
205                 $false = false;
206                 return $false;
207             }
208             foreach (array_reverse(array_keys($ParentPages)) as $k){
209                 if($Part =& $ParentPages[$k]->getPart($name)){
210                     $this->__indexed_parts[$name] =& $Part;
211                     break;
212                 }
213             }
214         }
215
216         return $Part;
217     }
218
219     function &getParentPages()
220     {
221         if(!isset($this->ParentPages)){
222             $this->ParentPages =& $this->nested_set->getAncestors();
223         }
224         return $this->ParentPages;
225     }
226
227
228     function getFilteredPart($name, $use_inherited_if_unavailable = false)
229     {
230         $method = $use_inherited_if_unavailable ? 'getInheritedPart' : 'getPart';
231         $Part =& $this->$method($name);
232         return is_object($Part) ? EditamFilter::getFilteredContent($Part) : false;
233     }
234
235     function &_findInheritedPart($name)
236     {
237         $Part =& $this->part->find('first', array('conditions'=>array('name = ?', $name)));
238     }
239
240     function &_getPartIndexedAs($name)
241     {
242         empty($this->__indexed_parts[$name]) ? $this->_loadIndexedParts() : null;
243         if(!isset($this->__indexed_parts[$name])){
244             $false = false;
245             return $false;
246         }
247         return $this->__indexed_parts[$name];
248     }
249
250     function _loadIndexedParts()
251     {
252         foreach (array_keys($this->parts) as $k){
253             $this->__indexed_parts[$this->parts[$k]->get('name')] =& $this->parts[$k];
254         }
255     }
256
257
258     function &getLayoutInstance()
259     {
260         if($this->layout->getType() != 'ContentLayout'){
261             $this->layout->load(true);
262             if($this->layout->getType() != 'ContentLayout'){
263                 if($this->nested_set->isRoot()){
264                     $false = false;
265                     return $false;
266                 }
267                 $Parent =& $this->nested_set->getParent();
268                 $Layout =& $Parent->getLayoutInstance();
269                 return $Layout;
270             }
271         }
272         return $this->layout;
273     }
274
275     function getLayout()
276     {
277         $Layout =& $this->getLayoutInstance();
278         return isset($Layout->content) ? $Layout->get('content') : false;
279     }
280
281     function getLayoutName()
282     {
283         if(empty($this->layout->name)){
284             $this->layout->load();
285         }
286         if(empty($this->layout->name) && $parent =& $this->nested_set->getParent()){
287             return $parent->getLayoutName();
288         }else{
289             return false;
290         }
291         return $this->layout->name;
292     }
293
294     function &findByUrl($url, $show_unpublished = false)
295     {
296         $Page =& $this->_Behaviour->findPageByUrl($url, $show_unpublished);
297         return $Page;
298     }
299
300     function &_findByUrl($url_parts, $show_unpublished = false, $missing_page_mode = false)
301     {
302         if($result =& $this->findBySql($this->_getSqlForUrlFinder($url_parts, $show_unpublished, $missing_page_mode), 1)){
303             return $result[0];
304         }
305         $false = false;
306         return $false; // Trick for avoiding pass by reference notices on PHP4
307     }
308
309     function _getSqlForUrlFinder($url_parts, $show_unpublished = false, $missing_page_mode = false)
310     {
311         $url_parts = is_array($url_parts) ? $url_parts : explode('/',$url_parts.'/');
312         $url_parts = array_diff($url_parts,array(''));
313         if(empty($url_parts[0]) || $url_parts[0] != '/'){
314             array_unshift($url_parts, '/');
315         }
316         $total_url_parts = count($url_parts);
317         $url_parts = array_reverse($url_parts);
318         $sql = '';
319         $locale = Ak::lang();
320         $status_condition = $show_unpublished ? '' : " AND status = 'published'";
321         foreach ($url_parts as $position => $url_part){
322             $position = $position +1;
323             $condition = $missing_page_mode && $position ==  1 ? ' behaviour = "page_missing" ' :
324             'slug = '.$this->castAttributeForDatabase('slug',$url_part);
325             $condition .= ' AND locale = '.$this->castAttributeForDatabase('locale',$locale).' ';
326             if($position == 1){
327                 $sql = 'SELECT * FROM pages WHERE '.$condition.
328                 ($show_unpublished?'':' AND status = '.$this->castAttributeForDatabase('status','published'));
329             }else{
330                 $sql .= 'SELECT id FROM pages WHERE '.$condition.$status_condition;
331                 $sql .= $total_url_parts == $position ? str_repeat(')',$total_url_parts-1) : '';
332             }
333             if($position < $total_url_parts){
334                 $sql .= ' AND parent_id IN (';
335             }
336         }
337         return $sql;
338     }
339
340     function &findMissingPageForUrl($url)
341     {
342         $Page =& $this->_Behaviour->findMissingPageForUrl($url);
343         return $Page;
344     }
345
346     /**
347      * Callbacks
348      */
349     function _updatePublishedAt()
350     {
351         if(empty($this->published_at) && $this->status == 'published'){
352             $this->published_at = Ak::getDate();
353         }
354     }
355     function _updateVirtual()
356     {
357         $this->set('is_virtual', $this->isVirtual());
358     }
359
360     function _updateLocale()
361     {
362         empty($this->locale) ? $this->set('locale',Ak::lang()) : null;
363     }
364
365     /**
366      * Page behaviours
367      */
368
369     function loadBehaviour($behaviour = null)
370     {
371         $behaviour = empty($behaviour) ? $this->behaviour : $behaviour;
372         $this->behaviour = !empty($behaviour) && in_array($behaviour, array_keys($this->getAvailableBehaviours())) ? $behaviour : '';
373
374         $this->_Behaviour =& $this->getBehaviourInstance();
375     }
376
377     function initiateBehaviour(&$controller)
378     {
379         $this->loadBehaviour($this->get('behaviour'));
380         $this->_Behaviour->init($controller);
381     }
382
383     function &getBehaviourInstance()
384     {
385         $behaviour_class = empty($this->behaviour) ? 'BaseBehaviour' : AkInflector::camelize($this->behaviour).'Behaviour';
386         require_once(AK_APP_DIR.DS.'behaviours'.DS.AkInflector::underscore($behaviour_class).'.php');
387         $Instance =& new $behaviour_class();
388         return $Instance;
389     }
390
391     function getUrl()
392     {
393         return $this->_Behaviour->getPageUrl();
394     }
395
396     function getInheritedSlug($force = false, $include_locale = EDITAM_IS_MULTILINGUAL)
397     {
398         if($force || empty($this->_inherited_slug)){
399             if(!empty($this->nested_set) && !empty($this->lft) && !empty($this->rgt)){
400                 $this->_inherited_slug = str_replace('//','/',
401                 ($include_locale?'/'.$this->locale:'').'/'.trim(join('/',array_keys($this->collect($this->nested_set->getSelfAndAncestors(),'slug','title'))),'/').'/');
402             }
403         }
404         return $this->_inherited_slug;
405     }
406
407     function hasCache()
408     {
409         return $this->_Behaviour->hasPageCache();
410     }
411
412     function canUsePageCache()
413     {
414         return $this->_Behaviour->canUsePageCache();
415     }
416
417     function render()
418     {
419         return $this->_Behaviour->renderPage();
420     }
421
422     function isVirtual()
423     {
424         return $this->_Behaviour->isPageVirtual();
425     }
426
427     function process()
428     {
429         return $this->_Behaviour->process();
430     }
431
432     function getChildUrl()
433     {
434         return $this->_Behaviour->getChildUrl();
435     }
436
437     /**
438      * Page elements discovery functions
439      */
440     function getAvailableBehaviours()
441     {
442         static $behaviours = array();
443         if(empty($behaviours)){
444             if(defined('EDITAM_AVAILABLE_BEHAVIOURS')){
445                 foreach (Ak::toArray(EDITAM_AVAILABLE_BEHAVIOURS) as $behaviour){
446                     $behaviours[$behaviour] = $this->t($behaviour);
447                 }
448             }else{
449                 foreach (Ak::dir(AK_APP_DIR.DS.'behaviours') as $file){
450                     if(substr($file,-14) == '_behaviour.php'){
451                         $behaviour = substr($file,0,-14);
452                         if($behaviour != 'base'){
453                             $behaviours[$behaviour] = $this->t($behaviour);
454                         }
455                     }
456                 }
457             }
458             $behaviours = array_map(array('AkInflector','humanize'), $behaviours);
459         }
460         return $behaviours;
461     }
462
463     function getAvailableStatuses()
464     {
465         return array(
466         'published' => $this->t('Published'),
467         'draft' => $this->t('Draft'),
468         'reviewed' => $this->t('Reviewed'),
469         'hidden' => $this->t('Hidden')
470         );
471     }
472
473     function moveBeside(&$Page, $position = 'left')
474     {
475         $method = $position == 'left' ? 'moveToRightOf' : 'moveToLeftOf';
476         $this->_validateSlug($Page->parent_id);
477         if(!$this->hasErrors()){
478             $this->nested_set->$method($Page);
479             return true;
480         }
481         return false;
482     }
483
484
485     function clearCachedPages()
486     {
487         $Cache =& Ak::cache();
488         $Cache->init(EDITAM_CACHE_LIFE);
489         $Cache->clean(AK_HOST);
490
491     }
492
493     function clearOldPagesFromCache()
494     {
495         $Cache =& Ak::cache();
496         $Cache->init(EDITAM_CACHE_LIFE);
497         $Cache->clean(AK_HOST, 'old');
498     }
499 }
500
501
502
503 ?>
504
Note: See TracBrowser for help on using the browser.