Example #1
0
 /**
  * Run the web application for the current request.
  *
  * NOTE: Both a module and page must be specified in the URL. If they are not BOTH specified, the server will REDIRECT the request to the full URL.
  *       Therefore, you should be sure that when posting form data to a module/page, you use a full path. {@link WFRequestController::WFURL}
  *
  * Will pass control onto the current module for processing.
  *
  * Create a WFModuleInvocation based on the current HTTP Request, get the results, and output the completed web page.
  *
  * @todo Handle 404 situation better -- need to be able to detect this nicely from WFModuleInvocation.. maybe an Exception subclass?
  * @todo PATH_INFO with multiple params, where one is blank, isn't working correctly. IE, /url/a//c gets turned into /url/a/c for PATH_INFO thus we skip a "null" param.
  *       NOTE: Partial solution; use /WFNull/ to indicate NULL param instead of // until we figure something out.
  *       NOTE: Recent change to REQUEST_URI instead of PATH_INFO to solve decoding problem seems to have also solved the // => / conversion problem... test more!
  *       WORRY: That the new PATH_INFO calculation will fail when using aliases other than WWW_ROOT. IE: /products/myProduct might break it...
  * @todo The set_error_handler doesn't seem to work very well. PHP issue? Or am I doing it wrong? For instance, it doesn't catch $obj->nonExistantMethod().
  */
 function handleHTTPRequest()
 {
     // point all error handling to phocoa's internal mechanisms since anything that happens after this line (try) will be routed through the framework's handler
     $this->registerErrorHandlers();
     try {
         $relativeURI = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
         // need to run this to convert absolute URI's to relative ones (sent by SOME http clients)
         if ($relativeURI === false) {
             throw new WFRequestController_NotFoundException("Malformed URI: {$_SERVER['REQUEST_URI']}");
         }
         $modInvocationPath = ltrim(substr($relativeURI, strlen(WWW_ROOT)), '/');
         $paramsPos = strpos($modInvocationPath, '?');
         if ($paramsPos !== false) {
             $modInvocationPath = substr($modInvocationPath, 0, $paramsPos);
         }
         if ($modInvocationPath == '') {
             $modInvocationPath = WFWebApplication::sharedWebApplication()->defaultInvocationPath();
         }
         // allow routing delegate to munge modInvocationPath
         $webAppDelegate = WFWebApplication::sharedWebApplication()->delegate();
         if (is_object($webAppDelegate) && method_exists($webAppDelegate, 'rerouteInvocationPath')) {
             $newInvocationPath = $webAppDelegate->rerouteInvocationPath($modInvocationPath);
             if ($newInvocationPath) {
                 $modInvocationPath = $newInvocationPath;
             }
         }
         // create the root invocation; only skin if we're not in an XHR
         $this->rootModuleInvocation = new WFModuleInvocation($modInvocationPath, NULL, $this->isAjax() ? NULL : WFWebApplication::sharedWebApplication()->defaultSkinDelegate());
         // get HTML result of the module, and output it
         $html = $this->rootModuleInvocation->execute();
         // respond to WFRPC::PARAM_ENABLE_AJAX_IFRAME_RESPONSE_MODE for iframe-targeted XHR. Some XHR requests (ie uploads) must be done by creating an iframe and targeting the form
         // post to the iframe rather than using XHR (since XHR doesn't support uploads methinks). This WFRPC flag makes these such "ajax" requests need to be wrapped slightly differently
         // to prevent the HTML returned in the IFRAME from executing in the IFRAME which would cause errors.
         if (isset($_REQUEST[WFRPC::PARAM_ENABLE_AJAX_IFRAME_RESPONSE_MODE]) && $_REQUEST[WFRPC::PARAM_ENABLE_AJAX_IFRAME_RESPONSE_MODE] == 1) {
             header('Content-Type: text/xml');
             $html = "<" . "?xml version=\"1.0\"?" . "><raw><![CDATA[\n{$html}\n]]></raw>";
         }
         print $html;
     } catch (WFPaginatorException $e) {
         // paginator fails by default should "route" to bad request. This keeps bots from going crazy.
         header("HTTP/1.0 400 Bad Request");
         print "Bad Request: " . $e->getMessage();
     } catch (WFRequestController_RedirectException $e) {
         header("HTTP/1.1 {$e->getCode()}");
         header("Location: {$e->getRedirectURL()}");
     } catch (WFRequestController_HTTPException $e) {
         header("HTTP/1.0 {$e->getCode()}");
         print $e->getMessage();
     } catch (WFRequestController_BadRequestException $e) {
         header("HTTP/1.0 400 Bad Request");
         print "Bad Request: " . $e->getMessage();
     } catch (WFRequestController_NotFoundException $e) {
         header("HTTP/1.0 404 Not Found");
         print $e->getMessage();
     } catch (WFRequestController_InternalRedirectException $e) {
         // internal redirect are handled without going back to the browser... a little bit of hacking here to process a new invocationPath as a "request"
         // @todo - not sure what consequences this has on $_REQUEST; seems that they'd probably stay intact which could foul things up?
         $_SERVER['REQUEST_URI'] = $e->getRedirectURL();
         WFLog::log("Internal redirect to: {$_SERVER['REQUEST_URI']}");
         self::handleHTTPRequest();
     } catch (WFRedirectRequestException $e) {
         header("Location: " . $e->getRedirectURL());
     } catch (Exception $e) {
         $this->handleException($e);
     }
 }
