From 7dbb4ad5ee9a66f3a99dc60308d106bce3c144f9 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 02:05:20 -0400 Subject: [PATCH 01/54] New Panorama Builde Webform Element There is just too much here: - New JS to deal with adding hotspots, keeping track, etc. - CSS - The new Element. Needs quite more work, settings, configuration options, etc. I s writing data to JSON correctly, but needs another commit to read it, because of the nested reality of the elements (its a single Render Element with a deep hierarchy) - Editing added Hotspots is still not working, but close. - New Drupal Ajax Command to add hotspots on the fly - Also, this brings also an improvement to the Entity Reference View Display we had. Now we can render out Thumbnails! Means visual auto autoselect! - Sadly it requires a patch in Webforms if ($element['#selection_handler'] != 'views') { //$options = self::translateOptions($options, $element); } at \Drupal\webform\Element\WebformEntityTrait::setOptions Because it assumes that views is the only views selection handler possible. Leaving our custom Solr Views Work out! And we did a great job convincing D8 that ours is legit! But not webfoms. Finally. Found out that Webforms can only Save Submissions (no worries, not our SBF!) like the ones that go into logs and its own Database that are 2 levels deep. Another big bug. So yeah.... 2 AM. I need to sleep. --- css/scenebuilder_format_strawberryfield.css | 40 ++ js/scenebuilder-pannellum_strawberryfield.js | 71 ++ src/Ajax/AddHotSpotCommand.php | 63 ++ src/Element/WebformPanoramaTour.php | 616 ++++++++++++++++++ .../ViewsSolrSelection.php | 489 ++++++++++++++ .../WebformElement/WebformPanoramaTour.php | 61 ++ src/Plugin/views/display/EntityReference.php | 238 +++++++ webform_strawberryfield.libraries.yml | 15 +- 8 files changed, 1592 insertions(+), 1 deletion(-) create mode 100644 css/scenebuilder_format_strawberryfield.css create mode 100644 js/scenebuilder-pannellum_strawberryfield.js create mode 100644 src/Ajax/AddHotSpotCommand.php create mode 100644 src/Element/WebformPanoramaTour.php create mode 100644 src/Plugin/EntityReferenceSelection/ViewsSolrSelection.php create mode 100644 src/Plugin/WebformElement/WebformPanoramaTour.php create mode 100644 src/Plugin/views/display/EntityReference.php diff --git a/css/scenebuilder_format_strawberryfield.css b/css/scenebuilder_format_strawberryfield.css new file mode 100644 index 0000000..9fd0ccd --- /dev/null +++ b/css/scenebuilder_format_strawberryfield.css @@ -0,0 +1,40 @@ +.hotspot_marker_wrapper { + background-color: deepskyblue; + border-radius: 50%; + position: absolute; + top: 0px; + left: 0px; + display: none; + opacity: 0; + -moz-transition: opacity .3s ease-in-out; + -webkit-transition: opacity .3s ease-in-out; + -o-transition: opacity .3s ease-in-out; + -ms-transition: opacity .3s ease-in-out; + transition: opacity .3s ease-in-out; + z-index: 1 +} +.hotspot_editor_marker { + border-radius: 50%; + background-color: deepskyblue; + width: 150px; + height: 150px; + position: absolute; + left:-75px; + top:-75px; + opacity: 0; + animation: scaleIn 4s infinite cubic-bezier(.36, .11, .89, .32); +} +@keyframes scaleIn { + from { + transform: scale(.5, .5); + opacity: .5; + } + to { + transform: scale(2.5, 2.5); + opacity: 0; + } +} + + + + diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js new file mode 100644 index 0000000..42380ee --- /dev/null +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -0,0 +1,71 @@ +(function ($, Drupal, drupalSettings, pannellum) { + + + + function marker(event, $marker, $container) { + var pos = mousePosition(event,$container); + $marker.css("left", pos.x + 'px'); + $marker.css("top", pos.y + 'px'); + $marker.css("opacity", '1.0'); + $marker.css("display", 'block'); + $marker.fadeIn('slow'); + $marker.fadeOut('slow'); + } + + + function mousePosition(event,$container) { + var bounds = $container.getBoundingClientRect(); + var pos = {}; + // pageX / pageY needed for iOS + pos.x = (event.clientX || event.pageX) - bounds.left; + pos.y = (event.clientY || event.pageY) - bounds.top; + return pos; + } + + Drupal.AjaxCommands.prototype.webform_strawberryfield_pannellum_editor_addHotSpot = function(ajax, response, status) { + console.log('adding hotspot'); + // we need to find the first '.strawberry-panorama-item' id that is child of data-drupal-selector="edit-panorama-tour-hotspots" + $targetScene= $(response.selector).find('.strawberry-panorama-item').attr("id"); + $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); + $scene.panorama.addHotSpot(response.hotspot); + }; + + + Drupal.behaviors.webform_strawberryfield_pannellum_editor = { + attach: function(context, settings) { + $('.strawberry-panorama-item[data-iiif-image]').once('attache_pne') + .each(function (index, value) { + var hotspots = []; + // Get the node uuid for this element + var element_id = $(this).attr("id"); + // Check if we got some data passed via Drupal settings. + if (typeof(drupalSettings.format_strawberryfield.pannellum[element_id]) != 'undefined') { + console.log('initializing Panellum ') + console.log(Drupal.FormatStrawberryfieldPanoramas.panoramas); + Drupal.FormatStrawberryfieldPanoramas.panoramas.forEach(function(item, key) { + + var element_id_marker = element_id + '_marker'; + var $newmarker = $( "
"); + + $("#" +element_id+ " .pnlm-ui").append( $newmarker ); + + item.panorama.on('mousedown', function clicker(e) { + + $hotspot_cors = item.panorama.mouseEventToCoords(e); + var $jquerycontainer = $(item.panorama.getContainer()); + + $button_container = $jquerycontainer.closest("[data-drupal-selector='edit-panorama-tour-hotspots-temp']"); + + $button_container.find("[ data-drupal-hotspot-property='yaw']").val($hotspot_cors[1]); + $button_container.find("[ data-drupal-hotspot-property='pitch']").val($hotspot_cors[0]); + + marker(e,$newmarker,item.panorama.getContainer()); + + } + ); + }); + } + + })}} + +})(jQuery, Drupal, drupalSettings, window.pannellum); diff --git a/src/Ajax/AddHotSpotCommand.php b/src/Ajax/AddHotSpotCommand.php new file mode 100644 index 0000000..2d7f105 --- /dev/null +++ b/src/Ajax/AddHotSpotCommand.php @@ -0,0 +1,63 @@ +selector = $selector; + $this->hotspot = $hotspot; + $this->sceneid = $sceneid; + + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + + return [ + 'command' => 'webform_strawberryfield_pannellum_editor_addHotSpot', + 'selector' => $this->selector, + 'hotspot' => $this->hotspot, + 'sceneid' => $this->sceneid, + ]; + } + +} + diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php new file mode 100644 index 0000000..899facf --- /dev/null +++ b/src/Element/WebformPanoramaTour.php @@ -0,0 +1,616 @@ + 'entity_autocomplete', + '#title' => t('Select a Scene'), + '#target_type' => 'node', + '#selection_handler' => 'solr_views', + '#selection_settings' => [ + 'view' => [ + 'view_name' => 'ado_selection_by_type', + 'display_name' => 'entity_reference_solr_2', + 'arguments' => ['Panorama'] + ], + ], + ]; + $element['hotspots'] = [ + '#type' => 'value' + ]; + return $elements; + } + + /** + * {@inheritdoc} + */ + public static function processWebformComposite( + &$element, + FormStateInterface $form_state, + &$complete_form + ) { + + $element = parent::processWebformComposite( + $element, + $form_state, + $complete_form + ); + $element_name = $element['#name']; + + // We need this button to validate. important + // NEVER add '#limit_validation_errors' => [], + $element['select_button'] = [ + '#title' => 'Select Scene', + '#type' => 'submit', + '#value' => t('Select Scene'), + '#name' => $element['#name'] . '_select_button', + '#submit' => [[get_called_class(), 'selectSceneSubmit']], + '#ajax' => [ + 'callback' => [get_called_class(), 'selectSceneCallBack'], + ], + '#button_type' => 'default', + '#visible' => 'true', + ]; + $element['hotspots_temp'] = [ + '#type' => 'fieldset', + '#title' => 'Hotspots for this Scene', + '#attributes' => [ + 'data-drupal-selector' => $element['#name'] . '-hotspots', + ], + '#attached' => [ + 'library' => [ + 'webform_strawberryfield/scenebuilder_pannellum_strawberry', + 'core/drupal.dialog.ajax', + ], + ] + ]; + // @TODO Modal will need to be enabled on the formatter too. + + if ($form_state->getValue(['panorama_tour', 'scene'])) { + $nodeid = $form_state->getValue(['panorama_tour', 'scene']); + + if ($form_state->getValue(['panorama_tour', 'hotspots_temp', 'ado'])) { + + $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); + // We have to do the same when saving the actual Hotspot. + + $element['hotspots_temp']['entities'] = [ + '#type' => 'value', + '#default_value' => $nodeid, + ]; + } + $vb = \Drupal::entityTypeManager()->getViewBuilder( + 'node' + ); // Drupal\node\NodeViewBuilder + $viewmode = 'digital_object_with_pannellum_panorama_'; + $node = \Drupal::entityTypeManager()->getStorage('node')->load( + $nodeid + ); + $errors = []; + + + if ($node) { + $nodeview = $vb->view($node, $viewmode); + $element['hotspots_temp']['yaw'] = [ + '#title' => t('Hotspot Yaw'), + '#type' => 'textfield', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'yaw', + ], + ]; + $element['hotspots_temp']['pitch'] = [ + '#title' => t('Hotspot Pitch'), + '#type' => 'textfield', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'pitch', + ], + ]; + + $element['hotspots_temp']['type'] = [ + '#title' => t('Hotspot Type'), + '#type' => 'select', + '#options' => [ + 'text' => 'Text', + 'url' => 'An External URL', + 'ado' => 'Another Digital Object', + 'scene' => 'Another Panorama Scene', + ], + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'type', + ], + ]; + $element['hotspots_temp']['label'] = [ + '#title' => t('Label'), + '#type' => 'textfield', + '#size' => '12', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'label', + ], + ]; + $element['hotspots_temp']['content'] = [ + '#title' => t('Content'), + '#type' => 'textarea', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'content', + ], + ]; + $element['hotspots_temp']['ado'] = [ + '#type' => 'entity_autocomplete', + '#title' => t('Select a Digital Object'), + '#target_type' => 'node', + '#selection_handler' => 'solr_views', + '#selection_settings' => [ + 'view' => [ + 'view_name' => 'ado_selection_by_type', + 'display_name' => 'entity_reference_solr_2', + 'arguments' => ['Photograph'] + ], + ], + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'content', + ], + ]; + + + // To make sure menu variables are passed + // we limit validation errors to those elements + $limit = array_merge($element['#parents'], ['hotspots_temp']); + + $element['hotspots_temp']['add_hotspot'] = [ + '#type' => 'submit', + '#value' => t('Add Hotspot'), + '#name' => $element['#name'] . '_addhotspot_button', + '#submit' => [[static::class, 'addHotspotSubmit']], + '#ajax' => [ + 'callback' => [static::class, 'addHotSpotCallBack'], + ], + '#button_type' => 'default', + '#visible' => 'true', + '#limit_validation_errors' => FALSE + //'#limit_validation_errors' => [$limit] + ]; + // Make sure the top element gets our validation + // Since all this subelements are really not triggering that + // Lets check if hotspots had errors + + if (!empty($form_state->get('hotspot_custom_errors'))) { + $hotspot_errors = $form_state->get('hotspot_custom_errors'); + + foreach ($element['hotspots_temp'] as $key => &$field) + + if (array_key_exists($key, $hotspot_errors)) { + error_log('found errors'); + $field['#attributes']['class'] = ['form-item--error-message', 'alert', 'alert-danger', 'alert-sm']; + $field['#prefix'] = $hotspot_errors[$key]; + } + } + + $element['hotspots_temp']['node'] = $nodeview; + $element['hotspots_temp']['node']['#weight'] = 10; + $element['hotspots_temp']['added_hotspots'] = [ + '#type' => 'details', + '#attributes' => [ + 'data-drupal-loaded-node-hotspot-table' => $nodeid + ], + '#weight' => 11, + ]; + + // Get the hotspot submitted data + if ($form_state->isRebuilding() && !empty( + $form_state->get( + $element_name . '-hotspots' + ) + )) { + // Json will be UTF-8 correctly encoded/decoded! + $hotspot_list = $form_state->get($element_name . '-hotspots'); + + $table_header = [ + 'coordinates' => t('Hotspot Coordinates'), + 'type' => t('Type'), + 'label' => t('Label'), + 'operations' => t('Operations'), + ]; + + foreach ($hotspot_list as $key => $hotspot) { + // Key will be in element['#name'].'_'.count($hotspot_list)+1; + $table_options[$key] = [ + 'coordinates' => $hotspot->yaw . "," . $hotspot->pitch, + 'type' => $hotspot->type, + 'label' => isset($hotspot->label) ? $hotspot->label : t('no name'), + 'operations' => [ + 'data' => [ + '#type' => 'submit', + '#value' => t('Edit'), + ], + ] + ]; + } + + + $element['hotspots_temp']['added_hotspots'] = [ + '#prefix'=> '
', + '#suffix'=> '
', + '#title' => t('Hotspots in this scene'), + '#type' => 'tableselect', + '#default_value' => $form_state->get('current-hotspots'), + '#name' => $element['#name'] . '_added_hotspots', + '#header' => $table_header, + '#options' => $table_options, + '#empty' => t('No Hotspots yet for this Scene'), + '#js_select' => TRUE, + '#weight' => 11, + '#multiple' => TRUE, + '#attributes' => [ + 'data-drupal-loaded-node-hotspot-table' => $nodeid + ], + ]; + + } + } + } + $element['#element_validate'][] = [static::class, 'validateHotSpotItems']; + return $element; + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return \Drupal\Core\Ajax\AjaxResponse + */ + public static function selectSceneCallBack( + array $form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + $element = NestedArray::getValue( + $form, + array_slice($button['#array_parents'], 0, -1) + ); + + $response = new AjaxResponse(); + $data_selector = $element['hotspots_temp']['#attributes']['data-drupal-selector']; + $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; + $response->addCommand( + new ReplaceCommand( + '[data-drupal-selector="' . $data_selector . '"]', + $element['hotspots_temp'] + ) + ); + return $response; + } + + /** + * Submit Hanlder for the Nominatim reconciliation call. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function selectSceneSubmit( + array &$form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + error_log('selectSceneSubmit'); + $input = $form_state->getUserInput(); + // Hack. No explanation. + + //error_log(var_export($form_state->getValue(['panorama_tour','scene']),true)); + $main_element_parents = array_slice($button['#array_parents'], 0, -1); + + // Fetch main element so we can use that webform_id key to store the full output of nominatim + $top_element = NestedArray::getValue($form, $main_element_parents); + $my_hotspots = $top_element['#name'] . '-hotspots'; + + + if (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::addHotspotSubmit( + $form, + $form_state + ); + } + + + // Rebuild the form. + $form_state->setRebuild(TRUE); + } + + /** + * Submit Handler for adding a Hotspot. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function addHotspotSubmit( + array &$form, + FormStateInterface $form_state + ) { + error_log('addHotspotSubmit'); + $button = $form_state->getTriggeringElement(); + $main_element_parents = array_slice($button['#array_parents'], 0, -1); + + // Fetch main element so we can use that webform_id key to store the full output of nominatim + $top_element = NestedArray::getValue($form, $main_element_parents); + + $my_hotspots_key = $top_element['#name'] . '-hotspots'; + + $existing_objects = $form_state->get($my_hotspots_key) ? $form_state->get( + $my_hotspots_key + ) : []; + + $hotspot = new \stdClass; + + $hotspot->pitch = $form_state->getValue( + [$top_element['#name'], 'hotspots_temp', 'pitch'] + ); + $hotspot->yaw = $form_state->getValue( + [$top_element['#name'], 'hotspots_temp', 'yaw'] + ); + $hotspot->type = $form_state->getValue( + [$top_element['#name'], 'hotspots_temp', 'type'] + ); + $hotspot->text = $form_state->getValue( + [$top_element['#name'], 'hotspots_temp', 'label'] + ); + + $hotspot->id = $top_element['#name'] . '_' . (count($existing_objects) + 1); + if ($hotspot->type == 'url') { + $hotspot->URL = $hotspot->text; + $hotspot->type = 'info'; + } + if ($hotspot->type == 'ado') { + $nodeid = $form_state->getValue( + ['panorama_tour', 'hotspots_temp', 'ado'] + ); + // Now the fun part. Since this autocomplete is not part of the process + // chain we never get the value transformed into id. + // So. we do it directly + $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); + $url = \Drupal\Core\Url::fromRoute('entity.node.canonical', ['node' => $nodeid],[]); + $url = $url->toString(); + $attributes = new \stdClass; + $attributes->{'data-dialog-type'} = 'modal'; + $attributes->class = 'use-ajax'; + $attributes->{'data-dialog-options'} = '{"width":800}'; + $hotspot->type = 'info'; + + $hotspot->URL = $url; + $hotspot->attributes = $attributes; + } + if ($hotspot->type == 'text') { + $hotspot->text = $hotspot->text; + $hotspot->type = 'info'; + } + error_log(var_export($hotspot,true)); + + $existing_objects[$hotspot->id] = $hotspot; + error_log($my_hotspots_key); + // @TODO make sure people don't add twice the same coordinates! + $form_state->set($my_hotspots_key, $existing_objects); + + return; + + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return \Drupal\Core\Ajax\AjaxResponse + */ + public static function addHotSpotCallBack( + array $form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + + $element = NestedArray::getValue( + $form, + array_slice($button['#array_parents'], 0, -2) + ); + error_log('addHotSpotCallBack'); + + $response = new AjaxResponse(); + $data_selector = $element['hotspots_temp']['added_hotspots']['#attributes']['data-drupal-loaded-node-hotspot-table']; + $my_hotspots_key = $element['#name'] . '-hotspots'; + + + $existing_objects = $form_state->get($my_hotspots_key) ? $form_state->get( + $my_hotspots_key + ) : []; + if (count($existing_objects) > 0) { + + $data_selector2 = $element['hotspots_temp']['#attributes']['data-drupal-selector']; + + $response->addCommand( + new AddHotSpotCommand( + '[data-drupal-selector="' . $data_selector2 . '"]', + end($existing_objects), + 'webform_strawberryfield_pannellum_editor_addHotSpot' + ) + ); + } + $response->addCommand( + new ReplaceCommand( + '[data-drupal-loaded-node-hotspot-table="' . $data_selector . '"]', + $element['hotspots_temp']['added_hotspots'] + ) + ); + return $response; + } + + public static function valueCallback( + &$element, + $input, + FormStateInterface $form_state + ) { + error_log('valueCallback'); + /** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */ + $element_manager = \Drupal::service('plugin.manager.webform.element'); + $composite_elements = static::getCompositeElements($element); + $composite_elements = WebformElementHelper::getFlattened( + $composite_elements + ); + + // Get default value for inputs. + $default_value = []; + foreach ($composite_elements as $composite_key => $composite_element) { + $element_plugin = $element_manager->getElementInstance( + $composite_element + ); + + error_log($composite_key); + if ($element_plugin->isInput($composite_element)) { + $default_value[$composite_key] = ''; + } + } + // We need to move our internal hotspot form state var into + // the value field and back. + $my_hotspots_key = $element['#name'] . '-hotspots'; + $existing_objects = $form_state->get($my_hotspots_key) ? (array) $form_state->get( + $my_hotspots_key + ) : []; + $list_of_hotspots = array_values($existing_objects); + foreach ($list_of_hotspots as $hotspot) { + $default_value['hotspots'][] = (array) $hotspot; + } + + if ($input === FALSE) { + if (empty($element['#default_value']) || !is_array( + $element['#default_value'] + )) { + $element['#default_value'] = []; + } + return $element['#default_value'] + $default_value; + } + + $to_return = (is_array($input)) ? $input + $default_value : $default_value; + + return $to_return; + + // TODO: Change the autogenerated stub + } + + public static function validateWebformComposite( + &$element, + FormStateInterface $form_state, + &$complete_form + ) { + error_log('validateWebformComposite'); + // What i learned. This god forgotten function + // Destroys my submission values... + // Since this is a composite and was never meant to have more than one button + // triggering element can be wrong + // Lets use the input and search of the actual element + + + $trigger = $form_state->getTriggeringElement(); + // IMPORTANT: Must get values from the $form_states since sub-elements + // may call $form_state->setValueForElement() via their validation hook. + // @see \Drupal\webform\Element\WebformEmailConfirm::validateWebformEmailConfirm + // @see \Drupal\webform\Element\WebformOtherBase::validateWebformOther + $value = NestedArray::getValue( + $form_state->getValues(), + $element['#parents'] + ); + + // Only validate composite elements that are visible. + $has_access = (!isset($element['#access']) || $element['#access'] === TRUE); + if ($has_access) { + // Validate required composite elements. + $composite_elements = static::getCompositeElements($element); + $composite_elements = WebformElementHelper::getFlattened( + $composite_elements + ); + foreach ($composite_elements as $composite_key => $composite_element) { + $is_required = !empty($element[$composite_key]['#required']); + $is_empty = (isset($value[$composite_key]) && $value[$composite_key] === ''); + if ($is_required && $is_empty) { + WebformElementHelper::setRequiredError( + $element[$composite_key], + $form_state + ); + } + } + } + + // Clear empty composites value. + if (empty(array_filter($value))) { + error_log('is empty'); + $element['#value'] = NULL; + $form_state->setValueForElement($element, NULL); + } + + } + + public static function validateHotSpotItems( + &$element, + FormStateInterface $form_state, + &$complete_form + ) { + + $input = $form_state->getUserInput(); + // Hack. No explanation. + if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button') { + if (!is_numeric($form_state->getValue( + [$element['#name'], 'hotspots_temp', 'yaw']))) { + // This is needed because we are validating internal subelements during + // Hotspot adding, but there is really no real form submit. + // We want the form to keep on rebuild. + $form_state->set('hotspot_custom_errors', ['yaw'=>t('Yaw needs to be numeric and not empty')]); + } + else { + $form_state->set('hotspot_custom_errors', NULL); + } + } + } + + +} diff --git a/src/Plugin/EntityReferenceSelection/ViewsSolrSelection.php b/src/Plugin/EntityReferenceSelection/ViewsSolrSelection.php new file mode 100644 index 0000000..80090ba --- /dev/null +++ b/src/Plugin/EntityReferenceSelection/ViewsSolrSelection.php @@ -0,0 +1,489 @@ + 'entity.manager']; + + /** + * The loaded View object. + * + * @var \Drupal\views\ViewExecutable + */ + protected $view; + + /** + * The entity type manager service. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityTypeManager; + + /** + * The module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** + * Constructs a new ViewsSelection object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + EntityTypeManagerInterface $entity_type_manager, + ModuleHandlerInterface $module_handler, + AccountInterface $current_user, + RendererInterface $renderer = NULL + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->entityTypeManager = $entity_type_manager; + $this->moduleHandler = $module_handler; + $this->currentUser = $current_user; + if (!$renderer) { + @trigger_error( + 'Calling ViewsSelection::__construct() with the $renderer argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0.', + E_USER_DEPRECATED + ); + $renderer = \Drupal::service('renderer'); + } + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('module_handler'), + $container->get('current_user'), + $container->get('renderer') + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'view' => [ + 'view_name' => NULL, + 'display_name' => NULL, + 'arguments' => [], + ], + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm( + array $form, + FormStateInterface $form_state + ) { + $form = parent::buildConfigurationForm($form, $form_state); + + $view_settings = $this->getConfiguration()['view']; + $displays = Views::getApplicableViews('entity_reference_display'); + // Filter views that list the entity type we want, and group the separate + // displays by view. + // $entity_type = $this->entityTypeManager->getDefinition($this->configuration['target_type']); + $view_storage = $this->entityTypeManager->getStorage('view'); + + $options = []; + foreach ($displays as $data) { + list($view_id, $display_id) = $data; + $view = $view_storage->load($view_id); + $display = $view->get('display'); + $options[$view_id . ':' . $display_id] = $view_id . ' - ' . $display[$display_id]['display_title']; + } + + // The value of the 'view_and_display' select below will need to be split + // into 'view_name' and 'view_display' in the final submitted values, so + // we massage the data at validate time on the wrapping element (not + // ideal). + $form['view']['#element_validate'] = [ + [ + get_called_class(), + 'settingsFormValidate', + ], + ]; + + if ($options) { + $default = !empty($view_settings['view_name']) ? $view_settings['view_name'] . ':' . $view_settings['display_name'] : NULL; + $form['view']['view_and_display'] = [ + '#type' => 'select', + '#title' => $this->t('View used to select the entities'), + '#required' => TRUE, + '#options' => $options, + '#default_value' => $default, + '#description' => '

' . $this->t( + 'Choose the view and display that select the entities that can be referenced.
Only views with a display of type "Entity Reference" are eligible.' + ) . '

', + ]; + + $default = !empty($view_settings['arguments']) ? implode( + ', ', + $view_settings['arguments'] + ) : ''; + $form['view']['arguments'] = [ + '#type' => 'textfield', + '#title' => $this->t('View arguments'), + '#default_value' => $default, + '#required' => FALSE, + '#description' => $this->t( + 'Provide a comma separated list of arguments to pass to the view.' + ), + ]; + } + else { + if ($this->currentUser->hasPermission( + 'administer views' + ) && $this->moduleHandler->moduleExists('views_ui')) { + $form['view']['no_view_help'] = [ + '#markup' => '

' . $this->t( + 'No eligible views were found. Create a view with an Entity Reference display, or add such a display to an existing view.', + [ + ':create' => Url::fromRoute('views_ui.add')->toString(), + ':existing' => Url::fromRoute('entity.view.collection') + ->toString(), + ] + ) . '

', + ]; + } + else { + $form['view']['no_view_help']['#markup'] = '

' . $this->t( + 'No eligible views were found.' + ) . '

'; + } + } + return $form; + } + + /** + * Initializes a view. + * + * @param string|null $match + * (Optional) Text to match the label against. Defaults to NULL. + * @param string $match_operator + * (Optional) The operation the matching should be done with. Defaults + * to "CONTAINS". + * @param int $limit + * Limit the query to a given number of items. Defaults to 0, which + * indicates no limiting. + * @param array|null $ids + * Array of entity IDs. Defaults to NULL. + * + * @return bool + * Return TRUE if the view was initialized, FALSE otherwise. + */ + protected function initializeView( + $match = NULL, + $match_operator = 'CONTAINS', + $limit = 0, + $ids = NULL + ) { + $view_name = $this->getConfiguration()['view']['view_name']; + $display_name = $this->getConfiguration()['view']['display_name']; + + // Check that the view is valid and the display still exists. + $this->view = Views::getView($view_name); + if (!$this->view || !$this->view->access($display_name)) { + \Drupal::messenger()->addWarning( + t( + 'The reference view %view_name cannot be found.', + ['%view_name' => $view_name] + ) + ); + return FALSE; + } + + $this->view->setDisplay($display_name); + + // Pass options to the display handler to make them available later. + // We can not pass 'match' as option since \Drupal\views\Plugin\views\display\EntityReference::query + // Tries to deal with this as it was an SQL Query! + $lang_code = \Drupal::languageManager()->getCurrentLanguage()->getId(); + // We are aiming at LoD here. We want to allow other than default language + // To be valid too. + $ids_solr = NULL; + if (!empty($ids)) { + foreach ($ids as $id) { + $ids_solr[] = 'entity:node/' . $id . ':' . $lang_code; + $ids_solr[] = 'entity:node/' . $id . ':' . 'en'; + $ids_solr[] = 'entity:node/' . $id . ':' . LanguageInterface::LANGCODE_NOT_SPECIFIED; + $ids_solr[] = 'entity:node/' . $id . ':' . LanguageInterface::LANGCODE_NOT_APPLICABLE; + } + // Remove doubles. + $ids_solr = array_unique($ids_solr); + } + + $entity_reference_options = [ + 'match_operator_solr' => $match_operator, + 'match_solr' => $match, + 'limit' => $limit, + 'ids_solr' => $ids_solr, + 'ids' => $ids, + ]; + + $this->view->displayHandlers->get($display_name)->setOption( + 'entity_reference_options', + $entity_reference_options + ); + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function getReferenceableEntities( + $match = NULL, + $match_operator = 'CONTAINS', + $limit = 0 + ) { + $entities = []; + if ($display_execution_results = $this->getDisplayExecutionResults( + $match, + $match_operator, + $limit + )) { + $entities = $this->stripAdminAndAnchorTagsFromResults( + $display_execution_results + ); + } + + return $entities; + } + + /** + * Fetches the results of executing the display. + * + * @param string|null $match + * (Optional) Text to match the label against. Defaults to NULL. + * @param string $match_operator + * (Optional) The operation the matching should be done with. Defaults + * to "CONTAINS". + * @param int $limit + * Limit the query to a given number of items. Defaults to 0, which + * indicates no limiting. + * @param array|null $ids + * Array of entity IDs. Defaults to NULL. + * + * @return array + * The results. + */ + protected function getDisplayExecutionResults( + string $match = NULL, + string $match_operator = 'CONTAINS', + int $limit = 0, + array $ids = NULL + ): array { + $display_name = $this->getConfiguration()['view']['display_name']; + $arguments = $this->getConfiguration()['view']['arguments']; + $results = []; + + if ($this->initializeView($match, $match_operator, $limit)) { + $results = $this->view->executeDisplay($display_name, $arguments); + } + return $results ?? []; + } + + /** + * Strips all admin and anchor tags from a result list. + * + * These results are usually displayed in an autocomplete field, which is + * surrounded by anchor tags. Most tags are allowed inside anchor tags, except + * for other anchor tags. + * + * @param array $results + * The result list. + * + * @return array + * The provided result list with anchor tags removed. + */ + protected function stripAdminAndAnchorTagsFromResults(array $results): array { + $allowed_tags = Xss::getAdminTagList(); + if (($key = array_search('a', $allowed_tags)) !== FALSE) { + unset($allowed_tags[$key]); + } + + $stripped_results = []; + + foreach ($results as $id => $row) { + /* @var $entityadapter EntityAdapter */ + if (isset($row['#row'])) { + // Means field render or default + $entityadapter = $row['#row']->_object; + $entity = $entityadapter->getValue(); + } + elseif (isset($row['#node'])) { + $entity = $row['#node']; + } + else { + // We don't know what we got, but its no content entity. + break; + } + + $stripped_results[$entity->bundle()][$entity->id( + )] = ViewsRenderPipelineMarkup::create( + Xss::filter($this->renderer->renderPlain($row), $allowed_tags) + ); + } + return $stripped_results; + } + + /** + * {@inheritdoc} + */ + public function countReferenceableEntities( + $match = NULL, + $match_operator = 'CONTAINS' + ) { + $this->getReferenceableEntities($match, $match_operator); + return $this->view->pager->getTotalItems(); + } + + /** + * {@inheritdoc} + */ + public function validateReferenceableEntities(array $ids) { + // This class differs from the SQL implemententation + // Because we need to transform Solr entity:node/9:en into 9 + $display_name = $this->getConfiguration()['view']['display_name']; + $arguments = $this->getConfiguration()['view']['arguments']; + $result = []; + if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) { + // Get the results. + $entities = $this->view->executeDisplay($display_name, $arguments); + $result = array_keys($entities); + foreach ($result as &$id) { + // We want to convert entity:node/9:en into 9 + $parts = explode(':', str_replace('entity:node/', '', $id)); + $id = $parts[0]; + } + } + return $result; + } + + /** + * Element validate; Check View is valid. + */ + public static function settingsFormValidate( + $element, + FormStateInterface $form_state, + $form + ) { + // Split view name and display name from the 'view_and_display' value. + if (!empty($element['view_and_display']['#value'])) { + list($view, $display) = explode( + ':', + $element['view_and_display']['#value'] + ); + } + else { + $form_state->setError( + $element, + t('The views entity selection mode requires a view.') + ); + return; + } + + // Explode the 'arguments' string into an actual array. Beware, explode() + // turns an empty string into an array with one empty string. We'll need an + // empty array instead. + $arguments_string = trim($element['arguments']['#value']); + if ($arguments_string === '') { + $arguments = []; + } + else { + // array_map() is called to trim whitespaces from the arguments. + $arguments = array_map('trim', explode(',', $arguments_string)); + } + + $value = [ + 'view_name' => $view, + 'display_name' => $display, + 'arguments' => $arguments, + ]; + $form_state->setValueForElement($element, $value); + } + +} diff --git a/src/Plugin/WebformElement/WebformPanoramaTour.php b/src/Plugin/WebformElement/WebformPanoramaTour.php new file mode 100644 index 0000000..b2c171c --- /dev/null +++ b/src/Plugin/WebformElement/WebformPanoramaTour.php @@ -0,0 +1,61 @@ +elementManager->isExcluded('webform_metadata_panoramatour') ? $this->t('Panorama Multi Scene Builder') : parent::getPluginLabel(); + } + + /** + * {@inheritdoc} + */ + protected function formatHtmlItemValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + return $this->formatTextItemValue($element, $webform_submission, $options); + } + + /** + * {@inheritdoc} + */ + protected function formatTextItemValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + $value = $this->getValue($element, $webform_submission, $options); + + $lines = []; + if (!empty($value['scene'])) { + $lines[] = $value['scene']; + } + if (!empty($value['hotspots'])) { + $lines[] = $value['hotspots']; + } + + + return $lines; + } + +} diff --git a/src/Plugin/views/display/EntityReference.php b/src/Plugin/views/display/EntityReference.php new file mode 100644 index 0000000..7497e41 --- /dev/null +++ b/src/Plugin/views/display/EntityReference.php @@ -0,0 +1,238 @@ + 'entity_reference']; + $options['defaults']['default']['style'] = FALSE; + $options['row']['contains']['type'] = ['default' => 'entity_reference']; + $options['defaults']['default']['row'] = FALSE; + + // Set the display title to an empty string (not used in this display type). + $options['title']['default'] = ''; + $options['defaults']['default']['title'] = FALSE; + + return $options; + } + + /** + * {@inheritdoc} + */ + public function optionsSummary(&$categories, &$options) { + parent::optionsSummary($categories, $options); + // Disable 'title' so it won't be changed from the default set in + // \Drupal\views\Plugin\views\display\EntityReference::defineOptions. + unset($options['title']); + } + + /** + * {@inheritdoc} + */ + public function getType() { + return 'entity_reference'; + } + + /** + * {@inheritdoc} + */ + public function execute() { + return $this->view->render($this->display['id']); + } + + /** + * Builds the view result as a renderable array. + * + * @return array + * Renderable array or empty array. + */ + public function render() { + if (!empty($this->view->result) && $this->view->style_plugin->evenEmpty()) { + return $this->view->style_plugin->render($this->view->result); + } + return []; + } + + /** + * {@inheritdoc} + */ + public function usesExposed() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function query() { + // Make sure the id field is included in the results. + // @see \Drupal\search_api\Plugin\views\ResultRow::$lazyLoad + // "search_api_id" is always the base field id that gets lazy resolved + // against the getId() == entity->id on result rows. + // But that means you can not filter against it using directly the id int. + // Means our id's passed by \Drupal\webform_strawberryfield\Plugin\EntityReferenceSelection\ViewsSolrSelection + // Need to have that form too. + $id_field = $this->view->storage->get('base_field'); + $id_table = $this->view->storage->get('base_table'); + + // This is weird. Since this extends DisplayPluginBase and viewsexecutable + // Assumes that the $query is of a base type instead extending an interface + // The Dev IDE gets can not access the real properties we have at hand + // For this specific type of query. So this helps. + /* @var $search_api_query \Drupal\search_api\Plugin\views\query\SearchApiQuery */ + $search_api_query = $this->view->query; + + $this->id_field_alias = $this->view->query->addField($id_table, $id_field); + if (!empty($this->view->live_preview)) { + // We hate blind Views on the UI. Give it a starting Match so we can + // see the logic happening + } + + // Make sure the id field is included in the results. + $id_field = $this->view->storage->get('base_field'); + $id_table = $this->view->storage->get('base_table'); + $this->id_field_alias = $search_api_query->addField($id_table, $id_field); + + $options = $this->getOption('entity_reference_options'); + // Restrict the autocomplete options based on what's been typed already. + if (isset($options['match_solr'])) { + $style_options = $this->getOption('style'); + // See if we need to use some escape mechanism from here + // @see \Drupal\search_api_solr\Utility\Utility + $value = $options['match_solr']; + if ($options['match_operator_solr'] !== '=') { + $value = $value . '%'; + if ($options['match_operator_solr'] != 'STARTS_WITH') { + $value = '%' . $value; + } + } + + // Multiple search fields are OR'd together. + $match_condition_group = $search_api_query->createConditionGroup('OR'); + + // we can't use $field_id as field. + // We need to resolve it back to its original Solr field. + // Build the condition using the selected search fields. + foreach ($style_options['options']['search_fields'] as $field_id) { + if (!empty($field_id)) { + $realfieldname = $this->getHandlers('field')[$field_id]->field; + // Add an OR condition for the field. + // We use = operator since we expect fields to be ngrams indexed + $match_condition_group->addCondition($realfieldname, $value, '='); + } + } + + $search_api_query->addConditionGroup($match_condition_group, 'MATCH'); + } + // Add an IN condition for validation. + if (!empty($options['ids_solr'])) { + $search_api_query->addWhere(0, $id_field, $options['ids_solr'], 'IN'); + } + $this->view->setItemsPerPage($options['limit']); + } + + /** + * {@inheritdoc} + */ + public function validate() { + $errors = parent::validate(); + // Verify that search fields are set up. + $style = $this->getOption('style'); + if (!isset($style['options']['search_fields'])) { + $errors[] = $this->t('Display "@display" needs a selected search fields to work properly. See the settings for the Entity Reference list format.', ['@display' => $this->display['display_title']]); + } + else { + // Verify that the search fields used actually exist. + $fields = array_keys($this->handlers['field']); + foreach ($style['options']['search_fields'] as $field_alias => $enabled) { + if ($enabled && !in_array($field_alias, $fields)) { + $errors[] = $this->t('Display "@display" uses field %field as search field, but the field is no longer present. See the settings for the Entity Reference list format.', ['@display' => $this->display['display_title'], '%field' => $field_alias]); + } + } + } + return $errors; + } + +} diff --git a/webform_strawberryfield.libraries.yml b/webform_strawberryfield.libraries.yml index eae0373..08ad1f9 100644 --- a/webform_strawberryfield.libraries.yml +++ b/webform_strawberryfield.libraries.yml @@ -10,4 +10,17 @@ webform_strawberryfield.nodeactions.toggle: - core/jquery - core/drupal - core/drupalSettings - - core/drupal.form \ No newline at end of file + - core/drupal.form +scenebuilder_pannellum_strawberry: + version: 1.0 + js: + js/scenebuilder-pannellum_strawberryfield.js: {minified: false} + css: + component: + css/scenebuilder_format_strawberryfield.css: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings + - format_strawberryfield/pannellum + - format_strawberryfield/iiif_pannellum_strawberry \ No newline at end of file From 152f625e7b1431fecdb7739f0746b7f7b0665585 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 02:08:59 -0400 Subject: [PATCH 02/54] Ups. Copy pasta fixed --- src/Plugin/WebformElement/WebformPanoramaTour.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/WebformElement/WebformPanoramaTour.php b/src/Plugin/WebformElement/WebformPanoramaTour.php index b2c171c..b786fad 100644 --- a/src/Plugin/WebformElement/WebformPanoramaTour.php +++ b/src/Plugin/WebformElement/WebformPanoramaTour.php @@ -12,7 +12,7 @@ use Drupal\webform\Plugin\WebformElement\WebformCompositeBase; /** - * Provides an 'LoC Subject Heading' element. + * Provides an 'Panorama Builder' element. * * @WebformElement( * id = "webform_metadata_panoramatour", From b3ea9b47f681baf761a49624a378577b765e9a1d Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 13:57:52 -0400 Subject: [PATCH 03/54] Widen the fixed range for now This requires a second set of arguments One to include things another to exclude --- src/Element/WebformPanoramaTour.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 899facf..9ba1238 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -186,7 +186,7 @@ public static function processWebformComposite( 'view' => [ 'view_name' => 'ado_selection_by_type', 'display_name' => 'entity_reference_solr_2', - 'arguments' => ['Photograph'] + 'arguments' => ['Image'] ], ], '#attributes' => [ From 2224dd2529f773eb6a0025d48ffbf35cb38dc285 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 13:58:23 -0400 Subject: [PATCH 04/54] Small fix for Nominatim (since i'm here) When Nomintatim gives us no results, better to have those options initialized! --- src/Element/WebformNominatim.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Element/WebformNominatim.php b/src/Element/WebformNominatim.php index 37a2902..e81d27d 100644 --- a/src/Element/WebformNominatim.php +++ b/src/Element/WebformNominatim.php @@ -160,7 +160,7 @@ public static function processWebformComposite( 'display_name' => t('Display Name'), 'category' => t('Category'), ]; - + $table_options = []; foreach($nominatim_features as $key => $feature) { $table_options[$key+1] = [ From af2cf7223f910a54ba56798cf43faf60606d1b75 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 13:59:00 -0400 Subject: [PATCH 05/54] Remove dpm() from nominatim --- src/Plugin/WebformElement/WebformNominatim.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Plugin/WebformElement/WebformNominatim.php b/src/Plugin/WebformElement/WebformNominatim.php index 27a2bf4..1fdd9c7 100644 --- a/src/Plugin/WebformElement/WebformNominatim.php +++ b/src/Plugin/WebformElement/WebformNominatim.php @@ -56,8 +56,7 @@ protected function formatHtmlItem(array $element, WebformSubmissionInterface $we if ($format == 'map') { $location = $value['location']; - dpm($location); - $center = urlencode($value['location']); + $center = urlencode($value['location']); // To build an OpenStreetmap link we need, e.g // "osm_type": "way", // "osm_id": 270859746, From e80cf47648f415196418b8efbce0bc55a5b4c48e Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 16:27:05 -0400 Subject: [PATCH 06/54] So stupid. I was overwriting my node id.... Don't code when tired. Don't. Don't Also. Removing for now Validation until its really working --- src/Element/WebformPanoramaTour.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 9ba1238..bab0d25 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -106,12 +106,12 @@ public static function processWebformComposite( if ($form_state->getValue(['panorama_tour', 'hotspots_temp', 'ado'])) { - $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); + $othernodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); // We have to do the same when saving the actual Hotspot. $element['hotspots_temp']['entities'] = [ '#type' => 'value', - '#default_value' => $nodeid, + '#default_value' => $othernodeid, ]; } $vb = \Drupal::entityTypeManager()->getViewBuilder( @@ -186,7 +186,7 @@ public static function processWebformComposite( 'view' => [ 'view_name' => 'ado_selection_by_type', 'display_name' => 'entity_reference_solr_2', - 'arguments' => ['Image'] + 'arguments' => ['Image'], ], ], '#attributes' => [ @@ -217,7 +217,7 @@ public static function processWebformComposite( // Since all this subelements are really not triggering that // Lets check if hotspots had errors - if (!empty($form_state->get('hotspot_custom_errors'))) { + /*if (!empty($form_state->get('hotspot_custom_errors'))) { $hotspot_errors = $form_state->get('hotspot_custom_errors'); foreach ($element['hotspots_temp'] as $key => &$field) @@ -227,7 +227,7 @@ public static function processWebformComposite( $field['#attributes']['class'] = ['form-item--error-message', 'alert', 'alert-danger', 'alert-sm']; $field['#prefix'] = $hotspot_errors[$key]; } - } + }*/ $element['hotspots_temp']['node'] = $nodeview; $element['hotspots_temp']['node']['#weight'] = 10; From 0e145cc3d968cbd69f7b72a06c7f8369f107116d Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 3 Oct 2019 16:54:49 -0400 Subject: [PATCH 07/54] Indent and fix url type --- src/Element/WebformPanoramaTour.php | 137 ++++++++++++++-------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index bab0d25..59454a4 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -112,7 +112,7 @@ public static function processWebformComposite( $element['hotspots_temp']['entities'] = [ '#type' => 'value', '#default_value' => $othernodeid, - ]; + ]; } $vb = \Drupal::entityTypeManager()->getViewBuilder( 'node' @@ -127,73 +127,76 @@ public static function processWebformComposite( if ($node) { $nodeview = $vb->view($node, $viewmode); $element['hotspots_temp']['yaw'] = [ - '#title' => t('Hotspot Yaw'), - '#type' => 'textfield', - '#size' => '6', - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'yaw', - ], - ]; + '#title' => t('Hotspot Yaw'), + '#type' => 'textfield', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'yaw', + ], + ]; $element['hotspots_temp']['pitch'] = [ - '#title' => t('Hotspot Pitch'), - '#type' => 'textfield', - '#size' => '6', - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'pitch', - ], - ]; + '#title' => t('Hotspot Pitch'), + '#type' => 'textfield', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'pitch', + ], + ]; $element['hotspots_temp']['type'] = [ - '#title' => t('Hotspot Type'), - '#type' => 'select', - '#options' => [ - 'text' => 'Text', - 'url' => 'An External URL', - 'ado' => 'Another Digital Object', - 'scene' => 'Another Panorama Scene', - ], - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'type', - ], - ]; - $element['hotspots_temp']['label'] = [ - '#title' => t('Label'), - '#type' => 'textfield', - '#size' => '12', - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'label', - ], - ]; - $element['hotspots_temp']['content'] = [ - '#title' => t('Content'), - '#type' => 'textarea', - '#size' => '6', - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'content', + '#title' => t('Hotspot Type'), + '#type' => 'select', + '#options' => [ + 'text' => 'Text', + 'url' => 'An External URL', + 'ado' => 'Another Digital Object', + //'scene' => 'Another Panorama Scene', LOGIC STILL MISSING! + ], + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'type', + ], + ]; + $element['hotspots_temp']['label'] = [ + '#title' => t('Label'), + '#type' => 'textfield', + '#size' => '12', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'label', + ], + ]; + $element['hotspots_temp']['url'] = [ + '#title' => t('url'), + '#description' => t('Only applies to Hotspots of type "An External URL"'), + '#type' => 'url', + '#size' => '12', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'url', + ], + ]; + + $element['hotspots_temp']['ado'] = [ + '#type' => 'entity_autocomplete', + '#title' => t('Select a Digital Object'), + '#description' => t('Only applies to Hotspots of type "Another Digital Object"'), + '#target_type' => 'node', + '#selection_handler' => 'solr_views', + '#selection_settings' => [ + 'view' => [ + 'view_name' => 'ado_selection_by_type', + 'display_name' => 'entity_reference_solr_2', + 'arguments' => ['Image'], ], - ]; - $element['hotspots_temp']['ado'] = [ - '#type' => 'entity_autocomplete', - '#title' => t('Select a Digital Object'), - '#target_type' => 'node', - '#selection_handler' => 'solr_views', - '#selection_settings' => [ - 'view' => [ - 'view_name' => 'ado_selection_by_type', - 'display_name' => 'entity_reference_solr_2', - 'arguments' => ['Image'], - ], - ], - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'content', - ], - ]; + ], + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'ado', + ], + ]; // To make sure menu variables are passed @@ -349,7 +352,7 @@ public static function selectSceneSubmit( if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button' - && empty($form_state->get('hotspot_custom_errors')) + && empty($form_state->get('hotspot_custom_errors')) ) { static::addHotspotSubmit( $form, @@ -402,7 +405,7 @@ public static function addHotspotSubmit( $hotspot->id = $top_element['#name'] . '_' . (count($existing_objects) + 1); if ($hotspot->type == 'url') { - $hotspot->URL = $hotspot->text; + $hotspot->URL = $hotspot->url; $hotspot->type = 'info'; } if ($hotspot->type == 'ado') { @@ -587,7 +590,6 @@ public static function validateWebformComposite( $element['#value'] = NULL; $form_state->setValueForElement($element, NULL); } - } public static function validateHotSpotItems( @@ -612,5 +614,4 @@ public static function validateHotSpotItems( } } - } From b2360e8b1d13b48e5d886a4ef96a5e5bbabeada4 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Wed, 30 Oct 2019 16:58:03 -0400 Subject: [PATCH 08/54] Nominatim reverses Lat/Long to Long/Lat on the API I wish i knew, now i know --- src/Element/WebformNominatim.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Element/WebformNominatim.php b/src/Element/WebformNominatim.php index e81d27d..34769c9 100644 --- a/src/Element/WebformNominatim.php +++ b/src/Element/WebformNominatim.php @@ -411,8 +411,8 @@ public static function nominatimTableSubmit( // Lat and Long are in http://en.wikipedia.org/wiki/en:WGS-84 $values = [ 'value' => $nominatim_features[$selected_option]->label, - 'lat' => $nominatim_features[$selected_option]->value->geometry->coordinates[0], - 'lng' => $nominatim_features[$selected_option]->value->geometry->coordinates[1], + 'lat' => $nominatim_features[$selected_option]->value->geometry->coordinates[1], + 'lng' => $nominatim_features[$selected_option]->value->geometry->coordinates[0], 'category' => $nominatim_features[$selected_option]->value->properties->category, 'display_name' => $nominatim_features[$selected_option]->label, 'osm_id' => $nominatim_features[$selected_option]->value->properties->osm_id, From 0f9fa9fc70b5bf842acec5b61616006d7587ca45 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Sun, 8 Dec 2019 23:46:13 -0500 Subject: [PATCH 09/54] PanoramaTour Builder loading of stored values Almost there. Saving and reading back of a single Panorama is working. Also JS loading of hotspots over existing ones in the Source Entity. Still `@TODO:` - Multiple Scenes loading for editing: Idea would be to pop first scene and do as we do now, store the other scenes in temp storage in the formstate, so we can use a select box to go back and forth. Would work the same to add other scenes - Remove the nasty hotspots_tmp array from form state during valueCallback. Tried and i killed hotspot generation via JS. I just need to cleanup those values when done processing, but not during interaction. I have some ideas, but need to re-recall the order of operation of all the callbacks we have, maybe i can even cleanup this during element validation. But pretty sure its Webform that is blindly pushing all internal elements into the #value key of the top/visible element, breaking the need. Will get there. - Remove all the debug statements! Looks like a Xmas tree now --- js/scenebuilder-pannellum_strawberryfield.js | 37 ++++++++++- src/Element/WebformPanoramaTour.php | 65 +++++++++++++++++--- webform_strawberryfield.module | 2 +- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index 42380ee..189ad36 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -12,6 +12,11 @@ $marker.fadeOut('slow'); } + function loadExistingHotSpots($selector,$hotspots) { + return null; + } + + function mousePosition(event,$container) { var bounds = $container.getBoundingClientRect(); @@ -25,7 +30,9 @@ Drupal.AjaxCommands.prototype.webform_strawberryfield_pannellum_editor_addHotSpot = function(ajax, response, status) { console.log('adding hotspot'); // we need to find the first '.strawberry-panorama-item' id that is child of data-drupal-selector="edit-panorama-tour-hotspots" - $targetScene= $(response.selector).find('.strawberry-panorama-item').attr("id"); + console.log(response.selector); + $targetScene = $(response.selector).find('.strawberry-panorama-item').attr("id"); + console.log($targetScene); $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); $scene.panorama.addHotSpot(response.hotspot); }; @@ -38,9 +45,31 @@ var hotspots = []; // Get the node uuid for this element var element_id = $(this).attr("id"); + console.log('Checking for loaded Panoramatour builder Hotspots'); + console.log(drupalSettings.webform_strawberryfield.WebformPanoramaTour); + for (var parentselector in drupalSettings.webform_strawberryfield.WebformPanoramaTour) { + if (Object.prototype.hasOwnProperty.call(drupalSettings.webform_strawberryfield.WebformPanoramaTour, parentselector)) { + + + $targetScene = $("[data-webform_strawberryfield-selector='" + parentselector + "']").find('.strawberry-panorama-item').attr("id"); + console.log(parentselector); + console.log($targetScene); + $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); + if ((typeof $scene !== 'undefined')) { + drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function(hotspot, key) + { + console.log(hotspot); + console.log($scene); + $scene.panorama.addHotSpot(hotspot); + }); + } + } + } + + // Check if we got some data passed via Drupal settings. if (typeof(drupalSettings.format_strawberryfield.pannellum[element_id]) != 'undefined') { - console.log('initializing Panellum ') + console.log('initializing Panellum Panorama Builder') console.log(Drupal.FormatStrawberryfieldPanoramas.panoramas); Drupal.FormatStrawberryfieldPanoramas.panoramas.forEach(function(item, key) { @@ -48,6 +77,10 @@ var $newmarker = $( "
"); $("#" +element_id+ " .pnlm-ui").append( $newmarker ); + // Feed with existing Hotspots first + + + item.panorama.on('mousedown', function clicker(e) { diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 59454a4..49651eb 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -71,7 +71,26 @@ public static function processWebformComposite( $complete_form ); $element_name = $element['#name']; + // Fetch saved/existing hotspots and transform them into StdClass Objects + + $hotspot_list = $form_state->getValue(['panorama_tour','hotspots']); + if (!empty($hotspot_list) && empty($form_state->get( + $element_name . '-hotspots' + ))) { + $hotspot_list = array_map(function ($item) { + return (object) $item; + }, $hotspot_list); + // Now set our internal temp storage for hotspots + $form_state->set($element_name . '-hotspots', $hotspot_list); + // We really don't know nor should assume what HTML id the loaded scene has + // But we do know who is the parent + // @TODO next iteration, we need this per Scene ID! + } + + + + dpm($form_state); // We need this button to validate. important // NEVER add '#limit_validation_errors' => [], $element['select_button'] = [ @@ -91,14 +110,29 @@ public static function processWebformComposite( '#title' => 'Hotspots for this Scene', '#attributes' => [ 'data-drupal-selector' => $element['#name'] . '-hotspots', + 'data-webform_strawberryfield-selector' => $element['#name'] . '-hotspots', ], '#attached' => [ 'library' => [ + 'format_strawberryfield/pannellum', 'webform_strawberryfield/scenebuilder_pannellum_strawberry', 'core/drupal.dialog.ajax', ], + 'drupalSettings' => [ + 'webform_strawberryfield' => [ + 'WebformPanoramaTour' => [ + $element['#name'] . '-hotspots' => + $hotspot_list + ], + ] + ] ] ]; + + // Note. The $element['hotspots_temp'] 'data-drupal-selector' gets + // Modified during rendering to 'edit-' . $element['#name'] . '-hotspots-temp' + + // @TODO Modal will need to be enabled on the formatter too. if ($form_state->getValue(['panorama_tour', 'scene'])) { @@ -117,6 +151,9 @@ public static function processWebformComposite( $vb = \Drupal::entityTypeManager()->getViewBuilder( 'node' ); // Drupal\node\NodeViewBuilder + //@TODO we need to viewmode this to be configurable! + //@TODO We could also generate a view mode on the fly. + $viewmode = 'digital_object_with_pannellum_panorama_'; $node = \Drupal::entityTypeManager()->getStorage('node')->load( $nodeid @@ -152,7 +189,7 @@ public static function processWebformComposite( 'text' => 'Text', 'url' => 'An External URL', 'ado' => 'Another Digital Object', - //'scene' => 'Another Panorama Scene', LOGIC STILL MISSING! + 'scene' => 'Another Panorama Scene', // Still missing ], '#attributes' => [ 'data-drupal-loaded-node' => $nodeid, @@ -233,6 +270,7 @@ public static function processWebformComposite( }*/ $element['hotspots_temp']['node'] = $nodeview; + $element['hotspots_temp']['node']['#weight'] = 10; $element['hotspots_temp']['added_hotspots'] = [ '#type' => 'details', @@ -250,6 +288,10 @@ public static function processWebformComposite( )) { // Json will be UTF-8 correctly encoded/decoded! $hotspot_list = $form_state->get($element_name . '-hotspots'); + } + + + if (!empty($hotspot_list)) { $table_header = [ 'coordinates' => t('Hotspot Coordinates'), @@ -259,11 +301,14 @@ public static function processWebformComposite( ]; foreach ($hotspot_list as $key => $hotspot) { + if (is_array($hotspot)) { + $hotspot = (object) $hotspot; + } // Key will be in element['#name'].'_'.count($hotspot_list)+1; $table_options[$key] = [ 'coordinates' => $hotspot->yaw . "," . $hotspot->pitch, 'type' => $hotspot->type, - 'label' => isset($hotspot->label) ? $hotspot->label : t('no name'), + 'label' => isset($hotspot->text) ? $hotspot->text : t('no name'), 'operations' => [ 'data' => [ '#type' => 'submit', @@ -291,11 +336,11 @@ public static function processWebformComposite( 'data-drupal-loaded-node-hotspot-table' => $nodeid ], ]; - } } } $element['#element_validate'][] = [static::class, 'validateHotSpotItems']; + dpm($element); return $element; } @@ -328,7 +373,7 @@ public static function selectSceneCallBack( } /** - * Submit Hanlder for the Nominatim reconciliation call. + * Submit Hanlder for the Select Scene Submit call. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -345,7 +390,6 @@ public static function selectSceneSubmit( //error_log(var_export($form_state->getValue(['panorama_tour','scene']),true)); $main_element_parents = array_slice($button['#array_parents'], 0, -1); - // Fetch main element so we can use that webform_id key to store the full output of nominatim $top_element = NestedArray::getValue($form, $main_element_parents); $my_hotspots = $top_element['#name'] . '-hotspots'; @@ -379,7 +423,6 @@ public static function addHotspotSubmit( $button = $form_state->getTriggeringElement(); $main_element_parents = array_slice($button['#array_parents'], 0, -1); - // Fetch main element so we can use that webform_id key to store the full output of nominatim $top_element = NestedArray::getValue($form, $main_element_parents); $my_hotspots_key = $top_element['#name'] . '-hotspots'; @@ -535,10 +578,12 @@ public static function valueCallback( } $to_return = (is_array($input)) ? $input + $default_value : $default_value; + error_log('return of valueCallback'); + error_log(print_r($to_return,true)); + // Remove the hotspots_temp structure from our final value. return $to_return; - // TODO: Change the autogenerated stub } public static function validateWebformComposite( @@ -583,7 +628,8 @@ public static function validateWebformComposite( } } } - + error_log('validateWebformComposite'); + error_log(print_r($value,true)); // Clear empty composites value. if (empty(array_filter($value))) { error_log('is empty'); @@ -599,6 +645,7 @@ public static function validateHotSpotItems( ) { $input = $form_state->getUserInput(); + error_log('validateHotSpotItems'); // Hack. No explanation. if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button') { if (!is_numeric($form_state->getValue( @@ -606,7 +653,7 @@ public static function validateHotSpotItems( // This is needed because we are validating internal subelements during // Hotspot adding, but there is really no real form submit. // We want the form to keep on rebuild. - $form_state->set('hotspot_custom_errors', ['yaw'=>t('Yaw needs to be numeric and not empty')]); + $form_state->set('hotspot_custom_errors', ['yaw'=>t('Yaw needs to be numeric and not empty')]); } else { $form_state->set('hotspot_custom_errors', NULL); diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 054bf55..fe10dc3 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -239,6 +239,6 @@ function template_preprocess_webform_metadata_nominatim(array &$variables) { $variables['fetchbox'] = $variables['element']['value']; $variables['fetchbox_button'] = $variables['element']['nominatim']; $variables['fetchbox_table'] = $variables['element']['feature']; +} -} \ No newline at end of file From 3770c26170504a83b40f0fc4cd457bb2a99681a7 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Mon, 9 Dec 2019 15:25:04 -0500 Subject: [PATCH 10/54] Add Model window on Click action for Hotspots with URLs Since Hotspots are not in the DOM yet, Drupal can not -ajaxify- them manually. This makes use of format_strawberryfield ISSUE-20 branch and simple adds, on hotspot add, Click handlers that open Modals with the content. All the logic is really on Strawberryfield Formatter. --- js/scenebuilder-pannellum_strawberryfield.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index 189ad36..09afe25 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -47,26 +47,26 @@ var element_id = $(this).attr("id"); console.log('Checking for loaded Panoramatour builder Hotspots'); console.log(drupalSettings.webform_strawberryfield.WebformPanoramaTour); + // Feed with existing Hotspots comming from the Webform Element data. for (var parentselector in drupalSettings.webform_strawberryfield.WebformPanoramaTour) { if (Object.prototype.hasOwnProperty.call(drupalSettings.webform_strawberryfield.WebformPanoramaTour, parentselector)) { - - - $targetScene = $("[data-webform_strawberryfield-selector='" + parentselector + "']").find('.strawberry-panorama-item').attr("id"); + $targetScene = $("[data-webform_strawberryfield-selector='" + parentselector + "']").find('.strawberry-panorama-item').attr("id"); console.log(parentselector); console.log($targetScene); $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); if ((typeof $scene !== 'undefined')) { - drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function(hotspot, key) + drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function(hotspotdata, key) { - console.log(hotspot); - console.log($scene); - $scene.panorama.addHotSpot(hotspot); + if (hotspotdata.hasOwnProperty('URL')) { + hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; + hotspot.clickHandlerArgs = hotspotdata.URL; + } + $scene.panorama.addHotSpot(hotspotdata); }); } } } - // Check if we got some data passed via Drupal settings. if (typeof(drupalSettings.format_strawberryfield.pannellum[element_id]) != 'undefined') { console.log('initializing Panellum Panorama Builder') @@ -77,10 +77,6 @@ var $newmarker = $( "
"); $("#" +element_id+ " .pnlm-ui").append( $newmarker ); - // Feed with existing Hotspots first - - - item.panorama.on('mousedown', function clicker(e) { From 13cc02ec29e2f5320864e14cb165d8760c429693 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Mon, 9 Dec 2019 15:32:39 -0500 Subject: [PATCH 11/54] Attach Click handlers also for Newly added via UI hotspots This is done just to unify functionality since this was already working with the normal ajax-ify method of Drupal (since they become part of the DOM while dialog ajax is already loaded and get processed correctly). --- js/scenebuilder-pannellum_strawberryfield.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index 09afe25..e337c6f 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -34,6 +34,13 @@ $targetScene = $(response.selector).find('.strawberry-panorama-item').attr("id"); console.log($targetScene); $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); + // add click handlers for new Hotspots if they have an URL. + // Empty URLs are handled by Drupal.FormatStrawberryfieldhotspotPopUp() + if (response.hotspot.hasOwnProperty('URL')) { + response.hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; + response.hotspot.clickHandlerArgs = response.hotspot.URL; + } + $scene.panorama.addHotSpot(response.hotspot); }; From b1391cdb6deecb648590a42bbcd299e3ec227614 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Mon, 9 Dec 2019 15:38:54 -0500 Subject: [PATCH 12/54] Remove dpm() and remove hotspot attributes No need to committ this sha-na-ni-gans Also, i never liked the idea of having extra data in the hotspot metadata. Now this looks better. --- src/Element/WebformPanoramaTour.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 49651eb..0af3776 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -88,9 +88,6 @@ public static function processWebformComposite( } - - - dpm($form_state); // We need this button to validate. important // NEVER add '#limit_validation_errors' => [], $element['select_button'] = [ @@ -340,7 +337,7 @@ public static function processWebformComposite( } } $element['#element_validate'][] = [static::class, 'validateHotSpotItems']; - dpm($element); + return $element; } @@ -461,14 +458,9 @@ public static function addHotspotSubmit( $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); $url = \Drupal\Core\Url::fromRoute('entity.node.canonical', ['node' => $nodeid],[]); $url = $url->toString(); - $attributes = new \stdClass; - $attributes->{'data-dialog-type'} = 'modal'; - $attributes->class = 'use-ajax'; - $attributes->{'data-dialog-options'} = '{"width":800}'; $hotspot->type = 'info'; - $hotspot->URL = $url; - $hotspot->attributes = $attributes; + $hotspot->URL = $url;; } if ($hotspot->type == 'text') { $hotspot->text = $hotspot->text; From bb0e9487167877ceea2a006b33840d42aeec58df Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 10 Dec 2019 09:53:03 -0500 Subject: [PATCH 13/54] Check if there are hotspots before trying to iterate over them --- js/scenebuilder-pannellum_strawberryfield.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index e337c6f..bcb4858 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -62,14 +62,15 @@ console.log($targetScene); $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); if ((typeof $scene !== 'undefined')) { - drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function(hotspotdata, key) - { - if (hotspotdata.hasOwnProperty('URL')) { - hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; - hotspot.clickHandlerArgs = hotspotdata.URL; - } - $scene.panorama.addHotSpot(hotspotdata); - }); + if (drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector]!== null) { + drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function (hotspotdata, key) { + if (hotspotdata.hasOwnProperty('URL')) { + hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; + hotspot.clickHandlerArgs = hotspotdata.URL; + } + $scene.panorama.addHotSpot(hotspotdata); + }); + } } } } From 447c07b6168fd6081451069b9fa3118c204f31cf Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 10 Dec 2019 11:44:10 -0500 Subject: [PATCH 14/54] Fix my blindness --- js/scenebuilder-pannellum_strawberryfield.js | 31 ++++++++++---------- src/Element/WebformPanoramaTour.php | 23 ++++++++------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index bcb4858..5ce32fc 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -12,11 +12,6 @@ $marker.fadeOut('slow'); } - function loadExistingHotSpots($selector,$hotspots) { - return null; - } - - function mousePosition(event,$container) { var bounds = $container.getBoundingClientRect(); @@ -33,15 +28,18 @@ console.log(response.selector); $targetScene = $(response.selector).find('.strawberry-panorama-item').attr("id"); console.log($targetScene); - $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); - // add click handlers for new Hotspots if they have an URL. - // Empty URLs are handled by Drupal.FormatStrawberryfieldhotspotPopUp() - if (response.hotspot.hasOwnProperty('URL')) { - response.hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; - response.hotspot.clickHandlerArgs = response.hotspot.URL; + if (response.hasOwnProperty('hotspot')) { + $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); + // add click handlers for new Hotspots if they have an URL. + // Empty URLs are handled by Drupal.FormatStrawberryfieldhotspotPopUp() + console.log(response); + if (response.hotspot.hasOwnProperty('URL')) { + response.hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; + response.hotspot.clickHandlerArgs = response.hotspot.URL; + } + + $scene.panorama.addHotSpot(response.hotspot); } - - $scene.panorama.addHotSpot(response.hotspot); }; @@ -64,13 +62,14 @@ if ((typeof $scene !== 'undefined')) { if (drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector]!== null) { drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function (hotspotdata, key) { + console.log(hotspotdata); if (hotspotdata.hasOwnProperty('URL')) { - hotspot.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; - hotspot.clickHandlerArgs = hotspotdata.URL; + hotspotdata.clickHandlerFunc = Drupal.FormatStrawberryfieldhotspotPopUp; + hotspotdata.clickHandlerArgs = hotspotdata.URL; } $scene.panorama.addHotSpot(hotspotdata); }); - } + } } } } diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 0af3776..8fcf77c 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -72,7 +72,7 @@ public static function processWebformComposite( ); $element_name = $element['#name']; // Fetch saved/existing hotspots and transform them into StdClass Objects - + $hotspot_list = []; $hotspot_list = $form_state->getValue(['panorama_tour','hotspots']); if (!empty($hotspot_list) && empty($form_state->get( $element_name . '-hotspots' @@ -87,7 +87,6 @@ public static function processWebformComposite( // @TODO next iteration, we need this per Scene ID! } - // We need this button to validate. important // NEVER add '#limit_validation_errors' => [], $element['select_button'] = [ @@ -211,6 +210,11 @@ public static function processWebformComposite( 'data-drupal-loaded-node' => $nodeid, 'data-drupal-hotspot-property' => 'url', ], + /* '#states' => [ + 'visible' => [ + ':input[name="hotspots_temp[type]"]' => array('value' => 'url'), + ], + ] */ ]; $element['hotspots_temp']['ado'] = [ @@ -309,7 +313,7 @@ public static function processWebformComposite( 'operations' => [ 'data' => [ '#type' => 'submit', - '#value' => t('Edit'), + '#value' => t('Delete'), ], ] ]; @@ -320,15 +324,13 @@ public static function processWebformComposite( '#prefix'=> '
', '#suffix'=> '
', '#title' => t('Hotspots in this scene'), - '#type' => 'tableselect', - '#default_value' => $form_state->get('current-hotspots'), + '#type' => 'table', + //'#default_value' => [], '#name' => $element['#name'] . '_added_hotspots', '#header' => $table_header, - '#options' => $table_options, + '#rows' => $table_options, '#empty' => t('No Hotspots yet for this Scene'), - '#js_select' => TRUE, '#weight' => 11, - '#multiple' => TRUE, '#attributes' => [ 'data-drupal-loaded-node-hotspot-table' => $nodeid ], @@ -370,7 +372,7 @@ public static function selectSceneCallBack( } /** - * Submit Hanlder for the Select Scene Submit call. + * Submit Handler for the Select Scene Submit call. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -401,7 +403,6 @@ public static function selectSceneSubmit( ); } - // Rebuild the form. $form_state->setRebuild(TRUE); } @@ -460,7 +461,7 @@ public static function addHotspotSubmit( $url = $url->toString(); $hotspot->type = 'info'; - $hotspot->URL = $url;; + $hotspot->URL = $url; } if ($hotspot->type == 'text') { $hotspot->text = $hotspot->text; From 9edb44ef1020527f23525d7440acc44377b571f7 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 10 Dec 2019 23:12:14 -0500 Subject: [PATCH 15/54] Moving forward. Reading Multiple Scenes now WIP but will commit. Now, if the #default value is an array of scenes, it takes the first scene and pops it into the #default saving the rest of the scenes in a temporary form state. I also remove the "Multiple" Option for this element, the idea is to have multiple Scenes in a Tour, but not Multiple Tours in a single Object. Too much overhead and adds little to the UX. A lot of new overriden stuff but also some cleanup and less hardcoded options. Not ready, but way better than today in the morning. tomorrow we will have this done! --- src/Element/WebformPanoramaTour.php | 88 +++++++++++++++---- .../WebformElement/WebformPanoramaTour.php | 22 +++++ 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 8fcf77c..606dfe7 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -65,15 +65,25 @@ public static function processWebformComposite( &$complete_form ) { + // Just in case someone wants to trick us + // This is already disabled on the Config Form for the Element + unset($element['#multiple']); + $element = parent::processWebformComposite( $element, $form_state, $complete_form ); + + + $element_name = $element['#name']; // Fetch saved/existing hotspots and transform them into StdClass Objects - $hotspot_list = []; - $hotspot_list = $form_state->getValue(['panorama_tour','hotspots']); + $hotspot_list = $form_state->getValue([$element['#name'],'hotspots']); + $all_scenes_key = $element['#name'] . '-allscenes'; + $all_scenes = $form_state->get($all_scenes_key); + + if (!empty($hotspot_list) && empty($form_state->get( $element_name . '-hotspots' ))) { @@ -130,11 +140,16 @@ public static function processWebformComposite( // @TODO Modal will need to be enabled on the formatter too. + // Some changes here. If we have multiple values we need a new flag + // For the current selected index in the Scene list. + - if ($form_state->getValue(['panorama_tour', 'scene'])) { - $nodeid = $form_state->getValue(['panorama_tour', 'scene']); + if ($form_state->getValue([$element['#name'], 'scene'])) { + $nodeid = $form_state->getValue([$element['#name'], 'scene']); - if ($form_state->getValue(['panorama_tour', 'hotspots_temp', 'ado'])) { + + + if ($form_state->getValue([$element['#name'], 'hotspots_temp', 'ado'])) { $othernodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); // We have to do the same when saving the actual Hotspot. @@ -147,7 +162,7 @@ public static function processWebformComposite( $vb = \Drupal::entityTypeManager()->getViewBuilder( 'node' ); // Drupal\node\NodeViewBuilder - //@TODO we need to viewmode this to be configurable! + //@TODO we need viewmode to be configurable! //@TODO We could also generate a view mode on the fly. $viewmode = 'digital_object_with_pannellum_panorama_'; @@ -158,6 +173,24 @@ public static function processWebformComposite( if ($node) { + // If we have multiple Scenes,deal with it. + if (!empty($all_scenes) && is_array($all_scenes)) { + dpm($all_scenes); + foreach($all_scenes as $scene) { + $all_scenes_nodeids[] = $scene['scene']; + } + $all_scene_nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($all_scenes_nodeids); + foreach ($all_scene_nodes as $entity) { + $options[$entity->id()] = $entity->label(); + } + + $element['hotspots_temp']['editing_scene'] = [ + '#title' => t('Select Scene'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $node->id() + ]; + } $nodeview = $vb->view($node, $viewmode); $element['hotspots_temp']['yaw'] = [ '#title' => t('Hotspot Yaw'), @@ -385,8 +418,6 @@ public static function selectSceneSubmit( error_log('selectSceneSubmit'); $input = $form_state->getUserInput(); // Hack. No explanation. - - //error_log(var_export($form_state->getValue(['panorama_tour','scene']),true)); $main_element_parents = array_slice($button['#array_parents'], 0, -1); $top_element = NestedArray::getValue($form, $main_element_parents); @@ -394,7 +425,7 @@ public static function selectSceneSubmit( if (isset($input['_triggering_element_name']) && - $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button' + $input['_triggering_element_name'] == $top_element['#name'].'_addhotspot_button' && empty($form_state->get('hotspot_custom_errors')) ) { static::addHotspotSubmit( @@ -451,7 +482,7 @@ public static function addHotspotSubmit( } if ($hotspot->type == 'ado') { $nodeid = $form_state->getValue( - ['panorama_tour', 'hotspots_temp', 'ado'] + [$top_element['#name'], 'hotspots_temp', 'ado'] ); // Now the fun part. Since this autocomplete is not part of the process // chain we never get the value transformed into id. @@ -470,7 +501,7 @@ public static function addHotspotSubmit( error_log(var_export($hotspot,true)); $existing_objects[$hotspot->id] = $hotspot; - error_log($my_hotspots_key); + // @TODO make sure people don't add twice the same coordinates! $form_state->set($my_hotspots_key, $existing_objects); @@ -537,7 +568,6 @@ public static function valueCallback( $composite_elements = WebformElementHelper::getFlattened( $composite_elements ); - // Get default value for inputs. $default_value = []; foreach ($composite_elements as $composite_key => $composite_element) { @@ -545,7 +575,6 @@ public static function valueCallback( $composite_element ); - error_log($composite_key); if ($element_plugin->isInput($composite_element)) { $default_value[$composite_key] = ''; } @@ -553,6 +582,9 @@ public static function valueCallback( // We need to move our internal hotspot form state var into // the value field and back. $my_hotspots_key = $element['#name'] . '-hotspots'; + // Used for multiscenes + $all_scenes_key = $element['#name'] . '-allscenes'; + $existing_objects = $form_state->get($my_hotspots_key) ? (array) $form_state->get( $my_hotspots_key ) : []; @@ -560,20 +592,37 @@ public static function valueCallback( foreach ($list_of_hotspots as $hotspot) { $default_value['hotspots'][] = (array) $hotspot; } - + error_log('valueCallback triggering element is'); + error_log(print_r($form_state->getTriggeringElement()['#name'], true)); if ($input === FALSE) { + // OK, first load or webform auto processing, no input. + + error_log('valueCallback input empty'); if (empty($element['#default_value']) || !is_array( $element['#default_value'] )) { $element['#default_value'] = []; + } else { + // Check if Default value is an array of scenes or just a single scene + if (isset($element['#default_value']['scene'])) { + error_log('single scene'); + // Means single scene. + } elseif (isset($element['#default_value'][0]['scene'])) { + error_log('multi scene'); + $form_state->set($all_scenes_key, $element['#default_value']); + $element['#default_value'] = $element['#default_value'][0]; + + // Multi scene! + } + } + return $element['#default_value'] + $default_value; } $to_return = (is_array($input)) ? $input + $default_value : $default_value; error_log('return of valueCallback'); error_log(print_r($to_return,true)); - // Remove the hotspots_temp structure from our final value. return $to_return; @@ -622,7 +671,7 @@ public static function validateWebformComposite( } } error_log('validateWebformComposite'); - error_log(print_r($value,true)); + // Clear empty composites value. if (empty(array_filter($value))) { error_log('is empty'); @@ -640,7 +689,7 @@ public static function validateHotSpotItems( $input = $form_state->getUserInput(); error_log('validateHotSpotItems'); // Hack. No explanation. - if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == 'panorama_tour_addhotspot_button') { + if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == $element['#name'].'_addhotspot_button') { if (!is_numeric($form_state->getValue( [$element['#name'], 'hotspots_temp', 'yaw']))) { // This is needed because we are validating internal subelements during @@ -651,6 +700,11 @@ public static function validateHotSpotItems( else { $form_state->set('hotspot_custom_errors', NULL); } + } else { + // means running without pressing the buttom + // We remove hotspots_temp from the final submission + error_log('unsetting hotspots_temp'); + $form_state->unsetValue([$element['#name'], 'hotspots_temp']); } } diff --git a/src/Plugin/WebformElement/WebformPanoramaTour.php b/src/Plugin/WebformElement/WebformPanoramaTour.php index b786fad..5760061 100644 --- a/src/Plugin/WebformElement/WebformPanoramaTour.php +++ b/src/Plugin/WebformElement/WebformPanoramaTour.php @@ -8,6 +8,7 @@ namespace Drupal\webform_strawberryfield\Plugin\WebformElement; +use Drupal\Core\Form\FormStateInterface; use Drupal\webform\WebformSubmissionInterface; use Drupal\webform\Plugin\WebformElement\WebformCompositeBase; @@ -22,6 +23,7 @@ * multiline = TRUE, * composite = TRUE, * states_wrapper = TRUE, + * default_key = "panorama_tour", * ) */ class WebformPanoramaTour extends WebformCompositeBase { @@ -58,4 +60,24 @@ protected function formatTextItemValue(array $element, WebformSubmissionInterfac return $lines; } + public function supportsMultipleValues() { + // Make sure people can not change this value. + return FALSE; + } + + public function getDefaultProperties() { + $defaults = parent::getDefaultProperties(); + unset($defaults['multiple']); + return $defaults; + } + + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + $form['element']['multiple']['#disabled'] = TRUE; + $form['element']['multiple']['#description'] = '' . $this->t('You can only build one Tour with this Webform Element.But it supports multiple Scenes') . ''; + // Disable Multiple Elements option + return $form; + } + + } From b9eee850b71cb6a87558e4b9f1c4dddb2a7b8ab1 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 12 Dec 2019 00:58:35 -0500 Subject: [PATCH 16/54] Just template stubs but there Will use this to move hotspot part of the form to one side of the Webform element so make this "decent" --- .../webform-metadata-panoramatour.html.twig | 15 +++++++++++ webform_strawberryfield.module | 25 ++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 templates/webform-metadata-panoramatour.html.twig diff --git a/templates/webform-metadata-panoramatour.html.twig b/templates/webform-metadata-panoramatour.html.twig new file mode 100644 index 0000000..11d4102 --- /dev/null +++ b/templates/webform-metadata-panoramatour.html.twig @@ -0,0 +1,15 @@ +{# +/** + * @file + * Default theme implementation of a Nominatim metadata composite webform element. + * + * Available variables: + * - content: The Panoramatour webform element to be output. + * + * @see template_preprocess_webform_metadata_panoramatour() + * + * @ingroup themeable + */ +#} + +{{ content }} \ No newline at end of file diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index fe10dc3..18e4e2d 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -215,10 +215,13 @@ function webform_strawberryfield_form_node_form_alter(&$form, FormStateInterface */ function webform_strawberryfield_theme() { $info = [ - 'webform_metadata_nominatim' => [ - 'render element' => 'element', - ] - ]; + 'webform_metadata_nominatim' => [ + 'render element' => 'element', + ], + 'webform_metadata_panoramatour' => [ + 'render element' => 'element', + ] + ]; return $info; } @@ -241,4 +244,18 @@ function template_preprocess_webform_metadata_nominatim(array &$variables) { $variables['fetchbox_table'] = $variables['element']['feature']; } +/** + * Prepares variables for location composite element templates. + * + * Default template: webform-metadata-panoramatour.html.twig. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + */ +function template_preprocess_webform_metadata_panoramatour(array &$variables) { + $variables['content'] = $variables['element']; + +} + From 21b425457e6dfd326272810ac6c4d22f3592d5c7 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 12 Dec 2019 01:00:09 -0500 Subject: [PATCH 17/54] Stronger JS Make sure we can iterate over the thing there. Also, push back to HTML the hfov value. Used to define the initial horizontal field of view. We can do that now! --- js/scenebuilder-pannellum_strawberryfield.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/scenebuilder-pannellum_strawberryfield.js b/js/scenebuilder-pannellum_strawberryfield.js index 5ce32fc..e8591a9 100644 --- a/js/scenebuilder-pannellum_strawberryfield.js +++ b/js/scenebuilder-pannellum_strawberryfield.js @@ -60,7 +60,9 @@ console.log($targetScene); $scene = Drupal.FormatStrawberryfieldPanoramas.panoramas.get($targetScene); if ((typeof $scene !== 'undefined')) { - if (drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector]!== null) { + if (drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector]!== null + && typeof drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector][Symbol.iterator] === 'function' + ) { drupalSettings.webform_strawberryfield.WebformPanoramaTour[parentselector].forEach(function (hotspotdata, key) { console.log(hotspotdata); if (hotspotdata.hasOwnProperty('URL')) { @@ -88,12 +90,14 @@ item.panorama.on('mousedown', function clicker(e) { $hotspot_cors = item.panorama.mouseEventToCoords(e); + console.log(item.panorama.getHfov()); var $jquerycontainer = $(item.panorama.getContainer()); $button_container = $jquerycontainer.closest("[data-drupal-selector='edit-panorama-tour-hotspots-temp']"); $button_container.find("[ data-drupal-hotspot-property='yaw']").val($hotspot_cors[1]); $button_container.find("[ data-drupal-hotspot-property='pitch']").val($hotspot_cors[0]); + $button_container.find("[ data-drupal-hotspot-property='hfov']").val(item.panorama.getHfov()); marker(e,$newmarker,item.panorama.getContainer()); From 5f7ab89bc6c2137d815b1f8cfacb815e430d0447 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 12 Dec 2019 01:17:06 -0500 Subject: [PATCH 18/54] Gosh. Total refactor. Super de-factor Still need to remove error_logs all over the place but now it actuall allows to change between Scenes, and add dynamically hotspots, swap back and forth and only saves what we need, more and not less. Also works when starting from scratch. Well, looking good and not failing after so many tests... Pretty tired, will just continue first hour in the morning. This is pretty sweet and i feel kinda the largest dev for realz in this panorama thingy we are into --- src/Element/WebformPanoramaTour.php | 632 +++++++++++++----- .../WebformElement/WebformPanoramaTour.php | 4 +- 2 files changed, 463 insertions(+), 173 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 606dfe7..6139578 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -8,6 +8,7 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Ajax\InvokeCommand; +use Drupal\Core\Ajax\SettingsCommand; use Drupal\webform_strawberryfield\Ajax\AddHotSpotCommand; use Drupal\webform\Utility\WebformElementHelper; use Drupal\Core\Entity\Element\EntityAutocomplete; @@ -27,6 +28,7 @@ public function getInfo() { //@TODO add an extra option to define auth_type. //@TODO expose as an select option inside \Drupal\webform_strawberryfield\Plugin\WebformElement\WebformGetty $info = parent::getInfo(); + $info['#theme'] = 'webform_metadata_panoramatour'; return $info; } @@ -37,22 +39,7 @@ public static function getCompositeElements(array $element) { $elements = []; $class = '\Drupal\webform_strawberryfield\Element\WebformPanoramaTour'; //@TODO all this settings need to be exposed to the Webform element. - $elements['scene'] = [ - '#type' => 'entity_autocomplete', - '#title' => t('Select a Scene'), - '#target_type' => 'node', - '#selection_handler' => 'solr_views', - '#selection_settings' => [ - 'view' => [ - 'view_name' => 'ado_selection_by_type', - 'display_name' => 'entity_reference_solr_2', - 'arguments' => ['Panorama'] - ], - ], - ]; - $element['hotspots'] = [ - '#type' => 'value' - ]; + return $elements; } @@ -76,37 +63,65 @@ public static function processWebformComposite( ); - $element_name = $element['#name']; - // Fetch saved/existing hotspots and transform them into StdClass Objects - $hotspot_list = $form_state->getValue([$element['#name'],'hotspots']); + $all_scenes_key = $element['#name'] . '-allscenes'; $all_scenes = $form_state->get($all_scenes_key); + // If no initial 'scene' value, use first key of the allmighty + // Full array. + $sceneid = NULL; + + if (!$form_state->getValue([$element['#name'], 'scene']) && !empty($all_scenes)) { + $scene = reset($all_scenes); + $sceneid = $scene['scene']; + + } else { + + $sceneid = $form_state->getValue([$element['#name'], 'scene']); + $form_state->setValue([$element_name, 'scene'],$sceneid); + } + + // Fetch saved/existing hotspots and transform them into StdClass Objects + $hotspot_list = []; + - if (!empty($hotspot_list) && empty($form_state->get( - $element_name . '-hotspots' - ))) { - $hotspot_list = array_map(function ($item) { - return (object) $item; - }, $hotspot_list); - // Now set our internal temp storage for hotspots - $form_state->set($element_name . '-hotspots', $hotspot_list); - // We really don't know nor should assume what HTML id the loaded scene has - // But we do know who is the parent - // @TODO next iteration, we need this per Scene ID! + foreach ($all_scenes as $scene) { + // Fetching from full array + error_log('fetching from full array'); + if ($scene['scene'] == $sceneid) { + $hotspot_list = $scene['hotspots']; + } } // We need this button to validate. important // NEVER add '#limit_validation_errors' => [], + $element['scene'] = [ + '#type' => 'entity_autocomplete', + '#title' => t('Select a Scene'), + '#target_type' => 'node', + '#selection_handler' => 'solr_views', + '#selection_settings' => [ + 'view' => [ + 'view_name' => 'ado_selection_by_type', + 'display_name' => 'entity_reference_solr_2', + 'arguments' => ['Panorama'] + ], + ], + ]; + $element['hotspots'] = [ + '#type' => 'value' + ]; + + $element['select_button'] = [ '#title' => 'Select Scene', '#type' => 'submit', '#value' => t('Select Scene'), '#name' => $element['#name'] . '_select_button', - '#submit' => [[get_called_class(), 'selectSceneSubmit']], + '#submit' => [[static::class, 'selectSceneSubmit']], '#ajax' => [ - 'callback' => [get_called_class(), 'selectSceneCallBack'], + 'callback' => [static::class, 'selectSceneCallBack'], ], '#button_type' => 'default', '#visible' => 'true', @@ -138,16 +153,16 @@ public static function processWebformComposite( // Note. The $element['hotspots_temp'] 'data-drupal-selector' gets // Modified during rendering to 'edit-' . $element['#name'] . '-hotspots-temp' - // @TODO Modal will need to be enabled on the formatter too. // Some changes here. If we have multiple values we need a new flag // For the current selected index in the Scene list. + //dpm($form_state->getValues()); - if ($form_state->getValue([$element['#name'], 'scene'])) { - $nodeid = $form_state->getValue([$element['#name'], 'scene']); + if ($sceneid) { + $nodeid = $sceneid; if ($form_state->getValue([$element['#name'], 'hotspots_temp', 'ado'])) { @@ -175,22 +190,36 @@ public static function processWebformComposite( if ($node) { // If we have multiple Scenes,deal with it. if (!empty($all_scenes) && is_array($all_scenes)) { - dpm($all_scenes); - foreach($all_scenes as $scene) { - $all_scenes_nodeids[] = $scene['scene']; + //dpm($all_scenes); + foreach($all_scenes as $key => $scene) { + $all_scenes_nodeids[$key] = $scene['scene']; } $all_scene_nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($all_scenes_nodeids); foreach ($all_scene_nodes as $entity) { $options[$entity->id()] = $entity->label(); } - - $element['hotspots_temp']['editing_scene'] = [ - '#title' => t('Select Scene'), + // If we have loaded values, replace autocomplete with this + $element['scene'] = [ + '#title' => t('Editing Scene'), '#type' => 'select', '#options' => $options, - '#default_value' => $node->id() - ]; + '#default_value' => $node->id(), + '#ajax' => [ + 'callback' => [static::class, 'changeSceneCallBack'], + 'event' => 'change', + ], + '#submit' => [[static::class, 'changeSceneSubmit']], + ]; } + $element['hotspots_temp']['label'] = [ + '#title' => t('The label to display on mouse over'), + '#type' => 'textfield', + '#size' => '12', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'label', + ], + ]; $nodeview = $vb->view($node, $viewmode); $element['hotspots_temp']['yaw'] = [ '#title' => t('Hotspot Yaw'), @@ -201,6 +230,15 @@ public static function processWebformComposite( 'data-drupal-hotspot-property' => 'yaw', ], ]; + $element['hotspots_temp']['hfov'] = [ + '#title' => t('hfov'), + '#type' => 'textfield', + '#size' => '6', + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'hfov', + ], + ]; $element['hotspots_temp']['pitch'] = [ '#title' => t('Hotspot Pitch'), '#type' => 'textfield', @@ -225,35 +263,24 @@ public static function processWebformComposite( 'data-drupal-hotspot-property' => 'type', ], ]; - $element['hotspots_temp']['label'] = [ - '#title' => t('Label'), - '#type' => 'textfield', - '#size' => '12', - '#attributes' => [ - 'data-drupal-loaded-node' => $nodeid, - 'data-drupal-hotspot-property' => 'label', - ], - ]; $element['hotspots_temp']['url'] = [ - '#title' => t('url'), - '#description' => t('Only applies to Hotspots of type "An External URL"'), + '#title' => t('URL to open on Hotspot Click'), '#type' => 'url', '#size' => '12', '#attributes' => [ 'data-drupal-loaded-node' => $nodeid, 'data-drupal-hotspot-property' => 'url', ], - /* '#states' => [ + '#states' => [ 'visible' => [ - ':input[name="hotspots_temp[type]"]' => array('value' => 'url'), + ':input[name="'.$element['#name'].'[hotspots_temp][type]"]' => array('value' => 'url'), ], - ] */ + ] ]; - + //@TODO expose arguments to the Webform element config UI. $element['hotspots_temp']['ado'] = [ '#type' => 'entity_autocomplete', - '#title' => t('Select a Digital Object'), - '#description' => t('Only applies to Hotspots of type "Another Digital Object"'), + '#title' => t('Select a Digital Object that will open on Hotspot Click'), '#target_type' => 'node', '#selection_handler' => 'solr_views', '#selection_settings' => [ @@ -267,6 +294,11 @@ public static function processWebformComposite( 'data-drupal-loaded-node' => $nodeid, 'data-drupal-hotspot-property' => 'ado', ], + '#states' => [ + 'visible' => [ + ':input[name="'.$element['#name'].'[hotspots_temp][type]"]' => array('value' => 'ado'), + ], + ] ]; @@ -285,23 +317,22 @@ public static function processWebformComposite( '#button_type' => 'default', '#visible' => 'true', '#limit_validation_errors' => FALSE - //'#limit_validation_errors' => [$limit] ]; - // Make sure the top element gets our validation - // Since all this subelements are really not triggering that - // Lets check if hotspots had errors - /*if (!empty($form_state->get('hotspot_custom_errors'))) { - $hotspot_errors = $form_state->get('hotspot_custom_errors'); - foreach ($element['hotspots_temp'] as $key => &$field) + $element['hotspots_temp']['set_sceneorientation'] = [ + '#type' => 'submit', + '#value' => t('Set Initial Scene Orientation'), + '#name' => $element['#name'] . '_setsceneorientation_button', + '#submit' => [[static::class, 'setSceneOrientation']], + '#ajax' => [ + 'callback' => [static::class, 'setSceneOrientationCallBack'], + ], + '#button_type' => 'default', + '#visible' => 'true', + '#limit_validation_errors' => FALSE + ]; - if (array_key_exists($key, $hotspot_errors)) { - error_log('found errors'); - $field['#attributes']['class'] = ['form-item--error-message', 'alert', 'alert-danger', 'alert-sm']; - $field['#prefix'] = $hotspot_errors[$key]; - } - }*/ $element['hotspots_temp']['node'] = $nodeview; @@ -314,17 +345,6 @@ public static function processWebformComposite( '#weight' => 11, ]; - // Get the hotspot submitted data - if ($form_state->isRebuilding() && !empty( - $form_state->get( - $element_name . '-hotspots' - ) - )) { - // Json will be UTF-8 correctly encoded/decoded! - $hotspot_list = $form_state->get($element_name . '-hotspots'); - } - - if (!empty($hotspot_list)) { $table_header = [ @@ -404,6 +424,99 @@ public static function selectSceneCallBack( return $response; } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return \Drupal\Core\Ajax\AjaxResponse + */ + public static function changeSceneCallBack( + array $form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + $element = NestedArray::getValue( + $form, + array_slice($button['#array_parents'], 0, -1) + ); + error_log('changeSceneCallBack'); + $response = new AjaxResponse(); + $element_name = $element['#name']; + $data_selector = $element['hotspots_temp']['#attributes']['data-webform_strawberryfield-selector']; + $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; + + + // Now update the JS settings + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $existing_objects = []; + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $current_scene) { + $existing_objects = $scene['hotspots']; + break; + } + } + + $settingsclear = [ + 'webform_strawberryfield' => [ + 'WebformPanoramaTour' => [ + $element_name . '-hotspots' => + NULL + ], + ] + ]; + $settings = [ + 'webform_strawberryfield' => [ + 'WebformPanoramaTour' => [ + $element_name . '-hotspots' => + $existing_objects + ], + ] + ]; + error_log('attaching replacement Drupal settings for the viewer'); + error_log(print_r($settings,true)); + // Why twice? well because merge is deep merge. Gosh JS! + // And merge = FALSE clears even my brain settings... + $response->addCommand(new SettingsCommand($settingsclear, TRUE)); + $response->addCommand(new SettingsCommand($settings, TRUE)); + + } + // And now replace the container + $response->addCommand( + new ReplaceCommand( + '[data-webform_strawberryfield-selector="' . $data_selector . '"]', + $element['hotspots_temp'] + ) + ); + return $response; + } + + /** + * Submit Handler for the Select Scene Submit call. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function changeSceneSubmit( + array &$form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + error_log('changeSceneSubmit'); + $input = $form_state->getUserInput(); + // Hack. No explanation. + $main_element_parents = array_slice($button['#array_parents'], 0, -1); + + $top_element = NestedArray::getValue($form, $main_element_parents); + error_log('triggering element name'. $input['_triggering_element_name']); + + // Rebuild the form. + $form_state->setRebuild(TRUE); + } + /** * Submit Handler for the Select Scene Submit call. * @@ -421,8 +534,8 @@ public static function selectSceneSubmit( $main_element_parents = array_slice($button['#array_parents'], 0, -1); $top_element = NestedArray::getValue($form, $main_element_parents); - $my_hotspots = $top_element['#name'] . '-hotspots'; + error_log('triggering element name'. $input['_triggering_element_name']); if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == $top_element['#name'].'_addhotspot_button' @@ -433,6 +546,36 @@ public static function selectSceneSubmit( $form_state ); } + elseif (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == $top_element['#name'].'_setsceneorientation_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::setSceneOrientation( + $form, + $form_state + ); + } + else { + $current_scene = $form_state->getValue([$top_element['#name'], 'scene']); + $alreadythere = FALSE; + if ($current_scene) { + $all_scenes_key = $top_element['#name'] . '-allscenes'; + $all_scenes = $form_state->get($all_scenes_key); + foreach ($all_scenes as $scene) { + if ($scene['scene'] == $current_scene) { + $alreadythere = TRUE; + break; + } + } + if (!$alreadythere) { + $all_scenes[] = [ + 'scene' => $current_scene, + 'hotspots' => [], + ]; + $form_state->set($all_scenes_key,$all_scenes); + } + } + } // Rebuild the form. $form_state->setRebuild(TRUE); @@ -448,65 +591,97 @@ public static function addHotspotSubmit( array &$form, FormStateInterface $form_state ) { + error_log('addHotspotSubmit'); $button = $form_state->getTriggeringElement(); - $main_element_parents = array_slice($button['#array_parents'], 0, -1); + $hot_spot_values_parents = array_slice($button['#parents'], 0, -1); + + $element_name = $hot_spot_values_parents[0]; + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $scene_key = 0; + $existing_objects = []; + error_log(print_r($form_state->getValues(), TRUE)); + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $form_state->getValue( + [$element_name, 'scene'] + )) { + $scene_key = $key; + $existing_objects = $scene['hotspots']; + break; + } + } - $top_element = NestedArray::getValue($form, $main_element_parents); + $hotspot = new \stdClass; - $my_hotspots_key = $top_element['#name'] . '-hotspots'; + $hotspot->pitch = $form_state->getValue( + [$element_name, 'hotspots_temp', 'pitch'] + ); + $hotspot->yaw = $form_state->getValue( + [$element_name, 'hotspots_temp', 'yaw'] + ); + $hotspot->type = $form_state->getValue( + [$element_name, 'hotspots_temp', 'type'] + ); + $hotspot->text = $form_state->getValue( + [$element_name, 'hotspots_temp', 'label'] + ); - $existing_objects = $form_state->get($my_hotspots_key) ? $form_state->get( - $my_hotspots_key - ) : []; + $hotspot->id = $element_name . '_' . $current_scene . '_' .(count($existing_objects) + 1); + if ($hotspot->type == 'url') { + $hotspot->URL = $hotspot->url; + $hotspot->type = 'info'; + } + if ($hotspot->type == 'ado') { + $nodeid = $form_state->getValue( + [$element_name, 'hotspots_temp', 'ado'] + ); + // Now the fun part. Since this autocomplete is not part of the process + // chain we never get the value transformed into id. + // So. we do it directly + $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput( + $nodeid + ); + $url = \Drupal\Core\Url::fromRoute( + 'entity.node.canonical', + ['node' => $nodeid], + [] + ); + $url = $url->toString(); + $hotspot->type = 'info'; + + $hotspot->URL = $url; + } + if ($hotspot->type == 'text') { + $hotspot->text = $hotspot->text; + $hotspot->type = 'info'; + } - $hotspot = new \stdClass; - $hotspot->pitch = $form_state->getValue( - [$top_element['#name'], 'hotspots_temp', 'pitch'] - ); - $hotspot->yaw = $form_state->getValue( - [$top_element['#name'], 'hotspots_temp', 'yaw'] - ); - $hotspot->type = $form_state->getValue( - [$top_element['#name'], 'hotspots_temp', 'type'] - ); - $hotspot->text = $form_state->getValue( - [$top_element['#name'], 'hotspots_temp', 'label'] - ); + $existing_objects[] = $hotspot; - $hotspot->id = $top_element['#name'] . '_' . (count($existing_objects) + 1); - if ($hotspot->type == 'url') { - $hotspot->URL = $hotspot->url; - $hotspot->type = 'info'; - } - if ($hotspot->type == 'ado') { - $nodeid = $form_state->getValue( - [$top_element['#name'], 'hotspots_temp', 'ado'] - ); - // Now the fun part. Since this autocomplete is not part of the process - // chain we never get the value transformed into id. - // So. we do it directly - $nodeid = EntityAutocomplete::extractEntityIdFromAutocompleteInput($nodeid); - $url = \Drupal\Core\Url::fromRoute('entity.node.canonical', ['node' => $nodeid],[]); - $url = $url->toString(); - $hotspot->type = 'info'; - - $hotspot->URL = $url; - } - if ($hotspot->type == 'text') { - $hotspot->text = $hotspot->text; - $hotspot->type = 'info'; - } - error_log(var_export($hotspot,true)); + // @TODO make sure people don't add twice the same coordinates! - $existing_objects[$hotspot->id] = $hotspot; + // Push hotspots there - // @TODO make sure people don't add twice the same coordinates! - $form_state->set($my_hotspots_key, $existing_objects); + $allscenes[$scene_key]['hotspots'] = $existing_objects; + $form_state->set($all_scenes_key, $allscenes); + error_log('done updating original array'); + + } - return; + + // This is strange but needed. + // If we are creating a new panorama, addhotspot submit button + // uses the scene submit (because this is nested) and hidden on initialize + // but if we start with data, its called directly. + // So we have the setRebuild on the parent caller + // \Drupal\webform_strawberryfield\Element\WebformPanoramaTour::selectSceneSubmit + // and also here. all good + $form_state->setRebuild(TRUE); } /** @@ -526,15 +701,25 @@ public static function addHotSpotCallBack( array_slice($button['#array_parents'], 0, -2) ); error_log('addHotSpotCallBack'); - + $element_name = $element['#name']; $response = new AjaxResponse(); $data_selector = $element['hotspots_temp']['added_hotspots']['#attributes']['data-drupal-loaded-node-hotspot-table']; - $my_hotspots_key = $element['#name'] . '-hotspots'; - - - $existing_objects = $form_state->get($my_hotspots_key) ? $form_state->get( - $my_hotspots_key - ) : []; + $existing_object = []; + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $scene_key = 0; + $existing_objects = []; + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $form_state->getValue( + [$element_name, 'scene'] + )) { + $scene_key = $key; + $existing_objects = $scene['hotspots']; + } + } + } if (count($existing_objects) > 0) { $data_selector2 = $element['hotspots_temp']['#attributes']['data-drupal-selector']; @@ -556,6 +741,97 @@ public static function addHotSpotCallBack( return $response; } + /** + * Submit Handler for adding a Hotspot. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function setSceneOrientation( + array &$form, + FormStateInterface $form_state + ) { + + error_log('setSceneOrientation'); + $button = $form_state->getTriggeringElement(); + $hot_spot_values_parents = array_slice($button['#parents'], 0, -1); + + $element_name = $hot_spot_values_parents[0]; + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $scene_key = 0; + $existing_objects = []; + error_log(print_r($form_state->getValues(), TRUE)); + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $form_state->getValue( + [$element_name, 'scene'] + )) { + $scene_key = $key; + break; + } + } + + $allscenes[$scene_key]['hfov'] =$form_state->getValue( + [$element_name, 'hotspots_temp', 'hfov']); + $allscenes[$scene_key]['pitch'] =$form_state->getValue( + [$element_name, 'hotspots_temp', 'pitch']); + $allscenes[$scene_key]['yaw'] =$form_state->getValue( + [$element_name, 'hotspots_temp', 'yaw']); + + $form_state->set($all_scenes_key, $allscenes); + error_log('done updating original array'); + + } + + + + // This is strange but needed. + // If we are creating a new panorama, addhotspot submit button + // uses the scene submit (because this is nested) and hidden on initialize + // but if we start with data, its called directly. + // So we have the setRebuild on the parent caller + // \Drupal\webform_strawberryfield\Element\WebformPanoramaTour::selectSceneSubmit + // and also here. all good + $form_state->setRebuild(TRUE); + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return \Drupal\Core\Ajax\AjaxResponse + */ + public static function setSceneOrientationCallBack( + array $form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + $element = NestedArray::getValue( + $form, + array_slice($button['#array_parents'], 0, -1) + ); + error_log('setSceneOrientationCallBack'); + $response = new AjaxResponse(); + $element_name = $element['#name']; + $data_selector = $element['hotspots_temp']['#attributes']['data-webform_strawberryfield-selector']; + $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; + + // And now replace the container + $response->addCommand( + new ReplaceCommand( + '[data-webform_strawberryfield-selector="' . $data_selector . '"]', + $element['hotspots_temp'] + ) + ); + return $response; + } + + + + + public static function valueCallback( &$element, $input, @@ -579,48 +855,69 @@ public static function valueCallback( $default_value[$composite_key] = ''; } } - // We need to move our internal hotspot form state var into - // the value field and back. - $my_hotspots_key = $element['#name'] . '-hotspots'; + // Used for multiscenes $all_scenes_key = $element['#name'] . '-allscenes'; + $current_scene_key = $element['#name'] . '-currentscene'; - $existing_objects = $form_state->get($my_hotspots_key) ? (array) $form_state->get( - $my_hotspots_key - ) : []; - $list_of_hotspots = array_values($existing_objects); - foreach ($list_of_hotspots as $hotspot) { - $default_value['hotspots'][] = (array) $hotspot; + if ($form_state->get($all_scenes_key)) { + // Merging with saved $all_scenes + error_log('Merge default values with whatever is that we got before'); + $default_value = array_merge($form_state->get($all_scenes_key), $default_value); + $default_value['scene'] = $form_state->get($current_scene_key); } - error_log('valueCallback triggering element is'); - error_log(print_r($form_state->getTriggeringElement()['#name'], true)); - if ($input === FALSE) { + + if ($input !== FALSE && $input !== NULL) { + error_log('we have input'); + error_log(print_r($input,true)); + } + + + + if (($input === FALSE) && !$form_state->get($all_scenes_key)) { // OK, first load or webform auto processing, no input. - error_log('valueCallback input empty'); + error_log('valueCallback input empty and no all scenes yet'); if (empty($element['#default_value']) || !is_array( $element['#default_value'] )) { $element['#default_value'] = []; } else { + // Check if Default value is an array of scenes or just a single scene if (isset($element['#default_value']['scene'])) { - error_log('single scene'); + error_log('Got a single scene'); + // cast into an array + $element['#default_value'] = [$element['#default_value']]; + // This will be our current scene + $element['#default_value']['scene'] = $element['#default_value'][0]['scene']; // Means single scene. + $form_state->set($all_scenes_key, $element['#default_value']); + $form_state->set($current_scene_key, $element['#default_value']['scene']); + } elseif (isset($element['#default_value'][0]['scene'])) { error_log('multi scene'); $form_state->set($all_scenes_key, $element['#default_value']); - $element['#default_value'] = $element['#default_value'][0]; - + $element['#default_value']['scene'] = $element['#default_value'][0]['scene']; + $form_state->set($current_scene_key, $element['#default_value']['scene']); // Multi scene! } + foreach ($element['#default_value'] as $scene) { + // $scene could be a god damn button which is a translateable + if (is_array($scene) && $scene['scene'] == $element['#default_value']['scene']) { + $element['#default_value']['hotspots'] = $scene['hotspots']; + break; + } + } } return $element['#default_value'] + $default_value; } $to_return = (is_array($input)) ? $input + $default_value : $default_value; + error_log('what is in the element default_value before valuecallback return'); + error_log(print_r(array_keys($element['#default_value']),true)); error_log('return of valueCallback'); error_log(print_r($to_return,true)); @@ -685,26 +982,19 @@ public static function validateHotSpotItems( FormStateInterface $form_state, &$complete_form ) { - - $input = $form_state->getUserInput(); error_log('validateHotSpotItems'); - // Hack. No explanation. - if (isset($input['_triggering_element_name']) && $input['_triggering_element_name'] == $element['#name'].'_addhotspot_button') { - if (!is_numeric($form_state->getValue( - [$element['#name'], 'hotspots_temp', 'yaw']))) { - // This is needed because we are validating internal subelements during - // Hotspot adding, but there is really no real form submit. - // We want the form to keep on rebuild. - $form_state->set('hotspot_custom_errors', ['yaw'=>t('Yaw needs to be numeric and not empty')]); + + if ($triggering = $form_state->getTriggeringElement()) { + if (reset($triggering['#parents']) == $element['#name']) { + error_log('triggered by our Tour builder'); + // Means it was something inside the button } else { - $form_state->set('hotspot_custom_errors', NULL); + error_log('Clear our redundant values'); + $form_state->unsetValue([$element['#name'], 'scene']); + $form_state->unsetValue([$element['#name'], 'hotspots']); + $form_state->unsetValue([$element['#name'], 'hotspots_temp']); } - } else { - // means running without pressing the buttom - // We remove hotspots_temp from the final submission - error_log('unsetting hotspots_temp'); - $form_state->unsetValue([$element['#name'], 'hotspots_temp']); } } diff --git a/src/Plugin/WebformElement/WebformPanoramaTour.php b/src/Plugin/WebformElement/WebformPanoramaTour.php index 5760061..a80951a 100644 --- a/src/Plugin/WebformElement/WebformPanoramaTour.php +++ b/src/Plugin/WebformElement/WebformPanoramaTour.php @@ -49,13 +49,13 @@ protected function formatTextItemValue(array $element, WebformSubmissionInterfac $value = $this->getValue($element, $webform_submission, $options); $lines = []; - if (!empty($value['scene'])) { + /*if (!empty($value['scene'])) { $lines[] = $value['scene']; } if (!empty($value['hotspots'])) { $lines[] = $value['hotspots']; } - + */ return $lines; } From 2b09078a7d230e777822e770bb8cee4c4b5f76ea Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 12 Dec 2019 15:33:13 -0500 Subject: [PATCH 19/54] Delete, add Scenes. Better and better Large app. But working --- src/Element/WebformPanoramaTour.php | 230 +++++++++++++++--- .../webform-metadata-panoramatour.html.twig | 6 +- webform_strawberryfield.module | 74 +++++- 3 files changed, 278 insertions(+), 32 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 6139578..e7eeeb1 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -62,7 +62,6 @@ public static function processWebformComposite( $complete_form ); - $element_name = $element['#name']; $all_scenes_key = $element['#name'] . '-allscenes'; @@ -128,7 +127,6 @@ public static function processWebformComposite( ]; $element['hotspots_temp'] = [ '#type' => 'fieldset', - '#title' => 'Hotspots for this Scene', '#attributes' => [ 'data-drupal-selector' => $element['#name'] . '-hotspots', 'data-webform_strawberryfield-selector' => $element['#name'] . '-hotspots', @@ -207,10 +205,32 @@ public static function processWebformComposite( '#ajax' => [ 'callback' => [static::class, 'changeSceneCallBack'], 'event' => 'change', - ], + ], '#submit' => [[static::class, 'changeSceneSubmit']], - ]; + ]; + // Hide the select button, at least visually + // because it defines the main submit + $element['select_button'] = [ + '#attributes' => ['class' => ['js-hide']] + ]; + } + + $nodeview = $vb->view($node, $viewmode); + $element['hotspots_temp']['node'] = $nodeview; + + $element['hotspots_temp']['node']['#weight'] = 10; + $element['hotspots_temp']['added_hotspots'] = [ + '#type' => 'details', + '#attributes' => [ + 'data-drupal-loaded-node-hotspot-table' => $nodeid + ], + '#weight' => 11, + ]; + + + + $element['hotspots_temp']['label'] = [ '#title' => t('The label to display on mouse over'), '#type' => 'textfield', @@ -220,7 +240,7 @@ public static function processWebformComposite( 'data-drupal-hotspot-property' => 'label', ], ]; - $nodeview = $vb->view($node, $viewmode); + $element['hotspots_temp']['yaw'] = [ '#title' => t('Hotspot Yaw'), '#type' => 'textfield', @@ -333,6 +353,19 @@ public static function processWebformComposite( '#limit_validation_errors' => FALSE ]; + $element['hotspots_temp']['delete_scene'] = [ + '#type' => 'submit', + '#value' => t('Delete this Scene'), + '#name' => $element['#name'] . '_deletescene_button', + '#submit' => [[static::class, 'deleteScene']], + '#ajax' => [ + 'callback' => [static::class, 'deleteSceneCallBack'], + ], + '#button_type' => 'default', + '#visible' => 'true', + '#limit_validation_errors' => FALSE + ]; + $element['hotspots_temp']['node'] = $nodeview; @@ -424,6 +457,28 @@ public static function selectSceneCallBack( return $response; } + /** + * Submit Handler for the Select Scene Submit call. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function changeSceneSubmit( + array &$form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + error_log('changeSceneSubmit'); + $input = $form_state->getUserInput(); + // Hack. No explanation. + $main_element_parents = array_slice($button['#array_parents'], 0, -1); + + $top_element = NestedArray::getValue($form, $main_element_parents); + error_log('triggering element name'. $input['_triggering_element_name']); + + // Rebuild the form. + $form_state->setRebuild(TRUE); + } /** * @param array $form @@ -494,29 +549,6 @@ public static function changeSceneCallBack( return $response; } - /** - * Submit Handler for the Select Scene Submit call. - * - * @param array $form - * @param \Drupal\Core\Form\FormStateInterface $form_state - */ - public static function changeSceneSubmit( - array &$form, - FormStateInterface $form_state - ) { - $button = $form_state->getTriggeringElement(); - error_log('changeSceneSubmit'); - $input = $form_state->getUserInput(); - // Hack. No explanation. - $main_element_parents = array_slice($button['#array_parents'], 0, -1); - - $top_element = NestedArray::getValue($form, $main_element_parents); - error_log('triggering element name'. $input['_triggering_element_name']); - - // Rebuild the form. - $form_state->setRebuild(TRUE); - } - /** * Submit Handler for the Select Scene Submit call. * @@ -555,6 +587,15 @@ public static function selectSceneSubmit( $form_state ); } + elseif (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == $top_element['#name'].'_deletescene_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::deleteScene( + $form, + $form_state + ); + } else { $current_scene = $form_state->getValue([$top_element['#name'], 'scene']); $alreadythere = FALSE; @@ -742,7 +783,7 @@ public static function addHotSpotCallBack( } /** - * Submit Handler for adding a Hotspot. + * Submit Handler for Settin the Scene Orientation. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -828,8 +869,139 @@ public static function setSceneOrientationCallBack( return $response; } + /** + * Submit Handler for Settin the Scene Orientation. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function deleteScene( + array &$form, + FormStateInterface $form_state + ) { + + error_log('deleteScene'); + $button = $form_state->getTriggeringElement(); + $hot_spot_values_parents = array_slice($button['#parents'], 0, -1); + + $element_name = $hot_spot_values_parents[0]; + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $scene_key = 0; + $existing_objects = []; + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $form_state->getValue( + [$element_name, 'scene'] + )) { + $scene_key = $key; + break; + } + } + unset($allscenes[$scene_key]); + // which will reorder keys + $allscenes = array_merge($allscenes); + // remove any hotspot pointing to this scene + foreach ($allscenes as &$scene) { + $scene['hotspots'] = array_filter($scene['hotspots'], function($e) use($scene_key) { + $e = (array) $e; + return (!isset($e["sceneId"]) || $e["sceneId"]!=$scene_key); + }); + } + $form_state->set($all_scenes_key, $allscenes); + if (!empty($allscenes)) { + $firstscene = reset($allscenes); + $firstsceneid = $firstscene['scene']; + $form_state->setValue([$element_name, 'scene'],$firstsceneid); + } else { + $form_state->setValue([$element_name, 'scene'],NULL); + } + \Drupal::messenger()->addMessage(t('The Scene was removed')); + error_log('done removing Scene and hotspots'); + } + // This is strange but needed. + // If we are creating a new panorama, addhotspot submit button + // uses the scene submit (because this is nested) and hidden on initialize + // but if we start with data, its called directly. + // So we have the setRebuild on the parent caller + // \Drupal\webform_strawberryfield\Element\WebformPanoramaTour::selectSceneSubmit + // and also here. all good + $form_state->setRebuild(TRUE); + } + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return \Drupal\Core\Ajax\AjaxResponse + */ + public static function deleteSceneCallBack( + array $form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + $element = NestedArray::getValue( + $form, + array_slice($button['#array_parents'], 0, -2) + ); + error_log('deleteSceneCallBack'); + $response = new AjaxResponse(); + $element_name = $element['#name']; + $data_selector = $element['#attributes']['data-drupal-selector']; + $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; + error_log('data selector'); + error_log($data_selector); + + $settingsclear = [ + 'webform_strawberryfield' => [ + 'WebformPanoramaTour' => [ + $element_name . '-hotspots' => + NULL + ], + ] + ]; + $response->addCommand(new SettingsCommand($settingsclear, TRUE)); + + // Now update the JS settings + if ($form_state->getValue([$element_name, 'scene'])) { + $all_scenes_key = $element_name . '-allscenes'; + $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'scene']); + $existing_objects = []; + foreach ($allscenes as $key => &$scene) { + if ($scene['scene'] == $current_scene) { + $existing_objects = $scene['hotspots']; + break; + } + } + + $settings = [ + 'webform_strawberryfield' => [ + 'WebformPanoramaTour' => [ + $element_name . '-hotspots' => + $existing_objects + ], + ] + ]; + error_log('attaching replacement Drupal settings for the viewer after delete'); + error_log(print_r($settings, TRUE)); + // Why twice? well because merge is deep merge. Gosh JS! + // And merge = FALSE clears even my brain settings... + $response->addCommand(new SettingsCommand($settings, TRUE)); + } + + // And now replace the container + $response->addCommand( + new ReplaceCommand( + '[data-drupal-selector="' . $data_selector . '"]', + $element + ) + ); + + return $response; + } public static function valueCallback( diff --git a/templates/webform-metadata-panoramatour.html.twig b/templates/webform-metadata-panoramatour.html.twig index 11d4102..2d35edb 100644 --- a/templates/webform-metadata-panoramatour.html.twig +++ b/templates/webform-metadata-panoramatour.html.twig @@ -5,11 +5,13 @@ * * Available variables: * - content: The Panoramatour webform element to be output. - * + * - sceneselect: The select form/select dropdown user to choose a Panorama Object + * - hotspotui: All the Inputs used on the UI + * - hotspotbuttons: All the buttons used for doing stuff on loaded scenes + * - node: the actual loaded Panorama Viewer * @see template_preprocess_webform_metadata_panoramatour() * * @ingroup themeable */ #} - {{ content }} \ No newline at end of file diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 18e4e2d..7999537 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -236,6 +236,7 @@ function webform_strawberryfield_theme() { */ function template_preprocess_webform_metadata_nominatim(array &$variables) { $variables['content'] = $variables['element']; + unset($variables['content']['value']); unset($variables['content']['nominatim']); unset($variables['content']['feature']); @@ -254,8 +255,79 @@ function template_preprocess_webform_metadata_nominatim(array &$variables) { * - element: An associative array containing the properties of the element. */ function template_preprocess_webform_metadata_panoramatour(array &$variables) { - $variables['content'] = $variables['element']; + $variables['content'] = $variables['element']; + /*unset($variables['element']); + /* + "scene" => array:31 [â–¶] + "hotspots" => array:19 [â–¶] + "select_button" => array:32 [â–¶] + "hotspots_temp" => array:33 [â–¼ + "#type" => "fieldset" + "#title" => "Hotspots for this Scene" + "#attributes" => array:2 [â–¶] + "#attached" => array:2 [â–¶] + "label" => array:28 [â–¶] + "yaw" => array:28 [â–¶] + "hfov" => array:28 [â–¶] + "pitch" => array:28 [â–¶] + "type" => array:26 [â–¶] + "url" => array:29 [â–¶] + "ado" => & array:40 [â–¶] + "add_hotspot" => array:31 [â–¶] + "set_sceneorientation" => array:31 [â–¶] + "delete_scene" => array:31 [â–¶] + "node" => array:17 [â–¶] + "added_hotspots" => array:34 [â–¶] + "#process" => array:3 [â–¶] + "#pre_render" => array:1 [â–¶] + "#value" => null + "#theme_wrappers" => array:1 [â–¶] + "#defaults_loaded" => true + "#tree" => true + "#parents" => array:2 [â–¶] + "#array_parents" => array:4 [â–¶] + "#weight" => 0.003 + "#processed" => true + "#required" => false + "#title_display" => "before" + "#description_display" => "after" + "#errors" => null + "#id" => "edit-panorama-tour-hotspots-temp--WgAHmLMT7gw" + "#groups" => &2 array:10 [â–¶] + "#ajax_processed" => false + ] + + $variables['sceneselect'] = $variables['content']['scene']; + $variables['scenebutton'] = $variables['content']['select_button']; + $variables['hotspotui'] = [ + 'label' => $variables['content']['hotspots_temp']['label'], + 'url' => $variables['content']['hotspots_temp']['ado'], + 'ado' => $variables['content']['hotspots_temp']['ado'], + 'type' => $variables['content']['hotspots_temp']['type'], + 'yaw' => $variables['content']['hotspots_temp']['yaw'], + 'pitch' => $variables['content']['hotspots_temp']['pitch'], + 'hfov' => $variables['content']['hotspots_temp']['hfov'], + ]; + $variables['hotspotbuttons'] = [ + 'add_hotspot' => $variables['content']['hotspots_temp']['add_hotspot'], + 'set_sceneorientation' => $variables['content']['hotspots_temp']['set_sceneorientation'], + 'delete_scene' => $variables['content']['hotspots_temp']['delete_scene'], + ]; + $variables['node'] = $variables['content']['hotspots_temp']['node']; + unset($variables['content']['scene']); + unset($variables['content']['select_button']); + unset($variables['content']['hotspots_temp']['pitch']); + unset($variables['content']['hotspots_temp']['label']); + unset($variables['content']['hotspots_temp']['yaw']); + unset($variables['content']['hotspots_temp']['url']); + unset($variables['content']['hotspots_temp']['ado']); + unset($variables['content']['hotspots_temp']['type']); + unset($variables['content']['hotspots_temp']['add_hotspot']); + unset($variables['content']['hotspots_temp']['set_sceneorientation']); + unset($variables['content']['hotspots_temp']['delete_scene']); + unset($variables['content']['hotspots_temp']['node']); + */ } From 3ef163fe8a38dc9b206268ba2a4058f8f7ccf1ef Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 12 Dec 2019 15:50:22 -0500 Subject: [PATCH 20/54] Fix empty values for new DO. --- src/Element/WebformPanoramaTour.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index e7eeeb1..727a61b 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -65,7 +65,7 @@ public static function processWebformComposite( $element_name = $element['#name']; $all_scenes_key = $element['#name'] . '-allscenes'; - $all_scenes = $form_state->get($all_scenes_key); + $all_scenes = $form_state->get($all_scenes_key) ? $form_state->get($all_scenes_key) : []; // If no initial 'scene' value, use first key of the allmighty // Full array. From 1531337535d3f18efe3560d93c46d2bb2b1b3480 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Fri, 13 Dec 2019 00:02:38 -0500 Subject: [PATCH 21/54] Some Zuper naive styling Not my taste, basically doing prefix and suffix. But no time today to do it proper prerender or preprocess callbacks. --- src/Element/WebformPanoramaTour.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 727a61b..77bab9b 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -145,7 +145,7 @@ public static function processWebformComposite( ], ] ] - ] + ], ]; // Note. The $element['hotspots_temp'] 'data-drupal-selector' gets @@ -219,11 +219,14 @@ public static function processWebformComposite( $nodeview = $vb->view($node, $viewmode); $element['hotspots_temp']['node'] = $nodeview; - $element['hotspots_temp']['node']['#weight'] = 10; + $element['hotspots_temp']['node']['#weight'] = -10; + $element['hotspots_temp']['node']['#prefix'] = '
'; + $element['hotspots_temp']['node']['#attributes']['class'] = ['col-8']; $element['hotspots_temp']['added_hotspots'] = [ '#type' => 'details', '#attributes' => [ - 'data-drupal-loaded-node-hotspot-table' => $nodeid + 'data-drupal-loaded-node-hotspot-table' => $nodeid, + 'class' => ['row'], ], '#weight' => 11, ]; @@ -232,6 +235,7 @@ public static function processWebformComposite( $element['hotspots_temp']['label'] = [ + '#prefix' => '
', '#title' => t('The label to display on mouse over'), '#type' => 'textfield', '#size' => '12', @@ -327,6 +331,7 @@ public static function processWebformComposite( $limit = array_merge($element['#parents'], ['hotspots_temp']); $element['hotspots_temp']['add_hotspot'] = [ + '#prefix' => '
', '#type' => 'submit', '#value' => t('Add Hotspot'), '#name' => $element['#name'] . '_addhotspot_button', @@ -350,7 +355,7 @@ public static function processWebformComposite( ], '#button_type' => 'default', '#visible' => 'true', - '#limit_validation_errors' => FALSE + '#limit_validation_errors' => FALSE, ]; $element['hotspots_temp']['delete_scene'] = [ @@ -363,13 +368,11 @@ public static function processWebformComposite( ], '#button_type' => 'default', '#visible' => 'true', - '#limit_validation_errors' => FALSE + '#limit_validation_errors' => FALSE, + '#suffix' => '
' // Closes the btn group, row and the col ]; - $element['hotspots_temp']['node'] = $nodeview; - - $element['hotspots_temp']['node']['#weight'] = 10; $element['hotspots_temp']['added_hotspots'] = [ '#type' => 'details', '#attributes' => [ @@ -411,7 +414,6 @@ public static function processWebformComposite( '#suffix'=> '', '#title' => t('Hotspots in this scene'), '#type' => 'table', - //'#default_value' => [], '#name' => $element['#name'] . '_added_hotspots', '#header' => $table_header, '#rows' => $table_options, @@ -447,7 +449,6 @@ public static function selectSceneCallBack( $response = new AjaxResponse(); $data_selector = $element['hotspots_temp']['#attributes']['data-drupal-selector']; - $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; $response->addCommand( new ReplaceCommand( '[data-drupal-selector="' . $data_selector . '"]', From 874005bb2e6b1ad84b2eb7faf0c2d3778f6d4941 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Fri, 13 Dec 2019 01:36:26 -0500 Subject: [PATCH 22/54] Scene Hotspots, new Add Scene button, Select existing scenes and cleanup Also should fix the JS that is hiding the search button on the top (weirddd) --- js/hidenodeaction-webform_strawberryfield.js | 8 +- src/Element/WebformPanoramaTour.php | 208 +++++++++++-------- 2 files changed, 131 insertions(+), 85 deletions(-) diff --git a/js/hidenodeaction-webform_strawberryfield.js b/js/hidenodeaction-webform_strawberryfield.js index 95ef0aa..dd5b9e1 100644 --- a/js/hidenodeaction-webform_strawberryfield.js +++ b/js/hidenodeaction-webform_strawberryfield.js @@ -21,9 +21,9 @@ if ($('.path-node fieldset[data-strawberryfield-selector="strawberry-webform-widget"]').length) { if ($('.webform-confirmation',context).length) { // Exclude webform own edit-actions containter - $('.path-node div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); + $('.path-node .node-form div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); } else if ($('div.field--widget-strawberryfield-webform-inline-widget').length) { - $('.path-node div[data-drupal-selector="edit-actions"]').not('.webform-actions').hide(); + $('.path-node .node-form div[data-drupal-selector="edit-actions"]').not('.webform-actions').hide(); } @@ -31,14 +31,14 @@ if ($moderationstate.length) { var $select = $moderationstate.on('change', function () { - $('.path-node div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); + $('.path-node .node-form div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); }); } var $nodetitle = $('input[data-drupal-selector="edit-title-0-value"]', context).once('show-hide-actions'); if ($nodetitle.length) { var $select = $nodetitle.on('input', function () { - $('.path-node div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); + $('.path-node .node-form div[data-drupal-selector="edit-actions"]').not('.webform-actions').show(); }); } diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 77bab9b..4e4c47f 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -126,6 +126,7 @@ public static function processWebformComposite( '#visible' => 'true', ]; $element['hotspots_temp'] = [ + '#weight' => 4, '#type' => 'fieldset', '#attributes' => [ 'data-drupal-selector' => $element['#name'] . '-hotspots', @@ -196,6 +197,13 @@ public static function processWebformComposite( foreach ($all_scene_nodes as $entity) { $options[$entity->id()] = $entity->label(); } + // If we have loaded values, create a copy of select but just to add + //new ones + $element['newscene'] = $element['scene']; + $element['newscene']['#weight'] = 2; + $element['newselect_button'] = $element['select_button']; + $element['newselect_button']['#weight'] = 3; + $element['newselect_button']['#value'] = t('Add another Scene'); // If we have loaded values, replace autocomplete with this $element['scene'] = [ '#title' => t('Editing Scene'), @@ -301,6 +309,33 @@ public static function processWebformComposite( ], ] ]; + $optionscenes = $options; + // Remove from linkable scenes the current loaded one + unset($optionscenes[$nodeid]); + if (!empty($optionscenes)) { + $element['hotspots_temp']['adoscene'] = [ + '#title' => t('Linkable Scenes'), + '#type' => 'select', + '#options' => $optionscenes, + '#states' => [ + 'visible' => [ + ':input[name="'.$element['#name'].'[hotspots_temp][type]"]' => array('value' => 'scene'), + ], + ], + '#attributes' => [ + 'data-drupal-loaded-node' => $nodeid, + 'data-drupal-hotspot-property' => 'type', + ], + ]; + } else { + $element['hotspots_temp']['adoscene']['#type'] = 'markup'; + $element['hotspots_temp']['adoscene']['#markup'] = t('You need to add another scene to make an multi scene Hotspot!'); + $element['hotspots_temp']['adoscene']['#states'] = [ + 'visible' => [ + ':input[name="'.$element['#name'].'[hotspots_temp][type]"]' => array('value' => 'scene'), + ], + ]; + } //@TODO expose arguments to the Webform element config UI. $element['hotspots_temp']['ado'] = [ '#type' => 'entity_autocomplete', @@ -369,6 +404,9 @@ public static function processWebformComposite( '#button_type' => 'default', '#visible' => 'true', '#limit_validation_errors' => FALSE, + '#attributes' => [ + 'class' => ['btn-warning'] + ], '#suffix' => '' // Closes the btn group, row and the col ]; @@ -431,6 +469,79 @@ public static function processWebformComposite( return $element; } + /** + * Submit Handler for the Select Scene Submit call. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public static function selectSceneSubmit( + array &$form, + FormStateInterface $form_state + ) { + $button = $form_state->getTriggeringElement(); + error_log('selectSceneSubmit'); + $input = $form_state->getUserInput(); + // Hack. No explanation. + $main_element_parents = array_slice($button['#array_parents'], 0, -1); + + $top_element = NestedArray::getValue($form, $main_element_parents); + + if (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == $top_element['#name'].'_addhotspot_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::addHotspotSubmit( + $form, + $form_state + ); + } + elseif (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == $top_element['#name'].'_setsceneorientation_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::setSceneOrientation( + $form, + $form_state + ); + } + elseif (isset($input['_triggering_element_name']) && + $input['_triggering_element_name'] == $top_element['#name'].'_deletescene_button' + && empty($form_state->get('hotspot_custom_errors')) + ) { + static::deleteScene( + $form, + $form_state + ); + } + else { + // Try first with the new scene button, if no value, go back to scene button. + $current_scene = $form_state->getValue([$top_element['#name'], 'newscene']); + $current_scene = $current_scene ? $current_scene : $form_state->getValue([$top_element['#name'], 'scene']); + $alreadythere = FALSE; + if ($current_scene) { + $all_scenes_key = $top_element['#name'] . '-allscenes'; + $all_scenes = $form_state->get($all_scenes_key); + foreach ($all_scenes as $scene) { + if ($scene['scene'] == $current_scene) { + $alreadythere = TRUE; + break; + } + } + if (!$alreadythere) { + $all_scenes[] = [ + 'scene' => $current_scene, + 'hotspots' => [], + ]; + $form_state->set($all_scenes_key,$all_scenes); + } + } + } + + // Rebuild the form. + $form_state->setRebuild(TRUE); + } + /** * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -444,15 +555,15 @@ public static function selectSceneCallBack( $button = $form_state->getTriggeringElement(); $element = NestedArray::getValue( $form, - array_slice($button['#array_parents'], 0, -1) + array_slice($button['#array_parents'], 0, -2) ); $response = new AjaxResponse(); - $data_selector = $element['hotspots_temp']['#attributes']['data-drupal-selector']; + $data_selector = $element['#attributes']['data-drupal-selector']; $response->addCommand( new ReplaceCommand( '[data-drupal-selector="' . $data_selector . '"]', - $element['hotspots_temp'] + $element ) ); return $response; @@ -500,7 +611,6 @@ public static function changeSceneCallBack( $response = new AjaxResponse(); $element_name = $element['#name']; $data_selector = $element['hotspots_temp']['#attributes']['data-webform_strawberryfield-selector']; - $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; // Now update the JS settings @@ -550,79 +660,6 @@ public static function changeSceneCallBack( return $response; } - /** - * Submit Handler for the Select Scene Submit call. - * - * @param array $form - * @param \Drupal\Core\Form\FormStateInterface $form_state - */ - public static function selectSceneSubmit( - array &$form, - FormStateInterface $form_state - ) { - $button = $form_state->getTriggeringElement(); - error_log('selectSceneSubmit'); - $input = $form_state->getUserInput(); - // Hack. No explanation. - $main_element_parents = array_slice($button['#array_parents'], 0, -1); - - $top_element = NestedArray::getValue($form, $main_element_parents); - - error_log('triggering element name'. $input['_triggering_element_name']); - - if (isset($input['_triggering_element_name']) && - $input['_triggering_element_name'] == $top_element['#name'].'_addhotspot_button' - && empty($form_state->get('hotspot_custom_errors')) - ) { - static::addHotspotSubmit( - $form, - $form_state - ); - } - elseif (isset($input['_triggering_element_name']) && - $input['_triggering_element_name'] == $top_element['#name'].'_setsceneorientation_button' - && empty($form_state->get('hotspot_custom_errors')) - ) { - static::setSceneOrientation( - $form, - $form_state - ); - } - elseif (isset($input['_triggering_element_name']) && - $input['_triggering_element_name'] == $top_element['#name'].'_deletescene_button' - && empty($form_state->get('hotspot_custom_errors')) - ) { - static::deleteScene( - $form, - $form_state - ); - } - else { - $current_scene = $form_state->getValue([$top_element['#name'], 'scene']); - $alreadythere = FALSE; - if ($current_scene) { - $all_scenes_key = $top_element['#name'] . '-allscenes'; - $all_scenes = $form_state->get($all_scenes_key); - foreach ($all_scenes as $scene) { - if ($scene['scene'] == $current_scene) { - $alreadythere = TRUE; - break; - } - } - if (!$alreadythere) { - $all_scenes[] = [ - 'scene' => $current_scene, - 'hotspots' => [], - ]; - $form_state->set($all_scenes_key,$all_scenes); - } - } - } - - // Rebuild the form. - $form_state->setRebuild(TRUE); - } - /** * Submit Handler for adding a Hotspot. * @@ -696,12 +733,22 @@ public static function addHotspotSubmit( $hotspot->URL = $url; } + + if ($hotspot->type == 'scene') { + $sceneid = $form_state->getValue( + [$element_name, 'hotspots_temp', 'adoscene'] + ); + + $hotspot->type = 'scene'; + $hotspot->sceneId = "{$sceneid}"; + } + if ($hotspot->type == 'text') { $hotspot->text = $hotspot->text; $hotspot->type = 'info'; } - + error_log(var_export($hotspot,true)); $existing_objects[] = $hotspot; // @TODO make sure people don't add twice the same coordinates! @@ -951,9 +998,7 @@ public static function deleteSceneCallBack( $response = new AjaxResponse(); $element_name = $element['#name']; $data_selector = $element['#attributes']['data-drupal-selector']; - $element['hotspots_temp']['#title'] = 'Hotspots processed via ajax for this Scene'; - error_log('data selector'); - error_log($data_selector); + $settingsclear = [ 'webform_strawberryfield' => [ @@ -1165,6 +1210,7 @@ public static function validateHotSpotItems( else { error_log('Clear our redundant values'); $form_state->unsetValue([$element['#name'], 'scene']); + $form_state->unsetValue([$element['#name'], 'newscene']); $form_state->unsetValue([$element['#name'], 'hotspots']); $form_state->unsetValue([$element['#name'], 'hotspots_temp']); } From 72336090c12cf154e77aed08e581a550b13eb3b3 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Fri, 13 Dec 2019 02:24:52 -0500 Subject: [PATCH 23/54] Better checks for broken scenes --- src/Element/WebformPanoramaTour.php | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 4e4c47f..192ae89 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -88,7 +88,7 @@ public static function processWebformComposite( foreach ($all_scenes as $scene) { // Fetching from full array error_log('fetching from full array'); - if ($scene['scene'] == $sceneid) { + if (isset($scene['scene']) && $scene['scene'] == $sceneid) { $hotspot_list = $scene['hotspots']; } } @@ -185,13 +185,15 @@ public static function processWebformComposite( ); $errors = []; - + $all_scenes_nodeids = []; if ($node) { // If we have multiple Scenes,deal with it. if (!empty($all_scenes) && is_array($all_scenes)) { //dpm($all_scenes); foreach($all_scenes as $key => $scene) { - $all_scenes_nodeids[$key] = $scene['scene']; + if (isset($scene['scene'])) { + $all_scenes_nodeids[$key] = $scene['scene']; + } } $all_scene_nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($all_scenes_nodeids); foreach ($all_scene_nodes as $entity) { @@ -312,7 +314,7 @@ public static function processWebformComposite( $optionscenes = $options; // Remove from linkable scenes the current loaded one unset($optionscenes[$nodeid]); - if (!empty($optionscenes)) { + $element['hotspots_temp']['adoscene'] = [ '#title' => t('Linkable Scenes'), '#type' => 'select', @@ -327,15 +329,7 @@ public static function processWebformComposite( 'data-drupal-hotspot-property' => 'type', ], ]; - } else { - $element['hotspots_temp']['adoscene']['#type'] = 'markup'; - $element['hotspots_temp']['adoscene']['#markup'] = t('You need to add another scene to make an multi scene Hotspot!'); - $element['hotspots_temp']['adoscene']['#states'] = [ - 'visible' => [ - ':input[name="'.$element['#name'].'[hotspots_temp][type]"]' => array('value' => 'scene'), - ], - ]; - } + //@TODO expose arguments to the Webform element config UI. $element['hotspots_temp']['ado'] = [ '#type' => 'entity_autocomplete', @@ -523,7 +517,7 @@ public static function selectSceneSubmit( $all_scenes_key = $top_element['#name'] . '-allscenes'; $all_scenes = $form_state->get($all_scenes_key); foreach ($all_scenes as $scene) { - if ($scene['scene'] == $current_scene) { + if (isset($scene['scene']) && $scene['scene'] == $current_scene) { $alreadythere = TRUE; break; } @@ -620,7 +614,7 @@ public static function changeSceneCallBack( $current_scene = $form_state->getValue([$element_name, 'scene']); $existing_objects = []; foreach ($allscenes as $key => &$scene) { - if ($scene['scene'] == $current_scene) { + if (isset($scene['scene']) && $scene['scene'] == $current_scene) { $existing_objects = $scene['hotspots']; break; } @@ -684,7 +678,7 @@ public static function addHotspotSubmit( $existing_objects = []; error_log(print_r($form_state->getValues(), TRUE)); foreach ($allscenes as $key => &$scene) { - if ($scene['scene'] == $form_state->getValue( + if (isset($scene['scene']) && $scene['scene'] == $form_state->getValue( [$element_name, 'scene'] )) { $scene_key = $key; @@ -1213,6 +1207,7 @@ public static function validateHotSpotItems( $form_state->unsetValue([$element['#name'], 'newscene']); $form_state->unsetValue([$element['#name'], 'hotspots']); $form_state->unsetValue([$element['#name'], 'hotspots_temp']); + $form_state->unsetValue([$element['#name'], 'newselect_button']); } } } From eaaa503fe073b94b443c3ac97f486653b089c8b8 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Wed, 22 Jan 2020 09:03:13 -0500 Subject: [PATCH 24/54] Reformat strawberryFieldharvester.php Webform handler No Code change, just reformatting, visually, the code. --- .../strawberryFieldharvester.php | 238 ++++++++++++------ 1 file changed, 165 insertions(+), 73 deletions(-) diff --git a/src/Plugin/WebformHandler/strawberryFieldharvester.php b/src/Plugin/WebformHandler/strawberryFieldharvester.php index 55d2d02..e780577 100644 --- a/src/Plugin/WebformHandler/strawberryFieldharvester.php +++ b/src/Plugin/WebformHandler/strawberryFieldharvester.php @@ -37,8 +37,8 @@ * submission = \Drupal\webform\Plugin\WebformHandlerInterface::SUBMISSION_OPTIONAL, * ) */ -class strawberryFieldharvester extends WebformHandlerBase -{ +class strawberryFieldharvester extends WebformHandlerBase { + /** * @var bool */ @@ -55,6 +55,7 @@ class strawberryFieldharvester extends WebformHandlerBase * @var \Drupal\webform\WebformTokenManagerInterface */ protected $tokenManager; + /** * @var \Drupal\Core\File\FileSystemInterface */ @@ -64,10 +65,12 @@ class strawberryFieldharvester extends WebformHandlerBase * @var \Drupal\file\FileUsage\FileUsageInterface */ protected $fileUsage; + /** * @var \Drupal\Component\Transliteration\TransliterationInterface */ protected $transliteration; + /** * @var \Drupal\Core\Language\LanguageManagerInterface */ @@ -83,6 +86,7 @@ class strawberryFieldharvester extends WebformHandlerBase /** * strawberryFieldharvester constructor. + * * @param array $configuration * @param $plugin_id * @param $plugin_definition @@ -96,8 +100,29 @@ class strawberryFieldharvester extends WebformHandlerBase * @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerChannelFactoryInterface $logger_factory, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, WebformSubmissionConditionsValidatorInterface $conditions_validator, WebformTokenManagerInterface $token_manager, FileSystemInterface $file_system, FileUsageInterface $file_usage, TransliterationInterface $transliteration, LanguageManagerInterface $language_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $logger_factory, $config_factory, $entity_type_manager, $conditions_validator); + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + LoggerChannelFactoryInterface $logger_factory, + ConfigFactoryInterface $config_factory, + EntityTypeManagerInterface $entity_type_manager, + WebformSubmissionConditionsValidatorInterface $conditions_validator, + WebformTokenManagerInterface $token_manager, + FileSystemInterface $file_system, + FileUsageInterface $file_usage, + TransliterationInterface $transliteration, + LanguageManagerInterface $language_manager + ) { + parent::__construct( + $configuration, + $plugin_id, + $plugin_definition, + $logger_factory, + $config_factory, + $entity_type_manager, + $conditions_validator + ); $this->entityTypeManager = $entity_type_manager; $this->tokenManager = $token_manager; $this->fileSystem = $file_system; @@ -110,7 +135,12 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { return new static( $configuration, $plugin_id, @@ -131,27 +161,25 @@ public static function create(ContainerInterface $container, array $configuratio /** * @return bool */ - public function isWidgetDriven(): bool - { + public function isWidgetDriven(): bool { return $this->isWidgetDriven; } /** * @param bool $isWidgetDriven */ - public function setIsWidgetDriven(bool $isWidgetDriven): void - { + public function setIsWidgetDriven(bool $isWidgetDriven): void { $this->isWidgetDriven = $isWidgetDriven; } - /** * {@inheritdoc} */ - public function postLoad(WebformSubmissionInterface $webform_submission) - { - parent::postLoad($webform_submission); // TODO: Change the autogenerated stub + public function postLoad(WebformSubmissionInterface $webform_submission) { + parent::postLoad( + $webform_submission + ); // TODO: Change the autogenerated stub } @@ -159,12 +187,11 @@ public function postLoad(WebformSubmissionInterface $webform_submission) /** * {@inheritdoc} */ - public function defaultConfiguration() - { + public function defaultConfiguration() { // @TODO this will be sent to Esmero. return [ 'submission_url' => 'https://api.example.org/SOME/ENDPOINT', - 'upload_scheme' => 'public://' + 'upload_scheme' => 'public://', ]; } @@ -182,8 +209,10 @@ public function getSummary() { /** * {@inheritdoc} */ - public function buildConfigurationForm(array $form, FormStateInterface $form_state) - { + public function buildConfigurationForm( + array $form, + FormStateInterface $form_state + ) { $form['submission_url'] = [ '#type' => 'textfield', '#title' => $this->t('Secondary submission URL to api.example.org'), @@ -195,7 +224,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form['upload_scheme'] = [ '#type' => 'radios', '#title' => $this->t('Permanent destination for uploaded files'), - '#description' => $this->t('The Permanent URI Scheme destination for uploaded files.'), + '#description' => $this->t( + 'The Permanent URI Scheme destination for uploaded files.' + ), '#default_value' => $this->configuration['upload_scheme'], '#required' => TRUE, '#options' => $scheme_options, @@ -204,8 +235,10 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } - public function submitConfigurationForm(array &$form, FormStateInterface $form_state) - { + public function submitConfigurationForm( + array &$form, + FormStateInterface $form_state + ) { parent::submitConfigurationForm($form, $form_state); $this->applyFormStateToConfiguration($form_state); } @@ -213,8 +246,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s /** * {@inheritdoc} */ - public function preSave(WebformSubmissionInterface $webform_submission) - { + public function preSave(WebformSubmissionInterface $webform_submission) { $values = $webform_submission->getData(); $cleanvalues = $values; @@ -224,23 +256,38 @@ public function preSave(WebformSubmissionInterface $webform_submission) // Check which elements carry files around $allelements = $webform_submission->getWebform()->getElementsManagedFiles(); foreach ($allelements as $element) { - $originalelement = $webform_submission->getWebform()->getElement($element); - // Track what fields map to file entities. - $entity_mapping_structure['entity:file'][] = $originalelement['#webform_key']; - // Process each managed files field. - $processedcleanvaluesforfield = $this->processFileField($originalelement, $webform_submission, $cleanvalues); - // Merge since different fields can contribute to same as:filetype structure. - $processedcleanvalues = array_merge_recursive($processedcleanvalues, $processedcleanvaluesforfield); - } + $originalelement = $webform_submission->getWebform()->getElement( + $element + ); + // Track what fields map to file entities. + $entity_mapping_structure['entity:file'][] = $originalelement['#webform_key']; + // Process each managed files field. + $processedcleanvaluesforfield = $this->processFileField( + $originalelement, + $webform_submission, + $cleanvalues + ); + // Merge since different fields can contribute to same as:filetype structure. + $processedcleanvalues = array_merge_recursive( + $processedcleanvalues, + $processedcleanvaluesforfield + ); + } // Check also which elements carry entity references around // @see https://www.drupal.org/project/webform/issues/3067958 if (isset($entity_mapping_structure['entity:node'])) { //@TODO change this stub. Get every element that extends Drupal\webform\Plugin\WebformElementEntityReferenceInterface() - $entity_mapping_structure['entity:node'] = array_unique($entity_mapping_structure['entity:node'],SORT_STRING); + $entity_mapping_structure['entity:node'] = array_unique( + $entity_mapping_structure['entity:node'], + SORT_STRING + ); } if (isset($entity_mapping_structure['entity:file'])) { - $entity_mapping_structure['entity:file'] = array_unique($entity_mapping_structure['entity:file'],SORT_STRING); + $entity_mapping_structure['entity:file'] = array_unique( + $entity_mapping_structure['entity:file'], + SORT_STRING + ); } // Distribute all processed AS values for each field into its final JSON // Structure, e.g as:image, as:application, as:documents, etc. @@ -267,7 +314,7 @@ public function preSave(WebformSubmissionInterface $webform_submission) // of the strawberry_field_widget_state_id if the submission is also saved $webform_submission->setData($cleanvalues); - $cleanvalues = json_encode( $cleanvalues, JSON_PRETTY_PRINT); + $cleanvalues = json_encode($cleanvalues, JSON_PRETTY_PRINT); try { $tempstore->set( @@ -275,21 +322,36 @@ public function preSave(WebformSubmissionInterface $webform_submission) $cleanvalues ); } catch (TempStoreException $e) { - $this->messenger()->addError($this->t('Sorry, we have issues writing metadata to your session storage. Please reload this form and/or contact your system admin.')); - $this->loggerFactory->get('archipelago')->error('Webform @webformid can not write to temp storage! with error @message. Attempted Metadata input was
%data
', [ - '@webformid' => $this->getWebform()->id(), - '%data' => print_r($webform_submission->getData(), TRUE), - '@error' => $e->getMessage(), - ]); + $this->messenger()->addError( + $this->t( + 'Sorry, we have issues writing metadata to your session storage. Please reload this form and/or contact your system admin.' + ) + ); + $this->loggerFactory->get('archipelago')->error( + 'Webform @webformid can not write to temp storage! with error @message. Attempted Metadata input was
%data
', + [ + '@webformid' => $this->getWebform()->id(), + '%data' => print_r($webform_submission->getData(), TRUE), + '@error' => $e->getMessage(), + ] + ); } - } elseif ($this->IsWidgetDriven()) { - $this->messenger()->addError($this->t('We lost TV reception in the middle of the match...Please contact your system admin.')); - $this->loggerFactory->get('archipelago')->error('Webform @webformid lost connection to temp storage and its Widget!. No Widget State id present. Attempted Metadata input was
%data
', [ - '@webformid' => $this->getWebform()->id(), - '%data' => print_r($webform_submission->getData(), TRUE), - ]); + } + elseif ($this->IsWidgetDriven()) { + $this->messenger()->addError( + $this->t( + 'We lost TV reception in the middle of the match...Please contact your system admin.' + ) + ); + $this->loggerFactory->get('archipelago')->error( + 'Webform @webformid lost connection to temp storage and its Widget!. No Widget State id present. Attempted Metadata input was
%data
', + [ + '@webformid' => $this->getWebform()->id(), + '%data' => print_r($webform_submission->getData(), TRUE), + ] + ); } parent::preSave($webform_submission); // TODO: Change the autogenerated stub @@ -298,41 +360,59 @@ public function preSave(WebformSubmissionInterface $webform_submission) /** * {@inheritdoc} */ - public function validateForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) - { + public function validateForm( + array &$form, + FormStateInterface $form_state, + WebformSubmissionInterface $webform_submission + ) { $values = $webform_submission->getData(); - if ((!isset($values["strawberry_field_widget_state_id"]) || empty($values["strawberry_field_widget_state_id"])) && $this->IsWidgetDriven()) { - $this->messenger()->addError($this->t('Sorry, we have issues reading your session storage identifier. Error was logged. Please reload this form and/or contact your system admin.')); - - $this->loggerFactory->get('archipelago')->error('Webform @webformid lost connection to temp storage!. No Widget State id present. Attempted Metadata input was
%data
', [ - '@webformid' => $this->getWebform()->id(), - '%data' => print_r($webform_submission->getData(), TRUE), - ]); + if ((!isset($values["strawberry_field_widget_state_id"]) || empty($values["strawberry_field_widget_state_id"])) && $this->IsWidgetDriven( + )) { + $this->messenger()->addError( + $this->t( + 'Sorry, we have issues reading your session storage identifier. Error was logged. Please reload this form and/or contact your system admin.' + ) + ); + + $this->loggerFactory->get('archipelago')->error( + 'Webform @webformid lost connection to temp storage!. No Widget State id present. Attempted Metadata input was
%data
', + [ + '@webformid' => $this->getWebform()->id(), + '%data' => print_r($webform_submission->getData(), TRUE), + ] + ); } // All data is available here $webform_submission->getData())); // @TODO what should be validated here? - parent::validateForm($form, $form_state, $webform_submission); // TODO: Change the autogenerated stub + parent::validateForm( + $form, + $form_state, + $webform_submission + ); // TODO: Change the autogenerated stub } /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) - { + public function submitForm( + array &$form, + FormStateInterface $form_state, + WebformSubmissionInterface $webform_submission + ) { $values = $webform_submission->getData(); if (isset($values["strawberry_field_widget_state_id"])) { - $this->setIsWidgetDriven(true); + $this->setIsWidgetDriven(TRUE); } - // @TODO add a full-blown values cleaner - // @TODO add the webform name used to create this as additional KEY - // @TODO make sure widget can read that too. - // @If Widget != setup form, ask for User feedback - // @TODO, i need to alter node submit handler to add also the - // Entities full URL as an @id to the top of the saved JSON. - // FUN! + // @TODO add a full-blown values cleaner + // @TODO add the webform name used to create this as additional KEY + // @TODO make sure widget can read that too. + // @If Widget != setup form, ask for User feedback + // @TODO, i need to alter node submit handler to add also the + // Entities full URL as an @id to the top of the saved JSON. + // FUN! // Get the URL to post the data to. // @todo esmero a.k.a as Fedora-mockingbird $post_url = $this->configuration['submission_url']; @@ -349,7 +429,11 @@ public function submitForm(array &$form, FormStateInterface $form_state, Webform * @return array * Associative array keyed by AS type with binary info. */ - public function processFileField(array $element, WebformSubmissionInterface $webform_submission, $cleanvalues) { + public function processFileField( + array $element, + WebformSubmissionInterface $webform_submission, + $cleanvalues + ) { $key = $element['#webform_key']; $type = $element['#type']; @@ -361,7 +445,9 @@ public function processFileField(array $element, WebformSubmissionInterface $web $fids = (is_array($value)) ? $value : [$value]; $original_value = isset($original_data[$key]) ? $original_data[$key] : []; - $original_fids = (is_array($original_value)) ? $original_value : [$original_value]; + $original_fids = (is_array( + $original_value + )) ? $original_value : [$original_value]; // Delete the old file uploads? // @TODO build some cleanup logic here. Could be moved to attached field hook. @@ -381,7 +467,8 @@ public function processFileField(array $element, WebformSubmissionInterface $web } /* @see \Drupal\strawberryfield\StrawberryfieldFilePersisterService */ - $processedAsValues = \Drupal::service('strawberryfield.file_persister')->generateAsFileStructure($fids, $key, $cleanvalues); + $processedAsValues = \Drupal::service('strawberryfield.file_persister') + ->generateAsFileStructure($fids, $key, $cleanvalues); return $processedAsValues; } @@ -389,7 +476,11 @@ public function processFileField(array $element, WebformSubmissionInterface $web /** * {@inheritdoc} */ - public function confirmForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) { + public function confirmForm( + array &$form, + FormStateInterface $form_state, + WebformSubmissionInterface $webform_submission + ) { // We really want to avoid being redirected. This is how it is done. //@TODO manage file upload if there is no submission save handler //@ see \Drupal\webform\Plugin\WebformElement\WebformManagedFileBase::postSave @@ -400,12 +491,12 @@ public function confirmForm(array &$form, FormStateInterface $form_state, Webfor /** * {@inheritdoc} */ - public function preprocessConfirmation(array &$variables) - { + public function preprocessConfirmation(array &$variables) { if ($this->isWidgetDriven()) { unset($variables['back']); } } + /** * {@inheritdoc} */ @@ -444,7 +535,8 @@ protected function getUriSchemeForManagedFile(array $element) { if (isset($element['#uri_scheme'])) { return $element['#uri_scheme']; } - $scheme_options = \Drupal\webform\Plugin\WebformElement\WebformManagedFileBase::getVisibleStreamWrappers(); + $scheme_options = \Drupal\webform\Plugin\WebformElement\WebformManagedFileBase::getVisibleStreamWrappers( + ); if (isset($scheme_options['private'])) { return 'private'; } From c2476f5d8828f4578fa516655507648b41f93032 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Wed, 22 Jan 2020 09:29:52 -0500 Subject: [PATCH 25/54] Testing webform_strawberryfield_preprocess_webform_element_image_file This is just WIP. --- webform_strawberryfield.module | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 7999537..a404cf5 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -330,4 +330,87 @@ function template_preprocess_webform_metadata_panoramatour(array &$variables) { */ } +/** + * Impleements hook_preprocess_HOOK() + * + * @see template_preprocess_webform_element_image_file() + * + * @param array $variables + * An associative array containing the following key: + * - element: The webform element. + * - value: The content for the element. + * - options Associative array of options for element. + * - file: The element's File object. + * - style_name: An image style name. + * - format: Image formatting (link or modal) + */ +function webform_strawberryfield_preprocess_webform_element_image_file(array &$variables) { + if (!empty($variables['file'])) { + /** @var \Drupal\file\FileInterface $file */ + $file = $variables['file']; + + $style_name = $variables['style_name']; + $format = $variables['format']; + + $uri = $file->getFileUri(); + $url = Url::fromUri(file_create_url($uri)); + dpm($url); + + $extension = pathinfo($uri, PATHINFO_EXTENSION); + $is_image = in_array($extension, ['gif', 'png', 'jpg', 'jpeg', 'jp2', 'tiff']); + + // Build image. + if ($is_image && \Drupal::moduleHandler()->moduleExists('image') && $style_name && ImageStyle::load($style_name)) { + $variables['image'] = [ + '#theme' => 'image_style', + '#style_name' => $variables['style_name'], + ]; + } + else { + // Note: The 'image' template uses root-relative paths. + // The 'image' is preprocessed to use absolute URLs. + // @see webform_preprocess_image(). + $variables['image'] = [ + '#theme' => 'image', + ]; + } + $variables['image'] += [ + '#uri' => $uri, + '#attributes' => [ + 'class' => ['webform-image-file'], + 'alt' => $file->getFilename(), + 'title' => $file->getFilename(), + ], + ]; + + // For the Results table always display the file name as a tooltip. + if (strpos(\Drupal::routeMatch()->getRouteName(), 'webform.results_submissions') !== FALSE) { + $variables['attached']['library'][] = 'webform/webform.tooltip'; + $variables['image']['#attributes']['class'][] = 'js-webform-tooltip-link'; + } + + // Wrap 'image' in a link/modal. + if ($format && $format != 'image') { + $variables['image'] = [ + '#type' => 'link', + '#title' => $variables['image'], + '#url' => $url, + ]; + switch ($format) { + case 'modal': + $variables['image'] += [ + '#attributes' => ['class' => ['js-webform-image-file-modal', 'webform-image-file-modal']], + '#attached' => ['library' => ['webform/webform.element.image_file.modal']], + ]; + break; + + case 'link': + $variables['image'] += ['#attributes' => ['class' => ['webform-image-file-link']]]; + break; + } + } + + } +} + From 2c760ab38cb5764d455e1e58ffbf151a39092dab Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Wed, 22 Jan 2020 12:58:39 -0500 Subject: [PATCH 26/54] Allow File preview based on IIIF server or temp direct access - This also changes a class name so we don't end messing the final URL by appending the Global Base URL as webform does. There are a few pieces of code i totally don't like (like the CSS style there) but also i feel we need to make some decisions and avoid sometimes leaving everything as a custom/setable option. --- webform_strawberryfield.module | 72 ++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index a404cf5..1dd4b15 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -331,7 +331,7 @@ function template_preprocess_webform_metadata_panoramatour(array &$variables) { } /** - * Impleements hook_preprocess_HOOK() + * Implements hook_preprocess_HOOK() * * @see template_preprocess_webform_element_image_file() * @@ -345,7 +345,13 @@ function template_preprocess_webform_metadata_panoramatour(array &$variables) { * - format: Image formatting (link or modal) */ function webform_strawberryfield_preprocess_webform_element_image_file(array &$variables) { + if (!empty($variables['file'])) { + // TODO do we need a setting for this? + $max_width = 320; + + //@TODO this is quite a copy of what Format_strawberry does. We should + // Move all that logic to helper methods into \Drupal\format_strawberryfield\Tools\IiifHelper /** @var \Drupal\file\FileInterface $file */ $file = $variables['file']; @@ -354,7 +360,60 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $uri = $file->getFileUri(); $url = Url::fromUri(file_create_url($uri)); - dpm($url); + if (!$file->isTemporary()) { + + $iiifserversettings = \Drupal::config( + 'format_strawberryfield.iiif_settings' + ); + $iiifhelper = new IiifHelper( + $iiifserversettings->get('pub_server_url'), + $iiifserversettings->get('int_server_url') + ); + + $iiifidentifier = urlencode( + \Drupal::service('stream_wrapper_manager')->getTarget( + $file->getFileUri() + ) + ); + + if ($iiifidentifier == NULL || empty($iiifidentifier)) { + // Nothing to do, lets leave this untouched. + return; + + } + + $iiifpublicinfojson = $iiifhelper->getPublicInfoJson($iiifidentifier); + $iiifsizes = $iiifhelper->getImageSizes($iiifidentifier); + + if (!$iiifsizes) { + $message = t( + 'We could not fetch Image sizes from IIIF @url', + [ + '@url' => $iiifpublicinfojson, + ] + ); + \Drupal::logger('webform_strawberryfield')->warning($message); + // Nothing to do, lets leave this untouched. + return; + } + else { + //@see \template_preprocess_image for further theme_image() attributes. + // Look. This one uses the public accesible base URL. That is how world works. + $iiifserverthumb = "{$iiifserversettings->get('pub_server_url')}/{$iiifidentifier}" . "/full/{$max_width},/0/default.jpg"; + $url = $iiifserverthumb; + } + } + else { + // Its a temporary file, just uploaded, IIIF can not see it yet + + $route_parameters = [ + 'uuid' => $file->uuid(), + 'format' => 'default.'. pathinfo($file->getFilename(), PATHINFO_EXTENSION) + ]; + $publicurl = Url::fromRoute('format_strawberryfield.tempiiifbinary', $route_parameters); + $url = $publicurl->toString(); + + } $extension = pathinfo($uri, PATHINFO_EXTENSION); $is_image = in_array($extension, ['gif', 'png', 'jpg', 'jpeg', 'jp2', 'tiff']); @@ -374,12 +433,17 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v '#theme' => 'image', ]; } + // Change the class from webform-image-file to webform-strawberryfield-image-file + // to avoid webform_preprocess_image() messing up our IIIF link + // by appending the current global. + // @TODO Style element here feels like a hack. We can do better $variables['image'] += [ - '#uri' => $uri, + '#uri' => $url, '#attributes' => [ - 'class' => ['webform-image-file'], + 'class' => ['webform-strawberryfield-image-file'], 'alt' => $file->getFilename(), 'title' => $file->getFilename(), + 'style' => "max-width:{$max_width}px;height:auto", ], ]; From 9d942090e4e5d7e288870c4cf8e1537c72e0c332 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:19:37 -0500 Subject: [PATCH 27/54] Help Inline Rendered Webforms to get rid of the form element Yes, sounds crazy. But right now we are rendering a form inside a form, and even when that is working, it is messing up with AJAX States, etc. This should keep the internal webform/form functionality, but since we already have a form element on the actual entity edit/add operation, we can simple, when outputting bring this into a Div! Ha. Hopefully this works. I know i will need more try/test before getting it right. --- src/Element/WebformPanoramaTour.php | 1 - src/Element/WebformWithOverride.php | 7 +- .../webform_inline_fieldwidget.html.twig | 28 ++++++ webform_strawberryfield.module | 91 ++++--------------- 4 files changed, 52 insertions(+), 75 deletions(-) create mode 100644 templates/webform_inline_fieldwidget.html.twig diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 192ae89..c110d3d 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -76,7 +76,6 @@ public static function processWebformComposite( $sceneid = $scene['scene']; } else { - $sceneid = $form_state->getValue([$element['#name'], 'scene']); $form_state->setValue([$element_name, 'scene'],$sceneid); } diff --git a/src/Element/WebformWithOverride.php b/src/Element/WebformWithOverride.php index 7d6f8f4..02d6662 100644 --- a/src/Element/WebformWithOverride.php +++ b/src/Element/WebformWithOverride.php @@ -65,11 +65,16 @@ public static function preRenderWebformElement($element) { $new_settings = $element['#override']; $webform->setSettingsOverride($new_settings); $values['strawberryfield:override'] = $new_settings; - } // Build the webform. $element['webform_build'] = $webform->getSubmissionForm($values); + // Adds this marker so we can suggest another template. + // Where we can get rid of the internal 'form' rendering + // Allowing DOM to set the so needed classes for the wrapper + // So states and other WEbform AJAX libraries can work correctly. + // We don't want a full blown preprocess (yet). + $element['webform_build']['#attributes']['data-webform-inline-fieldwidget'] = 'true'; // Set custom form action. if (!empty($element['#action'])) { diff --git a/templates/webform_inline_fieldwidget.html.twig b/templates/webform_inline_fieldwidget.html.twig new file mode 100644 index 0000000..93c0a2d --- /dev/null +++ b/templates/webform_inline_fieldwidget.html.twig @@ -0,0 +1,28 @@ +{# +/** + * @file + * Theme implementation for a 'webform' element overriden by our module + * + * This is an copy of the webform.html.twig theme_wrapper but changeas + * form element for a div, so it can live in HTML DOM inside an Entity Form + * as a Widget + * 'title_prefix' and 'title_suffix' variables needed for + * + * Available variables + * - attributes: A list of HTML attributes for the wrapper element. + * - children: The child elements of the webform. + * - title_prefix: Additional output populated by modules, intended to be + * displayed in front of the main title tag that appears in the template. + * - title_suffix: Additional output populated by modules, intended to be + * displayed after the main title tag that appears in the template. + * + * @see template_preprocess_webform() + * + * @ingroup themeable + */ +#} + + {{ title_prefix }} + {{ children }} + {{ title_suffix }} + \ No newline at end of file diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 1dd4b15..3dc59f2 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -220,7 +220,7 @@ function webform_strawberryfield_theme() { ], 'webform_metadata_panoramatour' => [ 'render element' => 'element', - ] + ], ]; return $info; } @@ -255,79 +255,24 @@ function template_preprocess_webform_metadata_nominatim(array &$variables) { * - element: An associative array containing the properties of the element. */ function template_preprocess_webform_metadata_panoramatour(array &$variables) { - $variables['content'] = $variables['element']; - /*unset($variables['element']); - /* - "scene" => array:31 [â–¶] - "hotspots" => array:19 [â–¶] - "select_button" => array:32 [â–¶] - "hotspots_temp" => array:33 [â–¼ - "#type" => "fieldset" - "#title" => "Hotspots for this Scene" - "#attributes" => array:2 [â–¶] - "#attached" => array:2 [â–¶] - "label" => array:28 [â–¶] - "yaw" => array:28 [â–¶] - "hfov" => array:28 [â–¶] - "pitch" => array:28 [â–¶] - "type" => array:26 [â–¶] - "url" => array:29 [â–¶] - "ado" => & array:40 [â–¶] - "add_hotspot" => array:31 [â–¶] - "set_sceneorientation" => array:31 [â–¶] - "delete_scene" => array:31 [â–¶] - "node" => array:17 [â–¶] - "added_hotspots" => array:34 [â–¶] - "#process" => array:3 [â–¶] - "#pre_render" => array:1 [â–¶] - "#value" => null - "#theme_wrappers" => array:1 [â–¶] - "#defaults_loaded" => true - "#tree" => true - "#parents" => array:2 [â–¶] - "#array_parents" => array:4 [â–¶] - "#weight" => 0.003 - "#processed" => true - "#required" => false - "#title_display" => "before" - "#description_display" => "after" - "#errors" => null - "#id" => "edit-panorama-tour-hotspots-temp--WgAHmLMT7gw" - "#groups" => &2 array:10 [â–¶] - "#ajax_processed" => false - ] - - $variables['sceneselect'] = $variables['content']['scene']; - $variables['scenebutton'] = $variables['content']['select_button']; - $variables['hotspotui'] = [ - 'label' => $variables['content']['hotspots_temp']['label'], - 'url' => $variables['content']['hotspots_temp']['ado'], - 'ado' => $variables['content']['hotspots_temp']['ado'], - 'type' => $variables['content']['hotspots_temp']['type'], - 'yaw' => $variables['content']['hotspots_temp']['yaw'], - 'pitch' => $variables['content']['hotspots_temp']['pitch'], - 'hfov' => $variables['content']['hotspots_temp']['hfov'], - ]; - $variables['hotspotbuttons'] = [ - 'add_hotspot' => $variables['content']['hotspots_temp']['add_hotspot'], - 'set_sceneorientation' => $variables['content']['hotspots_temp']['set_sceneorientation'], - 'delete_scene' => $variables['content']['hotspots_temp']['delete_scene'], - ]; - $variables['node'] = $variables['content']['hotspots_temp']['node']; - unset($variables['content']['scene']); - unset($variables['content']['select_button']); - unset($variables['content']['hotspots_temp']['pitch']); - unset($variables['content']['hotspots_temp']['label']); - unset($variables['content']['hotspots_temp']['yaw']); - unset($variables['content']['hotspots_temp']['url']); - unset($variables['content']['hotspots_temp']['ado']); - unset($variables['content']['hotspots_temp']['type']); - unset($variables['content']['hotspots_temp']['add_hotspot']); - unset($variables['content']['hotspots_temp']['set_sceneorientation']); - unset($variables['content']['hotspots_temp']['delete_scene']); - unset($variables['content']['hotspots_temp']['node']); - */ +} + +/** + * Implements hook_theme_suggestions_HOOK_alter() for webforms. + * + * @param $suggestions + * @param array $variables + */ +function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, array $variables) { + // Add our own webform suggestion based on a given data attribute set by + // \Drupal\webform_strawberryfield\Element\WebformWithOverride::preRenderWebformElement + $element = $variables['element']; + // This is in particular for 'webform_inline_fieldwidget' element + if (isset($element['#attributes']['data-webform-inline-fieldwidget'])) { + $suggestions[] = $element['#type']; + $suggestions[] = $element['#type']. '__' . $element['#webform_id']; + } } /** From 79b723266b1d949c9fcd9233370d31fb13d2c465 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:23:42 -0500 Subject: [PATCH 28/54] Got wrong $element type, which here will be always form because we are inside our own render element, but we are altering not our own, but its child. Nevermind --- webform_strawberryfield.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 3dc59f2..fb796b1 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -270,8 +270,8 @@ function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, $element = $variables['element']; // This is in particular for 'webform_inline_fieldwidget' element if (isset($element['#attributes']['data-webform-inline-fieldwidget'])) { - $suggestions[] = $element['#type']; - $suggestions[] = $element['#type']. '__' . $element['#webform_id']; + $suggestions[] = 'webform_inline_fieldwidget'; + $suggestions[] = 'webform_inline_fieldwidget__' . $element['#webform_id']; } } From ef22bae13440e518e4778665d452968f6fef68e7 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:25:27 -0500 Subject: [PATCH 29/54] Wrong naming convention for the template I really need to focus after 00:00 --- ...fieldwidget.html.twig => webform-inline-fieldwidget.html.twig} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/{webform_inline_fieldwidget.html.twig => webform-inline-fieldwidget.html.twig} (100%) diff --git a/templates/webform_inline_fieldwidget.html.twig b/templates/webform-inline-fieldwidget.html.twig similarity index 100% rename from templates/webform_inline_fieldwidget.html.twig rename to templates/webform-inline-fieldwidget.html.twig From 214f25d48f2a4824a2570a265b87761d561fd26f Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:38:14 -0500 Subject: [PATCH 30/54] nope, not working yet. --- ...dwidget.html.twig => webform--inlinefieldwidget.html.twig} | 0 webform_strawberryfield.module | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename templates/{webform-inline-fieldwidget.html.twig => webform--inlinefieldwidget.html.twig} (100%) diff --git a/templates/webform-inline-fieldwidget.html.twig b/templates/webform--inlinefieldwidget.html.twig similarity index 100% rename from templates/webform-inline-fieldwidget.html.twig rename to templates/webform--inlinefieldwidget.html.twig diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index fb796b1..f41c084 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -270,8 +270,8 @@ function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, $element = $variables['element']; // This is in particular for 'webform_inline_fieldwidget' element if (isset($element['#attributes']['data-webform-inline-fieldwidget'])) { - $suggestions[] = 'webform_inline_fieldwidget'; - $suggestions[] = 'webform_inline_fieldwidget__' . $element['#webform_id']; + $suggestions[] = 'webform__inlinefieldwidget'; + $suggestions[] = 'webform__inlinefieldwidget__' . $element['#webform_id']; } } From ed93a2296acc49b6fb942b29c421bbb9f2cb67e5 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:48:26 -0500 Subject: [PATCH 31/54] Another attempt.Forgot the theme hook! --- webform_strawberryfield.module | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index f41c084..c0d5aa6 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -221,6 +221,10 @@ function webform_strawberryfield_theme() { 'webform_metadata_panoramatour' => [ 'render element' => 'element', ], + 'webform_inline_fieldwidget_form' => [ + 'render element' => 'element', + 'template' => 'webform__inlinefieldwidget', + ], ]; return $info; } @@ -270,8 +274,8 @@ function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, $element = $variables['element']; // This is in particular for 'webform_inline_fieldwidget' element if (isset($element['#attributes']['data-webform-inline-fieldwidget'])) { - $suggestions[] = 'webform__inlinefieldwidget'; - $suggestions[] = 'webform__inlinefieldwidget__' . $element['#webform_id']; + $suggestions[] = 'webform_inline_fieldwidget_form'; + $suggestions[] = 'webform_inline_fieldwidget_form__' . $element['#webform_id']; } } From f19f0f74272b6cc9719ed7cbbb30f7f839414900 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:49:15 -0500 Subject: [PATCH 32/54] wrong name! --- webform_strawberryfield.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index c0d5aa6..7446419 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -223,7 +223,7 @@ function webform_strawberryfield_theme() { ], 'webform_inline_fieldwidget_form' => [ 'render element' => 'element', - 'template' => 'webform__inlinefieldwidget', + 'template' => 'webform--inlinefieldwidget', ], ]; return $info; From 80b90b3648792543676a8f927d2e23e0fa3c91c3 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:54:04 -0500 Subject: [PATCH 33/54] And another one, can't get the name right! --- ...g => webform_strawberryfield-inlinefieldwidget.html.twig} | 0 webform_strawberryfield.module | 5 ++--- 2 files changed, 2 insertions(+), 3 deletions(-) rename templates/{webform--inlinefieldwidget.html.twig => webform_strawberryfield-inlinefieldwidget.html.twig} (100%) diff --git a/templates/webform--inlinefieldwidget.html.twig b/templates/webform_strawberryfield-inlinefieldwidget.html.twig similarity index 100% rename from templates/webform--inlinefieldwidget.html.twig rename to templates/webform_strawberryfield-inlinefieldwidget.html.twig diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 7446419..23613e4 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -223,7 +223,7 @@ function webform_strawberryfield_theme() { ], 'webform_inline_fieldwidget_form' => [ 'render element' => 'element', - 'template' => 'webform--inlinefieldwidget', + 'template' => 'webform_strawberryfield-inlinefieldwidget', ], ]; return $info; @@ -274,8 +274,7 @@ function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, $element = $variables['element']; // This is in particular for 'webform_inline_fieldwidget' element if (isset($element['#attributes']['data-webform-inline-fieldwidget'])) { - $suggestions[] = 'webform_inline_fieldwidget_form'; - $suggestions[] = 'webform_inline_fieldwidget_form__' . $element['#webform_id']; + $suggestions = ['webform_inline_fieldwidget_form', 'webform_inline_fieldwidget_form__' . $element['#webform_id']]; } } From 03e44897f682c02bbf9e763b07e62c418f0bf39a Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 00:59:19 -0500 Subject: [PATCH 34/54] Better! --- webform_strawberryfield.module | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 23613e4..0403bbf 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -222,8 +222,9 @@ function webform_strawberryfield_theme() { 'render element' => 'element', ], 'webform_inline_fieldwidget_form' => [ - 'render element' => 'element', + 'variables' => [], 'template' => 'webform_strawberryfield-inlinefieldwidget', + 'base hook' => 'webform' ], ]; return $info; From c4401b1737048bc011a40c55d1124039156acbc3 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 01:03:56 -0500 Subject: [PATCH 35/54] Unsure about what happened here... copy paste error? --- webform_strawberryfield.module | 1 + 1 file changed, 1 insertion(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 0403bbf..06626ba 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -9,6 +9,7 @@ use Drupal\Core\Url; use Drupal\webform\WebformSubmissionForm; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\file\Entity\File; +use Drupal\format_strawberryfield\Tools\IiifHelper; /** * Alters a webform to help with embbeding in a node crud context. From 194d29afda0822da8241528a1ec5c99e45473e2b Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 01:21:23 -0500 Subject: [PATCH 36/54] Deal with 8.7.x v/s 8.8 and ::getTarget method This is just an exception when running beta2 on 8.7, which should never happen because our composer is asking for 8.8. But during dev it can get complex so better do this for now --- webform_strawberryfield.module | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 06626ba..5e196d6 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -319,12 +319,16 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $iiifserversettings->get('pub_server_url'), $iiifserversettings->get('int_server_url') ); - - $iiifidentifier = urlencode( - \Drupal::service('stream_wrapper_manager')->getTarget( - $file->getFileUri() - ) - ); + // Deal with Drupal 8.8.x v/s 8.7 + if (method_exists(\Drupal::service('stream_wrapper_manager'),'getTarget')) { + $iiifidentifier = urlencode( + \Drupal::service('stream_wrapper_manager')->getTarget( + $file->getFileUri() + )); + } + else { + $iiifidentifier = urlencode(file_uri_target($file->getFileUri())); + } if ($iiifidentifier == NULL || empty($iiifidentifier)) { // Nothing to do, lets leave this untouched. From 3673c81e93f0a8344549e50fe3706b480125ee46 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 02:04:34 -0500 Subject: [PATCH 37/54] Quick test for EXIF. This is not FINAL!! but you know that... --- webform_strawberryfield.module | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 5e196d6..dc7843b 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -366,6 +366,33 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v ]; $publicurl = Url::fromRoute('format_strawberryfield.tempiiifbinary', $route_parameters); $url = $publicurl->toString(); + // Extract EXIF and show it to the user! + /** @var \Drupal\Core\File\FileSystem $file_system */ + $scheme = \Drupal::service('file_system')->uriScheme($uri); + $templocation = NULL; + // If the file isn't stored locally make a temporary copy. + if (!isset(\Drupal::service('stream_wrapper_manager') + ->getWrappers(StreamWrapperInterface::LOCAL)[$scheme])) { + // Local stream. + $cache_key = md5($uri); + $templocation = \Drupal::service('file_system')->copy($uri, 'temporary://sbr_' . $cache_key . '_' . basename($uri), FileSystemInterface::FILE_EXISTS_REPLACE); + } else { + $templocation = \Drupal::service('file_system')->realpath($file->getFileUri()); + } + if ($templocation) { + $result = exec('exiftool -json -q '.escapeshellcmd($templocation), $output, $status); + if ($status != 0) { + dpm('Ups. We could not get EXIF from your file!'); + } else { + dpm('exiftool -json -q '.escapeshellcmd($templocation)); + dpm($output); + + } + dpm($result); + } + + + } From 67a21358c18bc2d39a8b892fbb6ad065d41a01b0 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 02:18:09 -0500 Subject: [PATCH 38/54] Missing use statement fot exif test --- webform_strawberryfield.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index dc7843b..319f313 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -10,6 +10,8 @@ use Drupal\webform\WebformSubmissionForm; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\file\Entity\File; use Drupal\format_strawberryfield\Tools\IiifHelper; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; +use Drupal\Core\File\FileSystemInterface; /** * Alters a webform to help with embbeding in a node crud context. From a89862b53675a0f62d811a37116534acd31646c7 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 02:30:17 -0500 Subject: [PATCH 39/54] Give it a chance to fail --- webform_strawberryfield.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 319f313..2002971 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -388,6 +388,12 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v } else { dpm('exiftool -json -q '.escapeshellcmd($templocation)); dpm($output); + $variables['exif'] = [ + '#type' => 'markup', + '#markup' => $output + ]; + + $output; } dpm($result); From 4d70ce16cc899e5670506785ac3b0433bf6cc46d Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 02:43:46 -0500 Subject: [PATCH 40/54] Try again but better. --- webform_strawberryfield.module | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 2002971..5de06f3 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -307,6 +307,8 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v /** @var \Drupal\file\FileInterface $file */ $file = $variables['file']; + $variables['image']['#type'] = 'container'; + $style_name = $variables['style_name']; $format = $variables['format']; @@ -382,21 +384,19 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $templocation = \Drupal::service('file_system')->realpath($file->getFileUri()); } if ($templocation) { - $result = exec('exiftool -json -q '.escapeshellcmd($templocation), $output, $status); + $result = exec('exiftool -json -q '.escapeshellcmd($templocation), $output, $status); if ($status != 0) { - dpm('Ups. We could not get EXIF from your file!'); - } else { - dpm('exiftool -json -q '.escapeshellcmd($templocation)); - dpm($output); - $variables['exif'] = [ + \Drupal::service('messenger') + ->addMessage(t('Ups. We could not get EXIF from your file. Sorry.')); + } + else { + $variables['image']['exif'] = [ '#type' => 'markup', '#markup' => $output - ]; + ]; - $output; } - dpm($result); } @@ -409,7 +409,7 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v // Build image. if ($is_image && \Drupal::moduleHandler()->moduleExists('image') && $style_name && ImageStyle::load($style_name)) { - $variables['image'] = [ + $variables['image']['singleimage'] = [ '#theme' => 'image_style', '#style_name' => $variables['style_name'], ]; @@ -418,15 +418,15 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v // Note: The 'image' template uses root-relative paths. // The 'image' is preprocessed to use absolute URLs. // @see webform_preprocess_image(). - $variables['image'] = [ - '#theme' => 'image', + $variables['image']['singleimage'] = [ + '#theme' => 'image' ]; } // Change the class from webform-image-file to webform-strawberryfield-image-file // to avoid webform_preprocess_image() messing up our IIIF link // by appending the current global. // @TODO Style element here feels like a hack. We can do better - $variables['image'] += [ + $variables['image']['singleimage'] += [ '#uri' => $url, '#attributes' => [ 'class' => ['webform-strawberryfield-image-file'], @@ -439,26 +439,26 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v // For the Results table always display the file name as a tooltip. if (strpos(\Drupal::routeMatch()->getRouteName(), 'webform.results_submissions') !== FALSE) { $variables['attached']['library'][] = 'webform/webform.tooltip'; - $variables['image']['#attributes']['class'][] = 'js-webform-tooltip-link'; + $variables['image']['singleimage']['#attributes']['class'][] = 'js-webform-tooltip-link'; } // Wrap 'image' in a link/modal. if ($format && $format != 'image') { - $variables['image'] = [ + $variables['image']['singleimage'] = [ '#type' => 'link', '#title' => $variables['image'], '#url' => $url, ]; switch ($format) { case 'modal': - $variables['image'] += [ + $variables['image']['singleimage'] += [ '#attributes' => ['class' => ['js-webform-image-file-modal', 'webform-image-file-modal']], '#attached' => ['library' => ['webform/webform.element.image_file.modal']], ]; break; case 'link': - $variables['image'] += ['#attributes' => ['class' => ['webform-image-file-link']]]; + $variables['image']['singleimage'] += ['#attributes' => ['class' => ['webform-image-file-link']]]; break; } } From 42a399ee84fc4099b9562cf3d90be309632f172c Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 03:38:05 -0500 Subject: [PATCH 41/54] Try to add a table. Lets see --- webform_strawberryfield.module | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 5de06f3..6c663c1 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -308,6 +308,10 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $file = $variables['file']; $variables['image']['#type'] = 'container'; + unset($variables['image']['#theme']); + $variables['image']['singleimage']['#attributes'] = $variables['image']['#attributes']; + unset($variables['image']['#uri']); + unset($variables['image']['#attributes']); $style_name = $variables['style_name']; $format = $variables['format']; @@ -390,12 +394,28 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v ->addMessage(t('Ups. We could not get EXIF from your file. Sorry.')); } else { - $variables['image']['exif'] = [ - '#type' => 'markup', - '#markup' => $output - ]; + $more_str = implode('',$output); + + $json = json_decode($more_str, TRUE); + $json_error = json_last_error(); + if ($json_error == JSON_ERROR_NONE) { + $more = [ + '#type' => 'table', + '#caption' => $this->t('EXIF Data'), + '#header' => [$this->t('Property'), $this->t('Value')], + '#rows' => $json + ]; + } else { + $more = 'Sorry, we could not fetch EXIF data for this file'; + } + + $variables['image']['exif'] = [ + '#theme' => 'webform_element_more', + '#more' => $more, + '#more_title' => t('Exif'), + ]; } } From 476f9992a8cd74b33144f734248ef590c8fe3056 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 03:39:25 -0500 Subject: [PATCH 42/54] Oh gosh, procedural nightmare --- webform_strawberryfield.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 6c663c1..6831a5b 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -401,8 +401,8 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v if ($json_error == JSON_ERROR_NONE) { $more = [ '#type' => 'table', - '#caption' => $this->t('EXIF Data'), - '#header' => [$this->t('Property'), $this->t('Value')], + '#caption' => t('EXIF Data'), + '#header' => [t('Property'), t('Value')], '#rows' => $json ]; From 99a26aeeac58586b46275ab1d252776c14d7fb14 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 03:41:09 -0500 Subject: [PATCH 43/54] mmm. Format is not matching --- webform_strawberryfield.module | 1 + 1 file changed, 1 insertion(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 6831a5b..04e9560 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -397,6 +397,7 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $more_str = implode('',$output); $json = json_decode($more_str, TRUE); + dpm($json); $json_error = json_last_error(); if ($json_error == JSON_ERROR_NONE) { $more = [ From 6a0938032282ed74300228e3cbe600df3860fe5d Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 03:43:14 -0500 Subject: [PATCH 44/54] 0 index --- webform_strawberryfield.module | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 04e9560..a04af72 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -397,14 +397,14 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $more_str = implode('',$output); $json = json_decode($more_str, TRUE); - dpm($json); $json_error = json_last_error(); - if ($json_error == JSON_ERROR_NONE) { + //This will end with all data in an [0] index. + if ($json_error == JSON_ERROR_NONE && count($json)) { $more = [ '#type' => 'table', '#caption' => t('EXIF Data'), '#header' => [t('Property'), t('Value')], - '#rows' => $json + '#rows' => $json[0]; ]; } else { From fdd4f425b0050f6102a704be48565bb1a65cb491 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 03:43:37 -0500 Subject: [PATCH 45/54] Sleepy --- webform_strawberryfield.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index a04af72..f499bcf 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -404,7 +404,7 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v '#type' => 'table', '#caption' => t('EXIF Data'), '#header' => [t('Property'), t('Value')], - '#rows' => $json[0]; + '#rows' => $json[0], ]; } else { From 919673d137c06ccd0de79541ba45772e0a1369e2 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 04:02:39 -0500 Subject: [PATCH 46/54] And done --- webform_strawberryfield.module | 98 ++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index f499bcf..d2a5fc6 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -296,7 +296,9 @@ function webform_strawberryfield_theme_suggestions_webform_alter(&$suggestions, * - style_name: An image style name. * - format: Image formatting (link or modal) */ -function webform_strawberryfield_preprocess_webform_element_image_file(array &$variables) { +function webform_strawberryfield_preprocess_webform_element_image_file( + array &$variables +) { if (!empty($variables['file'])) { // TODO do we need a setting for this? @@ -328,11 +330,15 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $iiifserversettings->get('int_server_url') ); // Deal with Drupal 8.8.x v/s 8.7 - if (method_exists(\Drupal::service('stream_wrapper_manager'),'getTarget')) { + if (method_exists( + \Drupal::service('stream_wrapper_manager'), + 'getTarget' + )) { $iiifidentifier = urlencode( \Drupal::service('stream_wrapper_manager')->getTarget( $file->getFileUri() - )); + ) + ); } else { $iiifidentifier = urlencode(file_uri_target($file->getFileUri())); @@ -370,66 +376,92 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v $route_parameters = [ 'uuid' => $file->uuid(), - 'format' => 'default.'. pathinfo($file->getFilename(), PATHINFO_EXTENSION) + 'format' => 'default.' . pathinfo( + $file->getFilename(), + PATHINFO_EXTENSION + ), ]; - $publicurl = Url::fromRoute('format_strawberryfield.tempiiifbinary', $route_parameters); + $publicurl = Url::fromRoute( + 'format_strawberryfield.tempiiifbinary', + $route_parameters + ); $url = $publicurl->toString(); // Extract EXIF and show it to the user! /** @var \Drupal\Core\File\FileSystem $file_system */ - $scheme = \Drupal::service('file_system')->uriScheme($uri); + $scheme = \Drupal::service('file_system')->uriScheme($uri); $templocation = NULL; // If the file isn't stored locally make a temporary copy. - if (!isset(\Drupal::service('stream_wrapper_manager') - ->getWrappers(StreamWrapperInterface::LOCAL)[$scheme])) { + if (!isset( + \Drupal::service('stream_wrapper_manager') + ->getWrappers(StreamWrapperInterface::LOCAL)[$scheme] + )) { // Local stream. $cache_key = md5($uri); - $templocation = \Drupal::service('file_system')->copy($uri, 'temporary://sbr_' . $cache_key . '_' . basename($uri), FileSystemInterface::FILE_EXISTS_REPLACE); - } else { - $templocation = \Drupal::service('file_system')->realpath($file->getFileUri()); + $templocation = \Drupal::service('file_system')->copy( + $uri, + 'temporary://sbr_' . $cache_key . '_' . basename($uri), + FileSystemInterface::FILE_EXISTS_REPLACE + ); + } + else { + $templocation = \Drupal::service('file_system')->realpath( + $file->getFileUri() + ); } if ($templocation) { - $result = exec('exiftool -json -q '.escapeshellcmd($templocation), $output, $status); + $result = exec( + 'exiftool -json -q ' . escapeshellcmd($templocation), + $output, + $status + ); if ($status != 0) { \Drupal::service('messenger') - ->addMessage(t('Ups. We could not get EXIF from your file. Sorry.')); + ->addMessage( + t('Ups. We could not get EXIF from your file. Sorry.') + ); } else { - $more_str = implode('',$output); - + $more_str = implode('', $output); $json = json_decode($more_str, TRUE); $json_error = json_last_error(); //This will end with all data in an [0] index. if ($json_error == JSON_ERROR_NONE && count($json)) { + $rows = []; + foreach ($json[0] as $key => $value) { + if (!in_array($key, ['Directory', 'SourceFile'])) { + $rows[] = [$key, $value]; + } + } $more = [ '#type' => 'table', '#caption' => t('EXIF Data'), '#header' => [t('Property'), t('Value')], - '#rows' => $json[0], + '#rows' => $rows, ]; - - } else { + } + else { $more = 'Sorry, we could not fetch EXIF data for this file'; } - $variables['image']['exif'] = [ '#theme' => 'webform_element_more', - '#more' => $more, + '#more' => $more, '#more_title' => t('Exif'), ]; } } - - - - } $extension = pathinfo($uri, PATHINFO_EXTENSION); - $is_image = in_array($extension, ['gif', 'png', 'jpg', 'jpeg', 'jp2', 'tiff']); + $is_image = in_array( + $extension, + ['gif', 'png', 'jpg', 'jpeg', 'jp2', 'tiff'] + ); // Build image. - if ($is_image && \Drupal::moduleHandler()->moduleExists('image') && $style_name && ImageStyle::load($style_name)) { + if ($is_image && \Drupal::moduleHandler()->moduleExists( + 'image' + ) && $style_name && ImageStyle::load($style_name)) { $variables['image']['singleimage'] = [ '#theme' => 'image_style', '#style_name' => $variables['style_name'], @@ -440,7 +472,7 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v // The 'image' is preprocessed to use absolute URLs. // @see webform_preprocess_image(). $variables['image']['singleimage'] = [ - '#theme' => 'image' + '#theme' => 'image', ]; } // Change the class from webform-image-file to webform-strawberryfield-image-file @@ -458,7 +490,10 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v ]; // For the Results table always display the file name as a tooltip. - if (strpos(\Drupal::routeMatch()->getRouteName(), 'webform.results_submissions') !== FALSE) { + if (strpos( + \Drupal::routeMatch()->getRouteName(), + 'webform.results_submissions' + ) !== FALSE) { $variables['attached']['library'][] = 'webform/webform.tooltip'; $variables['image']['singleimage']['#attributes']['class'][] = 'js-webform-tooltip-link'; } @@ -473,7 +508,12 @@ function webform_strawberryfield_preprocess_webform_element_image_file(array &$v switch ($format) { case 'modal': $variables['image']['singleimage'] += [ - '#attributes' => ['class' => ['js-webform-image-file-modal', 'webform-image-file-modal']], + '#attributes' => [ + 'class' => [ + 'js-webform-image-file-modal', + 'webform-image-file-modal', + ], + ], '#attached' => ['library' => ['webform/webform.element.image_file.modal']], ]; break; From 583bbd379d2ba1badeca9bb1a31c64c2474877ea Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 23 Jan 2020 11:41:13 -0500 Subject: [PATCH 47/54] Wrong Constant --- webform_strawberryfield.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index d2a5fc6..1f0fc4b 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -400,7 +400,7 @@ function webform_strawberryfield_preprocess_webform_element_image_file( $templocation = \Drupal::service('file_system')->copy( $uri, 'temporary://sbr_' . $cache_key . '_' . basename($uri), - FileSystemInterface::FILE_EXISTS_REPLACE + FileSystemInterface::EXISTS_REPLACE ); } else { From 9c41c52c3d0ef9312bf0a2eff97791746c2252eb Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Sun, 26 Jan 2020 16:11:47 -0500 Subject: [PATCH 48/54] Add ImageStyle class use --- webform_strawberryfield.module | 1 + 1 file changed, 1 insertion(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index 1f0fc4b..db09cde 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -12,6 +12,7 @@ use Drupal\file\Entity\File; use Drupal\format_strawberryfield\Tools\IiifHelper; use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\File\FileSystemInterface; +use Drupal\image\Entity\ImageStyle; /** * Alters a webform to help with embbeding in a node crud context. From 3451b1570b9086ab085c4b808c992f7ff9672efe Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Sun, 26 Jan 2020 21:04:33 -0500 Subject: [PATCH 49/54] make sure we cast $templocation to the real path This was missing. System calls know nothing about streamwrappers, and i happened to know that and neglected! --- webform_strawberryfield.module | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webform_strawberryfield.module b/webform_strawberryfield.module index db09cde..089d9ce 100644 --- a/webform_strawberryfield.module +++ b/webform_strawberryfield.module @@ -403,6 +403,9 @@ function webform_strawberryfield_preprocess_webform_element_image_file( 'temporary://sbr_' . $cache_key . '_' . basename($uri), FileSystemInterface::EXISTS_REPLACE ); + $templocation = \Drupal::service('file_system')->realpath( + $templocation + ); } else { $templocation = \Drupal::service('file_system')->realpath( From 0ab33c83146f5a6d580f6a25f9728e06232daf6b Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 28 Jan 2020 16:50:35 -0500 Subject: [PATCH 50/54] Fixing one warning at a time Not a fix yet, just cleaning some messy undefined options. --- src/Element/WebformPanoramaTour.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index c110d3d..9a74aa4 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -195,6 +195,7 @@ public static function processWebformComposite( } } $all_scene_nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($all_scenes_nodeids); + $options = []; foreach ($all_scene_nodes as $entity) { $options[$entity->id()] = $entity->label(); } @@ -317,6 +318,7 @@ public static function processWebformComposite( $element['hotspots_temp']['adoscene'] = [ '#title' => t('Linkable Scenes'), '#type' => 'select', + "#empty_option" => t('- Select a loaded Scene -'), '#options' => $optionscenes, '#states' => [ 'visible' => [ @@ -515,10 +517,12 @@ public static function selectSceneSubmit( if ($current_scene) { $all_scenes_key = $top_element['#name'] . '-allscenes'; $all_scenes = $form_state->get($all_scenes_key); - foreach ($all_scenes as $scene) { - if (isset($scene['scene']) && $scene['scene'] == $current_scene) { - $alreadythere = TRUE; - break; + if (is_array($all_scenes)) { + foreach ($all_scenes as $scene) { + if (isset($scene['scene']) && $scene['scene'] == $current_scene) { + $alreadythere = TRUE; + break; + } } } if (!$alreadythere) { @@ -1128,7 +1132,6 @@ public static function valueCallback( $to_return = (is_array($input)) ? $input + $default_value : $default_value; error_log('what is in the element default_value before valuecallback return'); - error_log(print_r(array_keys($element['#default_value']),true)); error_log('return of valueCallback'); error_log(print_r($to_return,true)); From f931f10fa2ed5d19ed1c43d2b6984e2f5269ebc5 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 28 Jan 2020 16:52:59 -0500 Subject: [PATCH 51/54] Wrongly name variable --- src/Element/WebformPanoramaTour.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 9a74aa4..f39ecec 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -790,7 +790,7 @@ public static function addHotSpotCallBack( $element_name = $element['#name']; $response = new AjaxResponse(); $data_selector = $element['hotspots_temp']['added_hotspots']['#attributes']['data-drupal-loaded-node-hotspot-table']; - $existing_object = []; + $existing_objects = []; if ($form_state->getValue([$element_name, 'scene'])) { $all_scenes_key = $element_name . '-allscenes'; $allscenes = $form_state->get($all_scenes_key); From a530887116187ea339ce967f684a6dfef2fd3973 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 28 Jan 2020 17:00:16 -0500 Subject: [PATCH 52/54] More undefined values --- src/Element/WebformPanoramaTour.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index f39ecec..f21715f 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -186,6 +186,8 @@ public static function processWebformComposite( $all_scenes_nodeids = []; if ($node) { + // Will contain all currently loaded scenes as an Select option array. + $options = []; // If we have multiple Scenes,deal with it. if (!empty($all_scenes) && is_array($all_scenes)) { //dpm($all_scenes); @@ -195,7 +197,7 @@ public static function processWebformComposite( } } $all_scene_nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($all_scenes_nodeids); - $options = []; + foreach ($all_scene_nodes as $entity) { $options[$entity->id()] = $entity->label(); } @@ -311,7 +313,7 @@ public static function processWebformComposite( ], ] ]; - $optionscenes = $options; + $optionscenes = is_array($options) ? $options : []; // Remove from linkable scenes the current loaded one unset($optionscenes[$nodeid]); From fdb1213e48413c0592035608b199ea7165988e77 Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Tue, 28 Jan 2020 17:07:37 -0500 Subject: [PATCH 53/54] Dynamic Element swapping is totally not working This is just for testing, disabling the select element in the meantime. --- src/Element/WebformPanoramaTour.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index f21715f..766a4d9 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -203,13 +203,13 @@ public static function processWebformComposite( } // If we have loaded values, create a copy of select but just to add //new ones - $element['newscene'] = $element['scene']; + /* $element['newscene'] = $element['scene']; $element['newscene']['#weight'] = 2; $element['newselect_button'] = $element['select_button']; $element['newselect_button']['#weight'] = 3; $element['newselect_button']['#value'] = t('Add another Scene'); // If we have loaded values, replace autocomplete with this - $element['scene'] = [ + /* $element['scene'] = [ '#title' => t('Editing Scene'), '#type' => 'select', '#options' => $options, @@ -225,6 +225,7 @@ public static function processWebformComposite( $element['select_button'] = [ '#attributes' => ['class' => ['js-hide']] ]; + */ } From c11dddc50edd301ce4ec7f72906e8d1963c85b1b Mon Sep 17 00:00:00 2001 From: Diego Pino Navarro Date: Thu, 6 Feb 2020 17:41:38 -0500 Subject: [PATCH 54/54] Fix this thing for good (celebrating) Finally got it working and did some cleanup. set initiaThis loads stored complex Panorama Tours and also creates new ones on the fly. Connects Panoramas to each other, allows Other Objects to be used as Pop Ups and also can set initial Position for panoramas, so you are actually looking to where you want they they are loaded, or via navigation you end there. Catch/main thing here is our valuecallback and the fact that we are dealing with an initial situation where form state is not being preserved because there is no form rebuild happening but also, cache has not happened yet. The fix is a little over worked and required tapping into the whole formBuilder class and looking at the exact data flow inside the formstates. For security, consistency, since this is an actual APP inside a single form element, we keep now the full 'All Scenes' complex array inside a Form State Value but also as an input, serialized as a JSON string in a hidden field. Feels over-engineered, but for some Webform reason, Value elements don't play well/nor are seeing by the ValueCallback and the Composite Webform base class. What is still needed now? More testing (tonight) and fill the holes of missing element validation callbacks, to make sure people actually do what is required. This will take me 3-4 more hours. 1200 lines of code to make this happen. UI/UX is such a wonderful thing.... SO HAPPY!!! --- src/Element/WebformPanoramaTour.php | 471 ++++++++---------- .../WebformElement/WebformPanoramaTour.php | 15 +- 2 files changed, 223 insertions(+), 263 deletions(-) diff --git a/src/Element/WebformPanoramaTour.php b/src/Element/WebformPanoramaTour.php index 766a4d9..a2c0418 100644 --- a/src/Element/WebformPanoramaTour.php +++ b/src/Element/WebformPanoramaTour.php @@ -3,6 +3,7 @@ namespace Drupal\webform_strawberryfield\Element; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element\FormElement; use Drupal\webform\Element\WebformCompositeBase; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Ajax\AjaxResponse; @@ -15,7 +16,7 @@ /** - * Provides a webform element for a Getty Vocab element. + * Provides a webform element for a Panorama Tour Builder with hotspots. * * @FormElement("webform_metadata_panoramatour") */ @@ -25,21 +26,62 @@ class WebformPanoramaTour extends WebformCompositeBase { * {@inheritdoc} */ public function getInfo() { - //@TODO add an extra option to define auth_type. - //@TODO expose as an select option inside \Drupal\webform_strawberryfield\Plugin\WebformElement\WebformGetty - $info = parent::getInfo(); - $info['#theme'] = 'webform_metadata_panoramatour'; + // @TODO allow user to set which View is used to populate Panoramas + // Allow user to select which view mode is used to render inline panoramas + $class = get_class($this); + $info = [ + '#input' => TRUE, + '#access' => TRUE, + '#process' => [ + [$class, 'processWebformComposite'], + [$class, 'processAjaxForm'], + ], + '#pre_render' => [ + [$class, 'preRenderWebformPanoramaTourElement'], + ], + '#title_display' => 'invisible', + '#required' => FALSE, + '#flexbox' => TRUE, + '#theme' => 'webform_metadata_panoramatour', + ]; + return $info; } + + /** + * Render API callback: Hides display of the upload or remove controls. + * + * Upload controls are hidden when a file is already uploaded. Remove controls + * are hidden when there is no file attached. Controls are hidden here instead + * of in \Drupal\file\Element\ManagedFile::processManagedFile(), because + * #access for these buttons depends on the managed_file element's #value. See + * the documentation of \Drupal\Core\Form\FormBuilderInterface::doBuildForm() + * for more detailed information about the relationship between #process, + * #value, and #access. + * + * Because #access is set here, it affects display only and does not prevent + * JavaScript or other untrusted code from submitting the form as though + * access were enabled. The form processing functions for these elements + * should not assume that the buttons can't be "clicked" just because they are + * not displayed. + * + * @see \Drupal\file\Element\ManagedFile::processManagedFile() + * @see \Drupal\Core\Form\FormBuilderInterface::doBuildForm() + */ + public static function preRenderWebformPanoramaTourElement($element) { + return $element; + } + /** * {@inheritdoc} */ public static function getCompositeElements(array $element) { - $elements = []; - $class = '\Drupal\webform_strawberryfield\Element\WebformPanoramaTour'; - //@TODO all this settings need to be exposed to the Webform element. + $elements = []; + $elements['allscenes'] = [ + '#type' => 'hidden', + ]; return $elements; } @@ -52,41 +94,55 @@ public static function processWebformComposite( &$complete_form ) { + $element = parent::processWebformComposite($element, $form_state, $complete_form); + // Just in case someone wants to trick us // This is already disabled on the Config Form for the Element unset($element['#multiple']); + $element_name = $element['#name']; - $element = parent::processWebformComposite( - $element, - $form_state, - $complete_form - ); + $all_scenes = []; + + $all_scenes_key = $element_name . '-allscenes'; + $all_scenes_from_form_state = !empty($form_state->getValue([$element['#name'],'allscenes'])) ? json_decode($form_state->getValue([$element['#name'],'allscenes']),TRUE) : []; + + $all_scenes_from_form_value = !empty($form_state->get($all_scenes_key)) ? $form_state->get($all_scenes_key) : []; - $element_name = $element['#name']; - $all_scenes_key = $element['#name'] . '-allscenes'; - $all_scenes = $form_state->get($all_scenes_key) ? $form_state->get($all_scenes_key) : []; + // Basically. First try to get it from the defaults set by the ::valuecallback(). + // This will populate Form state value. + // But once that happens, the #value elements does not survive. + // So we store it in a form state variable. And keep setting it. + $all_scenes = !empty($all_scenes_from_form_value) ? $all_scenes_from_form_value : $all_scenes_from_form_state; + + if (!empty($all_scenes)) { + $form_state->set($all_scenes_key,$all_scenes ); + } + + // Double set it. One for the input, one for the form state. + // Reason is we have a complex ::valuecallback() here + // and when used in the widget a form inside a form + // This reasonably deals with both needs. + $element['allscenes']['#default_value'] = json_encode($all_scenes,true); + $form_state->setValue([$element['#name'],'allscenes'],json_encode($all_scenes,TRUE)); + + $currentscene = $form_state->getValue([$element['#name'], 'currentscene']); - // If no initial 'scene' value, use first key of the allmighty - // Full array. $sceneid = NULL; - if (!$form_state->getValue([$element['#name'], 'scene']) && !empty($all_scenes)) { + if (!$currentscene && !empty($all_scenes)) { $scene = reset($all_scenes); $sceneid = $scene['scene']; } else { - $sceneid = $form_state->getValue([$element['#name'], 'scene']); + $sceneid = $currentscene; $form_state->setValue([$element_name, 'scene'],$sceneid); } - - // Fetch saved/existing hotspots and transform them into StdClass Objects $hotspot_list = []; foreach ($all_scenes as $scene) { // Fetching from full array - error_log('fetching from full array'); if (isset($scene['scene']) && $scene['scene'] == $sceneid) { $hotspot_list = $scene['hotspots']; } @@ -106,24 +162,27 @@ public static function processWebformComposite( 'arguments' => ['Panorama'] ], ], + '#limit_validation_errors' => [$element['#parents']], ]; + $element['hotspots'] = [ '#type' => 'value' ]; - $element['select_button'] = [ '#title' => 'Select Scene', '#type' => 'submit', '#value' => t('Select Scene'), '#name' => $element['#name'] . '_select_button', - '#submit' => [[static::class, 'selectSceneSubmit']], + '#submit' => [[get_called_class(), 'selectSceneSubmit']], '#ajax' => [ - 'callback' => [static::class, 'selectSceneCallBack'], + 'callback' => [get_called_class(), 'selectSceneCallBack'], ], '#button_type' => 'default', '#visible' => 'true', + '#limit_validation_errors' => [$element['#parents']], ]; + $element['hotspots_temp'] = [ '#weight' => 4, '#type' => 'fieldset', @@ -148,17 +207,7 @@ public static function processWebformComposite( ], ]; - // Note. The $element['hotspots_temp'] 'data-drupal-selector' gets - // Modified during rendering to 'edit-' . $element['#name'] . '-hotspots-temp' - - // @TODO Modal will need to be enabled on the formatter too. - // Some changes here. If we have multiple values we need a new flag - // For the current selected index in the Scene list. - //dpm($form_state->getValues()); - - - - + // If we have a currently selected scene if ($sceneid) { $nodeid = $sceneid; @@ -175,14 +224,14 @@ public static function processWebformComposite( $vb = \Drupal::entityTypeManager()->getViewBuilder( 'node' ); // Drupal\node\NodeViewBuilder + //@TODO we need viewmode to be configurable! //@TODO We could also generate a view mode on the fly. + //$viewmode = 'digital_object_with_pannellum_panorama_'; - $viewmode = 'digital_object_with_pannellum_panorama_'; $node = \Drupal::entityTypeManager()->getStorage('node')->load( $nodeid ); - $errors = []; $all_scenes_nodeids = []; if ($node) { @@ -190,7 +239,6 @@ public static function processWebformComposite( $options = []; // If we have multiple Scenes,deal with it. if (!empty($all_scenes) && is_array($all_scenes)) { - //dpm($all_scenes); foreach($all_scenes as $key => $scene) { if (isset($scene['scene'])) { $all_scenes_nodeids[$key] = $scene['scene']; @@ -201,35 +249,29 @@ public static function processWebformComposite( foreach ($all_scene_nodes as $entity) { $options[$entity->id()] = $entity->label(); } - // If we have loaded values, create a copy of select but just to add - //new ones - /* $element['newscene'] = $element['scene']; - $element['newscene']['#weight'] = 2; - $element['newselect_button'] = $element['select_button']; - $element['newselect_button']['#weight'] = 3; - $element['newselect_button']['#value'] = t('Add another Scene'); - // If we have loaded values, replace autocomplete with this - /* $element['scene'] = [ + + $element['currentscene']['#weight'] = 2; + $element['currentscene'] = [ '#title' => t('Editing Scene'), '#type' => 'select', '#options' => $options, '#default_value' => $node->id(), '#ajax' => [ - 'callback' => [static::class, 'changeSceneCallBack'], + 'callback' => [get_called_class(), 'changeSceneCallBack'], 'event' => 'change', ], - '#submit' => [[static::class, 'changeSceneSubmit']], + '#submit' => [[get_called_class(), 'changeSceneSubmit']], ]; - // Hide the select button, at least visually + /*// Hide the select button, at least visually // because it defines the main submit $element['select_button'] = [ '#attributes' => ['class' => ['js-hide']] - ]; - */ + ];*/ + } - $nodeview = $vb->view($node, $viewmode); + $nodeview = $vb->view($node); $element['hotspots_temp']['node'] = $nodeview; $element['hotspots_temp']['node']['#weight'] = -10; @@ -244,9 +286,6 @@ public static function processWebformComposite( '#weight' => 11, ]; - - - $element['hotspots_temp']['label'] = [ '#prefix' => '
', '#title' => t('The label to display on mouse over'), @@ -293,7 +332,7 @@ public static function processWebformComposite( 'text' => 'Text', 'url' => 'An External URL', 'ado' => 'Another Digital Object', - 'scene' => 'Another Panorama Scene', // Still missing + 'scene' => 'Another Panorama Scene', ], '#attributes' => [ 'data-drupal-loaded-node' => $nodeid, @@ -361,20 +400,19 @@ public static function processWebformComposite( // To make sure menu variables are passed // we limit validation errors to those elements - $limit = array_merge($element['#parents'], ['hotspots_temp']); $element['hotspots_temp']['add_hotspot'] = [ '#prefix' => '
', '#type' => 'submit', '#value' => t('Add Hotspot'), '#name' => $element['#name'] . '_addhotspot_button', - '#submit' => [[static::class, 'addHotspotSubmit']], + '#submit' => [[get_called_class(), 'addHotspotSubmit']], '#ajax' => [ - 'callback' => [static::class, 'addHotSpotCallBack'], + 'callback' => [get_called_class(), 'addHotSpotCallBack'], ], '#button_type' => 'default', '#visible' => 'true', - '#limit_validation_errors' => FALSE + '#limit_validation_errors' => [$element['#parents']], ]; @@ -382,22 +420,22 @@ public static function processWebformComposite( '#type' => 'submit', '#value' => t('Set Initial Scene Orientation'), '#name' => $element['#name'] . '_setsceneorientation_button', - '#submit' => [[static::class, 'setSceneOrientation']], + '#submit' => [[get_called_class(), 'setSceneOrientation']], '#ajax' => [ - 'callback' => [static::class, 'setSceneOrientationCallBack'], + 'callback' => [get_called_class(), 'setSceneOrientationCallBack'], ], '#button_type' => 'default', '#visible' => 'true', - '#limit_validation_errors' => FALSE, + '#limit_validation_errors' => [$element['#parents']], ]; $element['hotspots_temp']['delete_scene'] = [ '#type' => 'submit', '#value' => t('Delete this Scene'), '#name' => $element['#name'] . '_deletescene_button', - '#submit' => [[static::class, 'deleteScene']], + '#submit' => [[get_called_class(), 'deleteScene']], '#ajax' => [ - 'callback' => [static::class, 'deleteSceneCallBack'], + 'callback' => [get_called_class(), 'deleteSceneCallBack'], ], '#button_type' => 'default', '#visible' => 'true', @@ -425,7 +463,7 @@ public static function processWebformComposite( 'label' => t('Label'), 'operations' => t('Operations'), ]; - + $table_options = []; foreach ($hotspot_list as $key => $hotspot) { if (is_array($hotspot)) { $hotspot = (object) $hotspot; @@ -444,7 +482,6 @@ public static function processWebformComposite( ]; } - $element['hotspots_temp']['added_hotspots'] = [ '#prefix'=> '
', '#suffix'=> '
', @@ -462,13 +499,58 @@ public static function processWebformComposite( } } } - $element['#element_validate'][] = [static::class, 'validateHotSpotItems']; + // TODO. + $element['#element_validate'][] = [get_called_class(), 'validatePanoramaTourElement']; return $element; } + /** - * Submit Handler for the Select Scene Submit call. + * {@inheritdoc} + */ + public static function valueCallback(&$element, $input, FormStateInterface $form_state) { + /** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */ + $element_manager = \Drupal::service('plugin.manager.webform.element'); + $composite_elements = static::getCompositeElements($element); + $composite_elements = WebformElementHelper::getFlattened($composite_elements); + + // Get default value for inputs. + $default_value = []; + foreach ($composite_elements as $composite_key => $composite_element) { + $element_plugin = $element_manager->getElementInstance($composite_element); + if ($element_plugin->isInput($composite_element)) { + $default_value[$composite_key] = ''; + } + } + if ($input === FALSE) { + if (empty($element['#default_value']) || !is_array($element['#default_value'])) { + $to_return = $element['#default_value'] = []; + } + else { + $to_return = $element['#default_value']; + $to_return['allscenes'] = array_filter($to_return, function($k) { + return is_integer($k); + }, ARRAY_FILTER_USE_KEY); + if (is_array($to_return['allscenes'])) { + $to_return['allscenes'] = json_encode($to_return['allscenes']); + } + } + + return $to_return + $default_value; + } + + return (is_array($input)) ? $input + $default_value : $default_value; + + } + + + + + + + /** + * Main Submit Handler for the Select Scene Submit call. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -478,7 +560,7 @@ public static function selectSceneSubmit( FormStateInterface $form_state ) { $button = $form_state->getTriggeringElement(); - error_log('selectSceneSubmit'); + $input = $form_state->getUserInput(); // Hack. No explanation. $main_element_parents = array_slice($button['#array_parents'], 0, -1); @@ -514,11 +596,13 @@ public static function selectSceneSubmit( } else { // Try first with the new scene button, if no value, go back to scene button. - $current_scene = $form_state->getValue([$top_element['#name'], 'newscene']); - $current_scene = $current_scene ? $current_scene : $form_state->getValue([$top_element['#name'], 'scene']); + $current_scene = $form_state->getValue([$top_element['#name'], 'scene']); + $current_scene = $current_scene ? $current_scene : $form_state->getValue( + [$top_element['#name'], 'currentscene'] + ); + $all_scenes_key = $top_element['#name'] . '-allscenes'; $alreadythere = FALSE; if ($current_scene) { - $all_scenes_key = $top_element['#name'] . '-allscenes'; $all_scenes = $form_state->get($all_scenes_key); if (is_array($all_scenes)) { foreach ($all_scenes as $scene) { @@ -533,7 +617,10 @@ public static function selectSceneSubmit( 'scene' => $current_scene, 'hotspots' => [], ]; - $form_state->set($all_scenes_key,$all_scenes); + + $form_state->setValue([$top_element['#name'], 'allscenes'],json_encode($all_scenes)); + $form_state->set($all_scenes_key, $all_scenes); + $form_state->setValue([$top_element['#name'], 'currentscene'], $current_scene); } } } @@ -579,15 +666,6 @@ public static function changeSceneSubmit( array &$form, FormStateInterface $form_state ) { - $button = $form_state->getTriggeringElement(); - error_log('changeSceneSubmit'); - $input = $form_state->getUserInput(); - // Hack. No explanation. - $main_element_parents = array_slice($button['#array_parents'], 0, -1); - - $top_element = NestedArray::getValue($form, $main_element_parents); - error_log('triggering element name'. $input['_triggering_element_name']); - // Rebuild the form. $form_state->setRebuild(TRUE); } @@ -607,7 +685,6 @@ public static function changeSceneCallBack( $form, array_slice($button['#array_parents'], 0, -1) ); - error_log('changeSceneCallBack'); $response = new AjaxResponse(); $element_name = $element['#name']; $data_selector = $element['hotspots_temp']['#attributes']['data-webform_strawberryfield-selector']; @@ -642,8 +719,6 @@ public static function changeSceneCallBack( ], ] ]; - error_log('attaching replacement Drupal settings for the viewer'); - error_log(print_r($settings,true)); // Why twice? well because merge is deep merge. Gosh JS! // And merge = FALSE clears even my brain settings... $response->addCommand(new SettingsCommand($settingsclear, TRUE)); @@ -671,30 +746,28 @@ public static function addHotspotSubmit( FormStateInterface $form_state ) { - error_log('addHotspotSubmit'); + $button = $form_state->getTriggeringElement(); $hot_spot_values_parents = array_slice($button['#parents'], 0, -1); - $element_name = $hot_spot_values_parents[0]; - if ($form_state->getValue([$element_name, 'scene'])) { - $all_scenes_key = $element_name . '-allscenes'; - $allscenes = $form_state->get($all_scenes_key); - $current_scene = $form_state->getValue([$element_name, 'scene']); + $all_scenes_key = $element_name . '-allscenes'; + + $allscenes = !empty($form_state->getValue([$element_name,'allscenes'])) ? json_decode($form_state->getValue([$element_name,'allscenes']),TRUE) : []; + + if ($form_state->getValue([$element_name, 'currentscene']) + && $allscenes) { + $current_scene = $form_state->getValue([$element_name, 'currentscene']); $scene_key = 0; $existing_objects = []; - error_log(print_r($form_state->getValues(), TRUE)); foreach ($allscenes as $key => &$scene) { - if (isset($scene['scene']) && $scene['scene'] == $form_state->getValue( - [$element_name, 'scene'] - )) { - $scene_key = $key; + if (isset($scene['scene']) && $scene['scene'] == $current_scene + ) { + $scene_key = (int) $key; $existing_objects = $scene['hotspots']; break; } } - $hotspot = new \stdClass; - $hotspot->pitch = $form_state->getValue( [$element_name, 'hotspots_temp', 'pitch'] ); @@ -707,7 +780,6 @@ public static function addHotspotSubmit( $hotspot->text = $form_state->getValue( [$element_name, 'hotspots_temp', 'label'] ); - $hotspot->id = $element_name . '_' . $current_scene . '_' .(count($existing_objects) + 1); if ($hotspot->type == 'url') { $hotspot->URL = $hotspot->url; @@ -748,8 +820,7 @@ public static function addHotspotSubmit( $hotspot->type = 'info'; } - error_log(var_export($hotspot,true)); - $existing_objects[] = $hotspot; + $existing_objects[] = (array) $hotspot; // @TODO make sure people don't add twice the same coordinates! @@ -757,19 +828,15 @@ public static function addHotspotSubmit( $allscenes[$scene_key]['hotspots'] = $existing_objects; $form_state->set($all_scenes_key, $allscenes); - error_log('done updating original array'); - - } + $form_state->setValue([$element_name, 'allscenes'],json_encode($allscenes)); + } else { + // Do we alert the user? Form needs to be restarted + static::messenger()->addError(t('Something bad happened with the Tour builder, sadly you will have you restart your session.')); + // We could set a form_state value and render it when the form rebuilds? + } - // This is strange but needed. - // If we are creating a new panorama, addhotspot submit button - // uses the scene submit (because this is nested) and hidden on initialize - // but if we start with data, its called directly. - // So we have the setRebuild on the parent caller - // \Drupal\webform_strawberryfield\Element\WebformPanoramaTour::selectSceneSubmit - // and also here. all good $form_state->setRebuild(TRUE); } @@ -789,21 +856,20 @@ public static function addHotSpotCallBack( $form, array_slice($button['#array_parents'], 0, -2) ); - error_log('addHotSpotCallBack'); $element_name = $element['#name']; $response = new AjaxResponse(); $data_selector = $element['hotspots_temp']['added_hotspots']['#attributes']['data-drupal-loaded-node-hotspot-table']; $existing_objects = []; - if ($form_state->getValue([$element_name, 'scene'])) { - $all_scenes_key = $element_name . '-allscenes'; - $allscenes = $form_state->get($all_scenes_key); + $current_scene = $form_state->getValue([$element_name, 'currentscene']); + if ($current_scene) { + $allscenes = $form_state->getValue([$element_name, 'allscenes']); + $allscenes = json_decode($allscenes, TRUE); $current_scene = $form_state->getValue([$element_name, 'scene']); $scene_key = 0; $existing_objects = []; foreach ($allscenes as $key => &$scene) { - if ($scene['scene'] == $form_state->getValue( - [$element_name, 'scene'] - )) { + if ($scene['scene'] == $current_scene + ) { $scene_key = $key; $existing_objects = $scene['hotspots']; } @@ -840,8 +906,6 @@ public static function setSceneOrientation( array &$form, FormStateInterface $form_state ) { - - error_log('setSceneOrientation'); $button = $form_state->getTriggeringElement(); $hot_spot_values_parents = array_slice($button['#parents'], 0, -1); @@ -852,7 +916,6 @@ public static function setSceneOrientation( $current_scene = $form_state->getValue([$element_name, 'scene']); $scene_key = 0; $existing_objects = []; - error_log(print_r($form_state->getValues(), TRUE)); foreach ($allscenes as $key => &$scene) { if ($scene['scene'] == $form_state->getValue( [$element_name, 'scene'] @@ -870,8 +933,7 @@ public static function setSceneOrientation( [$element_name, 'hotspots_temp', 'yaw']); $form_state->set($all_scenes_key, $allscenes); - error_log('done updating original array'); - + $form_state->setValue([$element_name, 'allscenes'],json_encode($allscenes)); } @@ -918,7 +980,7 @@ public static function setSceneOrientationCallBack( } /** - * Submit Handler for Settin the Scene Orientation. + * Submit Handler for Setting the Scene Orientation. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state @@ -958,6 +1020,7 @@ public static function deleteScene( }); } $form_state->set($all_scenes_key, $allscenes); + $form_state->setValue([$element_name, 'allscenes'],json_encode($allscenes)); if (!empty($allscenes)) { $firstscene = reset($allscenes); $firstsceneid = $firstscene['scene']; @@ -1050,169 +1113,71 @@ public static function deleteSceneCallBack( } - public static function valueCallback( - &$element, - $input, - FormStateInterface $form_state - ) { - error_log('valueCallback'); - /** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */ - $element_manager = \Drupal::service('plugin.manager.webform.element'); - $composite_elements = static::getCompositeElements($element); - $composite_elements = WebformElementHelper::getFlattened( - $composite_elements - ); - // Get default value for inputs. - $default_value = []; - foreach ($composite_elements as $composite_key => $composite_element) { - $element_plugin = $element_manager->getElementInstance( - $composite_element - ); - - if ($element_plugin->isInput($composite_element)) { - $default_value[$composite_key] = ''; - } - } - - // Used for multiscenes - $all_scenes_key = $element['#name'] . '-allscenes'; - $current_scene_key = $element['#name'] . '-currentscene'; - - if ($form_state->get($all_scenes_key)) { - // Merging with saved $all_scenes - error_log('Merge default values with whatever is that we got before'); - $default_value = array_merge($form_state->get($all_scenes_key), $default_value); - $default_value['scene'] = $form_state->get($current_scene_key); - } - - if ($input !== FALSE && $input !== NULL) { - error_log('we have input'); - error_log(print_r($input,true)); - } - - - - if (($input === FALSE) && !$form_state->get($all_scenes_key)) { - // OK, first load or webform auto processing, no input. - - error_log('valueCallback input empty and no all scenes yet'); - if (empty($element['#default_value']) || !is_array( - $element['#default_value'] - )) { - $element['#default_value'] = []; - } else { - - // Check if Default value is an array of scenes or just a single scene - if (isset($element['#default_value']['scene'])) { - error_log('Got a single scene'); - // cast into an array - $element['#default_value'] = [$element['#default_value']]; - // This will be our current scene - $element['#default_value']['scene'] = $element['#default_value'][0]['scene']; - // Means single scene. - $form_state->set($all_scenes_key, $element['#default_value']); - $form_state->set($current_scene_key, $element['#default_value']['scene']); - - } elseif (isset($element['#default_value'][0]['scene'])) { - error_log('multi scene'); - $form_state->set($all_scenes_key, $element['#default_value']); - $element['#default_value']['scene'] = $element['#default_value'][0]['scene']; - $form_state->set($current_scene_key, $element['#default_value']['scene']); - // Multi scene! - } - - foreach ($element['#default_value'] as $scene) { - // $scene could be a god damn button which is a translateable - if (is_array($scene) && $scene['scene'] == $element['#default_value']['scene']) { - $element['#default_value']['hotspots'] = $scene['hotspots']; - break; - } - } - } - - return $element['#default_value'] + $default_value; - } - - $to_return = (is_array($input)) ? $input + $default_value : $default_value; - error_log('what is in the element default_value before valuecallback return'); - error_log('return of valueCallback'); - error_log(print_r($to_return,true)); - - return $to_return; - - } - - public static function validateWebformComposite( - &$element, - FormStateInterface $form_state, - &$complete_form - ) { - error_log('validateWebformComposite'); - // What i learned. This god forgotten function - // Destroys my submission values... - // Since this is a composite and was never meant to have more than one button - // triggering element can be wrong - // Lets use the input and search of the actual element - - - $trigger = $form_state->getTriggeringElement(); + /** + * Validates a composite element. + */ + public static function validateWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) { // IMPORTANT: Must get values from the $form_states since sub-elements // may call $form_state->setValueForElement() via their validation hook. // @see \Drupal\webform\Element\WebformEmailConfirm::validateWebformEmailConfirm // @see \Drupal\webform\Element\WebformOtherBase::validateWebformOther - $value = NestedArray::getValue( - $form_state->getValues(), - $element['#parents'] - ); - + $value = NestedArray::getValue($form_state->getValues(), $element['#parents']); // Only validate composite elements that are visible. $has_access = (!isset($element['#access']) || $element['#access'] === TRUE); if ($has_access) { // Validate required composite elements. $composite_elements = static::getCompositeElements($element); - $composite_elements = WebformElementHelper::getFlattened( - $composite_elements - ); + $composite_elements = WebformElementHelper::getFlattened($composite_elements); foreach ($composite_elements as $composite_key => $composite_element) { $is_required = !empty($element[$composite_key]['#required']); $is_empty = (isset($value[$composite_key]) && $value[$composite_key] === ''); if ($is_required && $is_empty) { - WebformElementHelper::setRequiredError( - $element[$composite_key], - $form_state - ); + WebformElementHelper::setRequiredError($element[$composite_key], $form_state); } } } - error_log('validateWebformComposite'); // Clear empty composites value. if (empty(array_filter($value))) { - error_log('is empty'); $element['#value'] = NULL; $form_state->setValueForElement($element, NULL); } } - public static function validateHotSpotItems( + public static function validatePanoramaTourElement( &$element, FormStateInterface $form_state, &$complete_form ) { - error_log('validateHotSpotItems'); if ($triggering = $form_state->getTriggeringElement()) { if (reset($triggering['#parents']) == $element['#name']) { - error_log('triggered by our Tour builder'); // Means it was something inside the button } else { error_log('Clear our redundant values'); $form_state->unsetValue([$element['#name'], 'scene']); - $form_state->unsetValue([$element['#name'], 'newscene']); $form_state->unsetValue([$element['#name'], 'hotspots']); $form_state->unsetValue([$element['#name'], 'hotspots_temp']); - $form_state->unsetValue([$element['#name'], 'newselect_button']); + $form_state->unsetValue([$element['#name'], 'select_button']); + $form_state->unsetValue([$element['#name'], 'select_button']); + // sets that actual expanded array back into the element value + // but only when the validation is triggered by the main form, + // like in a next button action or a save one. + $value = NestedArray::getValue($form_state->getValues(), $element['#parents']); + // Let's try to get our direct form value into the main for element value + // if its not there, lets use our input. Feels safer since people won't + // have the chance to alter input via HTML. + // Inverse of that the process function does. + $all_scenes_key = $element['#name'] . '-allscenes'; + $all_scenes_from_form_state = !empty($form_state->getValue([$element['#name'],'allscenes'])) ? json_decode($form_state->getValue([$element['#name'],'allscenes']),TRUE) : []; + $all_scenes_from_form_value = !empty($form_state->get($all_scenes_key)) ? $form_state->get($all_scenes_key) : []; + // Basically. First try to get it from the defaults set by the ::valuecallback(). + // This will populate Form state value. + // But once that happens, the #value elements does not survive. + // So we store it in a form state variable. And keep setting it. + $all_scenes = !empty($all_scenes_from_form_value) ? $all_scenes_from_form_value : $all_scenes_from_form_state; + $form_state->setValueForElement($element, $all_scenes); } } } diff --git a/src/Plugin/WebformElement/WebformPanoramaTour.php b/src/Plugin/WebformElement/WebformPanoramaTour.php index a80951a..50caede 100644 --- a/src/Plugin/WebformElement/WebformPanoramaTour.php +++ b/src/Plugin/WebformElement/WebformPanoramaTour.php @@ -10,6 +10,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\webform\WebformSubmissionInterface; +use Drupal\webform\Plugin\WebformElement\WebformElement; use Drupal\webform\Plugin\WebformElement\WebformCompositeBase; /** @@ -47,16 +48,10 @@ protected function formatHtmlItemValue(array $element, WebformSubmissionInterfac */ protected function formatTextItemValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); - + // No preview for now + // Just too complex + // @TODO next iteration can force a Formatter to be used. $lines = []; - /*if (!empty($value['scene'])) { - $lines[] = $value['scene']; - } - if (!empty($value['hotspots'])) { - $lines[] = $value['hotspots']; - } - */ - return $lines; } @@ -74,7 +69,7 @@ public function getDefaultProperties() { public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); $form['element']['multiple']['#disabled'] = TRUE; - $form['element']['multiple']['#description'] = '' . $this->t('You can only build one Tour with this Webform Element.But it supports multiple Scenes') . ''; + $form['element']['multiple']['#description'] = '' . $this->t('You can only build one Tour with this Webform Element. But it supports multiple Scenes') . ''; // Disable Multiple Elements option return $form; }