Background

Goal

  • Make a contact form submission a defined entity with defined properties and entity class methods: a Contact Message.
  • Turn contact form categories into bundles of contact messages.
  • Leverage entity form controller for the contact message forms.
  • Make contact forms/messages fieldable. Without storage. Just fieldable.

Reasoning

  • I'm coming here from a Mollom module maintainer perspective:

    The module needs to implement a lot of special code in order to allow to protect the contact forms against spam. That is, because the contact message form is not an entity. For the Mollom module, this essentially translates into "allow protect arbitrary forms". The entire rest of its code only integrates with entities, which is standardized and much more simple to work with.

    Hence, by eliminating this special case, I expect to be able to cut away a very large chunk of integration code for arbitrary forms in the Mollom module, possibly even more than 50% of the module's code. Essentially, I want to be able to say: "Mollom can protect all of your entity forms." And that's it. :)

Possible future improvements/follow-ups

Intentionally not part of this patch:

  1. Storing contact messages/form submissions is a different can of worms, as it inherently means listing, viewing, editing, permissions, etc.pp.

    This is purposively not part and not within the scope of this issue. (see follow-up issues below)

  2. The default "Subject" and "Message" form fields could be converted into actual Field API fields.

    This patch here only exposes them as extra fields.

  3. Separate entity view modes for 'mail' and 'copy', so field values can be hidden from the mail copy that is sent to the sender.

  4. Turn the user contact form into a (locked) category/bundle, so it can be fully configured like any other contact category (e.g., form title, fields, auto-reply message, etc.).

Follow-up issues

Contained bug fixes

Major bugs in contact form submission handlers that need to be fixed separately if this patch is not committed:

  • Cookie is saved including "(not verified)" suffix.
  • global $user is not cloned before manipulation.
  • Auto-reply is sent with From: implode(', ', $category->recipients), which is not only invalid, but also exposes the category recipients e-mail addresses in the From header of the auto-reply mail.

Related issues

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Status: Active » Needs review
FileSize
30.42 KB

Contains a few nice-to-have code clean-up todos, but this shit works.

Status: Needs review » Needs work

The last submitted patch, contact.entity.1.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
4.95 KB
6.16 KB
33.58 KB

Fixed Field API integration.

Proof:

contact-entity.2.png

Status: Needs review » Needs work

The last submitted patch, contact.entity.2.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
12.84 KB
41.45 KB

Fixed typos and removed stale/obsolete @todos.
Fixed strange constructions in user cancel forms.

Status: Needs review » Needs work

The last submitted patch, contact.entity.5.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
7.89 KB
47.61 KB
  • Fixed bogus #maxlength for subject originally contained in contact_personal_form().
  • Added required category storage and list controller adjustments for Field UI.
  • Fixed EntityFormController::submit() does not clean submitted form values before buildEntity().
  • Fixed auto-reply e-mail processing and bogus test expectations.
sun’s picture

The good news: Contact messages are fully working entities. :)

Even better news: This work revealed a range of bugs in many different spots.

What still needs work:

Field API blows up if there are actual fields configured for the message. Configuration of the fields works fine. Only when visiting /contact, the system bails out with a "::getWidget() called on a non-object" fatal error (or similar).