Example #2
0
 /**
  * Load the shared.config file for the module and process it.
  *
  * The shared.config file is an OPTIONAL component.
  * If your module has no instances, or the instances don't need configuration, you don't need a shared.config file.
  *
  * The shared.config file can only configure properties of objects at this time.
  * Only primitive value types may be used. String, boolean, integer, double, NULL. NO arrays or objects allowed.
  *
  * <code>
  *   $__config = array(
  *       'instanceID' => array(
  *           'properties' => array(
  *              'propName' => 'Property Value',
  *              'propName2' => 123
  *           )
  *       ),
  *       'instanceID2' => array(
  *           'properties' => array(
  *              'propName' => 'Property Value',
  *              'propName2' => true
  *           )
  *       )
  *   );
  * </code>
  *
  * @param string The absolute path to the config file.
  * @throws Various errors if configs are encountered for for non-existant instances, etc. A properly config'd page should never throw.
  * @see loadConfigYAML
  */
 protected function loadConfigPHP($configFile)
 {
     // be graceful; if there is no config file, no biggie, just don't load config!
     if (!file_exists($configFile)) {
         return;
     }
     include $configFile;
     foreach ($__config as $id => $config) {
         WFLog::log("loading config for id '{$id}'", WFLog::TRACE_LOG);
         // get the instance to apply config to
         if (!isset($this->{$id})) {
             throw new WFException("Couldn't find shared instance with ID '{$id}' to configure.");
         }
         $configObject = $this->{$id};
         // atrributes
         if (isset($config['properties'])) {
             foreach ($config['properties'] as $keyPath => $value) {
                 switch (gettype($value)) {
                     case "boolean":
                     case "integer":
                     case "double":
                     case "string":
                     case "NULL":
                         // these are all OK, fall through
                         break;
                     default:
                         throw new WFException("Config value for shared instance id::property '{$id}::{$keyPath}' is not a vaild type (" . gettype($value) . "). Only boolean, integer, double, string, or NULL allowed.");
                         break;
                 }
                 WFLog::log("SharedConfig:: Setting '{$id}' property, {$keyPath} => {$value}", WFLog::TRACE_LOG);
                 $configObject->setValueForKeyPath($value, $keyPath);
             }
         }
     }
 }
Example #3
0
 function willNotRunAction()
 {
     WFLog::log("Running willNotRunAction...", WFLog::TRACE_LOG);
     if ($this->delegate && method_exists($this->delegate, 'willNotRunAction')) {
         $this->delegate->willNotRunAction($this, $this->parameters());
     }
 }
