This is certainly not a bug, but it has been bugging me.
Background
Premade schemes are passed as a concatenated comma-separated string of hexadecimal RGB color codes. The hex codes must be ordered respectable to fields. Like this (taken from Nista theme's template.php):
'fields' => array('base', 'link', 'top', 'bottom', 'text', 'navbar', 'navbarlink'),
premade schemes' => array(
'Aquamarine' => '#55c0e2,#000000,#085360,#007e94,#696969,#000000,#666666',
'Ash' => '#464849,#2f416f,#b3b7b8,#262b32,#494949,#000000,#dbdbdb',
'Belgian Chocolate' => '#d5b048,#3e2608,#331900,#971702,#494949,#000000,#666666',
'Blue Lagoon' => '#0072b9,#027ac6,#2385c2,#5ab5ee,#494949,#00436e,#07a0ff',
'Bluemarine' => '#3f3f3f,#336699,#6598cb,#6598cb,#000000,#000000,#666666',
'Citrus Blast' => '#e08800,#914d03,#f99d01,#fbd028,#494949,#f99d01,#4c4c4c',
'Cold Day' => '#0f005c,#434f8c,#4d91ff,#1a1575,#000000,#1a1575,#666666',
'Greenbeam' => '#a8c997,#0c7a00,#03961e,#7be000,#494949,#0c7a00,#b4b4b4',
'Majestic' => '#5c00b9,#5c00b9,#f028ab,#5f009e,#494949,#420087,#666666',
'Mediterrano' => '#ffe23d,#a9290a,#fc6d1d,#a30f1d,#494949,#000000,#666666',
'Mercury' => '#4a5461,#242424,#a9adbc,#d4d4d4,#707070,#d4d4d4,#666666',
'Nocturnal' => '#5b5fa9,#5b5faa,#0a2352,#9fa8d5,#494949,#0a2352,#666666',
'Olivia' => '#7db323,#4b6d0f,#b5d52a,#7db323,#191a19,#000000,#666666',
'Pink Plastic' => '#f41063,#651b54,#f391c6,#f41063,#898080,#000000,#666666',
'Shiny Tomato' => '#ff1a29,#c70000,#a1443a,#f21107,#515d52,#c70000,#9a9a9a',
'SR-71' => '#595959,#9a4c00,#333333,#9e9e9e,#696969,#000000,#666666',
'Teal Top' => '#228059,#0f3726,#34775a,#52bf90,#2d2d2d,#18583d,#8d8d8d',
),
Issue
In this case, managing fields is a hassle. Since there is no weight system and they are displayed as listed in 'fields', the 'fields' container becomes the subject of a lot of change during theme development. Also looking at a single schemes hex code string, it's hard to tell which field each value corresponds to.
Suggestion
I suggest the schemes' codes (and perhaps later also other options) are passed as an associative array. As some might've noticed from my posts, I'm a big fan of arrays and especially assorrays.
I imagine an array structure such as this:
$color['premade schemes']['Scheme name']['fields'] = array (
'field name' => 'field value',
);
// And in the year 1984, maybe we might be passing scheme-specific options
$color['premade schemes']['Scheme name']['options'] = array (
'key' => 'value',
);
Using assorays, each 'field name' could be matched against a declared field and the order could then be arbitrary. This would also allow masking of values, which in practical terms means sub-scheming. Let's say you have defined a reference field, and you only want to over-ride certain fields and let the rest depend on the reference scheme.
But for now, here's a snippet of how I'm handling premade schemes internally:
// Field definitions, this is the order they will be displayed and referenced
$color['fields'] = array(
'link',
'text',
'container-background',
'body-background-top',
'body-background-bottom',
'logo-top',
'logo-bottom',
'logo-border',
'footer-border',
'footer-top',
'footer-bottom',
'footer-text',
'frame-outer',
'frame-inner',
'sidebar-background',
'menu-top',
'menu-bottom',
'menu-text',
'menu-hover',
'menu-hover-text',
'submenu-frame',
'submenu-background',
'submenu-text',
'submenu-hover',
'submenu-hover-text',
);
// Unordered scheme
$_premade_schemes['Baby Blue'] = array (
'link' => '#201e66',
'text' => '#333333',
'logo-top' => '#f0f0f0',
'logo-bottom' => '#fafafa',
'logo-border' => '#98c1d4',
'container-background' => '#fafafa',
'body-background-top' => '#dedede',
'body-background-bottom' => '#b3bdc4',
'frame-outer' => '#777777',
'frame-inner' => '#aaaaaa',
'sidebar-background' => '#f5f5f5',
'menu-top' => '#201e66',
'menu-bottom' => '#26247a',
'menu-text' => '#ffffff',
'menu-hover' => '##fab43f',
'menu-hover-text' => '#000000',
'submenu-frame' => '#98c1d4',
'submenu-background' => '#f5f5f5',
'submenu-text' => '#222222',
'submenu-hover' => '#fab43f',
'submenu-hover-text' => '#000000',
'footer-border' => '#98c1d4',
'footer-top' => '#201e66',
'footer-bottom' => '#1a1852',
'footer-text' => '#f3f3f3',
);
$color['premade schemes'] = array();
/** Start of Color.module conversion **/
/** Concept can be used in color.module to change API but maintain current data scheme **/
// Iterate schemes
foreach($_premade_schemes as $scheme_name => $rgbs) {
// Create temporary array where key will be index of fieldname
$indexed = array();
// Iterate field values of current scheme
foreach($rgbs as $field_name => $rgb) {
// Find index of the fieldname
$index = array_search($field_name, $color['fields']);
// Store in temporary array as (key, value) = (index, rgb)
$indexed[$index] = $rgb;
}
// Sort the temprorary array according to keys (indices)
ksort($indexed);
// Implode with commas
$color['premade schemes'][$scheme_name] = implode(',', $indexed);
}
/** End **/
Which gives valid order for the current API of the Next Generation Color.module:
[premade schemes] => Array
(
[Baby Blue] => #201e66,#333333,#fafafa,#dedede,#b3bdc4,#f0f0f0,#fafafa,#98c1d4,#98c1d4,#201e66,#1a1852,#f3f3f3,#777777,#aaaaaa,#f5f5f5,#201e66,#26247a,#ffffff,##fab43f,#000000,#98c1d4,#f5f5f5,#222222,#fab43f,#000000
)
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | style-scetch1.gif | 274.21 KB | tonyn |
| #5 | style-scetch2.gif | 310.89 KB | tonyn |
| #5 | style-scetch3-new.gif | 127.33 KB | tonyn |
Comments
Comment #1
tonyn commentedI think we should merge this idea with http://drupal.org/node/315805.
How to pull this off
Grouping
I like this 'ksort' thing, because themers can use "KEY-section" ID's to group alphabetically.
header-text
header-link
header-link-hover
header-link-active
Of course, this would be a by product, to be expected, but not necessarily official. Happens by design!
Weight
I don't know how to do the weight thing, so I'll need to look more into it. We may have a function already in Drupal that can sort it. :)
In general, I'll mess with this, if you can provide any more info into sorting this stuff out and processing it (including the weight), please say.
Comment #2
Danial Namousi commented(By weight, I refer to the #weight attribute of field definitions in Forms API.)
How about simply exposing different actions in the Color.module API and letting theme developers create their own user interface and workflow?
Example of exposure:
Get schemes
Create scheme
Modify scheme
Delete scheme
Make default scheme
This way Color.module could provide it's own basic UI for managing schemes and ambitious theme developers could create their own interface and workflow. Then the Color.module developers wouldn't have to accommodate constant requests of extending or modifying the user interface.
I would be the first (and hopefully not the last) to use it.
Example API function:
Now here's an important note. By allowing theme developers to handle their own forms, the tag replacement method which is a powerful tool wouldn't need to be limited to just replacing color codes.
You see, as a theme developer, I want to be able to define a field which is not a color and which won't need to be accessible in the default Color.module scheme management UI but I do want Color.module to do tag replacement on it when it is referred to in the CSS.
I want to use
hook_colorto define the field and then refer to it when interacting with the API - using for instancecolor_scheme_create. How I set the value of that field is up to me! I just want Color.module to do everything else: store its value for different schemes, generate the new CSS, images, and so on.I could then use tag replacement to change any CSS attribute and create my own user interface for doing so. Also it would make sense that these fields be made available for reference in
$color['images']. Say I let the user chose the color and width of a stripe and I use a solid fill to generate the image, I would then need to refer to both the color field and the width field (which I have previously defined in$color['fields'].I don't know if all this makes sense, but if it does, I'd gladly to what I can to help make it work, and once it's done I'll give you a hell of a showcase.
You could use the structure for
$color['fields']that you proposed in your comment to add a parameter to define the type of value it will hold (default: color) and the Color.module UI could then ignore it if it does not accommodate the value type. And we could use my proposal of using assorrays for defining scheme values.(And yes, maybe Color.module should change name to Style.module, but we would need to kill this unborn baby first.) :-)
Comment #3
tonyn commentedAre you looking over my internet logs or something? Or can read minds? Likely you just dig this thing. That's good man.
Whatever we can do to break this into pieces so it's an effective API -- we'll do.
As for what you said about the style thing -- absolutely. It'd be cool if we could link all stylesheet's in Drupal into a style registry, where everything in a stylesheet is turned into an object that can be combined, organized or the like.
I'll be looking into ways to work this out -- I did sketch out some ideas I had about this a few days ago, so I can upload that here when I wake tomorrow...
If we can find a simple way to get this done and give users power to manipulate their styles, let's do it.
?
Tony
Comment #4
Danial Namousi commentedI just dig this thing, Tony. But I did just recently sign up on Github and browse the Color.module repository.
Anyhow, that sounds great! I look forward to your post.
Comment #5
tonyn commentedI had some stuff around, essentially I wanted to create a library (CMS/Framework agnostic) that could abstract CSS into objects.
After that, in drupal's case, CSS would be abstracted, turned into objects, and stored in the DB. Yatta yatta, the point is open for discussion.
Essentially, wouldn't it be cool to create a system that focuses on a very low scope and moves up? If the system worked as planned, it could work on a level low enough to handle any sort of CSS altering. Esp. with a focus on separating view from control from model.
It'd be nice to create a very granular, powerful system that people can do whatever they want. For instance, having a special stylesheet scheme show up on a certain page.
If you're good at mapping stuff out to make sense, it'd be awesome if you could lend a hand on how we can architecture this.
If there's code we can keep agnostic and low-level (so in FOSS spirit some parts can have the portability to be used on Joomla, for instance), then parts which specialize in integrating with Drupal's system (perhaps turning the CSS, now objects, finding their types, and assigning them special CCK fields to edit automagically?)
It sounds like a grandiose ambition. However it's not really as hard as it sounds. We have a lot of the stuff already there, we just need:
- Regex to pull the CSS statements out one by one
- Regex to take the above statements and pull put Attribute-type => Attribute-value
- A library of attributes and potential values (can be a string like 'left','right','center', or a integer/floatingpoint with a unit 0.20em) -- This stuff is already available in W3C
- Geshifilter is a good place to look for starters
Essentially if we get this abstracter off the ground and rolling, we could create a system that automatically finds out the fields in CSS (providing it's valid syntax).
I wanted to think it out more, so if you can provide any input on what would be most effective (it doesn't have to be what I proposed)
Attached are 2 old gif's, and a new sketch screenshotted from evernote. The uppermost note on sketch3 is kinda old.
Comment #6
Danial Namousi commented(I was supposed to put all my mind into a new project that kicks off today but here I am.)
I looked thru the sketches and I think you're quite on target. Also I like the way you want to attack it at a low-level and build on top of that. I dare to agree that the implementation of this module would be quite straightforward - the general solution most often is, but I believe the designing of it will be most challenging.
As an old object-oriented programmer, I feel familiar with your approach here and I appreciate it but what concerns me is how the rest of the developers community will take to OO. Objects aren't exactly abundant in Drupal. (Actually, my first thought when I got familiar with the Drupal framework was "This should've been done in Erlang or any other functional programming language".)
Visiblity API
About your note on (page specific) visibility, I've been in touch with tjholowaychuk (of Vision Media) who's maintaining the Visiblity API and discussed what the next generation of the API could offer developers. As I see it, for page specific visiblity or any other sort of visibility settings this module could rely on the next generation of Visibility API.
Inid.module
When I was outlining the Page Specific Visibilty Settings (PSVS) for Visibility API, I came across the problem of handling different sets of data for different handlers. A handler is the enitity that generated the settings and most likely is also the one to make use of it. In the case of PSVS a handler can be one of a module. A module may also have several handlers. Let's say cores Block.module would want to use Visibility API for displaying blocks on specific pages (doing what it does today). Every region in which a block may reside becomes a separate handler, but they are managed by one module - the Block.module. The handles would then be identified by separate inids.
Now, as I see it, the proposed Style.module would have to do the same for its stylesheets since several modules or schemes may refer to the same CSS selector and attribute.
To solve this, I introduced the concept of Internal ID or inid. Before interacting with the API, a module would have to get an inid from the Inid.module. It's a simple call which returns a unique identifier (per site). The module can request as many inids as it needs. The inid can be used by the module throughout its lifetime to interact with internal API:s such as Visibility API and Style API. The purpose of the inid would be identification, not authentication.
So a module could request an inid and pass that inid along when committing its stylesheets to Style.module. If another module wants to change the settings of the first module it could call hook
modulename_inid($op = 'list')and use it to identify the data of that module.Using inids also gives a very straightforward method for garbage collection. If an inid becomes obsolete, for instance when a module is no longer making use of it, the module would trash its inid objects using a designated function. That function would invoke the
hook_inid($op = 'garbage', $inids)which will tell all hooked up modules that they can do garbage collection on all data related to a certain inid. In the case of Style.module, it could delete the entries corresponding with that inid in its tables.The code for Inid.module is basically complete and documented, it just hasn't been published yet. I can commit it to Github and you could have a look to see how it can be used in this context.
Conclusion
As you could tell, I didn't make any points on the architecture itself or where we could start. I need some more time to think this through but I have to get some work done for a client today and I'll get back
later on this evening (my time).And thank you for sharing! I'm certain something good will come out of this.
Edit:
Something came up and I have to call it a day (at 9PM). I'll try to get back as promised.
Comment #7
Danial Namousi commentedI've been trying to wrap my head around this issue. The following are my conclusions. I will do my best to make myself understood. I am usually not good at explaining my thoughts: Here goes:
Style fields are variables that are set in style schemes. Such a field can be used universally. In CSS, an attribute can be tagged with a field and can then be replaced by its value. One should be able to refer to a style field throughout Drupal, very much like module variables.
The representation of a style field should not contain any value itself. Values are stored in style schemes. A style field should be represented by the most basic data such as name, label, description, type and/or callback functions.
The type would tells us how the field prefers to be treated and if the type can't be handled as specified, you may always fall back on treating it as text because that's essentially what it is. This helps us represent the field with the proper UI widget. The CSS attribute vocabulary should carry us a long way in describing field types. If a theme developer sets a variable for a border which is generated as an image, the type can be described by the CSS term border-width which can be mapped to an adequate widget when the field appears in the UI.
The callback functions and their corresponding parameters are used for custom validation, theming (widgets), etc.
The name is the unique identifier of the field (compare with module variables). Label and description are used for human interaction.
A style scheme is simply a set of style field values. It can be a partial or a complete set. A style scheme is global, which in this context means that there aren't separate schemes per theme or module. When a module or theme introduces new fields, a style scheme can be complemented with these values. So every style scheme is able to style everything.
There should be a built-in default scheme which is used as fallback for partial schemes. This scheme is a full set of values for every field that has been declared. The source of these values can differ, either taken from CSS or set thru the API.
We should considered allowing declaration of fields directly in a stylesheet. Doing this in the stylesheet makes sense since it allows the web designer, who should be considered the authority, to extract all parts of the stylesheet that should become subject to customization. (I've looked into how this could be done but I'll avoid details for now.)
Using universal style schemes, the web master only needs to manage one set of style fields. For theme developers, it offers re-usability of fields. If Zen theme declares some fields, a sub-theme can refer to these field names instead of introducing new ones. The same goes for a module such as Nice Menus. It can be expected that a by-product of this is that theme developers would refer to generic elements by the same field names. One scheme to rule them all.
So this would extract the entire scheme management from Color.module and turn it into a general solution available to all themes and modules. Color.module, amongst others, would rely on this extracted functionality and instead act upon changes in scheme states - triggering regeneration of images, shifting of colors, tag replacement, or simply do garbage collection on deletion, etc.
What the Style API would offer is simply put managing fields and sets of fields holding style-related values. A Style UI would offer a generic user-interface for managing schemes, and a well-designed Style API would make it easy to create other UI:s.
I've left a lot of the details out but I think the covers the essentials. Ideally, design should be delegated to designers, logic to programmers and choice to end-users.
So in conclusion, I humbly disagree with the object-oriented approach and the full extraction of CSS elements and attributes.
Comment #8
tonyn commentedHi,
The technical approach isn't as important as making a good style system. So now is a good time to start planning the technical stuff and how we should organize it. style will likely be the successor to color.module, and we should begin working on it after we get this baby finished. The idea is quite marvelous, and it's going to work.
However, we do have a colorization module to get done still. So maybe we should focus on getting this baby out to themers. :)
By the way, the stuff you written made OK sense, however when you draw it out/visualize it to connect the ideas it may be more effective? We should move style.module talk over here.
Comment #9
Danial Namousi commentedGreat! See you there.
And yes, you are absolutely right about visualization.