Amazingly http://api.drupal.org/api/drupal/modules--locale--locale.admin.inc/funct... is the only place where language deletion happens. We all know doing these in form submit hooks is equivalent to driving us off a cliff. This should be API-ified like we do for loading and saving languages proper.

Let's start with just reworking the code we have in the form submit function to a language deletion with hooks for modules to chime in.

Parent issue

#1260488: META: Introduce real language APIs

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gábor Hojtsy’s picture

Issue tags: +D8MI

Tagging.

tarmstrong’s picture

Assigned: Unassigned » tarmstrong

Assigning to me.

tarmstrong’s picture

Status: Active » Needs review
FileSize
3.37 KB

Here's a patch for adding a locale_language_delete() function in locale.module.

Gábor Hojtsy’s picture

Thanks! Some comments:

1. Let's modernize this code while we are here like we did with #1215716: Introduce locale_language_save(). The multilingual_settings_changed hook is about to be removed with these changes in favor of more specific hooks like _presave and _insert/_update in that function. We should have similar patterns applied here. Then other modules can react on the delete action of a language, and the node related code can then be moved to node module into a hook implementation that reacts to a language being removed. And other modules can react to this. This is a seriously missing feature in Drupal 6/7.

2. The $language argument is just a language code. We so far use $langcode for that, so let's stick to that here too. It is not a language object, so $language is misleading.

3. You have a static variable for the language list. This means if someone attempts to delete the same language code multiple times in a request, it will fire all the database queries and the hooks and fire the watchdog messages.

4. There might be other places where the language list is cached with drupal_static, those caches would need to be cleared I think. Whether it is this function's job or should be done via hooks is a question of whether they are internal to locale module I think or external.

That is all, keep up the good work :) Thanks!

Gábor Hojtsy’s picture

Status: Needs review » Needs work
Gábor Hojtsy’s picture

Oh, and also, let's not include the issue URL in the phpdoc comment (we never really do that) but do include a line of doc on the $langcode. You'll find standard docs for that on other functions where it is used. I know its a pattern, but its best to have it documented, especially on core API functions like CRUDs for stuff.

tarmstrong’s picture

Thanks for the feedback, Gábor. Here's an updated patch. I cleaned up some of the variable names so it's a little more readable now.

Two questions:

  1. In #1215716: Introduce locale_language_save(), locale_language_save() takes a $language object. Should we mirror this or does it make sense for delete to just take the langcode? In _save(), the $language parameter makes sense because it needs that information to save the language; in _delete(), we can get that information on our own. Is my logic sound? I think it's a more convenient API if it just needs a langcode.
  2. I'm checking to see if the language exists. If the language doesn't exist, should it fail noisily?
tarmstrong’s picture

Status: Needs work » Needs review
Gábor Hojtsy’s picture

Status: Needs review » Needs work

@tarmstrong: great improvements. Let's make it even better :)

1. Taking $langcode is good and consistent with node_delete(), user_delete(), etc. it makes best sense to keep consistency with the rest of Drupal's APIs.

2. I think we can return TRUE if we successfully deleted it, and FALSE if not. That is not really standard to things like node_delete() and user_delete(), but its good info for the outside to make noise if they want to. You can "make noise" in watchdog() but please no drupal_set_message() in an API function :) That should be the role of the UI. See the next point also :)

3. I'd move the drupal_set_message() back to the form API submit callback. API functions should not put out user messages. If you want to delete 10 languages at once with the API, you probably like to output a summary message not to show the user all 10 messages :) Its the responsibility outside of the API.

4. It is important to pass on the language code with the delete API invoke, so implementers of the API would know which language is removed. The old multilingual settings changed hook did not do that but that is a mistake :) Here we know exactly what info to pass. Again, like with node_delete(). Consistency is key. In fact now that I look at it, node_delete passes on the whole node object in its hook invokes. Hm. Hm. Then probably we should do that too. (And do the hook invoke before we actually delete the data, like that).

