It should not be necessary to copy image toolkit include files into the /includes directory. Some image toolkits are shipped with a module (for example, the Image module includes the ImageMagick image toolkit), and enabling this toolkit should be as easy as enabling the module and then going to the image toolkit administration page to select the new toolkit.

I have provided an implementation that allows image toolkit include files to be identified in a module's .info file. This scheme leverages the new registry functionality and reduces the administration effort required to get a new image toolkit deployed.

During testing I used the Image module as my example and made the following additions to its .info file:

files[] = image.module
files[] = image.admin.inc
files[] = image.imagemagick.inc
files[] = views.inc

image_toolkit[] = image.imagemagick.inc

The last line identifies the image.imagemagick.inc file as an image toolkit. Simply by enabling this module, the image.imagemagick.inc toolkit becomes an option on the image toolkits menu.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

paul.lovvik’s picture

FileSize
4.3 KB

And here is the patch.

pwolanin’s picture

We should coordinate a little this patch with the broader issue: http://drupal.org/node/259103

pwolanin’s picture

minor code suggestion:

instead of doing this:

$path = drupal_get_path('module', $info['name']) . '/' .

maybe you should just select the filename in the query too? since if you trace through what drupal_get_path() is doing, it's just calling dirname() on the filename field from {system}.

Of course, maybe it's beeter to continue to use the API in case that changes.

Dries’s picture

I don't know (yet) how often _get_toolkits_from_module_info() would get called but it seems like a hook-based approach could yield better performance. Right now, it takes an SQL query and a lot of unserialization -- both are expensive. If _get_toolkits_from_module_info() gets called on every page view, that would be a problem IMO. I guess there could exist situations where _get_toolkits_from_module_info() gets called a lot ... ?

pwolanin’s picture

@Dries - currently it is not called often. I discussed with Paul the alternatives between a module hook and the .info file. My suggestion to go this way was based on the idea that we may be moving towards modules that don't require a .module file. http://drupal.org/node/259412 So, I'm not sure what the tradeoff is between load/parse a file vs. this? I guess if you are running with an opcode cache the module hook would be faster?

paul.lovvik’s picture

@pwolanin - I think this functionality is orthogonal to the choice of whether pluggable toolkits should be implemented using federated function names or OOP. In fact, no matter how pluggable toolkits are implemented the same problem will arise. Specifically, that all such pluggable toolkits should be able to be deployed without moving files into Drupal's /includes directory.

pwolanin’s picture

@paul.lovick - yes, I agree that this problem will need to be solved for all toolkit/framework types. Probably it would make sense to get this patch in and then consider subsequently the question of standardizing the approach used by toolkits/frameworks

paul.lovvik’s picture

@Dries - pwolanin is correct in that _get_toolkits_from_module_info() is not called often. It is currently only called from image_get_available_toolkits, which is only called from system_image_toolkit_settings(). Therefore it would only be used if a user went to the toolkit selection page, which I expect would only be available to administrators and used very infrequently.

Also, if my patch to improve the user experience of setting up the Image module is approved (http://drupal.org/node/267489) image_get_available_toolkits would be called when the status report is displayed as well.

On my system (stock Drupal 7 + Image module) it takes 68 ms to execute the _get_toolkits_from_module_info() function. This is pretty fast, but I expect that time would increase with a very large database.

I will implement it with a module hook and compare the timing.

paul.lovvik’s picture

FileSize
3.78 KB

I reworked the patch to use a module hook instead. This improved the performance significantly:
* 4.2ms for the first execution
* 0.24ms for subsequent executions

It is definitely worth using the hook approach instead. Here is the new patch.

Dries’s picture

I agree that the hook-based approach is better.

I'd be happy to remove the "copy your toolkit into ./includes" mechanism. Is there a good reason to maintain backward compatibility? We can probably count the number of image toolkits on two fingers so I recommend that we try to clean this up as much as we can (if possible). Cleaning things up makes it easier to explain/document in books, workshops, etc -- and will make things a tad faster.

paul.lovvik’s picture

As far as backward compatibility, we have to be careful not to break the gd image toolkit. This one ships in Drupal core and is in the /includes directory, not associated with any module (at least not that I am aware of).

We could either keep it the way it is so gd continues to work unmodified, or we could associate the gd image toolkit with one of the core required modules. I was thinking we would leave it the way it is for the gd toolkit, but I'm definitely open to any guidance there.

Crell’s picture

I am about 70% of the way through writing an RFC on a pluggable framework for D7. It will be posted to the planet hopefully later tonight. The image system is one of the subsystems I consider a target. :-)

pwolanin’s picture

@Cell - look forward to reading it, though I think a simple re-write with procedural code is going to be a lot less pain than other options...

Dries’s picture

@Crell, I agree with pwolanin that it is going to be pretty hard to beat this patch in terms of simplicity.

@paul, associating the GD image toolkit with the system module _might_ be a good idea. There are other special cases in system.module. It's worth thinking about some more.

Dries’s picture

@Crell, Paul, pwolanin: I think we should go ahead with Paul's patch, or a simplification of that. Let's not block this by creating dependencies.

@Crell: propose the pluggable framework as a separate patch on top of this one. It will put us in a better position to compare both.

Crell’s picture

@Dries: RFC has been posted to the planet and dev list.

We certainly could go forward with this patch for now and convert later when/if it makes sense to. I just wonder if that will be more work in the long run, and if that's worth it. I will not block such an effort, however.

drewish’s picture

