diff --git a/.gitignore b/.gitignore
index 78b63a5..140221c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@
src/Tools/Iiif/IiifHelper.php
.idea/webform_strawberryfield.iml
src/.DS_Store
+.idea/runConfigurations/archipelago.xml
diff --git a/composer.json b/composer.json
index 461c6ae..298e794 100644
--- a/composer.json
+++ b/composer.json
@@ -14,6 +14,7 @@
],
"require": {
"php": ">=7.2",
+ "ext-json": "*",
"ml/json-ld": "^1.2",
"mtdowling/jmespath.php":"^2.5",
"strawberryfield/strawberryfield":"dev-1.0.0-RC3",
diff --git a/src/Controller/AuthAutocompleteController.php b/src/Controller/AuthAutocompleteController.php
index e0ddd0d..7651ef3 100644
--- a/src/Controller/AuthAutocompleteController.php
+++ b/src/Controller/AuthAutocompleteController.php
@@ -16,6 +16,7 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
+use GuzzleHttp\RequestOptions;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Url;
use Drupal\Component\Datetime\TimeInterface;
@@ -163,6 +164,9 @@ public function handleAutocomplete(Request $request, $auth_type, $vocab = 'subje
if (is_string($csrf_token)) {
$request_base = $request->getSchemeAndHttpHost().':'.$request->getPort();
$is_internal = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_ADDR'].':'.$_SERVER['SERVER_PORT'] == $request_base;
+ if (!$is_internal) {
+ $is_internal = $_SERVER['HTTP_HOST'] == $_SERVER['SERVER_NAME'];
+ }
}
if ($input) {
@@ -173,10 +177,9 @@ public function handleAutocomplete(Request $request, $auth_type, $vocab = 'subje
$cache_id = 'webform_strawberry:auth_lod:' . $cache_var;
$cached = $this->cacheGet($cache_id);
if ($cached) {
- error_log('cached');
return new JsonResponse($cached->data);
}
- if ($this->currentUser->isAnonymous() && !$is_internal) {
+ if ($this->currentUser->isAnonymous() && !$is_internal) {
sleep(1);
}
@@ -197,9 +200,15 @@ public function handleAutocomplete(Request $request, $auth_type, $vocab = 'subje
case 'viaf':
$results = $this->viaf($input);
break;
+ case 'mesh':
+ $results = $this->mesh($input, $vocab, $rdftype);
+ break;
+ case 'snac':
+ $results = $this->snac($input, $vocab, $rdftype);
+ break;
case 'europeana':
if ($apikey) {
- $results = $this->europeana($input, $vocab, $apikey);
+ $results = $this->europeana($input, $vocab, $apikey);
}
else {
$this->messenger()->addError(
@@ -273,7 +282,7 @@ protected function loc($input, $vocab, $rdftype) {
];
$path = $endpoint[$vocab];
- $input = urlencode($input);
+ $input = rawurlencode($input);
if ($vocab == 'rdftype') {
$urlindex = "/suggest/?q=" . $input . "&rdftype=" . $rdftype;
@@ -327,7 +336,7 @@ protected function loc($input, $vocab, $rdftype) {
* @return array
*/
protected function wikidata($input) {
- $input = urlencode($input);
+ $input = rawurlencode($input);
$urlindex = '&language=en&format=json&search=' . $input;
$baseurl = 'https://www.wikidata.org/w/api.php?action=wbsearchentities';
$remoteUrl = $baseurl . $urlindex;
@@ -602,7 +611,7 @@ protected function getty($input, $vocab = 'aat', $mode = 'fuzzy') {
* @return array
*/
protected function viaf($input) {
- $input = urlencode($input);
+ $input = rawurlencode($input);
$urlindex = '&query=' . $input;
$baseurl = 'https://viaf.org/viaf/AutoSuggest?';
$remoteUrl = $baseurl . $urlindex;
@@ -612,12 +621,12 @@ protected function viaf($input) {
$jsondata = [];
$results = [];
- $jsondata = json_decode($body, TRUE);
+ $jsondata = json_decode($body, TRUE) ?? [];
$json_error = json_last_error();
if ($json_error == JSON_ERROR_NONE) {
//WIKIdata will give is an success key will always return at least one, the query string
if (count($jsondata) > 1) {
- if (count($jsondata['result']) >= 1) {
+ if (isset($jsondata['result']) && is_array($jsondata['result']) && count($jsondata['result']) >= 1) {
foreach ($jsondata['result'] as $key => $item) {
$desc = (isset($item['nametype'])) ? '(' . $item['nametype'] . ')' : NULL;
$results[] = [
@@ -681,7 +690,7 @@ protected function europeana($input, $vocab, string $apikey) {
return $results;
}
- $input = urlencode($input);
+ $input = rawurlencode($input);
$urlindex = "/suggest?text=" . $input . "&type=" . $vocab ."&wskey=". $apikey ;
@@ -729,7 +738,7 @@ protected function europeana($input, $vocab, string $apikey) {
}
if (($vocab == 'agent') && isset($result['dateOfBirth'])) {
- $desc[] = $result['dateOfBirth'] . '/' . $result['dateOfDeath'] ?? '?';
+ $desc[] = $result['dateOfBirth'] . '/' . $result['dateOfDeath'] ?? '?';
}
$desc = !empty($desc) ? ' (' . implode(', ', $desc) . ')' : NULL;
@@ -761,6 +770,181 @@ protected function europeana($input, $vocab, string $apikey) {
return [];
}
+ /**
+ * @param $input
+ * The query
+ * @param $vocab
+ * The 'suggest' enabled endpoint at LoC
+ *
+ * @return array
+ */
+ protected function snac($input, $vocab, $rdftype) {
+ //@TODO make the following allowed list a constant since we use it in
+ // \Drupal\webform_strawberryfield\Plugin\WebformElement\WebformLoC
+ if (!in_array($vocab, [
+ 'Constellation',
+ 'rdftype',
+ ])) {
+ // Drop before tryin to hit non existing vocab
+ $this->messenger()->addError(
+ $this->t('@vocab for SNAC autocomplete is not in in our allowed list.',
+ [
+ '@vocab' => $vocab,
+ ]
+ )
+ );
+ $results[] = [
+ 'value' => NULL,
+ 'label' => "Wrong Vocabulary {$vocab} in SNAC Query",
+ ];
+ return $results;
+ }
+
+
+ $input = urlencode($input);
+
+
+ $remoteUrl = "https://api.snaccooperative.org";
+ $options = [
+ 'body' => json_encode([
+ "command" => "search",
+ "term" => $input,
+ "entity_type" => $rdftype != "thing" ? $rdftype : NULL,
+ "start" => 0,
+ "count" => 10,
+ "search_type" => "autocomplete",
+ ]),
+ RequestOptions::HEADERS => [
+ 'Content-Type' => 'application/json'
+ ]
+ ];
+ $body = $this->getRemoteJsonData($remoteUrl, $options, 'PUT');
+
+ $jsondata = [];
+ $results = [];
+ $jsondata = json_decode($body, TRUE);
+ $json_error = json_last_error();
+ if ($json_error == JSON_ERROR_NONE) {
+ if (!empty($jsondata['results']) && ($jsondata['total'] ?? 0) >= 1) {
+ foreach ($jsondata['results'] as $key => $entry) {
+ $nameEntry = reset($entry['nameEntries']);
+ $results[] = [
+ 'value' => $entry['ark'] ?? $entry['entityType']['uri'],
+ 'label' => $nameEntry['original'],
+ ];
+ }
+ }
+ else {
+ $results[] = [
+ 'value' => NULL,
+ 'label' => "Sorry no match from SNAC {$vocab}",
+ ];
+ }
+ return $results;
+ }
+ $this->messenger()->addError(
+ $this->t('Looks like data fetched from @url is not in JSON format.
JSON says: @jsonerror
Please check your URL!',
+ [
+ '@url' => $remoteUrl,
+ '@jsonerror' => $json_error,
+ ]
+ )
+ );
+ return [];
+ }
+
+
+
+ /**
+ * @param $input
+ * The query
+ * @param $vocab
+ * The 'suggest' enabled endpoint at LoC
+ *
+ * @return array
+ */
+ protected function mesh($input, $vocab, $rdftype) {
+
+ //@TODO make the following allowed list a constant since we use it in
+ // \Drupal\webform_strawberryfield\Plugin\WebformElement\WebformMesh
+ if (!in_array($vocab, [
+ 'descriptor',
+ 'term',
+ ])) {
+ // Drop before tryin to hit non existing vocab
+ $this->messenger()->addError(
+ $this->t('@vocab for MeSH autocomplete is not in in our allowed list.',
+ [
+ '@vocab' => $vocab,
+ ]
+ )
+ );
+ $results[] = [
+ 'value' => NULL,
+ 'label' => "Wrong Vocabulary {$vocab} in MeSH API Query",
+ ];
+ return $results;
+ }
+
+ // Here $rdftype acts as match
+ if (!in_array($rdftype, [
+ 'startswith',
+ 'contains',
+ 'exact',
+ ])) {
+ // Drop before tryin to hit non existing vocab
+ $this->messenger()->addError(
+ $this->t('@rdftype Match type for MeSH autocomplete is not valid. It may be "exact","startswith" or "contains"',
+ [
+ '@rdftype' => $rdftype,
+ ]
+ )
+ );
+ $results[] = [
+ 'value' => NULL,
+ 'label' => "Wrong Match type for {$vocab} in MeSH API Query",
+ ];
+ return $results;
+ }
+
+ $input = rawurlencode($input);
+ $urlindex = "/mesh/lookup/{$vocab}?label=" . $input .'&limit=10&match=' . $rdftype;
+ $baseurl = 'https://id.nlm.nih.gov';
+ $remoteUrl = $baseurl . $urlindex;
+ $options['headers'] = ['Accept' => 'application/json'];
+ $body = $this->getRemoteJsonData($remoteUrl, $options);
+
+ $results = [];
+ $jsondata = json_decode($body, TRUE);
+ $json_error = json_last_error();
+ if ($json_error == JSON_ERROR_NONE) {
+ if (count($jsondata) > 1) {
+ foreach ($jsondata as $entry) {
+ $results[] = [
+ 'value' => $entry['resource'],
+ 'label' => $entry['label'],
+ ];
+ }
+ }
+ else {
+ $results[] = [
+ 'value' => NULL,
+ 'label' => "Sorry no match from MeSH for {$vocab}",
+ ];
+ }
+ return $results;
+ }
+ $this->messenger()->addError(
+ $this->t('Looks like data fetched from @url is not in JSON format.
JSON says: @jsonerror
Please check your URL!',
+ [
+ '@url' => $remoteUrl,
+ '@jsonerror' => $json_error,
+ ]
+ )
+ );
+ return [];
+ }
+
/**
* @param $remoteUrl
* @param $options
@@ -768,7 +952,7 @@ protected function europeana($input, $vocab, string $apikey) {
* @return string
* A string that may be JSON (hopefully)
*/
- protected function getRemoteJsonData($remoteUrl, $options) {
+ protected function getRemoteJsonData($remoteUrl, $options, $method = 'GET') {
// This is expensive, reason why we process and store in cache
if (empty($remoteUrl)) {
// No need to alarm. all good. If not URL just return.
@@ -783,7 +967,18 @@ protected function getRemoteJsonData($remoteUrl, $options) {
return NULL;
}
try {
- $request = $this->httpClient->get($remoteUrl, $options);
+ if ($method == 'GET') {
+ $request = $this->httpClient->get($remoteUrl, $options);
+ }
+ elseif ($method == 'POST') {
+ $request = $this->httpClient->post($remoteUrl, $options);
+ }
+ elseif ($method == 'PUT') {
+ $request = $this->httpClient->put($remoteUrl, $options);
+ }
+ else {
+ return NULL;
+ }
// Do not cache if things go bad.
if ($request->getStatusCode() == '401') {
$this->setNotAllowed(TRUE);
@@ -816,7 +1011,7 @@ protected function getRemoteJsonData($remoteUrl, $options) {
catch (ServerException $exception) {
$this->useCaches = FALSE;
$responseMessage = $exception->getMessage();
- $this->loggerFactory->get('webform_strawberryfield')
+ $this->getLogger('webform_strawberryfield')
->error('We tried to contact @url but we could not.
The Remote server says: @response.
Check your query',
[
'@url' => $remoteUrl,
diff --git a/src/Controller/StrawberryRunnerModalController.php b/src/Controller/StrawberryRunnerModalController.php
index 8feb4dd..09fc623 100644
--- a/src/Controller/StrawberryRunnerModalController.php
+++ b/src/Controller/StrawberryRunnerModalController.php
@@ -138,17 +138,18 @@ public function openModalForm(WebformInterface $webform = NULL, Request $request
// In case we are editing an existing entity, this one gets the
// Strawberryfield value
$alldata = $source_entity->get($field_name)->getValue();
- $fielddata['value'] = !empty($alldata) ? $alldata[$delta]['value']: "{}";
+ $fielddata['value'] = $alldata[$delta]['value'] ?? "{}";
$entityid = $source_entity->id();
}
- $stored_value = (isset($fielddata['value']) && !empty($fielddata['value'])) ? $fielddata['value'] : "{}";
+ $stored_value = $fielddata['value'] ?? "{}";
$data_defaults = [
'strawberry_field_widget_state_id' => $widgetid,
// Can't remember why, but seems useful to pass around
'strawberry_field_widget_source_entity_uuid' => $source_uuid,
'strawberry_field_widget_source_entity_id' => $entityid,
+ 'strawberry_field_widget_autosave' => $entityid ? FALSE : TRUE,
'strawberry_field_stored_values' => json_decode($stored_value,true)
];
@@ -247,7 +248,7 @@ public function openModalForm(WebformInterface $webform = NULL, Request $request
// We delete both, the session and the accumulated errors.
/** @var \Drupal\Core\TempStore\PrivateTempStore $tempstore */
$tempstore = \Drupal::service('tempstore.private')->get('archipel');
- $tempstore->delete($clear_saved);
+ $tempstore->delete($clear_saved.'-draft');
$tempstore->delete($clear_saved.'-errors');
// Selector us built using the field name and the delta.
$response->addCommand(new \Drupal\Core\Ajax\HtmlCommand('#' . $selector .' > .fieldset-wrapper',
diff --git a/src/Element/WebformMesh.php b/src/Element/WebformMesh.php
new file mode 100644
index 0000000..d50cf13
--- /dev/null
+++ b/src/Element/WebformMesh.php
@@ -0,0 +1,108 @@
+ 'descriptor',
+ '#matchtype' => 'startswith'
+ ];
+ return $info;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getCompositeElements(array $element) {
+ $elements = [];
+ $vocab = 'descriptor';
+ $matchtype = 'startswith';
+ if (isset($element['#vocab'])) {
+ $vocab = $element['#vocab'];
+ }
+ if (isset($element['#matchtype'])) {
+ $matchtype = $element['#matchtype'];
+ }
+
+ $class = '\Drupal\webform_strawberryfield\Element\WebformMesh';
+ $elements['label'] = [
+ '#type' => 'textfield',
+ '#title' => t('MeSH @vocab Label',['@vocab' => $vocab]),
+ '#autocomplete_route_name' => 'webform_strawberryfield.auth_autocomplete',
+ '#autocomplete_route_parameters' => ['auth_type' => 'mesh', 'vocab' => $vocab, 'rdftype'=> $matchtype ,'count' => 10],
+ '#attributes' => [
+ 'data-source-strawberry-autocomplete-key' => 'label',
+ 'data-target-strawberry-autocomplete-key' => 'uri'
+ ],
+ ];
+
+ $elements['uri'] = [
+ '#type' => 'url',
+ '#title' => t('MeSH URL'),
+ '#attributes' => ['data-strawberry-autocomplete-value' => TRUE]
+ ];
+ $elements['label']['#process'][] = [$class, 'processAutocomplete'];
+ return $elements;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
+ // @Disclaimer: This function is the worst and deceiving. Keeping it here
+ // So i never make this error again. Because of
+ // \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::prepareMultipleWrapper
+ // Basically, in case of having multiple elements :: processWebformComposite
+ // is *never* called because it actually converts the 'WebformComposite' element into a
+ // \Drupal\webform\Element\WebformMultiple calling ::processWebformMultiple element
+
+ $vocab = 'descriptor';
+ $matchtype = 'startswith';
+
+ $element = parent::processWebformComposite($element, $form_state, $complete_form);
+ if (isset($element['#vocab'])) {
+ $vocab = $element['#vocab'];
+ }
+ if (isset($element['#matchtype'])) {
+ $matchtype = $element['#matchtype'];
+ }
+
+ $element['label']["#autocomplete_route_parameters"] =
+ ['auth_type' => 'mesh', 'vocab' => $vocab, 'rdftype'=> $matchtype ,'count' => 10];
+
+ return $element;
+ }
+
+ /**
+ * @param array $element
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * @param array $complete_form
+ *
+ * @return array
+ */
+ public static function processAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
+ $element = parent::processAutocomplete($element, $form_state, $complete_form);
+ $element['#attached']['library'][] = 'webform_strawberryfield/webform_strawberryfield.metadataauth.autocomplete';
+ $element['#attached']['drupalSettings'] = [
+ 'webform_strawberryfield_autocomplete' => [],
+ ];
+
+ $element['#attributes']['data-strawberry-autocomplete'] = 'mesh';
+ return $element;
+ }
+
+}
diff --git a/src/Element/WebformSnac.php b/src/Element/WebformSnac.php
new file mode 100644
index 0000000..ea67079
--- /dev/null
+++ b/src/Element/WebformSnac.php
@@ -0,0 +1,114 @@
+ 'Constellation',
+ '#rdftype' => 'thing'
+ ];
+ return $info;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getCompositeElements(array $element) {
+
+ $elements = [];
+ $vocab = 'Constellation';
+ $rdftype = 'thing';
+ if (isset($element['#vocab'])) {
+ $vocab = $element['#vocab'];
+ }
+ if (($vocab == 'rdftype') && isset($element['#rdftype'])) {
+ $rdftype = trim($element['#rdftype']);
+ }
+
+ $class = '\Drupal\webform_strawberryfield\Element\WebformLoC';
+ $elements['label'] = [
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#autocomplete_route_name' => 'webform_strawberryfield.auth_autocomplete',
+ '#autocomplete_route_parameters' => ['auth_type' => 'snac', 'vocab' => $vocab, 'rdftype'=> $rdftype ,'count' => 10],
+ '#attributes' => [
+ 'data-source-strawberry-autocomplete-key' => 'label',
+ 'data-target-strawberry-autocomplete-key' => 'uri'
+ ],
+
+ ];
+ $elements['uri'] = [
+ '#type' => 'url',
+ '#title' => t('Snac URL'),
+ '#attributes' => ['data-strawberry-autocomplete-value' => TRUE]
+ ];
+ $elements['label']['#process'][] = [$class, 'processAutocomplete'];
+ return $elements;
+ }
+
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function processWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
+ // @Disclaimer: This function is the worst and deceiving. Keeping it here
+ // So i never make this error again. Because of
+ // \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::prepareMultipleWrapper
+ // Basically, in case of having multiple elements :: processWebformComposite
+ // is *never* called because it actually converts the 'WebformComposite' element into a
+ // \Drupal\webform\Element\WebformMultiple calling ::processWebformMultiple element
+ // So basically whatever i do here gets skipped if multiple elements are allowed.
+ // Solution is acting here instead:
+ // \Drupal\webform_strawberryfield\Plugin\WebformElement\WebformLoC::prepareMultipleWrapper
+ $vocab = 'Constellation';
+ $rdftype = 'thing';
+
+ $element = parent::processWebformComposite($element, $form_state, $complete_form);
+ if (isset($element['#vocab'])) {
+ $vocab = $element['#vocab'];
+ }
+ if (($vocab == 'rdftype') && isset($element['#rdftype'])) {
+ $rdftype = trim($element['#rdftype']);
+ }
+ $element['label']["#autocomplete_route_parameters"] =
+ ['auth_type' => 'snac', 'vocab' => $vocab, 'rdftype'=> $rdftype ,'count' => 10];
+
+ return $element;
+ }
+
+ /**
+ * @param array $element
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * @param array $complete_form
+ *
+ * @return array
+ */
+ public static function processAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
+ $element = parent::processAutocomplete($element, $form_state, $complete_form);
+ $element['#attached']['library'][] = 'webform_strawberryfield/webform_strawberryfield.metadataauth.autocomplete';
+ $element['#attached']['drupalSettings'] = [
+ 'webform_strawberryfield_autocomplete' => [],
+ ];
+
+ $element['#attributes']['data-strawberry-autocomplete'] = 'Snac';
+ return $element;
+ }
+
+
+}
diff --git a/src/EventSubscriber/WebformStrawberryfieldDeleteTmpStorage.php b/src/EventSubscriber/WebformStrawberryfieldDeleteTmpStorage.php
index b50bc3f..d5a260d 100644
--- a/src/EventSubscriber/WebformStrawberryfieldDeleteTmpStorage.php
+++ b/src/EventSubscriber/WebformStrawberryfieldDeleteTmpStorage.php
@@ -111,8 +111,13 @@ public function onEntityInsert(StrawberryfieldCrudEvent $event) {
foreach ($field->getValue() as $delta => $value) {
$keyid = $this->getTempStoreKeyName($fieldname, $delta, '');
$tempstore->delete($keyid);
- // Delete also any cached errors
- $tempstore->delete($keyid.'-errors');
+ // Delete also any cached errors and drafts if the autosave session
+ // Generated this ADO. If not (e.g Clone we will have marked that
+ // ADO's UUID as not autosave so we do not delete another ongoing session
+ if ($this->tempStoreFactory->get('archipel_autosave')->get($entity->uuid())) {
+ $tempstore->delete($keyid . '-errors');
+ $tempstore->delete($keyid . '-draft');
+ }
}
}
diff --git a/src/Plugin/Field/FieldWidget/StrawberryFieldWebFormInlineWidget.php b/src/Plugin/Field/FieldWidget/StrawberryFieldWebFormInlineWidget.php
index 035a152..d514896 100644
--- a/src/Plugin/Field/FieldWidget/StrawberryFieldWebFormInlineWidget.php
+++ b/src/Plugin/Field/FieldWidget/StrawberryFieldWebFormInlineWidget.php
@@ -12,11 +12,13 @@
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Entity\EntityMalformedException;
+use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Url;
+use Drupal\node\Entity\Node;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\webform\WebformInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -265,7 +267,6 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'strawberry_webform_inline',
]);
$limit_validation_errors = $parents;
-
// We add 'data-drupal-selector' = 'strawberry_webform_widget'
// To allow JS to react/jquery select on this.
$element += [
@@ -292,24 +293,23 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
// Which means an abandoned Metadata Sessions somewhere
// Someone saved/drafted 'metadata' during a form session and left for coffee
// WE can reuse!
-
- if (($tempstore->getMetadata($tempstoreId) != NULL) && $items->getEntity()
- ->isNew()) {
+ $default_value = $items->getFieldDefinition()->getDefaultValue($items->getEntity());
+ $default_value = $default_value[$delta]['value'] ?? "{}";
+ $autosave = ($items->getEntity()->isNew() && $savedvalue['value'] == $default_value);
+ // If the SBF in this entity has data and its different to the default for
+ // the field then we are sure it can not be auto saved (e.g when cloning)
+ // Not should we load a session.
+ if (($tempstore->getMetadata($tempstoreId.'-draft') != NULL) && $autosave) {
$discard = $form_state->getUserInput()['_triggering_element_name'] ?? FALSE;
$discard = $discard == 'webform_strawberryfield_discard_session' ?? FALSE;
-
- $json_string = $tempstore->get($tempstoreId);
+ $autosave = TRUE;
+ $json_string = $tempstore->get($tempstoreId.'-draft');
$json = json_decode($json_string, TRUE);
$json_error = json_last_error();
if ($json_error == JSON_ERROR_NONE) {
$savedvalue['value'] = $json_string;
- $element['strawberry_webform_inline_message'] = [
- '#id' => 'ajax-value',
- '#theme' => 'status_messages',
- '#message_list' => [
- 'status' => [$this->t('We found and loaded a previous unfinished metadata session for you.')],
- ],
- ];
+ // Let's use the time to see if draft was changed before or after this call
+ // Since this form rebuilds itself with any webform ajax interaction
$webform_controller_url_clear = Url::fromRoute('webform_strawberryfield.modal_webform',
[
@@ -320,9 +320,11 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'clear_saved' => $tempstoreId,
]
);
+
$element['strawberry_webform_discard_session'] = [
'#type' => 'link',
'#title' => $this->t('Discard Session'),
+ '#description' => $this->t('We found and unfinished Metadata Session'),
'#url' => $webform_controller_url_clear,
'#attributes' => [
'class' => [
@@ -333,9 +335,12 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
],
];
+ $element['#title'] = $element['#title'] . $this->t(' (Unfinished Metadata Session loaded)');
}
}
+ $form_state->set('autosave', $autosave);
+
// If new this won't exist
$stored_value = !empty($savedvalue['value']) ? $savedvalue['value'] : "{}";
@@ -343,6 +348,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'strawberry_field_widget_state_id' => $this_widget_id,
'strawberry_field_widget_source_entity_uuid' => $entity_uuid,
'strawberry_field_widget_source_entity_id' => $entity_id,
+ 'strawberry_field_widget_autosave' => $autosave,
'strawberry_field_stored_values' => json_decode($stored_value, TRUE),
];
@@ -426,7 +432,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
];
}
- if ($this->getSetting('hide_cancel') === FALSE || $this->getSetting('hide_cancel') == NULL) {
+ if (($this->getSetting('hide_cancel') === FALSE || $this->getSetting('hide_cancel') == NULL) && $autosave == FALSE ){
$webform_controller_url_close = Url::fromRoute('webform_strawberryfield.close_modal_webform',
[
'state' => "$entity_uuid:$this_field_name:$delta:$this_widget_id",
@@ -538,6 +544,11 @@ public function validateWebform($element, FormStateInterface $form_state) {
}
$json_string = $tempstore->get($tempstoreId);
+ $autosave = $form_state->get('autosave');
+ $tempstore_noautosave = \Drupal::service('tempstore.private')->get('archipel_autosave');
+ $final_uuid = $form_state->getFormObject()->getEntity()->uuid();
+ $tempstore_noautosave->set($final_uuid, $autosave);
+
$json = json_decode($json_string, TRUE);
$json_error = json_last_error();
if ($json_error == JSON_ERROR_NONE) {
diff --git a/src/Plugin/WebformElement/WebformMesh.php b/src/Plugin/WebformElement/WebformMesh.php
new file mode 100644
index 0000000..fedf6d0
--- /dev/null
+++ b/src/Plugin/WebformElement/WebformMesh.php
@@ -0,0 +1,162 @@
+ 'descriptor',
+ 'matchtype' => 'startswith',
+ ] + parent::defineDefaultBaseProperties();
+ }
+
+ public function getDefaultProperties() {
+ $properties = parent::getDefaultProperties() + [
+ 'vocab' => 'descriptor',
+ 'matchtype' => 'startswith',
+ ];
+
+ return $properties;
+ }
+
+
+
+ public function prepare(
+ array &$element,
+ WebformSubmissionInterface $webform_submission = NULL
+ ) {
+
+ // @TODO explore this method to act on submitted data v/s element behavior
+ }
+
+ /**
+ * Set multiple element wrapper.
+ *
+ * @param array $element
+ * An element.
+ */
+ protected function prepareMultipleWrapper(array &$element) {
+
+ parent::prepareMultipleWrapper($element);
+
+ // Finally!
+ // This is the last chance we have to affect the render array
+ // This is where the original element type is also
+ // swapped by webform_multiple
+ // breaking all our #process callbacks.
+ $matchtype = trim($this->getElementProperty($element, 'matchtype'));
+ $matchtype = $matchtype?: $this->getDefaultProperty('matchtype');
+ $vocab = $this->getElementProperty($element, 'vocab');
+ $vocab = $vocab ?: $this->getDefaultProperty('vocab');
+ if (isset($element['#element']['#webform_composite_elements']['label'])) {
+ $element['#element']['#webform_composite_elements']['label']["#autocomplete_route_parameters"] =
+ [
+ 'auth_type' => 'mesh',
+ 'vocab' => $vocab,
+ 'rdftype' => $matchtype,
+ 'count' => 10
+ ];
+ }
+ // For some reason i can not understand, when multiples are using
+ // Tables, the #webform_composite_elements -> 'label' is not used...
+ if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) {
+ $element['#element']['label']["#autocomplete_route_parameters"] =
+ [
+ 'auth_type' => 'mesh',
+ 'vocab' => $vocab,
+ 'rdftype' => $matchtype,
+ 'matchtype' => 'startswith',
+ 'count' => 10
+ ];
+ }
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPluginLabel() {
+ return $this->elementManager->isExcluded('webform_metadata_mesh') ? $this->t('Medical Subject Heading MeSH') : 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['uri'])) {
+ $lines[] = $value['uri'];
+ }
+
+ if (!empty($value['label'])) {
+ $lines[] = $value['label'];
+ }
+ return $lines;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ $form = parent::form($form, $form_state);
+
+ $form['composite']['vocab'] = [
+ '#type' => 'select',
+ '#options' => [
+ 'descriptor' => 'Medical Subject Headings Descriptor (Subject Headings) API',
+ 'term' => 'Medical Subject Headings Term API',
+ ],
+ '#title' => $this->t("What MeSH Autocomplete API Type to use."),
+ '#description' => $this->t('See MeSH Subject HeadingsAPI'),
+
+ ];
+ $form['composite']['matchtype'] = [
+ '#type' => 'select',
+ '#options' => [
+ 'exact' => 'Exact, based on recommended Label. Will give you a single result or none.',
+ 'startswith' => 'Label Starts with.',
+ 'contains' => 'Label Contains.',
+ ],
+ '#title' => $this->t("What type of Match Query to perform"),
+ '#description' => $this->t('All match types return the same number of results. Exact matches only against the prefered label of the query.'),
+ ];
+ return $form;
+ }
+
+}
diff --git a/src/Plugin/WebformElement/WebformSnac.php b/src/Plugin/WebformElement/WebformSnac.php
new file mode 100644
index 0000000..dffe00d
--- /dev/null
+++ b/src/Plugin/WebformElement/WebformSnac.php
@@ -0,0 +1,174 @@
+ 'Constellation',
+ 'rdftype' => 'thing',
+ ] + parent::defineDefaultBaseProperties();
+ }
+
+ public function getDefaultProperties() {
+ $properties = parent::getDefaultProperties() + [
+ 'vocab' => 'Constellation',
+ 'rdftype' => 'thing',
+ ];
+
+ return $properties;
+ }
+
+
+
+ public function prepare(
+ array &$element,
+ WebformSubmissionInterface $webform_submission = NULL
+ ) {
+
+ // @TODO explore this method to act on submitted data v/s element behavior
+ }
+
+ /**
+ * Set multiple element wrapper.
+ *
+ * @param array $element
+ * An element.
+ */
+ protected function prepareMultipleWrapper(array &$element) {
+
+ parent::prepareMultipleWrapper($element);
+
+ // Finally!
+ // This is the last chance we have to affect the render array
+ // This is where the original element type is also
+ // swapped by webform_multiple
+ // breaking all our #process callbacks.
+ $vocab = 'Constellation';
+ $rdftype = 'thing';
+ $vocab = $this->getElementProperty($element, 'vocab');
+ $vocab = $vocab ?: $this->getDefaultProperty($vocab);
+ if ($vocab == 'rdftype') {
+ $rdftype = trim($this->getElementProperty($element, 'rdftype'));
+ }
+
+ $rdftype = $rdftype ?: $this->getDefaultProperty($rdftype);
+ // This seems to have been an old Webform module variation
+ // Keeping it here until sure its not gone for good
+ if (isset($element['#element']['#webform_composite_elements']['label'])) {
+ $element['#element']['#webform_composite_elements']['label']["#autocomplete_route_parameters"] =
+ [
+ 'auth_type' => 'snac',
+ 'vocab' => $vocab,
+ 'rdftype' => $rdftype,
+ 'count' => 10
+ ];
+ }
+ // For some reason i can not understand, when multiples are using
+ // Tables, the #webform_composite_elements -> 'label' is not used...
+ if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) {
+ $element['#element']['label']["#autocomplete_route_parameters"] =
+ [
+ 'auth_type' => 'snac',
+ 'vocab' => $vocab,
+ 'rdftype' => $rdftype,
+ 'count' => 10
+ ];
+ }
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPluginLabel() {
+ return $this->elementManager->isExcluded('webform_metadata_snac') ? $this->t('SNAC Constellation Terms') : 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['uri'])) {
+ $lines[] = $value['uri'];
+ }
+
+ if (!empty($value['label'])) {
+ $lines[] = $value['label'];
+ }
+ return $lines;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ $form = parent::form($form, $form_state);
+
+ $form['composite']['vocab'] = [
+ '#type' => 'select',
+ '#options' => [
+ 'Constellation' => 'All Constellation Entity Types',
+ 'rdftype' => 'By specific Constellation Entity Types',
+ ],
+ '#title' => $this->t("What SNAC query type to use."),
+ '#description' => $this->t('Specific Entity Types can be: person, corporateBody or family'),
+ ];
+ // Not sure if this has a sub authority and how that works/if suggest
+ $form['composite']['rdftype'] = [
+ '#type' => 'select',
+ '#options' => [
+ 'person' => 'person',
+ 'corporateBody' => 'corporateBody',
+ 'family' => 'family',
+ ],
+ '#title' => $this->t("What SNAC Entity type to use as filter"),
+ '#description' => $this->t('Can be one of: person, corporateBody or family'),
+ '#default_value' => 'person',
+ '#states' => [
+ 'visible' => [
+ ':input[name="properties[vocab]"]' => ['value' => 'rdftype'],
+ ],
+ ],
+ ];
+
+ return $form;
+ }
+
+}
diff --git a/src/Plugin/WebformHandler/strawberryFieldharvester.php b/src/Plugin/WebformHandler/strawberryFieldharvester.php
index ab99444..f58002d 100644
--- a/src/Plugin/WebformHandler/strawberryFieldharvester.php
+++ b/src/Plugin/WebformHandler/strawberryFieldharvester.php
@@ -557,14 +557,16 @@ public function preSave(WebformSubmissionInterface $webform_submission) {
);
}
try {
+ // Saves all what was done in the webform in our tempstore
+ // To be later retrieved by the Field Widget Submit
$tempstore->set(
$values["strawberry_field_widget_state_id"],
$cleanvalues
);
// Just in case we have stashed errors remove them
$tempstore->delete($values['strawberry_field_widget_state_id'] . '-errors');
-
- } catch (TempStoreException $e) {
+ }
+ 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.'
@@ -609,8 +611,8 @@ public function validateForm(
$values = $webform_submission->getData();
// So we can now unset cached errors on this step. Only if triggered by next here.
// Wizard navigation has its own way.
- if ((!isset($values['strawberry_field_widget_source_entity_id']) ||
- $values['strawberry_field_widget_source_entity_id'] === NULL) &&
+ if ((!isset($values['strawberry_field_widget_autosave']) ||
+ $values['strawberry_field_widget_autosave'] === TRUE) &&
isset($values['strawberry_field_widget_state_id'])) {
if (isset($form_state->getTriggeringElement()['#name']) && $form_state->getTriggeringElement()['#name'] == 'op') {
$current_page = $webform_submission->getCurrentPage();
@@ -666,10 +668,11 @@ public function submitForm(
/* @var $tempstore \Drupal\Core\TempStore\PrivateTempStore */
$tempstore = \Drupal::service('tempstore.private')->get('archipel');
$tempstore->set(
- $values['strawberry_field_widget_state_id'],
+ $values['strawberry_field_widget_state_id'].'-draft',
$cleanvalues
);
$form_state->set('in_draft', TRUE);
+ $form_state->set('draft_saved', TRUE);
} catch (TempStoreException $e) {
$this->messenger()->addError(
$this->t(
@@ -829,8 +832,8 @@ public function alterForm(array &$form, FormStateInterface $form_state, WebformS
// Instead of validating previous invisible steps and setting errors here
// we will block the submit button until all errors are cleared.
- if ((!isset($values['strawberry_field_widget_source_entity_id']) ||
- $values['strawberry_field_widget_source_entity_id'] === NULL) &&
+ if ((!isset($values['strawberry_field_widget_autosave']) ||
+ $values['strawberry_field_widget_autosave'] === TRUE) &&
isset($values['strawberry_field_widget_state_id'])
) {
$tempstore = \Drupal::service('tempstore.private')->get('archipel');
@@ -845,7 +848,9 @@ public function alterForm(array &$form, FormStateInterface $form_state, WebformS
$can_not = FALSE;
if (is_array($previous_errors)) {
+ $i = 0;
foreach ($all_pages as $pagekey => $pagekeyinfo) {
+ $i++;
if (($pagekeyinfo['#access'] === TRUE) &&
isset($previous_errors[$pagekey]) &&
is_array($previous_errors[$pagekey]) &&
@@ -854,8 +859,9 @@ public function alterForm(array &$form, FormStateInterface $form_state, WebformS
$this->messenger()
->addWarning(t('You can not submit this form yet.'), FALSE);
$this->messenger()
- ->addWarning(t('Please check the @steps step for missing or incorrect fields', [
+ ->addWarning(t('Please check the @steps step (@number) for missing or incorrect fields', [
'@steps' => $pagekeyinfo['#title'],
+ '@number' => $i,
]));
}
}
@@ -864,8 +870,8 @@ public function alterForm(array &$form, FormStateInterface $form_state, WebformS
}
}
}
- } elseif ((!isset($values['strawberry_field_widget_source_entity_id']) ||
- $values['strawberry_field_widget_source_entity_id'] === NULL) &&
+ } elseif ((!isset($values['strawberry_field_widget_autosave']) ||
+ $values['strawberry_field_widget_autosave'] === TRUE) &&
isset($values['strawberry_field_widget_state_id']) &&
isset($form['pages'])
) {
@@ -904,8 +910,8 @@ public function sbfDraftValidate(array &$form, FormStateInterface $form_state) {
}
$values = $this->getWebformSubmission()->getData();
// Only allow saving of drafts if the user is creating a new entity
- if ((!isset($values['strawberry_field_widget_source_entity_id']) ||
- $values['strawberry_field_widget_source_entity_id'] === NULL) &&
+ if ((!isset($values['strawberry_field_widget_autosave']) ||
+ $values['strawberry_field_widget_autosave'] === TRUE) &&
isset($values['strawberry_field_widget_state_id'])
) {
$this->setIsWidgetDriven(TRUE);
@@ -927,7 +933,8 @@ public function sbfDraftValidate(array &$form, FormStateInterface $form_state) {
$form_state->set('in_draft', TRUE);
$form_state->set('draft_saved', TRUE);
$this->getWebformSubmission()->validate();
- } catch (TempStoreException $e) {
+ }
+ 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.'