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.
> 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:
>
>>>> I've just updated the second of the two patches below. The two patches are not mutually
>>>> exclusive, but I'm leaning towards the second as more useful (for the things I'm doing). -- [[Will]]
+I think it's awesome that you're writing this code to explore the problem
+space, [[Will]] -- and these plugins are good stabs at at least part of it.
+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
+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).
+
+> 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
+[[!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.
+
+> 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.
+
+> 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.
+
+> 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.
+
+> 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.
+
+>>> And multiple link types are now supported; plugins can set the link
+>>> type when registering a link, and pagespec functions can match on them. --[[Joey]]
+
+----
+
#!/usr/bin/perl
# Interpret YAML data to make a web form
package IkiWiki::Plugin::form;
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);
- } # }}}
+ }
- sub getsetup () { #{{{
+ sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
- } #}}}
+ }
- sub makeFormFromYAML ($$$) { #{{{
+ sub makeFormFromYAML ($$$) {
my $page = shift;
my $YAMLString = shift;
my $q = shift;
# IkiWiki::decode_form_utf8($form);
return $form;
- } #}}}
+ }
- sub htmlize (@) { #{{{
+ sub htmlize (@) {
my %params=@_;
my $content = $params{content};
my $page = $params{page};
my $form = makeFormFromYAML($page, $content, undef);
return $form->render(submit => 'Update Form');
- } # }}}
+ }
- sub cgi_submit ($$) { #{{{
+ sub cgi_submit ($$) {
my $q=shift;
my $session=shift;
}
exit;
- } #}}}
+ }
package IkiWiki::PageSpec;
- sub match_form_eq ($$;@) { #{{{
+ sub match_form_eq ($$;@) {
my $page=shift;
my $argSet=shift;
my @args=split(/,/, $argSet);
} else {
return IkiWiki::FailReason->new("field value does not match");
}
- } #}}}
+ }
1
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?
- } # }}}
+ }
- sub getsetup () { #{{{
+ sub getsetup () {
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}) {
}
}
- sub preprocess (@) { #{{{
+ sub preprocess (@) {
my @argslist = @_;
my %params=@argslist;
}
return $html;
- } # }}}
+ }
- sub preprocess_table (@) { #{{{
+ sub preprocess_table (@) {
my %params=@_;
my @lines;
push @lines, '</table>';
return join("\n", @lines);
- } #}}}
+ }
package IkiWiki::PageSpec;
- sub match_data_eq ($$;@) { #{{{
+ sub match_data_eq ($$;@) {
my $page=shift;
my $argSet=shift;
my @args=split(/,/, $argSet);
} 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=@_;
}
return IkiWiki::FailReason->new("No data link on page $page with key $key matches glob $value");
- } #}}}
+ }
1