]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blobdiff - doc/todo/structured_page_data.mdwn
(no commit message)
[git.ikiwiki.info.git] / doc / todo / structured_page_data.mdwn
index 54e001fc6c92ebc08bdabdb9ad53eab74f745a20..da9da9663e2163b13e1d74f1333ee05316725ca3 100644 (file)
@@ -1,5 +1,7 @@
 This is an idea from [[JoshTriplett]].  --[[Joey]]
 
 This is an idea from [[JoshTriplett]].  --[[Joey]]
 
+* See further discussion at [[forum/an_alternative_approach_to_structured_data]].
+
 Some uses of ikiwiki, such as for a bug-tracking system (BTS), move a bit away from the wiki end
 of the spectrum, and toward storing structured data about a page or instead
 of a page. 
 Some uses of ikiwiki, such as for a bug-tracking system (BTS), move a bit away from the wiki end
 of the spectrum, and toward storing structured data about a page or instead
 of a page. 
@@ -82,6 +84,10 @@ See also:
 >    rather than all pages linked from a given page.
 >
 >The first use case is handled by having a template in the page creation.  You could
 >    rather than all pages linked from a given page.
 >
 >The first use case is handled by having a template in the page creation.  You could
+
+
+
+
 >have some type of form to edit the data, but that's just sugar on top of the template.
 >If you were going to have a web form to edit the data, I can imagine a few ways to do it:
 >
 >have some type of form to edit the data, but that's just sugar on top of the template.
 >If you were going to have a web form to edit the data, I can imagine a few ways to do it:
 >
@@ -172,34 +178,83 @@ Let me respond to a few of your comments.. --[[Joey]]
 On use cases, one use case is a user posting a bug report with structured
 data in it. A template is one way, but then the user has to deal with the
 format used to store the structured data. This is where a edit-time form
 On use cases, one use case is a user posting a bug report with structured
 data in it. A template is one way, but then the user has to deal with the
 format used to store the structured data. This is where a edit-time form
-becomes essential. Another use case is, after many such bugs have been filed,
+becomes essential.
+
+> This was the idea with the 'form' plugin.  With the 'data' plugin I was exploring
+> a different approach: try to keep the markup simple enough that the user can edit
+> the markup directly, and still have that be ok.  I admit it is a stretch, but I thought
+> it worth exploring.
+
+Another use case is, after many such bugs have been filed,
 wanting to add a new field to each bug report. To avoid needing to edit
 every bug report it would be good if the fields in a bug report were
 defined somewhere else, so that just that one place can be edited to add
 the new field, and it will show up in each bug report (and in each bug
 report's edit page, as a new form field).
 
 wanting to add a new field to each bug report. To avoid needing to edit
 every bug report it would be good if the fields in a bug report were
 defined somewhere else, so that just that one place can be edited to add
 the new field, and it will show up in each bug report (and in each bug
 report's edit page, as a new form field).
 
+> If I was going to do that, I'd use a perl script on a checked out
+> workspace.  I think you're describing a rare operation and
+> so I'd be happy not having a web interface for it.  Having said that,
+> if you just wanted to change the form for *new* pages, then you
+> can just edit the template used to create new pages.
+
 Re the form plugin, I'm uncomfortable with tying things into
 Re the form plugin, I'm uncomfortable with tying things into
-[[!cpan CGI::FormBuilder]] quite so tightly as you have. CGI::FormBuilder
+[[!cpan CGI::FormBuilder]] quite so tightly as you have.
+
+> Yeah :).  But I wanted to explore the space and that was the
+> easiest way to start.
+
+CGI::FormBuilder
 could easily change in a way that broke whole wikis full of pages. Also,
 needing to sanitize FormBuilder fields with security implications is asking
 for trouble, since new FormBuilder features could add new fields, or
 add new features to existing fields (FormBuilder is very DWIM) that open
 new security holes. 
 
 could easily change in a way that broke whole wikis full of pages. Also,
 needing to sanitize FormBuilder fields with security implications is asking
 for trouble, since new FormBuilder features could add new fields, or
 add new features to existing fields (FormBuilder is very DWIM) that open
 new security holes. 
 
+> There is a list of allowed fields.  I only interpret those.
+
 I think that having a type system, that allows defining specific types,
 like "email address", by writing code (that in turn can use FormBuilder),
 is a better approach, since it should avoid becoming a security problem.
 
 I think that having a type system, that allows defining specific types,
 like "email address", by writing code (that in turn can use FormBuilder),
 is a better approach, since it should avoid becoming a security problem.
 
+> That would be possible.  I think an extension to the 'data' plugin might
+> work here.
+
 One specific security hole, BTW, is that if you allow the `validate` field,
 FormBuilder will happily treat it as a regexp, and we don't want to expose
 arbitrary perl regexps, since they can at least DOS a system, and can
 probably be used to run arbitrary perl code.
 
 One specific security hole, BTW, is that if you allow the `validate` field,
 FormBuilder will happily treat it as a regexp, and we don't want to expose
 arbitrary perl regexps, since they can at least DOS a system, and can
 probably be used to run arbitrary perl code.
 
+> I validate the validate field :).  It only allows validate fields that match
+> `/^[\w\s]+$/`.  This means you can really only use the pre-defined
+> validation types in FormBuilder.
+
 The data plugin only deals with a fairly small corner of the problem space,
 but I think does a nice job at what it does. And could probably be useful
 in a large number of other cases.
 
 The data plugin only deals with a fairly small corner of the problem space,
 but I think does a nice job at what it does. And could probably be useful
 in a large number of other cases.
 