As with the other adjustments in this patch, this almost certainly means that Field API assumes an entity ID to exist somewhere. Most likely, it would magically start to work, if the contact_message would define an 'id' entity key in its entity info (even though that wouldn't ever be populated). In turn, there are most probably two possible routes here; the soft/lame one (add a fake ID key + property), or the hard one (bending/teaching Field API to stop presuming that an ID would exist). ;)

sun’s picture

FileSize
10.29 KB
53.49 KB
  1. Fixed Field Attach API throws fatal error on a fieldable entity without bundle value.
  2. Added Preview action for contact form, and entity render controller basics.
  3. Re-implemented contact form category selection as tabs/local tasks.

Status: Needs review » Needs work

The last submitted patch, contact.entity.9.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
3.92 KB
54.17 KB

Updated for entity type plugins.

Status: Needs review » Needs work

The last submitted patch, contact.entity.11.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
1.1 KB
53.79 KB
  1. Merged HEAD.
  2. Updated for latest changes in HEAD.

This blows up on the new Entity Field Query though... — it looks like that added an assumption to Field API that all fields would be attached to entities that are stored (and have a 'base table')... :(

sun’s picture

FileSize
5.11 KB
54.4 KB
  1. Fixed field_has_data() wrongly assumes that all entities would be stored.
  2. Fixed contact category tab/local-task should not appear when there is only one category.
  3. Fixed missing $default_method variable in user_cancel_confirm_form().

Also created #1848874: Clean up and simplify user cancel methods and processing and will copy those changes into that issue.

Status: Needs review » Needs work

The last submitted patch, contact.entity.14.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Status: Needs work » Needs review
FileSize
6.14 KB
57.16 KB
  1. Fixed type-hints, namespace imports, and phpDocs.
  2. Removed stray @todo.
  3. Added hook_field_extra_fields() for default contact form elements.
  4. Tweaked code comment for copy option.

Overall, this looks relatively complete to me now and could use some reviews - will fix the test failures now.

sun’s picture

FileSize
5.45 KB
59.26 KB

Updated ContactSidewideTest for new categories.

Status: Needs review » Needs work
Issue tags: -Killer End-User Features, -Entity system, -API clean-up, -Platform Initiative, -Field system

The last submitted patch, contact.entity.17.patch, failed testing.

dagmar’s picture

Status: Needs work » Needs review
Issue tags: +Killer End-User Features, +Entity system, +API clean-up, +Platform Initiative, +Field system

#17: contact.entity.17.patch queued for re-testing.

andypost’s picture

Awesome patch, I think a name "Message" would conflict with popular message module entity.
Suppose better name it as contact_message according a contact_category

sun’s picture

No worries:

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php

+ *   id = "contact_message",
+ *   label = @Translation("Contact message"),

The internal entity type name is properly namespaced to Contact module already. :)

dagmar’s picture

Question, could we have an option just don't provide a tab for the category?

This could allow to have independent contact forms, for example for different companies and users will not see all the other tabs.

sun’s picture

What we could do is to add a property + config option to each contact category that indicates whether to expose it as a tab - would that resolve that use-case? Sounds rather trivial to me.

That said, I'd prefer to do that in a follow-up issue, since this patch is large enough already, and actually this patch does not introduce any additional features on its own - it focuses on the entity + bundle + fieldable conversion only.

dagmar’s picture

Status: Needs review » Needs work
FileSize
36.2 KB
39.75 KB

I reeeeeally like this patch. I tested the version provided in #7 few days ago, and I happy to see #17 works much better. As far I can see is working fine, I can create and reorder the fields of a Contact Category. However I found some issues:

1) Before this patch, the default Recipients for the "Website feedback" category was automatically set to the site email. This is not configured automatically anymore:

bug-no-default.png

2) When I try to delete a field from a Contact Category I see this error, but the field is deleted after go to the UI again.

bug-on-delete.png

As @sun recommended in #23 I created two follow-ups for this issue:

#1849158: Expose contact category-specific forms and/or their URLs somewhere
#1849164: Provide templates for contact forms

dagmar’s picture

Status: Needs work » Needs review

Going back to needs review due those issues were not introduced in this patch

Issue 1: was introduced by #1496542: Convert site information to config system and of course, when the the contact module is installed, I didn't defined the site mail yet. Anyway this is can be fixed in a follow up if there is a solution for this...

The Issue 2 is already covered in #1845372: Deleting a field from a non-node entity bundle results in an Entity Field Query Exception

andypost’s picture

andypost’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

FileSize
9.75 KB
58.93 KB

Thanks for testing! :)

I've added a few more things that could possibly be improved in follow-ups to the issue summary.

  1. Added rendering of fields into contact message mail.
  2. Cleaned up code and comments.
  3. Removed fallback code for legacy site-wide contact form not passing a category.

I consider this patch pretty much complete (and done if testbot approves). :)

Status: Needs review » Needs work