Example #4
0
 function processBindingOptions($boundProperty, $options, &$boundValue)
 {
     parent::processBindingOptions($boundProperty, $options, $boundValue);
     switch ($boundProperty) {
         case 'contentValues':
             if ($options[WFBindingSetup::WFBINDINGSETUP_INSERTS_NULL_PLACEHOLDER]) {
                 $defaultValue = $options[WFBindingSetup::WFBINDINGSETUP_NULL_PLACEHOLDER];
                 $boundValue = array_merge(array($defaultValue), $boundValue);
             }
             break;
         case 'contentLabels':
             if ($options[WFBindingSetup::WFBINDINGSETUP_INSERTS_NULL_PLACEHOLDER]) {
                 $defaultLabel = $options[WFBindingSetup::WFBINDINGSETUP_NULL_PLACEHOLDER];
                 $boundValue = array_merge(array($defaultLabel), $boundValue);
             }
             break;
         case 'options':
             if ($options[WFBindingSetup::WFBINDINGSETUP_INSERTS_NULL_PLACEHOLDER]) {
                 $defaultLabel = $options[WFBindingSetup::WFBINDINGSETUP_NULL_PLACEHOLDER];
                 WFLog::log("BEFORE: " . var_export($boundValue, true));
                 $boundValue = array('' => $defaultLabel) + $boundValue;
                 WFLog::log("AFTER: " . var_export($boundValue, true));
             }
             break;
     }
 }
Example #5
0
 /**
  * Execute the RPC. This function is called by the PHOCOA infrastructure to "execute" the RPC request in the PHOCOA infrastructure.
  *
  * If the target/action method returns a {@link WFActionResponse} object, the response will be sent and execution will stop.
  * If the target/action method returns NULL, execution will fall through to the caller.
  *
  * @param object WFPage The current page.
  * @return void
  */
 function execute($page)
 {
     $bcMode = false;
     // setup backwards-compatibility for really old-school
     if (!$page->delegate() and strncmp($this->target, WFRPC::TARGET_PAGE, strlen(WFRPC::TARGET_PAGE)) == 0) {
         $bcMode = true;
         $this->action = $page->pageName() . '_' . $this->action . '_Action';
         $this->target = '#module#';
     }
     // calculate target
     $matches = array();
     if (!preg_match('/^(#page#|#module#)([^\\.]+)?(.*)/', $this->target, $matches)) {
         throw new WFException("Couldn't parse target: {$this->target}.");
     }
     list($targetStr, $pageOrModule, $outletId, $keyPath) = $matches;
     $targetObj = NULL;
     switch ($pageOrModule) {
         case WFRPC::TARGET_PAGE:
             $targetObj = $page;
             break;
         case WFRPC::TARGET_MODULE:
             $targetObj = $page->module();
             break;
         default:
             throw new WFException("Couldn't parse target: {$this->target}.");
     }
     if ($outletId) {
         $targetObj = $targetObj->outlet($outletId);
     } else {
         if ($targetObj instanceof WFPage && $this->action !== 'noAction') {
             $targetObj = $targetObj->delegate();
         }
     }
     if ($keyPath) {
         $targetObj = $targetObj->valueForKeyPath($keyPath);
     }
     $rpcCall = array($targetObj, $this->action);
     if (!is_callable($rpcCall)) {
         if ($bcMode) {
             // old-school
             throw new WFException("Backwards-compatibility mode WFRPC: action is not callable: " . $this->action);
         } else {
             // new school
             throw new WFException("WFRPC Invocation is not callable: " . $this->target . "->" . $this->action . "(). Please ensure that there is a method of that name on the specified object.");
         }
     }
     WFLog::log("Running target/action: '{$this->target}/{$this->action}'", WFLog::TRACE_LOG);
     $result = call_user_func_array($rpcCall, array_merge(array($page, $page->parameters()), $this->args));
     if ($result !== NULL) {
         if (!$this->isAjax()) {
             throw new WFException("Functions shouldn't return WFActionResponse objects if not in AJAX mode.");
         }
         // if the action method returned a WFActionResponse
         if (gettype($result) == 'string') {
             $result = new WFActionResponsePlain($result);
         }
         if (!$result instanceof WFActionResponse) {
             throw new WFException("Unexpected WFActionResponse.");
         }
         $result->send();
     }
 }