This seems pretty similar to #140932: Improve image-api: Image toolkits should be implemented as modules.. The change would allow you to create an imagemagick module that just implements the hook and points to its .inc file. Then the toolkit could implement hook_requirements and make use of the update status notifications.

That said I think I'd rather have all the toolkits as modules so GD isn't a special case.

pwolanin’s picture

One minor comment - looks like you could be using module_invoke_all to simplify this code:

+function _get_toolkits_from_modules() {
+  $output = array();
+  foreach (module_implements('image_toolkits', TRUE) as $module) {
+    $toolkits = module_invoke($module, "image_toolkits");
+    for ($i = 0; isset($toolkits[$i]); $i++) {
+      $path = drupal_get_path('module', $module) . '/' . $toolkits[$i];
+      $file = pathinfo($path);
+      $output[$path] = new stdClass();
+      $output[$path]->filename = $path;
+      $output[$path]->basename = $file['basename'];
+      $output[$path]->name = $file['filename'];
+    }
+  }
+  return $output;
+}

http://api.drupal.org/api/function/module_invoke_all/7

like:

$toolkits = module_invoke_all("image_toolkits");
foreach ($toolkits as $kit) {
  ...
}
paul.lovvik’s picture

I thought about using module_invoke_all, but I need the names of the modules as well as the toolkit include file, or the full path to the include file. I could make the return type more complex to also include the module name, but in either case it doesn't seem worthwhile to make the endpoints more complicated to simplify the core code.

I think we should be simplifying APIs as much as possible because that keeps the barrier for contributing more great capabilities low.

Crell’s picture

That's something I ran into when adding the page split functionality to the D6 menu system. module_invoke_all() is only useful if you don't care what modules returned what. If you do, you have to do it manually. Sounds like that's a worthwhile feature addition of some kind. It's also a separate issue from this patch, so it's probably best to go with the manual method for now and refactor later when/if we have a module_invoke_all_with_name() of some kind. (That would be a bad implementation, but you get the idea.)

paul.lovvik’s picture

FileSize
18.16 KB

Here is a new patch that moves the image.gd.inc file into the modules/system directory and locates this toolkit the same as any other contributed image toolkit.

Any feedback is greatly appreciated.

pwolanin’s picture

I think this can actually be simplified - we can indeed use module_invoke_all(), unless I'm missing something, since drupal_function_exists() will find the include file based on the registry.

One minor question - should we hack the repository to retain the revision history for image.gd.inc?

Crell’s picture

I'm against hacking the repository. Every time we do that, it makes it harder to ever do anything but CVS, and our specific customized CVS repository in particular. If someone is really tracing back the history of that file (how often does that happen over a long term?), they'll note that it disappeared and reappeared somewhere else between two given revisions and know to start looking there. No big deal, as long as there are no changes to the file at the same time (thus eliminating the need to diff between the two files).

pwolanin’s picture

there are no changes - it's a simple move.

Dries’s picture

Status: Needs review » Fixed

Committed to CVS HEAD. I didn't move the file because that would make it impossible to check out a DRUPAL-5 or DRUPAL-6 branch, AFAIK.

xmacinfo’s picture

Status: Fixed » Active

Tried this with CVS update and got this message:

warning: require_once(./includes/image.gd.inc) [function.require-once]: failed to open stream: No such file or directory in /Sites/drupal/includes/bootstrap.inc on line 1241.

I got this error while trying to load:
http://devel.drupal.cvs/admin/settings/image-toolkit

pwolanin’s picture

Status: Active » Fixed

@xmacinfo - you need to force a registry rebuild - go to admin/settings/performance and hit the "Clear cached data" button at the bottom

xmacinfo’s picture

Done. Thanks.

cburschka’s picture

Status: Fixed » Active

hook_image_toolkits should perhaps be mentioned in contributions/docs/developer/hooks/core.php. That way, it will be documented on api.drupal.org. A hook is only useful if module developers can find it...

Something like this should do it:

/** 
* Define image toolkits provided by this module.
*
* @return
*   An array of image toolkit names.
*/
function hook_image_toolkits() {
  return array('gd');
}
pwolanin’s picture

Status: Active » Fixed

@Arancaytar - looks like you have CVS access. It would be great if you could commit any updates you see as needed - anyone with CVS can alter the developer docs. http://drupal.org/cvs?commit=126084

cburschka’s picture

pwolanin: Oh. I'm afraid I didn't quite realize how the contrib repository works - I thought I only had commit access to the module I'm maintaining. Thanks for the info!

dropcube’s picture

Status: Fixed » Needs review
FileSize
688 bytes

The patch adds a line to the change log file.

cburschka’s picture

Status: Needs review » Needs work

That line should probably be descriptive, like the other lines in the changelog.

The change is already in, so you need to say "can now be added by modules without being copied to includes/".

dropcube’s picture

Status: Needs work » Needs review
FileSize
684 bytes

What about this one?

Susurrus’s picture

The English is a little off. Maybe something like:

Image toolkits can be added by modules and no longer need to be copied into the includes directory to be used.
dropcube’s picture

FileSize
697 bytes

Ok, this patch includes Susurrus' suggestion (sorry, English is not my native language)

paul.lovvik’s picture

This text doesn't exactly describe the behavior. You can no longer include an image toolkit by copying it into the includes directory. The text should describe this a bit more explicitly. I suggest this text:

Image toolkits are added by modules and are no longer copied into the includes directory to be used.

cburschka’s picture

FileSize
702 bytes

Yeah, that would be it.

StevenPatz’s picture

Status: Needs review » Reviewed & tested by the community
Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

Anonymous’s picture

Status: Fixed » Closed (fixed)

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