5. Cross dependencies are to be avoided. We actually have an issue to remove node related stuff from locale module at #540294: Move node language settings from Locale to Node module, but this issue is what makes it possible for language delete, so we should take advantage of it and do it here. Let's do our implementation of hook_locale_language_delete() then in node.module and move the node query there :) Decoupling is good :) This solution will make it possible for other modules to do the same things. Like we don't have this for path module or user module. If a language is deleted but you have paths in that language or comments or users, those will stay around. Kind of broken. Not to be solved here, but just as examples, that we make that possible here for them to hook into language deletions and act on it.

6. Now that we are adding hook_locale_language_delete(), we need to document that in locale.api.php, see again the locale_language_save() issue for that stuff.

tarmstrong’s picture

Status: Needs work » Needs review
FileSize
4.59 KB

Thanks again!

Should there be a pre_delete and a delete hook, or is that excessive?

Gábor Hojtsy’s picture

Once again, I think whatever other delete workflows have, we should if possible, but not any more if possible :) Consistency, consistency, consistency :)

tarmstrong’s picture

Just adding the @return line to phpdoc for locale_language_delete().

Gábor Hojtsy’s picture

Status: Needs review » Needs work

Very good, very good. Two things I noticed still:

1. The api.php function should have some kind of short example there. Like a short update to some imaginary data or something. Other functions like that have similar examples. It could use the node implementation as an example for simplicity :)

2. I'm not sure the page cache clearing belongs back to the form API submit. I think it made sense in the API function. Yeah, removing language does have wide reaching implications. :) This is probably debatable and will/might be debated by others :)

tarmstrong’s picture

Good point about the cache clear. I can't imagine a situation in which I would want to delete a language without deleting the page cache. But there are more creative people than me out there :)

Updated with the api.php change and the moved page cache clear.

Gábor Hojtsy’s picture

Status: Needs work » Needs review

Looks good to me now. Would be good to have more reviewers, but I think its good now as-is :)

Gábor Hojtsy’s picture

Status: Needs review » Needs work

In fact, we figured out in the save patch that we can partially remove the very ambiguous and non-standard multilingual_settings_changed hook once this patch lands. The latest save patch at http://drupal.org/node/1215716#comment-4938280 already deprecates the hook and offers more standard alternatives for insert and update language operations. This patch should complete the removal by implementing the delete hook on behalf of the field module (like it does for node module already). Yay for standardization!

tarmstrong’s picture

Updated to change field_multilingual_settings_changed() to field_locale_language_delete().

tarmstrong’s picture

Status: Needs work » Needs review
podarok’s picture

subscribe
works for me

moving cache_clear_all outside from db_delete stack, cause DrupalCacheInterface::clear can be overriden in contrib modules and not safe. Stopping at this point can delete all the strings from table locales_target without language deletion from table languages

    // Remove translations first.
    db_delete('locales_target')
      ->condition('language', $language->language)
      ->execute();

    // Remove the language.
    db_delete('languages')
      ->condition('language', $language->language)
      ->execute();

Deletion is very fast even on large tables so no need to use transaction (paranoic 8)) )

svendecabooter’s picture

Looks good!

Updated the patch a bit:
* Remove information about hook_multilingual_settings_changed() in locale.api.php. Not sure if this is too early to be removed, since #1215716: Introduce locale_language_save() has to land as well before the hook is completely gone. But it'll have to be removed at some point anyway...
* Removed useless return statement in locale_languages_delete_form_submit()

podarok’s picture

#20
looks good
except

podarok@pubuntu:/var/www/d8/drupal$ git apply -v locale_language_delete-1260528-20.patch
locale_language_delete-1260528-20.patch:151: trailing whitespace.
locale_language_delete-1260528-20.patch:154: trailing whitespace.
svendecabooter’s picture

Fixing whitespace

podarok’s picture

Status: Needs review » Reviewed & tested by the community

#22
nice!

looks like RTBC

Gábor Hojtsy’s picture

Agreed, looks good.

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Reviewed this and it looks like a nice clean-up that had great reviews. Committed to 8.x. Go i18n initiative, go! :-)

Status: Fixed » Closed (fixed)

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

Gábor Hojtsy’s picture

Issue tags: +language-base

Tagging for base language system.

Gábor Hojtsy’s picture

Issue summary: View changes

Add parent