Example #6
0
 function addChild(WFView $view)
 {
     parent::addChild($view);
     if ($view instanceof WFSubmit) {
         $this->numberOfSubmitButtons++;
         // if if the FIRST one, save it; otherwise,
         if ($this->calculatedDefaultSubmitID === WFForm::CALCULATED_DEFAULT_SUBMIT_ID_NONE) {
             $this->calculatedDefaultSubmitID = $view->id();
         } else {
             if (!$this->defaultSubmitID) {
                 $this->calculatedDefaultSubmitID = WFForm::CALCULATED_DEFAULT_SUBMIT_ID_CANNOT_DETERMINE;
                 WFLog::log("Form id: '{$this->id}' is unable to determine the default button for the form. You should set one via defaultSubmitID to avoid errors in some browsers.", WFLog::WARN_LOG);
             }
         }
     }
 }
Example #7
0
 /**
  * Create the dynamic widgets.
  *
  * Allows for creation of n widgets based on an WFArrayController. Various options allow you to set up nearly any type
  * of widget that is repeated for all objects in an array. Great for managing selection, editing data from joins, etc.
  *
  * Once the objects are instantiated, configured, and bound as appropriate, restoreState() will be called. You can be sure that
  * when this function exits that your widgets are in the same state as statically instantiated widgets from the page.
  * 
  * This will be called AFTER the _PageDidLoad method... which is what we need to wait for before creating our widgets. WFPage makes this call.
  *
  * Module code may need to call this function again, particularly if the content of they arrayController is changed by the current action.
  *
  * @return assoc_array An array of 'widgetID' => widget for all newly created widgets.
  * @todo Anything else wrong with calling more than once? This should be idempotent as well as re-callable with different data.
  */
 function createWidgets()
 {
     // check params
     if (!is_object($this->arrayController)) {
         throw new Exception("No WFArrayController assigned to WFDynamic. Set the arrayController object for WFDynamic '{$this->id}'.");
     }
     if (!$this->arrayController instanceof WFArrayController) {
         throw new Exception("arrayController must be a WFArrayController instance.");
     }
     // have we already done createWidgets for this version of the arrayController?
     if ($this->arrayController->changeCount() == $this->createdWidgetsChangeCount) {
         WFLog::log("Not rebuilding widgets because changeCount has not changed... " . $this->arrayController->changeCount() . ' ' . $this->id(), WFLog::TRACE_LOG);
         return;
     } else {
         WFLog::log("rebuilding widgets because changeCount changed... " . $this->arrayController->changeCount() . ' ' . $this->id(), WFLog::TRACE_LOG);
     }
     // update change count
     $this->createdWidgetsChangeCount = $this->arrayController->changeCount();
     // remove existing widgets from page
     foreach ($this->createdWidgets as $existingWidget) {
         WFLog::log("Removing dynamically created widget: " . $existingWidget->id(), WFLog::WARN_LOG);
         $this->page->removeInstance($existingWidget->id());
         if ($this->parent) {
             $this->parent->removeChild($existingWidget);
         }
     }
     $arrayController = $this->arrayController;
     $widgetClass = $this->widgetClass;
     $widgetBaseName = $this->id;
     $useUniqueNames = $this->useUniqueNames;
     $parentView = $this->calculateParent();
     $this->createdWidgets = array();
     // check params
     if (!class_exists($widgetClass)) {
         throw new Exception("There is no widget class '{$widgetClass}'.");
     }
     $this->processWidgetConfig();
     $currentIndex = 0;
     foreach ($arrayController->arrangedObjects() as $object) {
         // instantiate the widget with a unique ID
         if ($arrayController->usingIndexedMode()) {
             $id = $widgetBaseName . '_' . $currentIndex;
         } else {
             $id = $widgetBaseName . '_' . $arrayController->identifierHashForObject($object);
         }
         // instantiate widget
         if (!is_null($this->prototype)) {
             $widget = $this->prototype->cloneWithID($id);
             // copy onEvent script to the clone
             $widget->setOnEvent($this->prototype->getOnEvent());
         } else {
             $widget = new $widgetClass($id, $this->page());
         }
         // add to form if needed
         if ($parentView) {
             $parentView->addChild($widget);
         }
         // add to our list
         $this->createdWidgets[] = $widget;
         // set up the name
         if (!$useUniqueNames) {
             $widget->setName($widgetBaseName);
         }
         WFLog::log("WFDynamic:: created {$widgetClass} id={$id} name=" . $widget->name());
         // set up properties
         if ($this->formatter) {
             $widget->setFormatter($this->formatter);
         }
         foreach ($this->widgetConfig as $propName => $propInfo) {
             // set up custom value and/or binding
             if (!isset($propInfo['custom']) and !isset($propInfo['bind'])) {
                 throw new Exception("You must supply either 'custom' or 'bind' information. (propName: {$propName})");
             }
             $customValue = NULL;
             if (isset($propInfo['custom'])) {
                 $customSettings = $propInfo['custom'];
                 // make sure it has all expected pieces
                 if (!isset($customSettings['iterate'])) {
                     throw new Exception("An 'iterate' setting must be provided. (propName: {$propName})");
                 }
                 // use a different value for each iteration
                 if ($customSettings['iterate']) {
                     // use the keyPath of the current object
                     if (isset($customSettings['keyPath']) and $customSettings['keyPath'] !== false) {
                         if ($customSettings['keyPath'] == '#identifier#') {
                             if ($arrayController->usingIndexedMode()) {
                                 $customValue = $currentIndex;
                             } else {
                                 $customValue = $arrayController->identifierHashForObject($object);
                             }
                         } else {
                             $customValue = $object->valueForKeyPath($customSettings['keyPath']);
                         }
                     } else {
                         if (!is_array($customSettings['value'])) {
                             throw new Exception('If iterate and you supply a value, value must be an array. (propName: {$propName})');
                         }
                         $customValue = $customSettings['value'][$currentIndex];
                     }
                 } else {
                     if (!isset($customSettings['value'])) {
                         throw new Exception("If not iterate, a 'value' must be provided. (propName: {$propName})");
                     }
                     $customValue = $customSettings['value'];
                 }
                 WFLog::log("WFDynamic:: setting {$propName} to {$customValue} for {$id}", WFLog::TRACE_LOG);
                 $widget->setValueForKey($customValue, $propName);
             }
             // or are we using bindings
             if (isset($propInfo['bind'])) {
                 $bindingInfo = $propInfo['bind'];
                 WFLog::log("WFDynamic:: Binding property '{$propName}' to {$bindingInfo['instanceID']} => {$bindingInfo['controllerKey']}::{$bindingInfo['modelKeyPath']}", WFLog::TRACE_LOG);
                 // determine object to bind to:
                 if (!isset($bindingInfo['instanceID'])) {
                     throw new Exception("No instance id specified for binding property '{$propName}'.");
                 }
                 $bindToObject = NULL;
                 if ($bindingInfo['instanceID'] == '#module#') {
                     $bindToObject = $parentView->page()->module();
                 } else {
                     if ($bindingInfo['instanceID'] == '#custom#') {
                         if (!is_object($customValue)) {
                             throw new Exception("Could not determine custom bindToObject ({$bindingInfo['instanceID']}) for binding property '{$propName}'.");
                         }
                         $bindToObject = $customValue;
                     } else {
                         if ($bindingInfo['instanceID'] == '#current#') {
                             $bindToObject = $object;
                         } else {
                             $bindToObject = $parentView->page()->outlet($bindingInfo['instanceID']);
                         }
                     }
                 }
                 if (is_null($bindToObject)) {
                     throw new Exception("Could not determine bindToObject ({$bindingInfo['instanceID']}) for binding property '{$propName}'.");
                 }
                 $fullKeyPath = '';
                 if (isset($bindingInfo['controllerKey'])) {
                     $fullKeyPath .= $bindingInfo['controllerKey'];
                 }
                 if (isset($bindingInfo['modelKeyPath'])) {
                     if (!empty($fullKeyPath)) {
                         $fullKeyPath .= '.';
                     }
                     $fullKeyPath .= $bindingInfo['modelKeyPath'];
                 }
                 if (empty($fullKeyPath)) {
                     throw new Exception("No keyPath specified for binding property '{$propName}'");
                 }
                 // process options
                 $options = NULL;
                 if (isset($bindingInfo['options'])) {
                     $options = $bindingInfo['options'];
                 }
                 try {
                     $widget->bind($propName, $bindToObject, $fullKeyPath, $options);
                 } catch (Exception $e) {
                     throw $e;
                 }
             }
         }
         // now that we've loaded all widget options (config), call the callback so that the widget can set itself up.
         $widget->allConfigFinishedLoading();
         // OH BOY. This used to work the first way, and the 2nd way was commented out. But I ran into a bug (calling restoreState when not on proper form would restore incorrect state). No idea why it was the other way...
         // switching to the 2nd state probably broke something else :( BEWARE!
         // @todo RESOLVE this issue and leave correct code and remove incorrect code.
         if (0) {
             $this->restoreState();
         } else {
             // have widget restore state, only if we've posted! otherwise it will grab improper state
             if ($parentView and $parentView->page()->submittedFormName()) {
                 $widget->restoreState();
             }
         }
         $currentIndex++;
     }
     return $this->createdWidgets;
 }