+> I think the data plugin is more likely to be useful than the form plugin.
+> I was thinking of extending the data directive by allowing an 'id' parameter.
+> When you have an id parameter, then you can display a small form for that
+> data element.  The submission handler would look through the page source
+> for the data directive with the right id parameter and edit it.  This would
+> make the data directive more like the current 'form' plugin.
+
+> That is making things significantly more complex for less significant gain though. --[[Will]]
+
+> Oh, one quick other note.  The data plugin below was designed to handle multiple
+> data elements in a single directive.  e.g.
+
+    \[[!data key="Depends on" link="bugs/bugA" link="bugs/bugB" value=6]]
+
+> would match `data_eq(Depends on,6)`, `data_link(Depends on,bugs/bugA)`, `data_link(Depends on,bugs/bugB)`
+> or, if you applied the patch in [[todo/tracking_bugs_with_dependencies]] then you can use 'defined pagespecs'
+> such as `data_link(Depends on,~openBugs)`. <a id="another_kind_of_links" />The ability to label links like this allows separation of
+> dependencies between bugs from arbitrary links.
+>> This issue (the need for distinguished kinds of links) has also been brought up in other discussions: [[tracking_bugs_with_dependencies#another_kind_of_links]] (deps vs. links) and [[tag_pagespec_function]] (tags vs. links). --Ivan Z.
+
+----
+
     #!/usr/bin/perl
     # Interpret YAML data to make a web form
     package IkiWiki::Plugin::form;
     #!/usr/bin/perl
     # Interpret YAML data to make a web form
     package IkiWiki::Plugin::form;
@@ -209,21 +264,21 @@ in a large number of other cases.
     use CGI::FormBuilder;
     use IkiWiki 2.00;
     
     use CGI::FormBuilder;
     use IkiWiki 2.00;
     
-    sub import { #{{{
+    sub import {
        hook(type => "getsetup", id => "form", call => \&getsetup);
        hook(type => "htmlize", id => "form", call => \&htmlize);
        hook(type => "sessioncgi", id => "form", call => \&cgi_submit);
        hook(type => "getsetup", id => "form", call => \&getsetup);
        hook(type => "htmlize", id => "form", call => \&htmlize);
        hook(type => "sessioncgi", id => "form", call => \&cgi_submit);
-    } # }}}
+    }
     
     
-    sub getsetup () { #{{{
+    sub getsetup () {
        return
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
                },
        return
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
                },
-    } #}}}
+    }
     
     
-    sub makeFormFromYAML ($$$) { #{{{
+    sub makeFormFromYAML ($$$) {
        my $page = shift;
        my $YAMLString = shift;
        my $q = shift;
        my $page = shift;
        my $YAMLString = shift;
        my $q = shift;
@@ -302,9 +357,9 @@ in a large number of other cases.
        # IkiWiki::decode_form_utf8($form);
        
        return $form;
        # IkiWiki::decode_form_utf8($form);
        
        return $form;
-    } #}}}
+    }
     
     
-    sub htmlize (@) { #{{{
+    sub htmlize (@) {
        my %params=@_;
        my $content = $params{content};
        my $page = $params{page};
        my %params=@_;
        my $content = $params{content};
        my $page = $params{page};
@@ -312,9 +367,9 @@ in a large number of other cases.
        my $form = makeFormFromYAML($page, $content, undef);
     
        return $form->render(submit => 'Update Form');
        my $form = makeFormFromYAML($page, $content, undef);
     
        return $form->render(submit => 'Update Form');
-    } # }}}
+    }
     
     
-    sub cgi_submit ($$) { #{{{
+    sub cgi_submit ($$) {
        my $q=shift;
        my $session=shift;
        
        my $q=shift;
        my $session=shift;
        
@@ -377,11 +432,11 @@ in a large number of other cases.
        }
     
        exit;
        }
     
        exit;
