We've stripped out all previous .info load and parse code. We now need new code to load and parse our PHP skin .inc files.

Optionally consider to add support for CTools plugins and/or fork the plugin system. This is completely peripheral to the entire challenge at hand and could still be done months later.

See #897822: [META ISSUE] Stop storing skins in .info files; implement skins as PHP instead for previous discussion on the topic.

CommentFileSizeAuthor
#116 skinr-HEAD.include-path.115.patch4.82 KBsun
#115 patch_commit_93332f552779.patch1.49 KBmoonray
#114 skinr-HEAD.include-path.114.patch3.11 KBsun
#113 skinr-HEAD.include-path.113.patch1.38 KBsun
#112 patch_commit_d1f39a5c047d.patch814 bytesmoonray
#108 skinr-HEAD.include-subdir.108.patch3.79 KBsun
#107 skinr-HEAD.include-subdir.107.patch2.71 KBsun
#105 skinr-HEAD.include-revert-debug.105.patch2.15 KBsun
#103 patch_commit_cf6247acfe37.patch433 bytesmoonray
#92 skinr-HEAD.include.91.patch31.07 KBsun
#90 skinr-HEAD.include.88.patch30.38 KBsun
#88 skinr-HEAD.include.86.patch30.38 KBsun
#86 skinr-HEAD.include.85.patch28.42 KBsun
#84 skinr-HEAD.include.84.patch28.39 KBsun
#81 skinr-HEAD.include.80.patch39 KBsun
#73 skinr-HEAD.include.73.patch25.46 KBsun
#71 skinr-HEAD.include.71.patch16.37 KBjacine
#68 skinr-HEAD.include.68.patch29.66 KBsun
#66 skinr-HEAD.include.66.patch29.65 KBsun
#60 patch_commit_d876447915d9.patch10.01 KBmoonray
#47 956994-skinr-include-files-47.patch13.01 KBcoltrane
#45 956994-skinr-include-files-45.patch12.85 KBcoltrane
#43 956994-skinr-include-files-43.patch13.96 KBcoltrane
#40 956994-skinr-include-files-40.patch12.21 KBcoltrane
#37 956994-skinr-load-parse-36.patch8.3 KBcoltrane
#29 No API entry required for themes.8.89 KBmoonray
#29 Includes API code; API entry required for themes.8.97 KBmoonray
#20 skinr_956994_20.patch8.69 KBmoonray
#18 skinr_956994_18.patch8.71 KBmoonray
#13 test_skin.patch3.24 KBjacine
#12 test_skin.patch3.22 KBjacine
#11 test_skin.patch3.28 KBjacine
#10 test_skin.patch3.23 KBjacine

Comments

moonray’s picture

Here is a question to start things off:

  1. Should we use hook_library() and drupal_add_library to process the "attached" key in our .inc files, or is there an easier/better way? Be aware that there most likely will be multiple "attached" arrays for an element.
sun’s picture

Nope, hook_library() is for registering libraries that are shipped with a module. A module that exposes a skin may use hook_library() and attach that to a skin, but Skinr does and must not.

Skinr will solely use http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_pr...

moonray’s picture

  1. Our skin (not skin_info) object looks as follows. I feel it could use some naming tweaks and perhaps additional optimization.
    <?php
    $skin = new stdClass();
    $skin->theme; // Shared primary key: theme name this skin applies to (e.g. Bartik).
    $skin->module; // Shared primary key: module the element belongs to (e.g. panels).
    $skin->sid; // Shared primary key: element id this skin applies to (e.g. display-1-pane-3 or display-1-region-center).
    $skin->skins; // Applied skins; this is an array of skin options keyed by skin name (e.g. array('skin_name' => array('option1', 'option3'))).
    $skin->settings; // Additional settings; this is a serialized array for storage of additional settings by other modules. It never got used by skinr.
    ?>
    

    EDIT: $skin->skins also has an additional oddball key: _additional. This stores manually entered classes (e.g. array('skin_name' => array('option1', 'option3'), '_additional' => 'manuall-entered-class and-another-class'))

  2. EDIT: With our latest patch $skin->skins looks as follows:

    <?php
    array(
      '_options' => array(
        'skin_name' => array('option1', 'option3'),
        'skin_name2' => array('option7'),
      ),
      '_additional' => 'manuall-entered-class and-another-class',
    );
    ?>
    
jacine’s picture

I think we could stop calling the custom classes "additional." It was named that way since the template support was also there, but that's gone, so this should be named more appropriately. In our mockups for this, we've got this in an "Advanced" tab. Not sure whether that helps or not, but just thought I'd mention it.

$skin->skins definitely needs to change to $skin->options as I've expressed elsewhere.

Other than that, I don't have much to contribute to this conversation at this point.

moonray’s picture

How about:

<?php
$skin = new stdClass();
$skin->theme; // Shared primary key: theme name this skin applies to (e.g. Bartik).
$skin->module; // Shared primary key: module the element belongs to (e.g. panels).
$skin->sid; // Shared primary key: element id this skin applies to (e.g. display-1-pane-3 or display-1-region-center).
$skin->options; // Applied skins; this is an array of skin options keyed by skin name (e.g. array('skin_name' => array('option1', 'option3'))). We'll drop the _options and _additional parts.
$skin->classes; // The classes from the advanced options which was previously $skin->skins[_additional].
$skin->settings; // Additional settings; this is a serialized array for storage of additional settings by other modules. It never got used by skinr.
?>
  1. Skin object:
    1. Any suggestions for a better name than sid for the skin id (sun mentioned this is used for session id). Or should we leave this as is?
    2. Someone suggested in a previous ticket that having a unique $skin->name (which would be a combination of theme__module__sid) would be useful for import/export. Thoughts on that, please?
    3. Do we need the additional settings (it could be used to extend skinr, but wouldn't be used in skinr core)?
sociotech’s picture

Regarding #1, what's wrong with just using "id"? Since it's a skin object property there's no namespace conflict.

Regarding #2, I think skin import/export is a critical ability so I'm definitely in favor of a unique machine name that enables that.

Regarding #3, I'm unclear on the use case for this being in the Skinr module if it doesn't use it. Couldn't other modules just extend the class if they needed to add values for their own purposes?

jacine’s picture

I agree with @sociotech on #1 and #3.

As for #2, I'm hoping we can get feedback from @sun, @ericduran or someone else more familiar with exportables. I don't see how having this defined would hurt anything though, so I'd lean toward doing it.

Regarding CTools, I thought we decided against using it for a two reasons:

  1. The only benefit the CTools module would provide is the loading plugin files.
  2. We'd need to horribly complicate the syntax we've agreed upon, but making users explicitly define hooks.

I'm all for using CTools if it makes sense, but it doesn't appear to in this case. We need to keep skin creation as easy as possible.

sun’s picture

  1. Odd. Until now, I imagined that a sid would be an auto-incremented integer; i.e., an entirely internal database ID. Looks like it's actually a html_id...?
  2. Exporting/importing skin configurations requires to do not store the configuration in an auto-incremented primary key. As long as that is the case (it sounds like it is), it does not matter whether the primary key consists of 3 columns or just one.
  3. We don't need separate options and settings. We should use a single settings column, since 1) that is consistent with some schemas in core, and 2) clarifies that settings is not identical to the 'options' definition of the skin info hook. settings will only contain the configured key/value pairs of the skin options, not the options themselves.
  4. Additionally,
    $skin->options; // Applied skins; this is an array of skin options keyed by skin name (e.g. array('skin_name' => array('option1', 'option3'))).
    

    we should consider to "extract" (normalize) individual skins into separate table rows; i.e., $skin->name (ex. skin_name) should be a separate schema column and part of the primary key.

  5. Furthermore,
    $skin->module; // Shared primary key: module the element belongs to (e.g. panels).
    

    is an ambiguous column name. Normally, module refers to the module that exposed a configuration item in the first place. In Skinr, there are two kind of modules related to a configured skin. I'd suggest the following:

    $skin->module: The module (hm, or theme) that defined/registered the skin via hook_skinr_skin_info() (e.g., "example_skins").
    $skin->element_module: The module the element belongs to (e.g., "panels").
    $skin->element_id: The element ID the skin applies to (e.g., "display-1-pane-3" or "display-1-region-center").
    

Why 4 and 5, you ask? Because modules that are providing skins can be installed and uninstalled. If a module is uninstalled, then we want to i) remove all skin configurations for skins the module exposed, and ii) also remove all skin configurations that affect elements belonging to the module.