Example #8
0
 /**
  * Set the value of the checkbox control. What this does basically, is that if the passed value is the same as the checkedValue, then
  * the checkbox will become checked. Otherwise, it will become unchecked.
  * @param mixed The value to set for the control.
  */
 function setValue($v)
 {
     if ($v == $this->checkedValue) {
         $this->setChecked(true);
     } else {
         if ($v == $this->uncheckedValue) {
             $this->setChecked(false);
         } else {
             WFLog::log('Warning!!! Checkbox ID ' . $this->id . " checked state not restored because passed value '{$v}' is not equal to checked ({$this->checkedValue}) or unchecked ({$this->uncheckedValue}) value.");
         }
     }
 }
Example #9
0
 public function deprecated($message)
 {
     // intersects with E_DEPRECATED? http://php.net/manual/en/errorfunc.constants.php
     WFLog::log($message, 'deprecated', PEAR_LOG_NOTICE);
 }
Example #10
0
 /**
  * Inform a widget of all errors for the given key.
  *
  * Optionally [and by default], prune the errors that have been propagated from the current list. Since the caller will typically re-throw this exception to be caught by the WFPage,
  * the auto-pruning prevents errors from appearing twice, as the WFPage will automatically detect and report all errors as well (although not linked to widgets).
  *
  * @param string The key which generated the errors
  * @param object WFWidget The widget that the errors should be reported to.
  * @param bolean Prune errors for this key from the exception object.
  */
 public function propagateErrorsForKeyToWidget($key, $widget, $prune = true)
 {
     WF_LOG_DEPRECATED && WFLog::deprecated("WFErrorsException::propagateErrorsForKeyToWidget() is deprecated. Use WFPage::propagateErrorsForKeysToWidgets() or WFPage::propagateErrorsForKeyToWidget().");
     foreach ($this->errorsForKey($key) as $keyErr) {
         $widget->addError($keyErr);
     }
     if ($prune && isset($this->errors[$key])) {
         unset($this->errors[$key]);
     }
 }
