Moving fields between fieldsets on Dexterity - plone

In Archetypes, in order to move a field from a fieldset (or schemata) to another, we can do the following:
schema['creators'].schemata = 'default'
However, I'm not achieving the same using Dexterity. I've tried using form hints. Ex:
form.fieldset('default',
fields=['creators']
)
I notice that it doesn't work because the field "creators" is unknown at this time. (The ownership behavior wasn't evaluated yet).
Nevertheless, with form hints, I can move from "default" to another (eg. "ownership").
myfile = NamedFile(title=_(u"A file"))
form.fieldset('ownership', fields=['myfile'])
How can I do that? Writing my own behavior?
Thx!

You likely need to make the define the field you want to assign on an interface under your control. While this seems duplicative, it is a good idea for purposes of being complete and explicit. You can either:
(1) Declare 'creators' field on your content type interface (likely, recommended solution), or...
(2) Use your own behavior as documented here (and adding this behavior to the type's FTI in portal_types and associated setup XML): http://docs.plone.org/external/plone.app.dexterity/docs/behaviors/creating-and-registering-behaviors.html
The first solution should be the easiest. Any fields that you wish to control fieldset location or order of should likely be defined by your interfaces anyway.

Related

Can a plone.behavior be enabled for just some objects of a type?

I would like to set a marker interface to some objects that should have additional fields. If I remove this marker interface again the fields should be removed too.
Now I'm trying to understand plone.behavior. But I'm not sure if a behavior must be enabled for all objects of a type or is it possible to enable it for only a subset of objects of that type?
Take a look at collective.instancebehavior, an add-on aimed to do exactly what you want: to enable behaviors per content type instance.
Unfortunately I don't think there is a solution out of the box.
The simplest thing you can do is working on the form fields by overriding the updateFields method in the form.
This is untested demo code:
def updateFields(self):
if not IMyInterface.providedBy(self.context):
self.fields = (
self.fields.omit('IMyBehaviour.my_field')
)
As a reference have a look to:
https://github.com/plone/plone.app.users/search?utf8=%E2%9C%93&q=def+updateFields

Setting field defaults on programmatically created Dexterity items

I have a Dexterity content type based on plone.directives.form.Schema which has a number of form hints for assigning defaults:
#form.default_value(field=ITrial['start_on'])
def default_start_on(data):
return datetime.now()
Some of the defaults are more complex, passing back objects that are themselves instances of Dexterity types. These objects are essential for the main type's setup, which is triggered by various events.
I'm now in the process of testing. Ideally, I'd like to be able to use something like:
item = createContentInContainer(folder, 'ctcc.model.trial', 'item')
That is, I'd like the defaults to be picked up by the item without having to be manually passed into the constructor.
If I was using zope.schema I could use FieldProperty to set up proxies to the schema fields. Is there something equivalent for Dexterity, or perhaps a function for pushing an object through form creation?
Solution: I ended up going with David's option #1, intercepting ObjectCreatedEvent.
#grok.subscribe(ITrial, IObjectCreatedEvent)
def create_trial(trial, event):
if getattr(trial, 'start_on', None) is None:
trial.start_on = default_start_on(None)
It stills feels like I'm replicating part of form behaviour, but at least it's using the same functions that are providing the form defaults.
As you've discovered, the #form.default_value decorator is respected by z3c.form forms, but not when items are created in other ways. You have several options:
Write a handler for the ObjectCreatedEvent for your content type which sets up the necessary default values. This is the simplest approach to implement, but may not work if there are other handlers of this event that need the values in place.
Create your own subclass of Dexterity's generic Item class, and use it instead of Item as the basis for your content type. Then you can customize the __init__ method to set whatever you want. This would require migration of existing content items though, if you already have some.
This is a more complicated option. Replace the factory utility used to construct the content type. createContentInContainer ends up looking for an IFactory utility with a name equal to the factory attribute of the content type's FTI. The default implementation is in plone.dexterity.factory but you could replace it with a different one that does more.

How to disable the output of a field depending on the return from another field?

There are fields in my view that I would rather hide depending on the value of another field. I am lookinf for ways to do it in code, or otherwise but without enabling php filter.
I don't know which version of Views you use. I can't quite remember whether output rewriting was available in V2, but I suppose it was. In V3, it's there, waiting for you to use it.
Output rewriting accepts any HTML code and you can use replacements from what the query returned. I'm not sure you can use PHP there, perhaps you can, but I've never really tried. Anyways, let's say you have field_foo and field_bar, and that both are some select options or checkboxes or some other multiple choice thing, having key-value pairs in the database (like 1|foo, 2|bar etc.).
In this situation, you should have four options available:
[field_foo_value]
[field_foo_value_raw]
[field_bar_value]
[field_bar_value_raw]
(they'll probably be named a bit differently, I can't quite remember the exact naming convention). You can rewrite the "bar" field output like this:
<span class="visibility-[field_foo_value_raw]">[field_bar_value]</span>
Then, assuming the possible keys for "foo" are 1 and 2, you can write some CSS:
span.visibility-1 { display: inline; }
span.visibility-2 { display: none; }
If PHP is allowed, it should be even easier, but I have a feeling you can only use HTML. Anyway, I hope this helps.
Using hook_form_alter or hook_form_FORM_ID_alter you can alter the views_exposed_form form. From there, it should be possible to use CTools' Dependent to set visibility dependencies between fields. Views's exposed form layout is partially handled in the theme layer (see views-exposed-form.tpl.php) so more work is probably needed to hide/show the labels.
This module seams like it would do what you need. I've used it and it's easy to setup and quick to use.
http://drupal.org/project/conditional_fields

Add extra field in story content type using hook without CCK

I want to add extra field in story content type using hook, I don't want to use CCK, because am trying something different.
Please tell some suggestion with hook method.
If you do not use CCK, you will have to create your database table and code to add the form field, validate the form field, capture the data and save it in your field. I know cck can be a monster, but it does all this for you. I'd be happy to give you more info on all of this, but it is quite lengthy
There are lots of reasons that you may want to do this without CCK or Fields, and the best example is found at the node_example module in the examples project which can be found at: http://drupalcode.org/project/examples.git/tree/refs/heads/6.x-1.x:/node_example. You can also view the documentation on api.drupal.org.
The short version is that you're going to have to define your own node type using hook_node_info() and then define all the hooks for _load(), _insert(), _update(), _delete(), _access(), _validate(), and _view() in addition to defining your schema in your hook_schema and managing your tables on your own.
Sadly there is no good example for Drupal 7 as the node_example module for 7 was converted to use fields instead of the hooks listed above, which are still fully documented on api.drupal.org (they do now typically act on an array of nodes instead of a single node, but are otherwise identical).

Panel page URL - Need to access named arguments

I have a Panel page, which I have given a path of: books/travel-books/%city/%country/%page. The help text underneath the field says "The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form.", so I have named my arguments appropriately.
So now in my code, I need to get the values of those arguments. I've seen arg(0), but that requires me knowing which index the argument has. Is there anyway to access it by the name I gave it in the path? Something like arg('city')??
The reason being, that I need to have similar path arguments on many pages, and need to access the values of these in my module. But the arguments may be in different places for another page. For instance, another page might be at: flights/%city/%country. Then I want to access the city argument within the same function, but it is at a different index.
Can anyone help?
you can check for arg(0) first, see whether it is 'books', 'flights', whatever... then associate names accordingly. do it as a helper function in a custom module and call it before referencing (wherever you're referencing it).
The text that you quoted from the panels help text is referring to what panels call context.
Panels
Panels has a great use if you want to aware of what context a certain piece of content is being viewed. Fx if you had several shops with different products, you might want to control which blocks in a sidebar would be displayed, based on the shop that the product belonged to.
This is essential what context is in panels and what the named placeholdes are used for. You can be default add different kinds of context, fx nodes, users, taxonomy terms. You can then use the different pieces of context if various ways.
If panels default options is not enough, you can also create your own plugins to panels to make it handle your special cases. But it requires a lot of time to learn how panels work.
Other solutions
Using panels might not be the easiest option for you, it depends what you are aiming for. But if you don't need to make pages that is aware of the context, this would probably be a lot easier to do with views and theming. Views can handle arguments in urls very well, and it is a lot simpler to both style and configure.
In Drupal 7 you can get the arguments from hook_content_type_render
function <your plugin name>_content_type_render($subtype, $conf, $panel_args, $context) {
$block = new StdClass;
$block->title = t('test');
$block->content = 'test panel arg: '.$panel_args[0];
return $block;
}
Custom panel tutorial

Resources