The last submitted patch, contact.entity.27.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
743 bytes
58.95 KB

Whoopsie - Fixed contact message mail view is not rendered.

Status: Needs review » Needs work

The last submitted patch, contact.entity.29.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
782 bytes
58.97 KB

Fixed only variables can be passed by reference.

Hm. I'm not able to reproduce the Drupal\comment\Tests\CommentLanguageTest test failure locally.

sun’s picture

Title: Convert contact form submissions into full-blown entities, without storage » Turn contact form submissions into full-blown Contact Message entities, without storage

Slightly adjusting issue title to clarify that this is not a "conversion" issue like all the others, which are refactoring stuff, whereas this issue is a new feature.

Also, I think this feature is ready. Some parts of the new code could be simplified and improved later on, but that will require to rethink and refactor some Entity/Field API internals first.

I think this is a very good and important step into the right direction, not only for Contact module's future, but also to harness and demonstrate the power of our entity/field systems. Lastly, for many sites, this means that they do not necessarily have to install Webform module anymore.

sun’s picture

Final reviews, anyone? :)

Please note that this patch still contains user cancel method changes, which have been split out into #1848874: Clean up and simplify user cancel methods and processing already, but are required for this patch to work (since it dynamically switches the contact message author/sender form elements from input fields into #type 'item').

dagmar’s picture

Status: Needs review » Reviewed & tested by the community

Awesome, I tested this patch and besides #1845372: Deleting a field from a non-node entity bundle results in an Entity Field Query Exception is working fine.

As a really small improvement, it would be nice to see a 'View contact form' inside the operations of each category.

sun’s picture

A "View contact form" operation on the category listing page sounds like a good idea for #599770: Clean up the contact forms listing UI: Allow to set the default category and weights on the listing page

Also note that #1848874: Clean up and simplify user cancel methods and processing is still part of this patch. I don't mind for it to get committed as part of this patch here though.

andypost’s picture

There's similar contrib module EntityForms

Berdir’s picture

Some comments, nothing blocking I think, so not changing the status.