Example #11
0
 /**
  *  Stop tracking time; dump time to 'dieselsearch.log' file with given message.
  *
  *  @param string The message to log along with the elapsed time since {@link startTrackingTime()} was called.
  */
 function stopTrackingTime($msg)
 {
     $elapsed = microtime(true) - $this->logPerformanceInfo_t0;
     WFLog::logToFile('dieselsearch.log', "[{$elapsed}s] {$msg}");
 }
Example #12
0
 /**
  * Constructor.
  *
  * Sets up the smarty object for this module.
  */
 function __construct($id, $page)
 {
     parent::__construct();
     if (is_null($id)) {
         throw new Exception("id required for new " . get_class($this) . '.');
     }
     if (!$page instanceof WFPage) {
         throw new Exception("page must be a WFPage.");
     }
     // warn about invalid ID's
     switch ($id) {
         case 'new':
         case 'delete':
             WFLog::log("The id '{$id}' is dangerous to use because it is a reserved word in some browsers Javascript engines. For best compatibility, use a different ID.", WFLog::WARN_LOG);
             break;
     }
     $this->id = $id;
     $this->enabled = true;
     $this->children = array();
     $this->parent = NULL;
     $this->page = $page;
     $this->setId($id);
     $this->jsActions = array();
     $this->jsEvents = array();
     $this->originalOnEvent = NULL;
     // js/css import infrastructure
     $this->importInHead = false;
     $this->jsImports = array();
     $this->cssImports = array();
 }
