Overview

Namespaces

  • DcGeneral
    • Clipboard
    • Contao
      • Callback
      • Compatibility
      • DataDefinition
        • Definition
      • Dca
        • Builder
          • Legacy
        • Definition
        • Palette
        • Populator
      • Event
      • View
        • Contao2BackendView
          • Event
    • Controller
    • Data
    • DataDefinition
      • Builder
      • Definition
        • Properties
        • View
          • Panel
      • ModelRelationship
      • Palette
        • Builder
          • Event
        • Condition
          • Palette
          • Property
    • EnvironmentPopulator
    • Event
    • Exception
    • Factory
      • Event
    • Panel
    • View
      • Event

Classes

  • BaseView
  • ContaoBackendViewTemplate
  • ContaoWidgetManager
  • ListView
  • ParentView
  • TreeView

Interfaces

  • BackendViewInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3:  * PHP version 5
  4:  * @package    generalDriver
  5:  * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
  6:  * @author     Stefan Heimes <stefan_heimes@hotmail.com>
  7:  * @author     Tristan Lins <tristan.lins@bit3.de>
  8:  * @copyright  The MetaModels team.
  9:  * @license    LGPL.
 10:  * @filesource
 11:  */
 12: 
 13: namespace DcGeneral\Contao\View\Contao2BackendView;
 14: 
 15: use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
 16: use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent;
 17: use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent;
 18: use DcGeneral\Contao\Compatibility\DcCompat;
 19: use DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent;
 20: use DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent;
 21: use DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent;
 22: use DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent;
 23: use DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent;
 24: use DcGeneral\Contao\View\Contao2BackendView\Event\ResolveWidgetErrorMessageEvent;
 25: use DcGeneral\Data\ModelInterface;
 26: use DcGeneral\Data\PropertyValueBag;
 27: use DcGeneral\DataDefinition\Definition\Properties\PropertyInterface;
 28: use DcGeneral\EnvironmentInterface;
 29: use DcGeneral\Exception\DcGeneralInvalidArgumentException;
 30: use DcGeneral\Exception\DcGeneralRuntimeException;
 31: 
 32: /**
 33:  * Class ContaoWidgetManager.
 34:  *
 35:  * This class is responsible for creating widgets and processing data through them.
 36:  *
 37:  * @package DcGeneral\Contao\View\Contao2BackendView
 38:  */
 39: class ContaoWidgetManager
 40: {
 41:     /**
 42:      * The environment in use.
 43:      *
 44:      * @var EnvironmentInterface
 45:      */
 46:     protected $environment;
 47: 
 48:     /**
 49:      * The model for which widgets shall be generated.
 50:      *
 51:      * @var ModelInterface
 52:      */
 53:     protected $model;
 54: 
 55:     /**
 56:      * Create a new instance.
 57:      *
 58:      * @param EnvironmentInterface $environment The environment in use.
 59:      *
 60:      * @param ModelInterface       $model       The model for which widgets shall be generated.
 61:      */
 62:     public function __construct(EnvironmentInterface $environment, ModelInterface $model)
 63:     {
 64:         $this->environment = $environment;
 65:         $this->model       = $model;
 66: 
 67:         $this->preLoadRichTextEditor();
 68:     }
 69: 
 70:     /**
 71:      * Encode a value from the widget to native data of the data provider via event.
 72:      *
 73:      * @param string $property The property.
 74:      *
 75:      * @param mixed  $value    The value of the property.
 76:      *
 77:      * @return mixed
 78:      */
 79:     public function encodeValue($property, $value)
 80:     {
 81:         $environment = $this->getEnvironment();
 82: 
 83:         $event = new EncodePropertyValueFromWidgetEvent($environment, $this->model);
 84:         $event
 85:             ->setProperty($property)
 86:             ->setValue($value);
 87: 
 88:         $environment->getEventPropagator()->propagate(
 89:             $event::NAME,
 90:             $event,
 91:             array(
 92:                 $environment->getDataDefinition()->getName(),
 93:                 $property
 94:             )
 95:         );
 96: 
 97:         return $event->getValue();
 98:     }
 99: 
100:     /**
101:      * Decode a value from native data of the data provider to the widget via event.
102:      *
103:      * @param string $property The property.
104:      *
105:      * @param mixed  $value    The value of the property.
106:      *
107:      * @return mixed
108:      */
109:     public function decodeValue($property, $value)
110:     {
111:         $environment = $this->getEnvironment();
112: 
113:         $event = new DecodePropertyValueForWidgetEvent($environment, $this->model);
114:         $event
115:             ->setProperty($property)
116:             ->setValue($value);
117: 
118:         $environment->getEventPropagator()->propagate(
119:             $event::NAME,
120:             $event,
121:             array(
122:                 $environment->getDataDefinition()->getName(),
123:                 $property
124:             )
125:         );
126: 
127:         return $event->getValue();
128:     }
129: 
130:     /**
131:      * {@inheritdoc}
132:      */
133:     public function getEnvironment()
134:     {
135:         return $this->environment;
136:     }
137: 
138:     /**
139:      * {@inheritDoc}
140:      */
141:     public function hasWidget($property)
142:     {
143:         try
144:         {
145:             return $this->getWidget($property) !== null;
146:         }
147:         catch(\Exception $e)
148:         {
149:             // Fall though and return false.
150:         }
151:         return false;
152:     }
153: 
154:     /**
155:      * Get special labels.
156:      *
157:      * @param PropertyInterface $propInfo The property for which the X label shall be generated.
158:      *
159:      * @return string
160:      */
161:     protected function getXLabel($propInfo)
162:     {
163:         $strXLabel   = '';
164:         $environment = $this->getEnvironment();
165:         $defName     = $environment->getDataDefinition()->getName();
166:         $translator  = $environment->getTranslator();
167: 
168:         // Toggle line wrap (textarea).
169:         if ($propInfo->getWidgetType() === 'textarea' && !array_key_exists('rte', $propInfo->getExtra()))
170:         {
171:             $event = new GenerateHtmlEvent(
172:                 'wrap.gif',
173:                 $translator->translate('wordWrap', 'MSC'),
174:                 sprintf(
175:                     'title="%s" class="toggleWrap" onclick="Backend.toggleWrap(\'ctrl_%s\');"',
176:                     specialchars($translator->translate('wordWrap', 'MSC')),
177:                     $propInfo->getName()
178:                 )
179:             );
180: 
181:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $event);
182: 
183:             $strXLabel .= ' ' . $event->getHtml();
184:         }
185: 
186:         // Add the help wizard.
187:         if ($propInfo->getExtra() && array_key_exists('helpwizard', $propInfo->getExtra()))
188:         {
189:             $event = new GenerateHtmlEvent(
190:                 'about.gif',
191:                 $translator->translate('helpWizard', 'MSC'),
192:                 'style="vertical-align:text-bottom;"'
193:             );
194: 
195:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $event);
196: 
197:             $strXLabel .= sprintf(
198:                 ' <a href="contao/help.php?table=%s&amp;field=%s"
199:                 title="%s"
200:                 onclick="Backend.openWindow(this, 600, 500); return false;">%s</a>',
201:                 $defName,
202:                 $propInfo->getName(),
203:                 specialchars($translator->translate('helpWizard', 'MSC')),
204:                 $event->getHtml()
205:             );
206:         }
207: 
208:         // Add the popup file manager.
209:         if ($propInfo->getWidgetType() === 'fileTree')
210:         {
211:             // In Contao 3 it is always a file picker - no need for the button.
212:             if (version_compare(VERSION, '3.0', '<'))
213:             {
214:                 $event = new GenerateHtmlEvent(
215:                     'filemanager.gif',
216:                     $translator->translate('fileManager', 'MSC'),
217:                     'style="vertical-align:text-bottom;"'
218:                 );
219: 
220:                 $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $event);
221: 
222:                 $strXLabel .= sprintf(
223:                     ' <a href="contao/files.php"
224:                     title="%s"
225:                     onclick="Backend.getScrollOffset(); Backend.openWindow(this, 750, 500); return false;">%s</a>',
226:                     specialchars($translator->translate('fileManager', 'MSC')),
227:                     $event->getHtml()
228:                 );
229:             }
230:         }
231:         // Add table import wizard.
232:         elseif ($propInfo->getWidgetType() === 'tableWizard')
233:         {
234:             $urlEvent = new AddToUrlEvent('key=table');
235: 
236:             $importTableEvent = new GenerateHtmlEvent(
237:                 'tablewizard.gif',
238:                 $translator->translate('importTable.0', $defName),
239:                 'style="vertical-align:text-bottom;"'
240:             );
241: 
242:             $shrinkEvent = new GenerateHtmlEvent(
243:                 'demagnify.gif',
244:                 $translator->translate('shrink.0', $defName),
245:                 sprintf(
246:                     'title="%s" style="vertical-align:text-bottom; cursor:pointer;" onclick="Backend.tableWizardResize(0.9);"',
247:                     specialchars($translator->translate('shrink.1', $defName))
248:                 )
249:             );
250: 
251:             $expandEvent = new GenerateHtmlEvent(
252:                 'magnify.gif',
253:                 $translator->translate('expand.0', $defName),
254:                 sprintf(
255:                     'title="%s" style="vertical-align:text-bottom; cursor:pointer;" onclick="Backend.tableWizardResize(1.1);"',
256:                     specialchars($translator->translate('expand.1', $defName))
257:                 )
258:             );
259: 
260:             $environment->getEventPropagator()->propagate(ContaoEvents::BACKEND_ADD_TO_URL, $urlEvent);
261: 
262:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $importTableEvent);
263:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $shrinkEvent);
264:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $expandEvent);
265: 
266:             $strXLabel .= sprintf(
267:                 ' <a href="%s" title="%s" onclick="Backend.getScrollOffset();">%s</a> %s%s',
268:                 ampersand($urlEvent->getUrl()),
269:                 specialchars($translator->translate('importTable.1', $defName)),
270:                 $importTableEvent->getHtml(),
271:                 $shrinkEvent->getHtml(),
272:                 $expandEvent->getHtml()
273:             );
274:         }
275:         // Add list import wizard.
276:         elseif ($propInfo->getWidgetType() === 'listWizard')
277:         {
278:             $urlEvent = new AddToUrlEvent('key=list');
279: 
280:             $importListEvent = new GenerateHtmlEvent(
281:                 'tablewizard.gif',
282:                 $translator->translate('importList.0', $defName),
283:                 'style="vertical-align:text-bottom;"'
284:             );
285: 
286:             $environment->getEventPropagator()->propagate(ContaoEvents::BACKEND_ADD_TO_URL, $urlEvent);
287:             $environment->getEventPropagator()->propagate(ContaoEvents::IMAGE_GET_HTML, $importListEvent);
288: 
289:             $strXLabel .= sprintf(
290:                 ' <a href="%s" title="%s" onclick="Backend.getScrollOffset();">%s</a>',
291:                 ampersand($urlEvent->getUrl()),
292:                 specialchars($translator->translate('importList.1', $defName)),
293:                 $importListEvent->getHtml()
294:             );
295:         }
296: 
297:         return $strXLabel;
298:     }
299: 
300:     /**
301:      * Function for pre-loading the tiny mce.
302:      *
303:      * @return void
304:      */
305:     public function preLoadRichTextEditor()
306:     {
307:         foreach ($this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperties() as $property)
308:         {
309:             /** @var PropertyInterface $property */
310:             $extra = $property->getExtra();
311: 
312:             if (!isset($extra['eval']['rte']))
313:             {
314:                 continue;
315:             }
316: 
317:             if (strncmp($extra['eval']['rte'], 'tiny', 4) !== 0)
318:             {
319:                 continue;
320:             }
321: 
322:             list($file, $type) = explode('|', $extra['eval']['rte']);
323: 
324:             $propertyId = 'ctrl_' . $property->getName();
325: 
326:             $GLOBALS['TL_RTE'][$file][$propertyId] = array(
327:                 'id' => $propertyId,
328:                 'file' => $file,
329:                 'type' => $type
330:             );
331:         }
332:     }
333: 
334:     /**
335:      * Retrieve the instance of a widget for the given property.
336:      *
337:      * @param string $property Name of the property for which the widget shall be retrieved.
338:      *
339:      * @return \Contao\Widget
340:      *
341:      * @throws DcGeneralInvalidArgumentException If an undefined property name has been passed.
342:      */
343:     public function getWidget($property)
344:     {
345:         $environment         = $this->getEnvironment();
346:         $defName             = $environment->getDataDefinition()->getName();
347:         $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition();
348: 
349:         if (!$propertyDefinitions->hasProperty($property))
350:         {
351:             throw new DcGeneralInvalidArgumentException('Property ' . $property . ' is not defined in propertyDefinitions.');
352:         }
353: 
354:         $event = new BuildWidgetEvent($environment, $this->model, $propertyDefinitions->getProperty($property));
355: 
356:         $environment->getEventPropagator()->propagate(
357:             $event::NAME,
358:             $event,
359:             array(
360:                 $defName,
361:                 $property
362:             )
363:         );
364: 
365:         if ($event->getWidget())
366:         {
367:             return $event->getWidget();
368:         }
369: 
370:         $propInfo  = $propertyDefinitions->getProperty($property);
371:         $propExtra = $propInfo->getExtra();
372:         $varValue  = $this->decodeValue($property, $this->model->getProperty($property));
373:         $xLabel    = $this->getXLabel($propInfo);
374: 
375:         $strClass = $GLOBALS['BE_FFL'][$propInfo->getWidgetType()];
376:         if (!class_exists($strClass))
377:         {
378:             return null;
379:         }
380: 
381:         // FIXME TEMPORARY WORKAROUND! To be fixed in the core: Controller::prepareForWidget(..).
382:         if (in_array($propExtra['rgxp'], array('date', 'time', 'datim'))
383:             && !$propExtra['mandatory']
384:             && is_numeric($varValue) && $varValue == 0)
385:         {
386:             $varValue = '';
387:         }
388: 
389:         // OH: why not $required = $mandatory always? source: DataContainer 226.
390:         // OH: the whole prepareForWidget(..) thing is an only mess
391:         // Widgets should parse the configuration by themselves, depending on what they need.
392:         $propExtra['required'] = ($varValue == '') && $propExtra['mandatory'];
393: 
394:         $options = $propInfo->getOptions();
395:         $event   = new GetPropertyOptionsEvent($environment, $this->model);
396:         $event->setPropertyName($property);
397:         $event->setOptions($options);
398:         $environment->getEventPropagator()->propagate(
399:             $event::NAME,
400:             $event,
401:             $environment->getDataDefinition()->getName(),
402:             $property
403:         );
404: 
405:         if ($event->getOptions() !== $options)
406:         {
407:             $options = $event->getOptions();
408:         }
409: 
410:         $arrConfig = array(
411:             'inputType' => $propInfo->getWidgetType(),
412:             'label' => array(
413:                 $propInfo->getLabel(),
414:                 $propInfo->getDescription()
415:             ),
416:             'options' => $options,
417:             'eval' => $propExtra,
418:             // TODO: populate these.
419:             // 'options_callback' => null,
420:             // 'foreignKey' => null
421:             // 'reference' =>
422:         );
423: 
424:         if (version_compare(VERSION, '3.0', '>='))
425:         {
426:             $arrPrepared = \Widget::getAttributesFromDca(
427:                 $arrConfig,
428:                 $propInfo->getName(),
429:                 $varValue,
430:                 $property,
431:                 $defName,
432:                 new DcCompat($environment, $this->model, $property)
433:             );
434:         }
435:         else
436:         {
437:             $arrPrepared = BackendBindings::prepareForWidget($arrConfig, $propInfo->getName(), $varValue, $property, $defName);
438:         }
439: 
440:         // Bugfix CS: ajax subpalettes are really broken.
441:         // Therefore we reset to the default checkbox behaviour here and submit the entire form.
442:         // This way, the javascript needed by the widget (wizards) will be correctly evaluated.
443:         if ($arrConfig['inputType'] == 'checkbox'
444:             && is_array($GLOBALS['TL_DCA'][$defName]['subpalettes'])
445:             && in_array($property, array_keys($GLOBALS['TL_DCA'][$defName]['subpalettes']))
446:             && $arrConfig['eval']['submitOnChange']
447:         )
448:         {
449:             $arrPrepared['onclick'] = $arrConfig['eval']['submitOnChange'] ? "Backend.autoSubmit('".$defName."')" : '';
450:         }
451: 
452:         $objWidget = new $strClass($arrPrepared);
453:         // OH: what is this? source: DataContainer 232.
454:         $objWidget->currentRecord = $this->model->getId();
455: 
456:         $objWidget->wizard .= $xLabel;
457: 
458:         $event = new ManipulateWidgetEvent($environment, $this->model, $propInfo, $objWidget);
459:         $environment->getEventPropagator()->propagate(
460:             $event::NAME,
461:             $event,
462:             array(
463:                 $defName,
464:                 $property
465:             )
466:         );
467: 
468:         return $objWidget;
469:     }
470: 
471:     /**
472:      * Build the date picker string.
473:      *
474:      * @param \Contao\Widget $objWidget The widget instance to generate the date picker string for.
475:      *
476:      * @return string
477:      */
478:     protected function buildDatePicker($objWidget)
479:     {
480:         $translator = $this->getEnvironment()->getTranslator();
481:         // TODO: need better interface to Contao Config class here.
482:         $strFormat = $GLOBALS['TL_CONFIG'][$objWidget->rgxp . 'Format'];
483: 
484:         $arrConfig = array(
485:             'allowEmpty' => true,
486:             'toggleElements' => '#toggle_' . $objWidget->id,
487:             'pickerClass' => 'datepicker_dashboard',
488:             'format' => $strFormat,
489:             'inputOutputFormat' => $strFormat,
490:             'positionOffset' => array(
491:                 'x' => 130,
492:                 'y' => -185
493:             ),
494:             'startDay' => $translator->translate('weekOffset', 'MSC'),
495:             'days' => array_values((array)$translator->translate('DAYS', 'MSC')),
496:             'dayShort' => $translator->translate('dayShortLength', 'MSC'),
497:             'months' => array_values((array)$translator->translate('MONTHS', 'MSC')),
498:             'monthShort' => $translator->translate('monthShortLength', 'MSC')
499:         );
500: 
501:         switch ($objWidget->rgxp)
502:         {
503:             case 'datim':
504:                 $arrConfig['timePicker'] = true;
505: 
506:                 $time = ",\n      timePicker:true";
507:                 break;
508: 
509:             case 'time':
510:                 $arrConfig['timePickerOnly'] = true;
511: 
512:                 $time = ",\n      pickOnly:\"time\"";
513:                 break;
514:             default:
515:                 $time = '';
516:         }
517: 
518:         if (version_compare(DATEPICKER, '2.1', '>'))
519:         {
520:             return 'new Picker.Date($$("#ctrl_' . $objWidget->id . '"), {
521:                 draggable:false,
522:                 toggle:$$("#toggle_' . $objWidget->id . '"),
523:                 format:"' . \Date::formatToJs($strFormat) . '",
524:                 positionOffset:{x:-197,y:-182}' . $time . ',
525:                 pickerClass:"datepicker_dashboard",
526:                 useFadeInOut:!Browser.ie,
527:                 startDay:' . $translator->translate('weekOffset', 'MSC') . ',
528:                 titleFormat:"' . $translator->translate('titleFormat', 'MSC') . '"
529:             });';
530:         }
531: 
532:         return 'new DatePicker(' . json_encode('#ctrl_' . $objWidget->id) . ', ' . json_encode($arrConfig) . ');';
533:     }
534: 
535:     /**
536:      * Generate the help msg for a property.
537:      *
538:      * @param string $property The name of the property.
539:      *
540:      * @return string
541:      */
542:     protected function generateHelpText($property)
543:     {
544:         $environment = $this->getEnvironment();
545:         $propInfo    = $environment->getDataDefinition()->getPropertiesDefinition()->getProperty($property);
546:         $label       = $propInfo->getDescription();
547:         $widgetType  = $propInfo->getWidgetType();
548: 
549:         // TODO: need better interface to Contao Config class here.
550:         if (!$GLOBALS['TL_CONFIG']['showHelp'] || $widgetType == 'password' || !strlen($label))
551:         {
552:             return '';
553:         }
554: 
555:         return '<p class="tl_help tl_tip">' . $label . '</p>';
556:     }
557: 
558:     /**
559:      * Render the widget for the named property.
560:      *
561:      * @param string $property     The name of the property for which the widget shall be rendered.
562:      *
563:      * @param bool   $ignoreErrors Flag if the error property of the widget shall get cleared prior rendering.
564:      *
565:      * @return string
566:      *
567:      * @throws DcGeneralRuntimeException For unknown properties.
568:      */
569:     public function renderWidget($property, $ignoreErrors = false)
570:     {
571:         $environment         = $this->getEnvironment();
572:         $definition          = $environment->getDataDefinition();
573:         $propertyDefinitions = $definition->getPropertiesDefinition();
574:         $propInfo            = $propertyDefinitions->getProperty($property);
575:         $propExtra           = $propInfo->getExtra();
576:         $widget              = $this->getWidget($property);
577: 
578:         /** @var \Contao\Widget $widget */
579:         if (!$widget)
580:         {
581:             throw new DcGeneralRuntimeException('No widget for property ' . $property);
582:         }
583: 
584:         if ($ignoreErrors)
585:         {
586:             // Clean the errors array and fix up the CSS class.
587:             $reflection = new \ReflectionProperty(get_class($widget), 'arrErrors');
588:             $reflection->setAccessible(true);
589:             $reflection->setValue($widget, array());
590:             $reflection = new \ReflectionProperty(get_class($widget), 'strClass');
591:             $reflection->setAccessible(true);
592:             $reflection->setValue($widget, str_replace('error', '', $reflection->getValue($widget)));
593:         }
594: 
595:         $strDatePicker = '';
596:         if (isset($propExtra['datepicker']))
597:         {
598:             $strDatePicker = $this->buildDatePicker($widget);
599:         }
600: 
601:         $objTemplateFoo = new ContaoBackendViewTemplate('dcbe_general_field');
602:         $objTemplateFoo->setData(array(
603:             'strName'       => $property,
604:             'strClass'      => $propExtra['tl_class'],
605:             'widget'        => $widget->parse(),
606:             'hasErrors'     => $widget->hasErrors(),
607:             'strDatepicker' => $strDatePicker,
608:             // TODO: need 'update' value - (\Input::get('act') == 'overrideAll' && ($arrData['inputType'] == 'checkbox' || $arrData['inputType'] == 'checkboxWizard') && $arrData['eval']['multiple'])
609:             'blnUpdate'     => false, // $blnUpdate,
610:             'strHelp'       => $this->generateHelpText($property)
611:         ));
612: 
613:         return $objTemplateFoo->parse();
614:     }
615: 
616:     /**
617:      * {@inheritDoc}
618:      */
619:     public function processInput(PropertyValueBag $propertyValues)
620:     {
621:         foreach (array_keys($propertyValues->getArrayCopy()) as $property)
622:         {
623:             $widget = $this->getWidget($property);
624:             // NOTE: the passed input values are RAW DATA from the input provider - aka widget known values and not
625:             // native data as in the model.
626:             // Therefore we do not need to decode them but MUST encode them.
627:             $widget->value = $propertyValues->getPropertyValue($property);
628:             $widget->validate();
629: 
630:             if ($widget->hasErrors())
631:             {
632:                 foreach ($widget->getErrors() as $error)
633:                 {
634:                     $propertyValues->markPropertyValueAsInvalid($property, $error);
635:                 }
636:             }
637:             else
638:             {
639:                 $propertyValues->setPropertyValue($property, $this->encodeValue($property, $widget->value));
640:             }
641:         }
642:     }
643: 
644:     /**
645:      * {@inheritDoc}
646:      */
647:     public function processErrors(PropertyValueBag $propertyValues)
648:     {
649:         $propertyErrors = $propertyValues->getInvalidPropertyErrors();
650:         $definitionName = $this->getEnvironment()->getDataDefinition()->getName();
651: 
652:         if ($propertyErrors)
653:         {
654:             $propagator = $this->getEnvironment()->getEventPropagator();
655: 
656:             foreach ($propertyErrors as $property => $errors)
657:             {
658:                 $widget = $this->getWidget($property);
659: 
660:                 foreach ($errors as $error)
661:                 {
662:                     $event = new ResolveWidgetErrorMessageEvent($this->getEnvironment(), $error);
663:                     $propagator->propagate(
664:                         $event::NAME,
665:                         $event,
666:                         array(
667:                             $definitionName,
668:                             $property
669:                         )
670:                     );
671: 
672:                     $widget->addError($event->getError());
673:                 }
674:             }
675:         }
676:     }
677: }
678: 
contao-community-alliance/dc-general API documentation generated by ApiGen 2.8.0