wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.AssociateController.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.AssociateController.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/presentation/class.Controller.php");
20 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacade.php");
21 require_once(BASE."wcmf/lib/model/class.Node.php");
22 require_once(BASE."wcmf/lib/model/class.NodeUtil.php");
23 require_once(BASE."wcmf/lib/model/class.NullNode.php");
24 
25 /**
26  * @class AssociateController
27  * @ingroup Controller
28  * @brief AssociateController is a controller that (dis-)associates Nodes
29  * (by setting the parent/child relations).
30  *
31  * <b>Input actions:</b>
32  * - @em associate Associate one Node to another
33  * - @em disassociate Disassociate one Node from another
34  *
35  * <b>Output actions:</b>
36  * - @em ok In any case
37  *
38  * @param[in] oid The object id of the Node to associate a Node as child to
39  * @param[in] associateoids The object ids of the Nodes to (dis-)associate as parents/children (comma separated list)
40  * @param[in] associateAs The role of the associated Nodes as seen from oid: Either 'parent' or 'child'
41  * @param[out] manyToMany The created many to many Node if one was created
42  *
43  * @author ingo herwig <ingo@wemove.com>
44  */
46 {
47  /**
48  * @see Controller::validate()
49  */
50  function validate()
51  {
52  if(!PersistenceFacade::isValidOID($this->_request->getValue('oid')))
53  {
54  $this->setErrorMsg("No valid 'oid' given in data.");
55  return false;
56  }
57  return true;
58  }
59  /**
60  * @see Controller::hasView()
61  */
62  function hasView()
63  {
64  return false;
65  }
66  /**
67  * (Dis-)Associate the Nodes.
68  * @return Array of given context and action 'ok' in every case.
69  * @see Controller::executeKernel()
70  */
71  function executeKernel()
72  {
73  $persistenceFacade = &PersistenceFacade::getInstance();
74  $lockManager = &LockManager::getInstance();
75 
76  // get parent node
77  $parentOID = $this->_request->getValue('oid');
78  $lockManager->releaseLock($parentOID);
79  $parentNode = &$persistenceFacade->load($parentOID, BUILDDEPTH_SINGLE);
80 
81  // iterate over associateoids
82  $associateoids = $this->_request->getValue('associateoids');
83  $associateoidArray = split(',', $associateoids);
84  foreach ($associateoidArray as $associateoid)
85  {
86  $associateoid = trim($associateoid);
87  if(!PersistenceFacade::isValidOID($associateoid))
88  {
89  $this->setErrorMsg("Invalid oid given in data.");
90  $this->_response->setAction('ok');
91  return true;
92  }
93 
94  // if the current user has a lock on the object, release it
95  $lockManager->releaseLock($associateoid);
96  $childNode = &$persistenceFacade->load($associateoid, BUILDDEPTH_SINGLE);
97 
98  if ($parentNode != null && $childNode != null)
99  {
100  // create templates of parent and child
101  $parentType = PersistenceFacade::getOIDParameter($parentOID, 'type');
102  $parentTemplate = &$persistenceFacade->create($parentType, 1);
103  $childType = PersistenceFacade::getOIDParameter($associateoid, 'type');
104  $childTemplate = &$persistenceFacade->create($childType, 1);
105 
106  // process actions
107  if ($this->_request->getAction() == 'associate')
108  {
109  // check if we can directly associate child to parent
110  if ($this->_request->getValue('associateAs') == 'child' && $this->isDirectAssociation($parentTemplate, $childTemplate))
111  {
112  $parentNode->addChild($childNode);
113  $parentNode->setType($parentNode->getBaseType());
114  $childNode->save();
115  }
116  // check if we can directly associate parent to child
117  else if ($this->_request->getValue('associateAs') == 'parent' && $this->isDirectAssociation($childTemplate, $parentTemplate))
118  {
119  $childNode->addChild($parentNode);
120  $parentNode->save();
121  }
122  else
123  {
124  // if the parent is connected via an association object, we have to create one
125  $linkType = $this->findAssociationType($parentTemplate, $childTemplate);
126  if ($linkType != null)
127  {
128  $link = &$persistenceFacade->create($linkType, BUILDTYPE_SINGLE);
129  $parentNode->addChild($link);
130  $link->save();
131  $link = &$persistenceFacade->load($link->getOID(), BUILDTYPE_SINGLE);
132  $childNode->addChild($link);
133  $link->save();
134  $this->_response->setValue("manyToMany", $link);
135  }
136  else
137  {
138  $this->appendErrorMsg(Message::get("Cannot associate %1% and %2%. No direct connection and no connection type found.",
139  array($associateoid, $parentOID)));
140  }
141  }
142  }
143  elseif ($this->_request->getAction() == 'disassociate')
144  {
145  // check if it is a direct association from child to parent
146  if ($this->isDirectAssociation($parentTemplate, $childTemplate))
147  {
148  // use a NullNode to empty foreign key
149  $parentNode = new NullNode($parentTemplate->getBaseType());
150  $parentNode->addChild($childNode);
151  $childNode->save();
152  }
153  // check if it is a direct association from parent to child
154  else if ($this->isDirectAssociation($childTemplate, $parentTemplate))
155  {
156  // use a NullNode to empty foreign key
157  $childNode = new NullNode($childTemplate->getType());
158  $childNode->addChild($parentNode);
159  $parentNode->save();
160  }
161  else
162  {
163  // if the parent is connected via an association object, we have to delete that
164  $linkType = $this->findAssociationType($parentTemplate, $childTemplate);
165  if ($linkType != null)
166  {
167  // find association object as child of both (parent and child)
168  // since the same nm object can have different roles (= different oids),
169  // a simple array_intersect of the children oids does not work here
170  $parentNode->loadChildren();
171  $childNode->loadChildren();
172  $parentChildren = $parentNode->getChildren();
173  $childChildren = $childNode->getChildren();
174  for($i=0, $countI=sizeof($parentChildren); $i<$countI; $i++)
175  {
176  for($j=0, $countJ=sizeof($childChildren); $j<$countJ; $j++)
177  {
178  $objA = &$parentChildren[$i];
179  $objB = &$childChildren[$j];
180  if (($objA->getType() == $linkType || $objB->getType() == $linkType) && ($objA->getBaseOID() == $objB->getBaseOID()))
181  {
182  $objA->delete();
183  }
184  }
185  }
186  }
187  else
188  {
189  $this->appendErrorMsg(Message::get("Cannot disassociate %1% and %2%. No direct connection and no connection type found.",
190  array($associateoid, $parentOID)));
191  }
192  }
193  }
194  }
195  else
196  {
197  if ($parentNode == null)
198  $this->appendErrorMsg(Message::get("Cannot %1% %2% and %3%. Parent does not exist.", array($this->_request->getAction(), $associateoid, $parentOID)));
199  if ($childNode == null)
200  $this->appendErrorMsg(Message::get("Cannot %1% %2% and %3%. Child does not exist.", array($this->_request->getAction(), $associateoid, $parentOID)));
201  }
202  }
203  $this->_response->setAction('ok');
204  return true;
205  }
206 
207  /**
208  * Check if two Nodes are directly assiociated (in a direct parent-child relation)
209  * @param parent A template of the parent object (with children attached)
210  * @param child The child to check
211  * @return True/False
212  */
213  function isDirectAssociation(&$parent, &$child)
214  {
215  $childTypeChildren = $parent->getChildrenEx(null, $child->getType(), null, null);
216  if (sizeof($childTypeChildren) > 0) {
217  // the relation is navigable from parent to child
218  return true;
219  }
220  else {
221  // maybe the relation is only navigable from child to parent
222  $parents = NodeUtil::getPossibleParents($child, $child);
223  foreach ($parents as $tmpParent) {
224  if ($parent->getType() == $tmpParent->getType()) {
225  return true;
226  }
227  }
228  }
229  return false;
230  }
231 
232  /**
233  * Search for an child type of parent that establishes the association between a given
234  * parent and child or vice versa.
235  * @param parent A template of the parent object (with children attached)
236  * @param child The child to check
237  * @return The type
238  */
239  function findAssociationType(&$parent, &$child)
240  {
241  foreach ($parent->getChildren() as $possibleChild)
242  {
243  if (in_array('manyToMany', $possibleChild->getPropertyNames()))
244  {
245  $associationEnds = $possibleChild->getProperty('manyToMany');
246  if (in_array($child->getType(), $associationEnds))
247  return $possibleChild->getType();
248  }
249  }
250  foreach ($child->getChildren() as $possibleChild)
251  {
252  if (in_array('manyToMany', $possibleChild->getPropertyNames()))
253  {
254  $associationEnds = $possibleChild->getProperty('manyToMany');
255  if (in_array($parent->getType(), $associationEnds))
256  return $possibleChild->getType();
257  }
258  }
259  return null;
260  }
261 }
262 ?>
263 
get($message, $parameters=null, $domain='', $lang='')
addChild(&$child, $addtype=ADDCHILD_BACK)
Definition: class.Node.php:166
Controller is the base class of all controllers. If a Controller has a view it is expected to reside ...
NullNode is an implementation of the NullObject pattern, It inherits all functionality from Node (act...
getOIDParameter($oid, $param, $validate=true)
getPossibleParents(&$realNode, &$tplNode)
const BUILDDEPTH_SINGLE
isDirectAssociation(&$parent, &$child)
AssociateController is a controller that (dis-)associates Nodes (by setting the parent/child relation...
findAssociationType(&$parent, &$child)