wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.Controller.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.Controller.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.ObjectFactory.php");
21 require_once(BASE."wcmf/lib/util/class.Message.php");
22 require_once(BASE."wcmf/lib/util/class.FormUtil.php");
23 require_once(BASE."wcmf/lib/util/class.FileUtil.php");
24 require_once(BASE."wcmf/lib/presentation/class.View.php");
25 require_once(BASE."wcmf/lib/presentation/format/class.Formatter.php");
26 require_once(BASE."wcmf/lib/presentation/class.WCMFInifileParser.php");
27 require_once(BASE."wcmf/lib/model/class.NodeUtil.php");
28 require_once(BASE."wcmf/lib/i18n/class.Localization.php");
29 require_once(BASE."wcmf/lib/security/class.RightsManager.php");
30 require_once(BASE."wcmf/lib/util/class.Obfuscator.php");
31 require_once(BASE."wcmf/lib/util/class.Log.php");
32 
33 /**
34  * @class Controller
35  * @ingroup Presentation
36  * @brief Controller is the base class of all controllers. If a Controller has a view
37  * it is expected to reside in the directory configured in section smarty.templateDir.
38  * Additional smarty directories ('templates_c', 'configs', 'cache') are expected in a
39  * subdirectory of the template directory named 'smarty'.
40  *
41  * Error Handling:
42  * - use WCMFException::throwEx or action='failure' for fatal errors (displays FailureController)
43  * - use field _errorMsg for non fatal errors (message will be appended to errorMsg-data
44  * which will be displayed in the next view)
45  *
46  * @param[in] language The language of the requested data, optional
47  * @param[out] sid The session id
48  * @param[out] controller The name of the controller
49  * @param[out] errorMsg Any message set with setErrorMsg or appendErrorMsg, optional
50  * @param[out] success True, if errorMsg is empty or does not exist, False else
51  *
52  * @author ingo herwig <ingo@wemove.com>
53  */
55 {
56  var $_request = null;
57  var $_response = null;
58 
59  var $_errorMsg = '';
60  var $_view = null;
61  var $_delegate = null;
62 
63  /**
64  * Constructor.
65  * @param delegate A ControllerDelegate instance, if one is defined in the configuration (optional, default null does not work in PHP4).
66  */
67  function Controller(&$delegate)
68  {
69  $this->_request = new Request(null, null, null, array());
70  $this->_response = new Response(null, null, null, array());
71  $this->_delegate = &$delegate;
72  }
73  /**
74  * Initialize the Controller with request/response data. Which data is required is defined by the Controller.
75  * The base class method just stores the parameters in a member variable. Specialized Controllers may overide
76  * this behaviour for further initialization.
77  * @attention It lies in its responsibility to fail or do some default action if some data is missing.
78  * @param request A reference to the Request sent to the Controller. The sender attribute of the Request is the
79  * last controller's name, the context is the current context and the action is the requested one.
80  * All data sent from the last controller are accessible using the Request::getValue method. The request is
81  * supposed to be read-only. It will not be used any more after beeing passed to the controller.
82  * @param response A reference to the Response that will be modified by the Controller. The initial values for
83  * context and action are the same as in the request parameter and are meant to be modified according to the
84  * performed action. The sender attribute of the response is set to the current controller. Initially there
85  * are no data stored in the response.
86  */
87  function initialize(&$request, &$response)
88  {
89  $this->_request = &$request;
90  $this->_response = &$response;
91 
92  // restore the error message of a previous call
93  $this->appendErrorMsg($request->getValue('errorMsg'));
94 
95  if ($this->_delegate !== null)
96  $this->_delegate->postInitialize($this);
97  }
98  /**
99  * Check if the data given by initialize() meet the requirements of the Controller.
100  * Subclasses will override this method to validate against their special requirements.
101  * @return True/False whether the data are ok or not.
102  * In case of False a detailed description is provided by getErrorMsg().
103  */
104  function validate()
105  {
106  if ($this->_delegate !== null)
107  return $this->_delegate->validate($this);
108  else
109  return true;
110  }
111  /**
112  * Check if the Controller has a view.
113  * Subclasses must implement this method.
114  * @return True/False whether the Controller has a view or not.
115  */
116  function hasView()
117  {
118  // @todo make decision based on response format and remove this from
119  // subclasses if the default behaviour fits
120  WCMFException::throwEx("hasView() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
121  }
122  /**
123  * Execute the Controller resulting in its Action processed and/or its View beeing displayed.
124  * @return True/False wether following Controllers should be executed or not.
125  */
126  function execute()
127  {
128  if (Log::isDebugEnabled(__CLASS__))
129  {
130  Log::debug('Executing: '.get_class($this), __CLASS__);
131  Log::debug('Request: '.$this->_request->toString(), __CLASS__);
132  }
133 
134  // validate controller data
135  if (!$this->validate())
136  WCMFException::throwEx(Message::get("Validation failed for the following reason: %1%", array($this->_errorMsg)), __FILE__, __LINE__);
137 
138  if ($this->_delegate !== null)
139  $this->_delegate->preExecute($this);
140 
141  // set default values on response
142  $session = SessionData::getInstance();
143  $this->_response->setValue('sid', $session->getID());
144  $this->_response->setValue('controller', get_class($this));
145 
146  // execute controller logic
147  $result = $this->executeKernel();
148 
149  // append current error message to errorMsg-data
150  if (strlen($this->getErrorMsg()) > 0)
151  $this->_response->appendValue('errorMsg', $this->getErrorMsg());
152 
153  // create the view if existing
154  // @todo move response format condition into hasView
155  if ($this->hasView() && $result === false && $this->_response->getFormat() == MSG_FORMAT_HTML)
156  {
157  // check if a view template is defined
158  $viewTpl = $this->getViewTemplate($this->_response->getSender(), $this->_request->getContext(), $this->_request->getAction());
159  if (!$viewTpl) {
160  WCMFException::throwEx("View definition missing for ".get_class($this).". Action key: ".$actionKey, __FILE__, __LINE__);
161  }
162  $objectFactory = &ObjectFactory::getInstance();
163  $this->_view = &$objectFactory->createInstanceFromConfig('implementation', 'View');
164  if ($this->_view === null) {
165  WCMFException::throwEx($objectFactory->getErrorMsg(), __FILE__, __LINE__);
166  }
167  else
168  {
169  $this->_view->setup();
170  $this->_response->setView($this->_view);
171  $this->assignViewDefaults($this->_view);
172  }
173  }
174 
175  if ($this->_delegate !== null)
176  $result = $this->_delegate->postExecute($this, $result);
177 
178  // add success flag
179  if (strlen($this->getErrorMsg()) > 0)
180  $this->_response->setValue('success', false);
181  else
182  $this->_response->setValue('success', true);
183 
184  Formatter::serialize($this->_response);
185 
186  // display the view if existing
187  // @todo move response format condition into hasView
188  if ($this->hasView() && $result === false && $this->_response->getFormat() == MSG_FORMAT_HTML)
189  {
190  $viewTpl = realpath(BASE.$this->getViewTemplate($this->_response->getSender(), $this->_request->getContext(), $this->_request->getAction()));
191  if ($this->_view->caching && ($cacheId = $this->getCacheId()) !== null)
192  {
193  $this->_view->display($viewTpl, $cacheId);
194  }
195  else
196  $this->_view->display($viewTpl);
197  }
198 
199  if (Log::isDebugEnabled(__CLASS__))
200  {
201  Log::debug('Response: '.$this->_response->toString(), __CLASS__);
202  }
203  return $result;
204  }
205  /**
206  * Do the work in execute(): Load and process model and maybe asign data to view.
207  * Subclasses process their Action and assign the Model to the view.
208  * @return False or an an assoziative array with keys 'context' and 'action' describing how to proceed.
209  * Return false to break the action processing chain.
210  */
211  function executeKernel()
212  {
213  WCMFException::throwEx("executeKernel() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
214  }
215  /**
216  * Get a detailed description of the last error.
217  * @return The error message.
218  */
219  function getErrorMsg()
220  {
221  return $this->_errorMsg;
222  }
223  /**
224  * Set a detailed description of the last error.
225  * @param msg The error message.
226  */
227  function setErrorMsg($msg)
228  {
229  $this->_errorMsg = $msg;
230  }
231  /**
232  * Append a detailed description of the last error to the existing errors.
233  * @param msg The error message.
234  */
235  function appendErrorMsg($msg)
236  {
237  if (preg_match("/".$msg."$/", $this->_errorMsg))
238  return;
239 
240  if (strlen($this->_errorMsg) > 0)
241  $this->_errorMsg .= "\n";
242  $this->_errorMsg .= $msg;
243  }
244  /**
245  * Get the Request object.
246  * @return A reference to the Request object
247  */
248  function &getRequest()
249  {
250  return $this->_request;
251  }
252  /**
253  * Get the Response object.
254  * @return A reference to the Response object
255  */
256  function &getResponse()
257  {
258  return $this->_response;
259  }
260  /**
261  * Get the controller view.
262  * @return A reference to the controller view / or null if none is existing
263  */
264  function &getView()
265  {
266  return $this->_view;
267  }
268  /**
269  * Get the controller delegate.
270  * @return A reference to the controller view / or null if none is existing
271  */
272  function &getDelegate()
273  {
274  return $this->_delegate;
275  }
276  /**
277  * Get the template filename for the view from the configfile.
278  * @note static method
279  * @param controller The name of the controller
280  * @param context The name of the context
281  * @param action The name of the action
282  * @return The filename of the template or false, if now view is defined
283  */
284  function getViewTemplate($controller, $context, $action)
285  {
286  $view = '';
287  $parser = &WCMFInifileParser::getInstance();
288  $actionKey = $parser->getBestActionKey('views', $controller, $context, $action);
289  if (Log::isDebugEnabled(__CLASS__))
290  Log::debug('Controller::getViewTemplate: '.$controller."?".$context."?".$action.' -> '.$actionKey, __CLASS__);
291 
292  // get corresponding view
293  $view = $parser->getValue($actionKey, 'views', false);
294  return $view;
295  }
296  /**
297  * Get the id which should be used when caching the controllers view.
298  * This method will only be called, if the configuration entry smarty.caching is set to 1.
299  * The default implementation returns null. Subclasses should return an id that is unique
300  * to each different content of the same view.
301  * @return The id or null, if no cache id should be used.
302  */
303  function getCacheId()
304  {
305  return null;
306  }
307  /**
308  * Assign default variables to the view. This method is called after Controller execution.
309  * This method may be used by derived controller classes for convenient View setup.
310  * @param view A reference to the View to assign the variables to
311  * @attention Internal use only.
312  */
313  function assignViewDefaults(&$view)
314  {
315  $parser = &InifileParser::getInstance();
316  $rightsManager = &RightsManager::getInstance();
317  $authUser = &$rightsManager->getAuthUser();
318 
319  // assign current controller and context to smarty
320  $view->assign('_controller', $this->_response->getSender());
321  $view->assign('_context', $this->_response->getContext());
322  $view->assign('_action', $this->_response->getAction());
323  $view->assign('_responseFormat', $this->_response->getFormat());
324  $view->assign('messageObj', new Message());
325  $view->assign('formUtil', new FormUtil());
326  $view->assign('nodeUtil', new NodeUtil());
327  $view->assign('obfuscator', Obfuscator::getInstance());
328  $view->assign('applicationTitle', $parser->getValue('applicationTitle', 'cms'));
329  if ($authUser != null)
330  $view->assign_by_ref('authUser', $authUser);
331 
332  if ($this->_delegate !== null)
333  $this->_delegate->assignAdditionalViewValues($this);
334  }
335  /**
336  * Check if the current request is localized. This is true,
337  * if it has a language parameter that is not equal to Localization::getDefaultLanguage().
338  * @return True/False wether the request is localized or not
339  */
341  {
342  $localization = &Localization::getInstance();
343  if ($this->_request->hasValue('language') &&
344  $this->_request->getValue('language') != $localization->getDefaultLanguage()) {
345  return true;
346  }
347  return false;
348  }
349 }
350 ?>
initialize(&$request, &$response)
FormUtil provides basic support for HTML forms. It's mainly for creating input controls from definiti...
debug($message, $category)
Definition: class.Log.php:39
get($message, $parameters=null, $domain='', $lang='')
Controller(&$delegate)
NodeUtil provides services for the Node class. All methods are static.
Request holds the request values that are used as input to Controller instances. It is typically inst...
throwEx($message, $file='', $line='')
Controller is the base class of all controllers. If a Controller has a view it is expected to reside ...
assignViewDefaults(&$view)
isDebugEnabled($category)
Definition: class.Log.php:89
Use the Message class to output messages. You need not instantiate a Message object because the metho...
serialize(&$response)
getViewTemplate($controller, $context, $action)
Response holds the response values that are used as output from Controller instances. It is typically instantiated by the ActionMapper instance and filled during Controller execution.
const MSG_FORMAT_HTML