Normalizing the skin configurations into separate table rows per skin helps to maintain that data, and also, to properly export/import skin configurations. (Speaking in "features", you should be able to build one feature module that only configures typography across the site, as well as additional features that may touch the same elements but configure other styling aspects.)

EDIT: To summarize:

$skin = new stdClass();
$skin->theme: PK: The theme name the skin applies to (e.g. Bartik).
$skin->module: PK: The module (hm, or theme) that defined/registered the skin via hook_skinr_skin_info() (e.g., "example_skins").
$skin->name: PK: The skin's name, as registered via hook_skinr_skin_info() (e.g., "font_family").
$skin->element_module: The module the element belongs to (e.g., "panels").
$skin->element_id: The element ID the skin applies to (e.g., "display-1-pane-3" or "display-1-region-center").
$skin->settings: A (serialized) array of skin options (e.g., array('option1' => 'blue', 'option2' => 'red')).
moonray’s picture

A few things:

  1. Extracting skins into separate table rows leaves us with no place to store the "additional classes" in the skin. Where do we want to store that now?
  2. How do we distinguish between a theme and a module (what if they are named the same?) We need a $skin->source_type field. Also, perhaps we should then change the newly proposed $skin->module to $skin->source_name (it could be a theme instead of a module and we're defining a type here now). In keeping with code we have elsewhere (skinr_skin_info_process()) $skin->name might better be referred to as $skin->source_plugin.

EDIT: To summarize

<?php
$skin = new stdClass();
$skin->theme; // PK: The theme name the skin applies to (e.g. Bartik).
$skin->source_type; // Whether a module or theme implemented this skin: module || theme
$skin->source_name; // PK: The module or theme that defined/registered the skin via hook_skinr_skin_info() (e.g., "example_skins").
$skin->source_plugin; // PK: The skin's name, as registered via hook_skinr_skin_info() (e.g., "font_family").
$skin->element_module; // The module the element belongs to (e.g., "panels").
$skin->element_id; // The element ID the skin applies to (e.g., "display-1-pane-3" or "display-1-region-center").
$skin->settings; // A (serialized) array of skin options (e.g., array('option1' => 'blue', 'option2' => 'red')).
?>
jacine’s picture

StatusFileSize
new3.23 KB

Here's the patch for a test skin.

jacine’s picture

StatusFileSize
new3.28 KB

Err, here it is with the right CSS file path.

jacine’s picture

StatusFileSize
new3.22 KB

Ok, changing the hook based on recent discussions.

jacine’s picture

StatusFileSize
new3.24 KB

It would help if I returned the $skins. :)

jacine’s picture

A modified version of #13 has been committed: http://drupal.org/cvs?commit=461574

Now we need to write an actual test. Assumptions from @sun:

  1. The info hook is found + invoked.
  2. We get a skinr_test_font skin.
  3. It appears to be configurable.
  4. It appears in a 'typography' group.
  5. There are two options.
  6. Saving/applying the skin leads to skinr_test.css being loaded.
  7. The skin should only appear for blocks and regions
  8. The block or region gets the class of the selected option
coltrane’s picture

$skin->source_type;
$skin->source_name;

If $skin->source_name is unique on the install is $skin->source_type necessary? At the point the object exists we only care about applying the skin, and whether it was defined by a module or a theme doesn't change that application, right?

jacine’s picture

Ok, I am totally lost with where to even begin to write a test for this stuff. I could continue fumbling, but it's very frustrating and probably a waste of time when I could be fixing other issues, so I am going to stop now.

If $skin->source_name is unique on the install is $skin->source_type necessary? At the point the object exists we only care about applying the skin, and whether it was defined by a module or a theme doesn't change that application, right?

I would assume that $skin->source including the module or theme name would be enough here, but I'm not sure.

moonray’s picture

In response to #15: I'm not sure if there really is a use for it, but if someone needed to access the dir (for an include file), they would need to know whether to look in themes or modules.

moonray’s picture

Status: Needs work » Needs review
StatusFileSize
new8.71 KB

The attached patch implements plugin loading. The code is derived from wysiwyg module, but has some changes.

The following points still bother me, and I would like feedback on them:

  1. We implement a hook_skinr_api() to make sure our plugins are compatible with skinr. However, we have made no changes to any module plugins to warrant an api update (but we might? if so, they'll still be unique to D7 only, so no distinction is needed). As for skin plugins, those are an entirely new breed; if your file isn't in the new format, it's bound to be a .info file so no need to distinguish here either.
  2. We discussed having files load from the 'skins' dir automatically. However, what if a module that happens to have a 'skins' dir never wanted to have anything to do with the skinr module? We would still try to load those files and possibly wreck havoc. Because of that I think we probably should require an implementation of hook_skinr_include_directory() for any module that wants to have skins, and not have a default set.
  3. Should skinr_skinr_include_directory() be in skinr.module or skinr.handlers.inc? Should skinr_skinr_api() remain in skinr.handlers.inc or be moved to skinr.module?

EDIT: This patch most likely won't work with themes trying to implement hook_skinr_include_directory() or hook_skinr_api()

Status: Active » Needs work

The last submitted patch, skinr_956994_18.patch, failed testing.

moonray’s picture

StatusFileSize
new8.69 KB

Let's try an applicable patch with no whitespace issues.

jacine’s picture

Hi, I'm trying to test this. I have the following in my grids.module file:

/**
 * Implements hook_skinr_api().
 */
function grids_skinr_api() {
  return array('api', 2.0);
}

/**
 * Implements hook_skinr_include_directory().
 */
function grids_skinr_include_directory($type) {
  return $type; // This is what the instructions in the api docs say to do.
}

My directory is setup as follows:

grids/grids.info
grids/grids.module
grids/skins/ninesixty
grids/skins/ninesixty/ninesixty.inc

+  $directories = array();
+  foreach (module_implements('skinr_include_directory') as $module) {
+    // Check if the module implements the correct api version.

This is code is not listing my directory and my skins aren't loading. Am I missing something? The status is manually set to enable them, etc.

Also, I get that we don't really need a version for hook_skinr_api(), but we might, so can't we just define our path to the skins directory inside of hook_skinr_api()?

Powered by Dreditor.

jacine’s picture

I just tried pasting the same code into skinr itself, and it loaded, so there's definitely a problem with loading from modules at least.

jacine’s picture

Status: Needs review » Needs work
sun’s picture

So, I've discussed this whole hook_foo_api() approach a bit in IRC.

Direct problem space being: For Skinr, we should fundamentally differ between:

  1. Modules integrating with Skinr
  2. Extensions providing skins

The current code basically applies the hook_skinr_api() concept to both, but for 1), it may not be necessary, because if we're going to do major changes to Skinr's module integration API, then this will likely mean Skinr 3.x.

So for module integration hooks, we may use the regular module system. To advance on that a bit, Drupal 7 provides two new mechanisms to deal with this kind of situation:

  • hook_hook_info() allows to register so called "hook groups", which in turn allow (other) modules to place their Skinr integration code into $module.skinr.inc by registering the appropriate hooks.
  • If there's a hard dependency (i.e. required dependency) on Skinr, then modules are able to specify a specific, compatible version for it in their .info files:
    dependencies[] = skinr (=2.x)
    

The other issue is hook_skinr_api() for skins. For that, you need to know that Drupal 7 core uses advanced mechanisms to play nicely with module hooks:

  • Hook implementations of other modules are individually prepared and cached.
  • As mentioned above, hook implementations may be "lazy-loaded" on demand if a group has been specified.

However, the existing hook_foo_api() concepts are effectively destroying all of those built-in core facilities, because we invoke a single hook (which may involve lazy-loading already), and only later on we check the returned 'api' version in order to decide whether we actually want or need it, and since by that time, the code has been potentially loaded, we cannot use well-known/standard module system functions anymore, as their primary condition for invoking something is whether a function does exist or not. That's stupid.

What we really want is to leverage the built-in facilities in D7 core. Therefore, the basic idea would be to

  1. move the API version from the returned 'api' array property into the hook name itself.
  2. i.e., hook_skinr_api_2()
  3. this allows us to easily (lazy-)load all compatible implementations in a simple way:
    foreach (module_implements('skinr_api_2') as $module) {
      $module_skins = module_invoke($module, 'skinr_skin_info');
    }
    
  4. hence, if skinr_api_2 would be registered via hook_hook_info(), then module_implements('skinr_api_2') would automatically lazy-load the right + proper module include files containing compatible code for the requested Skinr version 2.
  5. it further allows code to be compatible with multiple Skinr API versions.

Please note that these are very fundamental considerations that have been triggered by reviewing the patch. It's possible that we want to use hook_skinr_api_2() to identify compatible module integration code, but also to retrieve filesystem paths to scan for skins.

However, given that IRC discussion, I'm pretty sure that a hook_skinr_api() (without version number) doesn't make sense anymore in times of D7.

moonray’s picture

#21: Your grids module had an error... you used a comma instead of the arrow. It should have been:

<?php
function grids_skinr_api() {
  return array('api' => 2.0);
}
?>

#24
Going to write an updated patch using these concepts.
EDIT: After re-reading your comments, it seems that you're suggesting we abandon the plugin system and move to a single implementation of hook_skinr_skin_info(). I thought we had discussed this and agreed that is not an option. Can you please verify that is what you're saying?

coltrane’s picture

sun's example:

foreach (module_implements('skinr_api_2') as $module) {
  $module_skins = module_invoke($module, 'skinr_skin_info');
}

does imply non-plugin implementations of skin definitions, and seems like a regression.

hook_skinr_api_2() seems great for satisfying fundamental #1, integration with Skinr.

To allow for plugins skinr_load_includes() could check for an (optional) include directory defined in the skinr_api_2 hook and otherwise load $module.skinr.inc and look for hook_plugin_skinr_info().

coltrane’s picture

Actually as I look into hook_hook_info() more I understand that module_implements() would load the include file, where skins would be defined, and we wouldn't need to load it ourselves. We would, however, need to come up with another way of providing plugins.

moonray’s picture

In further response to #24:

1) Using hook_hook_info() means loading *.skinr.inc files through module_load_include() which limits loading a file for a module in its own dir. This means that all the plugin files that are written for core modules (blocks, node, etc.) and panels and views can not be loaded using this mechanism. Considering that there are very few (if any) modules that have written functionality modules for skinr, this won't be giving us any improvement.