Example #13
0
 /**
  * Propagate the value from the UI widget to the bound object.
  *
  * This method will automatically call the validator for the binding, if it exists.
  * If the value is valid, it will also push the value onto the bound object with setValueForKeyPath.
  *
  * Any validation errors are stored in the widget's error list.
  *
  * If the value of the widget is equivalent to an empty string ($value === '') then value is converted into PHP NULL.
  * Since all values in widgets come from the UI, and there is no distinction in the UI world b/w "" and NULL, we normalize all "" values to NULL.
  * It is left up to objects to then distinguish between NULL and "" (via normalization in Key-Value Validation).
  *
  * @param string The name of the binding to propagate back to the bound object.
  * @param mixed The value from the UI widget (submitted by the form).
  * @return mixed The cleaned-up value from validateValueForKeyPath()
  */
 function propagateValueToBinding($bindingName, $value)
 {
     // the bindable property may not be bound! The is legitimate, so be graceful about it.
     $binding = $this->bindingByName($bindingName);
     if (is_null($binding)) {
         return $value;
     }
     // check OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE
     if ($binding->hasCoalescedOption(WFBinding::OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE) and $binding->coalescedOption(WFBinding::OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE) === $value) {
         $func = WFFunction::create('return "propagateValueToBinding() skipping push for {$bindingName} since value matched OPTION_DO_NOT_PUSH_VALUE_SEMAPHORE for for widget id \'{$id}\'";')->withArguments('bindingName', 'id')->curry($bindingName, $this->id);
         WFLog::log($func, WFLog::TRACE_LOG);
         return $value;
     }
     // assert for r/o bindings.
     if ($binding->bindingSetup()->readOnly()) {
         throw new Exception("Attempt to propagateValueToBinding for a read-only binding: {$this->id} / {$bindingName}.");
     }
     if (!$binding->canWriteBoundValue()) {
         throw new Exception("Attempt to propagateValueToBinding for a binding with that doesn't allow writing.");
     }
     // normalize "" string values to NULL. Do this pre-validation; that function can do normalization etc.
     // we simply cover the case of TOTALLY EMPTY STRING is equivalent to NULL here.
     if ($value === '') {
         $value = NULL;
     }
     $edited = false;
     $func = WFFunction::create('return "propagateValueToBinding() validating value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName);
     WFLog::log($func, WFLog::TRACE_LOG);
     $errors = array();
     $valid = $binding->bindToObject()->validateValueForKeyPath($value, $binding->bindToKeyPath(), $edited, $errors);
     if ($valid) {
         $func = WFFunction::create('return "propagateValueToBinding() Pushing value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName);
         WFLog::log($func, WFLog::TRACE_LOG);
         $binding->bindToObject()->setValueForKeyPath($value, $binding->bindToKeyPath());
     } else {
         $func = WFFunction::create('return "propagateValueToBinding() WILL NOT (did not validate) push value \'$value\' for bound object \'" . $class . "\' for widget id \'{$id}\' binding: \'{$bindingName}\'";')->withArguments('value', 'class', 'id', 'bindingName')->curry($value, get_class($this), $this->id, $bindingName);
         WFLog::log($func, WFLog::TRACE_LOG);
         // keep all returned errors
         $this->addErrors($errors);
     }
     // return cleaned-up value so UI can update
     return $value;
 }
Example #14
0
 /**
  * Validate the given value for the given key.
  *
  * Clients can normalize the value, and also report and error if the value is not valid.
  *
  * If the value is valid without modificiation, return TRUE and do not alter $edited or $error.
  * If the value is valid after being modified, return TRUE, and $edited to true.
  * IF the value is not valid, do not alter $value or $edited, but fill out the $error object with desired information.
  *
  * The default implementation (in WFObject) looks for a method named validate<key> and calls it, otherwise it returns TRUE. Here is the prototype:
  * <code>
  *      (boolean) function(&$value, &$edited, &$errors)
  * </code>
  *
  * @param mixed A reference to value to check. Passed by reference so that the implementation can normalize the data.
  * @param string keyPath the keyPath for the value.
  * @param boolean A reference to a boolean. This value will always be FALSE when the method is called. If the implementation edits the $value, set to TRUE.
  * @param array An array of WFError objects describing the error. The array is empty by default; you can add new error entries with:
  *      <code>
  *          $error[] = new WFError('My error message'); // could also add an error code (string) parameter.
  *      </code>
  * @return boolean TRUE indicates a valid value, FALSE indicates an error.
  */
 function validateValueForKey(&$value, $key, &$edited, &$errors)
 {
     $valid = true;
     // try calling validator
     $validatorMethod = 'validate' . ucfirst($key);
     if (method_exists($this, $validatorMethod)) {
         // track whether or not validator lies
         $errCount = count($errors);
         // run validator
         $valid = $this->{$validatorMethod}($value, $edited, $errors);
         // check for mismatch b/w $valid and errors generated
         $errCount = count($errors) - $errCount;
         if ($valid && $errCount) {
             throw new WFException("Validator for key '{$key}' returned TRUE but also returned errors.");
         } else {
             if (!$valid && $errCount === 0) {
                 throw new WFException("Validator for key '{$key}' returned FALSE but didn't provide any errors.");
             }
         }
     }
     if (!$valid) {
         WFLog::log("Errors: " . print_r($errors, true), WFLog::TRACE_LOG);
     }
     return $valid;
 }
Example #15
0
 function restoreState()
 {
     parent::restoreState();
     WFLog::log("WFCheckboxGroup restoreState()<bR>");
     // restore state of all children
     foreach ($this->children() as $checkbox) {
         $checkbox->restoreState();
     }
     // rebuild our meta-value
     $this->updateGroupValues();
     WFLog::log("WFCheckboxGroup restoreState() done, value now:" . $this->values);
 }
 function handleUploadedFile()
 {
     if ($this->hasUpload()) {
         try {
             call_user_func($this->hasUploadCallback, $this->page(), $this->page()->parameters(), $this);
             // send back success
             print "UPLOAD OK";
         } catch (Exception $e) {
             // send back error+noretry
             print "UPLOAD ERROR: " . $e->getMessage();
             WFLog::log("UPLOAD ERROR: {$e->getMessage()}", WFLog::TRACE_LOG, PEAR_LOG_ERR);
         }
         exit;
     }
 }