Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
* Brad Baxter
** Added thumbnails.
** Added Functional pages: UsersPage EditUserPage AddNewUserPage HelpPage EditBlurbPage EditHelpPage EditPagePage AdminPage and their corresponding image pages.
* Brad Baxter
** added link location to thumbnails
** moved Sidebar link back (it wasn't lining up right)
** reorganized Functional page
** added:
### MemberEditMappingsPageImage
### MemberEditMappingsPage
### MemberViewMappingsPageImage
### MemberViewMappingsPage
### AddNewUserPage
### AddNewUserPageImage
### MemberManageDataViewPageImage
### MemberManageDataViewPage
### MemberManageDataEditPageImage
### MemberManageDataEditPage
### MemberChangesPageImage
### MemberChangesPage
### MemberChangesDiffPageImage
### MemberChangesDiffPage
### GKRManageDataViewPageImage
### GKRManageDataViewPage
### GKRManageDataEditPageImage
### GKRManageDataEditPage
### GKRChangesPageImage
### GKRChangesPage
### GKRChangesDiffPageImage
### GKRChangesDiffPage
* Brad Baxter
** Began filling out [[Technical]] page.
** Began filling out [[Installation]] page.
** Began filling out [[Programming]] page.
* Brad Baxter
** Copyedit various pages
** Recreate some thumbnails
** Add the following
### MemberPublishPageImage
### MemberPublishPage
### GKRPublishPage
### GKRPublishPageImage
* Brad Baxter
** Added pages:
*** Config::Ini::Quote
*** Config::Ini
*** Tree::Indented
* Brad Baxter
** Added pages:
*** [[Config::Ini::Expanded Page 2]]
*** [[Config::Ini::Expanded Page 3]]
*** [[Config::Ini::Expanded]]
*** [[Config::Ini::Edit Page 2]]
*** [[Config::Ini::Edit]]
*** [[TemplatePlaceholders]]
*** [[Table Maker]]
*Brad Baxter
** Finished [[API]] and "Action" pages.
** Added [[Tutorials]] (with placeholders for the individual tutorial pages)
*Brad Baxter
** Completed most of the tutorials.
* Created gkr_mapping_wiki.html tiddlywiki document
* Organized framework as
** Introduction
*** A general overview of the mapping tool
*** Added [[Introduction]] page
** Functional
*** Page-by-page functional descriptions
*** Functional pages added: HomePage LoginPage GKRCollectionsPage GKRManageDataPage MemberPage MemberMappingPage MemberManageDataPage UsersPage EditUserPage AddNewUserPage (and accompanying image pages)
** Configuration
*** Intended to cover all the configuration files
*** Added (unfinished) [[Configuration]] page
** Technical
*** Intended to describe the technology used in the site
** Installation
*** Intended to give instructions for installing the mapping tool
** Programming
*** Intended to cover some of the programming details -- to help a maintenance developer
*** May also include the "API", i.e., the cgi parameters and values
* Bugs?:
** User from member A logs out. User from member B logs in and sees member A's page (instead of B's) (Need to clear cookies at logout maybe?)
* Brad Baxter
** Finished HowtoAddNewInstitution tutorial.
* Brad Baxter
** Created SiteMap, and added ForEachTiddlerPlugin to list tagged pages on each tag page.
** Divided [[Technical]] into multiple pages.
** Finished [[Page Templates]]
!Application Programming Interface (API)
Below are the CGI parameters that the mapping tool looks for, and the operations it performs. Note: some may not consider a discussion of CGI parameters to be an API, and that's okay with me.
!Actions
The ''action'' parameter tells the program which operation is being requested. General actions are those that do not need to know which member institution to focus on. Member actions are those that do need to know, and the member must be identified using the ''member'' parameter, or it will be construed from the user's group attribute.
!!General Actions
* [[action=login|ActionLogin]]
* [[action=logout|ActionLogout]]
* [[action=admin|ActionAdmin]]
* [[action=view_file|ActionViewFile]]
* [[action=edit_file|ActionEditFile]]
* [[action=save_file|ActionSaveFile]]
* [[action=edit_user|ActionEditUser]]
* [[action=add_user|ActionAddUser]]
* [[action=save_user|ActionSaveUser]]
* [[action=delete_user|ActionDeleteUser]]
* [[action=changes|ActionChanges]]
* [[action=diff|ActionDiff]]
* [[action=recover|ActionRecover]]
* [[action=users|ActionUsers]]
* [[action=help|ActionHelp]]
* [[action=home|ActionHome]]
{{center{
[img[featurebottom.png]]}}}
!!Member Actions
* [[action=view_tree|ActionViewTree]]
* [[action=edit_tree|ActionEditTree]]
* [[action=save_tree|ActionSaveTree]]
* [[action=view_coll|ActionViewColl]]
* [[action=edit_coll|ActionEditColl]]
* [[action=save_coll|ActionSaveColl]]
* [[action=manage_data|ActionManageData]]
* [[action=update_coll|ActionUpdateColl]]
* [[action=merge_coll|ActionMergeColl]]
* [[action=merge_gkr|ActionMergeGKR]]
* [[action=map_node|ActionMapNode]]
* [[action=save_node|ActionSaveNode]]
* [[action=publish|ActionPublish]]
!!Other Parameters
;valid (boolean)
:Causes the XHTML and CSS validation links to appear in the footer
:Example: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=home;valid=1
;debug (boolean)
:Programmer funtionality: prints @Debug array, which may not contain anything
:Example: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=home;debug=1
!action=add_user
This action is a request to view the [[Add New User|AddNewUserPage]] page, which shows a blank form and a list of user templates.
!!Parameters
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
:(Note that currently, there aren't links to add_user that also include parms -- but the processing is there if there ever are.)
!!Operations
* Creates a list of user templates to display next to the HTML form.
* Reads the current user credentials and decides which input fields to preselect and/or disable.
* Displays [[Add New User|AddNewUserPage]] page
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=add_user
!action=admin
This action is a request to view the [[Admin|AdminPage]] page. The user must be logged in and must be a supervisor.
!!Parameters
None.
!!Operations
* If the user is not logged in, the [[login|LoginPage]] page is displayed.
* If the user is not a supervisor, the [[error|ErrorPage]] page is displayed, saying "Sorry, you may not access the admin page."
* Otherwise, the [[Admin|AdminPage]] page is displayed.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=admin
!action=changes
This action is a request to view the [[Changes|GKRChangesPage]] page for a given file.
!!Parameters
;file
:The file name for which the changes display is being requested
!!Operations
* Check that the current user's group and permissions allows for viewing changes to the requested file.
* Validate the file name (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* Build list of changes from the backup files.
* Display the [[Changes|GKRChangesPage]] page.
!!Examples
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=members.ini
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=gkr_coll
!action=delete_user
This action is a request to delete a user record. It is called from the [[Edit User|EditUserPage]] page.
!!Parameters
;timestamp
:The time that the user record was retrieved
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
;file
:The name of the user file
;id
:The user id
;check_pwd
:The password of the user requesting the action
!!Operations
* Check if the user requesting the action is a coordinator or supervisor.
* Check that ''file'' equals ''id'', i.e., that the user record was retrieved prior to the delete request.
* Check the timestamp to be sure the record hasn't been changed since our retrieving it.
* Compare passwords to validate the current user.
* Check that a coordinator is not trying to delete a user they're not allowed to
* Delete the user record.
* Redirect to where the ''parms'' parameter indicates, or to the [[Users|UsersPage]] page.
!!Example
{{{
<form method="post" action="/cgi-bin/gkr_mapping">
<input type="hidden" name="id" value="john_user" />
<input type="hidden" name="timestamp" value="1265391776" />
<input type="hidden" name="file" value="john_user" />
<input type="hidden" name="parms" value="" />
<input type="hidden" name="action" value="delete_user" />
<input type="submit" name="submit" value="Delete User: john_user" />
<input type="password" name="check_pwd" /> Enter <em>Jane Smith's</em> password for deletion.
</form>
}}}
!action=diff
This action is a request to view a [[Diff|GKRChangesDiffPage]] page for a file and a previous version of it.
!!Parameters
;file
:The file name for which the diff display is being requested
;version
:The version number of a previous version of the above named file
!!Operations
* Check that the current user's group and permissions allows for viewing diffs of the requested file.
* Validate the file name (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* Generate a "diff display" for the two files (current file and previous version)
* Display the [[Diff|GKRChangesDiffPage]] page.
!!Notes
* Currently, the mapping tool doesn't allow you to view diffs between two previous versions of a file -- only between a previous version and the current file.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=diff;file=members.ini;version=.20091220.006
!action=edit_coll
This action is a request to view the [[Edit Coll|GKRManageDataEditPage]] page.
!!Parameters
;member
:The institution code whose collections page is being requested
!!Operations
* Get the collection data
* Check for errors in the collections data
* Display the [[Edit Coll|GKRManageDataEditPage]] page, including error messages if any.
!!Notes
* The user does not have to be logged in or be in this institution's group to request to view the Edit Coll page for the institution's collections, but ...
* If not, the user may not change the data.
* In this case, when the mapping tool displays the page, a reminder dialog box pops up to tell the user that they may not "Save Changes"
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_coll;member=gkr
!action=edit_file
This action is a request to view the contents of a file in a textarea field with the intention of saving changes to the file contents (see action=save_file below). The user must be logged in and must be a supervisor.
!!Parameters
;file
:The name of the file
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
!!Operations
* If the user is not logged in, the [[login|LoginPage]] is displayed.
* If the user is not a supervisor, the [[error|ErrorPage]] page is displayed, saying "Sorry, you may not access the edit_file page."
* Otherwise, the file name is validated (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* If the file is not recognized, the [[error|ErrorPage]] page is displayed, saying "Unrecognized file: 'file_name'"
* Finally, the [[Edit File|EditFilePage]] page is displayed showing the contents of the file in a textarea field with a "Save Changes" form submit button.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=members.ini
!action=edit_tree
This action is a request to view the [[Edit Communities/Collections Mappings|MemberEditMappingsPage]] page.
!!Parameters
;member
:The institution code whose mappings (tree) is being requested
!!Operations
!!Notes
* The user does not have to be logged in or be in this institution's group to request to view the Edit Mappings page for the institution's collections, but ...
* If not, the user may not change the data.
* In this case, when the mapping tool displays the page, a reminder dialog box pops up to tell the user that they may not "Save Changes"
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_tree;member=gkr
!action=edit_user
This action is a request to view the [[Edit User|EditUserPage]] page with the current user attributes loaded into an HTML form for editing.
!!Parameters
;user_file
:The file name of the user's profile file
:Actually, it's the userid of the user to be edited, but the ''userid'' parameter is already being used for something else.
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
!!Operations
* If the user is not logged in, the [[login|LoginPage]] page is displayed.
* The user_file value is validated (i.e., is it one of the users that would display on the [[Users|UsersPage]] page?)
* If the user_file isn't recognized, the [[error|ErrorPage]] page is displayed, saying "Unrecognized user: 'user_file'"
* Otherwise, checks are made to verify that the user requesting the action is allowed to edit the user named by user_file.
* If the checks fail, the [[error|ErrorPage]] page is displayed, saying "Sorry, you are not allowed to edit that user."
* Finally, parts of the page are disabled or not shown depending on the type of user requesting the action, and the [[Edit User|EditUserPage]] page is displayed with the user attributes loaded into the form.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=gatech_user
!action=help
This action is a request to view the [[Help|HelpPage]] for a given page in the interface.
!!Parameters
;page
:The "code" (e.g., ''home'') that identifies the page in the interface
!!Operations
* Check that the user is logged in.
* Generate a "help menu", a list of links to all the help pages
* Display the requested [[Help|HelpPage]] page.
!!Notes
* The help text is accessed via a template placeholder in the help_page page template
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=help;page=home
!action=home
This action is a request to view the [[Home|HomePage]] page.
!!Parameters
None.
!!Operations
* Display the [[Home|HomePage]] page (unconditionally).
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=home
!action=login
This action is a request to log in to the mapping tool. In order to log in, a user profile must exist for the userid. User profiles can be created by coordinator or supervisor users.
!!Parameters
;userid
:The user's userid as defined in the user profile
;password
:The user's password as defined in the user profile
:Note: if a user forgets the password, they^^[1]^^ must ask a coordinator or supervisor user to reset it for them.
!!Operations
*If the userid and password are not provided, the [[login|LoginPage]] page is displayed, saying "Welcome, please sign in."
*If the userid and password are not accepted, the [[login|LoginPage]] page is displayed, saying "Userid/Password not accepted."
*If the userid and password are accepted, the following happens:
** The user profile is ingested.
** A session file and cookie are created.
** Session files older than 7 days are deleted.
** The user's institution's member page is displayed. If the user's group is 'all', the GKR Communities and Collections page is displayed.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=login;userid=john_user;password=SantaClaus
Note: this example is not typical (and not recommended) usage. Typical usage would be submission of an HTML form with ''userid'' and ''password'' (and ''action'') as input fields.
--
[1] [[They]]
!action=logout
This action is a request to log out of the mapping tool. No parameters are needed -- the user's browser session cookie, gkr_sid, is used to know which user (actually, which session) to log out.
!!Parameters
None.
!!Operations
* The session file and cookie are deleted.
* The [[login|LoginPage]] page is displayed, saying "Logged out."
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=logout
!action=manage_data
This action is a request to view the [[Manage Data|MemberManageDataPage]] page for the member supplied.
!!Parameters
;member
:The institution code whose data is being requested
!!Operations
* Get some information from the member's configuration section
* Generate the list of files (collections and mappings) to display
* Display the [[Manage Data|MemberManageDataPage]] page.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=manage_data
!action=map_node
This action is a request to view the [[Mapping|MemberMappingPage]] page for the collection indicated.
!!Parameters
;member
:The institution code whose collection is being mapped
;node
:The collection node id -- this id is generated by the program when the [[Member|MemberPage]] page is displayed.
!!Operations
* Check that the institution isn't ''gkr'', which can't be mapped.
* Get the member's mappings data.
* Using the node_id, get the collection handle and name from the mappings data.
* Display the [[Mapping|MemberMappingPage]] page.
!!Notes
* The term ''node'' refers to a particular collection that is seen as a node in the indented tree structure.
* In time, it may be that we can drop the notion of node, because it was initially thought that a collection under one community may have a different mapping that the same collection under another community. That's not actually the case, but the code to handle nodes has been left in place in case this changes in the course of setting up the GKR.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=map_node;node=valdosta-383-363-359
!action=merge_coll
This action is a request to merge the institution's collections file with their mappings file. This would need to be done following an [[action=update_coll|ActionUpdateColl]] operation.
!!Parameters
;member
:The institution code whose collections/mappings are being merged
!!Operations
* Check that the user is logged in.
* Check that the user belongs to the institution's group (or to group ''all'').
* Get collections data and mappings data
* If institutions is ''gkr'', display an error (should call [[action=merge_gkr|ActionMergeGKR]] instead).
* Otherwise (if not ''gkr''), do the following:
** Loop through the (current) mappings file, getting the existing collection-to-collection correspondences (mappings) with ''gkr''.
** Loop through the (new) collections file, reassociating the ''gkr'' mappings with the collections where they appear here.
** If the resulting (new) mappings file is exactly like the current one, do nothing (unless {{code{force=1}}}).
** Otherwise (if there are changes, or if we want to force an update)
*** Make a backup of the current mappings file
*** Write the new mappings file out to disk (making it the new current file)
*** Display the [[Manage Data|MemberManageDataPage]] page (the only page where the "Merge Collections with Mappings" link appears).
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=merge_coll;member=valdosta
!action=merge_gkr
This action is a request to merge the GKR collections data with all of the member institutions' mappings data.
!!Parameters
;member
:This must be ''gkr'' (and the user must be in the ''gkr'' or ''all'' group)
!!Operations
* Check that the user is logged in.
* Check that the member is ''gkr''
* Get collections data and mappings data (these will be different if there is anything to merge)
* If these are the same, do nothing (unless {{code{force=1}}}).
* Otherwise (if they are difference or if we want to force a merge)
** Make a backup of the current mappings file
** Save the new mappings data (making it current)
** For each member institutions
*** Get their mappings data
*** Loop through each line of mappings data
*** When a gkr collection is found, replace it (by handle) with the collection information in the gkr mappings data
*** If the institution's mappings data has not consequently changed, do nothing (unless {{code{force=1}}})
*** Otherwise (if the mappings data has changed, or if we want to force an update)
**** Make a backup of the institution's mappings data
**** Save the new mappings data
* After all the institutions' data files have been merged, display the GKR [[Manage Data|GKRManageDataPage]] page (where the "Merge GKR Collections with all Mappings" link appears).
!!Notes
* This is different from [[action=merge_coll|ActionMergeColl]], which merges the institution's collections data with their mappings data.
* The difference is that [[action=merge_gkr|ActionMergeGKR]] updates the GKR collections in the mappings data while ''action=merge_coll'' updates the institution's collections in their mappings data.
* //And// ''action=merge_gkr'' updates all the member institutions' files in one go.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=merge_gkr;member=gkr
!action=publish
This action is a request to "publish" the changes that have been made since the last time ''action=publish'' was done. Having a "publish" step allows a user to make many mapping changes -- and perhaps change their mind in the process -- without affecting the publicly seen mappings until all the desired changes are complete.
!!Parameters
;member
:The institution whose mappings we're publishing
!!Operations
* Check that the user is logged in.
* Check that the user is in the institution's group (or group ''all'').
* Get the institution's mappings data
* Validate the mappings data for errors, and if there are any, redisplay the [[View Communities/Collections Mappings|MemberViewMappingsPage]] page with the error messages.
* Traverse the mappings data to create the lookup tables.
* If member ''gkr'', publish just the RSS feed for GKR.
* Otherwise, publish each of RSS, XML, CSV, JSON, YAML, Perl and display a confirmation page.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=publish
!action=recover
This action is a request to recover a previous version of the requested file.
!!Parameters
;file
:The file name to recover to
;version
:The version number to recover from
;timestamp
:The time that the changes were displayed
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Recover"
!!Operations
* Check that the current user's group and permissions allows for recovering previous versions of the requested file.
* Validate the file name (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* Read the contents of the previous version of the file (expecting ~UTF-8).
* Check the timestamps to make sure the file hasn't changed since we generated the Changes page.
* Check if the two versions are exactly the same (don't save if they are -- unless {{code{force=1}}})
* Make a backup of the current file (note that this is creating another previous version that may be recovered from).
* Save the previous contents to the current file
* Redirect to the Changes page, displaying the additional previous version (from the backup above)
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=recover;file=valdosta_coll;version=.20100219.001;timestamp=1266620189;parms=action%3dchanges%3bmember%3dvaldosta%3bfile%3dvaldosta_coll
(Note that this example is likely to be obsolete, meaning it will give you an error message.)
!action=save_coll
This action is a request to save changes to the collections data file. This is called when the user clicks "Save Changes" on the [[Edit Communities/Collections|GKRManageDataEditPage]] page.
!!Parameters
;timestamp
:The time that the collections data file was retrieved
;member
:The institution whose collections data we're saving
;coll
:The collections data (usually from a <textarea> field)
!!Operations
* Check that the user is logged in.
* Get the submitted collections data (expect ~UTF-8).
* Check that the user is in the institution's group (or group ''all'').
* Check timestamps to make sure the file hasn't changed since being retrieved
* Validate the collections data for errors, and if there are any, redisplay the [[Edit Communities/Collections|GKRManageDataEditPage]] page with the error messages.
* If the collections data hasn't changed, do nothing (unless {{code{force=1}}}).
* Otherwise (if the data has change, or if we want to force an update)
** Make a backup of the collections data
** Save the new collections data
** Display the [[Manage Data Files|MemberManageDataPage]] page (where the "Edit" link appears on the collections file line).
!!Example HTML Form
{{{
<form method="post" action="/cgi-bin/gkr_mapping">
<input type="submit" name="submit" value="Save Changes" />
<input type="reset" name="reset" value="Reset" />
<input type="hidden" name="timestamp" value="1265749348" />
<input type="hidden" name="member" value="gkr" />
<input type="hidden" name="action" value="save_coll" />
<textarea name="coll" cols="100" rows="20">
gkr cm 1000 Agriculture, Agriculture Operations, and Related Sciences
gkr cl 2000 Testing ... Agriculture, Agriculture Operations, and Related Sciences
gkr cm 1001 Architecture and Related Services
...
</textarea>
</form>
}}}
!action=save_file
This action is a request to save the contents of a file. It should appear in the context of an HTML form on the [[Edit File|EditFilePage]] page.
!!Parameters
;file
:The name of the file
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
;timestamp
:A timestamp containing the time that the file contents were retrieved
:The timestamp is used to safeguard against loosing data if two people are editing the same file. When a user clicks "Save Changes", if the file being saved has a newer modification time that the timestamp, it means someone changed the file since this user retrieved it. This user will have to re-retrieve the file and edit the newer version.
;force
:A boolean value (default is false)
:If ''force=1'', then the file contents will be saved whether or not they are different than what is in the file already.
!!Operations
* If the user is not logged in, the [[login|LoginPage]] page is displayed.
* If the user is not a supervisor, the [[error|ErrorPage]] page is displayed, saying "Sorry, you may not save files."
* Otherwise, the file contents are ingested and decoded as ~UTF-8.
* The file name is validated (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* The timestamp is compared to the file on disk. If the file on disk is newer than the timestamp, the [[error|ErrorPage]] page is displayed, saying
<<<
The file has changed since you retrieved it, so you may not save your
changes. Please retrieve the file again (possibly in a new window) and
make your changes to the current data.
<<<
* If ''force'' is false, the contents of the file on disk are compared to the contents being saved. If they are not different, the contents are not saved, and the [[error|ErrorPage]] is displayed, saying "No changes to save."
* If ''force'' is true or the contents are different, then a backup is made of the file, and the new contents are written to the file.
* Finally, the user is redirected to the page they were on when they requested ''action=edit_file''.
!!Example
{{{
<form method="post" action="/cgi-bin/gkr_mapping">
<input type="submit" name="submit" value="Save Changes" />
<input type="hidden" name="timestamp" value="1266464782" />
<input type="hidden" name="file" value="members.ini" />
<input type="hidden" name="parms" value="" />
<input type="hidden" name="action" value="save_file" />
<textarea name="text" cols="100" rows="20">
#---------------------------------------------------------------------
# GKR Member Institutions
...
</textarea>
</form>
}}}
!action=save_node
This action is a request to save a particular collection-to-collection(s) mapping. This is called when the user clicks "Save Changes" on the [[Mapping: [collection]|MemberMappingPage]] page.
!!Parameters
;timestamp
:The time that the page was displayed (will compare to the mappings data file modification time)
;member
:The institution whose mapping we're saving
;handle
:The handle of the collection that we're mapping
;node
:The node for the collection that we're mapping (the node identifies where the collection appears in the communities/collections hierarchy)
;gkr_item'(s)
:The GKR collection(s) that this local collection should be mapped to (note that the value of a gkr_item consists of the entire data line as it appears in the GKR collections data, e.g., ''gkr cl 2000 Agriculture, Agriculture Operations, and Related Sciences'', i.e., it's not just the handle).
:Note also that there may be any number of gkr_item's submitted for a given collection.
!!Operations
* Get the submitted gkr_item'(s) (expect ~UTF-8).
* Check timestamps to make sure the file hasn't changed since being retrieved
* Get the mappings data
* Merge the gkr_item'(s) into the mappings data (where the handle matches the submitted handle)
* Call [[action=save_tree|ActionSaveTree]] with the updated mappings data.
!!Notes
* Note that this action calls another action, ''save_tree'', so note the operations performed in that routine.
* In particular, you will see that a backup of the mappings data is made for ''save_tree'', which means a backup is made for ''save_node'', as well. So it is possible to recover a version of the mappings file at each point that a new mapping is made.
!!Example
Note in this example that the complexities of the <ul> elements are wrapped around <input type="checkbox" name="gkr_item" ...> fields.
{{{
<form method="post" action="/cgi-bin/gkr_mapping" onreset="highlight_checked();">
<input type="submit" name="submit" value="Save Changes" />
<input type="reset" name="reset" value="Reset" />
<input type="hidden" name="timestamp" value="1266872144" />
<input type="hidden" name="member" value="valdosta" />
<input type="hidden" name="handle" value="383" />
<input type="hidden" name="node" value="valdosta-383-363-359" />
<input type="hidden" name="action" value="save_node" />
<ul class="openclose">
<li class="open"><a class="cm" name="gkr-1000" href="#gkr-1000" onclick="return openclose(this);">Agriculture, Agriculture Operations, and Related Sciences</a>
<a class="handle" href="http://gkr.gatech.edu/manakin/handle/123456789/1000">(gkr 1000 »)</a>
<ul class="openclose" title="Parents: gkr cm 1000 Agriculture, Agriculture Operations, and Related Sciences">
<li class="bullet" title="Parent: Agriculture, Agriculture Operations, and Related Sciences"><span class="cl" id="gkr-2000-1000"><label>
<input type="checkbox" name="gkr_item" value="gkr cl 2000 Agriculture, Agriculture Operations, and Related Sciences" onclick="highlight(this);" />Agriculture, Agriculture Operations, and Related Sciences</label></span>
<a class="handle" href="http://gkr.gatech.edu/manakin/handle/123456789/2000">(gkr 2000 »)</a>
</li>
</ul></li>
<li class="open"><a class="cm" name="gkr-1001" href="#gkr-1001" onclick="return openclose(this);">Architecture and Related Services</a>
<a class="handle" href="http://gkr.gatech.edu/manakin/handle/123456789/1001">(gkr 1001 »)</a>
<ul class="openclose" title="Parents: gkr cm 1001 Architecture and Related Services">
<li class="bullet" title="Parent: Architecture and Related Services"><span class="cl" id="gkr-2001-1001"><label>
<input type="checkbox" name="gkr_item" value="gkr cl 2001 Architecture and Related Services" onclick="highlight(this);" />Architecture and Related Services</label></span>
<a class="handle" href="http://gkr.gatech.edu/manakin/handle/123456789/2001">(gkr 2001 »)</a>
</li>
</ul></li>
...
</ul>
</form>
}}}
!action=save_tree
This action is a request to save changes to the mappings data file. This is called when the user clicks "Save Changes" on the [[Edit Communities/Collections Mappings|MemberEditMappingsPage]] page.
!!Parameters
;timestamp
:The time that the mappings data file was retrieved
;member
:The institution whose mappings data we're saving
;tree
:The mappings data (usually from a <textarea> field)
!!Operations
* Check that the user is logged in.
* Get the submitted mappings data (expect ~UTF-8).
* Check that the user is in the institution's group (or group ''all'').
* Check timestamps to make sure the file hasn't changed since being retrieved
* Validate the mappings data for errors, and if there are any, redisplay the [[Edit Communities/Collections Mappings|MemberEditMappingsPage]] page with the error messages.
* If the mappings data hasn't changed, do nothing (unless {{code{force=1}}}).
* Otherwise (if the data has changed, or if we want to force an update)
** Make a backup of the mappings data
** Save the new mappings data
** Display the [[Member|MemberPage]] that shows the mappings tree.
!!Notes
* Displaying the Member page (instead of the Manage Data Files page) may seem inconsistent, and in fact it is (we display the Manage Data files page after we do ''action=save_coll''). But displaying the Member page allows us to call the ''save_tree'' routine whenever we have a ''save_node'' operation. Maybe this will change in the future, but for now, be aware that it's inconsistent.
* The term "tree" should be considered synonymous with the term "mappings".
!!Example
{{{
<form method="post" action="/cgi-bin/gkr_mapping">
<input type="submit" name="submit" value="Save Changes" />
<input type="reset" name="reset" value="Reset" />
<input type="hidden" name="timestamp" value="1266872144" />
<input type="hidden" name="member" value="valdosta" />
<input type="hidden" name="action" value="save_tree" />
<textarea name="tree" cols="100" rows="20">
valdosta cm 359 College of Arts and Sciences
valdosta cm 363 African American Studies
valdosta cl 383 AAS Faculty Research and Teaching Materials
valdosta cm 364 Biology
...
</textarea>
</form>
}}}
!action=save_user
This action is a request to save user profile attributes to disk. It is called when you click "Save Changes" on either the [[Add New User|AddNewUserPage]] page or the [[Edit User|EditUserPage]] page.
!!Parameters
;timestamp
:The time that the user record was retrieved (i.e., from [[EditUser|EditUserPage]] page)
;parms
:The parameters that the program will use to redirect the user back to the original page after clicking "Save Changes"
;file
:The name of the user file.
;id
:The user id.
;name
:The user name.
;email
:The user email address.
;group
:The user's institution group. The group code entered here should be one of the institution codes (e.g., gatech, uga, valdosta) or ''all''.
;note
:Any additional information.
;add, change, delete
:The add, change, and delete permissions.
:Note: currently, ''add'', ''change'', and ''delete'' are lumped together by the program as an ''edit'' permission, and ''add'', ''change'', and ''delete'' may be changed to ''edit'' in the future.
;admin
:The admin role. If a user isn't ''admin'' (or ''coordinator'' or ''supervisor'') they are ''public'' and will see a restricted set of pages.
:An ''admin'' user may log in to the mapping tool and may change their own password, but may not add or edit other users.
;coordinator
:The coordinator role
:A ''coordinator'' user may add or edit ''admin'' users in their own institution group (but may not give permissions greater than their own)
;supervisor
:The supervisor role
:A ''supervisor'' user may add or edit any user, including ''admin'', ''coordinator'', and ''supervisor'' users.
:A ''supervisor'' user has additional priviledges that include editing blurb text, help text, page templates, and configuration files -- everything, in fact, except the gkr_mapping program code itself.
;upd
:The yyyy/mm/dd update date (when the user record is saved).
;user
:The user id of the user who saved the record (not necessarily the user whose record is being saved).
!!Operations
* If we're saving the user we retrieved (i.e., if ''file'' equals ''id''), then check timestamp to see if the file has changed since being retrieved
* Otherwise, if we're saving a new user, then check if that user already exists.
* Create a user record from the data being submitted.
* Validate whether the user submitting the data has sufficient permissions to be doing so.
* Check whether the submitted passwords match up.
* Save the user record.
* Redirect to where the ''parms'' parameter indicates, or to the [[Users|UsersPage]] page.
!!Example HTML Form
{{{
<form method="post" action="/cgi-bin/gkr_mapping">
<input type="submit" name="submit" value="Save Changes" />
<input type="reset" name="reset" value="Reset" />
<input type="hidden" name="timestamp" value="1257521126" />
<input type="hidden" name="file" value="gatech_user" />
<input type="hidden" name="parms" value="" />
<input type="hidden" name="action" value="save_user" />
<input type="hidden" name="upd" value="2010/02/19" />
<input type="hidden" name="user" value="gatech_user" />
<dl id="user_form">
<dt>User ID: </dt><dd><input size="20" type="text" name="id" value="gatech_user"
disabled="disabled" /></dd>
<dt>Name: </dt><dd><input size="60" type="text" name="name"
value="Georgia Tech User" /></dd>
<dt>E-mail: </dt><dd><input size="60" type="text" name="email"
value="bmb@mail.libs.uga.edu" /></dd>
<dt>Note: </dt><dd><input size="60" type="text" name="note"
value="Use this template to add new gatech users" /></dd>
<dt>Change Password: </dt>
<dd><input type="password" name="check_pwd" /> Enter <em>Georgia Tech User's</em> password.</dd>
<dd><input type="password" name="_npwd1" /> Enter <em>this user's</em> new password.</dd>
<dd><input type="password" name="_npwd2" /> Enter new password again.</dd>
<dt>Group: </dt><dd><input size="40" type="text" name="group" value="gatech"
disabled="disabled" /></dd>
<dt>Permissions: </dt>
<dd><label><input type="checkbox" name="add" value="checked" checked="checked"
disabled="disabled" /> Add</label></dd>
<dd><label><input type="checkbox" name="change" value="checked" checked="checked"
disabled="disabled" /> Change</label></dd>
<dd><label><input type="checkbox" name="delete" value="checked" checked="checked"
disabled="disabled" /> Delete</label></dd>
<dd><label><input type="checkbox" name="admin" value="checked" checked="checked"
disabled="disabled" /> Admin</label></dd>
<dd><label><input type="checkbox" name="coordinator" value="checked"
disabled="disabled" /> Coordinator</label></dd>
<dd><label><input type="checkbox" name="supervisor" value="checked"
disabled="disabled" /> Supervisor</label></dd>
</dl>
</form>
}}}
!action=update_coll
This action is a request to update the institution's collections file from the institution's local IR.
!!Parameters
;member
:The institution, including ''gkr'', whose collections data we wish to update
!!Operations
* Check that the user is logged in.
* Check that the user is in the institution's group (or group ''all'').
* Get ''merge_url'', ''merge_type'', and ''merge_handle'' (if any) from institution's configuration section.
* Retrieve the ''merge_url'' Web page.
* Depending on ''merge_type'' ("normal dspace", "vtext dspace", or "normal digital commons"), parse the data with that scheme and looping code. (Note that if the ''merge_type'' is "manual", you would never call ''action=update_coll'').
* If the collections data hasn't changed, no nothing (unless {{code{force=1}}}).
* Otherwise (if the data has changed, or if we want to force an update)
** Make a backup of the current data (if this isn't a brand new collections file)
** Save the new collections data
** Display the [[Manage Data Files|MemberManageDataPage]] page (where the "Update Collections" link appears).
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=update_coll;member=valdosta
!action=users
This action is a request to view the [[Users|UsersPage]] page.
!!Parameters
None.
!!Operations
* Check the user is logged in.
* Generate list of users.
* Display [[Users|UsersPage]] page.
!!Notes:
* If the user is a supervisor, all the user names listed will be linked to the [[Edit User|EditUserPage]] page.
* Otherwise, if the user is a coordinator, his own name and the names of non-coordinator users in his instititution group will be linked.
* Otherwise, only the users own name will be linked.
* This linking is just a convenience to allow quick access to those users one may edit.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users
!action=view_coll
This action is a request to view the institution's collections data file.
!!Parameters
;member
:The institution whose collections data we wish to view
!!Operations
* Get the institution's collections data.
* Add line numbers to the data.
* Display the [[View Communities/Collections|MemberManageDataViewPage]] page.
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_coll;member=valdosta
!action=view_file
This action is a request to view a file. The user must be logged in. Some files, like ''program.ini'' and ''members.ini'' require the user to be a supervisor. In any case, only the files that appear on the [[Admin|AdminPage]] may be viewed via this action.
!!Parameters
;file
:The name of the file
;version
:The version of the file, if the file is a backup file
!!Operations
* If the user is not logged in, the [[login|LoginPage]] is displayed.
* Otherwise, the file name is validated (i.e., is it one of the files that show on the [[Admin|AdminPage]] page?)
* If the file is not recognized, the [[error|ErrorPage]] page is displayed, saying "Unrecognized file: 'file_name'"
* If recognized but may only be viewed by supervisors, and the user isn't a supervisor, the [[error|ErrorPage]] page is displayed, saying "Sorry, you may not view that file."
* If a ''version'' is supplied, it is tacked on to the end of the file name to create the backup file name.
* Finally, the [[View File|ViewFilePage]] page is displayed showing the contents of the file.
!!Examples
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_file;file=members.ini
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_file;file=members.ini;version=.20110307.001
!action=view_tree
This action is a request to the institution's mappings data file.
!!Parameters
;member
:The institution whose mappings data we wish to view
!!Operations
* Get the institution's mappings data.
* Add line numbers to the data
* Validate the mappings data, looking for erroneous formatting, handles, names, etc.
* Display the numbered mappings data (and error messages, if any) on the [[View Communities/Collections Mappings|MemberViewMappingsPage]] page.
!!Notes
* The term "tree" should be considered synonymous with the term "mappings".
!!Example
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_tree;member=valdosta
!GKR Mapping Tool Add New User Page
{{thumb{[img[GKR_Add_User_coordinator_thumb.jpg][AddNewUserPageImage]]}}} {{thumb{[img[GKR_Add_User_supervisor_thumb.jpg][AddNewUserPageImage]]}}} [[Images|AddNewUserPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=add_user]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=add_user
!!Page Elements
!!!Admin Users
* Admin level users may not add new users.
!!!Coordinators
* A Coordinator level user may admin users in his own group.
* The coordinator's own permissions and group are used as a template for the new user, and those values are not editable (they're disabled in the form).
* The user templates at the right may be used to jumpstart the new user record with additional information. Clicking one will display the Edit User page for that user. To add a new user, simply change the User ID and any other information available for editing.
* A coordinator will see a link to his own user record and a link to the template record for his group.
!!!Supervisors
* Supervisor level users may add any user in any group with any permissions -- even permissions greater than their own. (A supervisor might choose not to have Add, Change, and Delete privileges, but could still give those permissions to a new user.)
* Like the coordinator, a supervisor sees his own user template at the right, but also sees all of the template records -- both admin level and coordinator level -- for all of the institution groups.
!GKR Mapping Tool Add New User Page Image
!!Coordinator
{{image{[img[GKR_Add_User_coordinator.png]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Supervisor
{{image{[img[GKR_Add_User_supervisor.png]]}}}
!GKR Mapping Tool Admin Page
{{thumb{[img[GKR_Admin_thumb.jpg][AdminPageImage]]}}} [[Image|AdminPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=admin]]
!!Locations
* dev: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=admin
!!Page Elements
* Below the blurb is a list of files that may be viewed and edited.
!!Notes
Only Supervisor level users can use the Admin page. The link, "Admin", will be present in the banner.
Some of the files in the list, e.g., gkr, gkr_coll, asurams, asurams_coll, may be edited by any logged-in user (in the corresponding group) via the [[GKR Manage Data Files|GKRManageDataPage]] and the [[Member Institution Manage Data Files|MemberManageDataPage]] pages.
Some more of the files in the list (not visible in the page image here), e.g., home_blurb, home_help, home_page, may be edited by supervisors via the [[Edit Blurb|EditBlurbPage]], [[Edit Help|EditHelpPage]] and [[Edit Page|EditPagePage]] pages, as discussed above.
For the above files, the Admin page is just a convenience for supervisor users -- one place where everything can be gotten to. But there are some files, e.g., program.ini, members.ini, css, that can only be accessed (online) via the Admin page.
Click "View" to view a file, "Edit" to edit it, and "Changes" to review changes to a file and to recover previous versions.
!!Spellchecking
You'll notice that some terms have a gray background and a dotted red underscore, e.g.,
{{image{[img[GKR_Admin_spellcheck.png]]}}}
This is another convenience: when a supervisor is logged in, every page is run through a spellchecker that highlights words that might be misspelled. The highlighted terms above aren't really misspelled, just unrecognized. There is a dictionary file (.spell) where terms like this can be added so they will no longer be marked as mispelled. But keep in mind that it is only for supervisors that pages are spellchecked.
!GKR Mapping Tool Admin Page Image
{{image{[img[GKR_Admin.png]]}}}
<<forEachTiddler where 'tiddler.tags.contains("Brad Baxter")' sortBy tiddler.title>>
Brad Baxter
bmb@mail.libs.uga.edu
Here we attempt to cover some of the thought behind how the program code is written, with the maintenance programmer in mind.
As described in [[API]], there are two sets of actions, general actions (that don't need to focus on a specific institution) and member actions (that do). These are organized into two dispatch tables (the actions are on the left side and the subroutine names are on the right):
{{{
my %general_actions = (
login => \&login,
logout => \&logout,
admin => \&admin_page,
view_file => \&view_file_page,
edit_file => \&edit_file_page,
save_file => \&save_file,
edit_user => \&edit_user_page,
add_user => \&add_user_page,
save_user => \&save_user,
delete_user => \&delete_user,
changes => \&changes_page,
diff => \&diff_page,
recover => \&recover,
users => \&users_page,
help => \&help_page,
home => \&home_page,
);
my %member_actions = (
view_tree => \&view_tree_page,
edit_tree => \&edit_tree_page,
save_tree => \&save_tree,
view_coll => \&view_coll_page,
edit_coll => \&edit_coll_page,
save_coll => \&save_coll,
manage_data => \&manage_data_page,
update_coll => \&update_coll,
merge_coll => \&merge_coll,
merge_gkr => \&merge_gkr,
map_node => \&map_node_page,
save_node => \&save_node,
publish => \&publish,
);
}}}
When the program is called, it is called with one of those actions, or it is expected to assume a default action. The main code of the program, then, is as follows:
{{{
#---------------------------------------------------------------------
# main
if( my $sub = $general_actions{ $Action } ) { $sub->() }
home_page() unless $Member;
if( my $sub = $member_actions{ $Action } ) { $sub->() }
die "Unrecognized action: '$Action'\n" if $Action;
member_page();
exit;
}}}
In English:
* If the action is recognized as a general action, call that subroutine.
* Otherwise, if there isn't a member passed in, go to the home page.
* Otherwise, if a member is passed in and the action is recognized as a member action, call that subroutine.
* Otherwise, if an action was passed in, it can't be correct, because it wasn't recognized, so display an error message.
* Otherwise, if no action was passed (but a member was) go to the member page
Every subroutine (including {{code{home_page()}}} and {{code{member_page()}}}) is designed to display an HTML page and exit the program, or is designed to call another subroutine that displays a page and exits. So the logic of the program stops at any of the above subroutine calls, and the {{code{exit;}}} above should never be reached. It's there to show that the main code of the program is finished.
!NAME
Config::Ini - Ini configuration file processor
!SYNOPSIS
{{{
use Config::Ini;
my $ini = Config::Ini->new( 'file.ini' );
# traverse the values
for my $section ( $ini->get_sections() ) {
print "$section\n";
for my $name ( $ini->get_names( $section ) ) {
print " $name\n";
for my $value ( $ini->get( $section, $name ) ) {
print " $value\n";
}
}
}
}}}
!VERSION
VERSION: 1.03
!DESCRIPTION
This is an Ini configuration file processor.
!!Terminology
This document uses the terms //comment//, //section//, //name//, and //value// when referring to the following parts of the Ini file syntax:
{{{
# comment
[section]
name = value
}}}
In particular 'name' is the term used to refer to the named options within the sections. This terminology is also reflected in method names, like {{{get_sections()}}} and {{{get_names()}}}.
!!Syntax
!!!The //null section//
At the top of an Ini file, before any sections have been explicitly defined, name/value pairs may be defined. These are assumed to be in the 'null section', as if an explicit {{{[]}}} line were present.
{{{
# before any sections are defined,
# assume section eq '', the "null section"
name = value
name: value
}}}
This 'null section' concept allows for very simple configuration files, e.g.,
{{{
title = Hello World
color: blue
margin: 0
}}}
!!!Comments
Comments may begin with {{{'#'}}} or {{{';'}}}.
{{{
# comments may begin with # or ;, i.e.,
; semicolon is valid comment character
}}}
Comments may begin on a separate line or may follow section headings. But they may not follow values.
{{{
# this is a comment
[section] # this is a comment
name = value # this is NOT a comment (it is part of the value)
}}}
!!!Assignments
Spaces and tabs around the {{{'='}}} and {{{':'}}} assignment characters are stripped, i.e., they are not included in the name or value. Use heredoc syntax to set a value with leading spaces. Trailing spaces in values are left intact.
{{{
[section]
# spaces/tabs around '=' are stripped
# use heredoc to give a value with leading spaces
# trailing spaces are left intact
name=value
name= value
name =value
name = value
name = value
# colon is valid assignment character, too.
name:value
name: value
name :value
name : value
name : value
}}}
!!!Heredocs
Heredoc syntax may be used to assign values that span multiple lines. Heredoc syntax is supported in more ways than just the classic syntax, as illustrated below.
{{{
# classic heredoc:
name = <<heredoc
Heredocs are supported several ways.
This is the "classic" syntax, using a
"heredoc tag" to mark the begin and end.
heredoc
# ... and the following is supported because I kept doing this
name = <<heredoc
value
<<heredoc
# ... and also the following, because often no one cares what it's called
name = <<
value
<<
# ... and finally "block style" (for vi % support)
name = {
value
}
# ... and obscure variations, e.g.,
name = {heredoc
value
heredoc
}}}
That is, the heredoc may begin with {{{'<<'}}} or {{{'{'}}} with or without a tag. And it may then end with {{{'<<'}}} or {{{'}'}}} (with or without a tag, as it began). When a tag is used, the ending {{{'<<'}}} or {{{'}'}}} is optional.
!!!Heredoc :modifiers
There are several ways to modify the value in a heredoc as the Ini file is read in (i.e., as the object is initialized):
{{{
:chomp - chomps the last line
:join - chomps every line BUT the last one
:indented - unindents every line (strips leading whitespace)
:parse - splits on newline (and chomps last line)
:parse(regex) - splits on regex (still chomps last line)
}}}
The {{{':parse'}}} modifier uses {{{Text::ParseWords::parse_line()}}}, so CSV-like parsing is possible.
Modifiers may be stacked, e.g., {{{'<<:chomp:join:indented'}}} (or {{{'<<:chomp :join :indented'}}}), in any order, but note that {{{':parse'}}} is performed last.
{{{
# value is "Line1\nLine2\n"
name = <<
Line1
Line2
<<
# value is "Line1\nLine2"
name = <<:chomp
Line1
Line2
<<
# value is "Line1Line2\n"
name = <<:join
Line1
Line2
<<
# value is "Line1Line2"
name = <<:chomp:join
Line1
Line2
<<
# value is " Line1\n Line2\n"
name = <<
Line1
Line2
<<
# - indentations do NOT have to be regular to be unindented
# - any leading spaces/tabs on every line will be stripped
# - trailing spaces are left intact, as usual
# value is "Line1\nLine2\n"
name = <<:indented
Line1
Line2
<<
# modifiers may have spaces between them
# value is "Line1Line2"
name = << :chomp :join :indented
Line1
Line2
<<
# ... and should come after a heredoc "tag"
# value is "Line1Line2"
name = <<heredoc :chomp :join :indented
Line1
Line2
heredoc
}}}
The :parse modifier splits a single value into multiple values. It may be given with a regular expression parameter to split on other than newline (the default).
{{{
# :parse is same as :parse(\n)
name = <<:parse
value1
value2
<<
}}}
... is the same as
{{{
name = value1
name = value2
}}}
... and
{{{
name = <<:parse(/,\s+/)
"Tom, Dick, and Harry", Fred and Wilma
<<
}}}
... is the same as
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
}}}
The {{{':parse'}}} modifier chomps only the last line, so include {{{'\n'}}} if needed.
{{{
# liberal separators
name = <<:parse([,\s\n]+)
"Tom, Dick, and Harry" "Fred and Wilma"
Martha George, 'Hillary and Bill'
<<
}}}
... is the same as
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
name = Martha
name = George
name = Hillary and Bill
}}}
As illustrated above, the enclosing {{{'/'}}} characters around the regular expression are optional. You may also use matching quotes instead, e.g., {{{:parse('\s')}}}.
Modifiers must follow the heredoc characters {{{'<<'}}} (or {{{'{'}}}). If there is a heredoc tag, e.g., {{{'EOT'}}} below, the modifiers should follow it, too.
{{{
# I want " Hey"
name = <<EOT:chomp
Hey
EOT
}}}
!METHODS
!!Initialization Methods
!!!new()
Calling options:
{{{
new()
new( 'filename' )
new( file => 'filename' )
new( fh => $filehandle )
new( string => $string )
new( string => $string, file => 'filename' )
new( fh => $filehandle, file => 'filename' )
}}}
Use new() to create an object, e.g.,
{{{
my $ini = Config::Ini->new( 'inifile' );
}}}
If you pass any parameters, the {{{init()}}} method will be called. If you pass only one parameter, it's assumed to be the file name. Otherwise, use the named parameters, {{{'file'}}}, {{{'fh'}}}, or {{{'string'}}} to pass a filename, filehandle (already open), or string. The string is assumed to look like the contents of an Ini file.
The parameter, {{{'fh'}}} takes precedent over {{{'string'}}} which takes precedent over {{{'file'}}}. You may pass {{{file => 'filename'}}} with the other parameters to set the {{{'file'}}} attribute.
If you do not pass any parameters to {{{new()}}}, you can later call {{{init()}}} with the same parameters described above.
By default, if you give a filename or string, the module will not specify any encoding, and thus will rely on perl's default behavior. You can change this by setting $Config::Ini::encoding, e.g.,
{{{
$Config::Ini::encoding = "utf8";
my $ini = Config::Ini->new( file => 'filename' );
}}}
Alternatively, you may open the file yourself using the desired encoding and send the filehandle to new() (or init());
Set this to a false value, e.g., {{{''}}} or {{{0}}} to keep the module from specifying any encoding, i.e., to return to the default behavior.
!!!init()
Calling options:
{{{
init( 'filename' )
init( file => 'filename' )
init( fh => $filehandle )
init( string => $string )
init( string => $string, file => 'filename' )
init( fh => $filehandle, file => 'filename' )
}}}
Example:
{{{
my $ini = Config::Ini->new();
$ini->init( 'filename' );
}}}
!!Get Methods
!!!get_sections()
Use {{{get_sections()}}} to retrieve a list of the sections in the Ini file. They are returned in the order they appear in the file.
{{{
my @sections = $ini->get_sections();
}}}
If there is a 'null section', it will be the first in the list.
If a section appears twice in a file, it only appears once in this list. This implies that ...
{{{
[section1]
name1 = value
[section2]
name2 = value
[section1]
name3 = value
}}}
is the same as ...
{{{
[section1]
name1 = value
name3 = value
[section2]
name2 = value
}}}
!!!get_names()
Calling options:
{{{
get_names( $section )
get_names( '' )
get_names()
}}}
Use {{{get_names()}}} to retrieve a list of the names in a given section.
{{{
my @names = $ini->get_names( $section );
}}}
They are returned in the order they appear in the section.
If a name appears twice in a section, it only appears once in this list. This implies that ...
{{{
[section]
name1 = value1
name2 = value2
name1 = another
}}}
is the same as ...
{{{
[section]
name1 = value1
name1 = another
name2 = value2
}}}
Calling {{{get_names()}}} without a parameter is the same as calling it with a null string: it retrieves the names from the 'null section'. The two lines below are equivalent.
{{{
@names = $ini->get_names();
@names = $ini->get_names( '' );
}}}
!!!get()
Calling options:
{{{
get( $section, $name )
get( $section, $name, $i )
get( $name )
get( '', $name, $i )
}}}
Use {{{get()}}} to retrieve the value or values for a given name.
Note: when an Ini object is initialized, if a name appears more than once in a section, the values are pushed onto an array, and {{{get()}}} will return this array of values.
{{{
my @values = $ini->get( $section, $name );
}}}
Pass an array subscript as the third parameter to return only one of the values in this array.
{{{
my $value = $ini->get( $section, $name, 0 ); # get first one
my $value = $ini->get( $section, $name, 1 ); # get second one
my $value = $ini->get( $section, $name, -1 ); # get last one
}}}
If the Ini file lists names at the beginning, before any sections are given, the section name is assumed to be a null string ({{{''}}}). If you call {{{get()}}} with just one parameter, it is assumed to be a name in this 'null section'. If you want to pass an array subscript, then you must also pass a null string as the first parameter.
{{{
my @values = $ini->get( $name ); # assumes $section eq ''
my $value = $ini->get( '', $name, 0 ); # get first occurrence
my $value = $ini->get( '', $name, -1 ); # get last occurrence
}}}
This "null section" concept allows for very simple configuration files like:
{{{
title = Hello World
color: blue
margin: 0
}}}
!!Add/Set/Put Methods
Here, //add// denotes pushing values onto the end, //set//, modifying a single value, and //put//, replacing all values at once.
!!!add()
Calling options:
{{{
add( $section, $name, @values )
add( '', $name, @values )
}}}
Use {{{add()}}} to add to the value or values of an option. If the option already has values, the new values will be added to the end (pushed onto the array).
{{{
$ini->add( $section, $name, @values );
}}}
To add to the 'null section', pass a null string.
{{{
$ini->add( '', $name, @values );
}}}
!!!set()
Calling options:
{{{
set( $section, $name, $i, $value )
set( '', $name, $i, $value )
}}}
Use {{{set()}}} to assign a single value. Pass a value of {{{undef}}} to remove a value altogether. The {{{$i}}} parameter is the subscript of the values array to assign to (or remove).
{{{
$ini->set( $section, $name, -1, $value ); # set last value
$ini->set( $section, $name, 0, undef ); # remove first value
}}}
To set a value in the 'null section', pass a null string.
{{{
$ini->set( '', $name, 1, $value ); # set second value
}}}
!!!put()
Calling options:
{{{
put( $section, $name, @values )
put( '', $name, @values )
}}}
Use {{{put()}}} to assign all values at once. Any existing values are overwritten.
{{{
$ini->put( $section, $name, @values );
}}}
To put values in the 'null section', pass a null string.
{{{
$ini->put( '', $name, @values );
}}}
!!Delete Methods
!!!delete_section()
Calling options:
{{{
delete_section( $section )
delete_section( '' )
delete_section()
}}}
Use {{{delete_section()}}} to delete an entire section, including all of its options and their values.
{{{
$ini->delete_section( $section )
}}}
To delete the 'null section', don't pass any parameters or pass a null string.
{{{
$ini->delete_section();
$ini->delete_section( '' );
}}}
!!!delete_name()
Calling options:
{{{
delete_name( $section, $name )
delete_name( '', $name )
delete_name( $name )
}}}
Use {{{delete_name()}}} to delete a named option and all of its values from a section.
{{{
$ini->delete_name( $section, $name );
}}}
To delete an option from the 'null section', pass just the name, or pass a null string.
{{{
$ini->delete_name( $name );
$ini->delete_name( '', $name );
}}}
To delete just some of the values, you can use {{{set()}}} with a subscript, passing {{{undef}}} to delete each one. Or you can first get them into an array using {{{get()}}}, modify them in that array (e.g., delete some), and then use {{{put()}}} to replace the old values with the modified ones.
!!Other Accessor Methods
!!!file()
Calling options:
{{{
file()
file( $filename )
file( undef )
}}}
Use {{{file()}}} to get or set the {{{'file'}}} object attribute, which is intended to be the filename of the Ini file from which the object was created.
{{{
$inifile = $ini->file(); # get the name of the file
}}}
If {{{$value}}} is not given, {{{file()}}} returns the value of the {{{'file'}}} attribute. If {{{$value}}} is defined, {{{'file'}}} is set to {{{$value}}}. If {{{$value}}} is given but is {{{undef}}}, the {{{'file'}}} attribute is removed.
{{{
$ini->file(); # return the file name
$ini->file( 'myfile.ini' ); # change the file name
$ini->file( undef ); # remove the file attribute
}}}
!SEE ALSO
Config::Ini::Edit, Config::Ini::Expanded, Config::Ini::Quote, Config::~IniFiles, Config:: ... (many others)
!AUTHOR, COPYRIGHT, AND LICENSE
Brad Baxter, <bmb@mail.libs.uga.edu>
Copyright (C) 2010 by Brad Baxter
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available.
''Page 1'' | [[Page 2|Config::Ini::Edit Page 2]] | [[Next Page|Config::Ini::Edit Page 2]]
!NAME
Config::Ini::Edit - Ini configuration file reader and writer
!SYNOPSIS
{{{
use Config::Ini::Edit;
my $ini = Config::Ini::Edit->new( 'file.ini' );
# traverse the values
for my $section ( $ini->get_sections() ) {
print "$section\n";
for my $name ( $ini->get_names( $section ) ) {
print " $name\n";
for my $value ( $ini->get( $section, $name ) ) {
print " $value\n";
}
}
}
# rewrite the file
my $inifile = $ini->file();
open INI, '>:encoding(utf8)', $inifile or croak "Can't open $inifile: $!";
print INI $ini->as_string();
close INI;
}}}
!VERSION
VERSION: 1.03
!DESCRIPTION
This is an Ini configuration file processor. This class inherits from Config::Ini. It uses that module as well as Config::Ini::Quote, Text::~ParseWords and JSON;
!!Terminology
This document uses the terms //comment//, //section//, //name//, and //value// when referring to the following parts of the Ini file syntax:
{{{
# comment
[section]
name = value
}}}
In particular 'name' is the term used to refer to the named options within the sections. This terminology is also reflected in method names, like {{{get_sections()}}} and {{{get_names()}}}.
!!Syntax
!!!The //null section//
At the top of an Ini file, before any sections have been explicitly defined, name/value pairs may be defined. These are assumed to be in the 'null section', as if an explicit {{{[]}}} line were present.
{{{
# before any sections are defined,
# assume section eq '', the "null section"
name = value
name: value
}}}
This 'null section' concept allows for very simple configuration files, e.g.,
{{{
title = Hello World
color: blue
margin: 0
}}}
!!!Comments
Comments may begin with {{{'#'}}} or {{{';'}}}.
{{{
# comments may begin with # or ;, i.e.,
; semicolon is valid comment character
}}}
Comments may begin on a separate line or may follow section headings. Comments may not follow unquoted values.
{{{
# this is a comment
[section] # this is a comment
name = value # this is NOT a comment (it is part of the value)
}}}
But comments may follow quoted values.
{{{
# comments are allowed after quoted values
name = 'value' # this is a comment
name = "value" # this is a comment
}}}
!!!Assignments
Spaces and tabs around the {{{'='}}} and {{{':'}}} assignment characters are stripped, i.e., they are not included in the name or value. Use heredoc syntax to set a value with leading spaces. Trailing spaces in values are left intact.
{{{
[section]
# spaces/tabs around '=' are stripped
# use heredoc to give a value with leading spaces
# trailing spaces are left intact
name=value
name= value
name =value
name = value
name = value
# colon is valid assignment character, too.
name:value
name: value
name :value
name : value
name : value
}}}
!!!Heredocs
Heredoc syntax may be used to assign values that span multiple lines. Heredoc syntax is supported in more ways than just the classic syntax, as illustrated below.
{{{
# classic heredoc:
name = <<heredoc
Heredocs are supported several ways.
This is the "classic" syntax, using a
"heredoc tag" to mark the begin and end.
heredoc
# ... and the following is supported because I kept doing this
name = <<heredoc
value
<<heredoc
# ... and also the following, because often no one cares what it's called
name = <<
value
<<
# ... and finally "block style" (for vi % support)
name = {
value
}
# ... and obscure variations, e.g.,
name = {heredoc
value
heredoc
}}}
That is, the heredoc may begin with {{{'<<'}}} or {{{'{'}}} with or without a tag. And it may then end with {{{'<<'}}} or {{{'}'}}} (with or without a tag, as it began). When a tag is used, the ending {{{'<<'}}} or {{{'}'}}} is optional.
!!!Quoted Values
Values may be put in single or double quotes.
Single-quoted values will be parsed literally, except that imbedded single quotes must be escaped by doubling them, e.g.,
{{{
name = 'The ties that bind.'
$name = $ini->get( section => 'name' );
# $name eq "The ties that bind."
name = 'The ''ties'' that ''bind.'''
$name = $ini->get( section => 'name' );
# $name eq "The 'ties' that 'bind.'"
}}}
This uses {{{Config::Ini::Quote::parse_single_quoted()}}}.
Double-quoted values may be parsed a couple of different ways. By default, backslash-escaped unprintable characters will be unescaped to their actual Unicode character. This includes ascii control characters like {{{\n}}}, {{{\t}}}, etc., Unicode character codes like {{{\N}}} (Unicode next line), {{{\P}}} (Unicode paragraph separator), and hex-value escape sequences like {{{\x86}}} and {{{\u263A}}}.
If the {{{':html'}}} heredoc modifier is used (see Heredoc Modifiers below), then HTML entities will be decoded (using HTML::Entities) to their actual Unicode characters.
This uses {{{Config::Ini::Quote::parse_double_quoted()}}}.
See Config::Ini:Quote for more details.
!!!Heredoc :modifiers
There are several ways to modify the value in a heredoc as the Ini file is read in (i.e., as the object is initialized):
{{{
:chomp - chomps the last line
:join - chomps every line BUT the last one
:indented - unindents every line (strips leading whitespace)
:parse - splits on newline (and chomps last line)
:parse(regex) - splits on regex (still chomps last line)
:slash - unescapes backslash-escaped characters in double quotes (default)
:html - decodes HTML entities in double quotes
:json - parses javascript object notation (complex data types)
}}}
The {{{':parse'}}} modifier uses {{{Text::ParseWords::parse_line()}}}, so CSV-like parsing is possible.
The {{{':json'}}} modifier uses the JSON module to parse and dump complex data types (combinations of hashes, arrays, scalars). The value of the heredoc must be valid ~JavaScript Object Notation.
The {{{':slash'}}} and {{{':html'}}} modifiers are only valid when double quotes are used (surrounding the heredoc tag and modifiers). If no modifiers are given with double quotes, {{{':slash'}}} is the default.
{{{
name = <<"EOT :html"
vis-à-vis
EOT
name = <<"EOT"
\tSmiley: \u263A
EOT
}}}
Modifiers may be stacked, e.g., {{{'<<:chomp:join:indented'}}} (or {{{'<<:chomp :join :indented'}}}), in any order, but note that {{{':parse'}}} and {{{':json'}}} are performed last.
{{{
# value is "Line1\nLine2\n"
name = <<
Line1
Line2
<<
# value is "Line1\nLine2"
name = <<:chomp
Line1
Line2
<<
# value is "Line1Line2\n"
name = <<:join
Line1
Line2
<<
# value is "Line1Line2"
name = <<:chomp:join
Line1
Line2
<<
# value is " Line1\n Line2\n"
name = <<
Line1
Line2
<<
# - indentations do NOT have to be regular to be unindented
# - any leading spaces/tabs on every line will be stripped
# - trailing spaces are left intact, as usual
# value is "Line1\nLine2\n"
name = <<:indented
Line1
Line2
<<
# modifiers may have spaces between them
# value is "Line1Line2"
name = << :chomp :join :indented
Line1
Line2
<<
# ... and should come after a heredoc "tag"
# value is "Line1Line2"
name = <<heredoc :chomp :join :indented
Line1
Line2
heredoc
}}}
The {{{':parse'}}} modifier splits a single value into multiple values. It may be given with a regular expression parameter to split on other than newline (the default).
{{{
# :parse is same as :parse(\n)
name = <<:parse
value1
value2
<<
}}}
... is the same as
{{{
name = value1
name = value2
}}}
... and
{{{
name = <<:parse(/,\s+/)
"Tom, Dick, and Harry", Fred and Wilma
<<
}}}
... is the same as
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
}}}
The {{{':parse'}}} modifier chomps only the last line, so include {{{'\n'}}} if needed.
{{{
# liberal separators
name = <<:parse([,\s\n]+)
"Tom, Dick, and Harry" "Fred and Wilma"
Martha George, 'Hillary and Bill'
<<
}}}
... is the same as,
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
name = Martha
name = George
name = Hillary and Bill
name = <<:json
{ a: 1, b: 2, c: 3 }
<<
}}}
Given the above {{{':json'}}} example, {{{$ini->get( 'name' )}}} should return a hashref. Note that we accept bare hash keys ({{{$JSON::BareKey=1;}}}).
As illustrated above, the enclosing {{{'/'}}} characters around the regular expression are optional. You may also use matching quotes instead, e.g., {{{:parse('\s')}}}.
Modifiers must follow the heredoc characters {{{'<<'}}} (or {{{'{'}}}). If there is a heredoc tag, e.g., {{{'EOT'}}} below, the modifiers should follow it, too.
{{{
name = <<EOT:json
{ a: 1, b: 2, c: 3 }
EOT
}}}
If you want to use single or double quotes, surround the heredoc tag and modifiers with the appropriate quotes:
{{{
name = <<'EOT :indented'
line1
line2
EOT
name = <<"EOT :html"
vis-à-vis
EOT
}}}
Note, in heredocs, embedded single and double quotes do not have to be (and should not be) escaped. In other words leave single quotes as {{{"'"}}} (not {{{"''"}}}), and leave double quotes as {{{'"'}}} (not {{{'\"'}}}).
{{{
name = <<'EOT :indented'
'line1'
'line2'
EOT
# $name eq "'line1'\n'line2'\n"
$name = $ini->get( 'name' );
name = <<"EOT :html"
"vis-à-vis"
EOT
# $name eq qq{"vis-\xE0-vis"}
$name = $ini->get( 'name' );
}}}
If no heredoc tag is used, put the quotes around the modifiers.
{{{
name = <<":html"
vis-à-vis
<<
}}}
If no modifiers either, just use empty quotes.
{{{
name = <<""
vis-\xE0-vis
<<
}}}
Comments are allowed on the assignment line if quotes are used.
{{{
name = <<'EOT :indented' # this is a comment
line1
line2
EOT
}}}
But note:
{{{
name = <<EOT
'Line1' # this is NOT a comment
EOT
}}}
!GLOBAL SETTINGS
Note: the global settings below are stored in the object during {{{init()}}}. So if the global settings are subsequently changed, any existing objects will not be affected.
* $Config::Ini::Edit::keep_comments This boolean value will determine if comments are kept when an Ini file is loaded or when an Ini object is written out using {{{as_string()}}}. The default is true -- comments are kept. The rational is this: The {{{Edit}}} module is designed to allow you to read, edit, and rewrite Ini files. If a file contains comments to start with, you probably want to keep them.
* $Config::Ini::Edit::heredoc_style This string can be one of {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}} (default is {{{'<<'}}}). This determines the default heredoc style when the object is written out using {{{as_string()}}}. If a value was read in originally from a heredoc, it will be written out using that heredoc style, not this default style. The above values correspond respectively to the following styles.
{{{
# '<<'
name = <<EOT
Hey
EOT
# '<<<<'
name = <<EOT
Hey
<<EOT
# '{'
name = {EOT
Hey
EOT
# '{}'
name = {EOT
Hey
}EOT
}}}
''Page 1'' | [[Page 2|Config::Ini::Edit Page 2]] | [[Next Page|Config::Ini::Edit Page 2]]
[[Previous Page|Config::Ini::Edit]] | [[Page 1|Config::Ini::Edit]] | ''Page 2''
!METHODS
!!Initialization Methods
!!!new()
Calling options:
{{{
new( 'filename' )
new( file => 'filename' )
new( fh => $filehandle )
new( string => $string )
new( string => $string, file => 'filename' )
new( fh => $filehandle, file => 'filename' )
new( file => 'filename', keep_comments => 0 )
new( file => 'filename', heredoc_style => '{}' ), etc.
}}}
Use {{{new()}}} to create an object, e.g.,
{{{
my $ini = Config::Ini::Edit->new( 'inifile' );
}}}
If you pass any parameters, the {{{init()}}} method will be called. If you pass only one parameter, it's assumed to be the file name. Otherwise, use the named parameters, {{{'file'}}}, {{{'fh'}}}, or {{{'string'}}} to pass a filename, filehandle (already open), or string. The string is assumed to look like the contents of an Ini file.
The parameter, {{{'fh'}}} takes precedent over {{{'string'}}} which takes precedent over {{{'file'}}}. You may pass {{{file => 'filename'}}} with the other parameters to set the {{{'file'}}} attribute.
Other parameters are {{{'keep_comments'}}} and {{{'heredoc_style'}}} to override the defaults, true and {{{'<<'}}}, respectively. The values accepted for heredoc_style are {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}}.
If you do not pass any parameters to {{{new()}}}, you can later call {{{init()}}} with the same parameters described above.
By default, if you give a filename or string, the module will not specify any encoding, and thus will rely on perl's default behavior. You can change this by setting $Config::Ini::encoding, e.g.,
{{{
$Config::Ini::encoding = "utf8";
my $ini = Config::Ini->new( file => 'filename' );
}}}
Alternatively, you may open the file yourself using the desired encoding and send the filehandle to new() (or init());
Set this to a false value, e.g., {{{''}}} or {{{0}}} to keep the module from specifying any encoding, i.e., to return to the default behavior.
!!!init()
Calling options:
{{{
init( 'filename' )
init( file => 'filename' )
init( fh => $filehandle )
init( string => $string )
init( string => $string, file => 'filename' )
init( fh => $filehandle, file => 'filename' )
init( file => 'filename', keep_comments => 0 )
init( file => 'filename', heredoc_style => '{}' ), etc.
}}}
Example:
{{{
my $ini = Config::Ini::Edit->new();
$ini->init( 'filename' );
}}}
!!Get Methods
!!!get_sections()
Use {{{get_sections()}}} to retrieve a list of the sections in the Ini file. They are returned in the order they appear in the file.
{{{
my @sections = $ini->get_sections();
}}}
If there is a 'null section', it will be the first in the list.
If a section appears twice in a file, it only appears once in this list. This implies that ...
{{{
[section1]
name1 = value
[section2]
name2 = value
[section1]
name3 = value
}}}
is the same as ...
{{{
[section1]
name1 = value
name3 = value
[section2]
name2 = value
}}}
The {{{as_string()}}} method will output the latter.
!!!get_names()
Calling options:
{{{
get_names( $section )
get_names( '' )
get_names()
}}}
Use {{{get_names()}}} to retrieve a list of the names in a given section.
{{{
my @names = $ini->get_names( $section );
}}}
They are returned in the order they appear in the section.
If a name appears twice in a section, it only appears once in this list. This implies that ...
{{{
[section]
name1 = value1
name2 = value2
name1 = another
}}}
is the same as ...
{{{
[section]
name1 = value1
name1 = another
name2 = value2
}}}
The {{{as_string()}}} method will output the latter.
Calling {{{get_names()}}} without a parameter is the same as calling it with a null string: it retrieves the names from the 'null section'. The two lines below are equivalent.
{{{
@names = $ini->get_names();
@names = $ini->get_names( '' );
}}}
!!!get()
Calling options:
{{{
get( $section, $name )
get( $section, $name, $i )
get( $name ) (assumes $section eq '')
get( '', $name, $i )
}}}
Use {{{get()}}} to retrieve the value or values for a given name.
Note: when an Ini object is initialized, if a name appears more than once in a section, the values are pushed onto an array, and {{{get()}}} will return this array of values.
{{{
my @values = $ini->get( $section, $name );
}}}
Pass an array subscript as the third parameter to return only one of the values in this array.
{{{
my $value = $ini->get( $section, $name, 0 ); # get first one
my $value = $ini->get( $section, $name, 1 ); # get second one
my $value = $ini->get( $section, $name, -1 ); # get last one
}}}
If the Ini file lists names at the beginning, before any sections are given, the section name is assumed to be a null string ({{{''}}}). If you call {{{get()}}} with just one parameter, it is assumed to be a name in this 'null section'. If you want to pass an array subscript, then you must also pass a null string as the first parameter.
{{{
my @values = $ini->get( $name ); # assumes $section eq ''
my $value = $ini->get( '', $name, 0 ); # get first occurrence
my $value = $ini->get( '', $name, -1 ); # get last occurrence
}}}
!!Add/Set/Put Methods
Here, //add// denotes pushing values onto the end, //set//, modifying a single value, and //put//, replacing all values at once.
!!!add()
Calling options:
{{{
add( $section, $name, @values )
add( '', $name, @values )
}}}
Use {{{add()}}} to add to the value or values of an option. If the option already has values, the new values will be added to the end (pushed onto the array).
{{{
$ini->add( $section, $name, @values );
}}}
To add to the 'null section', pass a null string.
{{{
$ini->add( '', $name, @values );
}}}
!!!set()
Calling options:
{{{
set( $section, $name, $i, $value )
set( '', $name, $i, $value )
}}}
Use {{{set()}}} to assign a single value. Pass {{{undef}}} to remove a value altogether. The {{{$i}}} parameter is the subscript of the values array to assign to (or remove).
{{{
$ini->set( $section, $name, -1, $value ); # set last value
$ini->set( $section, $name, 0, undef ); # remove first value
}}}
To set a value in the 'null section', pass a null string.
{{{
$ini->set( '', $name, 1, $value ); # set second value
}}}
!!!put()
Calling options:
{{{
put( $section, $name, @values )
put( '', $name, @values )
}}}
Use {{{put()}}} to assign all values at once. Any existing values are overwritten.
{{{
$ini->put( $section, $name, @values );
}}}
To put values in the 'null section', pass a null string.
{{{
$ini->put( '', $name, @values );
}}}
!!Delete Methods
!!!delete_section()
Calling options:
{{{
delete_section( $section )
delete_section( '' )
delete_section()
}}}
Use {{{delete_section()}}} to delete an entire section, including all of its options and their values.
{{{
$ini->delete_section( $section )
}}}
To delete the 'null section', don't pass any parameters or pass a null string.
{{{
$ini->delete_section();
$ini->delete_section( '' );
}}}
!!!delete_name()
Calling options:
{{{
delete_name( $section, $name )
delete_name( '', $name )
delete_name( $name )
}}}
Use {{{delete_name()}}} to delete a named option and all of its values from a section.
{{{
$ini->delete_name( $section, $name );
}}}
To delete an option from the 'null section', pass just the name, or pass a null string.
{{{
$ini->delete_name( $name );
$ini->delete_name( '', $name );
}}}
To delete just some of the values, you can use {{{set()}}} with a subscript, passing {{{undef}}} to delete each one. Or you can first get them into an array using {{{get()}}}, modify them in that array (e.g., delete some), and then use {{{put()}}} to replace the old values with the modified ones.
!!Other Accessor Methods
!!!file()
Calling options:
{{{
file()
file( $filename )
file( undef )
}}}
Use {{{file()}}} to get or set the name of the object's Ini file. Pass the file name to set the value. Pass {{{undef}}} to remove the {{{'file'}}} attribute altogether.
{{{
$inifile_name = $ini->file(); # get
$ini->file( $inifile_name ); # set
$ini->file( undef ); # remove
}}}
!!!keep_comments()
Calling options:
{{{
keep_comments()
keep_comments( $boolean )
}}}
Use {{{keep_comments()}}} to get or set the object's {{{'keep_comments'}}} attribute. The default for this attribute is true, i.e., do keep comments. Pass a false value to turn comments off.
{{{
$boolean = $ini->keep_comments(); # get
$ini->keep_comments( $boolean ); # set
}}}
Note that {{{keep_comments()}}} accesses the value of the flag that is stored in the object -- not the value of the global setting.
!!!heredoc_style()
Calling options:
{{{
heredoc_style()
heredoc_style( $style )
}}}
Use {{{heredoc_style()}}} to get or set the default style used when heredocs are rendered by {{{as_string()}}}.
{{{
$style = $ini->heredoc_style(); # get
$ini->heredoc_style( $style ); # set
}}}
The value passed should be one of {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}}. The default is {{{'<<'}}}.
Note that {{{heredoc_style()}}} accesses the value of the style that is stored in the object -- not the value of the global setting.
See also {{{init()}}} and GLOBAL SETTINGS above.
!!!vattr( $section, $name, $i, $attribute, $value, ... )
Use {{{vattr()}}} to get or set value-level attributes, which include:
{{{
heretag : 'string'
herestyle : ( {, {}, <<, or <<<< )
quote : ( ', ", s, d, single, or double )
nquote : ( ', ", s, d, single, or double )
equals : ( '=', ':', ' = ', ': ', etc. )
escape : ( :slash and/or :html )
indented : indentation value (white space)
json : boolean
comment : 'string'
}}}
If {{{$i}}} is undefined, {{{0}}} is assumed. If there's an {{{$attribute}}}, but no {{{$value}}}, the value of that attribute is returned.
{{{
$value = $ini->vattr( $section, $name, 0, 'heretag' ); # get one
}}}
If no {{{$attribute}}} is given, {{{vattr()}}} returns all of the attribute names and values as a list (in pairs).
{{{
%attrs = $ini->vattr( $section, $name, 1 ); # get all
}}}
If {{{$attribute}}} is a hashref, values are set from that hash.
{{{
%ah = ( heretag=>'EOT', herestyle=>'{}' );
$ini->vattr( $section, $name, 1, \%ah );
}}}
Otherwise, attributes and values may be passed as named parameters.
{{{
$ini->vattr( $section, $name, 1, heretag=>'EOT', herestyle=>'{}' );
}}}
These value attributes are used to replicate the ini file when {{{as_string()}}} is called.
The attributes {{{'escape'}}}, {{{'indented'}}}, and {{{'json'}}} correspond to the similarly named heredoc modifiers; see {{{Heredoc Modifiers}}} above. The values of {{{'heretag'}}}, {{{'herestyle'}}}, and {{{'quote'}}} are used to begin and end the heredoc. Additionally, if double quotes are called for, characters in the value will be escaped according to the {{{'escape'}}} value.
The value of {{{'comment'}}} will be appended after the value, or if a heredoc, after the beginning of the heredoc. Note that the comment may also be accessed using {{{set_comment()}}} and {{{get_comment()}}}. See below.
The value of {{{'equals'}}} will be output between the name and value, e.g., {{{' = '}}} in {{{'name = value'}}}. The setting, {{{'nquote'}}}, is to the name what {{{'quote'}}} is to the value, i.e., if {{{"'"}}}, the name will be single quoted, if {{{'"'}}}, double quoted.
!!Comments Accessor Methods
An Ini file may contain comments. Normally, when your program reads an Ini file, it doesn't care about comments. But if you want to edit an Ini file using the Config::Ini::Edit module, you will want to keep the comments.
Set {{{$Config::Ini::Edit::keep_comments = 0;}}} if you do not want the Config::Ini::Edit object to retain the comments that are in the file. The default is {{{1}}} -- comments are kept. This applies to {{{new()}}}, {{{init()}}}, and {{{as_string()}}}, i.e., {{{new()}}} and {{{init()}}} will load the comments into the object, and {{{as_string()}}} will output these comments.
Or you can pass the {{{'keep_comments'}}} parameter to the {{{new()}}} or {{{init()}}} methods as described above.
!!!get_comments( $section, $name, $i )
Use {{{get_comments()}}} to return the comments that appear ''above'' a certain name. Since names may be repeated (forming an array of values), pass an array index ({{{$i}}}) to identify the comment desired. If {{{$i}}} is undefined, {{{0}}} is assumed.
{{{
my $comments = $ini->get_comments( $section, $name );
}}}
!!!get_comment( $section, $name, $i )
Use {{{get_comment()}}} (singular) to return the comments that appear ''on'' the same line as a certain name's assignment. Pass an array index ({{{$i}}}) to identify the comment desired. If {{{$i}}} is undefined, {{{0}}} is assumed.
{{{
$comment = $ini->get_comment( $section, $name );
}}}
!!!set_comments( $section, $name, $i, @comments )
Use {{{set_comments()}}} to specify comments for a given occurrence of a name. When {{{as_string()}}} is called, these comments will appear ''above'' the name.
{{{
$ini->set_comments( $section, $name, 0, 'Hello World' );
}}}
In an Ini file, comments must begin with {{{'#'}}} or {{{';'}}} and end with a newline. If your comments don't, {{{'# '}}} and {{{"\n"}}} will be added.
!!!set_comment( $section, $name, $i, @comments )
Use {{{set_comment()}}} to specify comments for a given occurrence of a name. When {{{as_string()}}} is called, these comments will appear ''on'' the same line as the name's assignment.
{{{
$ini->set_comment( $section, $name, 0, 'Hello World' );
}}}
In an Ini file, comments must begin with {{{'#'}}} or {{{';'}}}. If your comments don't, {{{'# '}}} will be added. If you pass an array of comments, they will be strung together on one line.
!!!get_section_comments( $section )
Use {{{get_section_comments()}}} to retrieve the comments that appear ''above'' the {{{[section]}}} line, e.g.,
{{{
# Comment 1
[section] # Comment 2
# $comments eq "# Comment 1\n"
my $comments = $ini->get_section_comments( $section );
}}}
!!!get_section_comment( $section )
Use {{{get_section_comment()}}} (note: singular 'comment') to retrieve the comment that appears ''on'' the same line as the {{{[section]}}} line.
{{{
# Comment 1
[section] # Comment 2
# $comment eq " # Comment 2\n"
my $comment = $ini->get_section_comment( $section );
}}}
!!!set_section_comments( $section, @comments )
Use {{{set_section_comments()}}} to set the value of the comments above the {{{[section]}}} line.
{{{
$ini->set_section_comments( $section, $comments );
}}}
!!!set_section_comment( $section, @comments )
Use {{{set_section_comment()}}} (singular) to set the value of the comment at the end of the {{{[section]}}} line.
{{{
$ini->set_section_comment( $section, $comment );
}}}
!!Recreating the Ini File Structure
!!!as_string()
Use {{{as_string()}}} to dump the Config::Ini::Edit object in an Ini file format. If {{{$Config::Ini::Edit::keep_comments}}} is true, the comments will be included.
{{{
print INIFILE $ini->as_string();
}}}
The value {{{as_string()}}} returns is not guaranteed to be exactly what was in the original Ini file. But you can expect the following:
- All sections and names will be retained.
- All values will resolve correctly, i.e., a call to {{{get()}}} will return the expected value.
- All comments will be present (if {{{'keep_comments'}}} is true).
- As many value attributes as possible will be retained, e.g., quotes, escapes, indents, etc. But the {{{':parse'}}} modifier will ''not'' be retained.
- If the same section appears multiple times in a file, all of its options will be output in only one occurrence of that section, in the position of the original first occurrence. E.g.,
{{{
[section1]
name1 = value
[section2]
name2 = value
[section1]
name3 = value
}}}
will be output as
{{{
[section1]
name1 = value
name3 = value
[section2]
name2 = value
}}}
(Note that as_string() inserts a blank line between sections if there is not a comment there.)
- If the same name appears multiple times in a section, all of its occurrences will be grouped together, at the same position as the first occurrence. E.g.,
{{{
[section]
name1 = value1
name2 = value2
name1 = another
}}}
will be output as
{{{
[section]
name1 = value1
name1 = another
name2 = value2
}}}
!SEE ALSO
Config::Ini, Config::Ini::Quote, Config::Ini::Expanded, Config::~IniFiles, Config:: ... (many others)
!AUTHOR
Brad Baxter, <bmb@mail.libs.uga.edu>
!COPYRIGHT AND LICENSE
Copyright (C) 2010 by Brad Baxter
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available.
[[Previous Page|Config::Ini::Edit]] | [[Page 1|Config::Ini::Edit]] | ''Page 2''
''Page 1'' | [[Page 2|Config::Ini::Expanded Page 2]] | [[Page 3|Config::Ini::Expanded Page 3]] | [[Next Page|Config::Ini::Expanded Page 2]]
!NAME
Config::Ini::Expanded - Ini configuration file reading/writing with template expansion capabilities.
!SYNOPSIS
{{{
use Config::Ini::Expanded;
my $ini = Config::Ini::Expanded->new( 'file.ini' );
# traverse the values
for my $section ( $ini->get_sections() ) {
print "$section\n";
for my $name ( $ini->get_names( $section ) ) {
print " $name\n";
for my $value ( $ini->get( $section, $name ) ) {
print " $value\n";
}
}
}
}}}
!VERSION
VERSION: 1.11
!DESCRIPTION
This is an Ini configuration file processor. This class inherits from Config::Ini::Edit (and Config::Ini). It uses those modules as well as Config::Ini::Quote, Text::~ParseWords and JSON;
!!Terminology
This document uses the terms //comment//, //section//, //name//, and //value// when referring to the following parts of the Ini file syntax:
{{{
# comment
[section]
name = value
}}}
In particular 'name' is the term used to refer to the named options within the sections. This terminology is also reflected in method names, like {{{get_sections()}}} and {{{get_names()}}}.
!!Syntax
!!!The //null section//
At the top of an Ini file, before any sections have been explicitly defined, name/value pairs may be defined. These are assumed to be in the 'null section', as if an explicit {{{[]}}} line were present.
{{{
# before any sections are defined,
# assume section eq '', the "null section"
name = value
name: value
}}}
This 'null section' concept allows for very simple configuration files, e.g.,
{{{
title = Hello World
color: blue
margin: 0
}}}
!!!Comments
Comments may begin with {{{'#'}}} or {{{';'}}}.
{{{
# comments may begin with # or ;, i.e.,
; semicolon is valid comment character
}}}
Comments may begin on a separate line or may follow section headings. Comments may not follow unquoted values.
{{{
# this is a comment
[section] # this is a comment
name = value # this is NOT a comment (it is part of the value)
}}}
But comments may follow quoted values.
{{{
# comments are allowed after quoted values
name = 'value' # this is a comment
name = "value" # this is a comment
}}}
!!!Assignments
Spaces and tabs around the {{{'='}}} and {{{':'}}} assignment characters are stripped, i.e., they are not included in the name or value. Use heredoc syntax to set a value with leading spaces. Trailing spaces in values are left intact.
{{{
[section]
# spaces/tabs around '=' are stripped
# use heredoc to give a value with leading spaces
# trailing spaces are left intact
name=value
name= value
name =value
name = value
name = value
# colon is valid assignment character, too.
name:value
name: value
name :value
name : value
name : value
}}}
!!!Heredocs
Heredoc syntax may be used to assign values that span multiple lines. Heredoc syntax is supported in more ways than just the classic syntax, as illustrated below.
{{{
# classic heredoc:
name = <<heredoc
Heredocs are supported several ways.
This is the "classic" syntax, using a
"heredoc tag" to mark the begin and end.
heredoc
# ... and the following is supported because I kept doing this
name = <<heredoc
value
<<heredoc
# ... and also the following, because often no one cares what it's called
name = <<
value
<<
# ... and finally "block style" (for vi % support)
name = {
value
}
# ... and obscure variations, e.g.,
name = {heredoc
value
heredoc
}}}
That is, the heredoc may begin with {{{'<<'}}} or {{{'{'}}} with or without a tag. And it may then end with {{{'<<'}}} or {{{'}'}}} (with or without a tag, as it began). When a tag is used, the ending {{{'<<'}}} or {{{'}'}}} is optional.
!!!Quoted Values
Values may be put in single or double quotes.
Single-quoted values will be parsed literally, except that imbedded single quotes must be escaped by doubling them, e.g.,
{{{
name = 'The ties that bind.'
$name = $ini->get( section => 'name' );
# $name eq "The ties that bind."
name = 'The ''ties'' that ''bind.'''
$name = $ini->get( section => 'name' );
# $name eq "The 'ties' that 'bind.'"
}}}
This uses {{{Config::Ini::Quote::parse_single_quoted()}}}.
Double-quoted values may be parsed a couple of different ways. By default, backslash-escaped unprintable characters will be unescaped to their actual Unicode character. This includes ascii control characters like {{{\n}}}, {{{\t}}}, etc., Unicode character codes like {{{\N}}} (Unicode next line), {{{\P}}} (Unicode paragraph separator), and hex-value escape sequences like {{{\x86}}} and {{{\u263A}}}.
If the {{{':html'}}} heredoc modifier is used (see Heredoc Modifiers below), then HTML entities will be decoded (using HTML::Entities) to their actual Unicode characters.
This uses {{{Config::Ini::Quote::parse_double_quoted()}}}.
See Config::Ini:Quote for more details.
!!!Heredoc :modifiers
There are several ways to modify the value in a heredoc as the Ini file is read in (i.e., as the object is initialized):
{{{
:chomp - chomps the last line
:join - chomps every line BUT the last one
:indented - unindents every line (strips leading whitespace)
:parse - splits on newline (and chomps last line)
:parse(regex) - splits on regex (still chomps last line)
:slash - unescapes backslash-escaped characters in double quotes (default)
:html - decodes HTML entities in double quotes
:json - parses javascript object notation (complex data types)
}}}
The {{{':parse'}}} modifier uses {{{Text::ParseWords::parse_line()}}}, so CSV-like parsing is possible.
The {{{':json'}}} modifier uses the JSON module to parse and dump complex data types (combinations of hashes, arrays, scalars). The value of the heredoc must be valid ~JavaScript Object Notation.
The {{{':slash'}}} and {{{':html'}}} modifiers are only valid when double quotes are used (surrounding the heredoc tag and modifiers). If no modifiers are given with double quotes, {{{':slash'}}} is the default.
{{{
name = <<"EOT :html"
vis-à-vis
EOT
name = <<"EOT"
\tSmiley: \u263A
EOT
}}}
Modifiers may be stacked, e.g., {{{'<<:chomp:join:indented'}}} (or {{{'<<:chomp :join :indented'}}}), in any order, but note that {{{':parse'}}} and {{{':json'}}} are performed last.
{{{
# value is "Line1\nLine2\n"
name = <<
Line1
Line2
<<
# value is "Line1\nLine2"
name = <<:chomp
Line1
Line2
<<
# value is "Line1Line2\n"
name = <<:join
Line1
Line2
<<
# value is "Line1Line2"
name = <<:chomp:join
Line1
Line2
<<
# value is " Line1\n Line2\n"
name = <<
Line1
Line2
<<
# - indentations do NOT have to be regular to be unindented
# - any leading spaces/tabs on every line will be stripped
# - trailing spaces are left intact, as usual
# value is "Line1\nLine2\n"
name = <<:indented
Line1
Line2
<<
# modifiers may have spaces between them
# value is "Line1Line2"
name = << :chomp :join :indented
Line1
Line2
<<
# ... and should come after a heredoc "tag"
# value is "Line1Line2"
name = <<heredoc :chomp :join :indented
Line1
Line2
heredoc
}}}
The {{{':parse'}}} modifier splits a single value into multiple values. It may be given with a regular expression parameter to split on other than newline (the default).
{{{
# :parse is same as :parse(\n)
name = <<:parse
value1
value2
<<
}}}
... is the same as
{{{
name = value1
name = value2
}}}
... and
{{{
name = <<:parse(/,\s+/)
"Tom, Dick, and Harry", Fred and Wilma
<<
}}}
... is the same as
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
}}}
The {{{':parse'}}} modifier chomps only the last line, so include {{{'\n'}}} if needed.
{{{
# liberal separators
name = <<:parse([,\s\n]+)
"Tom, Dick, and Harry" "Fred and Wilma"
Martha George, 'Hillary and Bill'
<<
}}}
... is the same as,
{{{
name = Tom, Dick, and Harry
name = Fred and Wilma
name = Martha
name = George
name = Hillary and Bill
}}}
As illustrated above, the enclosing {{{'/'}}} characters around the regular expression are optional. You may also use matching quotes instead, e.g., {{{:parse('\s')}}}.
{{{
name = <<:json
{ a: 1, b: 2, c: 3 }
<<
}}}
Given the above {{{':json'}}} example, {{{$ini->get('name')}}} should return a hashref. Note that we accept bare hash keys ({{{$JSON::BareKey=1;}}}).
Modifiers must follow the heredoc characters {{{'<<'}}} (or {{{'{'}}}). If there is a heredoc tag, e.g., {{{'EOT'}}} below, the modifiers should follow it, too.
{{{
name = <<EOT:json
{ a: 1, b: 2, c: 3 }
EOT
}}}
If you want to use single or double quotes, surround the heredoc tag and modifiers with the appropriate quotes:
{{{
name = <<'EOT :indented'
line1
line2
EOT
name = <<"EOT :html"
vis-à-vis
EOT
}}}
If no heredoc tag is used, put the quotes around the modifiers.
{{{
name = <<":html"
vis-à-vis
<<
}}}
If no modifiers either, just use empty quotes.
{{{
name = <<""
vis-\xE0-vis
<<
}}}
Comments are allowed on the assignment line if quotes are used.
{{{
name = <<'EOT :indented' # this is a comment
line1
line2
EOT
}}}
But note:
{{{
name = <<EOT
'Line1' # this is NOT a comment
EOT
}}}
!!!Quotes in Heredocs
In heredocs, embedded single and double quotes do not have to be (and should not be) escaped. In other words leave single quotes as {{{"'"}}} (not {{{"''"}}}), and leave double quotes as {{{'"'}}} (not {{{'\"'}}}).
{{{
name = <<'EOT :indented'
'line1'
'line2'
EOT
# $name eq "'line1'\n'line2'\n"
$name = $ini->get( 'name' );
name = <<"EOT :html"
"vis-à-vis"
EOT
# $name eq qq{"vis-\xE0-vis"}
$name = $ini->get( 'name' );
}}}
!GLOBAL SETTINGS
The global settings below are stored in the object during {{{init()}}}. So if the global settings are subsequently changed, any existing objects will not be affected.
!!$Config::Ini::Expanded::keep_comments
This boolean value will determine if comments are kept when an Ini file is loaded or when an Ini object is written out using {{{as_string()}}}. The default is false -- comments are not kept. The rational is this: Unlike the Config::Ini::Edit module, the {{{Expanded}}} module is not indented primarily to rewrite Ini files, so it is more likely that comments aren't needed in the object.
!!$Config::Ini::Expanded::heredoc_style
This string can be one of {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}} (default is {{{'<<'}}}). This determines the default heredoc style when the object is written out using {{{as_string()}}}. If a value was read in originally from a heredoc, it will be written out using that heredoc style, not this default style. The above values correspond respectively to the following styles.
{{{
# '<<'
name = <<EOT
Hey
EOT
# '<<<<'
name = <<EOT
Hey
<<EOT
# '{'
name = {EOT
Hey
EOT
# '{}'
name = {EOT
Hey
}EOT
}}}
!!$Config::Ini::Expanded::interpolates
This boolean value (default: {{{true}}}) will determine if expansion templates in double quoted values will automatically be interpolated as the Ini file is read in. This includes expansion templates like {{{'{INI:section:name}'}}}, {{{'{VAR:varname}'}}}, and {{{'{FILE:file_path}'}}}. The {{{'{INCLUDE:file_path}'}}} template will always be expanded, regardless of the value of {{{$Config::Ini::Expanded::interpolates}}}, because it is at the section level, not the value level.
Note that //interpolation// is not the same as //expansion//, such as when {{{get_expanded()}}} is called. Interpolation performs a simple one-pass replacement, while expansion performs a loop until there are no more replacements to do. It's like the difference between ...
{{{
s/{(.*)}/replace($1)/ge; # interpolation-like
}}}
and ...
{{{
1 while s/{(.*)}/replace($1)/ge; # expansion-like
}}}
See more about expansion templates below.
!!$Config::Ini::Expanded::expands
This boolean value (default: {{{false}}}) will determine if expansion templates in double quoted values will automatically be expanded as the Ini file is read in. This includes expansion templates like {{{'{INI:section:name}'}}}, {{{'{VAR:varname}'}}}, and {{{'{FILE:file_path}'}}}.
Note that this is different from what {{{interpolates}}} does, because templates will be fully expanded in a loop until there are no more templates in the value.
See more about expansion templates below.
!!$Config::Ini::Expanded::inherits
The value of this setting will be a null string (the default) to signify no inheritance, or an array reference pointing to an array of Config::Ini::Expanded (or Config::Ini::Edit or Config::Ini) objects.
If such an array of objects is given, then inheritance can take place when you call {{{$ini->get(...)}}}, {{{$ini->get_var(...)}}}, or {{{$ini->get_loop(...)}}}.
That is, if your object ({{{$ini}}}) does not have a value for the requested parameters, Config::Ini::Expanded will travel through the array of other objects (in the order given) until a value is found.
!!$Config::Ini::Expanded::no_inherit
The value of this setting will be a null string (the default) to signify that anything may be inherited, or a reference to a hash of hashes specifying section/name combinations that should not be inherited, e.g.,
{{{
$Config::Ini::Expanded::no_inherit = { section => { name1 => 1, name2 => 1 } };
}}}
Note the true ({{{1}}}) values for the names.
With the above example in force, when a program calls, e.g., {{{$ini-}}}get( section, 'name1' );> and gets no value from the {{{$ini}}} object, it will ''not'' inherit from any object in the {{{inherits}}} list.
Note that the {{{no_inherit}}} attribute does not affect inheritance that may take place when a program calls {{{$ini-}}}get_var()> or {{{$ini-}}}get_loop()>.
!!$Config::Ini::Expanded::no_override
The value of this setting will be a null string (the default) to signify that any inherited values may be overridden, or a reference to a hash of hashes specifying section/name combinations that may not be overridden, e.g.,
{{{
$Config::Ini::Expanded::no_override = { section => { name1 => 1, name2 => 1 } };
}}}
Note the true ({{{1}}}) values for the names.
With the above example in force, if an Ini object's {{{inherits}}} attribute is true, the module will not allow {{{section/name1}}} or {{{section/name2}}} to be set during the {{{init()}}} of that object. That is, those section/name combinations may not be overridden in an ini file (on the assumption that they will be set in an inherited object).
!!$Config::Ini::Expanded::loop_limit
During an expansion, e.g., when you call {{{get_expanded()}}}, a loop is started that ends when there are no more expansions to do. If this loops more than the value of {{{'loop_limit'}}}, the program will croak with a //Loop alert//.
The default {{{'loop_limit'}}} is 10, which should be sufficient for most situations. You can increase this limit if you need to have deeper nesting levels in your expansions.
Looping expansions allow for nested expansion templates like:
{{{
{FILE:{INI:section:{VAR:myname}}}
}}}
The inner-most templates are expanded first.
!!$Config::Ini::Expanded::size_limit
During an expansion like described above, the value being expanded may grow longer. If the length of the value exceeds the value of {{{'size_limit'}}}, the program will croak with a //Loop alert// (on the assumption that the large size is the result of a loop).
The default {{{'size_limit'}}} is 1_000_000. Increase this limit if you need to allow for larger values.
!!$Config::Ini::Expanded::include_root
This value is the path were {{{'{INCLUDE:file_path}'}}} and {{{'{FILE:file_path}'}}} will look when file contents are read in.
{{{
$Config::Ini::Expanded::include_root = '/web/data';
my $ini = $Config::Ini::Expanded->new( string => <<'__' );
[section]
name = "{FILE:stuff}"
{INCLUDE:ini/more.ini}
__
}}}
In the above example, the value of {{{$ini->get(section=>'name')}}} would be the contents of {{{'/web/data/stuff'}}}, and the contents of {{{'/web/data/ini/more.ini'}}} would be pulled in and used to augment the Ini file contents.
!!$Config::Ini::Expanded::encoding
This value is the character encoding expected for the ini data. It applies in new()/init() (which includes {{{'{INCLUDE:file_path}'}}}) and {{{'{FILE:file_path}'}}}.
(The default value is false, which will not specify an encoding, relying on perl's default behavior.)
{{{
$Config::Ini::Expanded::encoding = 'utf8';
my $ini = $Config::Ini::Expanded->new( string => <<'__' );
[section]
name = "{FILE:stuff}"
{INCLUDE:ini/more.ini}
__
}}}
In the above example, the character encoding for the {{{'string'}}} parameter value is assumed to be {{{'utf8'}}}. This encoding is then assumed for the {{{'{FILE:stuff}'}}} operation and for {{{'{INCLUDE:ini/more.ini}'}}}.
Set this to a false value, e.g., {{{''}}} or {{{0}}} to keep the module from specifying any encoding, i.e., to assume the default behavior.
!!$Config::Ini::Expanded::filter
This value is a subroutine reference. This subroutine will filter the data prior to expanding or interpolating it. This is intended to allow you to use different syntax for template placeholders. The subroutine expects to get a scalar reference and will update that scalar. For example, if you don't like how this looks:
{{{
{VAR:title}
{LOOP:text}{LVAR:line}
{END_LOOP:text}
}}}
then you might define a filter like this:
{{{
$ini->filter( sub {
for( ${$_[0]} ) {
s| <TMPL_VAR \s+ NAME="(.*?)"> |{VAR:$1}|gx;
s| <TMPL_LOOP \s+ NAME="(.*?)"> |{LOOP:$1}|gx;
s| <TMPL_LVAR \s+ NAME="(.*?)"> |{LVAR:$1}|gx;
s| </TMPL_LOOP \s+ NAME="(.*?)"> |{END_LOOP:$1}|gx;
}
} );
}}}
then you could change your template to look like this:
{{{
<TMPL_VAR NAME="title">
<TMPL_LOOP NAME="text"><TMPL_LVAR NAME="line">
</TMPL_LOOP NAME="text">
}}}
If you're familiar with HTML::Template, you'll notice that the new syntax looks similar to that module's. But there isn't a one-to-one correspondence, e.g., VAR and LVAR are two different things, and the </TMPL_LOOP...> end tag must still include the loop name from the begin tag.
!!$Config::Ini::Expanded::callbacks
This value is a hashref of subroutine references. These subroutines will be called if their keys appear in one of the following template placeholders:
{{{
{VAR:...}
{INI:...}
{LVAR:...}
}}}
Theses keys must appear in the "name" portion of the placeholder as if they were subroutine calls for that name, e.g.,
{{{
{VAR:escape_url(query)} (vs. {VAR:query})
{INI:section:escape_html(text)} (vs. {INI:section:text})
{LVAR:escape_js(parms)} (vs. {LVAR:parms})
}}}
The respective callback keys would then be
{{{
escape_url
escape_html
escape_js
}}}
e.g.,
{{{
$ini->callbacks( {
escape_url => sub { ... },
escape_html => sub { ... },
escape_js => sub { ... },
} );
}}}
The intention for callbacks is to implement the usual escape operations as implied by the above examples. But callbacks may be named anything you want, and they may do anything you want. The subroutines will be passed the value for the indicated name and should return the escaped (or otherwised munged) value.
''Page 1'' | [[Page 2|Config::Ini::Expanded Page 2]] | [[Page 3|Config::Ini::Expanded Page 3]] | [[Next Page|Config::Ini::Expanded Page 2]]
[[Previous Page|Config::Ini::Expanded]] | [[Page 1|Config::Ini::Expanded]] | ''Page 2'' | [[Page 3|Config::Ini::Expanded Page 3]] | [[Next Page|Config::Ini::Expanded Page 3]]
!EXPANSION TEMPLATES
!!Templates Overview
The Config::Ini::Expanded module exists in order to implement expansion templates. They take the following forms:
{{{
{INCLUDE:ini_file_path}
{FILE:file_path}
{INI:section:name}
{IF_INI:section:name}.......{ELSE[[_IF_INI]:section:name]}...{END_IF_INI:section:name}
{UNLESS_INI:section:name}...{ELSE[[_UNLESS_INI]:section:name]}...{END_UNLESS_INI:section:name}
{INI:section:name:i}
{IF_INI:section:name:i}.......{ELSE[[_IF_INI]:section:name:i]}...{END_IF_INI:section:name:i}
{UNLESS_INI:section:name:i}...{ELSE[[_UNLESS_INI]:section:name:i]}...{END_UNLESS_INI:section:name:i}
{VAR:varname}
{IF_VAR:varname}.......{ELSE[[_IF_VAR]:varname]}...{END_IF_VAR:varname}
{UNLESS_VAR:varname}...{ELSE[[_UNLESS_VAR]:varname]}...{END_UNLESS_VAR:varname}
{LOOP:loopname}
{LVAR:lvarname}
{IF_LVAR:lvarname}.......{ELSE[[_IF_LVAR]:lvarname]}...{END_IF_LVAR:lvarname}
{UNLESS_LVAR:lvarname}...{ELSE[[_UNLESS_LVAR]:lvarname]}...{END_UNLESS_LVAR:lvarname}
{LOOP:[loopname:]nestedloop}
{LVAR:[nestedloop:]nestedlvar}
{END_LOOP:[loopname:]nestedloop}
{LC:[loopname:]index} (0 ... last index)
{LC:[loopname:]counter} (1 ... last index + 1)
{LC:[loopname:]first}
{IF_LC:[loopname:]first}.......{ELSE[[_IF_LC][:loopname]:first]}...{END_IF_LC:[loopname:]first}
{UNLESS_LC:[loopname:]first}...{ELSE[[_UNLESS_LC][:loopname]:first]}...{END_UNLESS_LC:[loopname:]first}
{LC:[loopname:]last}
{IF_LC:[loopname:]last}.......{ELSE[[_IF_LC]:...]}...{END_IF_LC:[loopname:]last}
{UNLESS_LC:[loopname:]last}...{ELSE[[_UNLESS_LC]:...]}...{END_UNLESS_LC:[loopname:]last}
{LC:[loopname:]inner}
{IF_LC:[loopname:]inner}.......{ELSE[[_IF_LC]:...]}...{END_IF_LC:[loopname:]inner}
{UNLESS_LC:[loopname:]inner}...{ELSE[[_UNLESS_LC]:...]}...{END_UNLESS_LC:[loopname:]inner}
{LC:[loopname:]odd}
{IF_LC:[loopname:]odd}.......{ELSE[[_IF_LC]:...]}...{END_IF_LC:[loopname:]odd}
{UNLESS_LC:[loopname:]odd}...{ELSE[[_UNLESS_LC]:...]}...{END_UNLESS_LC:[loopname:]odd}
{LC:[loopname:]break(nn)} (e.g., break(2) == "even")
{IF_LC:[loopname:]break(nn)}.......{ELSE[[_IF_LC]:...]}...{END_IF_LC:[loopname:]break(nn)}
{UNLESS_LC:[loopname:]break(nn)}...{ELSE}[[_UNLESS_LC]:...]...{END_UNLESS_LC:[loopname:]break(nn)}
{END_LOOP:loopname}
{IF_LOOP:loopname}.......{ELSE[[_IF_LOOP]:loopname]}...{END_IF_LOOP:loopname}
{UNLESS_LOOP:loopname}...{ELSE[[_UNLESS_LOOP]:loopname]}...{END_UNLESS_LOOP:loopname}
}}}
Note that the {{{'{END...}'}}} tags contain the full contents of the corresponding beginning tag. By putting this onus on the user (i.e., supplying the full beginning tag in multiple places), it removes from the code the onus -- and processing time -- of parsing the text for balanced tags.
It can also be viewed as a positive explicit statement of were a tag begins and ends (albeit at the cost of verbosity).
A note about {{{'{ELSE}'}}}: it is not strictly necessary to qualify any {{{'{ELSE}'}}} tag, i.e., by adding the full contents of the begin tag, e.g., {{{'{ELSE_IF_VAR:maple}'}}}. This qualification is done internally for any "bare" ELSE's. But you may want to qualify your ELSE's anyway for clarity, e.g.,
{{{
{IF_VAR:tree}
{IF_VAR:maple}{VAR:maple}
{ELSE_IF_VAR:maple}{VAR:tree}
{END_IF_VAR:maple}
{ELSE_IF_VAR:tree}No tree.
{END_IF_VAR:tree}
}}}
In addition, you can //partially// qualify an ELSE with just a name, e.g.,
{{{
{IF_VAR:tree}
{IF_VAR:maple}{VAR:maple}
{ELSE:maple}{VAR:tree}
{END_IF_VAR:maple}
{ELSE:tree}No tree.
{END_IF_VAR:tree}
}}}
So if {{{'{ELSE_IF_VAR:maple'}}} looks confusingly like "elsif" logic is expected (it's not), then saying {{{'{ELSE:maple}'}}} is an option.
A note about {{{'{LOOP...}'}}}, {{{'{LVAR...}'}}}, {{{'{IF_LOOP...}'}}}, {{{'{UNLESS_LOOP...}'}}}, {{{'{IF_LVAR...}'}}}, {{{'{UNLESS_LVAR...}'}}}, and their ELSE and END tags: The extra qualification, e.g., {{{'{LOOP:loopname:nestedloop}'}}} vs. {{{'{LOOP:nestedloop}'}}} is only necessary if there is ambiguity and you need to disambiguate. Without any qualification, the named element (e.g., "nestedloop") will be searched for first in the current loop and if not found, back through the previous levels of loops.
Finally, a note about the loop context tags, {{{'{LC...}'}}}, {{{'{IF_LC...}'}}}, {{{'{UNLESS_LC...}'}}}, and their ELSE and END tags: The loopname portion of the tag is optional if the loop context is for the current loop (vs. a parent loop).
Since this is usually the case, you'll usually leave off the loopname. But if you want to access the loop context of a parent loop, the loopname must be included.
!!Templates Specifics
!!!{INCLUDE:ini_file_path}
The {{{'{INCLUDE:ini_file_path}'}}} template is expanded during {{{init()}}} as the Ini file is read in. This allows you to include Ini files in other Ini files. There is no limit to the amount of nesting allowed other than perl's own deep recursion limits.
{{{
[section]
name = value
{INCLUDE:second.ini}
[another_section]
name = value
}}}
The included Ini file will be loaded into the object as if its contents existed in the main Ini file where the template appears. It croaks if the file cannot be opened. It also croaks if {{{$self->include_root()}}} is not set (or is set to {{{'/'}}}), or if {{{'ini_file_path'}}} contains two dots {{{'..'}}}.
Note that this template is never expanded inside double-quoted values or during calls to {{{get_expanded()}}} or {{{get_interpolated()}}}. It is a section-level template, not a value-level template. See {{{'{FILE:file_path}'}}} below for value-level file inclusions.
!!!{FILE:file_path}
The {{{'{FILE:file_path}'}}} template is expanded inside double-quoted values and when you call {{{get_expanded()}}} and {{{get_interpolated()}}}. It replaces the template with the contents of {{{'include_root/file_path'}}}. It croaks if the file cannot be opened. It also croaks if {{{$self->include_root()}}} is not set (or is set to {{{'/'}}}), or if {{{'file_path'}}} contains two dots {{{'..'}}}.
{{{
[website]
homepage = {FILE:homepage.html}
...
print $ini->get_expanded( website => 'homepage' );
}}}
!!!{INI:section:name}
Includes:
{{{
{INI:section:name}
{INI:section:name:i}
}}}
The {{{'{INI:section:name}'}}} template is expanded inside double-quoted values and when you call {{{get_expanded()}}} and {{{get_interpolated()}}}. It performs a call to {{{get('section','name')}}} and replaces the template with the return value. If the value is undefined, the template is replaced silently with a null string.
You can provide an occurrence value (array subscript), e.g.,
{{{
name = <<""
This is the first value: {INI:section:name:0}, and
this is the second: {INI:section:name:1}.
<<
}}}
!!!{IF/UNLESS_INI:section:name}
Includes:
{{{
{IF_INI:section:name}...[{ELSE[[_IF_INI]:section:name]}...]{END_IF_INI:section:name}
{IF_INI:section:name:i}...[{ELSE[[_IF_INI]:section:name:i]}...]{END_IF_INI:section:name:i}
{UNLESS_INI:section:name}...[{ELSE[[_UNLESS_INI]:section:name]}...]{END_UNLESS_INI:section:name}
{UNLESS_INI:section:name:i}...[{ELSE[[_UNLESS_INI]:section:name:i]}...]{END_UNLESS_INI:section:name:i}
}}}
These templates provide for conditional text blocks based on the truth (or existence) of a named value in a section. An optional {{{'{ELSE}'}}} divider supplies an alternative text block.
Note that the {{{'{END...}'}}} tags must contain the full contents of the beginning tag, including the section, name, and (if supplied) index.
!!!{VAR:varname}
The {{{'{VAR:varname}'}}} template is expanded inside double-quoted values and when you call {{{get_expanded()}}} and {{{get_interpolated()}}}. It performs a call to {{{get_var('varname')}}} and replaces the template with the return value. If the value is undefined, the template is replaced silently with a null string.
{{{
[letter]
greeting = Hello {VAR:username}, today is {VAR:today}.
...
$greeting = $ini->get_expanded( letter => 'greeting' );
}}}
!!!{IF/UNLESS_VAR:varname}
Includes:
{{{
{IF_VAR:varname}...[{ELSE[[_IF_VAR]:varname]}...]{END_IF_VAR:varname}
{UNLESS_VAR:varname}...[{ELSE[[_UNLESS_VAR]:varname]}...]{END_UNLESS_VAR:varname}
}}}
These templates provide for conditional text blocks based on the truth (or existence) of a variable. An optional {{{'{ELSE}'}}} divider supplies an alternative text block.
Note that the {{{'{END...}'}}} tags must contain the full contents of the beginning tag, including the variable name.
!!!{LOOP:loopname}
Includes:
{{{
{LOOP:loopname}...{LVAR:lvarname}...{END_LOOP:loopname}
}}}
This template enables loops that are similar to those in HTML::Template, i.e., they iterate over an array of hashes. The {{{'{LVAR:...}'}}} tag is where you display the values from each hash.
Use set_loop() to provide the array reference -- and it may include nested loops, e.g.,
{{{
$ini->set_loop( loop1 => [{
var1 => 'val1',
var2 => 'val2',
loop2 => [{ innervar1 => 'innerval1', innervar2 => 'innerval1' }]
}] );
{LOOP:loop1}
{LVAR:var1}{LVAR:var2}
{LOOP:loop2}{LVAR:innervar1}{LVAR:innervar2}{END_LOOP:loop2}
{END_LOOP:loop1}
}}}
Note that nearly everything is "global". In other words, {{{'{VAR:...}'}}} and {{{'{INI:...}'}}} tags may be displayed inside any loop. Also, {{{'{LOOP:...}'}}} tags at the top level (named in the set_loop() call) may be displayed inside any other loop. And, nested {{{'{LOOP:...}'}}} tags may contain {{{'{LOOP:...}'}}} and {{{'{LVAR:...}'}}} tags from parent loops. In this case, loop name and lvar name collisions favor the current loop and then the closest parent.
So given:
{{{
$ini->put( section1 => 'name1', 'value1' );
$ini->set_var( var1 => 'val1' );
$ini->set_loop( loop1 => [{
var1 => 'loop1val1',
var2 => 'loop1val2',
loop2 => [{
loop2var1 => 'loop2val1',
var2 => 'loop2val2'
}]
}],
loop3 => [{
var1 => 'loop3val1',
loop3var2 => 'loop3val2'
}]
);
}}}
The following is possible:
{{{
{LOOP:loop1}
{INI:section1:name1}{VAR:var1}
{LVAR:var1} (i.e., 'loop1val1')
{LVAR:var2} (i.e., 'loop1val2')
{LOOP:loop2}
{INI:section_a:name1}{VAR:var1} (same as above)
{LVAR:var1} (i.e., 'loop1val1')
{LVAR:loop2var1} (i.e., 'loop2val1')
{LVAR:var2} (i.e., 'loop2val2')
{LOOP:loop3}
{LVAR:var1} (i.e., 'loop3val1')
{LVAR:loop3var2} (i.e., 'loop3val2')
{LVAR:var2} (i.e., 'loop2val2')
{END_LOOP:loop3}
{END_LOOP:loop2}
{END_LOOP:loop1}
}}}
Note that when a top-level loop is displayed inside a another loop, the other loop is considered a "parent" for the purposes of resolving the lvar names, e.g., {{{'{LVAR:var2}'}}} inside loop3 above resolves to the value from loop2. If loop3 were not inside another loop that defined var2, then {{{'{LVAR:var2}'}}} would be null inside loop3.
What can't be displayed are nested loops and lvars from a different loop. For example, the following is not possible:
{{{
{LOOP:loop3}
{LOOP:loop2}
...
{END_LOOP:loop2}
{END_LOOP:loop3}
}}}
This is because there would normally be many "loop2"s and, outside the context of {{{'{LOOP:loop1}'}}}, a reference to loop2 would be ambiguous (not to mention being tough to locate).
On the other hand, the following //is// possible:
{{{
{LOOP:loop1}
{LOOP:loop3}
{LOOP:loop2}
{LVAR:var1} (i.e., 'loop3val1')
{LVAR:var2} (i.e., 'loop2val2')
{END_LOOP:loop2}
{END_LOOP:loop3}
{END_LOOP:loop1}
}}}
This is because loop2 is now referenced in the context of looping through loop1, so determining what to display is not ambiguous. The fact that loop2 is being displayed inside loop3 (inside loop1) doesn't alter the fact that loop1 is now one of loop2's parents. What it does alter is where the value for var1 comes from, i.e., from loop3 (the closest parent), not loop1.
This illustrates again that for the purposes of loop and lvar name resolutions, a parent relationship, that is based on how the loops are displayed, trumps a parent relationship that is based on how the loops are defined.
If you're lost at this point, don't worry too much. Most of the time, things will happen as you intend. The above explanation attempts to be as complete as possible both to explain why things might not happen as you intended and to show what is possible (that, e.g., isn't possible in HTML::Template).
!!!{IF/UNLESS_LOOP:loopname}
Includes:
{{{
{IF_LOOP:loopname}...[{ELSE[[_IF_LOOP]:loopname]}...]{END_IF_LOOP:loopname}
{UNLESS_LOOP:loopname}...[{ELSE[[_UNLESS_LOOP]:loopname]}...]{END_UNLESS_LOOP:loopname}
}}}
These templates provide for conditional text blocks based on the existence of a loop. An optional {{{'{ELSE}'}}} divider supplies an alternative text block.
Note that the {{{'{END...}'}}} tags must contain the full contents of the beginning tag, including the loop name.
!!!{LVAR:lvarname}
As stated above, this template is used inside a {{{'{LOOP...}'}}} to display the values from each hash in the loop. Outside a loop, this template will be silently replaced with nothing.
Inside multiple nested loops, this template will be replaced with the value defined in the current loop or with a value found in one of the parent loops -- from the closest parent if it's defined in more than one. Note that for name collisions, "closest parent" is relative to how the templates are being displayed rather than how they are defined in set_loop().
Note that unlike in HTML::Template (and it's family of modules), {{{'{LVAR...}'}}} is distinct from {{{'{VAR...}'}}}. So you may define VAR's with the same names as LVAR's (values inside loops) without problems, because there are separate tags to access the separate "name spaces", as it were.
Note also that there is no set_lvar() method. This is because LVAR's are always set inside loops via set_loop().
!!!{IF_LVAR:lvarname}
Includes:
{{{
{IF_LVAR:lvarname}...[{ELSE[[_IF_LVAR]:lvarname]}...]{END_IF_LVAR:lvarname}
{UNLESS_LVAR:lvarname}...[{ELSE[[_UNLESS_LVAR]:lvarname]}...]{END_UNLESS_LVAR:lvarname}
}}}
These templates provide for conditional text blocks based on the truth (or existence) of a value inside a loop. An optional {{{'{ELSE}'}}} divider supplies an alternative text block.
Note that the {{{'{END...}'}}} tags must contain the full contents of the beginning tag, including the lvar name.
As with {{{'{LVAR...}'}}}, these conditionals do not have reasonable meaning outside a loop. That is, if used outside a loop, these will always register as false, so //something// might be displayed, but it probably won't mean what you think.
!!Loop Context Variables
In HTML::Template (and other modules based on it, like HTML::Template::Compiled) there are values you can access that reflect the current loop context, i.e., {{{__first__}}}, {{{__last__}}}, {{{__inner__}}}, {{{__odd__}}}, {{{__counter__}}}, as well as {{{__index__}}}, and {{{__break__}}} (from H::T::C).
These values are supported in this module, though without the double underscores (since the syntax separates them from the loop name). In addition, in this module, these values are available not only for the current loop, but also for parent loops (because the syntax includes the loop name).
As mentioned above, the loopname portion of the tag is optional if the loop context is for the current loop (vs. a parent loop).
Since this is usually the case, you'll usually leave off the loopname. But if you want to access the loop context of a parent loop, the loopname must be included.
!!!{LC:loopname:index} (0 ... last index)
The current index of the loop (starting with 0). This differs from {{{'{LC:loopname:counter}'}}}, which starts with 1.
!!!{LC[:loopname]:counter} (1 ... last index + 1)
The current counter (line number?) of the loop (starting with 1). This differs from {{{'{LC:loopname:index}'}}}, which starts with 0.
!!!{LC/IF_LC/UNLESS_LC:loopname:first}
Includes:
{{{
{LC[:loopname]:first}
{IF_LC[:loopname]:first}.......{ELSE[[_IF_LC][:loopname]:first]}...{END_IF_LC[:loopname]:first}
{UNLESS_LC[:loopname]:first}...{ELSE[[_UNLESS_LC][:loopname]:first]}...{END_UNLESS_LC[:loopname]:first}
}}}
The template {{{'{LC:loopname:first}'}}} displays "1" (true) if the current iteration is the first one, "" (false) if not. The IF and UNLESS templates provide for conditional text blocks based on these boolean values.
!!!{LC/IF_LC/UNLESS_LC:loopname:last}
Includes:
{{{
{LC[:loopname]:last}
{IF_LC[:loopname]:last}.......{ELSE[[_IF_LC][:loopname]:last]}...{END_IF_LC[:loopname]:last}
{UNLESS_LC[:loopname]:last}...{ELSE[[_UNLESS_LC][:loopname]:last]}...{END_UNLESS_LC[:loopname]:last}
}}}
The template {{{'{LC:loopname:last}'}}} displays "1" (true) if the current iteration is the last one, "" (false) if not. The IF and UNLESS templates provide for conditional text blocks based on these boolean values.
!!!{LC/IF_LC/UNLESS_LC:loopname:inner}
Includes:
{{{
{LC[:loopname]:inner}
{IF_LC[:loopname]:inner}.......{ELSE[[_IF_LC][:loopname]:inner]}...{END_IF_LC[:loopname]:inner}
{UNLESS_LC[:loopname]:inner}...{ELSE[[_UNLESS_LC][:loopname]:inner]}...{END_UNLESS_LC[:loopname]:inner}
}}}
The template {{{'{LC:loopname:inner}'}}} displays "1" (true) if the current iteration is not the first one and is not the last one, "" (false) otherwise. The IF and UNLESS templates provide for conditional text blocks based on these boolean values.
!!!{LC/IF_LC/UNLESS_LC:loopname:odd}
Includes:
{{{
{LC[:loopname]:odd}
{IF_LC[:loopname]:odd}.......{ELSE[[_IF_LC][:loopname]:odd]}...{END_IF_LC[:loopname]:odd}
{UNLESS_LC[:loopname]:odd}...{ELSE[[_UNLESS_LC][:loopname]:odd]}...{END_UNLESS_LC[:loopname]:odd}
}}}
The template {{{'{LC:loopname:odd}'}}} displays "1" (true) if the current iteration is odd, "" (false) if not. The IF and UNLESS templates provide for conditional text blocks based on these boolean values.
!!!{LC/IF_LC/UNLESS_LC:loopname:break(nn)} (e.g., break(2) == "even")
Includes:
{{{
{LC[:loopname]:break(nn)} (e.g., break(2) == "even")
{IF_LC[:loopname]:break(nn)}.......{ELSE[[_IF_LC][:loopname]:break(nn)]}...{END_IF_LC[:loopname]:break(nn)}
{UNLESS_LC[:loopname]:break(nn)}...{ELSE[[_UNLESS_LC][:loopname]:break(nn)]}...{END_UNLESS_LC[:loopname]:break(nn)}
}}}
The template {{{'{LC:loopname:break(nn)}'}}} displays "1" (true) if the current iteration modulo the number 'nn' is true, "" (false) if not. The IF and UNLESS templates provide for conditional text blocks based on these boolean values.
Note that {{{'{LC:loopname:break(2)}'}}} would be like saying {{{'{LC:loopname:even}'}}} if the "even" template existed -- which it doesn't.
This feature is designed to allow you to insert things, e.g., column headings, at regular intervals in a loop.
!!Postponed Expansion/Interpolation
Expansion and interpolation can be //postponed// for value-level expansion templates by placing an exclamation point (or 'bang' or 'not' symbol) just before the opening brace of an expansion template, e.g.,
{{{
!{INI:section:name}
!{IF_INI:section:name}...{END_IF_INI:section:name} (and UNLESS)
!{VAR:varname}
!{IF_VAR:varname}...{END_IF_VAR:varname} (and UNLESS)
!{LOOP:loopname}...!{LVAR:lvarname}...!{LC:counter}...{END_LOOP:loopname}
!{IF_LOOP:loopname}...{END_IF_LOOP:loopname} (and UNLESS)
!{FILE:file_path}
}}}
This feature allows you to use expansion templates to create other expansion templates that will be expanded later (by your own program or another program).
Note that postponing loops adds an additional complication: To postpone a nested loop, you must also postpone all of the loop-related templates within the loop (sorry about that).
You can do this even if the loop isn't nested (as shown above), if you want to be safe.
Depending on your needs, you may postpone multiple times with multiple bangs, e.g.,
{{{
!!{INI:section:name}
}}}
Each exclamation point will postpone the expansion one more time.
Note that these postponements happen inside {{{expand()}}} and {{{interpolate()}}}. Since the {{{'{INCLUDE:ini_file_path}'}}} template is expanded in {{{init()}}}, it is not a value-level template and does not have the postponement feature; its expansion cannot be postponed.
[[Previous Page|Config::Ini::Expanded]] | [[Page 1|Config::Ini::Expanded]] | ''Page 2'' | [[Page 3|Config::Ini::Expanded Page 3]] | [[Next Page|Config::Ini::Expanded Page 3]]
[[Previous Page|Config::Ini::Expanded Page 2]] | [[Page 1|Config::Ini::Expanded]] | [[Page 2|Config::Ini::Expanded Page 2]] | ''Page 3''
!METHODS
!!Initialization Methods
!!!new()
Calling options:
{{{
new( 'filename' )
new( file => 'filename' )
new( fh => $filehandle )
new( string => $string )
new( string => $string, file => 'filename' )
new( fh => $filehandle, file => 'filename' )
new( file => 'filename', keep_comments => 0 )
new( file => 'filename', heredoc_style => '{}' ), etc.
}}}
Use {{{new()}}} to create an object, e.g.,
{{{
my $ini = Config::Ini::Expanded->new( 'inifile' );
}}}
If you pass any parameters, the {{{init()}}} method will be called. If you pass only one parameter, it's assumed to be the file name. Otherwise, use the named parameters, {{{'file'}}}, {{{'fh'}}}, or {{{'string'}}} to pass a filename, filehandle (already open), or string. The string is assumed to look like the contents of an Ini file.
The parameter, {{{'fh'}}} takes precedent over {{{'string'}}} which takes precedent over {{{'file'}}}. You may pass {{{file => 'filename'}}} with the other parameters to set the {{{'file'}}} attribute.
Other parameters are:
{{{'keep_comments'}}} to override the default: false.
{{{'heredoc_style'}}} to override the default: {{{'<<'}}}. The values accepted for heredoc_style are {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}}.
{{{'interpolates'}}} to override the default: 1.
{{{'expands'}}} to override the default: 0.
{{{'inherits'}}} to override the default: {{{''}}} (no inheritance).
{{{'loop_limit}}} to override the default: 10.
{{{'size_limit'}}} to override the default: 1_000_000.
{{{'include_root'}}} to override the default: {{{''}}} (inclusions not allowed).
{{{'encoding'}}} to override the default: 'utf8'.
Also see GLOBAL SETTINGS above.
If you do not pass any parameters to {{{new()}}}, you can later call {{{init()}}} with the same parameters described above.
By default, if you give a filename or string, the module will not specify any encoding, and thus will rely on perl's default behavior. You can change this by setting $Config::Ini::encoding, e.g.,
{{{
$Config::Ini::encoding = "utf8";
my $ini = Config::Ini->new( file => 'filename' );
}}}
Alternatively, you may open the file yourself using the desired encoding and send the filehandle to new() (or init());
!!!init()
Calling options:
{{{
init( 'filename' )
init( file => 'filename' )
init( fh => $filehandle )
init( string => $string )
init( string => $string, file => 'filename' )
init( fh => $filehandle, file => 'filename' )
init( file => 'filename', keep_comments => 0 )
init( file => 'filename', heredoc_style => '{}' ), etc.
}}}
Example:
{{{
my $ini = Config::Ini::Expanded->new();
$ini->init( 'filename' );
}}}
!!Get Methods
!!!get_sections()
Use {{{get_sections()}}} to retrieve a list of the sections in the Ini file. They are returned in the order they appear in the file.
{{{
my @sections = $ini->get_sections();
}}}
If there is a 'null section', it will be the first in the list.
If a section appears twice in a file, it only appears once in this list. This implies that ...
{{{
[section1]
name1 = value
[section2]
name2 = value
[section1]
name3 = value
}}}
is the same as ...
{{{
[section1]
name1 = value
name3 = value
[section2]
name2 = value
}}}
The {{{as_string()}}} method will output the latter.
!!!get_names()
Calling options:
{{{
get_names( $section )
get_names( '' )
get_names()
}}}
Use {{{get_names()}}} to retrieve a list of the names in a given section.
{{{
my @names = $ini->get_names( $section );
}}}
They are returned in the order they appear in the section.
If a name appears twice in a section, it only appears once in this list. This implies that ...
{{{
[section]
name1 = value1
name2 = value2
name1 = another
}}}
is the same as ...
{{{
[section]
name1 = value1
name1 = another
name2 = value2
}}}
The {{{as_string()}}} method will output the latter.
Calling {{{get_names()}}} without a parameter is the same as calling it with a null string: it retrieves the names from the 'null section'. The two lines below are equivalent.
{{{
@names = $ini->get_names();
@names = $ini->get_names( '' );
}}}
!!!get()
Calling options:
{{{
get( $section, $name )
get( $section, $name, $i )
get( $name ) (assumes $section eq '')
get( '', $name, $i )
}}}
Use {{{get()}}} to retrieve the value or values for a given name.
Note: when an Ini object is initialized, if a name appears more than once in a section, the values are pushed onto an array, and {{{get()}}} will return this array of values.
{{{
my @values = $ini->get( $section, $name );
}}}
Pass an array subscript as the third parameter to return only one of the values in this array.
{{{
my $value = $ini->get( $section, $name, 0 ); # get first one
my $value = $ini->get( $section, $name, 1 ); # get second one
my $value = $ini->get( $section, $name, -1 ); # get last one
}}}
If the Ini file lists names at the beginning, before any sections are given, the section name is assumed to be a null string ({{{''}}}). If you call {{{get()}}} with just one parameter, it is assumed to be a name in this 'null section'. If you want to pass an array subscript, then you must also pass a null string as the first parameter.
{{{
my @values = $ini->get( $name ); # assumes $section eq ''
my $value = $ini->get( '', $name, 0 ); # get first occurrence
my $value = $ini->get( '', $name, -1 ); # get last occurrence
}}}
If the section and name are not found, {{{get()}}} will inherit from any inherited objects, and if still not found, will return no value.
!!!get_interpolated()
Calling options:
{{{
get_interpolated( $section, $name )
get_interpolated( $section, $name, $i )
get_interpolated( $name ) (assumes $section eq '')
get_interpolated( '', $name, $i )
}}}
Use {{{get_interpolated()}}} to retrieve the value or values for a given name just as you would use {{{get()}}} (see above). But unlike with {{{get()}}}, the value will be interpolated and then returned.
{{{
$value = $ini->get_interpolated( $section, $name );
}}}
The following expansion templates may be interpolated:
{{{
{INI:section:name}
{INI:section:name:i}
}}}
It expands these by calling {{{get('section','name')}}} or {{{get('section','name',i)}}} and {{{interpolate()}}}.
{{{
{VAR:varname}
}}}
It expands this by calling {{{get_var('varname')}}} and {{{interpolate()}}}. See {{{get_var()}}} below.
{{{
{FILE:file_path}
}}}
It expands this by reading in the contents of {{{'include_root/file_path'}}}.
If expansion templates are nested, e.g.,
{{{
{FILE:{INI:section:{VAR:varname}}}
}}}
only the inner-most templates will be expanded, because {{{get_interpolated()}}} will only do one pass through the value.
If any template resolves to an undefined value, it will be replaced with a null string.
!!!interpolate( $value )
Use {{{interpolate()}}} to interpolate a value that may contain expansion templates. The interpolated value is returned. Typically you would not call {{{interpolate()}}}, but would instead call {{{get_interpolated()}}}, which itself calls {{{interpolate()}}}. But it is a supported method for those times when, for example, you might want to {{{get()}}} an uninterpolated value and expand it later.
{{{
$value = $ini->get( $section, $name );
...
$value = $ini->interpolate( $value );
}}}
!!!get_expanded()
Calling options:
{{{
get_expanded( $section, $name )
get_expanded( $section, $name, $i )
get_expanded( $name ) (assumes $section eq '')
get_expanded( '', $name, $i )
}}}
Use {{{get_expanded()}}} to retrieve the value or values for a given name just as you would use {{{get()}}} (see above). But unlike with {{{get()}}}, the value will be expanded and then returned.
{{{
$value = $ini->get_expanded( $section, $name );
}}}
The following expansion templates may be expanded:
{{{
{INI:section:name}
{INI:section:name:i}
}}}
It expands these by calling {{{get('section','name')}}} or {{{get('section','name',i)}}} and {{{expand()}}}.
{{{
{VAR:varname}
}}}
It expands this by calling {{{get_var('varname')}}} and {{{expand()}}}. See {{{get_var()}}} below.
{{{
{FILE:file_path}
}}}
It expands this by reading in the contents of {{{'include_root/file_path'}}}.
Expansion templates may be nested, e.g.,
{{{
{FILE:{INI:section:{VAR:varname}}}
}}}
The inner-most templates are expanded first.
If any template resolves to an undefined value, it will be replaced with a null string.
If there is a //Loop alert// condition (e.g., the number of expansion loops exceeds {{{'loop_limit'}}}, or the size of the value being expanded exceeds {{{'size_limit'}}}), {{{get_expanded()}}} (actually {{{expand()}}}) will croak.
!!!expand( $value )
Use {{{expand()}}} to expand a value that may contain expansion templates. The expanded value is returned. Typically you would not call {{{expand()}}}, but would instead call {{{get_expanded()}}}, which itself calls {{{expand()}}}. But it is a supported method for those times when, for example, you might want to {{{get()}}} an unexpanded value and expand it later.
{{{
$value = $ini->get( $section, $name );
...
$value = $ini->expand( $value );
}}}
If there is a //Loop alert// condition (e.g., the number of expansion loops exceeds {{{'loop_limit'}}}, or the size of the value being expanded exceeds {{{'size_limit'}}}), {{{expand()}}} will croak.
!!Add/Set/Put Methods
Here, //add// denotes pushing values onto the end, //set//, modifying a single value, and //put//, replacing all values at once.
!!!add()
Calling options:
{{{
add( $section, $name, @values )
add( '', $name, @values )
}}}
Use {{{add()}}} to add to the value or values of an option. If the option already has values, the new values will be added to the end (pushed onto the array).
{{{
$ini->add( $section, $name, @values );
}}}
To add to the 'null section', pass a null string.
{{{
$ini->add( '', $name, @values );
}}}
!!!set()
Calling options:
{{{
set( $section, $name, $i, $value )
set( '', $name, $i, $value )
}}}
Use {{{set()}}} to assign a single value. Pass {{{undef}}} to remove a value altogether. The {{{$i}}} parameter is the subscript of the values array to assign to (or remove).
{{{
$ini->set( $section, $name, -1, $value ); # set last value
$ini->set( $section, $name, 0, undef ); # remove first value
}}}
To set a value in the 'null section', pass a null string.
{{{
$ini->set( '', $name, 1, $value ); # set second value
}}}
!!!put()
Calling options:
{{{
put( $section, $name, @values )
put( '', $name, @values )
}}}
Use {{{put()}}} to assign all values at once. Any existing values are overwritten.
{{{
$ini->put( $section, $name, @values );
}}}
To put values in the 'null section', pass a null string.
{{{
$ini->put( '', $name, @values );
}}}
!!!set_var( 'varname', $value )
Use {{{set_var()}}} to assign a value to a {{{'varname'}}}. This value will be substituted into any expansion templates of the form, {{{'{VAR:varname}'}}}.
{{{
$ini->set_var( 'today', scalar localtime );
}}}
!!!get_var( $varname )
Use {{{get_var()}}} to retrieve the value of a varname. This method is called by {{{get_expanded()}}}, {{{get_interpolated()}}}, {{{expand()}}}, and {{{interpolate()}}} to expand templates of the form, {{{'{VAR:varname}'}}}.
{{{
$today = $ini->get_var( 'today' );
}}}
If the {{{$varname}}} is not found, {{{get_var()}}} will inherit from any inherited objects, and if still not found, will return no value.
!!!set_loop( 'loopname', $loop_structure )
Use {{{set_loop()}}} to assign a loop structure to a {{{'loopname'}}}. A loop structure is an array of hashes. The parameter, {{{'$loop_structure'}}}, should be this array's reference.
This value will be substituted into any expansion templates of the form, {{{'{LOOP:loopname}...{END_LOOP:loopname}'}}}.
{{{
$ini->set_loop( 'months', [{1=>'jan'},{2=>'feb'},...,{12=>'dec'}] );
}}}
The following will remove a loop:
{{{
$ini->set_loop( 'months' );
$ini->set_loop( 'months' => undef );
$ini->set_loop( { 'months' => undef } );
}}}
The following will remove all loops:
{{{
$ini->set_loop( undef );
}}}
!!!get_loop( $loopname )
Use {{{get_loop()}}} to retrieve the loop structure for a loopname. This method is called by {{{get_expanded()}}}, {{{get_interpolated()}}}, {{{expand()}}}, and {{{interpolate()}}} to expand templates of the form, {{{'{LOOP:loopname}...{END_LOOP:loopname}'}}} (as well as {{{'{IF_LOOP...}'}}} and {{{'{UNLESS_LOOP...}'}}}).
{{{
$aref = $ini->get_loop( 'months' );
}}}
If the {{{$loopname}}} is not found, {{{get_loop()}}} will inherit from any inherited objects, and if still not found, will return no value.
!!Delete Methods
!!!delete_section()
Calling options:
{{{
delete_section( $section )
delete_section( '' )
delete_section()
}}}
Use {{{delete_section()}}} to delete an entire section, including all of its options and their values.
{{{
$ini->delete_section( $section )
}}}
To delete the 'null section', don't pass any parameters or pass a null string.
{{{
$ini->delete_section();
$ini->delete_section( '' );
}}}
!!!delete_name()
Calling options:
{{{
delete_name( $section, $name )
delete_name( '', $name )
delete_name( $name )
}}}
Use {{{delete_name()}}} to delete a named option and all of its values from a section.
{{{
$ini->delete_name( $section, $name );
}}}
To delete an option from the 'null section', pass just the name, or pass a null string.
{{{
$ini->delete_name( $name );
$ini->delete_name( '', $name );
}}}
To delete just some of the values, you can use {{{set()}}} with a subscript, passing {{{undef}}} to delete each one. Or you can first get them into an array using {{{get()}}}, modify them in that array (e.g., delete some), and then use {{{put()}}} to replace the old values with the modified ones.
!!Other Accessor Methods
!!!file()
Calling options:
{{{
file()
file( $filename )
file( undef )
}}}
Use {{{file()}}} to get or set the name of the object's Ini file. Pass the file name to set the value. Pass {{{undef}}} to remove the {{{'file'}}} attribute altogether.
{{{
$inifile_name = $ini->file(); # get
$ini->file( $inifile_name ); # set
$ini->file( undef ); # remove
}}}
!!!keep_comments( $boolean )
Use {{{keep_comments()}}} to get or set the object's {{{'keep_comments'}}} attribute. The default for this attribute is false, i.e., do not keep comments. Pass a true value to turn comments on.
{{{
$boolean = $ini->keep_comments(); # get
$ini->keep_comments( $boolean ); # set
}}}
{{{keep_comments()}}} accesses the value of the flag that is stored in the object -- not the value of the global setting.
!!!heredoc_style( $style )
Use {{{heredoc_style()}}} to get or set the default style used when heredocs are rendered by {{{as_string()}}}.
{{{
$style = $ini->heredoc_style(); # get
$ini->heredoc_style( $style ); # set
}}}
The value passed should be one of {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}}. The default is {{{'<<'}}}.
{{{heredoc_style()}}} accesses the value of the style that is stored in the object -- not the value of the global setting.
!!!interpolates( 1 )
Use {{{interpolates()}}} to get or set the {{{'interpolates'}}} flag. This boolean value will determine if expansion templates in double quoted values will automatically be interpolated as the Ini file is read in. Also see {{{$Config::Ini::Expanded::interpolates}}} for more details.
{{{interpolates()}}} accesses the value of the flag that is stored in the object -- not the value of the global setting.
!!!expands( 0 )
Use {{{expands()}}} to get or set the {{{'expands'}}} flag. This boolean value will determine if expansion templates in double quoted values will automatically be expanded as the Ini file is read in. Also see {{{$Config::Ini::Expanded::expands}}} for more details.
{{{expands()}}} accesses the value of the flag that is stored in the object -- not the value of the global setting.
!!!inherits( [$ini_obj1, $ini_obj2, ... ] )
Use {{{inherits()}}} to get or set the {{{'inherits'}}} attribute. The value should be a null string to disable inheritance, or an array reference, like the anonymous array shown above. This array should contain a list of Ini objects (Config::Ini, Config::Ini::Edit, or Config::Ini::Expanded).
It must be an array reference even if there is only one object to inherit from, e.g.,
{{{
$ini->inherits( [$ini_obj] );
}}}
Also see {{{$Config::Ini::Expanded::inherits}}} for more details.
Note: ''don't'' inherit from yourself. If you do, you will get a deep recursion error if you call {{{get()}}} or {{{get_var()}}} and trigger inheritance. Note also that inheriting from yourself can happen if you inherit from an object that inherits from you.
{{{inherits()}}} accesses the value of the attribute that is stored in the object -- not the value of the global setting.
!!!loop_limit( 10 )
Use {{{loop_limit()}}} to get or set the {{{'loop_limit'}}} value. If an expansion loops more than the value of {{{'loop_limit'}}}, the program will croak with a //Loop alert//. Also see {{{$Config::Ini::Expanded::loop_limit}}} above for more details.
{{{loop_limit()}}} accesses the limit value that is stored in the object -- not the value of the global setting.
!!!size_limit( 1_000_000 )
Use {{{size_limit()}}} to get or set the {{{'size_limit'}}} value. If the length of an expanded value exceeds the value of {{{'size_limit'}}}, the program will croak with a //Loop alert//. Also see {{{$Config::Ini::Expanded::size_limit}}} above for more details.
{{{size_limit()}}} accesses the limit value that is stored in the object -- not the value of the global setting.
!!!include_root( $path )
Use {{{include_root()}}} to get or set the {{{'include_root'}}} value. This value is the path were {{{'{INCLUDE:file_path}'}}} and {{{'{FILE:file_path}'}}} will begin looking when file contents are read in. Also see {{{$Config::Ini::Expanded::include_root}}} for more details.
When a {{{'{INCLUDE:file_path}'}}} or {{{'{FILE:file_path}'}}} template is expanded, it will croak if {{{'include_root'}}} is not set (or is set to "/"), or if {{{'file_path'}}} contains two dots "..".
{{{include_root()}}} accesses the value of the path that is stored in the object -- not the value of the global setting.
!!!encoding( 'utf8' )
Use {{{encoding()}}} to get or set the {{{'encoding'}}} value. This value will determine the character encoding that is assumed when the ini object is created and when other files are included. Also see {{{$Config::Ini::Expanded::encoding}}} for more details.
{{{encoding()}}} accesses the value of the flag that is stored in the object -- not the value of the global setting.
See also {{{init()}}} and GLOBAL SETTINGS above.
!!!vattr( $section, $name, $i, $attribute, $value, ... )
Use vattr() to get or set value-level attributes, which include:
{{{
heretag : 'string'
herestyle : ( {, {}, <<, or <<<< )
quote : ( ', ", s, d, single, or double )
nquote : ( ', ", s, d, single, or double )
equals : ( '=', ':', ' = ', ': ', etc. )
escape : ( :slash and/or :html )
indented : indentation value (white space)
json : boolean
comment : 'string'
}}}
If {{{$i}}} is undefined, {{{0}}} is assumed. If there's an {{{$attribute}}}, but no {{{$value}}}, the value of that attribute is returned.
{{{
$value = $ini->vattr( $section, $name, 0, 'heretag' ); # get one
}}}
If no {{{$attribute}}} is given, {{{vattr()}}} returns all of the attribute names and values as a list (in pairs).
{{{
%attrs = $ini->vattr( $section, $name, 1 ); # get all
}}}
If {{{$attribute}}} is a hashref, values are set from that hash.
{{{
$ini->vattr( $section, $name, 1, { heretag=>'EOT', herestyle=>'{}' } );
}}}
Otherwise, attributes and values may be passed as named parameters.
{{{
$ini->vattr( $section, $name, 1, heretag=>'EOT', herestyle=>'{}' );
}}}
These value attributes are used to replicate the ini file when {{{as_string()}}} is called.
The attributes {{{'escape'}}}, {{{'indented'}}}, and {{{'json'}}} correspond to the similarly named heredoc modifiers; see {{{Heredoc Modifiers}}} above. The values of {{{'heretag'}}}, {{{'herestyle'}}}, and {{{'quote'}}} are used to begin and end the heredoc. Additionally, if double quotes are called for, characters in the value will be escaped according to the {{{'escape'}}} value.
The value of {{{'comment'}}} will be appended after the value, or if a heredoc, after the beginning of the heredoc. Note that the comment may also be accessed using {{{set_comment()}}} and {{{get_comment()}}}. See Comments Accessor Methods below.
The value of {{{'equals'}}} will be output between the name and value, e.g., {{{' = '}}} in {{{'name = value'}}}. The setting, {{{'nquote'}}}, is to the name what {{{'quote'}}} is to the value, i.e., if {{{"'"}}}, the name will be single quoted, if {{{'"'}}}, double quoted.
Note that replicating the ":parse" heredoc modifier is not supported, so if a file containing a ":parse" modifier is read and then rewritten using {{{as_string()}}}, the values will be written in their parsed form, and may not be a heredoc any more. For example:
{{{
[section]
name = <<:parse
one
two
<<
}}}
would be written by as_string() like this:
{{{
[section]
name = one
name = two
}}}
!!Comments Accessor Methods
An Ini file may contain comments. Normally, when your program reads an Ini file, it doesn't care about comments. In Config::Ini::Expanded, keep_comments is false by default.
Set {{{$Config::Ini::Expanded::keep_comments = 1;}}} if you go want the Config::Ini::Expanded object to retain the comments that are in the file. The default is {{{0}}} -- comments are not kept. This applies to {{{new()}}}, {{{init()}}}, and {{{as_string()}}}, i.e., {{{new()}}} and {{{init()}}} will load the comments into the object, and {{{as_string()}}} will output these comments.
Or you can pass the {{{'keep_comments'}}} parameter to the {{{new()}}} or {{{init()}}} methods as described above.
!!!get_comments( $section, $name, $i )
Use {{{get_comments()}}} to return the comments that appear ''above'' a certain name. Since names may be repeated (forming an array of values), pass an array index ({{{$i}}}) to identify the comment desired. If {{{$i}}} is undefined, {{{0}}} is assumed.
{{{
my $comments = $ini->get_comments( $section, $name );
}}}
!!!get_comment( $section, $name, $i )
Use {{{get_comment()}}} (singular) to return the comments that appear ''on'' the same line as a certain name's assignment. Pass an array index ({{{$i}}}) to identify the comment desired. If {{{$i}}} is undefined, {{{0}}} is assumed.
{{{
$comment = $ini->get_comment( $section, $name );
}}}
!!!set_comments( $section, $name, $i, @comments )
Use {{{set_comments()}}} to specify comments for a given occurrence of a name. When {{{as_string()}}} is called, these comments will appear ''above'' the name.
{{{
$ini->set_comments( $section, $name, 0, 'Hello World' );
}}}
In an Ini file, comments must begin with {{{'#'}}} or {{{';'}}} and end with a newline. If your comments don't, {{{'# '}}} and {{{"\n"}}} will be added.
!!!set_comment( $section, $name, $i, @comments )
Use {{{set_comment()}}} to specify comments for a given occurrence of a name. When {{{as_string()}}} is called, these comments will appear ''on'' the same line as the name's assignment.
{{{
$ini->set_comment( $section, $name, 0, 'Hello World' );
}}}
In an Ini file, comments must begin with {{{'#'}}} or {{{';'}}}. If your comments don't, {{{'# '}}} will be added. If you pass an array of comments, they will be strung together on one line.
!!!get_section_comments( $section )
Use {{{get_section_comments()}}} to retrieve the comments that appear ''above'' the {{{[section]}}} line, e.g.,
{{{
# Comment 1
[section] # Comment 2
# $comments eq "# Comment 1\n"
my $comments = $ini->get_section_comments( $section );
}}}
!!!get_section_comment( $section )
Use {{{get_section_comment()}}} (note: singular 'comment') to retrieve the comment that appears ''on'' the same line as the {{{[section]}}} line.
{{{
# Comment 1
[section] # Comment 2
# $comment eq " # Comment 2\n"
my $comment = $ini->get_section_comment( $section );
}}}
!!!set_section_comments( $section, @comments )
Use {{{set_section_comments()}}} to set the value of the comments above the {{{[section]}}} line.
{{{
$ini->set_section_comments( $section, $comments );
}}}
!!!set_section_comment( $section, @comments )
Use {{{set_section_comment()}}} (singular) to set the value of the comment at the end of the {{{[section]}}} line.
{{{
$ini->set_section_comment( $section, $comment );
}}}
!!Recreating the Ini File Structure
!!!as_string()
Use {{{as_string()}}} to dump the Config::Ini::Expanded object in an Ini file format. If {{{$Config::Ini::Expanded::keep_comments}}} is true, the comments will be included.
{{{
print INIFILE $ini->as_string();
}}}
The value {{{as_string()}}} returns is not guaranteed to be exactly what was in the original Ini file. This is particularly true for files processed by this module, because quoted values may be interpolated right away. For this reason, if you need to edit an Ini file, use the Config::Ini::Edit module.
But barring all that, you can expect the following:
- All sections and names will be retained.
- All values will resolve correctly, i.e., a call to {{{get()}}} will return the expected value.
- All comments will be present (if {{{'keep_comments'}}} is true).
- As many value attributes as possible will be retained, e.g., quotes, escapes, indents, etc. But the {{{':parse'}}} modifier will ''not'' be retained.
- If the same section appears multiple times in a file, all of its options will be output in only one occurrence of that section, in the position of the original first occurrence. E.g.,
{{{
[section1]
name1 = value
[section2]
name2 = value
[section1]
name3 = value
}}}
will be output as
{{{
[section1]
name1 = value
name3 = value
[section2]
name2 = value
}}}
(Note that as_string() inserts a blank line between sections if there is not a comment there.)
- If the same name appears multiple times in a section, all of its occurrences will be grouped together, at the same position as the first occurrence. E.g.,
{{{
[section]
name1 = value1
name2 = value2
name1 = another
}}}
will be output as
{{{
[section]
name1 = value1
name1 = another
name2 = value2
}}}
!SEE ALSO
Config::Ini, Config::Ini::Edit Config::Ini::Quote, Config::~IniFiles, Config:: ... (many others)
!AUTHOR
Brad Baxter, <bmb@mail.libs.uga.edu>
!COPYRIGHT AND LICENSE
Copyright (C) 2010 by Brad Baxter
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available.
[[Previous Page|Config::Ini::Expanded Page 2]] | [[Page 1|Config::Ini::Expanded]] | [[Page 2|Config::Ini::Expanded Page 2]] | ''Page 3''
!NAME
Config::Ini::Quote - Quoting strings for Config::Ini modules
!SYNOPSIS
{{{
use Config::Ini::Quote ':escape';
binmode(STDOUT, ":utf8");
my $smiley = qq{\t"smiley":\x{263a}\n};
$smiley = escape_for_double_quoted( $smiley );
print $smiley; # literally: \t"smiley":\u263A\n
$smiley = unescape_for_double_quoted( $smiley );
print $smiley; # same as: print qq{\t"smiley":\x{263a}\n};
use Config::Ini::Quote ':all';
print as_double_quoted( $smiley );
# literally: "\t\"smiley\":\u263A\n"
print as_single_quoted( $smiley );
# same as: print qq{'\t"smiley":\x{263a}\n'};
print as_heredoc( value => $smiley,
heretag => 'EOT', quote => '"' );
# literally:
# <<"EOT"
# \t"smiley":\u263A
# EOT
$smiley =~ s/"/\\"/g; # prepare for parse
print parse_double_quoted( qq{"$smiley"} );
# same as: print qq{\t"smiley":\x{263a}\n};
print parse_single_quoted( "'''Hello, World.'''" );
# 'Hello, World.'
}}}
!VERSION
VERSION: 1.01
!DESCRIPTION
This module is designed to provide backslash-escaped character support for the Config::Ini::Edit and Config::Ini::Expanded modules.
If requested, it will also interpret HTML entities in double quoted strings (using HTML::Entities).
!FUNCTIONS
No functions are exported by default. The following are exported with {{{':escape'}}}, e.g.
{{{
Use Config::Ini::Quote qw( :escape );
unescape_for_double_quoted()
escape_for_double_quoted()
}}}
All functions can be exported with {{{':all'}}}, e.g.,
{{{
Use Config::Ini::Quote qw( :all );
}}}
!!escape_for_double_quoted( $to_escape, $escape )
Escapes unprintable and unicode characters.
{{{
my $e = escape_for_double_quoted( $s );
}}}
The parameter, $escape, may contain any combination of these strings: {{{':slash'}}}, {{{':html'}}}. If $escape is false (e.g., {{{undef}}} or {{{''}}}), the default is {{{':slash'}}}, which encodes the following characters:
{{{
"\x00" as \0 (null)
"\x07" as \a (bell)
"\x08" as \b (backspace)
"\x09" as \t (tab)
"\x0a" as \n (new line)
"\x0b" as \v (vertical tab)
"\x0c" as \f (form feed)
"\x0d" as \r (carriage return)
"\x1b" as \e (ascii escape)
'\\' as \\ (escaped backslash)
"\x85" as \N (Unicode next line)
"\xa0" as \_ (Unicode non-breaking space)
"\x{2028}" as \L (Unicode line separator)
"\x{2029}" as \P (Unicode paragraph separator)
}}}
Other unprintable characters are encoded as {{{'\xXX'}}}, and (wide) Unicode characters as {{{'\uXXXX'}}}, where {{{'XX'}}} and {{{'XXXX'}}} are the hex code for the character.
If {{{$escape}}} matches {{{/:html/}}}, the string is first encoded using HTML::entities::encode's defaults, i.e., it will encode control chars, high-bit chars, and the {{{'<'}}}, {{{'&'}}}, {{{'>'}}}, {{{"'"}}} and {{{'"'}}} characters. If {{{$escape}}} also matches {{{/:slash/}}}, then any remaining characters from the above table, e.g., {{{'\n'}}}, {{{'\t'}}}, etc., will be encoded as above.
!!unescape_for_double_quoted( $to_unescape, $escape );
Converts escaped text to actual characters.
{{{
$smiley = unescape_for_double_quoted( '\t"smiley":\u263a\n' );
}}}
The parameter, {{{$escape}}}, may contain any combination of these strings: {{{':slash'}}}, {{{':html'}}}, as described in {{{escape_for_double_quoted()}}} above. By default, the codes in the above table, e.g., {{{'\n'}}}, {{{'\t'}}}, etc., and codes of the form {{{'\xXX'}}}, {{{'\xXXXX'}}}, and {{{'\uXXXX'}}} will be decoded to their actual characters. (Note that {{{'\xXXXX'}}} is accepted as a synonym for {{{'\uXXXX'}}}.)
If {{{$escape}}} matches {{{/:html/}}}, HTML entities will be decoded using HTML::Entities::decode's defaults. If {{{$escape}}} also matches {{{/:slash/}}}, then other codes, e.g., {{{'\n'}}}, {{{'\t'}}}, etc., will be decoded, too.
If you use {{{':html'}}}, the default escaping technique, {{{':slash'}}}, will be disabled unless you explicitly include it, too, e.g., {{{$escape eq ':html:slash'}}}.
!!as_double_quoted( $to_quote, $escape )
Double quotes a string -- with character escapes; double quotes are escaped as {{{\"}}}.
{{{
$e = as_double_quoted( $s );
}}}
Characters in the string will be escaped using {{{escape_for_double_quoted()}}} as described above, including how the {{{$escape}}} parameter works. Double quotes in the string will be escaped as {{{'\"'}}}. The value returned will begin and end with a double quote {{{'"'}}}.
!!as_single_quoted( $to_quote )
Single quotes a string -- without character escapes; single quotes are escaped by doubling: {{{"''"}}}. The value returned will begin and end with a single quote {{{"'"}}}.
{{{
$e = as_single_quoted( $s );
}}}
!!as_heredoc( ... )
{{{
as_heredoc(
value => $value,
heretag => $heretag,
herestyle => $herestyle,
quote => $quote,
escape => $escape,
indented => $indented,
comment => $comment,
extra => $extra
);
}}}
'Quotes' a string using heredoc notation.
{{{
$s = qq{\t"smiley":\x{263a}\n};
$e = as_heredoc(
value => $s, heretag => 'EOT',
herestyle => '<<', quote => '"' );
# <<"EOT"
# \t"smiley":\u263A
# EOT
}}}
The {{{'heretag'}}} parameter is the heredoc tag for the begin and end. If null, the 'tagless' style is used, e.g.,
{{{
$e = as_heredoc( value => $s, quote => '"' );
# <<""
# \t"smiley":\u263A
# <<
}}}
The {{{'herestyle'}}} parameter is one of: {{{'<<'}}}, {{{'<<<<'}}}, {{{'{'}}}, or {{{'{}'}}}. The default is {{{'<<'}}}. Examples may explain best:
{{{
$e = as_heredoc(
value => "Hey\n", heretag => 'EOT',
herestyle => '<<' ); # the default
# <<EOT
# Hey
# EOT
$e = as_heredoc(
value => "Hey\n", heretag => 'EOT',
herestyle => '<<<<' );
# <<EOT
# Hey
# <<EOT
$e = as_heredoc(
value => "Hey\n", heretag => 'EOT',
herestyle => '{' );
# {EOT
# Hey
# EOT
$e = as_heredoc(
value => "Hey\n", heretag => 'EOT',
herestyle => '{}' );
# {EOT
# Hey
# }EOT
}}}
The {{{'quote'}}} parameter should match {{{'/^\s*["d's]/i'}}}, e.g., {{{quote => '"'}}}, {{{quote => 'double'}}} (double quote), {{{quote => "'"}}}, {{{quote => 'single'}}} (single quote).
Unlike Perl's heredocs, if {{{'quote'}}} is not specified, single quote is the default.
If double quote, the text in the heredoc is escaped using {{{escape_for_double_quoted()}}}, except that {{{"\n"}}} and {{{"\t"}}} and double quotes are not escaped. A double quote is placed around the tag and modifiers.
{{{
$s = qq{one\t"fish"\ntwo\t'fish'\n};
$e = as_heredoc( value => $s, heretag => 'EOT', quote => '"' );
# <<"EOT"
# one "fish"
# two 'fish'
# EOT
}}}
If single quote or no quote (single quote implicit), the text is left alone. Single quotes in the string are not escaped. If single quote is explicit, a single quote is placed around the tag and modifiers.
{{{
$e = as_heredoc( value => $s, heretag => 'EOT', quote => "'" );
# <<'EOT'
# one "fish"
# two 'fish'
# EOT
$e = as_heredoc( value => $s, heretag => 'EOT' );
# <<EOT
# one "fish"
# two 'fish'
# EOT
}}}
Whether single or double quoted, if the string does not end with a newline, the :chomp modifier is added
{{{
chomp $s;
$e = as_heredoc( value => $s, heretag => 'EOT' );
# <<EOT:chomp
# one "fish"
# two 'fish'
# EOT
}}}
The {{{'escape'}}} parameter is the same as described in {{{escape_for_double_quoted()}}} above, i.e., it contains {{{':slash'}}} and/or {{{':html'}}}, defaulting to {{{':slash'}}}. It is recognized only with double quotes. The value of {{{'escape'}}} is appended to the heredoc tag, so the same types of quote escapes to be honored when the Ini file is read later.
{{{
$s = "vis-à -vis Beyoncé's naïve\n" .
"\tpapier-mâché résumé";
$e = as_heredoc(
value => $s, heretag => 'EOT',
herestyle => '{}', quote => 'double',
escape => ':slash' );
# {"EOT:chomp:slash"
# vis-\xE0-vis Beyonc\xE9's na\xEFve
# \tpapier-m\xE2ch\xE9 r\xE9sum\xE9
# }EOT
$e = as_heredoc(
value => $s, heretag => 'EOT',
herestyle => '{}', quote => 'double',
escape => ':html' );
# {"EOT:chomp:html"
# vis-à-vis Beyoncé's naïve
# papier-mâché résumé
# }EOT
$e = as_heredoc(
value => $s, heretag => 'EOT',
herestyle => '{}', quote => 'double',
escape => ':html:slash' );
# {"EOT:chomp:html:slash"
# vis-à-vis Beyoncé's naïve
# \tpapier-mâché résumé
# }EOT
}}}
The {{{'indented'}}} parameter will indent the value. If {{{'indented'}}} is whitespace, e.g., {{{'indented' => "\t"}}}, then that value will be inserted at the beginning of each line. Otherwise, if {{{'indented'}}} is true, then four spaces will be inserted.
{{{
$e = as_heredoc(
value => "The quick brown fox\n" .
"jumped over the lazy dog",
indented => 1 );
# <<:indented
# The quick brown fox
# jumped over the lazy dog
# <<
}}}
The {{{'comment'}}} parameter provides for a comment on the same line as the beginning of the heredoc. A comment is only allowed if there are quotes, so if quote is not given, the {{{'comment'}}} parameter is silently ignored.
If the comment does not begin with {{{'#'}}} or {{{';'}}}, {{{'# '}}} will be added to it.
{{{
$e = as_heredoc( value => "Hey", heretag => 'EOT',
quote => "'", comment => 'This is a comment' );
# <<'EOT:chomp' # This is a comment
# Hey
# EOT
}}}
The {{{'extra'}}} parameter allows you to pass along to {{{as_heredoc()}}} any modifiers that you want it to include, whether or not {{{as_heredoc()}}} recognizes them.
The value of extra will be added to any other modifiers that {{{as_heredoc()}}} would normally include.
{{{
$e = as_heredoc( value => '{"a":1,"b":2,"c":3}',
extra => ':json' );
# <<:chomp:json
# {"a":1,"b":2,"c":3}
# <<
}}}
In the above example {{{':json'}}} is not a modifier that Config::Ini::Quote recognizes. But Config::Ini::Edit does, and {{{as_heredoc()}}} passes it along.
!!parse_double_quoted( $to_parse, $types )
Parses a double-quoted string, unescaping escaped characters.
{{{
$s = parse_double_quoted( q{"\t\\"smiley\\":\\u263a\n"} );
}}}
It expects the value to begin and end with double quotes; other double quotes must be escaped {{{\"}}}.
!!parse_single_quoted( $to_parse )
Parses a single-quoted string; recognizes no escapes except doubled single quotes: {{{''}}}.
{{{
$s = parse_single_quoted( "'''Hello, World.'''" );
}}}
It expects the value to begin and end with single quotes; other single quotes must be escaped {{{"''"}}}.
!SEE ALSO
Config::Ini::Edit, Config::Ini::Expanded
!AUTHOR
Brad Baxter, <bmb@mail.libs.uga.edu>
!COPYRIGHT AND LICENSE
Copyright (C) 2010 by Brad Baxter
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available.
!Configuration Files
The configuration files, ''program.ini'' and ''members.ini'', may be edited by supervisor level users via links on the [[Admin page|AdminPage]].
!!program.ini
The central configuration file for the mapping tool program, ''program.ini'' contains [[Global Configuration Sections]] and [[Page Template Sections]]. When the program runs and ingests the ''program.ini'' file, it also includes the ''members.ini'' content via an "{INCLUDE:...}" template placeholder.
!!members.ini
Included into ''program.ini'', ''members.ini'' contains [[Member Definition Sections]] that define attributes for GKR and for all of the member institutions
!Documentation for Modules and Libraries
Obviously, this entire wiki is documentation. But this section is designed to provide general documentation for Perl modules and Javascript libraries that are not specific to the mapping tool. That is, the mapping tool uses these modules, but they were not written specifically for the mapping tool. So none of this documentation even mentions the mapping tool.
!!Local Perl Modules
;[[Config::Ini]]
:This is the base ini file processing module.
;[[Config::Ini::Edit]]
:This module allows the program to update the ini file on disk.
:The docs are long enough to need two pages; see also [[Config::Ini::Edit Page 2]].
;[[Config::Ini::Expanded]]
:This module has built-in templates and file includes.
:The docs are long enough to need three pages; see also [[Config::Ini::Expanded Page 2]], [[Config::Ini::Expanded Page 3]].
;[[Config::Ini::Quote]]
:This module is used by the other Config::Ini modules.
;[[Tree::Indented]]
:This module allows the program to parse indented text into a simple tree structure.
:Much of the key data, including the communities/collections and mappings, is stored as indented text.
!!Local Javascript Libraries
;[[Table Maker]]
:This library defines table_maker() and other functions that display lists and divs as tables
!GKR Mapping Tool Edit Blurb Page
{{thumb{[img[GKR_Edit_Blurb_thumb.jpg][EditBlurbPageImage]]}}} [[Image|EditBlurbPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_blurb;parms=action%3dhome]]
!!Locations
* dev: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_blurb;parms=action%3dhome
!!Page Elements
* The page header indicates the blurb that is being edited, e.g., "home_blurb" is the blurb for the home page.
* The input box contains the existing blurb, which is a snippet of XHTML.
* The "Save Changes" button will submit your changes, "Reset" will undo your current changes (standard HTML form reset function).
!!Notes
Only Supervisor level users may edit blurbs. The link, "Edit blurb", will be present in the banner.
The ~URLs shown above contain an extra parameter, "parms=action%3dhome". This is intended to help the program redirect the user back to the original page after clicking Save Changes.
The blurb contains a couple of "template placeholders" (more than these are available):
{{{
{INI:program:title}
{INI:program:gkr}
}}}
When the blurb is displayed, these placeholders will be replaced with text from the configuration file (an ini file). For example, the ini file for the GKR mapping tool contains the following settings:
{{{
[program]
gkr = Georgia Knowledge Repository
title = {INI:program:gkr} Mapping Tool
logo = /images/gkr_logo.gif
url = /cgi-bin/gkr_mapping
...
}}}
So in this case, the placeholder, <html><code>{INI:program:gkr}</code></html>, will become "Georgia Knowledge Repository" because it is the value of the "gkr" setting in the "program" section. And the placeholder, <html><code>{INI:program:title}</code></html> will become "Georgia Knowledge Repository Mapping Tool (because the "gkr" placeholder is filled in there, too).
Template placeholders are discussed in more depth in TemplatePlaceholders and in ''much'' more depth in [[Config::Ini::Expanded]].
!GKR Mapping Tool Edit Blurb Page Image
{{image{[img[GKR_Edit_Blurb.png]]}}}
!Edit File Page
{{thumb{[img[GKR_EditFilePage_thumb.jpg][EditFilePageImage]]}}} [[Images|EditFilePageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_blurb]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_blurb (but your permissions may not allow this)
!!Page Elements
* The file contents display in the text area input box.
* Click "Save Changes" to save your changes.
!Edit File Page Image
{{image{[img[GKR_EditFilePage.png]]}}}
!GKR Mapping Tool Edit Help Page
{{thumb{[img[GKR_Edit_Help_thumb.jpg][EditHelpPageImage]]}}} [[Image|EditHelpPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_help;parms=action%3dhelp%3bpage%3dhome]]
!!Locations
* dev: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_help;parms=action%3dhelp%3bpage%3dhome
!!Page Elements
* The page header indicates the help text that is being edited, e.g., "home_help" is the help text for the home page.
* The input box contains the existing help text, which is a snippet of XHTML.
* The "Save Changes" button will submit your changes, "Reset" will undo your current changes (standard HTML form reset function).
!!Notes
Only Supervisor level users may edit help text. When viewing a help page, the link, "Edit help", will be present in the banner.
The ~URLs shown above contain an extra parameter, "parms=action%3dhelp%3bpage%3dhome". This is intended to help the program redirect the user back to the original page after clicking Save Changes.
As we saw in the blurb in EditBlurbPage, the help text contains a some [[template placeholders|TemplatePlaceholders]] (again, more than these are available):
{{{
{VAR:page}
{INI:{VAR:page}:title}
}}}
When the help text is displayed, these placeholders will be replaced with text both generated by the program ({{code{ {VAR:...} }}}) and gotten from the configuration (ini) file ({{code{ {INI:...} }}}). For example, the ini file for the GKR mapping tool contains the following settings:
{{{
[program]
gkr = Georgia Knowledge Repository
title = {INI:program:gkr} Mapping Tool
logo = /images/gkr_logo.gif
url = /cgi-bin/gkr_mapping
...
[home]
...
title = {INI:program:title}
help = {FILE:home_help}
blurb = {FILE:home_blurb}
page = '{FILE:home_page}' # cookie gets set here (at login)
...
}}}
In the case of the to placeholders in the home_help text, the following chain of substitutions occurs:
# The placeholder, "{{code{ {VAR:code} }}}", is filled in by the program with "''home''", which is the "page code" for the home page.
# The placeholder, "{{code{ {INI:{VAR:code}:title} }}}", then becomes a new placeholder, "{{code{ {INI:home:title} }}}".
# "{{code{ {INI:home:title} }}}" becomes "{{code{ {INI:program:title} }}}" (because that's the value stored in the ini file).
# "{{code{ {INI:program:title} }}}" becomes "{{code{ {INI:program:gkr} Mapping Tool}}}", and finally,
# "{{code{ {INI:program:gkr} Mapping Tool}}}" becomes "''Georgia Knowledge Repository Mapping Tool''".
This illustrates that template placeholders may be nested and chained in whatever way makes sense. The above makes sense because if the title of the GKR ever needs to change, it only needs to be done in one place. The changed value will be picked by any of the placeholders that reference it.
Template placeholders are discussed in more depth in TemplatePlaceholders and in ''much'' more depth in [[Config::Ini::Expanded|Config::Ini::Expanded Page 2]].
!GKR Mapping Tool Edit Help Page Image
{{image{[img[GKR_Edit_Help.png]]}}}
!GKR Mapping Tool "Edit Page" Page
{{thumb{[img[GKR_Edit_Page_thumb.jpg][EditPagePageImage]]}}} [[Image|EditPagePageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_page;parms=action%3dhome]]
!!Locations
* dev: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_page;parms=action%3dhome
!!Page Elements
* The page header indicates the page template that is being edited, e.g., "home_page" is the page template for the home page.
* The input box contains the existing page template, which will become a full XHTML page once all the [[template placeholders|TemplatePlaceholders]] are filled in.
* The "Save Changes" button will submit your changes, "Reset" will undo your current changes (standard HTML form reset function).
!!Notes
Only Supervisor level users may edit page templates. The link, "Edit page", will be present in the banner.
The ~URLs shown above contain an extra parameter, "parms=action%3dhome". This is intended to help the program redirect the user back to the original page after clicking Save Changes.
As we saw in EditBlurbPage and EditHelpPage, the page template contains [[template placeholders|TemplatePlaceholders]] -- a lot of them. Some examples:
{{{
{VAR:cookie}
{INI:program:page_doctype_header}
}}}
Other examples (placeholders we didn't discuss before -- and that aren't visible in the page image here):
{{{
<ul>
{LOOP:members_loop}<li><a href="{INI:program:url}?member={LVAR:code}">
{LVAR:name} ({LVAR:code})</a>
<a href="{INI:program:rss_url}/{LVAR:code}.rdf">{INI:program:rss_icon}</a></li>
{END_LOOP:members_loop}</ul>
}}}
Note: "{{{ {LOOP:...}...{END_LOOP:...} }}}" defines markup for multiple elements, "members" in this case. And "{{{ {LVAR:...} }}}" marks the spot for the "loop variables" for each member.
We'll skip any more discussion of loops here in favor of again pointing to the in-depth treatments in TemplatePlaceholders and [[Config::Ini::Expanded]].
!GKR Mapping Tool "Edit Page" Page Image
{{image{[img[GKR_Edit_Page.png]]}}}
!GKR Mapping Tool Edit User Page
{{thumb{[img[GKR_Edit_User_admin_thumb.jpg][EditUserPageImage]]}}} {{thumb{[img[GKR_Edit_User_coordinator_thumb.jpg][EditUserPageImage]]}}} {{thumb{[img[GKR_Edit_User_supervisor_thumb.jpg][EditUserPageImage]]}}} [[Images|EditUserPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=gkr_user]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=gkr_user (but you might not have permission to go there ...)
!!Page Elements
!!!Admin Users
* An Admin level user may edit only his own user information.
* Not all of the information may be edited. The restricted parts will appear grayed out (disabled). (They still appear because it can be useful information.)
* Information that may be edited includes Name, E-mail, Note, and Password.
!!!Coordinators
* A Coordinator level user may edit his own user information and that of any admin users in his group.
* Again, not all of the information may be edited.
* Information that may be edited: Name, E-mail, Note, Password.
* Additionally a coordinator may enter a different User ID. However, this does not mean that the new ID will replace the old ID for the same user. Rather, it would be the same as adding a new user (or overwriting the user that may already exist with that ID).
* To change a user's ID, you must add a new user and delete the old one.
* So coordinators have an additional form element: Delete User. As with editing, a coordinator may delete any admin user in his group (and may even delete his own user record).
!!!Supervisors
* Supervisor level users may edit all information for any user and may delete any user.
!GKR Mapping Tool Edit User Page Images
!!Admin User
{{image{[img[GKR_Edit_User_admin.png]]}}}
!!Coordinator
{{image{[img[GKR_Edit_User_coordinator.png]]}}}
!!Supervisor
{{image{[img[GKR_Edit_User_supervisor.png]]}}}
!Error Page
{{thumb{[img[GKR_ErrorPage_thumb.jpg][ErrorPageImage]]}}} [[Images|ErrorPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=SantaClaus]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=SantaClaus
!!Page Elements
* The error message appears in the gray area.
* Otherwise, the page contains minimal links.
!Error Page Image
{{image{[img[GKR_ErrorPage.png]]}}}
Below are some of the usual template placeholders you'll see (and can use) in blurb text.
!Settings from the ini file
You can use the "{{code{ {INI:section:name} }}}" construct to insert any of the values defined in the configuration file, e.g.,
;{INI:program:gkr}
:The value of the ''gkr'' setting in the [program] section
;{INI:program:title}
:The value of the ''title'' setting in the [program] section
;etc.
!Values generated by the program
Depending on the action being performed, you can use the "{{code{ {VAR:variable_name} }}}" construct to insert values that the program has set up for the particular page, e.g.,
;{VAR:name}
:The institution name (sometimes)
:The user name (sometimes)
;{VAR:code}
:The institution code
;{VAR:id}
:A User ID
;{VAR:collection}
:The collection name
;{VAR:file}
:The file name
;{VAR:version}
:The file version number
;{VAR:message}
:An error message
;{VAR:page}
:The page "code"
!Conditional tests
Based on values generated by the program, you can set up conditional statements to include or exclude portions of text in the blurbs, e.g.,
;{VAR:logged_in}
:This will have the value "1" if the user id logged in.
;{~IF_VAR:logged_in} "logged in text" {~ELSE_IF_VAR:logged_in} "not logged in text" {~END_IF_VAR:logged_in}
:The "{{code{ {~IF_VAR:variable_name} }}}" construct (with the mandatory "{{code{ {~END_IF_VAR:...} }}}" and the optional "{{code{ {~ELSE_IF_VAR:...} }}}") can be used to display text based on whether or not the user is logged in.
Other examples of boolean values suitable for conditional tests, some of which are limited to certain pages:
;{VAR:update_ok} ([[Manage Data|MemberManageDataPage]])
:This will have the value "1" if the institution has a communities page defined that can be used to update the collections.
;{VAR:gkr} ([[Manage Data|MemberManageDataPage]])
:This will have the value "1" if the institution is GKR.
;{VAR:add_ok} ([[Users|UsersPage]])
:This will have the value "1" if you are viewing the Users page, and you're a coordinator or supervisor.
;{VAR:delete_ok} ([[Edit User|EditUserPage]])
:This will have the value "1" if you are viewing the Edit User page, and you're a coordinator or supervisor.
;{VAR:is_supervisor} (any page)
:This will have the value "1" if you are a supervisor.
;{VAR:edit} (any page)
:This will have the value "1" if you are able to edit mappings and collections.
;{VAR:publish} (any page)
:This will have the value "1" if the mappings file is newer than the rss file (i.e., if there are unpublished changes in the mappings data).
:If the institution is GKR, this will also have a value of "1" if the ''members.ini'' file is newer than the rss file (i.e., if there might be changes to the member names that we want to show in the GKR rss feed).
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
!Functional Descriptions by Page
Following are the significant pages that display in the mapping tool. For the sake of discussion, they are informally (and approximately) grouped below by scope and purpose. In the mapping tool, they will be displayed as appropriate.
!!General
<<<
;HomePage
:The home page for the mapping tool; lists all of the member institutions
;LoginPage
:The login page for the mapping tool; prompts for userid and password
;HelpPage
:One of many help pages -- every significant page in the mapping tool has a corresponding help page
;ErrorPage
:General purpose page for displaying error messages or informational messages.
<<<
{{center{
[img[featurebottom.png]]}}}
!!GKR Collections
<<<
;GKRCollectionsPage
:The {{bold{GKR Communities and Collections}}} page, showing the central GKR IR collections
;GKRManageDataPage
:The {{bold{Manage Data Files}}} page for the GKR collections; provides links for viewing and editing
;GKRManageDataViewPage
:For viewing the GKR collections in their internal format
;GKRManageDataEditPage
:For editing the GKR collections internal format
;GKRChangesPage
:For examining a list of previous versions as a file has changed over time
;GKRChangesDiffPage
:For viewing a "diff" (differences) display between a current file and a previous version
;GKRPublishPage
:Confirmation page after clicking "Publish". For GKR, since there aren't any mappings (GKR collections aren't mapped {{bold{to}}} anything), the rss feed simply lists links for the member institutions.
<<<
{{center{
[img[featurebottom.png]]}}}
!!Member Insitutions Collections
<<<
;MemberPage
:A member institution's list of IR collections
;MemberMappingPage
:A mapping page for an example collection
;MemberManageDataPage
:The {{bold{Manage Data Files}}} page for a member institution's collections
;MemberManageDataViewPage
:For viewing the member institution's collections in their internal format
;MemberManageDataEditPage
:For editing the member insitution's collections internal format
;MemberViewMappingsPage
:For viewing the member institution's mappings internal format
;MemberEditMappingsPage
:For editing the member institution's mappings internal format
;MemberChangesPage
:For examining a list of previous versions as a file has changed over time
;MemberChangesDiffPage
:For viewing a "diff" (differences) display between a current file and a previous version
;MemberPublishPage
:Confirmation page after clicking "Publish"; lists links to all of the various ways the mapping tables may be incorporated into harvesting (or even reporting) programs.
<<<
{{center{
[img[featurebottom.png]]}}}
!!Mapping Tool Users
<<<
;UsersPage
:A list of the users who may log in to the mapping tool
;EditUserPage
:An input form where user profile data may be changed
;AddNewUserPage
:An input form where new users may be created by coordinators or supervisors
<<<
{{center{
[img[featurebottom.png]]}}}
!!Administrative
<<<
;EditBlurbPage
:One of many such pages, where a page's "blurb" (short help text) may be changed by supervisors
;EditHelpPage
:For every help page there is an edit page where the help text may be edited by supervisors
;EditPagePage
:Every page's page template may be edited by supervisors
;AdminPage
:The central administrative page available to supervisors where configuration files may be edited.
;ViewFilePage
:General purpose page for viewing files not mentioned above.
;EditFilePage
:General purpose page for editing files not mentioned above.
<<<
!GKR Manage Data Files, Changes Diff Page
{{thumb{[img[GKR_Changes_Diff_thumb.jpg][GKRChangesDiffPageImage]]}}} [[Images|GKRChangesDiffPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=diff;file=gkr_coll;version=.20091117.005]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=diff;file=gkr_coll;version=.20091117.005 (but this version number might cease to exist ...)
!!Page Elements
* Below the blurb is a display of differences between the current file, gkr_coll and the previous version, gkr_coll.20091117.005.
* The differences are shown in red and green with a plus '+' or minus '-' in the first character position.
!!Notes
{{bold{All}}} of the "Diff" pages for any file that can be edited online will be very similar to this one.
!GKR Manage Data Files, Changes Diff Page Image
{{image{[img[GKR_Changes_Diff.png]]}}}
!GKR Manage Data Files, Changes Page
{{thumb{[img[GKR_Changes_thumb.jpg][GKRChangesPageImage]]}}} [[Images|GKRChangesPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=gkr_coll;member=gkr]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=gkr_coll;member=gkr
!!Page Elements
* Below the blurb is a list of the previous versions of the collections data file.
* Clicking "View" will display a page just like the [[View|GKRManageDataViewPage]] page discussed earlier, where you can view the selected version.
* Clicking "View diff" will display the [[Diff|GKRChangesDiffPage]] page, showing differences between the listed version and the current file (gkr_coll).
* Clicking "Recover" to replace the current file with that previous version.
!!Notes
The Change page for the GKR mappings is very similar to this page.
In fact {{bold{all}}} of the "Changes" pages for any file that can be edited online will be very similar to this one.
Note that there isn't a Recover page. After clicking "Recover" (and OK in the confirmation box), the Changes page will simply redisplay with a new backup file at the top of the list.
!GKR Manage Data Files, Changes Page Image
{{image{[img[GKR_Changes.png]]}}}
!GKR Communities and Collections
{{thumb{[img[GKR_Collections_thumb.jpg][GKRCollectionsPageImage]]}}} [[Image|GKRCollectionsPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr
!!Page Elements
* Below the blurb is the list of the GKR communities (in gray) and collections (in blue).
* The "Close All" link will collapse the list. "Open All" will expand it again.
!!Banner Links
!!!Public
* Before logging in, users will see one additional link, "Manage data". This link displays the [[Manage Data Files|GKRManageDataPage]] page. Public users may view the data files, while users that are logged in may also edit them.
{{image{[img[GKR_Collections_Banner_public.png]]}}}
!!!Admin and Coordinator
* After logging in, Admin level and Coordinator level users may also see a link, "[[Publish|MemberPublishPage]]". This link will display if there are changes in the mapping tool that have not been "published", i.e., that have not been included in the mapping data that the harvester will access.
{{image{[img[GKR_Collections_Banner_admin.png]]}}}
!!!Supervisor
* A Supervisor level user will again see some additional links.
{{image{[img[GKR_Collections_Banner_supervisor.png]]}}}
!GKR Communities and Collections Page Image
{{image{[img[GKR_Collections.png]]}}}
!GKR Manage Data Files, Edit Page
{{thumb{[img[GKR_Manage_Data_Edit_thumb.jpg][GKRManageDataEditPageImage]]}}} [[Images|GKRManageDataEditPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_coll;member=gkr]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_coll;member=gkr
!!Page Elements
* Below the blurb is the internal data representation of Georgia Knowledge Repository's communities (type {{code{cm}}}), and collections (type {{code{cl}}}).
!!Notes
Since the GKR collections data and GKR "mappings" data are always the same (after both are updated), the View page for GKR mappings would look the same as this example.
Note also that the data representation shown is {{bold{exactly}}} how the data is stored. One could conceivably cut and paste this out into an external management tool, update it there, and cut and paste it back into this page.
!GKR Manage Data Files, Edit Page Image
{{image{[img[GKR_Manage_Data_Edit.png]]}}}
!GKR Manage Data Files
{{thumb{[img[GKR_Manage_Data_public_thumb.jpg][GKRManageDataPageImage]]}}} {{thumb{[img[GKR_Manage_Data_admin_thumb.jpg][GKRManageDataPageImage]]}}} [[Images|GKRManageDataPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=manage_data]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=manage_data
!!Page Elements
!!!Public
* Before logging in, users will see "[[View|GKRManageDataViewPage]]" links for the collections data file and the mappings data file, allowing public users to view the data.
!!!Admin
* After logging in, users will see additional links, "[[Edit|GKRManageDataEditPage]]" and "[[Changes|GKRChangesPage]]".
* Click "View" to view the data, "Edit" to change it, and "Changes" to review past changes and for data recovery.
* In addition, the "Update Collections" link will fetch information from the GKR IR for updating the communities and collections data in the mapping tool.
* Finally, the "Merge GKR Collections with all Mappings" link will merge changes from the GKR collections file with the mapping data for all member institutions.
* The "Update ..." and "Merge ..." links allow the data in the mapping tool to stay up-to-date with the definitions in the GKR IR.
!GKR Manage Data Files Images
!!Public
{{image{[img[GKR_Manage_Data_public.png]]}}}
!!Admin
{{image{[img[GKR_Manage_Data_admin.png]]}}}
!GKR Manage Data Files, View Page
{{thumb{[img[GKR_Manage_Data_View_thumb.jpg][GKRManageDataViewPageImage]]}}} [[Images|GKRManageDataViewPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_coll;member=gkr]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_coll;member=gkr
!!Page Elements
* Below the blurb is the internal data representation (with line numbers added) of Georgia Knowledge Repository's communities (type {{code{cm}}}) and collections (type {{code{cl}}}).
!!Notes
Since the GKR collections data and GKR "mappings" data are always the same (after both are updated), the View page for GKR mappings would look the same as this example.
!GKR Manage Data Files, View Page Image
{{image{[img[GKR_Manage_Data_View.png]]}}}
!GKR Publish Confirmation Page
{{thumb{[img[GKR_Publish_thumb.jpg][GKRPublishPageImage]]}}} [[Images|GKRPublishPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=publish]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=gkr;action=publish
!!Page Elements
* The confirmation message shows a link to the RSS "feed" for the GKR Mapping Tool, e.g., http://gkr-prod.library.gatech.edu/gkr_mapping/rss/gkr.xml
* Clicking that link may display something like the following (depending how your browser displays feeds):
{{image{[img[GKR_RSS_feed.png]]}}}
!GKR Publish Confirmation Page Image
{{image{[img[GKR_Publish.png]]}}}
Several of the sections in ''program.ini'', namely ''[general]'', ''[images]'', ''[css]'', ''[javascript]'', and ''[menu]'', contain settings that are used on every page or on most pages. These settings are detailed below.
![general]
This section defines the following general settings:
;gkr
:The name of the project, i.e., ''Georgia Knowledge Repository''
;title
:The title of the program, i.e., ''{INI:program:gkr} Mapping Tool'', becoming ''Georgia Knowledge Repository Mapping Tool''
;logo
:The logo image displayed in the banner, i.e., ''{INI:images:path}/gkr_logo.gif'', becoming ''/gkr_mapping/images/gkr_logo.gif''
;url
:The url of the mapping program, i.e., ''/cgi-bin/gkr_mapping''
:This is relative to the host but absolute in the ~ScriptAlias directory.
;rss_url
:The url path to the mapper's lookup tables, i.e., ''/gkr_mapping/rss''
:This is relative to the host but absolute in the ~DocumentRoot directory.
:The trailing slash '/' is left off intentionally.
;rss_icon
:The <img> tag used for the small RSS icon on some pages
:Note that this is the full <img> tag, not just a path to the image file.
;page_doctype_header
:The <!DOCTYPE> and <html> tags that begin each page
:(but the <!DOCTYPE> tag is being left off for now because of IE problems)
;page_encoding
:The <meta> tag defining content type and charset
;page_favicon
:The <link> tag defining the small icon
;page_logo
:The <a><img> element used to display the logo in the banner
;page_footer
:The <div> element containing the footer elements and displays on every page
;valid
:The set of links for validating the page's XHTML and CSS
:These links will display if you include {{code{;valid=1}}} in the CGI parameters, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?valid=1
:Note that if you validate the XHTML, it will warn that the <!DOCTYPE> is missing -- as we expect.
:And if you validate the CSS, it will complain about ''-moz-border-radius'' -- which is fine -- and also about ''width'' for some of the media values. That's fine (and expected), too.
:Any other errors/warnings besides those should be investigated.
;head
:This value includes placeholders for the values that are common head elements on all the pages, including ''page_encoding'', ''page_favicon'', and the ''inhead'' values from the ''[javascript]'' and ''[css]'' sections.
;openclose
:This defines the "Open All/Close All" paragraph that appears on some pages
;blurb_hide
:This defines the "show/hide" toggle for the blurb that appears on most pages
![images]
This section defines one setting:
;path
:The url path to image files, i.e., ''/gkr_mapping/images''
:This is relative to the host but absolute in the ~DocumentRoot directory.
:The trailing slash '/' is left off intentionally.
![css]
This section defines the following settings:
;path
:The url path to css files, i.e., ''/gkr_mapping/css''
:This is relative to the host but absolute in the ~DocumentRoot directory.
:The trailing slash '/' is left off intentionally.
;inhead
:The css files that are included in the head of every page, e.g., ''gkr_mapping.css'', ''openclose.css'', ''media_inspector.css''
![javascript]
This section defines the following settings:
;path
:The url path to javascript files, i.e., ''/gkr_mapping/javascript''
:This is relative to the host but absolute in the ~DocumentRoot directory.
:The trailing slash '/' is left off intentionally.
;inhead
:The javascript files that are included in the head of every page, e.g., ''openclose.js'', ''cookies.js'', ''blurb_hide.js'', ''media_inspector.js'', ''table_maker.js''
;inbody
:The javascript code that is included just before the end body tag on every page
:This code executes the layout of the page, i.e., the header, the left and right margins, the body (main content), and the footer.
;reminders
:This code is included on those pages that have a "Save Changes" button.
:Users who are not logged in or are not in the group for that particular institution will see a reminder that they may not Save Changes.
:(Yes, we could just hide the button, but I didn't want to do that. Maybe down the road ...)
;highlight_unmapped
:This code is included on the communities/collections pages that have ''[Mapping]'' links.
:It highlights the ''[Mapping]'' links that have not been mapped yet.
:It also displays the "unmapped" count at the top of the list.
;highlight_checked
:This code is included on the mapping pages -- the ones that display the GKR collections with checkboxes.
:It highlights the collection names whose checkboxes are checked.
:It also defines the highlight() function that toggles the highlighting on and off when the user checks and unchecks a box.
;recover
:This code is included on the various "Changes" pages that contain "Recover" links for recovering previous versions of files.
:It defines ''recover()'', which simply prompts for confirmation when a user clicks "Recover".
;ok
:This code is included on any page that wants to prompt the user for confirmation for whatever link is clicked.
:It defines ''ok()'', which accepts a ''msg'' parameter and then adds some extra helpful text in the confirmation dialog box.
;blurb_hide
:This code is included on any page that has a blurb (i.e., most pages).
:It simply executes the blurb_hide() function when the page is loaded.
:Based on the current value of the "hide" cookie, the blurb will be hidden or not.
![menu]
This section defines values for the links that appear in the right side of the banner, e.g., "Login", "Manage data", "Help", etc. Each of these links is defined individually and then combined into the ''links'' value. Most pages will include this as ''{INI:menu:links}'', but some pages (notably the home page) may define their own combinations of these individual menu settings.
Conditional template placeholders are used to determine when a link should be visible, e.g., ''{~IF_VAR:logged_in}'', ''{~IF_VAR:member}'', etc. The spacing of the values may seem arbitrary, but it's designed to make the html source of the page appropriately indented.
;name
:When users are logged in, this value is their name.
:It includes their group and a link to edit their user profile.
;publish
:If the ''member'' parameter is defined (telling the program which institution to focus on) and there are changes that have not yet been published, this value is a "Publish" link.
;manage_data
:This setting is the "Manage data" link that appears on many pages.
;admin
:If the user is a supervisor, this value is the "Admin" link.
;edit_help
:If the user is a supervisor, this value is the "Edit help" link.
;edit_blurb
:If the user is a supervisor, this value is the "Edit blurb" link.
;edit_page
:If the user is a supervisor, this value is the "Edit page" link.
;users
:If the user is logged in, this value is the "Users" link.
;help
:If the user is logged in, this value is the "Help" link.
;login_logout
:If the user is logged in, this value is the "Logout" link, otherwise, it's the "Login" link.
;quick
:This value is not currently being used; it's left behind in case we decide we do want it.
:It defines the same links as the ''quick_list'' setting below, but the links are arranged in one line horizontally instead of being in a vertical list.
;quick_list
:This value defines the "Quick Links" menu, which contains links to each of the Communities and Collections pages.
;links
:This value combines all of the above settings into one full set of menu links.
!GKR Mapping Tool Help
{{thumb{[img[GKR_Help_thumb.jpg][HelpPageImage]]}}} [[Image|HelpPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=help;page=home]]
!!Locations
* dev: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=help;page=home
!!Page Elements
* The page header indicates the page to which the help pertains, e.g., {{bold{Help: home}}}
* Below the blurb, in the main body of the page is the help text for the referrenced page.
* In the left margin, below the Quick Links, is the "Help Menu", that contains links to all of the help pages.
!GKR Mapping Tool Help Page Image
{{image{[img[GKR_Help.png]]}}}
!Georgia Knowledge Repository (GKR) Mapping Tool Home
{{thumb{[img[GKR_Home_thumb.jpg][HomePageImage]]}}} [[Image|HomePageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=home]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/gkr_mapping/
!!Page Elements
* The banner logo image is a link to the home page.
* Above the page header are breadcrumbs.
* Below the page header is a "blurb" -- a short bit of help text.
* Below the blurb is the main body of the page, a list of links to the GKR's and member institutions' collections.
* In the left margin are "Quick Links" to this same list of links.
* Finally, in the footer is a [[Contact Us|http://www.galileo.usg.edu/contact/]] link.
!!Banner Links
!!!Public
* Before logging in, the only other link in the banner is "Login".
{{image{[img[GKR_Banner_public.png]]}}}
!!!Admin and Coordinator
* After logging in, an Admin level and Coordinator level user will see more links, including a link to the [[Users|UsersPage]] and [[Help|HelpPage]] pages.
{{image{[img[GKR_Banner_admin.png]]}}}
!!!Supervisor
* A Supervisor level user will see some additional links, including a link to the [[Admin|AdminPage]] page and to edit screens where page content can be changed.
{{image{[img[GKR_Banner_supervisor.png]]}}}
!Georgia Knowledge Repository (GKR) Mapping Tool Home Page Image
{{image{[img[GKR_Home.png]]}}}
!Tutorial: How to Add a Member Institution
These steps cover the steps required to add a new member institution to the GKR mapping tool. Only supervisor-level users have high enough privileges to do this.
!!Vital Statistics
Before an institution can be added to the mapping tool, the following information needs to be gathered:
;Institution Name
:e.g., ''Georgia Institute of Technology'', ''Georgia State University''
;Institution Code (lowercase)
:e.g., ''gatech'', ''gsu'' (this is usually taken from their Web domain, e.g., www.''gatech''.edu, 'www.''gsu''.edu
;Institution "Quick" Code (mixed case)
:e.g., ''~GaTech'', ''GSU''
:This code is used for the "Quick Links" menu and should be the same as the institution code except for upper/lower case.
;Institution IR Handle
:e.g., ''1853'', ''gsu.edu''
:For ~DSpace ~IRs, this is the institution handle that appears in ~URLs, e.g., ''http://smartech.gatech.edu/handle/1853/79''
:For Digital Commons ~IRs, there isn't really a concept of an institution handle, so we just use the domain, e.g., ''gsu.edu''
;Base IR URL
:e.g., ''http://smartech.gatech.edu/handle/1853'', ''http://digitalarchive.gsu.edu''
:We should be able to add a collection handle to the end of the "base" URL to create the address of that collection's page in the institution's IR, e.g., ''http://smartech.gatech.edu/handle/1853/79'', ''http://digitalarchive.gsu.edu/aysps_facpub'' (in these examples, ''79'' and ''qysps_facpub'' are collection handles).
;Institution IR Communities Page URL
:e.g., ''http://smartech.gatech.edu/community-list'', ''http://digitalarchive.gsu.edu/communities.html''
:We need to be able to parse the contents of this page to update the communities and collections data in the mapping tool.
;Institution IR "Type"
:e.g., ''normal dspace'', ''normal digital commons''
:Other types currently defined: ''vtext dspace'' (Valdosta), ''manual'' (institution doesn't have an IR yet)
;Collection Name Text Color
:e.g., ''#886600'',''#01f''
:In the mapping tool, we color code the collection names so that the institution's collections stand out from the GKR collections they are mapped to.
:(Tip: pick a color that is not "offensive" -- e.g., a rival university's trademark color -- and that is easy to read.)
{{center{
[img[featurebottom.png]]}}}
!!Fictional Institution
For the sake of example, we will discuss how to add the following fictional institution:
;Name
:~Chattooga-Menlo Community College
;Code
:chattooga-menlo
;Quick Code
:~Chattooga-Menlo
;Handle
:1853 (we're "borrowing" ~GaTech's handle, so we can more easily test our example)
;Base URL
:http://dspace.chattooga-menlo.edu/1853
;Communities Page URL
:http://gkr.gatech.edu/manakin/community-list (we're "borrowing" a real URL, so we can test updating collections)
;IR Type
:normal dspace
;Color
:green
{{center{
[img[featurebottom.png]]}}}
!!Configuration File
Each institution's vital statistics are stored in a section in the ''members.ini'' configuration file. Below are the steps for updating that file to include the new institution.
!!!Admin Page
As a supervisor user, you should see an "Admin" link in the banner. Click this link to go to the [[Admin|AdminPage]] page. The first filename listed there is "''members.ini''". Click "Edit" on that line to edit this file.
!!![members] Section
The ''members'' section of the configuration file looks something like this:
{{{
[members]
# Note: don't include 'gkr' here
asurams = Albany State University
ccga = College of Coastal Georgia
gatech = Georgia Institute of Technology
georgiasouthern = Georgia Southern University
gsu = Georgia State University
kennesaw = Kennesaw State University
mcg = Medical College of Georgia
uga = University of Georgia
valdosta = Valdosta State University
}}}
We're going to add our institution to this list in order by name, i.e.,
{{{
[members]
# Note: don't include 'gkr' here
asurams = Albany State University
ccga = College of Coastal Georgia
chattooga-menlo = Cattooga-Menlo Community College
gatech = Georgia Institute of Technology
georgiasouthern = Georgia Southern University
gsu = Georgia State University
kennesaw = Kennesaw State University
mcg = Medical College of Georgia
uga = University of Georgia
valdosta = Valdosta State University
}}}
{{center{
[img[featurebottom.png]]}}}
!!![chattooga-menlo] Section
Next, we're going to add a section to the configuration file for this institution. We start the section with the institution code in brackets, and then enter the other vital information using the settings as described in [[Member Definition Sections]].
Tip: cut and paste an existing institution's section and just change the values.
{{{
[chattooga-menlo]
code = chattooga-menlo
quick = Chattooga-Menlo
name = Chattooga-Menlo Community College
handle = 1853
baseurl = "http://dspace.chattooga-menlo.edu/{INI:chattooga-menlo:handle}"
tree = {FILE:mappings/chattooga-menlo}
coll = {FILE:collections/chattooga-menlo_coll}
cm_color = #333
cl_color = green
merge_type = normal dspace
merge_url = http://smartech.gatech.edu/community-list
}}}
!!!!Notes
* Note the places that the code (''chattooga-menlo'') appears, i.e., ''tree'', ''coll'', etc.
* Note the baseurl value. It contains a placeholder "{{code{ {INI:chattooga-menlo:handle} }}}" and it is in quotes. The quotes cause the placeholder to be replaced with the handle value (1853) immediately.
Click "Save Changes" to update the ''members.ini'' file. You will return to the Admin page, where you should see the following additional content:
* A ~Chattooga-Menlo link in the "Quick Links" menu at the left (but don't bother clicking it yet)
* Two new lines in the filename table
{{image{[img[GKR_AddInstitution_detail1.png]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Kick-start Collections and Mappings
Click the "Edit" link on the "Collections file" line and enter the following:
{{{
chattooga-menlo cm 1 Dummy
}}}
This is a dummy line with these fields
* chattooga-menlo (the institution code)
* cm (the field code that means "community" -- vs. cl for "collection")
* 1 (a dummy community handle)
* Dummy (a dummy community name)
Click "Save Changes" to add this line to the collections file (which creates a new file in the process).
Click the "Edit" link on the "Mappings file" line and enter the following:
{{{
chattooga-menlo cm 1 Dummy
}}}
Click "Save Changes" to add this line to the mappings file (which creates a new file in the process). Now you can click the ~Chattooga-Menlo quick link to go to the member page. You will see something like the following:
{{image{[img[GKR_AddInstitution_detail2.png]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Update Collections
These next steps show how to update the collections in the mapping tool from the communities page in the new institution's IR.
* From the new institution's [[Member|MemberPage]] page, click the "Manage Data" link in the banner.
* On the [[Manage Data|MemberManageDataPage]] page, click "Update Collections", and click "OK" in the confirmation box. When the update process completes, you should see a new "Last Modified" date and time on the "Collections file" line.
* Click "View" on the "Collections file" line to review the updated collections data. Click Back when done.
* On the [[Manage Data|MemberManageDataPage]] page, click "Merge Collections with Mappings", and click "OK" in the confirmation box. When the merge process completes, you should see a new "Last Modified" data and time on the "Mappings file" line.
* Click "View" on the "Mappings file" line to review the merged data. Click Back when done.
* You could also click "Changes" and then "View diff" to see that our "dummy" community was replaced with real data from the institution's IR.
* Finally, click the link in the Quick Links list to view the member page for the new institution. It should contain all of the communities and collections as defined in the institution's IR.
!!!What about the "manual" IR type?
The steps above to update the collections from the institution's IR will work if the institution has an IR. If the institution does not yet have an IR, then there are probably no collections to put in the collections and mappings file. In that case, you can manually edit those files to enter more detailed dummy data for testing.
{{center{
[img[featurebottom.png]]}}}
!!User Templates
Now that the new institution is defined in the mapping tool, it is time to set up users to maintain the mapping information for that institution. Whether or not there are already new users to define (see HowtoAddNewUser), it is helpful to set up user templates that may be used to set up real users.
!!!User Template: chattooga-menlo_user
Follow these steps to create an admin-level user template.
* Click the "Users" link in the banner to go to the [Users|UsersPage]] page.
* Click the "Add New Users" link on the Users page to go to the [[Add New User|AddNewUserPage]] page.
* Enter the following values for the input fields:
;User ID
:chattooga-menlo_user (the institution code followed by "_user")
;Name
:~Chattooga-Menlo Community College User (the institution name followed by " User")
;E-mail
:Enter any email address that might be useful (yours, mine, the contact form, etc.)
;Note
:"Use this template to add new chattooga-menlo users"
;Password
:Enter your password in the first box
:Enter a made up password in the next two boxes
:Since this user will not ever log in, it doesn't matter if anyone knows the password.
;Group
:chattooga-menlo (the institution code)
;Permissions
: [X] Add
: [X] Change
: [X] Delete
: [X] Admin
Click "Save Changes". Note that this user now appears on the Users page.
{{center{
[img[featurebottom.png]]}}}
!!!User Template: chattooga-menlo_coord
Follow these steps to create a coordinator-level user template.
* Click "~Chattooga-Menlo Community College User" on the Users page (or whatever you named the user template you just created above). Since this is what templates are for, we'll use it now.
* On the Edit User page, you'll see the above values filled in. Change them as follows:
;User ID
:chattooga-menlo_coord (the institution code followed by "_coord")
;Name
:~Chattooga-Menlo Community College Coordinator (the institution name followed by " Coordinator")
;E-mail
:Enter any email address that might be useful (yours, mine, the contact form, etc.)
;Note
:"Use this template to add new chattooga-menlo users"
;Password
:Enter your password in the first box
:Enter a made up password in the next two boxes
:Since this user will not ever log in, it doesn't matter if anyone knows the password.
;Group
:chattooga-menlo (the institution code)
;Permissions
: [X] Add
: [X] Change
: [X] Delete
: [X] Admin
: [X] Coordinator
Click "Save Changes". Note that this user now appears on the Users page.
{{image{[img[GKR_AddInstitution_detail3.png]]}}}
These user templates can now be used to add new admin-level and coordinator-level users for the new institution. If you know the passwords for the template users, you can log in as them and test access for editing the collections and mappings.
!Tutorial: How to Add a New User
!!Who can add users?
There are two groups of GKR Mapping Tool users who have high enough permissions to add new users: coordinators and supervisors. If you are one of these, then you should be able to follow these steps.
If you are not a coordinator or a supervisor, and you need to add a new user, you must ask a coordinator (who is in your group) or a supervisor to add the user, or you could ask a supervisor to make you a coordinator for your group.
!!How do I know what type of user I am?
You can find out what type of user you are by going to the [[Users|UsersPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users
{{thumb{[img[GKR_Users_admin_thumb.jpg][UsersPageImage]]}}}
Under the heading ''Type'' on the line with your name, you will see ''admin'', ''coordinator'', or ''supervisor''. This page will also show you the names and email addresses for supervisors and for the coordinators in your group. If you are a coordinator, this page will also show you which group you belong to; you have permission to add ''admin'' users in your group.
{{center{
[img[featurebottom.png]]}}}
!!Add New User Form
Following are the steps for adding a new user when using the form on the [[Add New User|AddNewUserPage]] page.
{{thumb{[img[GKR_Add_User_coordinator_thumb.jpg][AddNewUserPageImage]]}}}
* Go to the [[Add New User|AddNewUserPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=add_user
* Enter a ''User ID'', using these guidelines:
** the user id may not already exist (i.e., already being used by another user)
** one word, no spaces
** lowercase letters, numbers, and the following punctuation: period '.', hyphen '-', underscore '_' are acceptable
** examples: brad, brad.baxter, bbaxter, brad4, brad-baxter, brad_baxter, etc.
* Enter a ''Name'' for the user -- pretty much any characters are acceptable.
* Enter an ''E-mail'' address for the user.
* If desired, enter an optional ''Note'' for the user. For example, you might enter a phone number.
* In the first ''Password'' box, enter your password. Note: ''//your//'' password -- not the new user's password.
* In the second and third ''Password'' boxes, enter the new user's password.
* Enter a ''Group'' for the user. If you are a coordinator, this will already be filled in with your group, and you cannot change it. If you are a supervisor, change this to the most appropriate value: either one of the member institution's codes (e.g., ''valdosta'', ''uga'', ''gatech'', etc.) or ''all''. Typically, only supervisors will belong to group ''all''.
* Check the appropriate boxes for permissions, as follows. If you are a coordinator, these permissions will already be checked to match yours, and you cannot change them.
** Add, Change, Delete -- check all three to allow the user to edit collections and mappings data
** Coordinator -- check this to make the user a coordinator. Also check it for supervisors.
** Supervisor -- check this to make the user a supervisor.
* Click "Save Changes" to add the user.
{{center{
[img[featurebottom.png]]}}}
!!Edit User Form (Templates)
To the right of the form on the [[Add New User|AddNewUserPage]] page is a list of user templates. These are designed to aid in adding new users by filling in appropriate values for Group and permissions and example values for the other fields. Following are the (similar) steps for adding a new user when using the form on the [[Edit User|EditUserPage]] page.
{{thumb{[img[GKR_Edit_User_admin_thumb.jpg][EditUserPageImage]]}}}
* Go to the [[Add New User|AddNewUserPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=add_user
* Select one of the user templates in the list on the right. Supervisors will see a long list; coordinators, a shorter one.
* Change the ''User ID'', using the above guidelines.
* Change the ''Name''.
* Change the ''E-mail'' address.
* In the first ''Change Password'' box, enter your password.
* In the second and third ''Change Password'' boxes, enter the new user's password.
* Based on the user template you selected, the ''Group'' and permission values should be okay as they are.
* Click "Save Changes" to add the user.
After clicking "Save Changes", you should be taken to the [[Users|UsersPage]] page, and the new user will be included in that list.
!Tutorial: How to Change My Password
When your user profile is first set up, you will be given a userid and password. For security reasons, you should immediately change your password. And -- again for security reasons -- you should periodically change your password after that.
In order to go further, you must have logged in to the mapping tool; see HowtoStart.
!!Edit User
When you log in to the mapping tool, you will see your name in the upper right of every page. Clicking your name will display the "Edit User" page for your user profile. Some fields, like userid, group, and permissions may be grayed out, and you will not be permitted to change those. But other fields, like name, email, and note are ones you are allowed to change.
{{thumb{[img[GKR_Edit_User_admin_thumb.jpg][EditUserPageImage]]}}}
Another field you can change is the password field.
{{center{
[img[featurebottom.png]]}}}
!!Change Password
Note the following area on the Edit User page:
{{image{[img[GKR_ChangePassword.png]]}}}
To change your password
* Enter your current password (your name should show instead of "John Q. User") in the first input box after "Change Password:".
* Enter your new password (you are also "this user") in the second input box.
* Enter your new password again in the third input box.
Finally, click "Save Changes" to save your new password, and incidentally to save any other changes you might have made (name, email, etc.) Clicking "Save Changes" will save your user profile and will then display the [[Users|UsersPage]] page.
!Tutorial: How to Change a User's Password
The tutorial HowtoChangeMyPassword explains how to change your own password. This tutorial explains how to change another user's password. This might need to be done if a user forgets their password.
!!Who can change other users' passwords?
There are two groups of GKR Mapping Tool users who have high enough permissions to change other users' passwords: coordinators and supervisors. If you are one of these, then you should be able to follow these steps.
If you are not a coordinator or a supervisor, and you need to change a user's password, you must ask a coordinator (who is in your group) or a supervisor to do this, or you could ask a supervisor to make you a coordinator for your group. If you are a coordinator, this page will also show you which group you belong to and the names of the other users in your group; you have permission to change the passwords of ''admin'' users in your group.
!!How do I know what type of user I am?
You can find out what type of user you are by going to the [[Users|UsersPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users
{{thumb{[img[GKR_Users_admin_thumb.jpg][UsersPageImage]]}}}
Under the heading ''Type'' on the line with your name, you will see ''admin'', ''coordinator'', or ''supervisor''. This page will also show you the names and email addresses for supervisors and for the coordinators in your group.
{{center{
[img[featurebottom.png]]}}}
!!Edit User Form
Following are the steps for changing a user's password.
{{thumb{[img[GKR_Edit_User_coordinator_thumb.jpg][EditUserPageImage]]}}}
* Go to the [[Edit User|EditUserPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=valdosta_user
* In the first ''Change Password'' box, enter ''//your//'' password.
* In the second and third ''Change Password'' boxes, enter the user's new password.
* Click "Save Changes" to save the new password.
After clicking "Save Changes", you should be taken to the [[Users|UsersPage]] page.
!Tutorial: How to Edit a User Profile
Following are instructions for editing a user's profile and for deleting a user.
!!Who can edit user profiles?
There are two groups of GKR Mapping Tool users who have high enough permissions to edit user profiles: coordinators and supervisors. If you are one of these, then you should be able to follow these steps.
If you are not a coordinator or a supervisor, and you need to edit a user profile (besides yours), you must ask a coordinator (who is in your group) or a supervisor to edit the user, or you could ask a supervisor to make you a coordinator for your group.
!!How do I know what type of user I am?
You can find out what type of user you are by going to the [[Users|UsersPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users
{{thumb{[img[GKR_Users_admin_thumb.jpg][UsersPageImage]]}}}
Under the heading ''Type'' on the line with your name, you will see ''admin'', ''coordinator'', or ''supervisor''. This page will also show you the names and email addresses for supervisors and for the coordinators in your group. If you are a coordinator, this page will also show you which group you belong to and the names of the other users in your group; you have permission to edit profiles of ''admin'' users in your group.
{{center{
[img[featurebottom.png]]}}}
!!Edit User Form
Following are the steps for editing a user profile.
{{thumb{[img[GKR_Edit_User_coordinator_thumb.jpg][EditUserPageImage]]}}}
* Go to the [[Edit User|EditUserPage]] page, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=valdosta_user
First, to delete the user, go to the bottom of the form and enter ''//your//'' password next to ''Delete User: ...'' Then click the "Delete User: ..." submit button.
Next a note about ''User ID'': You might think that you can change this, but it's not that simple. If you do change it and click "Save Changes", you are actually adding a //new// user (it won't let you change this to an existing ID). So if you //do// want you change a user's User ID, you must add a new user profile for them using the new ID, and then you must //delete// this existing user profile (the one for the "old" ID).
Finally, following are the other changes allowed:
* You may change the ''Name''.
* You may change the ''E-mail'' address.
* You may change the ''Password'' as follows:
** In the first ''Change Password'' box, enter ''//your//'' password.
** In the second and third ''Change Password'' boxes, enter the user's new password.
* If you are a supervisor, you may change ''Group'' and permissions.
* Click "Save Changes" to save the changes to the user profile.
After clicking "Save Changes", you should be taken to the [[Users|UsersPage]] page, and the new values (depending what you changed) will be reflected in that list.
!Tutorial: How to Map an Institution's Collections
Mapping an institution's (i.e., your institution's) collections is the primary purpose of the mapping tool. The simple steps below -- repeated for each collection -- will accomplish that purpose.
In order to go further, you must have logged in to the mapping tool; see HowtoStart.
!!Communities and Collections
When you log in, you should see the communities and collections page for your institution, e.g., http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta
{{thumb{[img[GKR_Member_Collections_thumb.jpg][MemberPageImage]]}}}
Each collection (it is collections that will be mapped) has a "[Mapping]" link on the line after its name. If the collection has not been mapped yet, this link will have a gray background to help it stand out. If the collection //has// been mapped, the link will not have a gray background, but it will still be an active link. That is, you can remap any collection at any time. The collections that have been mapped will have the GKR central collections that they are mapped to listed below their names.
{{center{
[img[featurebottom.png]]}}}
!!Map a Collection
To map a collection, click the "[Mapping]" link next to its name. You will see the "Mapping:" page for that collection. All of the GKR central collections will be listed on this page, and each one of them will have a checkbox. If this collection has been mapped before, some of these checkboxes will already be checked.
{{thumb{[img[GKR_Member_Mapping_thumb.jpg][MemberMappingPageImage]]}}}
You can check any checkbox to map the collection to that GKR central collection. You can uncheck any checkbox to //unmap// the collection from that GKR central collection.
When you have checked all of the checkboxes that are appropriate, click "Save Changes" to save those selections and return to the communities and collections page for your institution. At the top of this page is an indication of how may collections are left to map, e.g., the message might read, "30 collections unmapped". When all of the collections have been mapped, this indicator will show that. The word "unmapped" is linked to a page that displays a list of unmapped collections. This page has links back to the collections mapping page for convenience in finding unmapped items.
{{center{
[img[featurebottom.png]]}}}
!!Lather, Rinse, Repeat
That's a [[joke|Joke]] :-) Repeat the steps above for each of your institution's collections. When you have finished mapping all of the collections (or otherwise feel that you are at a meaningful stopping point), you can publish all of the mappings.
!!Publish
!!!Background
The mapping tool allows you to map your institution's collections to GKR central collections. The purpose for doing so is to give the content loading process a way to correctly categorize content metadata by collection. That is, when the content loading process (harvester) gathers metadata from your institution's IR, that metadata is associated with your institution's collections -- which don't exist in the GKR IR. But the mapping tool defines the collection-to-collection correspondences that allow the harvester to know which GKR central collections to associate with your institution's metadata.
!!!Mapping Tables
In the mapping tool, your institution's collections are nicely named and formatted for (hopefully) clear viewing and mapping. But the harvester doesn't need all of that information. All it needs are the collections' handles. So when the mapping steps are done, that last step is to publish your changes into mapping tables that the harvester understands.
!!!Publish Link
To publish your changes, just click the "Publish" link in the upper right in the page's banner area (near your name). This link will appear if you have any unpublished changes. If it doesn't appear, and you want to publish anyway, this link should do that:
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=publish
{{thumb{[img[GKR_Member_Publish_thumb.jpg][MemberPublishPageImage]]}}}
!Tutorial: How to Recover Data
The instructions in HowtoMapCollections describe the process of mapping collections. In particular, when you select GKR central collections from the "Mapping:" page, you click "Save Changes" to save those selections. Each time you click "Save Changes", a backup is made of your institution's mappings data before those changes are saved. This page describes the steps that allow you to recover previous versions of your mappings data (i.e., previous ones of these backups).
In order to go further, you must have logged in to the mapping tool; see HowtoStart.
!!Manage Data Files
As stated above, when you click "Save Changes", a backup of the mappings data is made just prior to saving the changes. To see these previous versions of the mappings data, click the "Manage Data" link in the upper right in the banner.
The Manage Data Files page will show a line for the "Collections file" and a line for the "Mappings file". On the "Mappings file" line, click the "Changes" link. This will display the "Changes:" page for the mappings file. Incidentally, on this line is a "View" link where you can see the current contents of the mappings file.
{{thumb{[img[GKR_Member_Manage_Data_thumb.jpg][MemberManageDataPageImage]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Changes
The "Changes:" page contains a table with five columns, as follows:
;Column 1: File
:This column repeats the file name, which for the mappings file is your institution's "code", e.g., "valdosta"
;Column 2: Version
:This column lists the previous versions of the mappings file. The format is ".yyyymmdd.nnn' where 'yyyymmdd' is the date the backup was done, and 'nnn' is just a serial number starting with '001'.
;Column 3: View
:This column lists links that will display the contents of that version of the mappings file.
;Column 4: View diff
:This column lists links that will display the differences (hence "diff") between that version of the mappings file and the ''//current//'' version. (At this time, there isn't an option to compare two previous versions.) This diff display is intended to help you decide whether to recover a previous version, and if so, which one to recover.
;Column 5: Recover
:This column lists links that will recover that version of the file. That is, the previous version's contents will ''//replace//'' the current version's contents. In the process //another// backup is made of the current version before it is overwritten, so you can "recover from a recover" if desired.
{{thumb{[img[GKR_Member_Changes_thumb.jpg][MemberChangesPageImage]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Recover
After examining the diff displays to determine whether to recover a previous version, and which one, simply click the "Recover" link for that version. You will be prompted with a confirmation box, e.g.,
{{thumb{[img[GKR_RecoverConfirmation.jpg]]}}}
If you click "OK", a backup will be made, the previous version will overwrite the current version, and you will again see the "Changes:" page (with the five-column table). But this time, there will be a new version number on the first line of the table, which will contain the mappings prior to the recovery.
At this point, you are essentially finished recovering the data. You might take a look at the results to be sure it's what you wanted -- and you can even "recover from the recover" if you want. Otherwise, clicking your institutions code (e.g., "Valdosta") under "Quick Links:" on the left will return you to your institution's communities and collections page.
!Tutorial: How to get started using the GKR Mapping Tool
!!Are you a user?
In order to use the mapping tool, you must have a user profile defined for you. If this has not been done, please send a request via the [[Georgia Knowledge Repository initiative website Contact Us|http://www.library.gatech.edu/gkr/contact]] form.
!!Yes, I'm a user.
If you are a user, then you will have been given a userid and password for using the mapping tool, so you can do the following:
* Go to http://gkr-prod.library.gatech.edu/gkr_mapping
** If you are already logged in, you should see the communities and collections page for your institution, and you're ready to go.
** If you have not already logged in, you should see the mapping tool home page, which has a "Login" link in the upper right corner.
{{thumb{[img[GKR_Home_thumb.jpg][HomePageImage]]}}}
* If you have not logged in already, then click the "Login" link.
{{thumb{[img[GKR_Login_thumb.jpg][LoginPageImage]]}}}
* Enter your userid and password and click "Submit".
* You should now see the communities and collections page for your institution and you're ready to do.
{{thumb{[img[GKR_Member_Collections_thumb.jpg][MemberPageImage]]}}}
!!What next?
See HowtoMapCollections for the basic operations, or see [[Tutorials]] for more "How to" pages.
!Tutorial: How to Update Collections
The GKR Mapping Tool allows you to map your institution's collections to GKR central collections. But how does the mapping tool know what your institution's collections are? The answer is that the mapping tool gets that information from your institution's IR via a URL to your IR's Communities page.
In order to go further, you must have logged in to the mapping tool; see HowtoStart.
!!Manage Data Files
The Manage Data Files page was referred to in HowtoRecoverData, because it contains "Changes" links for that purpose. But below the Changes links on that page are two other links (depending on how your institution profile is configured), e.g.:
;[[Update Collections|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping]]
:Click this link to update the collections in the mapping tool from the institution's IR collection definitions.
:See: http://vtext.valdosta.edu/xmlui/community-list
;[[Merge Collections with Mappings|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping]]
:Click this link to merge changes from the collections file with the mapping data.
{{thumb{[img[GKR_Member_Manage_Data_thumb.jpg][MemberManageDataPageImage]]}}}
{{center{
[img[featurebottom.png]]}}}
!!Update Collections
As the above text says, you would click the "Update Collections" link to have the mapping tool retrieve your institution's IR's Communities page, parse out the communities and collections information from it, and update the mapping tool collections file with that information. If there are changes in the IR since the last update, the changes collections data will be saved (along with a backup). If there were no changes in the IR, no changes will happen in the mapping tool.
Clicking "Update Collections" is the first step in the overall process. This will update the collections information in the "collections" data file (vs. the "mappings" data file). When you click that link, a confirmation box will pop up, e.g.,
{{thumb{[img[GKR_UpdateConfirmation.jpg]]}}}
If you click OK, you may get a message (after a little wait) that says, "No changes to save." If so, then just click "Back" and you are finished. That is, you don't have to do the next step of merging the collections data with the mappings data.
On the other hand, if there are changes, they will be saved to the collections file, and the Manage Data Files page will redisplay with a new "Last Modified" date and time on the "Collections file" line. You will want to view the changed file by clicking the "View" link on that line. This will give you a quick look at whether or not the data looks corrupted. You should also click "Changes" and then "View diff" on the top line to see if the updates that happened look reasonable. These steps will help to verify that the update routines are working properly, and that the IR Communities page was accessed okay.
{{center{
[img[featurebottom.png]]}}}
!!Merge Collections with Mappings
The second and final step in the update process is to merge the changes to the collections file with the data in the mappings file. The mappings file should always be just like the collections file, except the mappings file will also include data lines for GKR central collections.
When you feel comfortable that the collections data was updated okay, merging it with the mappings data will ensure that the mapping tables the harvester gets are consistent with the collections it will see when it ingests your institution's IR metadata records.
On the Manage Data Files page, click "Merge Collections with Mappings". You should see a confirmation box pop up, e.g.,
{{thumb{[img[GKR_MergeConfirmation.jpg]]}}}
If you click OK, you may again get a message that says, "No changes to save." If so, then just click "Back" and you are finished.
On the other hand, if there are changes, they will be saved to the mappings file, and the Manage Data Files page will redisplay with a new "Last Modified" date and time on the "Mappings file" line. You will want to view the changed file by clicking the "View" link on that line. This will give you a quick look at whether or not the data looks corrupted. You should also click "Changes" and then "View diff" on the top line to see if the updates that happened look reasonable. These steps will help to verify that the merge routine is working properly.
If everything looks okay, then you are finished, and you can click your institution's code (e.g., "Valdosta") under "Quick Links:" on the left to return to your communities and collections page.
!Indented Tree Structures
The key data files, communities/collections and mappings, contain indented text that represents a tree structure. Each more-indented line in the text is considered a child of the less-indented line above it. This provides a simple means of representing both the communities and collections hierarchies and the collection-to-collection correspondences that are the crux of the mapping tool. Below is an excerpt from one of the mappings data files:
{{{
valdosta cm 359 College of Arts and Sciences
valdosta cm 363 African American Studies
valdosta cl 383 AAS Faculty Research and Teaching Materials
valdosta cm 364 Biology
valdosta cl 382 Biology Faculty Research and Teaching Materials
gkr cl 2008 Biology
gkr cl 2010 Biotechnology
valdosta cm 365 Chemistry
valdosta cl 380 Chemistry Faculty Research and Teaching Materials
gkr cl 2034 Chemical and Biomolecular Engineering
valdosta cm 366 English
valdosta cl 384 English Faculty Research and Teaching Materials
gkr cl 550 English Language and Literature
gkr cl 552 Creative Writing
valdosta cm 367 History
}}}
Each line contains the following space-separated fields:
* institution code, e.g., valdosta
* community(cm)/collection(cl) code, e.g., cm
* community/collection handle, e.g., 359
* community/collection name (taking up the rest of the line), e.g., College of Arts and Sciences
The indentation denotes parent/child relationships, e.g., collection 383 is a child of community 363, which is in turn a child of community 359. The most crucial parent/child relationships are the mappings themselves, where a gkr collection, e.g., "gkr cl 2008 Biology" is listed as a child of a valdosta collection, e.g., 382.
The gkr_mapping program uses the [[Tree::Indented]] module to parse this indented text and display it as nested <ul> lists in the mapping tool interface. The program then updates the tree structure and writes it back out again as indented text like above. Finally, the program uses the parent/child relationships between the institution's (e.g., Valdosta's) collections and the GKR collections to generate and publish the final lookup tables for the GKR harvesting processes.
!Installation of the Mapping Tool
This is an installation guide. It would be a good idea to read the [[Technical]] page first for some technical background regarding directory paths, data files, and data structures. That knowledge may help to understand some of the following steps and may aid in debugging.
* [[Step 1, Perl Modules, CPAN|Step1-PerlModulesCPAN]]
* [[Step 2, Mapping Tool Directories|Step2-MappingToolDirectories]]
* [[Step 3, Mapping Tool Distribution Tarballs|Step3-MappingToolDistributionTarballs]]
* [[Step 4, Local Perl Modules Distribution Tarballs|Step4-LocalPerlModulesDistributionTarballs]]
* [[Step 5, Modify Program Source File|Step5-ModifyProgramSourceFile]]
* [[Step 6, Web Server Permissions|Step6-WebServerPermissions]]
* [[Step 7, Testing the Mapping Tool|Step7-TestingMappingTool]]
Click InstallationSteps to display all of the above steps in one page.
See also [[Preinstallation]].
<<tiddler [[Step1-PerlModulesCPAN]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step2-MappingToolDirectories]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step3-MappingToolDistributionTarballs]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step4-LocalPerlModulesDistributionTarballs]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step5-ModifyProgramSourceFile]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step6-WebServerPermissions]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Step7-TestingMappingTool]]>>
!Georgia Knowledge Repository Mapping Tool Introduction
The goals of the Georgia Knowledge Repository (GKR) project (as stated in the [[project site|http://www.library.gatech.edu/gkr/]]) are to "advance scholarly communication by expanding the use of ~IRs by U.S. colleges and universities and by increasing the number of professionals with knowledge and skills in managing consortial ~IRs."
One of the deliverables of the project is: "Build a central repository of standardized metadata, featuring the repository collection mapping tool, harvested from eight ~IRs in the USG." The repository collection mapping tool is the subject of this wiki.
!~Collection-to-Collection Correspondences
The contents of the GKR IR will include metadata harvested from other ~IRs that describe their available documents. In the other ~IRs, these documents are grouped in collections organized by those institutions and specific to those campuses. Similarly, the documents described in the GKR IR are grouped in collections organized by GKR staff.
When metadata from another IR is harvested into the GKR IR, it's necessary to decide which GKR collections to associate the metadata with. The GKR mapping tool provides the information that allows these decisions to be made by the harvesting program.
When metadata from another IR is harvested into the GKR IR, the harvesting program must decide which GKR collections to associate the metadata with. The GKR mapping tool provides the information that allows the program to make these decisions.
A note about terminology: "collection" is a term used a lot in this documentation. In general, it refers to an entity in the Communities and Collections hierarchy that has a unique handle. So it might be really a collection, or it might be a community. In addition, the communities in the central GKR repository are referred to as "disciplines". In this documentation, "collection" usually refers to any of these.
{{center{
[img[featurebottom.png]]}}}
!GKR Mapping Tool
The [[GKR Mapping Tool|http://gkr-prod.library.gatech.edu/gkr_mapping/]] is designed to create data structures similar to the following for each of the member institution ~IRs:
{{{
<mappings>
<valdosta>
<handle id="1011">
<name>South Georgia State Normal College at Valdosta: Bulletin 1913-1918</name>
<gkr_handles>109</gkr_handles>
</handle>
<handle id="1035">
<name>English Student Presentations and Publications</name>
<gkr_handles>59</gkr_handles>
</handle>
...
<handle id="752">
<name>Management Faculty Research and Teaching Materials</name>
<gkr_handles>29,31,33</gkr_handles>
</handle>
<handle id="753">
<name>AF Faculty Research and Teaching Materials</name>
<gkr_handles>29,30</gkr_handles>
</handle>
...
<handle id="995">
<name>The Patricia Sutherland GSWC Scrapbook Collection</name>
<gkr_handles>109</gkr_handles>
</handle>
</valdosta>
</mappings>
}}}
The above XML document illustrates a lookup table for Valdosta State University. For the Valdosta collection handle, 1011, there is one GKR handle, 109. For the Valdosta collection handle, 752, there are three GKR handles, 29, 31, and 33. And so on for each Valdosta collection. Each of the institution's local collections may be mapped to any number of central GKR collections. And a GKR collection may be mapped to any number of local collections. It is a potentially "many to many" mapping.
When metadata records are harvested from Valdosta, they will have Valdosta collection handles associated with them. When these records are added to the GKR IR, the GKR mapping table above will let the harvester know which GKR collections to associate with each Valdosta collection.
While the lookup tables are relatively simple (though they may be lengthy when all the collections are mapped), maintaining these tables could prove difficult enough to be a stumbling block for the project. The online GKR mapping tool attempts to make the job of maintaining the collection-to-collection correspondences easy enough to be feasibly managed.
{{center{
[img[featurebottom.png]]}}}
!Member Institutions
The institutions that will participate in the GKR project are referred to in the mapping tool as "member" institutions. A list of the collections in each member institution's IR is maintained in the mapping tool. In addition, for each collection, a list of the mapped GKR IR collections is maintained in the tool.
The tool allows staff from each institution to log in and maintain this mapping data for their institution. So the responsibility for keeping track of the collection mappings falls not on GKR staff but on each individual institution's staff. This is appropriate: they are the ones familiar enough with their own collections to know how best to map them to the central GKR collections. This distributing of the work load among the institutions is another way that the mapping tool makes the job of mapping collections feasible for the project.
{{center{
[img[featurebottom.png]]}}}
!About this Documentation
Larry Wall, creator of the Perl, is noted (among many other things) for making this observation;
<<<
The following two statements are usually both true:
There's not enough documentation.
There's too much documentation.
-- http://en.wikiquote.org/wiki/Larry_Wall
<<<
I'm quite willing to concede that the contents of this wiki may be a good example of that.
!About this Wiki
This is a "[[TiddlyWiki|http://www.tiddlywiki.com]]". While it isn't a "true" wiki (because not everyone may edit it online), it does have nice editing, organization, and navigation features that make writing this documentation much easier than it might be.
It also has some quirks that may take getting used to, but these become less noticeable as you get familiar with the navigation. See WikiTips for some brief advice.
See also: SiteMap.
!Programmer Joke
How did the computer programmer get stuck in the shower? The shampoo bottle said, "Lather, Rinse, Repeat."
<<forEachTiddler
where
'tiddler.tags.contains("$1")'
>>
!Local CSS Files
In addition to the CSS styles defined in the configuration files, the following are stand-alone CSS files that are used in the interface:
;gkr_mapping.css
:the main CSS file for the mapping tool
;openclose.css
:to support the openclose.js javascript library (see above)
;media_inspector.css
:to support the media_inspector.js javascript library (see above)
!Local Javascript Libraries
In addition to the javascript routines defined in the configuration files, the following files contain javascript libraries that are used in the interface:
;cookies.js
:defines getCookie(), setCookie(), deleteCookie(), and cookiesEnabled() for simple cookie processing
;blurb_hide.js
:defines blurb_hide() and showhide() for toggling the display of the blurb on each page
;openclose.js
:defines openclose(), openAll(), and closeAll() for collapsing/expanding the nested lists
;media_inspector.js
:defines getMediaType() and showMediaType() for enabling/disabling the table_maker functions
;table_maker.js
:defines table_maker() and other related functions for displaying lists as tables (usually for layout purposes)
:detailed documentation for table_maker() and related functions: [[Table Maker]]
!Georgia Knowledge Repository (GKR) Mapping Tool Login
{{thumb{[img[GKR_Login_thumb.jpg][LoginPageImage]]}}} [[Image|LoginPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=login]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=login
!!Page Elements
* Input boxes for Userid and Password
* Submit button
!Login Page Image
{{image{[img[GKR_Login.png]]}}}
[[Introduction]]
[[Tutorials]]
[[Functional]]
[[Technical]]
[[Programming]]
[[Configuration]]
[[Installation]]
[[Documentation]]
<<toggleSideBar Sidebar "Show/Hide Sidebar" hide>>
!members.ini
The first section in ''members.ini'', ''[members]'', contains a list of institution codes and names. The next section, ''[gkr]'', defines settings for the GKR "institution". The rest of the sections define settings for each institution listed in ''[members]''. The mapping tool program uses these settings to handle interface and data details for each member.
![members]
Following is an example ''[members]'' section, containing all of the institutions defined at the time of this writing:
{{{
[members]
# Note: don't include 'gkr' here
asurams = Albany State University
ccga = College of Coastal Georgia
georgiahealth = Georgia Health Sciences University
gatech = Georgia Institute of Technology
georgiasouthern = Georgia Southern University
gsu = Georgia State University
kennesaw = Kennesaw State University
ngcsu = North Georgia College and State University
uga = University of Georgia
valdosta = Valdosta State University
}}}
The note about 'gkr' in the above section is a reminder that even though the ''[gkr]'' section below defines the same settings as the member sections that follow it, 'gkr' is not itself considered a "member"; rather, it's the "host" or "central" organizing entity.
!Settings that Define an Institution
The following settings are common to the member institutions and to the GKR central "institution".
;code
:The code that identifies the institution in the mapping tool. This code is used various places, e.g., section names, file names, group names, etc. These codes should be all lower case.
;quick
:This is the version of the above code that is displayed in the "Quick Links" list in the left margin of each page. The intention is that the "quick" code is the same as the "code" except for capitalization.
;name
:This is the institution name. For member institutions, it should match what is listed in the ''[members]'' section. This name is displayed in a lot of places in the interface.
;handle
:This is the institution's IR handle. For ~DSpace ~IRs, it's the top-level handle used in ~URLs, e.g., "1853" in http://smartech.gatech.edu/handle/1853/78. For non-~DSpace ~IRs, it's whatever makes sense in the circumstances, e.g., "gsu.edu" for GSU, "kennesaw.edu" for Kennesaw (Digital Commons doesn't have the top-level handle concept, so the edu domain is assigned to be the institution handle).
;baseurl
:This should be the URL of the institution's IR. It's a "base" URL because it's expected that you can add community and collection handles to this URL to create full ~URLs to those local communities and collections pages in the local IR.
;tree
:The value of this setting will be the full contents of the institution's "mappings" data file. This is how the mapping tool gets this data.
:Note the settings name: "tree" (after the [[Tree::Indented]] module used to parse the data). You might think this should be called "mappings" or "maps" or something like that. But during development, "tree" was the name given to this data before it came to be called "mappings", so it's a historical artifact.
;coll
:The value of this setting will be the full contents of the institution's collections data file. Again, this is how the mapping tool gets this data for processing.
;cm_color
:This is the RGB color used to display the community names in the Community and Collections displays.
;cl_color
:This is the RGB color used to display the collection names in the Community and Collections displays. The motivation for having different colors is so the GKR collections in the mappings display stand out from the local institution's collections (and the collections from the communities).
;merge_type
:This value tells the mapping tool which merge routine to use to update the institution's collections. The choices (to date) are
:;normal dspace
::This routine parses the typical ~DSpace communities and collections page, e.g., http://smartech.gatech.edu/community-list
:;normal digital commons
::This routine parses the typical Digital Commons communities page, e.g., http://digitalcommons.kennesaw.edu/communities.html
:;vtext dspace
::This routine is specific to the ~DSpace page displayed in Valdosta's vtext ~DSpace, i.e., http://vtext.valdosta.edu/xmlui/community-list
:;manual
::This isn't a routine at all. Rather, it simply indicates that there isn't (yet) a source for this information, so it must be maintained manually in the mapping tool.
;merge_url
:This URL is where the mapping tool retrieves the institution's local collections data for merging into the mapping tool collections data file (and from there into the mappings data file). The ~URLs above are examples.
![gkr]
The excerpt below shows the settings for the Georgia Knowledge Repository (GKR). Note: the handle value "123456789" is a clue that this is a test/development IR.
{{{
[gkr]
code = gkr
quick = GKR
name = Georgia Knowledge Repository
handle = 123456789
baseurl = "http://gkr.gatech.edu/manakin/handle/{INI:gkr:handle}"
tree = {FILE:mappings/gkr}
coll = {FILE:collections/gkr_coll}
cm_color = #333
cl_color = #00008A
merge_type = normal dspace
merge_url = http://gkr.gatech.edu/manakin/community-list
}}}
![asurams]
Below are the settings for Albany State University (ASU). The code "asurams" was adopted from their asurams.edu domain.
Note that their merge type is "manual", and that the base URL for their IR is ... the mapping tool! These are just some of the indications that ~ASURams hasn't been properly set up in the mapping tool; their IR doesn't exist yet.
{{{
[asurams]
code = asurams
quick = ASURams
name = Albany State University
handle = 123456789
baseurl = http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=asurams;handle=
tree = {FILE:mappings/asurams}
coll = {FILE:collections/asurams_coll}
cm_color = #333
cl_color = #CC9900
merge_type = manual
}}}
![ccga]
Below are the settings for College of Coastal Georgia (CCGA).
Like ~ASURams above, CCGA has not yet been properly set up, because they don't have an IR yet.
{{{
[ccga]
code = ccga
quick = CCGA
name = College of Coastal Georgia
handle = 123456789
baseurl = http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=ccga;handle=
tree = {FILE:mappings/ccga}
coll = {FILE:collections/ccga_coll}
cm_color = #333
cl_color = #03c
merge_type = manual
}}}
![georgiahealth]
Below are the settings for Georgia Health Sciences University.
{{{
[georgiahealth]
code = georgiahealth
quick = GeorgiaHealth
name = Georgia Health Sciences University
handle = 10675.2
baseurl = http://gkr-mcg.library.gatech.edu/handle/{INI:georgiahealth:handle}
tree = {FILE:mappings/georgiahealth}
coll = {FILE:collections/georgiahealth_coll}
cm_color = #333
cl_color = #036
merge_type = hosted dspace
merge_url = http://gkr-mcg.library.gatech.edu/community-list
}}}
![gatech]
Below are the settings for Georgia Tech (~GaTech). These settings are full and complete. ~GaTech has a ~DSpace IR, has its communities and collections defined in the mapping tool, and has a merge URL from which the mapping tool can update this data.
{{{
[gatech]
code = gatech
quick = GaTech
name = Georgia Institute of Technology
handle = 1853
baseurl = "http://smartech.gatech.edu/handle/{INI:gatech:handle}"
tree = {FILE:mappings/gatech}
coll = {FILE:collections/gatech_coll}
cm_color = #333
cl_color = #886600
merge_type = normal dspace
merge_url = http://smartech.gatech.edu/community-list
}}}
![georgiasouthern]
Below are the settings for Georgia Southern University (~GeorgiaSouthern). Georgia Southern's IR is in a state of flux, so settings like the ''baseurl'' and ''handle'' are not finalized. This state of flux prompted me to add an additional setting ''merge_handle'' (a regular expression used instead of the ''handle'' value), so that the mapping tool could successfully parse from their merge URL.
{{{
[georgiasouthern]
code = georgiasouthern
quick = GeorgiaSouthern
name = Georgia Southern University
handle = 123456789
baseurl = http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=georgiasouthern;handle=
tree = {FILE:mappings/georgiasouthern}
coll = {FILE:collections/georgiasouthern_coll}
cm_color = #333
cl_color = #036
merge_type = normal dspace
merge_url = http://eaglespace.georgiasouthern.edu/jspui/community-list
merge_handle = '(?:123456789|10518)' # because they're in flux ...
}}}
![gsu]
Below are the settings for Georgia State University (GSU). GSU's settings are essentially full and complete; they have a Digital Commons IR, communities and collections defined in the mapping tool, and a merge URL from which the mapping tool can update this data.
{{{
[gsu]
code = gsu
quick = GSU
name = Georgia State University
handle = gsu.edu
baseurl = http://digitalarchive.gsu.edu
tree = {FILE:mappings/gsu}
coll = {FILE:collections/gsu_coll}
cm_color = #333
cl_color = #01f
merge_type = normal digital commons
merge_url = http://digitalarchive.gsu.edu/communities.html
}}}
![kennesaw]
Below are the settings for Kennesaw State University (Kennesaw). Like GSU, Kennesaw's settings are essentially full and complete; they have a Digital Commons IR, communities and collections defined in the mapping tool, and a merge URL from which the mapping tool can update this data.
{{{
[kennesaw]
code = kennesaw
quick = Kennesaw
name = Kennesaw State University
handle = kennesaw.edu
baseurl = http://digitalcommons.kennesaw.edu
tree = {FILE:mappings/kennesaw}
coll = {FILE:collections/kennesaw_coll}
cm_color = #333
cl_color = #c93
merge_type = normal digital commons
merge_url = http://digitalcommons.kennesaw.edu/communities.html
}}}
![ngcsu]
Below are the settings for North Georgia College and State University.
{{{
[ngcsu]
code = ngcsu
quick = NGCSU
name = North Georgia College and State University
handle = ngcsu.edu
baseurl = http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=ngcsu;handle=
tree = {FILE:mappings/ngcsu}
coll = {FILE:collections/ngcsu_coll}
cm_color = #333
cl_color = #6D8389
#cl_color = #003054
merge_type = manual
}}}
![uga]
Below are the settings for University of Georgia (UGA). UGA's ~DSpace IR is still under development, as indicated by the handle, "123456789".
{{{
[uga]
code = uga
quick = UGA
name = University of Georgia
handle = 123456789
baseurl = "http://dspace.galib.uga.edu/dspace/handle/{INI:uga:handle}"
tree = {FILE:mappings/uga}
coll = {FILE:collections/uga_coll}
cm_color = #333
cl_color = #900
merge_type = normal dspace
merge_url = http://dspace.galib.uga.edu/dspace/community-list
}}}
![valdosta]
Below are the settings for Valdosta State University (Valdosta), and they are essentially full and complete. Their ~DSpace IR is in production, they have communities and collections defined in the mapping tool, and they have a merge URL from which the mapping tool can update this data. Because their ~DSpace implementation is different from a "normal" ~DSpace, a merge type (and merge routine) named "vtext dspace" was developed just for their situation.
{{{
[valdosta]
code = valdosta
quick = Valdosta
name = Valdosta State University
handle = 10428
baseurl = "http://vtext.valdosta.edu/xmlui/handle/{INI:valdosta:handle}"
tree = {FILE:mappings/valdosta}
coll = {FILE:collections/valdosta_coll}
cm_color = #333
cl_color = #DD0000
merge_type = vtext dspace
merge_url = http://vtext.valdosta.edu/xmlui/community-list
}}}
!Member Institution Manage Data Files, Changes Diff Page
{{thumb{[img[GKR_Member_Changes_Diff_thumb.jpg][MemberChangesDiffPageImage]]}}} [[Images|MemberChangesDiffPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=diff;file=valdosta_coll;version=.20091116.001]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=diff;file=valdosta_coll;version=.20091116.001 (but this version number might cease to exist ...)
!!Page Elements
* Below the blurb is a display of differences between the current file, valdosta_coll and the previous version, valdosta_coll.20091116.001.
* The differences are shown in red and green with a plus '+' or minus '-' in the first character position.
!Member Institution Manage Data Files, Changes Diff Page Image
{{image{[img[GKR_Member_Changes_Diff.png]]}}}
!Member Institution Manage Data Files, Changes Page
{{thumb{[img[GKR_Member_Changes_thumb.jpg][MemberChangesPageImage]]}}} [[Images|MemberChangesPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=valdosta_coll;member=valdosta]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=changes;file=valdosta_coll;member=valdosta
!!Page Elements
* Below the blurb is a list of the previous versions of the collections data file.
* Clicking "View" will display a page just like the above View page, where you can view the selected version.
* Clicking "View diff" will display the differences between the listed version and the current file (valdosta_coll).
* Clicking "Recover" to replace the current file with that previous version.
!Member Institution Manage Data Files, Changes Page Image
{{image{[img[GKR_Member_Changes.png]]}}}
!Member Institution Manage Data Files, Edit Mappings Page
{{thumb{[img[GKR_Member_Edit_Mappings_thumb.jpg][MemberEditMappingsPageImage]]}}} [[Images|MemberEditMappingsPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_tree;member=valdosta]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_tree;member=valdosta
!!Page Elements
* Below the blurb is the internal data representation of the member institution's (in this case, Valdosta State University's) communities (type 'cm'), collections (type 'cl'), and mappings. Mappings are also type 'cl', but they begin with 'gkr' and appear as children of collections.
!Member Institution Manage Data Files, Edit Mappings Page Image
{{image{[img[GKR_Member_Edit_Mappings.png]]}}}
!Member Institution Manage Data Files, Edit Page
{{thumb{[img[GKR_Member_Manage_Data_Edit_thumb.jpg][MemberManageDataEditPageImage]]}}} [[Images|MemberManageDataEditPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_coll;member=valdosta]]
!!Locations
* development: hhttp://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=edit_coll;member=valdosta
!!Page Elements
* Below the blurb is the internal data representation of the member institution's (in this case, Valdosta State University's) communities (type 'cm') and collections (type 'cl').
!Member Institution Manage Data Files, Edit Page Image
{{image{[img[GKR_Member_Manage_Data_Edit.png]]}}}
!Member Institution Manage Data Files
{{thumb{[img[GKR_Member_Manage_Data_thumb.jpg][MemberManageDataPageImage]]}}} [[Image|MemberManageDataPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=manage_data]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=manage_data
!!Page Elements
* Similar to the [[GKR Manage Data Files|GKRManageDataPage]] page, this page displays links for the collections data and the mappings data files for the member institution.
* Click "View" to view the data, "Edit" to change it, and "Changes" to review past changes and for data recovery.
* In addition, the "Update Collections" link will fetch information from the member institution's IR and update the communities and collections data in the mapping tool.
* The "Merge Collections with Mappings" link will merge changes from the member institution's collections file (which presumably was updated by clicking "Update Collections") with the institution's mapping data.
* This is slightly different from the merge operation on the [[GKR Manage Data Files|GKRManageDataPage]] page (which merges its changes with all the institutions), but the strategy is similar: step 1) Update Collections to sync them with the IR; step 2) Merge collections changes with the mapping data. Between the steps, the collections data may be reviewed to spot problems with the update prior to the merge.
!Member Institution Manage Data Files Page Image
{{image{[img[GKR_Member_Manage_Data.png]]}}}
!Member Institution Manage Data Files, View Page
{{thumb{[img[GKR_Member_Manage_Data_View_thumb.jpg][MemberManageDataViewPageImage]]}}} [[Images|MemberManageDataViewPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_coll;member=valdosta]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_coll;member=valdosta
!!Page Elements
* Below the blurb is the internal data representation (with line numbers added) of the member institution's (in this case, Valdosta State University's) communities (type 'cm') and collections (type 'cl').
!Member Institution Manage Data Files, View Page Image
{{image{[img[GKR_Member_Manage_Data_View.png]]}}}
!Member Institution Collection Mapping Page
{{thumb{[img[GKR_Member_Mapping_thumb.jpg][MemberMappingPageImage]]}}} [[Image|MemberMappingPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=map_node;node=valdosta-382-364-359]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=map_node;node=valdosta-382-364-359
!!Page Elements
* The page heading, e.g., "{{bold{Mapping: Biology Faculty Research and Teaching Materials (382)}}}" indicates which of the member institution's collections is about to be mapped.
* Below the (hidden) blurb is the list of the GKR IR communities and collections. Each collection has a checkbox.
* Some GKR collections in this example have already been selected. The user may select any other collection to add to those, and may also unselect those.
* When all the desired GKR collections have been selected, clicking "Save Changes" will save those selections and return the user to the [[member institution's collections page|MemberPage]], where those selections will now appear.
!Member Institution Collection Mapping Page Image
{{image{[img[GKR_Member_Mapping.png]]}}}
!Member Institution Communities and Collections
{{thumb{[img[GKR_Member_Collections_thumb.jpg][MemberPageImage]]}}} [[Image|MemberPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta
!!Page Elements
* Below the blurb (which is hidden in the example image) is the list of the member institution's (in this case Valdosta's) communities (in gray) and collections (in red).
* By each collection is a "[Mapping]" link that will display a [[Mapping|MemberMappingPage]] page (where the real work of mapping happens).
* Some "[Mapping]" links have a gray background to indicate that this collection has not been mapped yet.
* At the top of the list is a status message, e.g., "28 collections unmapped" that indicates the mapping work yet to be done.
* Under the collections that have been mapped are the GKR collections (in blue) that the member institution's collections have been mapped to.
* In addition to the "Close All" and "Open All" links, each community name and each collection name may be clicked to collapse and expand that particular part of the list.
* Displayed this way, it is easy to review the current mappings.
!Member Institution Communities and Collections Page Image
{{image{[img[GKR_Member_Collections.png]]}}}
!Member Institution Publish Confirmation Page
{{thumb{[img[GKR_Member_Publish_thumb.jpg][MemberPublishPageImage]]}}} [[Images|MemberPublishPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=publish]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=publish
!!Page Elements
* The confirmation message shows a link to the RSS "feed" for the institution's mapping table, e.g., http://gkr-prod.library.gatech.edu/gkr_mapping/rss/valdosta.xml
* In addition, it lists links to all of the various ways the mapping tables may be incorporated into harvesting (or even reporting) programs, e.g.,
<<<
http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.xml
http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.csv
http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.json
http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.yaml
http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.perl
<<<
* Clicking those links in turn will display the same (logical) mapping table in various formats, as shown in the following examples
!!XML Mapping Table
Note that this is XML, but it is not an RSS feed.
{{{
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<valdosta handle="380">
<gkr>2034</gkr>
</valdosta>
<valdosta handle="382">
<gkr>2008</gkr>
<gkr>2010</gkr>
</valdosta>
<valdosta handle="384">
<gkr>550</gkr>
<gkr>552</gkr>
</valdosta>
</mappings>
}}}
!!CSV Mapping Table
Note that the GKR handles are grouped in quotes as a single value. The program would split this value again to get a list.
{{{
member,handle,gkr handles
valdosta,380,"2034"
valdosta,382,"2008,2010"
valdosta,384,"550,552"
}}}
!!JSON Mapping Table
Note that the JSON, YAML, and Perl tables below represent identical data structures, logically the same as the XML and CSV representations above. Deciding which of the five representations to use will be a matter of preference and/or available programming modules.
{{{
{
"valdosta" : {
"380" : [
2034
],
"382" : [
2008,
2010
],
"384" : [
550,
552
]
}
}
}}}
!!YAML Mapping Table
{{{
---
valdosta:
380:
- 2034
382:
- 2008
- 2010
384:
- 550
- 552
}}}
!!Perl Mapping Table
{{{
{
'valdosta' => {
'384' => [
'550',
'552'
],
'382' => [
'2008',
'2010'
],
'380' => [
'2034'
]
}
}
}}}
!Member Institution Publish Confirmation Page Image
{{image{[img[GKR_Member_Publish.png]]}}}
!Member Institution Manage Data Files, View Mappings Page
{{thumb{[img[GKR_Member_View_Mappings_thumb.jpg][MemberViewMappingsPageImage]]}}} [[Images|MemberViewMappingsPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_tree;member=valdosta]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_tree;member=valdosta
!!Page Elements
* Below the blurb is the internal data representation (with line numbers added) of Valdosta State University's communities (type 'cm'), collections (type 'cl'), and mappings. Mappings are also type 'cl', but they begin with 'gkr' and appear as children of collections.
!Member Institution Manage Data Files, View Mappings Page Image
{{image{[img[GKR_Member_View_Mappings.png]]}}}
The ''program.ini'' file contains sections that define each of the pages in the mapping tool interface. Each section typically contains the same settings, e.g., ''title'', ''blurb'', ''help'', ''page''. But some pages have fewer of these and some also include their own sets of links. The settings for each page are detailed below.
Note that the values for ''blurb'', ''help'', and ''page'' are easy to figure: they contain the section name with "{{code{ {FILE: }}}" in front and the setting name (with an underscore) at the end, e.g., "{{code{ blurb = {FILE:login_blurb} }}}".
Note also that the value of ''title'' is used inside the <title> tag in the <head> of the html source. In many -- but not all -- cases, it is also used inside the <h1> tag in the <body>.
![login]
This section defines the settings for the [[login|LoginPage]] page.
{{thumb{[img[GKR_Login_thumb.jpg][LoginPageImage]]}}}
;links
:The login page only displays one banner link: either "Login" or "Logout", depending.
;title
:''Login''
;blurb
:''{FILE:login_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:login_page}'' (this page template is in this file)
![home]
This section defines the settings for the [[home|HomePage]] page.
{{thumb{[img[GKR_Home_thumb.jpg][HomePageImage]]}}}
;links
:Most pages use the menu links defined in the ''[menu]'' section above. But the home page defines its own set, which doesn't include the member-specific links: "Publish" and "Manage data", i.e., the home page doesn't focus on a particular member, so we don't know which member to include in those links' hrefs.
;title
:''{INI:program:title}'', i.e., ''Georgia Knowledge Repository Mapping Tool''
;help
:''{FILE:home_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:home_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:home_page}'' (this page template is in this file)
![member]
This section defines the settings for the "member" page, which includes the [[GKR Communities and Collections|GKRCollectionsPage]], and [[Member Institution Communities and Collections|MemberPage]] pages.
{{thumb{[img[GKR_Member_Collections_thumb.jpg][MemberPageImage]]}}}
;title
:''{VAR:name} ({VAR:code})'', e.g., ''Valdosta State University (valdosta)''. I.e., it's the institution's name and its code as defined in ''members.ini''.
;help
:''{FILE:member_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:member_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:member_page}'' (this page template is in this file)
![map_node]
This section defines the settings for the [[mapping|MemberMappingPage]] page.
{{thumb{[img[GKR_Member_Mapping_thumb.jpg][MemberMappingPageImage]]}}}
;title
:''Map a Collection''
;help
:''{FILE:map_node_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:map_node_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:map_node_page}'' (this page template is in this file)
![view_tree]
This section defines the settings for the [[view mappings|MemberViewMappingsPage]] page.
{{thumb{[img[GKR_Member_View_Mappings_thumb.jpg][MemberViewMappingsPageImage]]}}}
;title
:''View Communities/Collections Mappings''
;help
:''{FILE:view_tree_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:view_tree_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:view_tree_page}'' (this page template is in this file)
![edit_tree]
This section defines the settings for the [[edit mappings|MemberEditMappingsPage]] page.
{{thumb{[img[GKR_Member_Edit_Mappings_thumb.jpg][MemberEditMappingsPageImage]]}}}
;title
:''Edit Communities/Collections Mappings''
;help
:''{FILE:edit_tree_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:edit_tree_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:edit_tree_page}'' (this page template is in this file)
![view_coll]
This section defines the settings for the [[view GKR collections|GKRManageDataViewPage]] and [[view member collections|MemberManageDataViewPage]] pages.
{{thumb{[img[GKR_Member_Manage_Data_View_thumb.jpg][MemberManageDataViewPageImage]]}}}
;title
:''View Communities/Collections''
;help
:''{FILE:view_coll_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:view_coll_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:view_coll_page}'' (this page template is in this file)
![edit_coll]
This section defines the settings for the [[edit GKR collections|GKRManageDataEditPage]] and [[edit member collections|MemberManageDataEditPage]] pages.
{{thumb{[img[GKR_Member_Manage_Data_Edit_thumb.jpg][MemberManageDataEditPageImage]]}}}
;title
:''Edit Communities/Collections''
;help
:''{FILE:edit_coll_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:edit_coll_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:edit_coll_page}'' (this page template is in this file)
![manage_data]
This section defines the settings for the [[manage GKR data files|GKRManageDataPage]] and [[manage member data files|MemberManageDataPage]] pages.
{{thumb{[img[GKR_Member_Manage_Data_thumb.jpg][MemberManageDataPageImage]]}}}
;title
:''Manage Data Files''
;help
:''{FILE:manage_data_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:manage_data_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:manage_data_page}'' (this page template is in this file)
![users]
This section defines the settings for the [[users|UsersPage]] page.
{{thumb{[img[GKR_Users_admin_thumb.jpg][UsersPageImage]]}}}
;title
:''Users''
;help
:''{FILE:users_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:users_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:users_page}'' (this page template is in this file)
![admin]
This section defines the settings for the [[admin|AdminPage]] page.
{{thumb{[img[GKR_Admin_thumb.jpg][AdminPageImage]]}}}
;title
:''Admin''
;help
:''{FILE:admin_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:admin_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:admin_page}'' (this page template is in this file)
![view_file]
This section defines the settings for the [[view file|ViewFilePage]] page.
{{thumb{[img[GKR_ViewFilePage_thumb.jpg][ViewFilePageImage]]}}}
;title
:''View File: {VAR:file}'', e.g., ''View File: program.ini''
;help
:''{FILE:view_file_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:view_file_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:view_file_page}'' (this page template is in this file)
![edit_file]
This section defines the settings for the [[edit file|EditFilePage]] page, including EditBlurbPage, EditHelpPage, EditPagePage, etc.
{{thumb{[img[GKR_EditFilePage_thumb.jpg][EditFilePageImage]]}}}
;title
:''Edit File: {VAR:file}'', e.g., ''Edit File: members.ini''
;help
:''{FILE:edit_file_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:edit_file_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:edit_file_page}'' (this page template is in this file)
![edit_user]
This section defines the settings for the [[edit user|EditUserPage]] page.
{{thumb{[img[GKR_Edit_User_admin_thumb.jpg][EditUserPageImage]]}}}
;title
:Edit User: {VAR:file}, e.g., ''Edit User: brad'' (note that the file name is the userid)
;help
:''{FILE:edit_user_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:edit_user_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:edit_user_page}'' (this page template is in this file)
![add_user]
This section defines the settings for the [[add user|AddNewUserPage]] page.
{{thumb{[img[GKR_Add_User_coordinator_thumb.jpg][AddNewUserPageImage]]}}}
;title
:''Add New User''
;help
:''{FILE:add_user_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:add_user_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:add_user_page}'' (this page template is in this file)
![changes]
This section defines the settings for the ''changes'' page, including GKRChangesPage, MemberChangesPage, etc.
{{thumb{[img[GKR_Member_Changes_thumb.jpg][MemberChangesPageImage]]}}}
;title
:''Changes: {VAR:file}'', e.g., ''Changes: valdosta'' (in this case the file is named the same as the institution code)
;help
:''{FILE:changes_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:changes_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:changes_page}'' (this page template is in this file)
![diff]
This section defines the settings for the ''diff'' page, including GKRChangesDiffPage, MemberChangesDiffPage, etc.
{{thumb{[img[GKR_Member_Changes_Diff_thumb.jpg][MemberChangesDiffPageImage]]}}}
;title
:''Diff: {VAR:file}'', e.g., ''Diff: valdosta''
;help
:''{FILE:diff_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:diff_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:diff_page}'' (this page template is in this file)
![help]
This section defines the settings for the [[help|HelpPage]] page.
{{thumb{[img[GKR_Help_thumb.jpg][HelpPageImage]]}}}
;links
:Like the home page, the help page defines its own set of menu links. The only difference from the standard set (defined in ''[menu]'') is the addition of the "Edit help" link for supervisor users. This link is most applicable when you're viewing a help page.
;title
:''Help''
;help
:''{FILE:help_help}'' (the snippet of help text is in this file)
;blurb
:''{FILE:help_blurb}'' (the snippet of blurb text is in this file)
;page
:''{FILE:help_page}'' (this page template is in this file)
![error]
This section defines the settings for the [[error|ErrorPage]] page, which is displayed when something goes wrong, e.g.,
:http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=SantaClaus
The error page does double duty also as the confirmation page for the "Publish" link, e.g., GKRPublishPage, MemberPublishPage. Note that the error page does not define blurb or help text, and it also does not display any menu links (standard or otherwise).
{{thumb{[img[GKR_ErrorPage_thumb.jpg][ErrorPageImage]]}}}
;title
:''Please Note''.
;page
:''{FILE:error_page}'' (this page template is in this file)
!Examples
!!Page Section Example
Below is one of the page sections in ''program.ini''. Note that by storing in separate files the majority of the data that makes up a page, these page sections can be quite terse. Note also that the value for ''page'' is in single quotes just so we can put a comment after it. But we don't want to use double quotes here, because double quotes would cause the program to read in the contents of ''member_page'' whether it needed those contents or not.
{{{
#---------------------------------------------------------------------
# construct the member page
[member]
title = {VAR:name} ({VAR:code})
help = {FILE:member_help}
blurb = {FILE:member_blurb}
page = '{FILE:member_page}' # cookie gets set here (at login)
}}}
!!Help Text Example
Below are the full contents of the file, ''diff_help'', the name of which appears in the setting, "{{code{ help = {FILE:diff_help} }}}" in the ''[diff]'' section. You can see that it is a snippet of XHTML tags and text (~UTF-8, please). Template placeholders are allowed here -- you just have to learn which ones may be recognized (see [[Example Placeholders]]).
{{{
<h2>Help for: Diff</h2>
<p>
This page contains help information for the Diff ("Differences") page.
The page shows a display of differences between the current file
and a previous version.
Only the differences and some surrounding lines are displayed.
</p>
<p>
<i>"<span style="color:green">Green</span> is what you've got.
<span style="color:red">Red</span> is what you'll get instead."</i>
</p>
<p>
In other words, if you choose to recover the previous version,
you will lose all the green lines (if any) and gain all the red
lines (if any).
</p>
}}}
!!Blurb Text Example
Below are the contents of the file, ''diff_blurb'', the name of which appears in the setting, "{{code{ blurb = {FILE:diff_blurb} }}}" in the ''[diff]'' section. You can see that, like the help text above, this is a snippet of XHTML tags and text (~UTF-8, please). Template placeholders are allowed here, too. See [[Example Placeholders]] to see which ones may be recognized.
{{{
<p>
Below is a display of differences between the current file,
<i>{VAR:file}</i> and the previous version, <i>{VAR:file}{VAR:version}</i>.
Only the differences and some surrounding lines are displayed.
</p>
<p>
<i>"<span style="color:green">Green</span> is what you've got.
<span style="color:red">Red</span> is what you'll get instead."</i>
</p>
<p>
In other words, if you choose to recover the version, <i>{VAR:version}</i>,
you will lose all the green lines (if any) and gain all the red
lines (if any).
</p>
}}}
!!Page Template Example
Below are the contents of the file, ''diff_page'', the name of which appears in the setting, "{{code{ page = {FILE:diff_page} }}}" in the ''[diff]'' section. Unlike the help text and blurb text above, this is more than just XHTML tags and text. There are tags that define the overall structure of the XHTML, and there is some text, but most of the page template is comprised of template placeholders.
See [[Page Templates]] for an in-depth discussion of the makeup of these templates.
{{{
Content-Type: text/html; charset=UTF-8
{INI:program:page_doctype_header}
<head>
{INI:program:head}
<title>{INI:diff:title}</title>
</head>
<body>
<div id="page">
<div id="header">
{INI:program:page_logo}
{INI:menu:links}
<div class="clear"></div>
</div>
<div id="left_margin">
{INI:menu:quick_list}
</div>
<div id="body">
<div id="breadcrumb">
<a href="{INI:program:url}?action=home">Home</a>
{IF_VAR:member}» <a href="{INI:program:url}?member={VAR:code}">{VAR:name} ({VAR:code})</a>
<a href="{INI:program:rss_url}/{VAR:code}.rdf">{INI:program:rss_icon}</a>
{ELSE_IF_VAR:member}» <a href="{INI:program:url}?action=admin">Admin</a>{END_IF_VAR:member}
» <a href="{INI:program:url}?member={VAR:code};action=changes;file={VAR:file}">Changes</a>
» {INI:diff:title}
</div>
<h1>{INI:diff:title}: {VAR:file} vs. {VAR:file}{VAR:version}</h1>
<div class="blurb">
<div id="blurb">{INI:diff:blurb}</div>
{INI:program:blurb_hide}
</div>
{INI:javascript:blurb_hide}
<div id="view">
<pre>{VAR:diff}</pre>
</div>
</div>
<div id="footer" class="footer">
{INI:program:page_footer}
</div>
</div>
{INI:javascript:inbody}
</body>
</html>
}}}
!Sample Page Template
Following is an example page template; it's for the [[Diff|MemberChangesDiffPage]] display. After the example are discussions of each part to give an idea of how the pages are structured in the mapping tool.
{{{
Content-Type: text/html; charset=UTF-8
{INI:program:page_doctype_header}
<head>
{INI:program:head}
<title>{INI:diff:title}</title>
</head>
<body>
<div id="page">
<div id="header">
{INI:program:page_logo}
{INI:menu:links}
<div class="clear"></div>
</div>
<div id="left_margin">
{INI:menu:quick_list}
</div>
<div id="body">
<div id="breadcrumb">
<a href="{INI:program:url}?action=home">Home</a>
{IF_VAR:member}» <a href="{INI:program:url}?member={VAR:code}">{VAR:name} ({VAR:code})</a>
<a href="{INI:program:rss_url}/{VAR:code}.rdf">{INI:program:rss_icon}</a>
{ELSE_IF_VAR:member}» <a href="{INI:program:url}?action=admin">Admin</a>{END_IF_VAR:member}
» <a href="{INI:program:url}?member={VAR:code};action=changes;file={VAR:file}">Changes</a>
» {INI:diff:title}
</div>
<h1>{INI:diff:title}: {VAR:file} vs. {VAR:file}{VAR:version}</h1>
<div class="blurb">
<div id="blurb">{INI:diff:blurb}</div>
{INI:program:blurb_hide}
</div>
{INI:javascript:blurb_hide}
<div id="view">
<pre>{VAR:diff}</pre>
</div>
</div>
<div id="footer" class="footer">
{INI:program:page_footer}
</div>
</div>
{INI:javascript:inbody}
</body>
</html>
}}}
!HTTP Headers
At the very top of the template are the HTTP headers, typically the ~Content-Type header. In some page templates, you may also see a placeholder for a ~Set-Cookie header.
{{{
Content-Type: text/html; charset=UTF-8
}}}
!Document Begin
The [program] section in the configuration file defines the "page_doctype_header" value that is used to start every page. This will include the opening <html> tag and may also include the <!DOCTYPE> tag.
{{{
{INI:program:page_doctype_header}
}}}
!HTML Head
The <head> tag contains the head elements defined in the configuration file and the <title> tag, the value of which is also defined in the configuration section for the page.
{{{
<head>
{INI:program:head}
<title>{INI:diff:title}</title>
</head>
}}}
!HTML Body
The <body> tag is just as you would expect. The <div> tags and javascript included in the <body> tag are discussed below.
{{{
<body>
...
</body>
}}}
!!Page Div
The page div is a wrapper for the other main div's in the body. The [[table_maker()|Table Maker]] function will convert this div into a table element in which the header, left_margin, body, and footer div's will be table cells. This table defines the layout of the page. Note that this table is not generated for aural user agents (i.e., screen readers); they will just see the div's.
{{{
<div id="page">
...
</div>
}}}
!!Header Div
The header div is a container for the banner portion of the page. It contains the page logo, the menu links, and a "clear" div to undo floats.
{{{
<div id="header">
{INI:program:page_logo}
{INI:menu:links}
<div class="clear"></div>
</div>
}}}
!!Left Margin Div
The left_margin div is a container for the Quick Links menu that appears on most pages. It will also contain the Help Menu link when you're viewing a help page.
{{{
<div id="left_margin">
{INI:menu:quick_list}
</div>
}}}
!!Body Div
The "body" div is a container for the main content of the page. It typically starts with a breadcrumb div, an <h1> tag page heading, a couple of blurb divs, and then ends with the "real" contents of the page -- in this case a "view" div containing the diff display.
{{{
<div id="body">
<div id="breadcrumb">
<a href="{INI:program:url}?action=home">Home</a>
{IF_VAR:member}» <a href="{INI:program:url}?member={VAR:code}">{VAR:name} ({VAR:code})</a>
<a href="{INI:program:rss_url}/{VAR:code}.rdf">{INI:program:rss_icon}</a>
{ELSE_IF_VAR:member}» <a href="{INI:program:url}?action=admin">Admin</a>{END_IF_VAR:member}
» <a href="{INI:program:url}?member={VAR:code};action=changes;file={VAR:file}">Changes</a>
» {INI:diff:title}
</div>
<h1>{INI:diff:title}: {VAR:file} vs. {VAR:file}{VAR:version}</h1>
<div class="blurb">
<div id="blurb">{INI:diff:blurb}</div>
{INI:program:blurb_hide}
</div>
{INI:javascript:blurb_hide}
<div id="view">
<pre>{VAR:diff}</pre>
</div>
</div>
}}}
!!Footer Div
The footer div is a container for the footer elements.
{{{
<div id="footer" class="footer">
{INI:program:page_footer}
</div>
}}}
!!In-body Javascript
After the body div and before the end </body> tag is the javascript that runs when a page is loaded (including the table_maker() function calls). This placement means that we don't have to arrange for an ''onload'' attribute in the <body> tag.
{{{
{INI:javascript:inbody}
}}}
!Document End
The end of the page document is simply the end html tag.
{{{
</html>
}}}
<!--{{{-->
<div class='header' macro='gradient vert #FFF #FFF '>
<div class='gradient'>
<div class='titleLine' >
<span class='searchBar' macro='search'></span>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
</div>
</div>
<div id='bodywrapper'>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<div id='displayFooter'></div>
</div>
<!--}}}-->
!Perl CGI
The program, gkr_mapping, is a Perl CGI program.
The [[modules|Perl Modules]] it uses include core Perl modules, some CPAN modules, and some locally developed modules.
It uses ini-type configuration files for a number of purposes, including the main program's [[configuration settings|Global Configuration Sections]] (paths, key values, etc.), the [[member institutions' settings|Member Definition Sections]], and all of the [[page templates|Page Template Sections]].
The program and it's configuration files make up a sort of mini Web framework.
!Perl Modules
The following Perl modules are used in the mapping tool program.
!!Core Modules (installed with Perl)
;[[Encode|http://perldoc.perl.org/Encode.html]]
:For handling ~UTF-8 encoded data
;[[Fcntl|http://perldoc.perl.org/Fcntl.html]]
:For file locking primarily
;[[POSIX|http://perldoc.perl.org/POSIX.html]]
:For file locking primarily
;[[File::Path|http://perldoc.perl.org/File/Path.html]]
:For creating paths (mkpath())
;[[CGI::Cookie|http://perldoc.perl.org/CGI/Cookie.html]]
:For Cookie handling
;[[Digest::MD5|http://perldoc.perl.org/Digest/MD5.html]]
:For Session ~IDs
;[[File::Temp|http://perldoc.perl.org/File/Temp.html]]
:For spellchecking
;[[Data::Dumper|http://perldoc.perl.org/Data/Dumper.html]]
:For debugging
!!CPAN Modules (may need to be installed)
;[[CGI::Minimal|http://search.cpan.org/dist/CGI-Minimal/]]
:For CGI parameter handling
;[[XML::Simple|http://search.cpan.org/dist/XML-Simple/]]
:For parsing local institutions' communities XHTML pages
;[[LWP::Simple|http://search.cpan.org/dist/libwww-perl/lib/LWP/Simple.pm]]
:For updating collections data from institution's IR
!!Local Perl Modules
;[[Config::Ini]]
:This is the base ini file processing module.
;[[Config::Ini::Edit]]
:This module allows the program to update the ini file on disk.
:The docs are long enough to need two pages; see also [[Config::Ini::Edit Page 2]].
;[[Config::Ini::Expanded]]
:This module has built-in templates and file includes.
:The docs are long enough to need three pages; see also [[Config::Ini::Expanded Page 2]], [[Config::Ini::Expanded Page 3]].
;[[Config::Ini::Quote]]
:This module is used by the other Config::Ini modules.
;[[Tree::Indented]]
:This module allows the program to parse indented text into a simple tree structure.
:Much of the key data, including the communities/collections and mappings, is stored as indented text.
!Perl Version
The mapping tool requires at least Perl version 5.8.8. So far, it has been tested only on:
{{{
$ /usr/local/bin/perl -v
This is perl, v5.8.8 built for sun4-solaris
}}}
!Preinstallation Steps
Note that these instructions are for the developer who is preparing a set of distribution tarballs for [[Installation]].
* Check out the mapping tool files from svn (see [[Version Control]])
* Export the files to a clean directory, e.g.,
{{{
svn export . ~/gkr/exported
}}}
* make htdocs, dbs, and cgi-bin tarballs, e.g.,
{{{
cd ~/gkr/exported/htdocs; tar cvf gkr_htdocs.tar ./* && gzip gkr_htdocs.tar
cd ~/gkr/exported/dbs; tar cvf gkr_dbs.tar ./* && gzip gkr_dbs.tar
cd ~/gkr/exported/cgi-bin; tar cvf gkr_cgi-bin.tar ./* && gzip gkr_cgi-bin.tar
}}}
* copy the *.tar.gz (tarball) files to the gkr svn dist directory for committing
{{{
cp ~/gkr/exported/htdocs/gkr_htdocs.tar.gz $working/misc/GKR/trunk/dist
cp ~/gkr/exported/dbs/gkr_dbs.tar.gz $working/misc/GKR/trunk/dist
cp ~/gkr/exported/cgi-bin/gkr_cgi-bin.tar.gz $working/misc/GKR/trunk/dist
}}}
* copy them to the active htdocs/gkr_mapping/dist directory for downloading
{{{
cp ~/gkr/exported/htdocs/gkr_htdocs.tar.gz $devweb/gkr_mapping/dist
cp ~/gkr/exported/dbs/gkr_dbs.tar.gz $devweb/gkr_mapping/dist
cp ~/gkr/exported/cgi-bin/gkr_cgi-bin.tar.gz $devweb/gkr_mapping/dist
}}}
* copy these files (or the more current versions) from the gkr svn dist directory to the active htdocs/gkr_mapping/dist directory for downloading<br />(remove older duplicates)
{{{
cp $working/misc/GKR/trunk/dist/Config-Ini-1.03.tar.gz $devweb/gkr_mapping/dist
cp $working/misc/GKR/trunk/dist/Config-Ini-Edit-1.03.tar.gz $devweb/gkr_mapping/dist
cp $working/misc/GKR/trunk/dist/Config-Ini-Expanded-1.11.tar.gz $devweb/gkr_mapping/dist
cp $working/misc/GKR/trunk/dist/Config-Ini-Quote-1.01.tar.gz $devweb/gkr_mapping/dist
cp $working/misc/GKR/trunk/dist/Tree-Indented-1.02.tar.gz $devweb/gkr_mapping/dist
}}}
* Tar 'em all up for a single download.
{{{
cd $devweb/gkr_mapping/dist; rm gkr_mapping.tar; tar cvf gkr_mapping.tar ./*
}}}
!Program Data Files
Included with the mapping tool distribution are the following kinds of data files:
* the two configuration files, program.ini and members.ini
* page templates, help text, and blurb text.
* communities and collections for each institution.
* mappings for each institution.
* user profiles
* css
* javascript
* html (one ''index.html'' file)
* wiki files
* image files
!Program Directory Structure
There are three main areas where files associated with the mapping tool are stored: ''cgi-bin'', ''htdocs'', and ''dbs''.
!cgi-bin
The cgi-bin area is the usual Apache ''~ScriptAlias'' directory. Related to the mapping tool, it will contain the one program: gkr_mapping. It will have to be executable by the Apache httpd process.
{{{
/cgi-bin/gkr_mapping
}}}
{{center{
[img[featurebottom.png]]}}}
!htdocs
The htdocs area is the usual Apache ''~DocumentRoot'' directory. It will have a gkr_mapping directory that contains subdirectories for the mapping tool CSS, Javascript, images, and wiki files.
In addition, the gkr_mapping directory contains an ''index.html'' that redirects to the cgi-bin program, e.g., it would redirect this address
:http://gkr-prod.library.gatech.edu/gkr_mapping
to this address
:http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping
Finally, the gkr_mapping directory contains an ''rss'' directory where the RSS feeds are published and a ''tables'' directory where the mapping tool's lookup tables are published.
{{{
/htdocs/gkr_mapping/...
index.html
css/...
gkr_mapping.css -> .../dbs/gkr_mapping/gkr_mapping.css (symlink)
javascript/...
images/...
wiki/...
rss/...
tables/...
}}}
An example RSS address: http://gkr-prod.library.gatech.edu/gkr_mapping/rss/valdosta.xml
An example lookup table address: http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.xml
{{center{
[img[featurebottom.png]]}}}
!dbs
The dbs area is not accessible by Apache except through the execution of the gkr_mapping program. It contains a directory, gkr_mapping, where most of the data files described above are located. Because these data files may be written to by the gkr_mapping program, they must have write permissions granted (in some way) to the Apache httpd process that executes gkr_mapping.
{{{
/dbs/gkr_mapping/...
program.ini (main configuration file)
members.ini (configuration for the member institutions)
gkr_mapping.css (main css file--needs a symlink from the htdocs area)
admin_blurb (_blurb/_help/_page templates)
admin_help
admin_page
... (more templates)
collections/... (collections for each institution)
mappings/... (mappings for each institution)
users/... (user profiles)
sessions/... (login session files)
bak/... (backup files)
perlib/... (local Perl modules)
}}}
!Programming Details of the Mapping Tool
The gkr_mapping program uses a simple Web framework strategy to organize the interface into a set of [[functional|Functional]] pages. The program performs a number of different operations. Requests are communicated to it via CGI parameters in ~URLs or in HTML forms. The program responds by performing the operation and displaying a page. Of course, sometimes the operation is simply to display a page.
This communication via parameters takes (roughly) a Subject -> Verb -> Adjective -> Object approach. The Subject is always the user. Depending on the user's permissions and institution group, the program may or may not allow the Verb to be done or to be done to the requested Object. For example:
{{{
http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta;action=map_node;node=valdosta-383-363-359
}}}
The Verb (action) is "map_node". The Object is node "valdosta-383-363-359". And the Adjective is member "valdosta". So the above URL is saying, "I (the user) wish to map the valdosta (collection) node valdosta-383-363-359."
Not all ~URLs have that many parameters. In most cases missing parts of the request have understood defaults, e.g., if you are not logged in, http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping is a request to view the home page, as if you had explicitely said http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=home. But if you //are// logged in then http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping would display your institution's member page, as if you had explicitly said http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?member=valdosta (assuming your user profile has you assigned to the valdosta group).
{{center{
[img[featurebottom.png]]}}}
!More Details
All of the parameters are discussed on the [[API]] page. The [[Coding Strategies]] page attempts to cover some of the thought behind how the program code is written (with the maintenance programmer in mind), and [[Version control]] gives details about where all the versions of the code are kept as changes have been made over time.
!![[API]]
The Application Programming Interface (API), covering all the parameters and the operations they perform
!![[Coding Strategies]]
A discussion if how the program code is written
!![[Version Control]]
Details about where the authoritative copies of the program code and supporting files are stored
!Session Files
When a user logs in, a session file is created in the dbs area as noted above, ''/dbs/gkr_mapping/sessions/''.
Session files are named randomly, e.g., 372c001b32ad8851e8d46c79efc56c4e and this name is stored in a browser session cookie named ''gkr_sid''.
Each session file contains a single line of space-separated fields, e.g.,
{{{
john_user all ae 20100209 example@example.com John Q. User
}}}
These fields are as follows:
* user id, e.g. john_user
* group, e.g., all
* permissions, e.g., ae (a=is_admin, e=may_edit)
* yyyymmdd date, e.g., 20100209
* email address, e.g., example@example.com
* user name (taking up the rest of the line), e.g., John Q. User
The permissions field is a string of characters where each character represents a qualifier. The full list of qualifiers is:
* p (is a public user)
* a (is an admin user -- vs. a public user)
* c (is a coordinator)
* e (may edit collections and mappings data)
* s (is a supervisor)
Every time a user logs in, older session files (i.e., from previous days) are deleted.
<<closeAll>><<permaview>><<newTiddler>><<newJournal 'YYYY MMM DD'>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
The tabs below echo the links in the banner above. Each tab lists all of the pages that are tagged as belonging to that general area.
<<tabs SiteMapTabs
[[Introduction ]] "Introductory pages" [[introduction]]
[[Tutorials ]] "Tutorial pages" [[tutorials]]
[[Functional ]] "Tutorial pages" [[functional]]
[[Technical ]] "Tutorial pages" [[technical]]
[[Programming ]] "Tutorial pages" [[programming]]
[[Configuration ]] "Tutorial pages" [[configuration]]
[[Installation ]] "Tutorial pages" [[installation]]
[[Documentation ]] "Tutorial pages" [[documentation]]
>>
a wiki for the mapping tool
<html><a href="http://gkr-prod.library.gatech.edu/gkr_mapping/">Georgia Knowledge Repository Mapping Tool</a></html>
!!Step 1, Perl Modules, CPAN
Install the following modules from CPAN, if they have not been installed already.
* CGI::Minimal
* XML::Simple
!!Step 2, Mapping Tool Directories
As the [[Program Directory Structure]] page explains, there are three directories containing the files that the mapping tool needs:
* cgi-bin
* htdocs
* dbs
On the development server, these directories are located here:
* /galileo/dev/cgi-bin/
* /galileo/dev/htdocs/
* /galileo/dev/dbs/
As you can see, these are located under the same parent (''/galileo/dev/'') but they do not have to be. For the sake of this installation guide, we'll assume the following hypothetical locations. You should use your actual paths instead:
* /apps/scripts/cgi-bin/
* /web/docs/htdocs/
* /data/dbs/
Note also that these paths to not have to end in "cgi-bin", "htdocs", and "dbs". They might instead be:
* /apps/cgi/
* /apps/docs/
* /apps/data/
The steps below will show the changes you can make for your situation.
For clarity, in the rest of this installation guide, we'll use the hypothetical "cgi-bin", "htdocs", and "dbs" versions above.
!!Step 3, Mapping Tool Distribution Tarballs
There are three distribution tarballs that correspond to the three directories listed above. Below are their names and instructions for installing them:
Note that all of these are contained in [[gkr_mapping.tar|http://dev.galileo.usg.edu/gkr_mapping/dist/gkr_mapping.tar]] if you wish to download them all in one file (2.8 Megs -- 90% of that is this wiki and its images). After downloading this file, simply untar it to get the tarballs listed below.
* [[gkr_cgi-bin.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/gkr_cgi-bin.tar.gz]]
## click the file name to download and save
## unzip this file.
## cd to ''/apps/scripts/cgi-bin/'' (the "cgi-bin" directory) and untar the contents.
## You should see two new files, e.g., ''/apps/scripts/cgi-bin/gkr_mapping'' and ''/apps/scripts/cgi-bin/gkr_mapping.inc''
* [[gkr_htdocs.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/gkr_htdocs.tar.gz]]
## click the file name to download and save
## unzip this file.
## cd to ''/web/docs/htdocs/'' (the "htdocs" directory) and untar the contents.
## They will be written to a "gkr_mapping" directory, e.g., ''/web/docs/htdocs/gkr_mapping/''
* [[gkr_dbs.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/gkr_dbs.tar.gz]]
## click the file name to download and save
## unzip this file.
## cd to ''/data/dbs/'' (the "dbs" directory) and untar the contents.
## They will be written to a "gkr_mapping" directory, e.g., ''/data/dbs/gkr_mapping/''
* ''Extra step!'': CSS symbolic link
## cd to "/web/docs/htdocs/gkr_mapping/css/"
## create symbolic link, e.g., ''ln -s /data/dbs/gkr_mapping/gkr_mapping.css gkr_mapping.css''
!!Step 4, Local Perl Modules Distribution Tarballs
There are more tarballs included in the distribution for the local Perl modules used in the mapping tool. You may choose not to process these files. The modules are already preinstalled in the gkr_dbs.tar.gz (they will be written to, e.g., ''/data/dbs/gkr_mapping/perlib/''). Click a filename to download it.
Note that all of these are contained in [[gkr_mapping.tar|http://dev.galileo.usg.edu/gkr_mapping/dist/gkr_mapping.tar]] if you wish to download them all in one file (2.8 Megs). After downloading this file, simply untar it to get the tarballs listed below.
* [[Config-Ini-1.03.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/Config-Ini-1.03.tar.gz]]
* [[Config-Ini-Edit-1.03.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/Config-Ini-Edit-1.03.tar.gz]]
* [[Config-Ini-Expanded-1.11.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/Config-Ini-Expanded-1.11.tar.gz]]
* [[Config-Ini-Quote-1.01.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/Config-Ini-Quote-1.01.tar.gz]]
* [[Tree-Indented-1.02.tar.gz|http://dev.galileo.usg.edu/gkr_mapping/dist/Tree-Indented-1.02.tar.gz]]
If desired, you could install these in your system's standard Perl modules location (so other Perl scripts could use them), but the mapping tool expects to get them from the "dbs" area, where they are already pre-installed.
You could also do a partial install just to run the 'make test' step to insure that all the modules' tests pass on your system.
These tarballs are packaged for uploading to CPAN (which may or may not have occurred yet), so the usual installation steps should work, i.e.,
* unzip/untar the distribution file
* perl Makefile.PL (or perl Makefile.PL PREFIX=/your/perlib LIB=/your/perlib)
* make
* make test
* make install
!!Step 5, Modify Program Source File
After installing the above distribution files, the mapping tool program files should be located here:
<<<
''/apps/scripts/cgi-bin/gkr_mapping''
''/apps/scripts/cgi-bin/gkr_mapping.inc''
<<<
Following are a few lines that need to be changed in those files for installation at your site.
!!!She-bang Line
The first line in the file ''gkr_mapping'' is the "she-bang" line and should currently look like this:
{{{
#!/usr/local/bin/perl -T
}}}
If your Perl 5.8.? binary is already at ''/usr/local/bin/perl'', you do not have to change this line. If it isn't, you have a few choices:
# Edit the above line to contain the path to your Perl binary (but leave the "''-T''" there!)
# Copy your Perl binary to ''/usr/local/bin/perl''.
# Make a symbolic link to your Perl binary at ''/usr/local/bin/perl''.
The last two steps are preferable to the first, so that the source file need not be changed at all.
{{center{
[img[featurebottom.png]]}}}
!!!Global Installation Values
The file ''gkr_mapping.inc'' (which gets included into the source at runtime) contains these lines:
{{{
#---------------------------------------------------------------------
# Global installation values
# During installation, only these three or four should need changing
$Host = "gkr-prod.library.gatech.edu"; # Web domain
$HTpath = "/galileo/dev/htdocs"; # path to DocumentRoot
$DBSpath = "/galileo/dev/dbs"; # path to data files
$CGIurl = "cgi-bin"; # for URL paths
1; # return true
}}}
* Change the ''$Host'' value to your site's domain, e.g., ''gkr.usg.edu''
** Do ''not'' include the protocol, e.g., "<nowiki>http://</nowiki> or "<nowiki>https://</nowiki>
* Change the ''$~HTpath'' value to your site's actual directory path, e.g., ''/web/docs/htdocs''
** This should be the same path where you untarred ''gkr_htdocs.tar.gz'' above.
** This path should ''begin'' with a slash '''/''', i.e., from the absolute root, but it should ''not'' end with a slash -- ending slashes get added by the program.
* Change the ''$~DBSpath'' value to your site's actual directory path, e.g., ''/data/dbs''
** This should be the same path where you untarred gkr_dbs.tar.gz above.
** This path should ''begin'' with a slash '''/''', i.e., from the absolute root, but it should ''not'' end with a slash -- ending slashes get added by the program.
* //''If''// your cgi-bin url path is not literally "cgi-bin", then you should change the ''$~CGIurl'' value.
** For example, if the mapping tool address is really ''<nowiki>http://gkr.gatech.edu/cgi/gkr_mapping</nowiki>'', then the ''$~CGIurl'' value should be "''cgi''".
** This value should ''not'' begin ''or'' end with a slash '''/'''.
!!Step 6, Web Server Permissions
!!!cgi-bin
Obviously, your web server (e.g., apache) must have execute permissions for the Perl CGI program, ''/cgi-bin/gkr_mapping''.
!!!htdocs
Obviously, your apache must have read permissions for the files in ''/htdocs/gkr_mapping/''. In addition, your apache process must have write permissions for the files in these directories:
* /htdocs/gkr_mapping/rss
* /htdocs/gkr_mapping/tables
The "publish" action will write RSS feeds in the "rss" directory and will write mapping table files in the "tables" directory, e.g.,
* http://gkr-prod.library.gatech.edu/gkr_mapping/rss/gkr.xml
* http://gkr-prod.library.gatech.edu/gkr_mapping/tables/valdosta.xml
!!!dbs
The files in the ''/dbs/gkr_mapping/'' directory are not directly accessed by your apache, except via the mapping tool program. But the apache process must have write permission for files in that directory (and it's subdirectories), because they are the data files that the mapping tool updates. These include:
* the mappings data
* the collections data
* the user profiles
* session files
* blurb text, help text, page templates
* backup files
!!Step 7, Testing the Mapping Tool
Note: the links below are just examples. Please also click through the normal navigation as much as you can to test each of the listed functions.
* Home
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=home
* Login/Logout
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=login
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=logout
* Member pages
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=gatech
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=gsu
* Mapping/Unmapping
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=map_node;node=asurams-201-101
* Publish
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=publish
* Manage Data
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=gkr;action=manage_data
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=manage_data
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=gatech;action=manage_data
* View Collections
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=view_coll;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=view_coll;member=asurams
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=view_coll;member=gatech
* Edit Collections
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_coll;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_coll;member=asurams
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_coll;member=gatech
* Collections Changes
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=changes;file=gkr_coll;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=changes;file=asurams_coll;member=asurams
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=changes;file=gatech_coll;member=gatech
* Collections Diffs (note -- example only; your version will be different)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=diff;file=asurams_coll;version=.20100405.001
* Collections Recover (note -- example only; your version, etc., will be different)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=recover;file=asurams_coll;version=.20100405.001;timestamp=1270483095;parms=action%3dchanges%3bmember%3dasurams%3bfile%3dasurams_coll
* View Mappings
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=view_tree;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=view_tree;member=asurams
* Edit Mappings
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_tree;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_tree;member=asurams
* Mappings Changes
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=changes;file=gkr;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=changes;file=asurams;member=asurams
* Mappings Diffs (note -- example only; your version, etc., will be different)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=diff;file=asurams;version=.20100405.003
* Mappings Recover (note -- example only; your version, etc., will be different)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?member=asurams;action=recover;file=asurams;version=.20100405.003;timestamp=1270485248;parms=action%3dchanges%3bmember%3dasurams%3bfile%3dasurams
* Update Collections
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=update_coll;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=update_coll;member=gatech
* Merge Collections
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=merge_gkr;member=gkr
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=merge_coll;member=gatech
* Help
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=help;page=home
* Edit Help (supervisor only)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_help;parms=action%3dhelp%3bpage%3dhome
* Edit Blurb (supervisor only)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_blurb;parms=action%3dhome
* Edit page (supervisor only)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_file;file=home_page;parms=action%3dhome
* Admin (supervisor only)
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=admin
* Users
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=users
* Add User
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=add_user
* Edit User
http://gkr.galileo.usg.edu/cgi-bin/gkr_mapping?action=edit_user;user_file=brad
* Delete User
(Use the Edit User page for this.)
/***
!Local
***/
/*{{{*/
.thumb img {
vertical-align:top;
}
.thumb_right img {
vertical-align:top;
float: right;
}
.image img {
border:1px solid #ccc;
padding: 2px;
vertical-align:top;
}
.bold {
font-weight: bold;
}
.code {
border: 1px solid #B2B6BE;
background: #EBEEF1;
font-family: monospace;
padding: 0 2px 0 2px;
}
.center {
text-align:center;
}
/*}}}*/
/***
Inspired by k2
!General
***/
/*{{{*/
body {
background: #EDEDED;
}
#contentWrapper{
background: #fff;
border:1px solid #DDD;
margin: 0 auto;
/*width: 780px;*/
width: 90%;
padding:0;
}
/*}}}*/
/***
!Links
***/
/*{{{*/
a,
a.tiddlyLink,
a.button,
a.externalLink,
#sidebarOptions .sliderPanel a{
color: #1D65BC;
text-decoration: none;
background: transparent;
border: 0;
}
a:hover,
a.tiddlyLink:hover,
a.button:hover,
a.externalLink:hover,
#sidebarOptions .sliderPanel a:hover
{
border: 0;
color: #1D65BC;
text-decoration: underline;
background:transparent;
}
.button:active {background:#1d65bc; border:0;}
.viewer .button:active, .viewer .marked, .viewer .highlight {
color: #fff !important;
background: #3371a3;
border: 0;
}
/*}}}*/
/***
!Header
***/
/*{{{*/
.gradient {margin-top:20px; background:#3371A3;}
.titleLine{padding: 20px 20px 10px 20px;}
.titleLine a:hover{color:#fff; border-bottom:1px dotted #eee; text-decoration:none;}
.titleLine a{color:#fff; border-bottom:1px dotted #ccc;}
.siteTitle {
font-size: 2.2em;
font-weight: bold;
color:#fff;
}
.siteSubtitle {
font-size: 1.0em;
display: block;
margin: .3em auto 1em;
color:#fff;
}
/*}}}*/
/***
!TopMenu
***/
/*{{{*/
#topMenu br {display:none; }
#topMenu { background: #3371A3; font-size:1em; }
#topMenu { padding:5px 32px; }
#topMenu .button, #topMenu .tiddlyLink {
margin-left:0.1em; margin-right:0.1em;
padding:0.5em;
color:white; font-weight:normal;
}
#topMenu a.button:hover, #topMenu a.tiddlyLink:hover { background:#fff; color:#333; text-decoration:none;}
.searchBar {float:right; font-size:0.9em;}
.searchBar .button {display:block; border:none; color:#ccc;}
.searchBar .button:hover{border:none; color:#eee;}
.searchBar input{
border: 1px inset #1d65bc; background:#dbdee3;
}
.searchBar input:focus {
border: 1px inset #3371a3; background:#fff;
}
/*}}}*/
/***
!Display
***/
/***
!!!Display General
***/
/*{{{*/
#displayArea { margin: 0em 15.7em 0em 1em; }
#tiddlerDisplay{padding-top:1em;}
/*}}}*/
/***
!!!Tiddler
***/
/*{{{*/
.tiddler {margin-bottom:1em; padding-bottom:1em;}
.tiddler {padding-left:2em;}
.title {color:#333; font-size:1.8em; border-bottom:1px solid #333; padding-bottom:0.3px;}
.subtitle { font-size:90%; color:#bbb; padding-left:0.25em; margin-top:0.1em; }
.shadow .title {
color: #aaa;
}
h1,h2,h3,h4,h5 { color: #333; background: transparent; padding-bottom:2px; border-bottom: 1px dotted #666; }
* html .viewer pre {
margin-left: 0em;
}
.viewer hr {
border: 0;
border-top: solid 1px #333;
margin: 0 8em;
color: #333;
}
.viewer a.button {color:#000; border:1px solid #1D65BC; font-weight:bold;}
.viewer a.button:hover{color:#fff; background:#3371a3; text-decoration:none;}
.tagClear {clear:left;}
.toolbar .button {color:#bbb; border:none;}
.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active {background:transparent; color:#111; border:none; text-decoration:underline;}
/*.tiddler {border-bottom:3px solid #EEF1F3; padding-bottom:2em; padding-top:0em;}*/
.tiddler{
background-image:url('featurebottom.png');
background-repeat:no-repeat;
background-position:bottom center;
padding-bottom:3em;
padding-top:0em;
}
.title {border-bottom:none; margin-right:8em;}
h1,h2,h3,h4,h5 { color: #333; background: transparent; padding-bottom:2px; border-bottom: none; }
.viewer pre, .viewer code {
border: 1px solid #B2B6BE;
background: #EBEEF1;}
.tagging {
margin-right: 2em;
}
.tagging, .tagged {
border: 1px solid #dbdee3;
background-color: #ebeef1;
}
.selected .tagging, .selected .tagged {
background-color: #dbdee3;
border: 1px solid #B2B6BE;
}
.tagging .listTitle, .tagged .listTitle {
color: #bbb;
}
.selected .tagging .listTitle, .selected .tagged .listTitle {
color: #014;
}
.tagging .button:hover, .tagged .button:hover {
border: none; background:transparent; text-decoration:underline; color:#014;
}
.tagged .highlight, .tagged .marked, .tagged a.button:active {text-decoration:underline; background:transparent; color:#014;}
.tagging .button, .tagged .button {
color:#bbb;
}
.selected .tagging .button, .selected .tagged .button {
color:#014;
}
.viewer blockquote {
border-left:7px solid #ebeef1;
}
.viewer table {
border: 1px solid #3371a3;
}
.viewer th, thead td {
background: #3371a3;
border: 1px solid #3371a3;
color: #fff;
}
.viewer td, .viewer tr {
border: 1px solid #3371a3;
}
/*}}}*/
/***
!!!Editor
***/
/*{{{*/
* html .editor textarea, * html .editor input {
width: 98%;
}
.editor input, .editor textarea {
border: 1px solid #1d65bc; background:#ebeef1;
}
.editor {padding-top:0.3em;}
.editor textarea:focus, .editor input:focus {
border: 1px inset #3371a3; background:#fff;
}
/*}}}*/
/***
!Sidebar
***/
/*{{{*/
#sidebar{
position:relative;
float:right;
margin-bottom:1em;
display:inline;
width: 16em;
}
#sidebar .tabSelected, #sidebar .tabSected:hover {
color: #000;
background: #dbdee3;
border-top: solid 1px #B2B6BE;
border-left: solid 1px #B2B6BE;
border-right: solid 1px #B2B6BE;
border-bottom:solid 1px #dbdee3 !important;
padding-bottom:1px;
text-decoration:none;
}
#sidebarOptions, #sidebarTabs {border-left: 1px solid #B2B6BE;}
#sidebarTabs {border-bottom: 1px solid #B2B6BE;}
#sidebar .tabUnselected, #sidebar .tabUnselected:hover {
color: #F0F3F5;
background: #B2B6BE ;
border: solid 1px #B2B6BE ;
padding-bottom:1px;
}
#sidebarTabs .tabContents {border:none; background:#DBDEE3; }
#sidebarTabs .tabContents {border-top:1px solid #B2B6BE;}
#sidebarTabs .tabContents .tabContents {border-left:1px solid #b2b6be;}
#sidebarOptions .sliderPanel {
background: #EBEEF1; border:none;
}
#sidebarOptions input {
border: 1px solid #1d65bc;
}
#sidebarOptions input:hover, #sidebarOptions input:active, #sidebarOptions input:focus {
border: 1px inset #3371a3;
}
#sidebar {background: #EBEEF1 ; right:0;}
#sidebar .button:active, #sidebar .marked, #sidebar .highlight {color:#014; background:transparent;text-decoration:none}
/*}}}*/
/***
!!Popups
***/
/*{{{*/
.popup {
background: #3371a3;
border: 1px solid #333;
}
.popup hr {
color: #333;
background: #333;
border-bottom: 1px;
}
.popup li.disabled {
color: #333;
}
.popup li a, .popup li a:visited {
color: #eee;
border: none;
}
.popup li a:hover {
background: #3371a3;
color: #fff;
border: none;
text-decoration:underline;
}
/*}}}*/
/***
!!Message Area
***/
/*{{{*/
#messageArea {
border: 2px dashed #3371a3;
background: #dbdee3;
color: #fff;
font-size:90%;
}
#messageArea .button {
color: #1d65bc;
background: #ebeef1;
text-decoration:none;
font-weight:bold;
border:none;
}
#messageArea a.button {color:#1d65bc;}
#messageArea .button:hover {text-decoration:underline;}
/*}}}*/
/***
!!Tabs
***/
/*{{{*/
.viewer .tabSelected, .viewer .tabSelected:hover{
color: #014;
background: #eee;
border-left: 1px solid #B2B6BE;
border-top: 1px solid #B2B6BE;
border-right: 1px solid #B2B6BE;
}
.viewer .tabUnselected, .viewer .tabUnselected:hover {
color: #fff;
background: #B2B6BE;
}
. viewer .tabContents {
color: #014;
background: #ebeef1;
border: 1px solid #B2B6BE;
}
/*}}}*/
.blog h2, .blog h3, .blog h4{
margin:0;
padding:0;
border-bottom:none;
}
.blog {margin-left:1.5em;}
.blog .excerpt {
margin:0;
margin-top:0.3em;
padding: 0;
margin-left:1em;
padding-left:1em;
font-size:90%;
border-left:1px solid #ddd;
}
#tiddlerWhatsNew h1, #tiddlerWhatsNew h2 {border-bottom:none;}
div[tags~="RecentUpdates"], div[tags~="lewcidExtension"] {margin-bottom: 2em;}
#topMenu .fontResizer {float:right;}
#topMenu .fontResizer .button{border:1px solid #3371A3;}
#topMenu .fontResizer .button:hover {border:1px solid #fff; color:#3371A3;}
#sidebarTabs .txtMainTab .tiddlyLinkExisting {
font-weight: normal;
font-style: normal;
}
#sidebarTabs .txtMoreTab .tiddlyLinkExisting {
font-weight: bold;
font-style: normal;
}
!NAME
''Table Maker'', a collection of javascript functions that modify the HTML DOM to make <div>s, <ul>s, <ol>s, and <dl>s into tables.
!SYNOPSIS
HTML like the following:
{{{
<html>
<head>
<title>table_maker test</title>
<script type="text/javascript" src="/javascript/table_maker/table_numbers.js"></script>
<script type="text/javascript" src="/javascript/table_maker/table_maker.js"></script>
</head>
<body onload="table_maker(hflmrmff)"> <!-- HeaderFull LeftMiddle RightMiddle FooterFull -->
<div id="page">
<div id="header" > Header </div>
<div id="left_margin" > Left Margin </div>
<div id="body" > Body </div>
<div id="right_margin" > Right Margin </div>
<div id="footer" > Footer </div>
</div>
</body>
</html>
}}}
... should format into a table like the following. (Note that the above values, like 'hflmrmff', 'page', 'header', 'left_margin', etc. have been predefined in the table_numbers.js file, but the table_maker functions are not limited to these values -- most everything is configurable.)
<html> <TABLE id="page"> <TBODY> <TR valign="top"> <TD id="td_header" colspan="3" align="center"> <DIV id="header-x"> Header </DIV> </TD> </TR> <TR valign="top"> <TD id="td_left_margin"> <DIV id="left_margin"> Left Margin </DIV> </TD> <TD id="td_body"> <DIV id="body"> Body </DIV> </TD> <TD id="td_right_margin"> <DIV id="right_margin"> Right Margin </DIV> </TD> </TR> <TR valign="top"> <TD id="td_footer" colspan="3" align="center"> <DIV id="footer-x"> Footer </DIV> </TD> </TR> </TBODY> </TABLE> </html>
!DESCRIPTION
The ''Table Maker'' collection of functions includes the following:
{{{
table_maker() dispatch table for the following functions:
div_table() make a set of <div>s into a table
ul_table() make a <ul> tag (and its <li>s) into a table
ol_table() make a <ol> tag (and its <li>s) into a table
dl_table() make a <dl> tag (and its <dt>s and <dd>s) into a table
}}}
See http://dev.galileo.usg.edu/table_maker.html for a demonstration of the various functions.
The motivation for this set of functions is to make it easier to define tables used for page layout. The HTML is marked up (presumably reasonably semantically) as <div>s, <ul>s, <ol>s, and <dl>s. When the page is rendered, the table maker javascript functions convert that markup in to layout tables.
Of course, W3C's Web Content Accessibility Guidelines 1.0 (http://www.w3.org/TR/WCAG10/) say this:
Tables should be used to mark up truly tabular information ("data tables"). Content developers should avoid using them to lay out pages ("layout tables"). Tables for any use also present special problems to users of screen readers.
That said, in the author's experience, using CSS techniques for page layout presents its own very special problems for the web designer. The fault isn't necessarily with CSS (though the author does have a few bones to pick in that regard), but rather with the fact that buggy browsers have such a large share of users, one is forced to learn not just the standard CSS rules, but all the buggy variations. The CSS that results from all of the necessary chicanery ends up being so cryptic as to be nearly impossible to maintain.
By contrast, it is arguable that rendering tables is one area where most browsers, even the buggiest ones, have a long history of getting things right (or at least consitent). So it's a shame not to be able to take advantage of this to avoid the world of pain that CSS layout leads to.
Therefore, ''Table Maker'' is an option for accomplishing the following:
1) Making it easy to define layout tables //without// putting a single <table> tag in your HTML
2) Using media inspection techniques, making it possible //not// to render the layout tables at all for certain media types, notably 'aural'
3) Finally, using browser detection, making it possible to have standard CSS layout for modern browsers, and use tables for older/buggy ones.
!FUNCTIONS
!!table_maker(aSpecs)
This function is a dispatch table for the other functions, {{{div_table()}}}, {{{ul_table()}}}, {{{ol_table()}}}, and {{{dl_table()}}}. You can call {{{table_maker()}}} with an array of table specs objects to create multiple tables with one function call.
(Side note: the 'a' in 'aSpecs' above is intended to indicate that the parameter is typically an array (though as noted below, it will also accept a single object (aka, a hash)).)
For example:
{{{
table_maker( [
{ "divid" : "tmdiv",
"rows" : [
[["tdtmc1",["tmc1"]],["tdtmc2",["tmc2"]],["tdtmc3",["tmc3"]]]
]
},
{ "ulid" : "tmul" },
{ "olid" : "tmol" },
{ "dlid" : "tmdl" }
] );
}}}
The above example will call {{{div_table()}}}, {{{ul_table()}}}, {{{ol_table()}}}, and {{{dl_table()}}}, passing each object it finds in the array.
The function knows which of these other functions to call based on the existence in each object of either {{{divid}}} (for {{{div_table()}}}), {{{ulid}}} (for {{{ul_table()}}}), {{{olid}}} (for {{{ol_table()}}}), or {{{dlid}}} (for {{{dl_table()}}}).
In addition to accepting arrays of table specs objects, {{{table_maker()}}} will also accept a single object and will dispatch the appropriate function to process it.
For example:
{{{
table_maker( {
"divid" : "tmdiv",
"rows" : [
[["tdtmc1",["tmc1"]],["tdtmc2",["tmc2"]],["tdtmc3",["tmc3"]]]
]
} ); // calls div_table()
table_maker( { "ulid" : "tmul" } ); // calls ul_table()
table_maker( { "olid" : "tmol" } ); // calls ol_table()
table_maker( { "dlid" : "tmdl" } ); // calls dl_table()
}}}
!!div_table(oSpecs)
This function converts a <div> tag into a <table> tag. Use the {{{divid}}} attribute to specify the id of the <div> tag that is to become the table.
Use the {{{rows}}} attribute to specify which child <div>s to place into table cells and how to layout those table cells. In addition, you can give id values for each table cell and any additional table cell attributes you may need.
Note that only the <div> tag whose id matches the {{{divid}}} value is changed from a <div> tag to something else (a <table> tag), and as a result, the original <div> tag will not exist in the DOM. All of the other <div> tags identified in the table specs are //moved// into table cells. So those <div> tags do still exist in the DOM. (Note also that any children of the {{{divid}}} <div> tag that are //not// moved into the table will be //lost// (will not exist in the DOM) when the {{{divid}}} <div> tag is removed in favor of the new <table> tag.)
Contrast this with how {{{ul_table()}}}, {{{ol_table()}}}, and {{{dl_table()}}} work (see below). The {{{ul_table()}}} function turns the <ul> tag into a <table> tag and the <li> tags into <td> tags. As a result, neither the <ul> or any of its child <li> tags will exist in the DOM. The same is true for the <ol> and <li> tags after running {{{ol_table()}}}, and for the <dl>, <dt>, and <dd> tags after running {{{dl_table()}}}. In the DOM all of these tags are changed to <table> and <td> tags (with <tbody> and <tr> tags inserted as needed).
All of this is pertinent when you author the CSS properties for the page.
The {{{div_table()}}} function expects a table specs object (hence the 'o' in 'oSpecs' above) with these required attributes:
* divid The id of the <div> tag to make into a table.
* rows An n-element array of specs for the table rows. Each row element is itself an array of cell specs. Each element in this cell specs array is an array with the following elements:
{{{
[0] id value to assign to the cell
[1] ids of <div>s to move into the cell (an n-element array)
[2] pairs of attribute names and values (an even numbered n-element array)
}}}
(Note, it bears studying the above description: there are a lot of arrays in the mix, and having an understanding of what role each set of arrays plays is important. I'll be trying to come up with better diagrams, but it's easy to overdo it and make things even less clear. So study each array in the example below to make sure you follow what's happening.)
For example, following are the specs for ''table 1'' (formatted like this diagram):
{{{
+---------------------------------------------+
| |
| header |
| |
+---------------------------------------------+
| | | |
| left | body | right |
| margin | | margin |
| +---------------------------+ |
| | | |
| | footer | |
| | | |
+---------------------------------------------+
var t1 = {
"divid" : "page",
"rows" : [
[
[ "td_header", [ "header" ], [ "colspan", 3 ] ]
],
[
[ "td_left_margin", [ "left_margin" ], [ "rowspan", 2 ] ],
[ "td_body", [ "body" ] ],
[ "td_right_margin", [ "right_margin" ], [ "rowspan", 2 ] ]
],
[ [ "td_footer", [ "footer" ] ] ]
]
};
}}}
See http://dev.galileo.usg.edu/table_numbers_docs.html for a complete description of all the available predefined table layout variables (like {{{t1}}} above).
!!ul_table(oSpecs)
This function converts a <ul> tag (or an <ol> tag, too) into a table. Each <li> child tag becomes a table cell. The attributes of the <ul> tag become those of the <table> tag. The attributes of each <li> tag become those of each <td> tag.
Depending on the value of the {{{grid}}} attribute (see below), there may be one or more cells per row. If {{{grid}}} specifies multiple columns, the {{{orien}}} (orientation) attribute may be {{{"v"}}} to orient the cells vertically. That is, instead of the following horizontal layout (the default):
{{{
1 2 3 4
5 6 7 8
}}}
if {{{orien}}} is {{{"v"}}} (rather than {{{"h"}}}), the layout would be:
{{{
1 3 5 7
2 4 6 8
}}}
The {{{bullet}}} attribute will cause a //bullet cell// to be added. The {{{usebull}}} attribute will cause a //usebull cell// to be added. The {{{prefix}}} attribute can give an id prefix which will cause id values to be added to cells whose corresponding <li> tags do not already have an id.
For example:
{{{
<ul id="ul11" class="ul-table" >
<li class="ul-li"><b>*</b> LI 1</li>
<li class="ul-li"><b>*</b> LI 2</li>
<li class="ul-li"><b>*</b> LI 3</li>
<li class="ul-li"><b>*</b> LI 4</li>
<li class="ul-li"><b>*</b> LI 5</li>
</ul>
<script type="text/javascript">
ul_table( {
"ulid" : "ul11" , // ids the <ul> tag
"grid" : 2 , // two columns
"orien" : "h" , // horizontal orientation
"bullet" : "o" , // simple one-character bullet
"usebull" : 0 // use the "<b>*</b>" element (child[0])
} );
</script>
}}}
The above should result in a layout like the following (note that diagrams in this document include borders around table cells for clarity; in practice, tables made from lists typically wouldn't have these borders):
<html> <TABLE class="ul-table" id="ul11"> <TBODY> <TR valign="top"> <TD class="ul-li-usebull"><B>*</B></TD> <TD class="ul-li-bullet">o</TD> <TD class="ul-li"> LI 1</TD> <TD class="ul-li-usebull"><B>*</B></TD> <TD class="ul-li-bullet">o</TD> <TD class="ul-li"> LI 2</TD> </TR> <TR valign="top"> <TD class="ul-li-usebull"><B>*</B></TD> <TD class="ul-li-bullet">o</TD> <TD class="ul-li"> LI 3</TD> <TD class="ul-li-usebull"><B>*</B></TD> <TD class="ul-li-bullet">o</TD> <TD class="ul-li"> LI 4</TD> </TR> <TR valign="top"> <TD class="ul-li-usebull"><B>*</B></TD> <TD class="ul-li-bullet">o</TD> <TD class="ul-li"> LI 5</TD> <td colspan="3"></td> </TR> </TBODY> </TABLE> </html>
Note that the //usebull// cells come first, followed by the //bullet// cells and then a cell with the rest of the original <li> contents.
A note about the class attribute: When an <li> tag has a class assigned, that value will be used to assign a class to each of the usebull and bullet cells, suffixed with {{{"-usebull"}}} and {{{"-bullet"}}}, respectively. So in the above example, the usebull cells will all have {{{class="ul-li-usebull"}}} and the bullet cells will all have {{{class="ul-li-bullet"}}}.
* ulid The id value of the <ul> tag (or <ol> tag) to make into a table. If the id is for an <ol> tag, the resulting table will not have number cells. If you want numbering, use {{{ol_table()}}}, which incidentally will also accept a <ul> tag and provide numbering for it (see below).
* grid The number of columns to put in the new table (default is one column). The {{{grid}}} value should be an integer greater than 1.
* orien The orientation of the grid layout (default is {{{"h"}}}). The {{{orien}}} value should be {{{"h"}}} for horizontal or {{{"v"}}} for vertical. (See discussion of {{{orien}}} above.)
* bullet Character or characters to use as a bullet for each item. These characters will be placed in a separate cell, a //bullet cell//. The {{{bullet}}} value can be any displayable characters or html tags, or entities like {{{"•"}}}.
* usebull Index of the <li> tag's child element to use as a bullet. This element will be moved from the <li> tag cell to its own separate cell, a //usebull cell//. The {{{usebull}}} value should be an integer equal to or greater than 0.
Note that this index value is an integer that refers to a child of the <li> tag. If you want the {{{usebull}}} cell to contain multiple elements from the <li>, simply wrap those elements with an appropriate tag, e.g., <span> or <div>. The {{{usebull}}} value should then refer to the position of this (<span> or <div>) child tag.
* prefix A prefix to use to assign id values to cells. The {{{prefix}}} value will be ignored if the <li> tag has an id already. If used, a sequence number will be added to {{{prefix}}} to form the id of the cell.
In addition, //bullet cells// will be given this id suffixed with {{{"-bullet"}}}, and //usebull cells// will be given this id suffixed with {{{"-usebull"}}}.
If the <li> tag has an id already, this id -- suffixed with {{{"-bullet"}}} or {{{"-usebull"}}} -- will be used for bullet and usebull cells.
!!ol_table(oSpecs)
This function converts an <ol> tag (or a <ul> tag) into a table. Each <li> child tag becomes a table cell. The attributes of the <ol> tag become those of the <table> tag. The attributes of each <li> tag become those of each <td> tag.
Depending on the value of the {{{grid}}} attribute, there may be one or more cells per row. If {{{grid}}} specifies multiple columns, the {{{orien}}} (orientation) attribute may be {{{"v"}}} to orient the cells vertically. That is, instead of the following horizontal layout (the default):
{{{
1 2 3 4
5 6 7 8
}}}
if {{{orien}}} is {{{"v"}}} (rather than {{{"h"}}}), the layout would be:
{{{
1 3 5 7
2 4 6 8
}}}
The {{{bullet}}} attribute will cause a //bullet cell// to be added. The {{{usebull}}} attribute will cause a //usebull cell// to be added. The {{{prefix}}} attribute can give an id prefix which will cause id values to be added to cells whose corresponding <li> tags do not already have an id.
The {{{number}}} attribute will cause a //number cell// to be added. Numbering is on by default.
For example:
{{{
<ol id="ol11" class="ol-table" >
<li class="ol-li"><b>*</b> LI 1</li>
<li class="ol-li"><b>*</b> LI 2</li>
<li class="ol-li"><b>*</b> LI 3</li>
<li class="ol-li"><b>*</b> LI 4</li>
<li class="ol-li"><b>*</b> LI 5</li>
</ol>
<script type="text/javascript">
ol_table( {
"olid" : "ol11" , // ids the <ol> tag
"grid" : 2 , // two columns
"orien" : "v" , // vertical orientation
"bullet" : "o" , // simple one-character bullet
"number" : "arabic" , // (this default may be left off)
"usebull" : 0 // use the "<b>*</b>" element (child[0])
} );
</script>
}}}
The above should result in a layout like the following:
<html> <TABLE class="ol-table" id="ol11"> <TBODY> <TR valign="top"> <TD class="ol-li-usebull"><B>*</B></TD> <TD class="ol-li-bullet">o</TD> <TD class="ol-li-number">1.</TD> <TD class="ol-li"> LI 1</TD> <TD class="ol-li-usebull"><B>*</B></TD> <TD class="ol-li-bullet">o</TD> <TD class="ol-li-number">4.</TD> <TD class="ol-li"> LI 4</TD> </TR> <TR valign="top"> <TD class="ol-li-usebull"><B>*</B></TD> <TD class="ol-li-bullet">o</TD> <TD class="ol-li-number">2.</TD> <TD class="ol-li"> LI 2</TD> <TD class="ol-li-usebull"><B>*</B></TD> <TD class="ol-li-bullet">o</TD> <TD class="ol-li-number">5.</TD> <TD class="ol-li"> LI 5</TD> </TR> <TR valign="top"> <TD class="ol-li-usebull"><B>*</B></TD> <TD class="ol-li-bullet">o</TD> <TD class="ol-li-number">3.</TD> <TD class="ol-li"> LI 3</TD> <td colspan="4"></td> </TR> </TBODY> </TABLE> </html>
Note that the //usebull// cells come first, followed by the //bullet// cells, followed in turn by the //number// cells, and then a cell with the rest of the original <li> contents.
A note about the class attribute: When an <li> tag has a class assigned, that value will be used to assign a class to each of the usebull, bullet, and number cells, suffixed with {{{"-usebull"}}}, {{{"-bullet"}}}, and {{{"-number"}}} respectively. So in the above example, the usebull cells will all have {{{class="ol-li-usebull"}}}, the bullet cells will all have {{{class="ol-li-bullet"}}}, and the number cells will all have {{{class="ol-li-number"}}}.
* olid The id value of the <ol> tag (or <ul> tag) to make into a table. If the id is for an <ul> tag, the resulting table will have number cells by default. Also see {{{ul_table()}}} above.
* grid The number of columns to put in the new table (default is one column). The {{{grid}}} value should be an integer greater than 1.
* orien The orientation of the grid layout (default is {{{"h"}}}). The {{{orien}}} value should be {{{"h"}}} for horizontal or {{{"v"}}} for vertical.
* bullet Character or characters to use as a bullet for each item. These characters will be placed in a separate cell, a //bullet cell//. The {{{bullet}}} value can be any displayable characters or html tags, or entities like {{{"•"}}}.
* usebull Index of the <li> tag's child element to use as a bullet. This element will be moved from the <li> tag cell to its own separate cell, a //usebull cell//. The {{{usebull}}} value should be an integer equal to or greater than 0.
Note that this index value is an integer that refers to a child of the <li> tag. If you want the {{{usebull}}} cell to contain multiple elements from the <li>, simply wrap those elements with an appropriate tag, e.g., <span> or <div>. The {{{usebull}}} value should then refer to the position of this (<span> or <div>) child tag.
* prefix A prefix to use to assign id values to cells. The {{{prefix}}} value will be ignored if the <li> has an id already. If used, a sequence number will be added to {{{prefix}}} to form the id of the cell.
In addition, //bullet cells// will be given this id suffixed with {{{"-bullet"}}}, //usebull cells// will be given this id suffixed with {{{"-usebull"}}}, and //number cells// will be given this id suffixed with {{{"-number"}}}.
If the <li> tag has an id already, this id -- suffixed with {{{"-bullet"}}}, {{{"-usebull"}}}, or {{{"-number"}}} -- will be used for bullet and usebull cells.
* number Type of numbering to use (default is {{{'arabic'}}}). The {{{number}}} value should be one of the following {{{'arabic'}}}, {{{'upper-roman'}}}, {{{'upper-alpha'}}}, {{{'lower-roman'}}}, {{{'lower-alpha'}}}, or {{{'none'}}} (to disable numbering). See the {{{'NUMBERING'}}} section below for a discussion of the different types of numbering.
!!dl_table(oSpecs)
This function converts a <dl> tag into a table. Each <dt> and <dd> child tag becomes a table cell. The attributes of the <dl> tag become those of the <table> tag. The attributes of each <dt> and <dd> tag become those of each corresponding <td> tag.
The {{{bullet}}} attribute will cause a //bullet cell// to be added. The {{{usebull}}} attribute will cause a //usebull cell// to be added. The {{{prefix}}} attribute can give an id prefix which will cause id values to be added to cells whose corresponding <li> tags do not already have an id.
The {{{number}}} attribute will cause a //number cell// to be added. Numbering is off by default. Yes, this does imply that numbering can be added for <dl> lists, just as {{{ol_table}}} can supply numbering for <ul> lists.
For example:
{{{
<dl id="dl11" class="dl-table" >
<dt class="dl-dt"><b>*</b> DT 1</dt>
<dd class="dl-dd">DD 1.1</dd>
<dt class="dl-dt"><b>*</b> DT 2</dt>
<dd class="dl-dd">DD 2.1</dd>
<dd class="dl-dd">DD 2.2</dd>
<dt class="dl-dt"><b>*</b> DT 3</dt>
<dd class="dl-dd">DD 3.1</dd>
<dt class="dl-dt"><b>*</b> DT 4</dt>
<dt class="dl-dt"><b>*</b> DT 5</dt>
<dd class="dl-dd">DD 5.1</dd>
</dl>
<script type="text/javascript">
dl_table( {
"dlid" : "dl11" , // ids the <dl> tag
"bullet" : "o" , // simple character bullet
"usebull" : 0 , // use <b>*</b>
"number" : "arabic" // needed for numbering (off by default)
} );
</script>
}}}
The above should result in a layout like the following:
<html> <TABLE class="dl-table" id="dl11"> <TBODY> <TR valign="top"> <TD class="dl-dt-usebull"><B>*</B></TD> <TD class="dl-dt-bullet">o</TD> <TD class="dl-dt-number">1.</TD> <TD class="dl-dt"> DT 1</TD> <TD class="dl-dd">DD 1.1</TD> </TR> <TR valign="top"> <TD rowspan="2" class="dl-dt-usebull"><B>*</B></TD> <TD rowspan="2" class="dl-dt-bullet">o</TD> <TD rowspan="2" class="dl-dt-number">2.</TD> <TD rowspan="2" class="dl-dt"> DT 2</TD> <TD class="dl-dd">DD 2.1</TD> </TR> <TR valign="top"> <TD class="dl-dd">DD 2.2</TD> </TR> <TR valign="top"> <TD class="dl-dt-usebull"><B>*</B></TD> <TD class="dl-dt-bullet">o</TD> <TD class="dl-dt-number">3.</TD> <TD class="dl-dt"> DT 3</TD> <TD class="dl-dd">DD 3.1</TD> </TR> <TR valign="top"> <TD class="dl-dt-usebull"><B>*</B></TD> <TD class="dl-dt-bullet">o</TD> <TD class="dl-dt-number">4.</TD> <TD class="dl-dt"> DT 4</TD> </TR> <TR valign="top"> <TD class="dl-dt-usebull"><B>*</B></TD> <TD class="dl-dt-bullet">o</TD> <TD class="dl-dt-number">5.</TD> <TD class="dl-dt"> DT 5</TD> <TD class="dl-dd">DD 5.1</TD> </TR> </TBODY> </TABLE> </html>
Note that the //usebull// cells come first, followed by the //bullet// cells, followed in turn by the //number// cells, and then a cell with the rest of the original <dt> contents. Finally, if there are any <dd> tags, they are associated with the immediately preceding <dt> tag.
A note about the class attribute: When an <dt> tag has a class assigned, that value will be used to assign a class to each of the usebull, bullet, and number cells, suffixed with {{{"-usebull"}}}, {{{"-bullet"}}}, and {{{"-number"}}} respectively. So in the above example, the usebull cells will all have {{{class="dl-dt-usebull"}}}, the bullet cells will all have {{{class="dl-dt-bullet"}}}, and the number cells will all have {{{class="dl-dt-number"}}}.
Note finally that bullets and numbers are always associated with <dt> tags, never with <dd> tags. If a <dd> tag does not have a <dt> tag preceding it, it is an error, and {{{dl_table()}}} will fail.
* dlid The id value of the <dl> tag to make into a table.
* bullet Character or characters to use as a bullet for each item. These characters will be placed in a separate cell, a //bullet cell//. The {{{bullet}}} value can be any displayable characters or html tags, or entities like {{{"•"}}}.
* usebull Index of the <dt> (not <dd>) tag's child element to use as a bullet. This element will be moved from the <dt> tag to its own separate cell, a //usebull cell//. The {{{usebull}}} value should be an integer equal to or greater than 0.
Note that this index value is an integer that refers to a child of the <dt> tag. If you want the {{{usebull}}} cell to contain multiple elements from the <dt>, simply wrap those elements with an appropriate tag, e.g., <span> or <div>. The {{{usebull}}} value should then refer to the position of this (<span> or <div>) child tag.
* prefix A prefix to use to assign id values to cells. The {{{prefix}}} value will be ignored if the <dt> or <dd> has an id already. If used, a sequence number will be added to {{{prefix}}} to form the id of the cell. In addition, //bullet cells// will be given this id suffixed with {{{"-bullet"}}}, //usebull cells// will be given this id suffixed with {{{"-usebull"}}}, and //number cells// will be given this id suffixed with {{{"-number"}}}. If a <dt> tag has an id already, this id -- suffixed with {{{"-bullet"}}}, {{{"-usebull"}}}, or {{{"-number"}}} -- will be used for bullet and usebull cells.
* number Type of numbering to use (default is no numbering). The {{{number}}} value should be one of the following {{{'arabic'}}}, {{{'upper-roman'}}}, {{{'upper-alpha'}}}, {{{'lower-roman'}}}, {{{'lower-alpha'}}}, or {{{'none'}}} (for no numbering, the default). See the {{{'NUMBERING'}}} section below for a discussion of the different types of numbering.
!NUMBERING
As described above, {{{ol_table()}}} and {{{dl_table()}}} provide a numbering option. In {{{ol_table()}}}, this option is on by default, using the {{{'arabic'}}} type of numbering. In {{{dl_table()}}}, numbering is off by default.
The types of numbering are as follows (note that all numbers are suffixed with a period {{{"."}}}):
* arabic Numbering uses arabic digits:
{{{
1. 2. 3. 4. 5. ...
}}}
* upper-roman Numbering uses upper-case Roman numerals:
{{{
I. II. III. IV. V. ...
}}}
* lower-roman Numbering uses lower-case Roman numerals:
{{{
i. ii. iii. iv. v. ...
}}}
* upper-alpha Numbering uses upper-case alphabetic characters (A-Z):
{{{
A. B. C. D. E. ...
}}}
For numbers above 26, the characters are doubled:
{{{
AA. BB. CC. DD. EE. ...
}}}
For numbers above 52, the characters are tripled:
{{{
AAA. BBB. CCC. DDD. EEE. ...
}}}
And so forth for each multiple of 26.
* lower-alpha Numbering uses lower-case alphabetic characters (a-z):
{{{
a. b. c. d. e. ...
}}}
For numbers above 26, the characters are doubled:
{{{
aa. bb. cc. dd. ee. ...
}}}
For numbers above 52, the characters are tripled:
{{{
aaa. bbb. ccc. ddd. eee. ...
}}}
And so forth for each multiple of 26.
!!The {{{value}}} attribute
Normally, <li> tags in <ol> lists may set a {{{value}}} attribute that assigns a number to that item.
Subsequent items that don't have {{{value}}} attributes will be numbered from there, e.g.,
{{{
<ol>
<li >Item</li> <!-- 1 -->
<li >Item</li> <!-- 2 -->
<li value="10">Item</li> <!-- 10 -->
<li >Item</li> <!-- 11 -->
<li >Item</li> <!-- 12 -->
</ol>
}}}
<html> <ol> <li >Item</li> <!-- 1 --> <li >Item</li> <!-- 2 --> <li value="10">Item</li> <!-- 10 --> <li >Item</li> <!-- 11 --> <li >Item</li> <!-- 12 --> </ol> </html>
The ''Table Maker'' functions that offer a numbering option ({{{ol_table()}}} and {{{dl_table()}}}) will respond to a {{{value}}} attribute in an <li> or <dt> tag, and alter the numbering accordingly. (This is recognized in <li> tags for either <ol> or <ul> tags that are converted using {{{ol_table()}}}.)
For example:
{{{
<ol id="ol12">
<li >LI 1</li>
<li >LI 2</li>
<li value="10">LI 3</li>
<li >LI 4</li>
<li >LI 5</li>
</ol>
<script type="text/javascript">
ol_table( {
"olid" : "ol12" ,
"grid" : 2 ,
"bullet" : "*"
} );
</script>
}}}
The above should result in a layout like the following:
<html> <TABLE id="ol12"> <TBODY> <TR valign="top"> <TD>*</TD> <TD>1.</TD> <TD>LI 1</TD> <TD>*</TD> <TD>2.</TD> <TD>LI 2</TD> </TR> <TR valign="top"> <TD>*</TD> <TD>10.</TD> <TD value="10">LI 3</TD> <TD>*</TD> <TD>11.</TD> <TD>LI 4</TD> </TR> <TR valign="top"> <TD>*</TD> <TD>12.</TD> <TD>LI 5</TD> <td colspan="3"></td> </TR> </TBODY> </TABLE> </html>
!Main Menu Tags
This wiki uses the following tags to indicate to which of the main areas a page pertains. Note that these main menu tags are all lowercase to differentiate them from their corresponding main pages.
* [[introduction]]
* [[tutorials]]
* [[functional]]
* [[technical]]
* [[programming]]
* [[configuration]]
* [[installation]]
* [[documentation]]
See also: SiteMap
!Journaling/Change Log Tags
* [[Brad Baxter]]
!Other Tags
* [[api]]
* [[tag]]
* [[lewcidExtension]]
* [[systemConfig]]
!Technical Details of the Mapping Tool
* The GKR Mapping Tool is a Perl CGI program.
* The pages are built dynamically from page templates, filled in based on the values of the CGI parameters passed to the program.
* Data is stored in files (i.e., not, e.g., in an SQL database), and may be updated online via the mapping tool, or updated "behind the scenes" via a unix (solaris, linux, etc.) shell client.
* When files are updated online via the mapping tool, previous versions are saved as backup files. These files are full copies, i.e., they are not merely incremental diffs. Since all of the files are fairly small, and there will likely not be a large number of member institutions, this backup strategy should be maintainable for some time.
The pages linked to below cover more technical details.
* [[Perl Version]]
* [[Perl CGI]]
* [[Program Data Files]]
* [[Program Directory Structure]]
* [[User Files]]
* [[Session Files]]
* [[Indented Tree Structures]]
* [[Templating System]]
* [[Perl Modules]]
* [[Local Javascript Libraries]]
* [[Local CSS Files]]
Click TechnicalDetails to display all of the above steps in one page.
<<tiddler [[Perl Version]]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Perl CGI]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Program Data Files]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Program Directory Structure]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[User Files]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Session Files]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Indented Tree Structures]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Templating System]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Perl Modules]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Local Javascript Libraries]]>>
{{center{
[img[featurebottom.png]]}}}
<<tiddler [[Local CSS Files]]>>
!Template Placeholders in Config::Ini::Expanded
The configuration files for the GKR mapping tool program define page templates for every page in the interface. The templates (and the ini files themselves) contain many "template placeholders". These placeholders are filled in just before a page is displayed using values defined in the ini files and by the progam.
Below is a brief list of the main placeholders that you may see in the ini files and page templates. There are many more than these; a full discussion is available in [[Config::Ini::Expanded|Config::Ini::Expanded Page 2]].
{{{
{INCLUDE:ini_file_path}
{FILE:file_path}
{INI:section:name}
{VAR:varname}
{LOOP:loopname}
{LVAR:lvarname}
{END_LOOP:loopname}
}}}
!{INCLUDE:ini_file_path}
This placeholder is replaced with the contents of the file denoted by "ini_file_path", e.g., near the end of the ''program.ini'' file is this line:
{{{
{INCLUDE:members.ini}
}}}
When the program reads the ''program.ini'' file (using Config::Ini::Expanded::new()), the contents of the ''members.ini'' file replaces the above line. This effectively merges (includes) the members file into the program file.
!{FILE:file_path}
This placeholder is replaced with the contents of the file denoted by "file_path". (This is a different operation than the {INCLUDE:...} placeholder.) For example, in ''program.ini'' are the following lines:
{{{
#---------------------------------------------------------------------
# construct the view_tree page
[view_tree]
title = View Communities/Collections Mappings
help = {FILE:view_tree_help}
blurb = {FILE:view_tree_blurb}
page = {FILE:view_tree_page}
}}}
This section, which is defining the contents of the ''view_tree'' page, contains three {FILE:...} placeholders. When the program wants one of those values, e.g., when it wants the value for the ''help'' setting, the corresponding file (e.g., ''view_tree_help'') is read and it's contents replace the {FILE:...} placeholder.
!{INI:section:name}
This placeholder is replaced with the value of the ''name'' setting in the ''section'' section, e.g.,
{{{
#---------------------------------------------------------------------
[images]
path = /gkr_mapping/images
#---------------------------------------------------------------------
# general program section with values used globally
[program]
gkr = Georgia Knowledge Repository
title = {INI:program:gkr} Mapping Tool
logo = {INI:images:path}/gkr_logo.gif
}}}
The above snippet from ''program.ini'' contains two {INI:...} placeholders. The value of the ''gkr'' setting in the ''program'' section is "Georgia Knowledge Repository", so the placeholder in the value of the ''title'' setting, "{INI:program:gkr} Mapping Tool", will become "Georgia Knowledge Repository Mapping Tool" when the program requests the ''title'' value. Similarly, the value of the ''path'' setting in the ''images'' section is "/gkr_mapping/images", so the placeholder in the value of the ''logo'' setting, "{INI:images:path}/gkr_logo.gif", will become "/gkr_mapping/images/gkr_logo.gif" when the program requests the ''logo'' value.
!{VAR:varname}
This placeholder is replaced with the value defined by the program for the ''varname'' setting. The program may set these values for any purpose, e.g., the ''map_node_blurb'' file contains the following snippet:
{{{
<p>
This page is prepared for mapping the {VAR:name}
<b><i>{VAR:collection}</i></b> collection.
</p>
}}}
The program (somewhat arbitrarily) sets ''name'' to the institution name and ''collection'' to the name of the collection in question, so the above snippet may come out looking like the following:
<<<
This page is prepared for mapping the Valdosta State University //''AAS Faculty Research and Teaching Materials''// collection.
<<<
!{LOOP:loopname}{LVAR:lvarname}{~END_LOOP:loopname}
This placeholder is replaced with values defined for the ''loopname'' loop. Setting up values for loops is much more complex than with the previous placeholders. What follows is a simple example. Again, see [[Config::Ini::Expanded|Config::Ini::Expanded Page 2]] for a more thorough discussion of loops and of the additional placeholders not covered here.
For this simple example, let's say that we set up a loop like this:
{{{
$ini->set_loop( members => [
{ code => 'gatech', quick => 'GaTech', name => 'Georgia Institute of Technology' },
{ code => 'uga', quick => 'UGA', name => 'University of Georgia' },
{ code => 'valdosta', quick => 'Valdosta', name => 'Valdosta State University' },
] );
}}}
And then we define a template like this:
{{{
<ul>
{LOOP:members}<li>{LVAR:quick}: {LVAR:name}</li>
{END_LOOP:members}</ul>
</ul>
}}}
Then the result of filling in that template should be the following:
{{{
<ul>
<li>GaTech: Georgia Institute of Technology</li>
<li>UGA: University of Georgia</li>
<li>Valdosta: Valdosta State University</li>
</ul>
}}}
Notes:
* The ''members'' in {LOOP:members} refers to the ''members'' in "$ini->set_loop( members => ["
* The ''quick'' in {LVAR:quick} and the ''name'' in {LVAR:name} refer to the ''quick'' and ''name'' in "{ code => 'gatech', quick => '~GaTech', name => 'Georgia Institute of Technology' },"
* The structure that set_loop() expects is an array of hashes, and the order of the hashes in the array defines the order of the resulting loop substitutions
* In the hashes is an "LVAR" value, ''code'', that isn't asked for in the template. This is fine; it's not required that every value be used.
!Templating System
The gkr_mapping program uses its configuration file to define page templates for every page displayed in the interface. The [[Config::Ini::Expanded]] module is both a configuration file processor and a templating system. It manages filling in all of the [[template placeholders|TemplatePlaceholders]] that are used extensively in the page templates and other data files.
Examples include:
{{{
{VAR:cookie}
The program will set a Set-Cookie: header which will be filled in here.
{INI:program:page_doctype_header}
This 'page_doctype_header' value is gotten from the [program] section of the ini file.
{INI:home:links}
Similarly, this 'links' value is gotten from the [home] section of the ini file.
The HTML snippet below demonstrates how to mark up a loop.
<ul>
{LOOP:members_loop}<li><a href="{INI:program:url}?member={LVAR:code}">
{LVAR:name} ({LVAR:code})</a>
<a href="{INI:program:rss_url}/{LVAR:code}.rdf">{INI:program:rss_icon}</a></li>
{END_LOOP:members_loop}</ul>
}}}
The available template placeholders are outlined in [[TemplatePlaceholders]] and are discussed in more depth in [[Config::Ini::Expanded Page 2]].
!Testing various constructs
<<forEachTiddler where 'tiddler.tags.contains("tag")'
write '"[["+ tiddler.title+ "]]" + "<<tiddler [["+tiddler.title+"]]$))\n"'
>>
<<forEachTiddler where 'tiddler.tags.contains("tag")'
sortBy 'tiddler.title.toUpperCase()'
write '" [["+tiddler.title+" ]] \"view ["+tiddler.title+"]\" [["+tiddler.title+"]] "'
begin '"<<tabs txtMyAutoTab "'
end '">"+">"'
none '"//No tiddler tagged with \"tag\"//"'
>>
<<tiddler ListTaggedTiddlers with: "tag">>
!When/If a user ... they ...
I know that "when a user ... they" is bad grammar. But to avoid reconstructing my thoughts to say "when users ... they" to get around saying "when a user ... he or she" (to avoid being accused of gender bias by saying "when a user ... he"), I'm going to treat this wiki as an informal setting and say "when a user ... they" to mean "when a user ... he or she".
/***
|Name|ToggleSideBarMacro|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#ToggleSideBarMacro|
|Version|1.0|
|Requires|~TW2.x|
!Description:
Provides a button for toggling visibility of the SideBar. You can choose whether the SideBar should initially be hidden or displayed.
!Demo
<<toggleSideBar "Toggle Sidebar">>
!Usage:
{{{<<toggleSideBar>>}}} <<toggleSideBar>>
additional options:
{{{<<toggleSideBar label tooltip show/hide>>}}} where:
label = custom label for the button,
tooltip = custom tooltip for the button,
show/hide = use one or the other, determines whether the sidebar is shown at first or not.
(default is to show the sidebar)
You can add it to your tiddler toolbar, your MainMenu, or where you like really.
If you are using a horizontal MainMenu and want the button to be right aligned, put the following in your StyleSheet:
{{{ .HideSideBarButton {float:right;} }}}
!History
*23-07-06: version 1.0: completely rewritten, now works with custom stylesheets too, and easier to customize start behaviour.
*20-07-06: version 0.11
*27-04-06: version 0.1: working.
!Code
***/
//{{{
config.macros.toggleSideBar={};
config.macros.toggleSideBar.settings={
styleHide : "#sidebar { display: none;}\n"+"#contentWrapper #displayArea { margin-right: 1em;}\n"+"",
styleShow : " ",
arrow1: "«",
arrow2: "»"
};
config.macros.toggleSideBar.handler=function (place,macroName,params,wikifier,paramString,tiddler)
{
var tooltip= params[1]||'toggle sidebar';
var mode = (params[2] && params[2]=="hide")? "hide":"show";
var arrow = (mode == "hide")? this.settings.arrow1:this.settings.arrow2;
var label= (params[0]&¶ms[0]!='.')?params[0]+" "+arrow:arrow;
var theBtn = createTiddlyButton(place,label,tooltip,this.onToggleSideBar,"button HideSideBarButton");
if (mode == "hide")
{
(document.getElementById("sidebar")).setAttribute("toggle","hide");
setStylesheet(this.settings.styleHide,"ToggleSideBarStyles");
}
};
config.macros.toggleSideBar.onToggleSideBar = function(){
var sidebar = document.getElementById("sidebar");
var settings = config.macros.toggleSideBar.settings;
if (sidebar.getAttribute("toggle")=='hide')
{
setStylesheet(settings.styleShow,"ToggleSideBarStyles");
sidebar.setAttribute("toggle","show");
this.firstChild.data= (this.firstChild.data).replace(settings.arrow1,settings.arrow2);
}
else
{
setStylesheet(settings.styleHide,"ToggleSideBarStyles");
sidebar.setAttribute("toggle","hide");
this.firstChild.data= (this.firstChild.data).replace(settings.arrow2,settings.arrow1);
}
return false;
}
setStylesheet(".HideSideBarButton .button {font-weight:bold; padding: 0 5px;}\n","ToggleSideBarButtonStyles");
//}}}
!NAME
Tree::Indented - a module to parse indented text into a simple tree structure
!VERSION
VERSION: 1.01
!EXPORTS
Nothing is exported by default. The following may be exported individually; all three may be exported using the {{{:all}}} tag:
{{{
- parse_indented_text
- traverse_tree_simple
- traverse_tree
}}}
Examples:
{{{
use Tree::Indented qw( parse_indented_text traverse_tree );
use Tree::Indented qw( :all );
}}}
!SYNOPSIS
{{{
use Tree::Indented qw( :all );
# indented text
my $text = <<'__';
Fagaceae
Fagus
Fagus crenata - Japanese Beech
Fagus engleriana - Chinese Beech
Fagus grandifolia - American Beech
Pinaceae
Pinus
Pinus clausa - Sand Pine
Pinus cubensis - Cuban Pine
Pinus elliottii - Slash Pine
__
# create a tree structure (nested arrays)
my $tree = parse_indented_text( text => $text );
# this should print the same indented text as above
print traverse_tree_simple( tree => $tree );
# this should print nested <ul> lists
print traverse_tree(
tree => $tree,
# defines begin/end tags for groups and items:
tags => {
group => { # $_[0] is the indentation string
begin => sub { "\n$_[0]<ul>\n" },
end => sub { "$_[0]</ul>" },
},
item => {
begin => sub { "$_[0]<li>" },
end => sub { "</li>\n" },
},
},
);
}}}
!DESCRIPTION
This module is designed to provide a simple way of dealing with indented text as a tree structure, where a more-indented line of text is considered a child of the less-indented line above it.
!SUBROUTINES
Descriptions and parameters for the exportable subroutines are detailed below.
Note that all parameters must be passed as named parameters (hash references are not supported in this version), e.g.,
{{{
my $tree = parse_indented_text( text => $text, char => "\t", num => 1 );
}}}
!!parse_indented_text()
This routine creates a tree structure from indented text. It returns an array reference that will normally include many nested arrays.
!!!Parameters:
!!!!text
A string or scalar reference to a string that contains indented text.
{{{
my $tree = parse_indented_text( text => $text );
}}}
Note that in the indented text that is passed to this routine, the newline character {{{"\n"}}} is special: it always marks the end of a line of text.
!!!!char
The indentation character (typically a space or a tab) -- default is one space. This character x the {{{num}}} parameter will be the indentation string for each level.
{{{
my $tree = parse_indented_text( text => $text, char => "\t", num => 1 );
}}}
You may set {{{$Tree::Indented::Char}}} to change the default.
{{{
$Tree::Indented::Char = "\t";
my $tree = parse_indented_text( text => $text, num => 1 );
}}}
!!!!num
The number of indentation characters that make up the indentation string per level of the structure -- default is 4, meaning, normally, four spaces per level.
{{{
my $tree = parse_indented_text( text => $text, num => 2 ); # two spaces
}}}
You may set {{{$Tree::Indented::Num}}} to change the default.
{{{
$Tree::Indented::Num = 2;
my $tree = parse_indented_text( text => $text );
}}}
!!!!strict
A boolean value that determines if badly indented text will cause the subroutine to die. The default is false, i.e., badly indented text is accepted;
If a line begins with more indentation characters than expected, and {{{strict}}} is false, the extra indentation characters are simply considered part of the value of that line. But if {{{strict}}} is true, the subroutine will die.
{{{
my $tree = parse_indented_text( text => $text, strict => 1 );
}}}
Be aware that the default value of false for {{{strict}}} means that the routine will not die even for pathologically badly indented text, but the resulting tree in those cases may not be what you expect.
You may set {{{$Tree::Indented::Strict}}} to change the default.
{{{
$Tree::Indented::Strict = 1;
my $tree = parse_indented_text( text => $text );
}}}
!!traverse_tree_simple()
This routine traverses a tree to form indented text. It should be considered the opposite of parse_indented_text(), i.e., the indented text that traverse_tree_simple() returns should be parsable by parse_indented_text() (with proper values for {{{char}}}, {{{num}}}, and {{{tab}}}).
Additionally, the code in this routine may be used as a template for traversing a tree if neither traverse_tree_simple() nor traverse_tree() meets your needs. This implies that the structure of the tree used by this module is not expected to change, and it's not.
!!!Parameters:
!!!!tree
This parameter must be a tree structure generated by parse_indented_text().
{{{
my $tree = parse_indented_text( text => $text );
my $new_text = traverse_tree_simple( tree => $tree );
}}}
!!!!tab
This parameter is the string used to indent each level of text. The default is four spaces, which corresponds to the default {{{char}}} and {{{num}}} parameters in parse_indented_text().
You may ask why it's {{{char}}} and {{{num}}} for one routine and {{{tab}}} for the other. Good question. :-) Short answer: the code is simpler this way.
{{{
my $new_text = traverse_tree_simple( tree => $tree, tab => "\t" );
}}}
You may set {{{$Tree::Indented::Char}}} and {{{$Tree::Indented::Num}}} to change the default for {{{tab}}}.
{{{
$Tree::Indented::Char = "\t";
$Tree::Indented::Num = 1;
my $new_text = traverse_tree_simple( tree => $tree );
}}}
!!!!level
This parameter is an integer that indicates the level we're at. You should not pass this -- it's used by traverse_tree_simple() itself as it recurses through the tree.
!!traverse_tree()
This routine traverses a tree to form more complex output. It allows you to define {{{begin}}} and {{{end}}} tags for each {{{item}}} and each {{{group}}} (a "group" is a parent plus its children), and to define {{{change}}} and {{{display}}} routines to be called for each item.
It also passes along all of an item's parents as it recurses through the tree.
By the way, to make traverse_tree() produce the same output as traverse_tree_simple(), pass the following {{{display}}} value:
{{{
my $new_text = traverse_tree(
tree => $tree,
display => sub { "$_[1]$_[0]\n" }
);
}}}
!!!Parameters:
!!!!tree, tab, level
These are the same parameters described above for traverse_tree_simple(), and the same rule applies for {{{level}}} -- you shouldn't pass it.
!!!!tags
This parameter is a hash reference. The hash should have two keys, {{{group}}} and {{{item}}}, and each of their values should be a hash with two keys, {{{begin}}} and {{{end}}}. The values for {{{begin}}} and {{{end}}} should be subroutine references (most likely anonymous subs) that are callbacks.
As the routine recurses through the tree, each item and each group will be surrounded by the {{{begin}}} and {{{end}}} subroutines' return values. These subroutines will expect to get the current indentation string ({{{tab}}} x {{{level}}}) and the current parents.
The example in the SYNOPSIS shows an example {{{tags}}} hash that outputs a tree as a set of nested <ul> lists. Below is a more complex version that also adds information about parents as {{{title}}} attributes.
{{{
print traverse_tree(
tree => $tree,
tags => {
group => {
begin => sub {
my( $indent, $parents ) = @_;
my @p = @$parents;
my $p = @p? qq' title="Parents: @p"': '';
"\n$indent<ul$p>\n";
},
end => sub { "$_[0]</ul>" },
},
item => {
begin => sub {
my( $indent, $parents ) = @_;
my $p = $parents->[-1]||'';
$p = qq' title="Parent: $p"' if $p;
"$indent<li$p>";
},
end => sub { "</li>\n" },
},
},
);
}}}
!!!!change
This parameter is also a callback, i.e., a subroutine reference, and it expects to get the item value, the indentation string, and the current parents.
The item value will be //replaced// with the return value of this callback (and will then be passed to the {{{display}}} callback if present).
The following example removes the immediate parent's value from the beginning of each item.
{{{
print traverse_tree(
tree => $tree,
display => sub { "$_[1]$_[0]\n" },
change => sub {
my( $item, $indent, $parents ) = @_;
if( my $p = $parents->[-1] ) {
$item =~ s/\Q$p //;
}
$item; # don't forget to return this
}
);
}}}
Output using the text in the SYNOPSIS:
{{{
Fagaceae
Fagus
crenata - Japanese Beech
engleriana - Chinese Beech
grandifolia - American Beech
Pinaceae
Pinus
clausa - Sand Pine
cubensis - Cuban Pine
elliottii - Slash Pine
}}}
!!!!display
This parameter is also a callback, i.e., a subroutine reference, and it also expects to get the item value, the indentation string, and the current parents.
The return value of this callback will be added (instead of the item value) to the string that traverse_tree_simple() is building as its return value.
The following example produces the same output as above.
{{{
print traverse_tree(
tree => $tree,
display => sub {
my( $item, $indent, $parents ) = @_;
if( my $p = $parents->[-1] ) {
$item =~ s/\Q$p //;
}
"$indent$item\n";
}
);
}}}
You might wonder: What's the difference between {{{change}}} and {{{display}}}, since they both affect the value added to the overall return value? The answer is: parents. When you supply a {{{change}}} callback, the item value is changed not only in the overall return value, but also in the list of parents associated with each child. The {{{display}}} callback will not affect the list of parents.
!!!!parents
This parameter is an array reference to a list of the current item's parents (and grandparents, etc.) This list contains the actual item values found as the tree is traversed (though the values may have been changed by a {{{change}}} callback).
This parameter is like {{{level}}}, it is passed along as the subroutine recurses, and you should not pass it (though I suppose you could if you wanted everything to start with a particular "root" parent value).
!AUTHOR, COPYRIGHT, AND LICENSE
Brad Baxter, <bbaxter@cpan.org>
Copyright (C) 2010 by Brad Baxter
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.
!Simple Stepwise Tutorials
The following tutorials are not fancy by any means. But hopefully they may form a resource from which to develop fancy ones.
!!Basics
Following are tutorials for some of the basic functions of the mapping tool:
;[[HowtoStart]]
:The basics of getting started using the mapping tool
;[[HowtoMapCollections]]
:Step by step process of mapping an institution's collections
;[[HowtoRecoverData]]
:Steps for recovering previous versions of the collections and mappings data
;[[HowtoUpdateCollections]]
:Discussion of how the update process works and the steps to perform (and possibly recover from) an update
;[[HowtoChangeMyPassword]]
:Simple steps for changing your password
{{center{
[img[featurebottom.png]]}}}
!!Admin
Following are tutorials for some of the administrative functions:
;[[HowtoAddNewUser]]
:Instructions for coordinators and supervisors for adding a new user
;[[HowtoEditUser]]
:Instructions for coordinators and supervisors for editing user information
;[[HowtoChangeTheirPassword]]
:Instructions for coordinators and supervisors for changing a user's password (e.g., if they forget it)
;[[HowtoAddNewInstitution]]
:Instructions for supervisors for adding a new member institution
!User Files
User profile files are stored in the dbs area as noted above, ''/dbs/gkr_mapping/users/''. They are in a simple configuration file format, e.g.,:
{{{
id: john_user
name: John Q. User
email: example@example.com
group: all
add: checked
change: checked
delete: checked
admin: checked
upd: 2010/02/05
user: brad
pwd: xxxxxxxxxxxxx
}}}
!GKR Mapping Tool Users
{{thumb{[img[GKR_Users_admin_thumb.jpg][UsersPageImage]]}}} {{thumb{[img[GKR_Users_coordinator_thumb.jpg][UsersPageImage]]}}} {{thumb{[img[GKR_Users_supervisor_thumb.jpg][UsersPageImage]]}}} [[Images|UsersPageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=users
!!Page Elements
!!!All Users
* Users must log in before they can see the mapping tool Users page.
* On the Users page, logged-in users will see all the users, regardless of institution group, who can log in to the mapping tool.
* The email addresses are "mailto:" links for convenience.
!!!Admin Users
* For Admin level users, the logged-in user's name will be a link to the Edit User page, where some information (like name and password) may be changed.
* Note that the logged-in user's name in the upper right banner area is also a link to this Edit User page.
!!!Coordinators
* A Coordinator level user will see an "Add New User" link above the list, because coordinators have permission to add users in their group (e.g., a Valdosta coordinator can add Valdosta users).
* In addition to the logged-in user's name being a link, all the names of admin users in the coordinators group will be links to the Edit User page. This is because coordinators have permission to edit admin level users in their group. Note that other coordinators in a coordinator's group will not be linked, because coordinators may not edit other coordinators' user information.
!!!Supervisors
* Supervisor level users will also see an "Add New User" link.
* In addition, every user's name is linked to the Edit User page for that user. This is because supervisors have permission to edit any user in any group, including coordinators and other supervisors.
Note that the example page images do not show names of real people, but the actual user page does.
!GKR Mapping Tool Users Page Images
!!Admin User
{{image{[img[GKR_Users_admin.png]]}}}
!!Coordinator
{{image{[img[GKR_Users_coordinator.png]]}}}
!!Supervisor
{{image{[img[GKR_Users_supervisor.png]]}}}
!Version Control
The files that comprise the mapping tool (and this wiki) are stored in a Subversion (SVN) repository on our GALILEO development server. On that server the file path for the svn client is
{{{
file:///galileo/svn/repository/misc/GKR/
e.g.,
$ svn list file:///galileo/svn/repository/misc/GKR/
}}}
The Web URL is
{{{
https://svn.galib.uga.edu:8008/galileo_repo/misc/GKR/
}}}
Both locations require login credentials and are not currently readily available to users outside our programming group.
!View File Page
{{thumb{[img[GKR_ViewFilePage_thumb.jpg][ViewFilePageImage]]}}} [[Images|ViewFilePageImage]], [[Link|http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?maction=view_file;file=home_blurb]]
!!Locations
* development: http://gkr-prod.library.gatech.edu/cgi-bin/gkr_mapping?action=view_file;file=home_blurb (but your permissions may not allow this)
!!Page Elements
* The file contents display in the gray area.
!View File Page Image
{{image{[img[GKR_ViewFilePage.png]]}}}
!Brief Wiki Navigation Tips
!!Step 1:
* Open AdvancedOptions and uncheck "Enable animations" -- unless you like them.
* I also like to check "Clicking on links to open tiddlers causes them to close", but again, it may be a matter of taste. (A "tiddler" is ~TiddlyWiki-speak for the individual page sections -- the bits you can open and close.)
* Close ~AdvancedOptions.
!!Step 2:
* Open and close a few page sections (I will not be calling them "tiddlers") to get the feel of it.
* Click open some of the "tags" menus to see what they offer.
* Try a couple of searches.
!!Step 3:
* Open [[Tutorials]] and start reading.
* Enjoy!
Feel free to e-mail me at "Brad Baxter" <bmb@mail.libs.uga.edu> if you have any comments, suggestions, or problems.
<<forEachTiddler where 'tiddler.tags.contains("api")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("configuration")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("documentation")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("functional")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("installation")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("introduction")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("lewcidExtension")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("programming")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("systemConfig")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("tag")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("technical")' sortBy tiddler.title>>
<<forEachTiddler where 'tiddler.tags.contains("tutorials")' sortBy tiddler.title>>