+++ b/core/modules/contact/contact.moduleundefined
@@ -115,6 +133,78 @@ function contact_menu() {
 /**
+ * Menu title callback: Returns the link title for editing a category.
+ *
+ * @param \Drupal\contact\Plugin\Core\Entity\Category $category
+ *   A contact category entity.
+ *
+ * @return string
+ *   The unsanitized contact category label.
+ */
+function contact_menu_title_category(Category $category) {
+  return $category->label();

I think it should be possible to use entity_page_label() for this, by relying on load arguments in the router.

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,128 @@
+class Message extends Entity {

Will need a EntityNG follow-up if there isn't one already. That might allow to change the recipients thing below into a custom field that can load the recipient on demand or so... ?

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,128 @@
+   *
+   * @todo This technically does not belong here, since it only belongs to user
+   *   contact forms, whereas the recipient(s) of site contact form are
+   *   configured per Category. However, while a wildcard contact.category.user
+   *   would be possible, it would require a entity storage controller override
+   *   in order to dynamically replace the Category::$recipients with the user
+   *   account's e-mail address upon Entity::create(). Doing so would inherently
+   *   break the vertical extensibility of entity storage controllers (as we
+   *   would have to extend from DatabaseStorageController or some other
+   *   hard-coded base class) and there is no horizontal extensibility in
+   *   Drupal, nor support for mixins in PHP yet.
+   *
+   * @var \Drupal\user\User
+   */
+  public $recipient;

Not sure I understand this correctly, but this isn't a problem that's specific for this, is it? I don't think we have a single non-config entity in core that does *not* require a custom storage controller ;)

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,128 @@
+    // If this entity was created without category, we do not have a bundle and
+    // we need to prevent EntityFormController from calling into Field Attach
+    // functions, since those will bail out with a fatal error due to the bundle
+    // being NULL.
+    $info = entity_get_info($this->entityType);
+    if (!isset($this->category)) {
+      $info['fieldable'] = FALSE;
+    }

That's a bit nasty...

+++ b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.phpundefined
@@ -14,15 +14,6 @@
 
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('comment');
-
-  protected $profile = 'standard';

Ah, that's why the user cancel tests are so slow, nice! :)

sun’s picture

Thanks for your review, @Berdir!

I think it should be possible to use entity_page_label() for this

Hm. That function sounds like its purpose is to supply an entity's label for the page title...? This callback here is about the menu link title though - the page title is detached from that.

I think that's a separate discussion, which we should definitely have for D8. All of the entity type providing modules need to implement way too many callbacks right now, and I think that should be unified and simplified - perhaps even on the Entity class itself (e.g., label() + blockTitle()... or similar).

Will need a EntityNG follow-up if there isn't one already. That might allow to change the recipients thing below into a custom field that can load the recipient on demand or so... ?
...
Not sure I understand this correctly, but this isn't a problem that's specific for this, is it? I don't think we have a single non-config entity in core that does *not* require a custom storage controller ;)

Yeah, probably. That said, I can't really make sense of the current EntityNG situation — it looks like we're continuing to convert everything to Entity right now... and will have a giant Entity to EntityNG conversion later on, ultimately eliminating EntityNG?

The recipient is actually an external data source that needs to be passed in (for the user contact form only). It may be comparable to how the parent/context entity is passed into the new, decoupled comments - but I didn't have time to check and review how the new comments are doing that.

Technically, I guess it's a user reference property/field, but it only applies to the user contact form — as mentioned in the issue summary, there's room for improving the situation and exploring whether that should be a category/bundle in follow-up issues.

As long as it is not a bundle, that's also the reason for why the entity needs to disable 'fieldable'.

As mentioned, I'm happy to investigate possible ways to improve the user contact form handling. However, given the size of this patch, I'd really prefer to do that in separate issues. :)

catch’s picture

Status: Reviewed & tested by the community » Needs work

The general approach here is interesting and I quite like it.

A few things came up, the only things I really had trouble with were:

- relying on no base table being set to indicate no storage. Dpesn't feel right although I don't have a great suggestion.
- putting this straight into entity info means that patches like the entity reference one are going to list it as something which could be referenced, which of course it can't be. This is strongly linked to the no storage issue.

Both of those need more thought here, so marking CNW.

On the other hand it's interesting that there's only a couple of one-line changes to support entities with no ID and that seems pretty harmless, although I could be missing something.

+++ b/core/modules/contact/contact.admin.incundefined
@@ -37,7 +37,7 @@ function contact_category_add() {
- * @param Drupal\contact\Plugin\Core\Entity\Category $category

I think the coding style is without the forward slash.

+++ b/core/modules/contact/contact.moduleundefined
@@ -115,6 +133,78 @@ function contact_menu() {
+/**
+ * Implements hook_menu_local_tasks_alter().
+ */
+function contact_menu_local_tasks_alter(&$data, $router_item, $root_path) {

I have no idea what this is doing nor why it is needed.

+++ b/core/modules/contact/contact.pages.incundefined
@@ -188,126 +64,39 @@ function contact_site_form_submit($form, &$form_state) {
+  if (!drupal_container()->get('flood')->isAllowed('contact', $limit, $interval)) {
+    drupal_set_message(t("You cannot send more than %limit messages in @interval. Try again later.", array(
+      '%limit' => $limit,
+      '@interval' => format_interval($interval),
+    )), 'error');

Does AccessDeniedHttpException let you pass a custom message?

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,128 @@
+   * @todo This technically does not belong here, since it only belongs to user
+   *   contact forms, whereas the recipient(s) of site contact form are
+   *   configured per Category. However, while a wildcard contact.category.user
+   *   would be possible, it would require a entity storage controller override
+   *   in order to dynamically replace the Category::$recipients with the user
+   *   account's e-mail address upon Entity::create(). Doing so would inherently
+   *   break the vertical extensibility of entity storage controllers (as we
+   *   would have to extend from DatabaseStorageController or some other
+   *   hard-coded base class) and there is no horizontal extensibility in

Like berdir said, we're not at the point with the entity/storage controllers where core entities can get away with out them. Would be good if they could, but they're all still drastically different and doing their own weird things a lot of the time.

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,128 @@
+    // If this entity was created without category, we do not have a bundle and
+    // we need to prevent EntityFormController from calling into Field Attach
+    // functions, since those will bail out with a fatal error due to the bundle
+    // being NULL.
+    $info = entity_get_info($this->entityType);
+    if (!isset($this->category)) {
+      $info['fieldable'] = FALSE;
+    }
+    return $info;

When does an entity get created without a category?

+++ b/core/modules/field/field.moduleundefined
@@ -1019,6 +1019,11 @@ function field_has_data($field) {
+    // Entities without a base table (which are not stored) cannot be queried.
+    $entity_info = entity_get_info($entity_type);
+    if (!isset($entity_info['base_table'])) {
+      continue;

I'm not sure about relying on the lack of base table, that seems a bit fragile. There could be a null storage controller but then we currently allow people to swap that out.

Berdir’s picture

- relying on no base table being set to indicate no storage. Dpesn't feel right although I don't have a great suggestion.
- putting this straight into entity info means that patches like the entity reference one are going to list it as something which could be referenced, which of course it can't be. This is strongly linked to the no storage issue.

I have a contrib module that exposes data from a remote CRM system as entities in Drupal which obviously don't have a base table. They do, however, have a "storage" (which is using http requests). It even provides (incomplete and hackish but working) EFQ and with that and efq_views, views integration.

So, I think this is a valid question, considering things like the hardcoded check in field_has_data(). Not sure what the answer is :)

To answer your questions, entity_page_label() might sound misleading, but we specifically kept it for title callbacks where you can't call the label method.

Yeah, probably. That said, I can't really make sense of the current EntityNG situation — it looks like we're continuing to convert everything to Entity right now... and will have a giant Entity to EntityNG conversion later on, ultimately eliminating EntityNG?

More or less. For content entities, the main issue is currently the comments conversion, which is currently containing things like a BC layer (which we need so that we can convert entities to NG without touching fields and once everything is converted convert fields and then throw the BC layer out) and performance improvements to not be that much slower on a page with 300 comments (comments being a nice example of that use case). So most of the other things are blocked on that. Like, e.g., the aggregator feed item as entity issue, where I'm already converting to EntityNG directly.

sun’s picture

Status: Needs work » Needs review
FileSize
6.5 KB
57.66 KB

- relying on no base table being set to indicate no storage. Dpesn't feel right although I don't have a great suggestion.

The actual reason for that exact condition is that the new Entity Query throws an exception on the exact same condition. I've clarified the comment.

The new Entity/Field Query generally seems to have much more limitations that the old EFQ did not have. One of those limitations is the aspect we're facing here: Technically, Field API should perfectly be able to query the field data tables only, without any kind of dependency on the entity storage - I do not understand why this dependency exists in HEAD. I don't know whether that was an intended design choice for whether it's an architectural bug — the new EFQ is fairly complex, so I didn't have time to study it yet.

Apparently, earlier patches in this issue worked just fine without any kind of tweak to Field API for that. Only after the new EFQ landed, those exceptions started to happen. That is why I do not see that detail to be a problem that needs to be figured out in this issue. I think this will have to be addressed in #1845372: Deleting a field from a non-node entity bundle results in an Entity Field Query Exception and/or related issues (I already commented over there).

- putting this straight into entity info means that patches like the entity reference one are going to list it as something which could be referenced, which of course it can't be. This is strongly linked to the no storage issue.

Well, I see two points there:

1) The issue summary already links to a follow-up issue that might introduce an actual storage of contact messages (which would be nice to have). If that won't happen for D8 core, then I guess that a contrib module would actually be able to plug it in. Therefore, I do not think it is not guaranteed that contact message entities will never be stored.

2) We have the same issue with config entities, and we have a very similar issue with regard to generating entity tokens and all entity types that cannot be rendered.

I think this overall issue needs to be addressed in a much larger scope. At least it doesn't make sense to me to implant a workaround for the contact message entity type only, when there is no sign of a solid idea or conclusion on how to resolve the overall problem space. That said, I'm fairly sure that the final solution will be based on entity type metadata (entity info property/ies), so I think that the contact message entity being introduced here can be brought in line very easily.

Does AccessDeniedHttpException let you pass a custom message?

It has a $message argument and so I tried, but its value is not used. I'm not sure whether it's supposed to be used for public/user-facing messages though, as it's documented with "internal message." We'll have to figure that out more generally - I'd agree that it would be most natural to just pass a message along with it — OTOH, an internal message (for logs) sounds helpful to me, too. (But the current $message does not appear anywhere.)

Changes:

  1. Removed/reverted irrelevant phpDoc @param type changes.
  2. Clarified base table condition in field_has_data().
  3. Clarified the purpose of contact_menu_local_tasks_alter().
  4. Replaced verbose todo on Message::$recipient with shorter clarification on its usage/effect.
  5. Clarified Message::entityInfo() override for user contact forms.

Status: Needs review » Needs work
Issue tags: -Killer End-User Features, -Entity system, -API clean-up, -Platform Initiative, -Field system

The last submitted patch, contact.entity.40.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: +Killer End-User Features, +Entity system, +API clean-up, +Platform Initiative, +Field system

#41: contact.entity.40.patch queued for re-testing.

sun’s picture

I'm not able to reproduce that test failure.

Looks like we have yet another random test failure:

The test did not complete due to a fatal error.
	Completion check	EntityQueryTest.php	272	Drupal\system\Tests\Entity\EntityQueryTest->testSort()
sun’s picture

FileSize
1.44 KB
57.3 KB

Replaced contact_menu_title_category() with entity_page_label().

With that, I think we should be back to RTBC here.

My stance on the two major issues that have been raised:

1) The question of which entity types can be referenced by an Entity Reference field and other things can reasonably not be addressed or solved by this issue/patch. Due to taxonomy vocabularies, that's a pre-existing problem space in HEAD. All this issue/patch is doing is to add another entity type to the existing stack of entity types. In turn, I don't see that as something to be fixed here.

2) The EntityNG conversion does not seem to be documented and could also involve risks regarding performance and other topics - in general, that API still appears to be in flux and does not appear to be stable enough in order to base major features onto it. There are other major feature requests in the queue that are not based on EntityNG yet. Therefore, I think that conversion should happen in a follow-up issue.

So I hope we can move forward here. :)

sun’s picture

Status: Needs review » Reviewed & tested by the community

Tentatively moving back to RTBC, since I think all review issues have been either fully addressed by code comments or issue comments.

andypost’s picture

+1 RTBC

Some follow-ups needs to be filed:
- views support
- settings to change a tabs/menu-items/dropdown for listing of categories if front
- upgrade path, the introduced UI change could break sites (tabs is not a coomon navigation

+++ b/core/modules/contact/contact.moduleundefined
@@ -115,6 +133,71 @@ function contact_menu() {
+function contact_menu_local_tasks_alter(&$data, $router_item, $root_path) {
+  if ($root_path === 'contact' || $root_path === 'contact/%') {

I think that we need a some settings for that, probably better to have config screen to change tabs/menu-items for that

webchick’s picture

Assigned: sun » catch

Looks like catch has got this one.

andypost’s picture

Patch needs re-roll for already commited part #1848874: Clean up and simplify user cancel methods and processing

sun’s picture

FileSize
1.03 KB
48.81 KB
  1. Merged HEAD.
  2. Clarified comment in system_element_info().
sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

I've added all known follow-up issues to the issue summary.

catch’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/contact/contact.moduleundefined
@@ -115,6 +133,71 @@ function contact_menu() {
+ * All categories of the site contact form are expanded into local tasks, one
+ * for each category. Each category presents a bundle for contact message
+ * entities and can have different fields attached to them. Without this
+ * expansion, all non-default categories would not be visible/accessible through
+ * the user interface.
+ */
+function contact_menu_local_tasks_alter(&$data, $router_item, $root_path) {

I still don't see why this is necessary. Are you doing this to avoid looping over the contact entities in hook_menu()?

If so there should be two simpler ways to do it:

- do the expansion in hook_menu(), it's ugly creating all those router items but it's considerably simpler and it's what we do for block/theme configuratoin for example.

- have an admin page with a list instead of tabs such as we do for menus and content types. This will also allow the UI to scale to some extent if someone defines 200 contact categories so seems like the better option.

I opened #1856936: Consistently define 'no storage' or 'external storage' for entity types for the storage / no storage / base table stuff.

sun’s picture

Status: Needs work » Needs review

The tab expansion happens for the public/user-facing /contact path, not the administration pages.

Almost identical code is contained in the following three issues:

#1067408: Themes do not have an installation status — expands tabs on admin/structure/blocks/% and admin/appearance/%
#1668292: Move simplified Profile module into core — expands tabs on admin/user/edit/%
#1825044: Turn contact form submissions into full-blown Contact Message entities, without storage — expands tabs on contact/%

The technical reasoning for this code is:

  1. There is only one route, which receives a dynamic argument. It is not correct to duplicate route definitions, if the routes are factually identical and only vary by request parameter. Doing so would not only harm overall system performance, but would also make it harder to override the routes.

  2. In all of these cases, the dynamic request parameter is solely used to expose a link as local task that has a valid value for the parameter filled in. All of the routes are identical, only a path parameter varies. The router/routing system translates inbound request parameters into routes. However, it is not its job to generate routes. The link expansion happens in a different layer of the system, the presentation layer. Which also means that the expansion only happens when actually needed.

  3. Expanding all possible arguments into separate routes in hook_menu() requires and adds a huge chain of dependencies to the router rebuilding. Furthermore, it also adds the reverse: the route definitions depend on a certain, undefined state of the dynamic parameters. Therefore, whenever a contact form category is added, edited, or deleted, the entire router would have to be rebuilt. For no good reason at all, since all of the routes are identical. We don't want and we don't need more of these improper route definitions in core. Quite the contrary, we want and need less of them. Ideally, we eliminate all of them for D8. They caused us nothing but harm in the past, proven by a dozen of major bugs in the core queue.

  4. Instead of rejecting patches like this that do things properly, I'd rather recommend that we should move forward with the tab expansion in all of the mentioned issues above. Once they are in place, we will have a better idea of how this algorithm could be abstracted and simplified for implementing modules. And even if that generalization won't happen for D8, we'd at least do things properly. However, given the new routing system in D8, I'm fairly confident that we will have to do significant changes to hook_menu() anyway.

Lastly, note that there is a follow-up issue filed already that asks for a simple configuration option for contact categories to control whether they are exposed as tabs on /contact or not, so they can be linked to and accessed via custom/individual links instead. If the routes were directly expanded in the router, then we'd unnecessarily have to create all the routes, but hide them, so they do not appear as tabs. That logic would make little sense, as it would mean to just add things in order to hide them. Instead, it makes much more sense to only generate and add what should actually be added. The router is the wrong place for that.

catch’s picture

Status: Needs review » Needs work

Let's just remove the tab functionality for contact forms then. I agree dynamic hook_menu() items are ugly and would also like to kill them, but that tab expansion functionality just does not belong in contact module.

sun’s picture

Um? How do you access the non-default contact category forms as a user/visitor without tabs?

sun’s picture

Assigned: catch » sun
Status: Needs work » Needs review
FileSize
5.26 KB
45.87 KB

I'm still not sure I understand, but if I interpret #54 correctly, then the direction is to

1) Remove the exposure of categories entirely for now, and instead,

2) Discuss and evaluate how to allow users to expose links to category-specific forms in a separate follow-up issue?

3) And in the worst case, simply accept that site admins will need to manually create links to specific category forms?

I hope I got that right — works for me.

Changes:

  1. Removed contact_menu_local_tasks_alter() / exposure of categories as tabs.
  2. Fixed contact message extra fields descriptions duplicate their labels.
sun’s picture

Feedback, anyone? :)

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

I'm not sure what the problem is with dynamic hook_menu implementations but anyway, catch's feedback has been addressed so back to RTBC.

catch’s picture

So the problem with dymamic hook_menu() implementations is they bloat the router table (although not as much as the millions of configuration forms under /admin), and we issue lots of uncacheable queries against the router table - mainly to find the current router item for the request. Having a huge router table is also part of the reason why we have our own, custom router storage in Drupal 8 for the new Symfony-based router rather than using theirs (although there's other reasons too, but Symfony's is designed for a couple of dozen router items and Drupal sites can have a couple of thousand).

I think it's fine to remove the dynamic links to contact category forms and add those back in a follow-up, could we do two follow-up issues then:

- add the contact links back somehow
- add this dynamic tab-expanding feature to the menu system (even if it takes a couple of specific implementations to get that right, I still don't think contact module is the place to start).

sun’s picture

sun’s picture

Status: Reviewed & tested by the community » Needs work

The last submitted patch, contact.entity.56.patch, failed testing.

sun’s picture

Status: Needs work » Needs review

#56: contact.entity.56.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, contact.entity.56.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: +Killer End-User Features, +Entity system, +API clean-up, +Platform Initiative, +Field system

#56: contact.entity.56.patch queued for re-testing.

sun’s picture

Status: Needs review » Reviewed & tested by the community

Jesus, we have a dozen different random test failures in HEAD currently. :-/

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Looks like this is ready to go now. Committed to 8.x.

sun’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Also: #1867030: Contact message preview appears at random form position after sorting fields in Manage fields (which could use technical/architectural feedback on entity previews, if anyone has ideas)

yched’s picture

    // The user contact form only has a recipient, not a category.
    // @todo Convert user contact form into a locked contact category.
    if ($message->recipient instanceof User) {
      $form['recipient'] = array(
        '#type' => 'item',
        '#title' => t('To'),
        '#value' => $message->recipient,
        'name' => array(
          '#theme' => 'username',
          '#account' => $message->recipient,
        ),
      );
    }
    else {
      $form['category'] = array(
        '#type' => 'value',
        '#value' => $message->category,
      );
    }

So this results in $message->bundle() === NULL on "personal user contact form" messages, which is a problem, bundle() should always be set I think.

This breaks #1852966: Rework entity display settings around EntityDisplay config entity when trying to view the result posting a user contact message, since the patch over there does:

class EntityDisplay extends ConfigEntityBase {
  public function __construct(array $values, $entity_type) {
    if (!isset($values['targetEntityType']) || !isset($values['bundle']) || !isset($values['viewMode'])) {
      throw new \InvalidArgumentException('Missing required properties for an EntiyDisplay entity.');
    }
}

In short, an empty value in one of those will cause headaches down the road, and we don't want to have to bullet-proof for those - every entity should have a bundle.

In order to get back that patch to RTBC, I'll remove those consistency checks for now with a @todo to add back when #1856556: Convert user contact form into a contact category/bundle is done.
Current tests should be ok because nothing currently tries to save display settings for the 'personal user contact' messages.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

larowlan’s picture

andypost’s picture

Status: Closed (fixed) » Needs work
+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.phpundefined
@@ -0,0 +1,126 @@
+ *   list_controller_class = "Drupal\contact\MessageListController",

There's no such file at all!

sun’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
632 bytes

Thanks. Not sure how that happened.

tstoeckler’s picture

Yup. +1

webchick’s picture

Status: Reviewed & tested by the community » Needs review

Hm. I can commit that of course, but why on earth did something not blow up hugely about this? Seems like we're lacking test coverage for list controllers? I searched the issue queue and didn't find an issue about that, but let's either add the tests here or file a follow-up (most likely major task, I guess) for them first.

sun’s picture

Status: Needs review » Reviewed & tested by the community

It does not blow up, because that particular meta info is not used anywhere. It's just simply needlessly registered information.

Contact messages are not stored, and thus they are not listed anywhere. Hence, there's no EntityListController.

We do not need to test code that is not used anywhere and which shouldn't exist in the first place.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok cool, thanks for the explanation.

Committed and pushed to 8.x. Thanks!

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.