Passing a dynamic argument to an Archetype instance

8 messages Options
Embed this post
Permalink
Ken Winter () Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
How can I set up an Archetypes content type that can accept a dynamic argument when invoked by a GET request?  

By "dynamic argument" I mean an argument that can have different values in each new request.  (In my case, the arg values are keys that the controller script uses to select records from a SQL database, but this question isn't tied to that particular use of the arg values.)

I'm guessing that this has something to do with actions and aliases.  I'm still stuck in Plone 2.5, so these are declared in my content class definition.  My declarations of interest look like this:

    actions = (
        {
        'id'          : 'view',
        'name'        : 'View',
        'action'      : 'string:${object_url}/view',
        'permissions' : (permissions.View,),
         },
        {
        'id'          : 'edit',
        'name'        : 'Edit',
        'action'      : 'string:${object_url}/edit',
        'permissions' : (permissions.ModifyPortalContent,),
         },
        )

    aliases = {
        '(Default)'  : 'rdb_read?mode=edit',
        'view'       : 'rdb_read?mode=view',
        'edit'       : 'rdb_read?mode=edit',
        }

rdb_read is the controller script that reads a record from the database and then opens the content object in either edit or view mode, depending on the "mode" argument.  That much works fine.  But the script now needs a second argument - call it "searchkey" - that will have the variable value.  So I can't just hard-code it into the alias definition like the value of the "mode" arg.  The things I have tried and/or thought of are:

1.  Call it with a url like ${object_url}/edit/rdb_read?mode=edit&searchkey=12345.  The result of this is that the whole query string ("?" and everything to the right of it) is ignored, and the page is effectively invoked with the URL ${object_url}/edit/rdb_read?mode=edit, in other words, with just what is hard-coded into the actions and aliases declarations.  The controller script never sees the searchkey arg at all.

2.  Maybe there is some way to insert a string variable into the alias declaration, so one of them might look something like:

        'edit'       : 'rdb_read?mode=edit&searchkey=${keyvalue}',

but if so, how to get the keyvalue variable instantiated from the invoking URL? Or is there a way to substitute a variable declaration for the whole alias string?

3.  The CMFFormController how-to (http://plone.org/documentation/how-to/forms) mentions that an "action" created by portal_form_controller.addFormAction() can have an "args ... string (typically a TALES expression) that will be passed to the action."  That seems like just what I need, but if it is I have no idea how to implement it.

Can anyone steer me in the right direction?

~ TIA
~ Ken
Ken Winter () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
I had a 4th idea that I thought would be a solution - but not quite:

4. The form controller script has access to the REQUEST, and the REQUEST includes the URL.  So have the script mine the searchkey value from there?  Alas, in the REQUEST that's available to the controller script, Plone or Zope or somebody has already removed the URL from the request that was originally sent and replaced it with a URL that doesn't have the query component.  So I guess the question is:  Is there some way my Plone/Zope page that originates the request can stick in another attribute that Plone/Zope won't edit out?  Probably not, but I thought I'd ask.

Here's idea 5, which I'm about to try:

5. Instead of doing this as a GET request, do it as a POST, in which case the dynamic arg values can be sent in as hidden fields.  Of course, this requires reimplementing forms in all of my request-originating pages, and who knows what snakes may lurk in that grass?  I'd appreciate any proactive advice you may have about this one.

~ Thanks
~ Ken

Raphael Ritz () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
Ken Winter wrote:

Hi Ken,

while I'm not sure I understand your issue couldn't you just
write a little script yourself that gets the custom var from
the query string, calls invokeFactory to construct the object
and then edits it accordingly?

Something like

context.invokeFactory(type_name=MyType,id=some_id)
new_object = context[some_id]
new_object.edit(myattr='shiny new value')
new_object.reindexObject()

(and then return whatever you see fit or re-direct
to the new object's URL)

Raphael


> I had a 4th idea that I thought would be a solution - but not quite:
>
> 4. The form controller script has access to the REQUEST, and the REQUEST
> includes the URL.  So have the script mine the searchkey value from there?
> Alas, in the REQUEST that's available to the controller script, Plone or
> Zope or somebody has already removed the URL from the request that was
> originally sent and replaced it with a URL that doesn't have the query
> component.  So I guess the question is:  Is there some way my Plone/Zope
> page that originates the request can stick in another attribute that
> Plone/Zope won't edit out?  Probably not, but I thought I'd ask.
>
> Here's idea 5, which I'm about to try:
>
> 5. Instead of doing this as a GET request, do it as a POST, in which case
> the dynamic arg values can be sent in as hidden fields.  Of course, this
> requires reimplementing forms in all of my request-originating pages, and
> who knows what snakes may lurk in that grass?  I'd appreciate any proactive
> advice you may have about this one.
>
> ~ Thanks
> ~ Ken
>
>


------------------------------------------------------------------------------
Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT
is a gathering of tech-side developers & brand creativity professionals. Meet
the minds behind Google Creative Lab, Visual Complexity, Processing, &
iPhoneDevCamp as they present alongside digital heavyweights like Barbarian
Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com 
_______________________________________________
Archetypes-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/archetypes-users
Ken Winter () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
In reply to this post by Ken Winter
I think I have almost succeeded with this solution:
Ken Winter wrote:
5. Instead of doing this as a GET request, do it as a POST, in which case the dynamic arg values can be sent in as hidden fields.  Of course, this requires reimplementing forms in all of my request-originating pages, and who knows what snakes may lurk in that grass?  I'd appreciate any proactive advice you may have about this one.
But I need help with one last (?) problem.

My Archetypes form now includes some new submit buttons, which I collectively call "go-buttons".  When the user clicks a go-button, the desired result is that another Archetypes object is displayed, and that object receives the dynamic args it requires via the kwargs of the state object.  

When the form is submitted (by the user clicking any kind of submit button), the .metadata file invokes a post-processing .cpy script, in the usual Archetypes manner.  That script determines whether the button pressed as a go-button.  If so, the script then:

1. Mines the dynamic arguments out of hidden fields and adds them to the state object with state.setKwargs();
2. Sets the next action like this: state.setNextAction('traverse_to:string:' + go_url), where go_url is the relative URL of the desired next Archetypes object to bring up; and then
3. Returns the state object (see 'P.S' below for its contents).

The problem is that, after the script returns, nothing more happens.  From the user's view, the original form still remains displayed, unchanged, not even refreshed.  If the user clicks a submit button again, the dialog box is displayed that says "You have already clicked the submit button.  Do you really want to submit this form again?"  In my event.log, the last debugging line that appears is the one at the end of the post-processing .cpy script.  It seems clear that whatever Archetypes component was supposed to receive the state object and dispatch the next request failed to do so.

Let me add that, when the user clicks on a submit button that is not a go-button, the post-processing script sets the next action to 'traverse_to_action:string:edit', and Archetypes handles this just fine.

Please help if you can.

~ Thanks
~ Ken

P.S.  Here is a formatted dump of the state object just before the post-processor .cpy script returns it:

2009-06-02T11:03:36 INFO rdb_update.cpy:
 state = {
        * status : 'go'
        * _validators : {
                * validate_base : 1
                }
        * errors : {
                }
        * button : 'go_button.new_teaching_package.details_1_1'
        * next_action : {
                * action : {
                        * expression : {
                                * text : 'string:base-teaching-package-contents/edit'
                                * _v_compiled : {
                                        * _vars : []
                                        * _s : 'base-teaching-package-contents/edit'
                                        * _expr : 'base-teaching-package-contents/edit'
                                        }
                                }
                        }
                * key : {
                        * key : ('rdb_update', 'go', None, None)
                        }
                * action_type : 'traverse_to'
                * action_arg : 'string:base-teaching-package-contents/edit'
                }
        * context : [<RDBBaseTeachingPackages at /groups/dhr3/practical-matters/base-teaching-packages>]
        * kwargs : {
                * portal_status_message : 'Changes saved.'
                * teaching_package_id : '5'
                }
        * id : 'rdb_update'
        * _is_validating : 0
        }


And here for comparison is a formatted dump of the state object that the same script returns (and Archetypes correctly responds to) after the user pressed a non-go button:

2009-06-02T11:44:42 INFO rdb_update.cpy:
 state = {
        * status : 'success'
        * _validators : {
                * validate_rdb : 1
                }
        * errors : {
                }
        * button : 'dbsave'
        * next_action : {
                * action : {
                        * expression : {
                                * text : 'string:edit'
                                * _v_compiled : {
                                        * _vars : []
                                        * _s : 'edit'
                                        * _expr : 'edit'
                                        }
                                }
                        }
                * key : {
                        * key : ('rdb_update', 'success', None, None)
                        }
                * action_type : 'traverse_to_action'
                * action_arg : 'string:edit'
                }
        * context : [<RDBBaseTeachingPackages at /groups/dhr3/practical-matters/base-teaching-packages>]
        * kwargs : {
                * portal_status_message : 'Changes saved.'
                }
        * id : 'rdb_update'
        * _is_validating : 0
        }

Raphael Ritz () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
Ken Winter wrote:

> I think I have almost succeeded with this solution:
>
> Ken Winter wrote:
>> 5. Instead of doing this as a GET request, do it as a POST, in which case
>> the dynamic arg values can be sent in as hidden fields.  Of course, this
>> requires reimplementing forms in all of my request-originating pages, and
>> who knows what snakes may lurk in that grass?  I'd appreciate any
>> proactive advice you may have about this one.
>>
>
> But I need help with one last (?) problem.
>
> My Archetypes form now includes some new submit buttons, which I
> collectively call "go-buttons".  When the user clicks a go-button, the
> desired result is that another Archetypes object is displayed, and that
> object receives the dynamic args it requires via the kwargs of the state
> object.  
>
> When the form is submitted (by the user clicking any kind of submit button),
> the .metadata file invokes a post-processing .cpy script, in the usual
> Archetypes manner.  That script determines whether the button pressed as a
> go-button.  If so, the script then:
>
> 1. Mines the dynamic arguments out of hidden fields and adds them to the
> state object with state.setKwargs();
> 2. Sets the next action like this: state.setNextAction('traverse_to:string:'
> + go_url), where go_url is the relative URL of the desired next Archetypes
> object to bring up;

If I understand your use case at all I think you should be using
'redirect_to' instead of 'traverse_to' here.

Does that make a difference?

Raphael


> and then
> 3. Returns the state object (see 'P.S' below for its contents).
>
> The problem is that, after the script returns, nothing more happens.  From
> the user's view, the original form still remains displayed, unchanged, not
> even refreshed.  If the user clicks a submit button again, the dialog box is
> displayed that says "You have already clicked the submit button.  Do you
> really want to submit this form again?"  In my event.log, the last debugging
> line that appears is the one at the end of the post-processing .cpy script.
> It seems clear that whatever Archetypes component was supposed to receive
> the state object and dispatch the next request failed to do so.
>
> Let me add that, when the user clicks on a submit button that is not a
> go-button, the post-processing script sets the next action to
> 'traverse_to_action:string:edit', and Archetypes handles this just fine.
>
> Please help if you can.
>
> ~ Thanks
> ~ Ken
>
> P.S.  Here is a formatted dump of the state object just before the
> post-processor .cpy script returns it:
>
> 2009-06-02T11:03:36 INFO rdb_update.cpy:
>  state = {
> * status : 'go'
> * _validators : {
> * validate_base : 1
> }
> * errors : {
> }
> * button : 'go_button.new_teaching_package.details_1_1'
> * next_action : {
> * action : {
> * expression : {
> * text : 'string:base-teaching-package-contents/edit'
> * _v_compiled : {
> * _vars : []
> * _s : 'base-teaching-package-contents/edit'
> * _expr : 'base-teaching-package-contents/edit'
> }
> }
> }
> * key : {
> * key : ('rdb_update', 'go', None, None)
> }
> * action_type : 'traverse_to'
> * action_arg : 'string:base-teaching-package-contents/edit'
> }
> * context : [<RDBBaseTeachingPackages at
> /groups/dhr3/practical-matters/base-teaching-packages>]
> * kwargs : {
> * portal_status_message : 'Changes saved.'
> * teaching_package_id : '5'
> }
> * id : 'rdb_update'
> * _is_validating : 0
> }
>
> And here for comparison is a formatted dump of the state object that the
> same script returns (and Archetypes correctly responds to) after the user
> pressed a non-go button:
>
> 2009-06-02T11:44:42 INFO rdb_update.cpy:
>  state = {
> * status : 'success'
> * _validators : {
> * validate_rdb : 1
> }
> * errors : {
> }
> * button : 'dbsave'
> * next_action : {
> * action : {
> * expression : {
> * text : 'string:edit'
> * _v_compiled : {
> * _vars : []
> * _s : 'edit'
> * _expr : 'edit'
> }
> }
> }
> * key : {
> * key : ('rdb_update', 'success', None, None)
> }
> * action_type : 'traverse_to_action'
> * action_arg : 'string:edit'
> }
> * context : [<RDBBaseTeachingPackages at
> /groups/dhr3/practical-matters/base-teaching-packages>]
> * kwargs : {
> * portal_status_message : 'Changes saved.'
> }
> * id : 'rdb_update'
> * _is_validating : 0
> }
>


------------------------------------------------------------------------------
OpenSolaris 2009.06 is a cutting edge operating system for enterprises
looking to deploy the next generation of Solaris that includes the latest
innovations from Sun and the OpenSource community. Download a copy and
enjoy capabilities such as Networking, Storage and Virtualization.
Go to: http://p.sf.net/sfu/opensolaris-get
_______________________________________________
Archetypes-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/archetypes-users
Ken Winter () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
Raphael Ritz wrote:
Ken Winter wrote:
> I think I have almost succeeded with this solution:
>
> Ken Winter wrote:
>> 5. Instead of doing this as a GET request, do it as a POST, in which case
>> the dynamic arg values can be sent in as hidden fields.  Of course, this
>> requires reimplementing forms in all of my request-originating pages, and
>> who knows what snakes may lurk in that grass?  I'd appreciate any
>> proactive advice you may have about this one.
>>
>
> But I need help with one last (?) problem.
>
> My Archetypes form now includes some new submit buttons, which I
> collectively call "go-buttons".  When the user clicks a go-button, the
> desired result is that another Archetypes object is displayed, and that
> object receives the dynamic args it requires via the kwargs of the state
> object.  
>
> When the form is submitted (by the user clicking any kind of submit button),
> the .metadata file invokes a post-processing .cpy script, in the usual
> Archetypes manner.  That script determines whether the button pressed as a
> go-button.  If so, the script then:
>
> 1. Mines the dynamic arguments out of hidden fields and adds them to the
> state object with state.setKwargs();
> 2. Sets the next action like this: state.setNextAction('traverse_to:string:'
> + go_url), where go_url is the relative URL of the desired next Archetypes
> object to bring up;

If I understand your use case at all I think you should be using
'redirect_to' instead of 'traverse_to' here.

Does that make a difference?

Raphael
Thanks, Raphael ~

I just tried 'redirect_to'.  Unlike 'traverse_to', it does bring up the target AT object.  Alas, it loses the args I was trying to pass in state.kwargs.

Here's what my event.log shows just before and just after the redirect:

2009-06-03T12:46:57 INFO rdb_update.cpy:
  state.getKwargs() = {'teaching_package_id': '5'}

2009-06-03T12:47:01 INFO rdb_read.cpy:
  state.getKwargs() = {}

The missing arg ('teaching_package_id') is also nowhere to be found in the REQUEST object that the latter script has access to.  So we're not quite there yet.

~ Ken
Ken Winter () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
In reply to this post by Raphael Ritz
Raphael Ritz wrote:
...
while I'm not sure I understand your issue couldn't you just
write a little script yourself that gets the custom var from
the query string, calls invokeFactory to construct the object
and then edits it accordingly?

Something like

context.invokeFactory(type_name=MyType,id=some_id)
new_object = context[some_id]
new_object.edit(myattr='shiny new value')
new_object.reindexObject()

(and then return whatever you see fit or re-direct
to the new object's URL)
Thanks, Raphael ~

My question about your suggestion is: Where/how can I "install" this little script so that the script gets called at the right point in the servicing of a GET request, and the args that it captures are passed on to the code that needs to make use of it in responding to the request?

My understanding of the process of responding to a GET request for an Archetypes object is this:

1.  The request arrives at the Zope web server.  Its URL includes an HTML query string, in the format "?arg1=val1&arg2=val2".  The query string is the only place that the args are available to the responding server.

2.  ZPA (Zope/Plone/Archetypes, which here I am treating as a single black box) follows the URL path and determines that the request is for an instance of an AT content type.  Based on the actions and aliases in the content type definition, ZPA rewrites the tail of the URL.  Among other things, it discards the query string from the original URL.  Then it calls the rewritten URL.  In my case, the rewritten URL triggers the controller script (.cpy) of the requested AT object.

3.  The .cpy script would be the right place to mine out and use the arg data.  But the arg data are not available to it anywhere, as far as I can tell.  The rewritten REQUEST object no longer contains them anywhere - for example, the URLs it shows are the ones that ZPA rewrote back in step 2, where it tossed out the args.

As a Plone product developer, I have full control over the URL that is built in step 1 and the script that is invoked in step 3.  But I don't have control over what ZPA does in step 2 - except what I put into my content type definition, and I don't know how to tweak that definition to get a live argument through to my script.

If someone can show where my understanding of this process is faulty, maybe we'll have a solution.

~ Thanks
~ Ken
Raphael Ritz () Re: Passing a dynamic argument to an Archetype instance
Reply Threaded More More options
Print post
Permalink
In reply to this post by Ken Winter
Ken Winter wrote:
>
[..]

> I just tried 'redirect_to'.  Unlike 'traverse_to', it does bring up the
> target AT object.  Alas, it loses the args I was trying to pass in
> state.kwargs.

and what if you use a cookie or session instead?
http://docs.zope.org/zope2/zope2book/source/Sessions.html#using-session-data

Raphael


>
> Here's what my event.log shows just before and just after the redirect:
>
> 2009-06-03T12:46:57 INFO rdb_update.cpy:
>   state.getKwargs() = {'teaching_package_id': '5'}
>
> 2009-06-03T12:47:01 INFO rdb_read.cpy:
>   state.getKwargs() = {}
>
> The missing arg ('teaching_package_id') is also nowhere to be found in the
> REQUEST object that the latter script has access to.  So we're not quite
> there yet.
>
> ~ Ken


------------------------------------------------------------------------------
OpenSolaris 2009.06 is a cutting edge operating system for enterprises
looking to deploy the next generation of Solaris that includes the latest
innovations from Sun and the OpenSource community. Download a copy and
enjoy capabilities such as Networking, Storage and Virtualization.
Go to: http://p.sf.net/sfu/opensolaris-get
_______________________________________________
Archetypes-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/archetypes-users