|
Cookbook /
ZAPBusinessDirectorySummary:Adventures in building a business directory
Version:
Prerequisites:ZAP
Status:
Maintainer:XES
Categories:
Page is Ready for Comments! Green is XES' responses to Caveman Is Caveman's responses. This is my adventure in building a business directory in ZAP. First, you need to understand ZAP's philosophy: store data; manipulate data; retrieve data. Seems simple, but it's really not. In this case, I took advantage only of ZAP's data saving abilities. At another time, I may use other features of ZAP, but page data retrieval wasn't my goal when I understand PageLists better. actually I would say the philosophy is along the lines of POST the form and then use form fields to call various functions and commands. Basically ZAP is a mini scripting language for a form. Also, my recommendation below is to use the data retrieval functions also, as it makes things somewhat easier. Until I understand ZAP's data saving abilities, I am tackling it from the point of view that ZAP makes it easy for me to save things that I can use PmWiki to retrieve. Taking it one step at a time. The storage retrieval options can be entirely separate from the saving. From a tutorial point of view, it's like learning a language one step at a time. You seem to want people to dive in whole-hog, but an example of using ZAP just to augment PmWiki works, too, and is more easily digested. I should have put an example of what the data page looks like in a page edit -- so now that I think of it, I will. That may help explain to a PmWiki power user WHY and HOW ZAP works.
Second, you need only one page with ZAP enabled on it. In the local/ directory, one Group.PageName.php file. On that page (I used Site/EditMyDirectory), I worked out the basis for this recipe. I'll start with my thought processes. it might not hurt to mention that if you do not have AuthUser enabled, you must reset the ZAP form permissions in the Group Attributes page for the form--probably to nopass. please rephrase. Too many negatives. If AuthUser is not enabled, why would I need to reset the ZAP form permissions in Group Attributes? What's the difference if AuthUser IS enabled? Also, I would NOT want to reset ZAP's permissions on the GroupAttributes page for the Site group. Why not on the individual page's permissions?
Here was my plan:
Wrestling with ZAPI don't understand everything in ZAP. But here's things I get, and a line-by-line logic for what I've done:
You don't need to do anything else to get the messages: ZAP is already set up to send the messages to PmWiki.
The page name is the name of the current author (or logged in user), from the group "Directory-Data" and it uses the pagelist template named "bizdir" (details later).
BTW, ZAP isn't enabled on PmWiki (yet) so you don't need Yes -- it keeps "code" consistent throughout the page. It's monospaced, indented, etc. It's obviously "code".
All forms that contain ZAP commands need to be between these two zap directives
However, since I want to use ZAP to send data to a different page, there are changes to this command that are vital. For security reasons, some directives require extra information in the (:zapform:) directive. NO LINE BREAK in the line!!:
(:zapform `Username`Business`Member`Phone`Fax`Email`Website` Photo`BusSummary`Address`myaction`savedata`button`passdata`datapage` :) This is a security/permissions line?. When a form is submitted, ZAP checks this line to make sure that the items being entered have permission to be submitted to PmWiki. "Username", "Business", "Member" to "Address" are form fields or data I want saved to the Directory-Data pages (you will see these in the input form lines below). "myaction" is the variable used for form submit actions, "savedata?" gives the form permission to save to PmWiki pages (which variables are saved are specified in the directive), "button" is an arbitrary field on the form, "ifbutton?" is an "if" conditional that tests the field "button", "passdata?" allows the ZAP form to use variables in the page URL to pass information between form submission clicks, and "datapage?" gives the ZAP application permission to save data to another page in the wiki.
You might recall the lock pattern was designed to prevent forged headers, and I think it works well. However with a pattern like yours, you are only controlling what fields can be set, and not any of their values. I would probably want to include the values for at least some of these fields: ie all hidden field, and especially the datapage. Else anyone with access to the page could get the session variable set, retrieve the key, enter whatever values the want and submit the form. ie overwrite someone else's page. I mean, unlikely but doable. That's why at least some values are important. The plus is, when you put the values in the lockpattern, you don't need to put them in the form. They are automatically added (I think). To put values into the lock pattern, try this:
Can you please correct this directive to be specific to my form? there is no "field1,field2,etc". The datapage is not Group.Page. myaction is not "whatever". Take the time to make this right, otherwise you and I are going to go back and forth a bunch of times. I went through the trouble of posting my exact form on the site. All the values are available. If I try to follow your example I'm bound to make mistakes. Like "passdata=field" -- WHAT is field? Am I supposed to leave it literally "field" or change it to something else? If something else, then what?
Also, why all this redundancy. If I'm setting all my savedata values here, why am I bothering with a savedata directive later? There is no good reason I can think of that I'm doing this twice. Why not have the zapform directive automatically set all these directives, and make them unnecessary in the rest of the form?
If you process a form through ZAP without telling it which page to save information to, ZAP will save the data on the same page as the form. In this case, it would be a disaster because every user's directory entry would overwrite others'. There are other ways to do this, but I decided I wanted a single central page from which every user could take care of their own directory entry. The "@" portion of the command is pulling in the $Author variable of the page, a shortcut called ZAPFixPage?. This means I'm pointing the form to the place I want the user's data stored.
"ZAPfixpage"? directives must be used in (:zapdata:) and (:zapget:) directives. They are processed BEFORE page variables, in order to set page variables. Other ZAP directives can use normal page variables or the "ZAPfixpage"? shortcuts.
Not sure this is necessary in your case, as you will always have the {$Name} variable = to the same thing... Ah, but if you are not putting the form in a groupfooter, this might be a great idea. You would have {$=Name} available in the pagelist template, but not in a conditional on the page, and you might need that. But then again, if you know the page name, you know this value already, somehow... The alternative is to test whether the current page name is equal to "{Directory-Data.{$Author}". I actually may be using the $Username field to create a drop-down list of usernames for the admin to choose from. I wanted to store this on the page instead of having to parse the pagenames for the username in other circumstances. It helps if I want to link the Business Directory list to the Profiles, for example -- I use it to link to the profiles for the emails in the PageList Template - [[ Profiles/{{=$BaseName}$:Username} | email form on profile page]]
If you have (:zapdata Directory-Data.@:) on the page (anywhere) you can change this input field to:
I see that now, but it wasn't documented, so I stuck with what I understand; PmWiki page text vars.
I will explain the Business line, which is intended for the user's business name. This form input line is straight out of PmWiki's forms abilities, and documented there.
All the other (:input text ... :) directives follow similar reasoning.
I don't know if you tested this, but I get html in my textarea if I do it like this. That's why I wrap it in the Keep directive. It also adds some other nice features, like getting rid of the need for \\ and preserving spaces as . But mostly to avoid problems with html in the text areas. I'm surprized you didn't have any problems... Hmmm. I think with or without keep, directives and the like are automatically disabled when saved. Might mention it. Anyway, heres how I would write this line:
I thought it was working fine for me, but the problem happens when I edit things in the text area, and the problem is saved TO the data page output. Fixing this required me to add a [[==]] at the beginning of the output line on the pagelist template (see below) because sometimes there's a leading space in the output from the page text var in the pagelist template, which causes bad formatting for my purposes in PmWiki, so I put nonsense at the beginning of the line so PmWiki wouldn't read that space as meaning that the input is code. Oh, and for some reason, when I use the PmWiki text var, I can't use the italic markup in the pagelist template anymore -- it comes through with two single quotes instead of being read to italicize the text :/ Maybe I can find another way to get it to ignore the leading space.
Because the textarea can be filled with multiline data, it requires an explicit beginning and ending. As you can see, I pre-filled in the user's Address information so that the person can edit it.
OK, kind of twisted logic. Probably a hodgepodge of several tries and somehow ended up with something that worked. :) Anyway, here's the idea: First, myaction is a flag to tell what parts of the form to display. It's a GET value passed back to the page via the passdata action (you'll see it in the browser address bar). When the page loads it's initially set to View in the form. Then if the GET value myaction is not equal to Update then it is changed to Update in the form. (No clue why I didn't just write "Update" instead of button. Probably had more than one button at some point. the {button} thing is an example of field replacement. Means: substitute in value of "button" field into this field. VERY powerful and useful feature in ZAP--but no reason for it here). Ok, so in short, when $myaction is Update, it shows the update form and says next time set $myaction to View. If $myaction is View or "", then the Update Form does not show up but the form passed myaction=Update to the page when the button is clicked (ie: change to update form). Does that help? No, not really. Here's what I want: I want the page to have the update form on it, it doesn't need to be hidden from view to start with, and when the Update button is clicked, it's submitted. I don't need people to click Update twice. How do I fix this? There were so many directives left, and it worked, so I was happy enough ;)
(:input submit button "Update":) (:zapend:) With some guidance, I could probably eliminate a number of these lines. I only need a single "Update" button that opens up the edit form, or saves the new data. Actually, I could probably eliminate the hiding of the form entirely.
You might take a look at the Show/Hide snippet (and the Rolodex, again) for simpler examples to see how this kind of thing works. Also the comments on the Snippets and Doc's section uses this kind of system, and the Tagging page for the snippets. Combining passdata with zapget can be very powerful! Not sure I'll get the time to do all that hunting through snippets right now. The snippets don't explain themselves, so until I understand more, they're not helpful as building blocks, only as direct copy/paste.
The entire ZAP form page
The saved data page (edit/source view)(email address changed to protect my inbox, line breaks added to multi-line entry for readability) This page is invisible in browse mode:
PageListTo list only the author's own directory entry: (:pagelist name={$Author} group=Directory-Data fmt=#bizdir:)
To list all directory entries:
PageList TemplateYou can't use ZAP's data retrieval for pagelist templates, so you must use PmWiki's built-in page text vars to retrieve ZAP data for pagelists. (:zapdata:) & (:zapget:) won't save you any typing here. I created a custom pagelist template. Custom templates need to be stored on the wiki page LocalTemplates.
[[#bizdir]]
(:if ! equal {<$FullName}:)
----
(:if:)
(:table:)
(:cellnr width=170px:){{=$BaseName}$:Photo}
(:cell:)
!!{{=$BaseName}$:Business}
!!!{{=$BaseName}$:Member}
{{=$BaseName}$:Address}\\
{{=$BaseName}$:Phone}\\
(:if ! equal {{=$BaseName}$:Fax} "":)
{{=$BaseName}$:Fax} (fax)
(:ifend:)
[[ Profiles/{{=$BaseName}$:Username} | email form on profile page]]\\
{{=$BaseName}$:Website}\\
(:if:)
(:if equal {$Author} {{=$BaseName}$:Username}:)
[[Site/EditMyDirectory | Edit My Directory Entry]]\\
(:if auth admin:)
[[{=$BaseName}?action=edit | Edit Directory Data]]
(:ifend:)
(:tableend:)
[==]{{=$BaseName}$:BusSummary}
(:if equal {$Author} {{=$BaseName}$:Username}:)
[[Site/EditMyDirectory | Edit My Directory Entry]]
[[#bizdirend]]
NOTE: The other time you have limitations with (:zapdata:) & (:zapget:) is if you attempt to pull data in from more than one data page to a page. If all the data fields available are unique, great. But if not you will have some overwriting going on. In that case, page text vars may be safer, since each page is explicitly addressed.
See AlsoContributorsXES December 08, 2006, at 10:00 PM |