So, I'm kind of stuck now. How do we improve this patch (if at all) without regressing to not using plugins?

moonray’s picture

Status: Needs work » Needs review
StatusFileSize
new8.97 KB
new8.89 KB

Since things aren't moving, and we do need to be able to load skins to be able to test, I've amended the above patch to include loading for themes.

The patch includes code to use hook_skinr_api_2() for module loading. Instead of using hook_skinr_api() and hook_skinr_include_directory($type), the two have been merged into hook_skinr_api_2($type) which will return the dir name (just as hook_skinr_include_directory() did previously).

For themes, instead of using hook_skinr_api_2() it will use the following line in the .info file (the same way that ctools does this). The reason to not put it in the template.php file is that it only gets loaded at theme level.
EDIT: Example of waht to include in .info file:

; Skirn plugins. You can place multiple skins under the "skins" folder.
skinr[api] = 2.0
skinr[skins] = skins

EDIT: The top patch has no API check for themes. The bottom patch includes an API check for themes.

jacine’s picture

Status: Needs review » Needs work

I've been running the patch for since #20 for about a week and all appears to be working well. I've also tested the top patch in #29 and it's working fine for themes. Nice job! :D

Regarding the API stuff, I'm fine with hook_skinr_api_2(), but I'd much prefer the top patch. Making themes include this information in the .info file doesn't feel right at all, and I'd like to avoid it if we can.

Here's some minor issues with the patch (mostly comments) that need work:

