Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
NavigationTree.php
1 <?php
2 /**
3  * Gentics Portal.Node PHP
4  * Author & Copyright (c) by Gentics Software GmbH
5  * sales@gentics.com
6  * http://www.gentics.com
7  * Licenses can be found in the LICENSE.txt file in the root-folder of this installation
8  * You must not use this software without a valid license agreement.
9  *
10  * Class for working with Gentics Content Connector
11  */
12 class NavigationTree extends CComponent
13 {
14 
15  /**
16  * id of the root folder for current widget
17  * @var $startfolderId
18  */
19  public $startfolderId = 0;
20 
21  /**
22  * id of the root folder for current widget
23  * @var $activeElement
24  */
25  public $activeElement = 0;
26 
27  /**
28  * array that contains categories tree
29  * @var $data
30  */
31  public $data = array();
32 
33  /**
34  * array that contains Ids for active categories
35  * @var $activePath
36  */
37  public $activePath = array();
38 
39  /**
40  * @var $lang string containg current languagecode
41  */
42  public $lang;
43 
44  /**
45  * @var bool $usePersonalisation defines if personalisation should be performed
46  */
47  public $usePersonalisation = true;
48 
49  public function getTreeCacheTime()
50  {
51  return Yii::app()->getModule('navigation')->cacheTime;
52  }
53 
54  public function getLatestUpdateTimestampCacheTime()
55  {
56  return Yii::app()->getModule('navigation')->timestampCacheTime;
57  }
58 
59  /**
60  * @var array $additionalParams defines additional Parameters to request
61  */
62  public $additionalParams = array();
63 
64  /**
65  * @var string $sortby defines the field for sorting
66  */
67  public $sortby = "sortorder:asc";
68 
69  /**
70  * Construction that will load nodes tree for given startfolderId and activePath for given activeElement
71  *
72  * @param integer $startfolderId id of folder that must be treated as root
73  * @param int $activeElement id
74  *
75  * @return \NavigationTree
76  */
77  public function __construct($startfolderId = 0, $activeElement = 0, $additionalParams = array(), $sortby = "sortorder:asc")
78  {
79  if ($startfolderId != '' && (int)$startfolderId > 0) {
80  $this->lang = substr(Yii::app()->language, 0, 2);
81 
82  $this->startfolderId = $startfolderId;
83  $this->activeElement = $activeElement;
84  $this->additionalParams = $additionalParams;
85  $this->sortby = $sortby;
86  $this->_loadData();
87  $this->_loadActivePath();
88  }
89  }
90 
91  /**
92  * Filter tree by some schema
93  *
94  * @param integer $user_id Id of the user to filter tree for
95  * @param bool $usePersonalisation defines if personalisation of received content should be performed
96  *
97  * @return array
98  */
99  public function cleanUp($user_id, $usePersonalisation = true)
100  {
101  //get filter fields from settings
102  $filters = $this->getFiltersList();
103 
104  $this->usePersonalisation = $usePersonalisation;
105 
106  //load tree data into local array
107  $data = $this->data;
108  // apply filter here
109  if ($filters && $data) {
110  $data = $this->_unsetNodes($filters, $user_id, $data);
111  }
112 
113  return $data;
114  }
115 
116  /**
117  * Private method that will unset not allowed data
118  *
119  * @param array $filters contains keys that will be used as filters
120  * @param array $user_id contains user permissions
121  * @param array $data points to the array with nodes tree
122  *
123  * @return array|null
124  */
125  private function _unsetNodes($filters, $user_id, $data)
126  {
127  /* check permissions for current node */
128  $persAttributes = array();
129  foreach ($filters as $filter) {
130  if (!empty($data['attributes'][$filter])) {
131  $persAttributes = array_merge($persAttributes, $data['attributes'][$filter]);
132  }
133  }
134 
135  //check if page has allowed startpageurl and exlude the branch if it does not.
136  if ($data['contentid'] != $this->startfolderId) {
137  $excludeURLs = Yii::app()->getModule('navigation')->excludeURLs;
138 
139  /* if isset startpage url for current language - use it, otherwise check if isset default startpage url and use it. In other cases set startpageurl to empty string */
140  $startpageUrl = isset($data['attributes']['startpageurl_' . $this->lang]) ? $data['attributes']['startpageurl_' . $this->lang] : (isset($data['attributes']['startpageurl']) ? $data['attributes']['startpageurl'] : '');
141 
142  /* if startpageurl is one those that should be excluded or empty - exclude it */
143  if (empty($startpageUrl) || in_array($startpageUrl, $excludeURLs)) {
144  return null;
145  }
146  }
147 
148  // Call function that will check permissions
149  if ($this->usePersonalisation && !Yii::app()->getModule('personalisation')->rule->checkAccess($user_id, $persAttributes)) {
150  /* if user does not have permissions - unset whole node */
151  return null;
152  } else if (isset($data['children'])) {
153  /* check every child for permission */
154  foreach ($data['children'] as $kChild => $child) {
155  $data['children'][$kChild] = $this->_unsetNodes($filters, $user_id, $child);
156  if (is_null($data['children'][$kChild])) {
157  unset($data['children'][$kChild]);
158  }
159  }
160  if (count($data['children']) == 0) {
161  unset($data['children']);
162  }
163  }
164  return $data;
165  }
166 
167  /**
168  * Filter tree by some schema
169  *
170  * @return array|bool
171  */
172  public function getFiltersList()
173  {
174  if (Yii::app()->getModule('contentSource')->contentSource->usePersonalisation) {
175  $filters = Yii::app()->getModule('contentSource')->contentSource->personalisationFields;
176  return $filters;
177  }
178 
179  return false;
180  }
181 
182  /**
183  * Load required tree data from cache. If not exists request Gentics Content Connector API for a new tree.
184  *
185  * @return array
186  */
187  private function _loadData()
188  {
189  $sysLangs = Yii::app()->getModule('language')->languages;
190  if (empty($sysLangs[$this->lang])) {
191  $sysLangs[$this->lang] = $this->lang;
192  }
193 
194  $requestFields = array(
195  'updatetimestamp',
196  'node_id',
197  'sortorder',
198  'navhidden',
199  'name',
200  'startpageurl'
201  );
202 
203  foreach ($sysLangs as $lang) {
204  $requestFields[] = 'name_' . $lang;
205  $requestFields[] = 'startpageurl_' . $lang;
206  }
207 
208  $requestFields = array_merge($requestFields,$this->additionalParams);
209 
210  $cacheId = 'navigation_'.$this->startfolderId.'&langs='.serialize($sysLangs);
211  $updateTimestamp = $this->_getYoungestTimestamp($this->startfolderId, $cacheId);
212  $cacheId .= $updateTimestamp;
213  $data = array();
214  $filtersList = '';
215 
216  //Load data
217  if (($data = Yii::app()->cache->get($cacheId)) === false) {
218  if ($filters = $this->getFiltersList()) {
219  $filtersList = implode(',', $filters);
220  }
221 
222  $data = Yii::app()->repositoryApi->requestNavigation(
223  array(
224  'rootfilter' => 'object.contentid=="'.$this->startfolderId.'"',
225  'sorting' => $this->sortby,
226  'navigation' => 'true',
227  'childfilter' => 'object.obj_type==10002 AND object.navhidden!=1',
228  'type' => 'php',
229  ),
230  CMap::mergeArray($this->getFiltersList(), $requestFields)
231  );
232  $data = unserialize($data);
233 
234  Yii::app()->cache->set($cacheId, $data, $this->getTreeCacheTime());
235  }
236  $this->data = isset($data[$this->startfolderId]) ? $data[$this->startfolderId] : array();
237 
238  // removed it for smaller debug log
239  //Yii::trace(print_r($this->data, true), 'navigation tree');
240 
241  }
242 
243  private function _getYoungestTimestamp($objectId, $objectCacheId)
244  {
245  $cacheId = 'youngest_' . $objectCacheId;
246  if (($updateTimestamp = Yii::app()->cache->get($cacheId)) == false) {
247  $updateTimestamp = Yii::app()->repositoryApi->requestNavigation(
248  array(
249  "filter" => 'object.contentid=="' . $objectId . '"',
250  "type" => "youngest"
251  ),
252  array("updatetimestamp")
253  );
254  Yii::app()->cache->set($cacheId, $updateTimestamp, $this->getLatestUpdateTimestampCacheTime());
255  }
256 
257  return $updateTimestamp;
258  }
259 
260  /**
261  * Load active path for current startfolderId and activeElement. If not exists request Gentics Content Connector API.
262  *
263  * @return array
264  */
265  private function _loadActivePath()
266  {
267  $cacheId = md5('activePath_' . NavigationTree::getUpdatetimestampByContentId($this->startfolderId) . '_' . $this->startfolderId . '_' . $this->activeElement);
268 
269  //Load data
270  if (($data = Yii::app()->cache->get($cacheId)) === false) {
271  if (!empty($this->activeElement)) {
272  $data = Yii::app()->repositoryApi->requestActivePath(
273  array(
274  'root' => $this->startfolderId,
275  'filter' => 'object.contentid=="'.$this->activeElement.'"',
276  'type' => 'php',
277  )
278  );
279 
280  $data = unserialize($data);
281  unset($data['status']);
282  $data = array_keys($data);
283  } else {
284  $data = array(0);
285  }
286 
287  Yii::app()->cache->set($cacheId, $data, $this->getTreeCacheTime());
288  Yii::trace('active path for element ' . $this->activeElement . ' putted to cache', 'test');
289  } else {
290  Yii::trace('active path for element ' . $this->activeElement . ' taken from cache', 'test');
291  }
292 
293  $this->activePath = $data;
294  }
295 
296  /**
297  * returnes array with active path for given $startfolderId
298  *
299  * @param varchar $startfolderId contentId to check timestamp
300  *
301  * @return string|bool
302  */
304  {
305  $cacheId = 'navigation_' . $startfolderId;
306 
307  if (($data = Yii::app()->cache->get($cacheId)) === false) {
308  return false;
309  } else {
310  return $data[(string)$startfolderId]['attributes']['updatetimestamp'];
311  }
312  }
313 
314  /**
315  * Returnes branch with given id if exists
316  *
317  * @param array $node contains navigation tree
318  * @param string $contentId contains id of content to search
319  *
320  * @return array|bool searched node
321  */
322  public static function extractBranch($node = array(), $contentId = '')
323  {
324  if ($node['contentid'] == $contentId) {
325  return $node;
326  } elseif (!empty($node['children'])) {
327  foreach ($node['children'] as $childNode) {
328  if ($searchedNode = self::extractBranch($childNode, $contentId)) {
329  return $searchedNode;
330  }
331  }
332  }
333 
334  return false;
335  }
336 }