-    } #}}}
+    }
     
     package IkiWiki::PageSpec;
     
     
     package IkiWiki::PageSpec;
     
-    sub match_form_eq ($$;@) { #{{{
+    sub match_form_eq ($$;@) {
        my $page=shift;
        my $argSet=shift;
        my @args=split(/,/, $argSet);
        my $page=shift;
        my $argSet=shift;
        my @args=split(/,/, $argSet);
@@ -412,7 +467,7 @@ in a large number of other cases.
        } else {
                return IkiWiki::FailReason->new("field value does not match");
        }
        } else {
                return IkiWiki::FailReason->new("field value does not match");
        }
-    } #}}}
+    }
     
     1
 
     
     1
 
@@ -428,22 +483,22 @@ in a large number of other cases.
     
     my $inTable = 0;
     
     
     my $inTable = 0;
     
-    sub import { #{{{
+    sub import {
        hook(type => "getsetup", id => "data", call => \&getsetup);
        hook(type => "needsbuild", id => "data", call => \&needsbuild);
        hook(type => "preprocess", id => "data", call => \&preprocess, scan => 1);
        hook(type => "preprocess", id => "datatable", call => \&preprocess_table, scan => 1);   # does this need scan?
        hook(type => "getsetup", id => "data", call => \&getsetup);
        hook(type => "needsbuild", id => "data", call => \&needsbuild);
        hook(type => "preprocess", id => "data", call => \&preprocess, scan => 1);
        hook(type => "preprocess", id => "datatable", call => \&preprocess_table, scan => 1);   # does this need scan?
-    } # }}}
+    }
     
     
-    sub getsetup () { #{{{
+    sub getsetup () {
        return
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
                },
        return
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
                },
-    } #}}}
+    }
     
     
-    sub needsbuild (@) { #{{{
+    sub needsbuild (@) {
        my $needsbuild=shift;
        foreach my $page (keys %pagestate) {
                if (exists $pagestate{$page}{data}) {
        my $needsbuild=shift;
        foreach my $page (keys %pagestate) {
                if (exists $pagestate{$page}{data}) {
@@ -458,7 +513,7 @@ in a large number of other cases.
        }
     }
     
        }
     }
     
-    sub preprocess (@) { #{{{
+    sub preprocess (@) {
        my @argslist = @_;
        my %params=@argslist;
        
        my @argslist = @_;
        my %params=@argslist;
        
@@ -498,9 +553,9 @@ in a large number of other cases.
        }
        
        return $html;
        }
        
        return $html;
-    } # }}}
+    }
     
     
-    sub preprocess_table (@) { #{{{
+    sub preprocess_table (@) {
        my %params=@_;
     
        my @lines;
        my %params=@_;
     
        my @lines;
@@ -520,11 +575,11 @@ in a large number of other cases.
        push @lines, '</table>';
     
        return join("\n", @lines);
        push @lines, '</table>';
     
        return join("\n", @lines);
-    } #}}}
+    }
     
     package IkiWiki::PageSpec;
     
     
     package IkiWiki::PageSpec;
     
-    sub match_data_eq ($$;@) { #{{{
+    sub match_data_eq ($$;@) {
        my $page=shift;
        my $argSet=shift;
        my @args=split(/,/, $argSet);
        my $page=shift;
        my $argSet=shift;
        my @args=split(/,/, $argSet);
@@ -544,9 +599,9 @@ in a large number of other cases.
        } else {
                return IkiWiki::FailReason->new("value does not match");
        }
        } else {
                return IkiWiki::FailReason->new("value does not match");
        }
-    } #}}}
+    }
     
     
-    sub match_data_link ($$;@) { #{{{
+    sub match_data_link ($$;@) {
        my $page=shift;
        my $argSet=shift;
        my @params=@_;
        my $page=shift;
        my $argSet=shift;
        my @params=@_;
@@ -570,6 +625,6 @@ in a large number of other cases.
        }
     
        return IkiWiki::FailReason->new("No data link on page $page with key $key matches glob $value");
        }
     
        return IkiWiki::FailReason->new("No data link on page $page with key $key matches glob $value");
-    } #}}}
+    }
     
     1
     
     1