Dev talk:Theme Areas

From Habari Project

(Redirected from Dev talk:Themes/Areas)
Jump to: navigation, search

Ideas (might be crazy):

context is a confusing word, in this.. context, so I'll explain things in terms of "scope". A theme can define 'areas' in the global scope or in more specific scopes. eg. a theme may define a header, footer, and sidebar area within the global scope of the theme. It may also define a sidebar area within the "single posts" scope. ie.

<plugable:theme>
  <name>K2</name>
  <author>K2 Team</author>
  <url>http://getk2.com/</url>
  <version>1.0</version>
  <template_engine>rawphpengine</template_engine>
  <description>A port of the popular design K2 and first Habari "theme"</description>
  <license url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software License 2.0</license>
 
  <area name="header" />
  <area name="footer" />
  <area name="sidebar" />
 
  <scope name="Single Posts">
    <matches>
      <rule name="display_entry" />
      <rule name="display_page" />
      <post:tag name="single" /><!-- just a thought ;) -->
    </matches>
 
    <area name="sidebar" />
  </scope>
 
</plugable:theme>

This means, that a user could place 'blocks' in the global scope areas, which would be displayed always. If an 'area' matches for a more specific scope (like "single posts" sidebar above) then 2 things could happen:

1) the global sidebar 'area' would be merged to "single posts" sidebar area, and both displayed. The "single posts" area above the global, since it has a higher specificity. (matching ALL matches exactly means highest specificity)

2) we allow the user to place 'areas' within 'areas'. So placing the global sidebar area in the "single posts" sidebar area, would make like a "symlink" to the global sidebar area.

Both of these methods would allow something like:

<plugable:theme>
  <name>K2</name>
  <author>K2 Team</author>
  <url>http://getk2.com/</url>
  <version>1.0</version>
  <template_engine>rawphpengine</template_engine>
  <description>A port of the popular design K2 and first Habari "theme"</description>
  <license url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software License 2.0</license>
 
  <area name="header" />
  <area name="footer" />
  <area name="sidebar" />
 
  <scope name="Single Posts">
    <matches>
      <rule name="display_entry" />
      <rule name="display_page" />
    </matches>
 
    <area name="sidebar" />
  </scope>
 
  <scope name="Single Pages">
    <matches>
      <rule name="display_page" />
    </matches>
 
    <area name="sidebar" />
  </scope>
 
  <scope name="Entries By Tag">
    <matches>
      <rule name="display_entries_by_tag" />
    </matches>
 
    <area name="sidebar" />
  </scope>
 
</plugable:theme>

The "context" term is just something I pulled over from a Drupal plugin I've been using. I'm not married to it.

In terms of having "scopes" merge blocks in the same named areas, I was thinking about this and specifically excluded that idea for three reasons:

1) It's difficult for a user to discern what's going on. It seems easier to me to tell a user that they must assign blocks explicitly into each area and context than to explain how one overrides another. I imagine the interface becoming very confusing - when editing blocks in the default scope, you'll see certain values. Then when you edit "different" blocks in another scope, you'll see those same values, because you're really editing the same thing, but in two places. I think this is confusing, and we should avoid it.

2) It's difficult to implement. Each scope would have its own set of block data stored in the database (this part isn't outlined yet, but it's in my head), including a block_order field for rearranging the order of blocks. Because the data would by necessity need to be kept in a block that is shared across scopes, there would have to be no duplicate data for that block. This would make it impossible to reorder blocks between scopes (which would be required if a block is inserted at the front of the list) since that single block would only have one block_order value. If multiple rows of data were used to store "identical" blocks but with different ordering data, then that's really the same as having the user explicitly create separate blocks with identical data, with the exception that the system would then need to handle duplicating the data across block rows that should be the same. This isn't efficient or elegant.

3) It makes it impossible to create a scope for an area with blocks that are not shared. If you wanted to use completely different sidebar content for a specific scope, you could not define any blocks for the default scope, because they would all be shared into this dedicated scope. You could instead define block content individually for every scope, but then you're back to the originally presented idea.

Perhaps a different solution would be to acknowledge that the blocks defined for the default scope will be used in that area for every scope until some block is defined for an alternate scope. If a user defines a block for an area in an alternate scope, only those block defined for that scope's area will be displayed in that condition.

--ringmaster 01:50, 20 April 2009 (UTC)

Arthus's thoughts

Option 2 seems to complicated.

Instead of automatically putting the more specific above the less specific (you might have a global block you always want to have top), the user should be able to rearrange the specific blocks in relation to the global blocks. These "inherited" blocks would be given a different color to differentiate them. Their information would be stored only in the parent context.

That is, global blocks would have multiple arrangements:

  1. The global "scope" arrangement, which applies for all scopes not explicitly defined.
  2. An arrangement within each scope, defined in relation to the scope-specific blocks.

When a new block is added to the global scope, it should automatically be placed at the bottom of all specific slopes. You should also be able to delete a global block from a scope.

We should definitely offer content-dependent filters, including:

  1. Query contains (search pages)
  2. Query is (search pages)
  3. Tag is (tag lists)
  4. Has tag (post pages)
  5. Is in date range (archive pages) — include easy ways to define ranges (all of 2007)
  6. Is in date range (post pages) — include easy ways to define ranges (all of 2007)
  7. Is slug (post pages) – include a selector