+++ skinr.module
@@ -421,38 +422,105 @@ function skinr_rule_visible($rid) {
+ * Load include files for skinr implemented by all modules.

Skinr should be capitalized.

+++ skinr.module
@@ -421,38 +422,105 @@ function skinr_rule_visible($rid) {
+ *   The type of a plugin; can be 'skins' or 'modules'.

Since this is such a point of confusion for people, we should briefly describe what each one is.

+++ skinr.module
@@ -421,38 +422,105 @@ function skinr_rule_visible($rid) {
+ * Return a list of directories by modules implementing hook_skinr_include_directory().

This is over 80 chars.

+++ skinr.module
@@ -421,38 +422,105 @@ function skinr_rule_visible($rid) {
+ *   An array containing module names suffixed with '_' and their defined
+ *   directory.

The return includes themes and modules, so it needs to be updated. Also, is this "_" suffix happening? I don't see it, but maybe I am missing something.

+++ skinr.module
@@ -421,38 +422,105 @@ function skinr_rule_visible($rid) {
+  // Load directories for modules.
+  foreach (module_implements('skinr_api_2') as $module) {
+    // Load the path where these plugins can be found.
+    $result = module_invoke($module, 'skinr_api_2', $plugintype);
+    if (isset($result) && is_string($result)) {
+      $directories['module'][$module] = drupal_get_path('module', $module) . '/' . $result;
+    }
+  }
+
+  // Load directories for themes.
+  foreach (list_themes() as $theme) {
+    if (!empty($theme->status) && !empty($theme->info['skinr'][$plugintype])) {
+      // Load the path where these plugins can be found.
+      $result = $theme->info['skinr'][$plugintype];
+      if (isset($result) && is_string($result)) {
+        $directories['theme'][$theme->name] = drupal_get_path('theme', $theme->name) . '/' . $result;

This "load" business isn't really accurate, right? It's just "looking" for module implementations of Skinr, so it can store the paths which will be used to load the files in skinr_load_includes(). Right?

+++ skinr.module
@@ -881,19 +931,21 @@ function skinr_get_skin_info() {
+          $skin_infos = array_merge_recursive($skin_infos, $result);

Please change $skin_infos to $skin_info.

+++ skinr.module
@@ -919,22 +971,24 @@ function skinr_get_group_info() {
+          $group_infos = array_merge_recursive($group_infos, $result);

And $group_info here.

Powered by Dreditor.

moonray’s picture

This is over 80 chars.

Actually, the first line in phpdocs is a description of the function, and should not be truncated at 80 characters (its' the exception as per drupal docs on the matter).

Please change $skin_infos to $skin_info.

Actually I believe it should be $skin_infos (plural). One $skin_info comes from a single plugin. This is a list of infos from all plugins. Feel free to correct me if you have a better argument against using the plural form. :)

I'm waiting to hear back on the last point before submitting the patch.

jacine’s picture

I really, really don't like $infos (anywhere), as I've mentioned previously in other issues as well. While it may be grammatically "correct," it is extremely uncommon to use the plural and because of that it looks downright illiterate. I've never been asked to provide my "Billing Informations" during checkout. Information and info can be both singular and plural interchangeably. There are a gazillion hooks in Drupal core with info in their names, and none of them refer to any of the contents as infos. None of them. In fact there isn't even one occurrence of "infos" anywhere in Drupal at all.

And from your description, and the description of what the function is doing, it seems like it could easily just be called $skins and $groups, with info dropped all together.

coltrane’s picture

On further thought I'm not sure the benefit of hook_skinr_api_2() when compared to having to support another integration style for themes is worth it. I think an installation running Skinr is unlikely to have a significant amount of old skins laying around such that there would be a performance problem around calling old skinr_api functions just to check API compatibility. Unless there's a core solution that also handles themes?

@moonray, does your patch in #20 account for themes?

jacine’s picture

@coltrane The patch in #29 works with themes, but #20 does not.

jurriaanroelofs’s picture

looks like you're making good progress, Im soon going to be able to do test 10 themes that I'm updating simultaneously so I'll get some feedback in this queue. Thanks for all the effort so far.

coltrane’s picture

Here's the approach of #20 (using hook_skinr_api()) with the skins directory being an option of the api implementation.

As I was working on this I wondered why $filter is an argument of

 function skinr_load_includes($type = 'skins', $filter = NULL) 

Are there cases where Skinr include files will not be of the format *.skinr.inc?

coltrane’s picture

StatusFileSize
new8.3 KB

Here's that patch for #36.

coltrane’s picture

Regarding #36, I was not understanding the distinction between module plugins and skin plugins, and that module plugins have a file include format of *.skinr.inc and skin plugins of *.inc. I've reopened #956932: Determine optimal skin include file format but after looking more at skinr.module it may just be necessary to refer to them both as plugins but prefaced with either "module" or "skin".

coltrane’s picture

Bah, I don't know how we'd have one method in Skinr that could return skin plugins from a module *and* a theme's template.php. We could write our own active theme template.php parsing, something like theme_implements(). Or, move all integration with Skinr into *.skinr.inc files and use drupal_system_listing() to retrieve a list of files and parse.

coltrane’s picture

Title: Write load and parse code for skin .inc files in PHP format » Write load and parse code for Skinr include files in PHP format
Status: Needs work » Needs review
StatusFileSize
new12.21 KB

Previous patches in this issue loaded Skinr include files using skinr_load_includes(). Skinr includes could be of two types:

  1. module plugins, which provide module-specific functionality integration with Skinr
  2. skins

These were defined differently, *.skinr.inc and skins/*.inc, respectively, but were loaded in the same function (skinr_load_includes()) and was quite confusing. [Part of the confusion came from the limited differentiation in the code between a module plugin and a skin plugin (further distinction is needed).]

There were recommendations on taking advantage (and following cues) from core by using include methods and lazy-loading plugins. However, there is desire to continue to allow skin plugins in themes, which is not possible using core's standard include file method module_implements().

So, I've tested and attached a method of writing Skinr plugins using specific file formats. This copies the existing *.skinr.inc formats for module plugins and applies it for skin plugin use.

The benefit of this method for all Skinr plugins is that

  1. Skins can exist in modules and themes, and likely elsewhere in the filesystem (sites/all/skins)
  2. Skinr doesn't have to figure out what type of include file it is (module or skin)

This method may add more processing overhead, I haven't benchmarked it.

To test this with your skins, define a plugin.skinr.inc file in a module or theme with the following hooks, swapping "plugin" with the name of your skin plugin:

function plugin_skinr_api() {
  return array('api' => '2.0');
}

function plugin_test_skinr_skin_info() {
  // current skin_info format array
}

plugin.skinr.inc could of course be under a skins directory in your module or theme.

This needs much testing, as I've only gotten stuff to load, not yet apply. And I'm considering this method to be in the RFC stage, so please comment.

Status: Needs review » Needs work

The last submitted patch, 956994-skinr-include-files-40.patch, failed testing.

coltrane’s picture

Yeah, patch in #40 does $name . '_skinr_skin_info' where name is the plugin name from [plugin].skinr.inc of the filename. So it needs some more thought to allow it to work for plugin entity groupings ... (there are far too many names for things in Skinr :P).

coltrane’s picture

StatusFileSize
new13.96 KB

Handling no skins defined so the tests pass in this patch.

Instead of including a plugin grouping in the function definition of an implementation of _skinr_skin_info() could skin authors not define a new skin file?

So before you'd have a skins directory and a .inc file containing two _skinr_skin_info() functions:

  • zen_grids_skinr_skin_info()
  • zen_styles_skinr_skin_info()

What I propose, along with this patch, is that you create

  • grids.skinr.inc with function grids_skinr_skin_info()
  • styles.skinr.inc with function styles_skinr_skin_info()

Or, we could consider allowing hook_skinr_api() to define plugin entity groups or something.

What works good for you?

coltrane’s picture

Status: Needs work » Needs review
coltrane’s picture

StatusFileSize
new12.85 KB

I spoke with sun at good length about progressing with module and theme Skinr plugins using a hook-like method, as opposed to the standalone Skinr include files in #43.

The attached patch follows his recommendations of using hook_hook_info() and builds on what moonray wrote in #29. skinr_get_directories() does API and *.skinr.inc checking instead of theme integration in .info files.

I also altered the plugins array (built in skinr_get_directories() and skinr_load_includes()) because the level of separation of "source type" (module or theme) seemed unnecessary to me and complicated the code far too much.

However, this patch is not exactly tight either. Because I wanted to support plugins under sub-directories like 'modules' (within a module - such as Skinr itself) and 'skins', the plugins array in skinr_get_plugins() (what was previously skinr_load_includes()) has a level for 'name', the prefix on *.skinr.inc files in a sub-directory of a Skinr skin or module plugin.

That seems a bit confusing, but I believe it allows for a scenario like the following:

A theme, mytheme, can define, at its root level, a mytheme.skinr.inc file, which implements hook_skinr_api_2():

function mytheme_skinr_api_2() {
  return array('directory' => 'skins');
}

And the sub-directory skins of the theme mytheme can contain multiple *.skinr.inc files, each implementing hook_skinr_skin_info():

  • grids.skinr.inc with function grids_skinr_skin_info()
  • styles.skinr.inc with function styles_skinr_skin_info()

You'll see that this removes the "entity grouping" pattern in the _skinr_skin_info() definitions.

I have this working for skin plugins in a module and a theme but I haven't exhaustively tested this of course. I'm unsure of the ramifications of altering the plugin array keys as it pertains to skinr_skin_info_process(), though I did update a few.

How do you test this?

1. alter your skin plugins to be a file called myplugin.skinr.inc
2. place it in the root of a module or theme
3. implement hook_skinr_api_2 in this file
3. this file can also implement hook_skinr_skin_info and define your skin array
- or, you can create a new *.skinr.inc file with this hook under a 'skins' directory of your module or theme
- but, you'll need to specify the 'skins' directory in your API hook of myplugin.skinr.inc

Status: Needs review » Needs work

The last submitted patch, 956994-skinr-include-files-45.patch, failed testing.

coltrane’s picture

Status: Needs work » Needs review
StatusFileSize
new13.01 KB

You think I'd have learned the first time. "Handling no skins defined so the tests pass in this patch."

Status: Needs review » Needs work

The last submitted patch, 956994-skinr-include-files-47.patch, failed testing.

jacine’s picture

Status: Needs work » Needs review

I just attempted to test this, and I'm not able to get it working. I have both module and theme skins running on a site I'm working on and can't get either to work. Here's what I did:

Theme = Sky

  1. Created a file called sky.skinr.inc
  2. in the root of my theme.

  3. Added the following code to it:
    function sky_skinr_api_2() {
      return array('directory' => 'skins');
    }
  4. Renamed my skin file from default.inc to sky/skins/default.skinr.inc.
  5. After that didn't work, I also tried putting all the code in sky.skinr.inc

Module = Grids

  1. Tried changing the name to grids/skins/ninesixty.skinr.inc and adding the hook_skinr_api_2() to grids.module
  2. Tried changing the name to grids/grids.skinr.inc and adding the hook_skinr_api_2() to that same file. This caused me to keep an empty .module file just to keep the module enabled. Hopefully this isn't something we are shooting for here.

None of it worked. Any ideas what I'm doing wrong?

Big note for anyone testing: Make sure you back up before testing these patches.

moonray’s picture

A few things about the patch in #47, aside from the question whether this is the right approach or not:

+function skinr_get_plugins() {
+  // Store hooks statically because the files can't be reincluded, so there's
+  // no use in being able to clear it.
+  $plugins = &drupal_static(__FUNCTION__);

The whole point of that comment was to point out that using drupal_static was pointless here. Use static.

As for the skinr_hook_info() function... I believe you're using it wrong. The point of that function is to load *.skinr.inc whenever the function hook_skinr_api_2() is called. However, that is the function that defines the include path, and thus should be put in the module's main file... also, this only lazy loads *.skinr.inc files in the root of the module, leaving out lazy loading for most of the plugins which will be in subfolders. Also, I believe it will only load MODULENAME.skinr.inc which is again less than ideal.

Just to recap: what was your objection to the patch in #29? It doesn't seem you've added much that improves functionality since then.

coltrane’s picture

Status: Needs review » Needs work

Thanks for the feedback moonray!

The whole point of that comment was to point out that using drupal_static was pointless here. Use static.

I didn't realize that, maybe it could be said in the comment more explicitly? I'll revert it back to using static if I write the next patch.

As for the skinr_hook_info() function... I believe you're using it wrong. The point of that function is to load *.skinr.inc whenever the function hook_skinr_api_2() is called. However, that is the function that defines the include path, and thus should be put in the module's main file... also, this only lazy loads *.skinr.inc files in the root of the module, leaving out lazy loading for most of the plugins which will be in subfolders.

You're right, though I think it is being used correctly in the patch. I didn't, however, explain what is happening and why. So allow me to try and provide that here.

Yes, by using hook_hook_info() a module, and module only, must place it's implementation in its .module file or in a *.skinr.inc in the root of the module's directory. That was understood and intended to be taken advantage of by defining skinr_hook_info().

However, I've seen that there is a desire to allow for filesystem grouping of skin definitions into sub-directories, like '/skins'. In an effort to continue to allow for that, and to avoid different handling for Skinr include file formats, the patch in #47 moves any Skinr integration plugin (whether it's a module or skin plugin) into *.skinr.inc files (though Skinr integration hooks can be implemented in a .module file as well).

However, that is the function that defines the include path, and thus should be put in the module's main file

The 'directory' key of implementations of hook_skinr_api_2() is not required and is only used when the plugin author wishes to place plugin files in a sub-directory. If that is not so then the skinr_skin_info() hooks should be implemented inside the *.skinr.inc file.

Also, I believe it will only load MODULENAME.skinr.inc which is again less than ideal.

hook_hook_info() does not load *.skinr.inc from themes, that's correct. A difference between #47 and #29 is that #47 alters your theme->info to allow for *.skinr.inc file implementations in themes. So the Skinr skin definitions and integration need not differ if the skin is being integrated from a module or from a theme. Does that make sense?

Just to recap: what was your objection to the patch in #29? It doesn't seem you've added much that improves functionality since then.

I have little objection to the patch in #29 and started with it for #45. I believe the only thing I've actually added is implementing hook_hook_info(). Otherwise the difference is:

  • allowing themes and modules to implement Skinr plugins in the exact same way
  • loading module and skin plugins the same way
  • making an effort, in my opinion, to make the code more concise and explanatory

#47 needs work for reasons that I understood from Jacine after she tested it out. The patch needs to handle non unique plugin names such that, for example, themes Zen and Fusion can each provide a skin plugin called 'grids'.

stepping back

On a side, I want to step back and talk about this issue and the Skinr code in general.

I'm not entirely happy with the approach using hook_hook_info(). I think it's a clever way to do lazy-loading, it follows cues and practices from core, and it allows for .module file separation which is a big goal for Skinr. Those are the good points. (I don't think we need lazy-loading.) The bad points happen when hook_hook_info() meets the other goals of Skinr, particularly with Skinr wanting to provide theme-level skin definitions.

To document, here's what I understand to be some of the main goals for Skinr plugins:

  • provide module plugins which are functional handlers
  • supporting module plugins in a sub-directory (like Skinr does with '/modules')
  • provide skin plugins
  • supporting skin plugins in a sub-directory (/skins)
  • allow multiple (non-unique) skin definitions for a single skin plugin
  • allowing skin plugins to come from a module or a theme
  • optionally allow standalone skin plugins
  • optionally allow skin inheritance

The other method than hook_hook_info() could be called the "atomic include file" method, which is in #40 and #43. That method is a standalone plugin include file that IMO bastardizes the hook paradigm, but works and satisfies most of the goals without introducing a ton of code. I'm also not happy with the approach :).

sun makes the strong argument, from a best-practices theory approach, that plugins should ultimately be hooks (if I am correctly summarizing my IRC conversation with him). *.skinr.inc files are an abstraction on .module hooks achieved using hook_hook_info() and Skinr plugins in themes should also be hooks, thus requiring us to invoke them. On its own, and in theory, that seems great, but it's once we try to achieve that and meet the goals of Skinr that it gets hairy and ugly and difficult. And I'm done writing for now, sorry for the book of a comment :).

ericduran’s picture

This is not an easy issue to read through. As a matter of fact is a very hard one :-). I currently have no opinion, but I seem to like coltrane approach because I like the themes and modules declaring their includes files in the same manner. I didn't test either patches lol so right now I'm pretty useless. I need sleep now, hopefully I'll be more helpful another day.

moonray’s picture

Really, the question we need to answer before writing more patches is: Do we need an API version check for skin plugins? If not, we only need to include a hook_skinr_include_directory() for the skin modules (see patch in #20). The reason I decided to use skinr[skins] = skins in the .info file was because this is something familiar for all that use ctools plugins (for panels layouts, for instance). The .info file is cached and thus has very little overhead.

If we decide we don't need API checks for skin plugins (which I don't feel we do), we can then look at handling the module plugins separately (which would require API checks as soon as we start overhauling them).

Jeff Burnz’s picture

#47 vs #29 which one is faster/less overhead? Differences in how module or theme implement a skin/plugin does not seem like a deal breaker - nice if they are the same but certainly not critical.

#29 is working for me nicely. Building a skin in my theme was very strait forward and using the info file to declare the directory (I assume this is what skinr[skins] = skins is doing) seems very intuitive.

coltrane’s picture

I don't think we need to have an API version check, no. hook_skinr_include_directory() is fine if we don't have an API hook.

Using .info files does mean less overhead but it means Skinr plugin authors have to use a different method to get their skins found. Could we use hook_skinr_include_directory() for themes in template.php?

jacine’s picture

We can't use hook_skinr_include_directory() in themes, as far as I know. I'm pretty sure we tried this before (in #20) and these types of hooks wont fire in the theme layer.

I completely agree with Jeff Burnz in #29.

It seems to me that we are jumping through hoops to try and treat the module and skin plugins the same, when they are just not the same at all. The only thing they actually have in common is that their files need to be loaded. It's quite frustrating that we can't seem to find a direction and just get it done.

moonray’s picture

In response to #54:

#47 vs #29 which one is faster/less overhead? Differences in how module or theme implement a skin/plugin does not seem like a deal breaker - nice if they are the same but certainly not critical.

#29 definitely has less overhead than #47. Without any tests I couldn't comment on how much impact the difference has on performance, though. Personally I prefer the #29 patch over trying to make theme and module implementations identical. Themes and modules have always been different and always will be. The difference in implementing is 1 line, and there are precedents in ctools to warrant this approach. Let's keep it simple.

In response to #55:
Let's forego the api version check for skin plugins. We're moving from .info based skins to php .inc based ones; there's no way to get confused about which implementation is being used.
As Jacine mentions in #56 template.php only gets loaded at the theme layer, which in most cases is too late to load up the skin plugins, so using hook_skinr_include_directory() there is not an option. Hence my approach (and ctools') if using the .info file.

In conclusion I propose we use the non-api implementing patch in #29. We might need to adjust it slightly to make sure module plugins do load using a api version check, though.

EDIT: However, the api check (and update to 2.0 from 1) for module plugins is not required until we actually change the way module plugins are written and processed. This is a behind-the-scenes tweak, though, that should not hold up a release of Skinr; as far as I know there are no custom module plugins yet, so this upgrade should not impact anyone.

coltrane’s picture

We can't use hook_skinr_include_directory() in themes, as far as I know. I'm pretty sure we tried this before (in #20) and these types of hooks wont fire in the theme layer.

I know we can't use it like module-level hooks, but is even the approach in #47 too late? #47 is basically a theme_implements().

In #30 Jacine said "Making themes include this information in the .info file doesn't feel right at all, and I'd like to avoid it if we can."

Is it worth whatever performance cost there is for us to search out implementations of a "hook" in a theme to satisfy that?

moonray’s picture

I believe Jacine was referring to including the API info in the .info file. Not to using the .info file for exposing the directory where skins are located. And since we agree that having API version checks on skin plugins is pointless, that removes this argument. ;-)

The check for $theme->name . '_skinr_api_2'; in #47 happens too late. In general template.php won't be loaded yet when this code fires, and the function won't exist yet.

moonray’s picture

Status: Needs work » Needs review
StatusFileSize
new10.01 KB

I've written a patch based on #29 and taking elements from #47.

  • Changed back to using hook_skinr_include_directory().
  • No need to change from hook_skinr_api() to hook_skinr_api_2() for module plugins until we actually have an updated API for module plugins.
  • Renamed skinr_load_includes() to skinr_load_plugins().
  • Added a per module API version check to skinr_load_plugins() (previously skinr_load_includes()) for only module plugins.

Please review said patch and provide your feedback.

coltrane’s picture

Status: Needs review » Needs work

General

I still don't like the wed between hooks and file names used for hook_plugin_skinr_skin_info(). It's complexity that puts Skinr further outside standard practices and it requires plugin authors to be sure their filename matches the skinr_skin_info function definition. One more thing to document. I'm open to discussing alternatives if this can be put on the dissection table.

The patch in #60 works.

I'm ok with #60 (after the particulars, below) but I think there are still some fundamental conflicts between the goals and the ideals. I believe sun would argue strongly for the ideals. I'm not him though and don't share that opinion to the depth he does, so I won't hold up #60.

Particulars

+++ skinr.module
@@ -421,38 +422,123 @@ function skinr_rule_visible($rid) {
+    // this function indicates API version 1. API version 2 would require
+    // hook_skinr_api_2() to be implemented.

If there's nothing else in the module about hook_skinr_api_2 I suggest removing the last sentence since it could add confusion. "Wait, what is that hook?"

+++ skinr.module
@@ -421,38 +422,123 @@ function skinr_rule_visible($rid) {
+        $file_list[$source] = drupal_system_listing("/{$filter}.inc\$/", $path, 'name', 0);

I think this needs the period in '.inc' escaped, "/{$filter}\.inc\$/" and technically $filter would need it's period escaped as well.

+++ skinr.module
@@ -421,38 +422,123 @@ function skinr_rule_visible($rid) {
+ * Return a list of directories by modules and themes implementing hook_skinr_api_2().

"implementing hook_skinr_api_2()" -> hook_skinr_api()

+++ skinr.module
@@ -421,38 +422,123 @@ function skinr_rule_visible($rid) {
+ *   An array containing a 'module' and/or 'theme' key. Eah of those is

"Each"

+++ skinr.module
@@ -421,38 +422,123 @@ function skinr_rule_visible($rid) {
+ * @see skinr_load_plugins()
+ * @see hook_skinr_api_2()

"hook_skinr_api"

I also think all the "infos" variables should become "info".

jacine’s picture

I know we can't use it like module-level hooks, but is even the approach in #47 too late? #47 is basically a theme_implements().

I'm not against something like this. I just don't fully understand the implications of it. If this is something that will work for us, then I'd like to hear from both @moonray and @sun on whether or not that will work out. It seems to me though, that it probably wouldn't work out because a theme would need to be enabled for the hook to be found? What about the use case where a subtheme needs to inherit from a base theme that isn't enabled? I know this seems like an edge case, but it's something that happens all the time and we need to make sure we're able to deal with it. Maybe there wouldn't be a problem here. I really don't know, but it is one of the things that concerns me.

In #30 Jacine said "Making themes include this information in the .info file doesn't feel right at all, and I'd like to avoid it if we can."

Yeah, I was referring to this line specifically:
skinr[api] = 2.0

Is it worth whatever performance cost there is for us to search out implementations of a "hook" in a theme to satisfy that?

I don't know the answer to this. Of course, I'd like Skinr to perform the best it possibly can, but I don't know what the performance implications are. All I can really go on are the comments that are going back and forth in this thread.

I still don't like the wed between hooks and file names used for hook_plugin_skinr_skin_info(). It's complexity that puts Skinr further outside standard practices and it requires plugin authors to be sure their filename matches the skinr_skin_info function definition. One more thing to document. I'm open to discussing alternatives if this can be put on the dissection table.

Can you elaborate on this? I'm sorry if I seem like a moron, but I really would like to make sure I understand your concerns. I don't see what the big deal is with naming the include file to match the function name. I guess I'm just used to doing that though. :)

coltrane’s picture

The approach in #47 for themes wasn't technically a hook being called, it was a plain function call. It searched through enabled themes and looked if THEME_skinr_api_2 was defined and if so invoked it. I hear that at the time the code executed template.php of the enabled theme wouldn't be included, but I'm pretty sure I tested with an enabled theme and it did work. I think it's worth further testing.

To elaborate on what I don't like about hook_plugin_skinr_skin_info(), it's the scheme of using "hook" and "plugin". To my knowledge, nothing else in Drupal does what Skinr is doing, which is looking for files called "plugin".inc in a module or theme, concatenating that module or theme's name with the "plugin" name and calling that a hook.

I understand this is the way it's been done, and legacy has weight here, but I think the goal of multiple skin definition plugins per module or theme can be accomplished another way than this.

To provide an extravagant example, let's say my theme's name is super_theme and I want to provide skin definitions in a plugin file called grid_styles.inc. I have to remember that both those names are used in the skinr_skin_info() definition which will be super_theme_grid_styles_skinr_skin_info(). It's not a big deal though. I'm happy to discuss other methods if the point is deemed sound and worth it.

moonray’s picture

For examples of where hook and plugin are being used look at: Ctools (and panels) and WYSIWYG modules.
EDIT: the loading code I used is also based on these modules, as per sun's suggestion. So, yes, there is precedent.

As for template.php... it works for the end-result where skins are already applied, but not for the part where you still need to set them in admin. Also, I don't believe it'll load in time for admin/appearance/skinr/skins to display the results. (some testing might prove me wrong, though).

I've discussed just using plugin name as hook with Jacine, and we concluded that it's technically possible, but would most likely be frowned upon by many drupalers due to the lack of module namespace.

sun’s picture

Assigned: Unassigned » sun
sun’s picture

Status: Needs work » Needs review
StatusFileSize
new29.65 KB

Headache. But should work. But of course, entirely untested. And I forgot to revert/remove the other API docs patch.

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.66.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new29.66 KB

Sorry, sloppy me.

jacine’s picture

Ok, I'm gonna test this now. Can we please take the docs out of this patch though? I want to commit that ASAP, and don't want it counting on this.

jacine’s picture

Er, or not. I'm not sure how I am supposed to test this.

I am trying with a module. I did:

function grids_skinr_api_2() {
  return array(
    'directory' => 'skins',
  );
}

and grids_skinr_skin_ninesixty_info(). I tried naming the file ninesixty.inc and ninesixty.skinr.inc and I'm not getting anything. :(

EDIT: I don't even get the blank Skinr settings form anymore with this patch applied.

jacine’s picture

StatusFileSize
new16.37 KB

I committed the API docs, so here is #68 without them. I'm still not able to get it to work, but I assume that's because I must be doing something wrong, so I'll leave it on needs review for now.

sun’s picture

Thanks for trying to test. Speaking of, this is not something you should even try to do manually. Writing unit tests now.

sun’s picture

StatusFileSize
new25.46 KB

Now with full-blown tests. Hooray! :)

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.73.patch, failed testing.

sun’s picture

Status: Needs work » Needs review

#73: skinr-HEAD.include.73.patch queued for re-testing.

sun’s picture

Hm. Tests are passing for me locally. No idea what's wrong with the testbot.

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.73.patch, failed testing.

jacine’s picture

The tests pass locally, but nothing works. There isn't even a skinr setting form for blocks anymore, which should be showing regardless of whether or not any skins have loaded.

I don't even have a clear idea of how this is supposed to be working right now, so I'm gonna stay with the patch in #29 until there is a consensus here, and it's explained, because honestly I am frustrated as hell with this issue and don't have anything of value to contribute anymore. :(

sun’s picture

Status: Needs work » Needs review

Sorry about that.

The latest patch incorporates the best ideas and code from this issue (resp. @moonray's #29, @coltrane's #47, and @moonray's #60) and most importantly, the in-depth discussion since #47, but has been restarted from scratch. In concrete terms:

  • Implements the hook_skinr_api_VERSION() approach, as outlined in #24.

    I spent some time with thinking through the API compatibility topic in the past weeks. The most important detail for Skinr is that all of the hooks implemented by modules and themes are almost always optional support code - the module/theme still works without Skinr; Skinr merely adds some visual configuration options.

    Especially because we currently have the precedent of entirely revamping Skinr's API right now, we have to take possible future API changes into account. Likewise, already existing Skinr integration code that has been "ported" from Drupal 6 without any changes should no longer be compatible now.

    This means that most of the built-in facilities in Drupal core are mostly useless for Skinr. The only facility that we can leverage is the built-in lazy-loading of $module.skinr.inc files of module_implements() - which only makes sense for third-party modules that optionally integrate with Skinr.

    The _VERSION suffix in the hook name works, but is not set in stone yet and still open for discussion. The positive consequence of it is that simple implementations only need to define the function without any function body or return value. I'm interested to hear arguments (not opinions) for or against the _VERSION suffix.

  • Implements a central skinr_implements() function to determine and collect modules and themes (extensions) that are compatible with the current Skinr API.

    This allows us to handle modules and themes consistently. However, I've double-checked the stated argument in the discussion above that we cannot use template.php for Skinr API hook implementations in themes. That is true for a multitude of reasons, which have been documented in the code. Therefore, themes cannot implement hook_skinr_api() in PHP, and need to put that information into the .info file instead.

  • Implements hook_skinr_skin_info() and hook_skinr_group_info() with optional support for plugin include files.

    I've tried to explain and clarify a couple of times that the most flexible and most simple API design treats the hook implementations in plugin files identically to the regular hook implementations. We do not need to force third-party modules integrating with Skinr to use plugin files. Likewise, if there would be a hypothetical dedicated 960gs module that only consists of skin integration code, it wouldn't necessarily make sense to use separate plugin files. Modules and themes can choose to use separate/atomic plugin files for their skins, but they are not forced to - the regular hooks work, too.

    As documented in .api.php, the plugin files are named skins/example.inc (whereas the 'skins' directory name is customizable).

  • Implements the very first range of actual unit tests for the Skinr API.

    We badly need to get away from stating arbitrary expectations in various issues and testing them manually. Every single expectation needs to live in a test, so ultimately, every single patch against Skinr is automatically tested against all expectations, so we don't break them, unless we intend to.

Hope this helps. Please ask any questions. Let's also try to read the code; I've tried to add explanations where needed.

@todo based on your review/testing:

  1. Fix lazy-loading of $module.skinr.inc with custom 'path' definitions (Skinr's own /modules directory) and test that those built-in files are loaded.
  2. Add a first simple test/expectation for hook_skinr_config_info() to verify that module integration information is returned.
  3. Check what's wrong with the testbot.
jacine’s picture

I did read the code (and the tests), thoroughly. I do appreciate all the extra comments you made, and that you got rid of all the "infos" references. I am especially confused by a few things:

  • Why are we including the hook_skinr_api_x() in skinr_test.skinr.inc and leaving a blank module file? This makes no sense to me. This will happen once per module, right? Is this the only way it's going to work? If I have a module with a couple of plugins, is this hook going to be randomly located one of the .inc files?
  • What does this mean for the file naming conventions? I see that skinr_test.skinr.inc wasn't touched and you've added the example.inc and I don't see anything that refers to an "example" plugin.
  • Since hook_skinr_skin_PLUGIN_info() an hook_skinr_skin_info() how does this affect being able to alter skin implementations? Are we going to need to alter hook_skinr_skin_PLUGIN_info() implementations separate from from the base hook?
  • I don't think the API version makes any sense in skin plugins whatsoever especially in themes, but I could care less about it at this point.

Everything you noted above sounds fine to me.

The one thing that would really help me, so that I could at least write the documentation is how these skins are supposed to be implemented in modules and themes with the latest round of patches. What I tried in #70 didn't work, and I am still not sure if my attempts were correct, even after reading all the docs and your last reply.

This is my understanding:

Modules

  1. Implement hook_skinr_api_VERSION() in .module file. What I don't know is:
    • If the api information is only useful for functionality plugins, what should this look like for my skin if I am using the default "skins" directory?
    • What's the deal with $module.skinr.inc. can/should that be used for skins as well?
  2. Include skins/$plugin_name.inc which implements hook_skinr_skin_info() or hook_skinr_skin_PLUGIN_info()

Themes

  1. Add the following to .info:

    skinr[api] = 2.0
    skinr[skins] = skins
  2. Include skins/$plugin_name.inc which implements hook_skinr_skin_info() or hook_skinr_skin_PLUGIN_info()
sun’s picture

StatusFileSize
new39 KB

Attached patch fixes 1 and 2, including tests. Patch size increased a bit, since I had to move the skinr.skinr.inc file.

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.80.patch, failed testing.

sun’s picture

Status: Needs work » Needs review

Why are we including the hook_skinr_api_x() in skinr_test.skinr.inc and leaving a blank module file? This makes no sense to me. This will happen once per module, right? Is this the only way it's going to work? If I have a module with a couple of plugins, is this hook going to be randomly located one of the .inc files?

We have various different Skinr integration scenarios:

  1. A third-party module (for example, Block) that only optionally integrates with Skinr to expose configurable/"skinable" elements that it outputs.

    Such a module likely wants to put the Skinr integration code into a separate $module.skinr.inc file, which is only loaded when Skinr is actually installed. On sites where Skinr does not exist, this code is dead/unused and never loaded.

    Whether such a module will also expose and implement skins depends on the module author and whether that makes sense in the first place. In case it also implements skins, it likely depends on the amount/number of skins

    • whether the module will directly put hook_skinr_skin_info() into $module.skinr.inc (i.e., no need for a separate/additional include file if there is only one default skin),
    • or whether the module will use separate plugin files for multiple skins, and thus, implement hook_skinr_skin_PLUGIN_info() in each plugin file (instead of or in addition to hook_skinr_skin_info()).
  2. A third-party module that depends on Skinr.

    Such a module has all of the previous options, but may rightfully want to put all of the hook implementations into the primary .module itself, because it requires Skinr module anyway to work and style the module's output.

    Thus, it has all remotely possible options, ranging from putting everything into the .module file, putting everything into $module.skinr.inc, and may also put individual skins into plugin files.

  3. A module that exposes/implements skins (and groups, FWIW).

    Such a module will most likely not implement hook_skinr_config_info() to integrate any output elements with Skinr, but merely register skins and groups. But anyway, it has all of the aforementioned options.

  4. A theme that exposes/implements skins and groups.

    Themes have all options too, but depending on the theme engine, there might not be a template.php file (or any other PHP code by default), so the choices a theme has are mainly limited by the theme itself. But as of now, I think we expect themes to only use skin plugin files anyway.

skinr_test.module exists to verify that code that has been placed in either $module.skinr.inc or skin plugin files (or both), and is correctly found and loaded by Skinr. Only because we want to test that, skinr_test.module itself is empty (for now).

What does this mean for the file naming conventions? I see that skinr_test.skinr.inc wasn't touched and you've added the example.inc and I don't see anything that refers to an "example" plugin.

  • $extension.skinr.inc may contain hook_skinr_skin_info().
  • A skin plugin file, e.g., skins/example.inc, must contain hook_skinr_skin_example_info() in order to be detected. Each existing plugin file is loaded automatically. If hook_skinr_skin_PLUGIN_info() is found after loading, it is invoked and therefore, the skin gets registered.

Since hook_skinr_skin_PLUGIN_info() an hook_skinr_skin_info() how does this affect being able to alter skin implementations? Are we going to need to alter hook_skinr_skin_PLUGIN_info() implementations separate from from the base hook?

Alter hooks are one time operations. Like everywhere else in Drupal, we first collect all information, and then invoke an alter hook to allow extensions to manipulate all of the collected information. If you want, the very rough flow is:

  1. Invoke hook_skinr_skin_info(), collect return values
  2. Optionally invoke any hook_skinr_skin_PLUGIN_info() that exist, add return values to what we have
  3. Lastly, invoke hook_skinr_skin_info_alter() to allow manipulations on everything

Note: This flow/pattern is identical for everything else (and not limited to Skinr).

I don't think the API version makes any sense in skin plugins whatsoever especially in themes, but I could care less about it at this point.

Especially for skin plugin files the API version is highly important, since those may or may not will be distributed through other means (skin repositories?) in the long run. That said, perhaps we need to put API version information into plugin files at some point - but we can deal with that later.

Implementation examples:
(As mentioned above, these are just one of many possibilities)

Module

  1. Implement hook_skinr_api_VERSION() into the .module file:
    function mymodule_skinr_api_2() {
      return array(
        'directory' => 'skins',
      );
    }
    

    Modules do not default to a 'skins' directory, because we cannot know whether some other contributed module might already use that directory for something else.

  2. Implement hook_skinr_skin_PLUGIN_info() in skins/funky.inc:
    function mymodule_skinr_skin_funky_info() {
      $skins['mymodule_funky'] = array(
        ...
      );
      return $skins;
    }
    

Theme

  1. Add the equivalent of hook_skinr_api_VERSION() to the theme's .info file:
    skinr[api] = 2
    

    Themes default to a 'skins' directory, because we want to provide sane defaults.

  2. Implement hook_skinr_skin_PLUGIN_info() in skins/awesome.inc:
    function mytheme_skinr_skin_awesome_info() {
      $skins['mytheme_awesome'] = array(
        ...
      );
      return $skins;
    }
    

Final note: The run-time functionality of Skinr (i.e., not administrative Skinr UI functionality) is most likely still incompatible with the plugin file system. We need to rethink and revise all of the other module code in separate steps.

sun’s picture

StatusFileSize
new28.39 KB

@Jacine committed the test changes, so let's try whether the testbot passes now.

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.84.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new28.42 KB

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.85.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new30.38 KB

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.86.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new30.38 KB

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.88.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
StatusFileSize
new31.07 KB

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.91.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
moonray’s picture

Status: Needs review » Needs work

Looked through the patch's code.
All in all I like the direction it's going. One thing to note, though: by removing skinr.skinr.inc you have removed rules functionality. However, you didn't add that functionality back anywhere. What was your plan for that? Also, why did you remove the file in the first place?

@todo Shoot me. Twice.

lol

sun’s picture

Status: Needs work » Needs review

@moonray: The skinr.skinr.inc file has been copied into the module's root folder. This change and some other test file additions have already been committed, in order to check whether the testbot might have a problem with those file changes. In short: The file still exists, but is located in the root folder now. The patch merely removes the orphan copy from the modules/ folder.

jacine’s picture

#92: skinr-HEAD.include.91.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, skinr-HEAD.include.91.patch, failed testing.

sun’s picture

Status: Needs work » Reviewed & tested by the community

well, ok, let's run tests locally then, before each and every commit.

jacine’s picture

Status: Reviewed & tested by the community » Fixed

I'm very happy to say in comment #100 that this has been committed! Yay :D

Thanks so much @moonray, @sun and @coltrane for all your help with this!

moonray’s picture

Finally! Thanks!

moonray’s picture

Status: Fixed » Needs work

Hrm... one problem: DRUPAL_PHP_FUNCTION_PATTERN isn't defined anywhere resulting in it being included as a string, making no plugins ever show up.
Seems like an easy fix. Just not sure what it's supposed to be?

moonray’s picture

Status: Needs work » Needs review
StatusFileSize
new433 bytes

Never mind. I wasn't at Drupal core 7.0 yet. Updating core fixed this.
There is however a small problem where plugins in sub-direcotries don't load. Attached patch should fix that.

jacine’s picture

@sun didn't recurse on purpose, but I think we need this. Is there a performance concern with this?

sun’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new2.15 KB

This is something we need to discuss. I don't really want to recurse.

If the goal is to have people create their skin plugins in the structure of

skins/example/example.css
skins/example/example.inc

instead of

skins/example/example.css
skins/example.inc

then we should enforce this structure consistently on all skin plugins; i.e., the latter structure would not work.

But first of all, let's remove the additional debug code that has been added to figure out testbot failures since #80.

jacine’s picture

Status: Reviewed & tested by the community » Active

Committed.

The first example is the goal. I don't mind enforcing that consistently.

sun’s picture

Status: Active » Needs review
StatusFileSize
new2.71 KB

This should normally work, but doesn't. Looks like we need to recurse.

sun’s picture

StatusFileSize
new3.79 KB

Here we go.

sun’s picture

And... @Jacine, you did not run the tests before committing #105. Two bogus changes in there, reverted in #108, making tests pass again for me.

jacine’s picture

Sorry. I didn't. It looked innocent enough. I'm testing the other one now.

jacine’s picture

Status: Needs review » Fixed

Ok, tested it manually, ran tests locally, and committed. Thank you @sun! :D

moonray’s picture

Status: Fixed » Needs review
StatusFileSize
new814 bytes

Not quite there yet: the path for plugins isn't set correctly. Because of this the JS and CSS files for these plugins aren't properly loaded.
Patch attached.

sun’s picture

StatusFileSize
new1.38 KB

Always start with tests...

@Jacine: It looks like you forgot to

cvs rm tests/skins/example.inc
cvs add tests/skins/example/example.inc

EDIT: Also looks like the old wasn't removed, according to http://drupal.org/cvs?commit=481328

sun’s picture

StatusFileSize
new3.11 KB

Added actual (debug) data to the test output.

moonray’s picture

StatusFileSize
new1.49 KB

Oops, forgot a part. Updated patch.

sun’s picture

StatusFileSize
new4.82 KB

Merged in the actual fixes.

jacine’s picture

Status: Needs review » Fixed

Committed patch in #116, hopefully correctly this time: http://drupal.org/cvs?commit=482342 Thanks :)

ChrisBryant’s picture

I know this is marked fixed, but I'd still like to report that I tested creating a skin using the current format, @sun's documentation notes in #83, and by having a look at the skinr_test module. I tested with the module approach without using a skins sub-directory and it went pretty smoothly. The module works and the class was applied to the test block I setup. It was pretty easy. :-)

I also tested having the skin include a javascript file and that worked nicely as well. The only thing I couldn't figure out (and this is because I'm not a dev and don't have a clue about php arrays) was the format for adding multiple css or javascript files. Here is what I tried:

    'attached' => array(
      'js' => array(
        'test.file.1.js', 'test.file.2.js'),
    ),
    'attached' => array(
      'js' => array(
        'js' => 'test.file.1.js',
        'js' => 'test.file.2.js'),
    ),

Neither of those approaches worked and I assume it needs to be more like the second approach, but don't know what should go in place of the "js" in the array. Either that or one of those should work and something isn't right with loading multiple files?

Thanks everyone for getting through this issue, nice work!!

sun’s picture

@ChrisBryant: Can you file a separate bug report for that multiple JS file issue, please? Thanks!

Status: Fixed » Closed (fixed)

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