<plugable:theme>
  <name>K2</name>
  <author>K2 Team</author>
  <url>http://getk2.com/</url>
  <version>1.0</version>
  <template_engine>rawphpengine</template_engine>
  <description>A port of the popular design K2 and first Habari "theme"</description>
  <license url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software License 2.0</license>
 
  <area name="header" />
  <area name="footer" />
  <area name="sidebar" />
 
  <scope name="Featured Posts">
    <matches>
      <rule name="display_entry" />
      <rule name="display_page" />
      <post:tag name="featured" />
    </matches>
 
    <area name="sidebar" />
  </scope>
 
  <scope name="Environmental Posts">
    <matches>
      <rule name="display_entry" />
      <rule name="display_page" />
      <post:tag name="green" />
    </matches>
 
    <area name="sidebar" />
  </scope>
 
</plugable:theme>

A featured post tagged "green" would match both rules, so would have to be resolved. When this post is first viewed (conflict detected), I propose this should happen:

  • A new (automatic) scope "view" would be created (similar to the views I reference above), which would include the blocks from both scopes (in mixed order).
  • The next time you go to the block management page, you'd see this new scope view and be able to adjust it accordingly

Finally, why is it even dependent upon the theme to define the scopes/contexts? This seems to be more of a "controller" issue than a "view" issue. Instead, the theme should define areas, leaving the core software and/or plugins (including the theme) to define scopes. The core software could then build the scope and its included blocks, then output it to the theme.

<plugable:theme>
  <name>K2</name>
  <author>K2 Team</author>
  <url>http://getk2.com/</url>
  <version>1.0</version>
  <template_engine>rawphpengine</template_engine>
  <description>A port of the popular design K2 and first Habari "theme"</description>
  <license url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software License 2.0</license>
 
  <area name="header" />
  <area name="footer" />
  <area name="sidebar" />
 
</plugable:theme>
public function filter_scopes($scopes) {
	$scopes['green_posts'] = array(
		'name' => _t('Green Posts'),
		'rules' => array(
			'display_entry',
			'display_page'
		),
		'tags' => array(
			'green'
		)
	);
	return $scopes;
}



Describing inheritance to users will be even more complicated than describing it to coders. It's to be avoided if possible.

I'm thinking only one table for blocks with these fields:

block_id
Unique row identifier
block_type
Type of the block
block_order
Order of the block in its area
block_area
The area in which this block is defined
block_scope
The scope for which this block is defined (null for default)
title
Title of the block for display
data
Serialized block content data to pass to the content hook

I do think that naming the scopes explicitly in the theme definition might not be necessary. Users could define the criteria of their own scopes, and then place the blocks they want for that scope into the areas there.

So an additional table for scopes:

scope_id
Unique row identifier
scope_name
Name of the scope defined by the user
scope_criteria
Serialized data that triggers the scope
scope_priority
The matching order of a scope

The tricky field here is the priority, which would let a matching scope supersede any other scopes. As I said in my last comment, merging is too much of a pain. We should simply stop at the first that matches. I don't know whether we should even provide an interface to rearrange scope priorities. Just make your scopes different enough that two scopes don't match the same thing.

The advantage of storing the scope information in the theme is that the theme, as distributed, would be aware of the presence of files that explicitly handle that scope. Although I suppose as a user you could always add those templates to facilitate your use, and the theme could programmatically (via a hook) add more scopes to coincide with the templates it provides.

In terms of how this all works, to kind of roll it together, the theme would determine the applicable scope, then request all of the blocks for that scope and area (assuming some existed, otherwise just the blocks for the default scope for that area). The Block class would unserialize the data into itself before running through the action_block_content_*() hooks. Then the theme would for each block:

  • Start a variable buffer
  • Place the properties of the block instance into the theme variables
  • Call $theme->display() for the right fallback template for that block
  • Release the variable buffer

Actually, I think making the arrangement of inherited blocks overt will make it easier to understand. No need for users to wonder why something isn't showing up, what they see in the backend UI would exactly mirror the public arrangement.

I don't really see the harm in automatically persisting globals to all matching scopes, provided that you make the interface usable.

In terms of the database, I have only 1 small change which would allow this. Instead of storing the block position within the block, you would store the block positions in the scope. (This assumes we have a scope table.) I'd also remove the "priority" flag since you wouldn't need it without an overt priority system.

block_id
Unique row identifier
block_type
Type of the block
title
Title of the block for display
data
Serialized block content data to pass to the content hook
scope_id
Unique row identifier
scope_name
Name of the scope defined by the user
scope_criteria
Serialized data that triggers the scope
blocks
Serialized array of areas to block ids, in order. Ex. array('sidebar' => array(1, 2, 3)).

To determine where blocks are "inherited" versus original, the least specific location of a block is considered the "parent" one. Ex. global > display_entry, display_entry > specific slug

I have mocked up a UI of what this might look like: http://skitch.com/arthus/bcju3/ui-mockup-1

--arthus 18:22, 21 April 2009 (UTC)


Please see my updated proposal: http://wiki.habariproject.org/en/User:arthus/Area

Personal tools