Merge remote branch 'davrieb/autotag' into autotag
authorJoey Hess <joey@kitenet.net>
Sat, 17 Apr 2010 16:50:23 +0000 (12:50 -0400)
committerJoey Hess <joey@kitenet.net>
Sat, 17 Apr 2010 16:50:23 +0000 (12:50 -0400)
Conflicts:
IkiWiki.pm
IkiWiki/Plugin/tag.pm

413 files changed:
.gitignore
Bundle/IkiWiki/Extras.pm
IkiWiki.pm
IkiWiki/Plugin/404.pm
IkiWiki/Plugin/amazon_s3.pm
IkiWiki/Plugin/anonok.pm
IkiWiki/Plugin/attachment.pm
IkiWiki/Plugin/autoindex.pm
IkiWiki/Plugin/blogspam.pm
IkiWiki/Plugin/bzr.pm
IkiWiki/Plugin/calendar.pm
IkiWiki/Plugin/color.pm
IkiWiki/Plugin/comments.pm
IkiWiki/Plugin/conditional.pm
IkiWiki/Plugin/creole.pm
IkiWiki/Plugin/cutpaste.pm
IkiWiki/Plugin/cvs.pm
IkiWiki/Plugin/darcs.pm
IkiWiki/Plugin/date.pm
IkiWiki/Plugin/editdiff.pm
IkiWiki/Plugin/editpage.pm
IkiWiki/Plugin/edittemplate.pm
IkiWiki/Plugin/filecheck.pm
IkiWiki/Plugin/format.pm
IkiWiki/Plugin/fortune.pm
IkiWiki/Plugin/getsource.pm
IkiWiki/Plugin/git.pm
IkiWiki/Plugin/google.pm
IkiWiki/Plugin/goto.pm
IkiWiki/Plugin/graphviz.pm
IkiWiki/Plugin/haiku.pm
IkiWiki/Plugin/highlight.pm
IkiWiki/Plugin/hnb.pm
IkiWiki/Plugin/html.pm
IkiWiki/Plugin/htmlscrubber.pm
IkiWiki/Plugin/httpauth.pm
IkiWiki/Plugin/img.pm
IkiWiki/Plugin/inline.pm
IkiWiki/Plugin/link.pm
IkiWiki/Plugin/linkmap.pm
IkiWiki/Plugin/listdirectives.pm
IkiWiki/Plugin/lockedit.pm
IkiWiki/Plugin/map.pm
IkiWiki/Plugin/mdwn.pm
IkiWiki/Plugin/mercurial.pm
IkiWiki/Plugin/meta.pm
IkiWiki/Plugin/mirrorlist.pm
IkiWiki/Plugin/moderatedcomments.pm
IkiWiki/Plugin/monotone.pm
IkiWiki/Plugin/more.pm
IkiWiki/Plugin/norcs.pm
IkiWiki/Plugin/opendiscussion.pm
IkiWiki/Plugin/openid.pm
IkiWiki/Plugin/orphans.pm
IkiWiki/Plugin/otl.pm
IkiWiki/Plugin/pagecount.pm
IkiWiki/Plugin/pagestats.pm
IkiWiki/Plugin/parentlinks.pm
IkiWiki/Plugin/passwordauth.pm
IkiWiki/Plugin/po.pm
IkiWiki/Plugin/poll.pm
IkiWiki/Plugin/polygen.pm
IkiWiki/Plugin/postsparkline.pm
IkiWiki/Plugin/progress.pm
IkiWiki/Plugin/rawhtml.pm
IkiWiki/Plugin/recentchanges.pm
IkiWiki/Plugin/relativedate.pm
IkiWiki/Plugin/remove.pm
IkiWiki/Plugin/rename.pm
IkiWiki/Plugin/repolist.pm
IkiWiki/Plugin/search.pm
IkiWiki/Plugin/shortcut.pm
IkiWiki/Plugin/sidebar.pm
IkiWiki/Plugin/signinedit.pm
IkiWiki/Plugin/sortnaturally.pm [new file with mode: 0644]
IkiWiki/Plugin/sparkline.pm
IkiWiki/Plugin/svn.pm
IkiWiki/Plugin/table.pm
IkiWiki/Plugin/tag.pm
IkiWiki/Plugin/template.pm
IkiWiki/Plugin/teximg.pm
IkiWiki/Plugin/textile.pm
IkiWiki/Plugin/tla.pm
IkiWiki/Plugin/toc.pm
IkiWiki/Plugin/toggle.pm
IkiWiki/Plugin/txt.pm
IkiWiki/Plugin/typography.pm
IkiWiki/Plugin/version.pm
IkiWiki/Plugin/websetup.pm
IkiWiki/Plugin/wikitext.pm
IkiWiki/Plugin/wmd.pm
IkiWiki/Render.pm
IkiWiki/Setup.pm
IkiWiki/Setup/Automator.pm
IkiWiki/Setup/Standard.pm
IkiWiki/Setup/Yaml.pm [new file with mode: 0644]
IkiWiki/Wrapper.pm
Makefile.PL
README
auto-blog.setup
debian/NEWS
debian/changelog
debian/control
debian/copyright
debian/postinst
doc/bugs/404_plugin_and_lighttpd.mdwn [new file with mode: 0644]
doc/bugs/Comments_dissapeared.mdwn [new file with mode: 0644]
doc/bugs/Error:_Your_login_session_has_expired._.mdwn
doc/bugs/Exception:_Unknown_function___96__this__39___.mdwn [new file with mode: 0644]
doc/bugs/External_links_with_Creole.mdwn [new file with mode: 0644]
doc/bugs/Tab_delimited_tables_don__39__t_work.mdwn [new file with mode: 0644]
doc/bugs/anonok_vs._httpauth.mdwn
doc/bugs/barfs_on_recentchange_entry_for_a_change_removing_an_invalid_pagespec.mdwn
doc/bugs/bzr_2.0_breaks_bzr_plugin.mdwn [new file with mode: 0644]
doc/bugs/clearenv_not_present_at_FreeBSD_.mdwn [new file with mode: 0644]
doc/bugs/clearenv_not_present_at_FreeBSD_/discussion.mdwn [new file with mode: 0644]
doc/bugs/depends_simple_mixup.mdwn [new file with mode: 0644]
doc/bugs/filecheck_failing_to_find_files.mdwn [new file with mode: 0644]
doc/bugs/firefox_doesn__39__t_want_to_load_updated_pages_at_ikiwiki.info.mdwn
doc/bugs/ikiwiki-transition_does_not_set_perl_moduels_path_properly.mdwn [new file with mode: 0644]
doc/bugs/map_sorts_by_pagename_and_not_title_when_show__61__title_is_used.mdwn
doc/bugs/post-update_hook_can__39__t_be_compiled_with_tcc.mdwn [new file with mode: 0644]
doc/bugs/rebuild_after_changing_the_underlaydir_config_option.mdwn [new file with mode: 0644]
doc/bugs/some_but_not_all_meta_fields_are_stored_escaped.mdwn [new file with mode: 0644]
doc/bugs/stray___60____47__p__62___tags.mdwn
doc/bugs/tagged__40____41___matching_wikilinks.mdwn
doc/bugs/templateForRecentChangesMissingCloseSpan.mdwn [new file with mode: 0644]
doc/bugs/wrapper_can__39__t_find_the_perl_modules.mdwn [new file with mode: 0644]
doc/download.mdwn
doc/examples/blog.mdwn
doc/examples/blog/archives.mdwn [new file with mode: 0644]
doc/examples/blog/comments.mdwn
doc/examples/blog/index.mdwn
doc/examples/blog/posts/first_post.mdwn
doc/examples/blog/sidebar.mdwn
doc/examples/softwaresite.mdwn
doc/examples/softwaresite/news.mdwn
doc/features.mdwn
doc/forum/How_does_ikiwiki_remember_times__63__.mdwn
doc/forum/Migrating_old_repository_to_new_ikiwiki_system__63__.mdwn
doc/forum/an_alternative_approach_to_structured_data.mdwn
doc/forum/google_openid_broken__63__.mdwn
doc/forum/how_to_setup_ikiwiki_on_a_remote_host.mdwn [new file with mode: 0644]
doc/forum/navigation_of_wiki_pages_on_local_filesystem_with_vim.mdwn
doc/forum/speeding_up_ikiwiki.mdwn
doc/forum/utf8_warnings_for___34____92__xAB__34__.mdwn [new file with mode: 0644]
doc/forum/where_are_the_tags.mdwn [new file with mode: 0644]
doc/forum/wishlist-discussion:_Editformular_showing_existing_tags.mdwn [new file with mode: 0644]
doc/git.mdwn
doc/ikiwiki-calendar.mdwn
doc/ikiwiki/directive/calendar.mdwn
doc/ikiwiki/directive/date.mdwn
doc/ikiwiki/directive/format.mdwn
doc/ikiwiki/directive/inline/discussion.mdwn
doc/ikiwiki/directive/map/discussion.mdwn
doc/ikiwiki/directive/meta.mdwn
doc/ikiwiki/directive/pagestats.mdwn
doc/ikiwiki/directive/sidebar.mdwn [new file with mode: 0644]
doc/ikiwiki/directive/table.mdwn
doc/ikiwiki/pagespec.mdwn
doc/ikiwiki/pagespec/po.mdwn
doc/ikiwiki/pagespec/sorting.mdwn
doc/ikiwikiusers.mdwn
doc/index.mdwn
doc/news/version_3.20091113.mdwn [deleted file]
doc/news/version_3.20091202.mdwn [deleted file]
doc/news/version_3.20091218.mdwn [deleted file]
doc/news/version_3.20100102.3.mdwn [deleted file]
doc/news/version_3.20100212.mdwn [new file with mode: 0644]
doc/news/version_3.20100302.mdwn [new file with mode: 0644]
doc/news/version_3.20100312.mdwn [new file with mode: 0644]
doc/news/version_3.20100403.mdwn [new file with mode: 0644]
doc/plugins/404.mdwn
doc/plugins/aggregate.mdwn
doc/plugins/autoindex.mdwn
doc/plugins/calendar.mdwn
doc/plugins/color.mdwn
doc/plugins/comments.mdwn
doc/plugins/conditional.mdwn
doc/plugins/conditional/discussion.mdwn
doc/plugins/contrib/field/discussion.mdwn [new file with mode: 0644]
doc/plugins/contrib/ftemplate/discussion.mdwn [new file with mode: 0644]
doc/plugins/contrib/highlightcode.mdwn
doc/plugins/contrib/pod/discussion.mdwn [new file with mode: 0644]
doc/plugins/contrib/postal.mdwn
doc/plugins/contrib/report.mdwn
doc/plugins/contrib/report/discussion.mdwn [new file with mode: 0644]
doc/plugins/contrib/xslt/discussion.mdwn
doc/plugins/contrib/ymlfront/discussion.mdwn [new file with mode: 0644]
doc/plugins/creole/discussion.mdwn
doc/plugins/cutpaste.mdwn
doc/plugins/date.mdwn
doc/plugins/ddate.mdwn
doc/plugins/editpage.mdwn
doc/plugins/filecheck.mdwn
doc/plugins/format.mdwn
doc/plugins/fortune.mdwn
doc/plugins/getsource.mdwn
doc/plugins/getsource/discussion.mdwn [new file with mode: 0644]
doc/plugins/goto.mdwn
doc/plugins/graphviz.mdwn
doc/plugins/haiku.mdwn
doc/plugins/httpauth.mdwn
doc/plugins/img.mdwn
doc/plugins/inline.mdwn
doc/plugins/linkmap.mdwn
doc/plugins/listdirectives.mdwn
doc/plugins/lockedit.mdwn
doc/plugins/map.mdwn
doc/plugins/mirrorlist.mdwn
doc/plugins/moderatedcomments.mdwn
doc/plugins/more.mdwn
doc/plugins/opendiscussion.mdwn
doc/plugins/orphans.mdwn
doc/plugins/pagecount.mdwn
doc/plugins/pagestats.mdwn
doc/plugins/parentlinks.mdwn
doc/plugins/poll.mdwn
doc/plugins/polygen.mdwn
doc/plugins/postsparkline.mdwn
doc/plugins/prettydate.mdwn
doc/plugins/progress.mdwn
doc/plugins/recentchanges.mdwn
doc/plugins/recentchangesdiff.mdwn
doc/plugins/relativedate.mdwn
doc/plugins/rename.mdwn
doc/plugins/repolist.mdwn
doc/plugins/rsync.mdwn
doc/plugins/shortcut.mdwn
doc/plugins/sidebar.mdwn
doc/plugins/sortnaturally.mdwn [new file with mode: 0644]
doc/plugins/sparkline.mdwn
doc/plugins/table.mdwn
doc/plugins/template.mdwn
doc/plugins/testpagespec.mdwn
doc/plugins/teximg.mdwn
doc/plugins/toc.mdwn
doc/plugins/toggle.mdwn
doc/plugins/txt.mdwn
doc/plugins/type/chrome.mdwn
doc/plugins/type/useful.mdwn [deleted file]
doc/plugins/type/widget.mdwn [new file with mode: 0644]
doc/plugins/typography.mdwn
doc/plugins/underlay.mdwn
doc/plugins/version.mdwn
doc/plugins/websetup.mdwn
doc/plugins/wmd.mdwn
doc/plugins/write.mdwn
doc/rcs.mdwn
doc/roadmap.mdwn
doc/roadmap/discussion.mdwn
doc/sandbox.mdwn
doc/sandbox/danc.mdwn [new file with mode: 0644]
doc/sandbox/prova_blog.html [new file with mode: 0644]
doc/security.mdwn
doc/style.css
doc/templates.mdwn
doc/tips/Importing_posts_from_Wordpress.mdwn
doc/tips/follow_wikilinks_from_inside_vim.mdwn [new file with mode: 0644]
doc/tips/htaccess_file.mdwn [new file with mode: 0644]
doc/tips/inside_dot_ikiwiki/discussion.mdwn
doc/tips/laptop_wiki_with_git.mdwn
doc/tips/spam_and_softwaresites.mdwn [new file with mode: 0644]
doc/tips/vim_syntax_highlighting.mdwn
doc/tips/yaml_setup_files.mdwn [new file with mode: 0644]
doc/todo/ACL.mdwn
doc/todo/More_flexible_po-plugin_for_translation.mdwn [new file with mode: 0644]
doc/todo/Multiple_categorization_namespaces.mdwn [new file with mode: 0644]
doc/todo/Separate_OpenIDs_and_usernames.mdwn
doc/todo/abbreviation.mdwn
doc/todo/allow_plugins_to_add_sorting_methods.mdwn [new file with mode: 0644]
doc/todo/allow_site-wide_meta_definitions.mdwn
doc/todo/auto-create_tag_pages_according_to_a_template.mdwn
doc/todo/auto_getctime_on_fresh_build.mdwn [new file with mode: 0644]
doc/todo/avatar.mdwn
doc/todo/beef_up_sidebar_to_allow_for_multiple_sidebars.mdwn
doc/todo/cdate_and_mdate_available_for_templates.mdwn [new file with mode: 0644]
doc/todo/conflict_free_comment_merges.mdwn
doc/todo/double-click_protection_for_form_buttons.mdwn [new file with mode: 0644]
doc/todo/enable-htaccess-files.mdwn
doc/todo/finer_control_over___60__object___47____62__s.mdwn [new file with mode: 0644]
doc/todo/git_attribution/discussion.mdwn
doc/todo/http_bl_support.mdwn [new file with mode: 0644]
doc/todo/link_plugin_perhaps_too_general__63__.mdwn [new file with mode: 0644]
doc/todo/mark_edit_as_trivial__44___identify__47__filter_on_trivial_changes.mdwn [new file with mode: 0644]
doc/todo/matching_different_kinds_of_links.mdwn
doc/todo/mercurial.mdwn
doc/todo/more_flexible_inline_postform.mdwn
doc/todo/openid_user_filtering.mdwn
doc/todo/optional_underlaydir_prefix.mdwn [new file with mode: 0644]
doc/todo/rewrite_ikiwiki_in_haskell.mdwn
doc/todo/salmon_protocol_for_comment_sharing.mdwn [new file with mode: 0644]
doc/todo/smarter_sorting.mdwn [new file with mode: 0644]
doc/todo/structured_page_data.mdwn
doc/todo/svg.mdwn
doc/todo/tracking_bugs_with_dependencies.mdwn
doc/todo/user-defined_templates_outside_the_wiki.mdwn [new file with mode: 0644]
doc/usage.mdwn
doc/users/David_Riebenbauer.mdwn
doc/users/Edward_Betts.mdwn
doc/users/KathrynAndersen.mdwn
doc/users/KathrynAndersen/discussion.mdwn [new file with mode: 0644]
doc/users/NicolasLimare.mdwn
doc/users/Will.mdwn
doc/users/ivan_shmakov.mdwn [new file with mode: 0644]
doc/users/jeanprivat.mdwn [new file with mode: 0644]
doc/users/jogo.mdwn
doc/users/jon.mdwn
doc/users/nil.mdwn [new file with mode: 0644]
doc/users/schmonz.mdwn
doc/users/tschwinge.mdwn
doc/wikitemplates.mdwn
docwiki.setup
ikiwiki-calendar.in [moved from ikiwiki-calendar with 81% similarity]
ikiwiki-transition.in [moved from ikiwiki-transition with 99% similarity]
ikiwiki.in
ikiwiki.spec
mdwn2man
plugins/rst
po/bg.po
po/cs.po
po/da.po
po/de.po
po/es.po
po/fr.po
po/gu.po
po/ikiwiki.pot
po/it.po
po/pl.po
po/sv.po
po/tr.po
po/underlay.setup
po/underlays/basewiki/ikiwiki.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/directive.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/formatting.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/markdown.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/openid.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/pagespec.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/pagespec/attachment.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/pagespec/po.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/pagespec/sorting.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/searching.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/subpage.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/subpage/linkingrules.de.po [new file with mode: 0644]
po/underlays/basewiki/ikiwiki/wikilink.de.po [new file with mode: 0644]
po/underlays/basewiki/index.de.po [new file with mode: 0644]
po/underlays/basewiki/recentchanges.de.po [new file with mode: 0644]
po/underlays/basewiki/sandbox.de.po [new file with mode: 0644]
po/underlays/basewiki/shortcuts.de.po [new file with mode: 0644]
po/underlays/basewiki/templates.de.po [new file with mode: 0644]
po/underlays/basewiki/templates/note.de.po [new file with mode: 0644]
po/underlays/basewiki/templates/popup.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/aggregate.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/brokenlinks.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/calendar.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/color.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/comment.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/copy.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/cut.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/cutpaste.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/date.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/edittemplate.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/format.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/fortune.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/graph.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/haiku.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/if.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/img.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/inline.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/linkmap.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/listdirectives.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/map.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/meta.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/more.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/orphans.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/pagecount.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/pagestats.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/pagetemplate.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/paste.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/ping.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/poll.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/polygen.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/postsparkline.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/progress.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/shortcut.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/sparkline.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/table.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/tag.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/taglink.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/template.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/testpagespec.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/teximg.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/toc.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/toggle.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/toggleable.de.po [new file with mode: 0644]
po/underlays/directives/ikiwiki/directive/version.de.po [new file with mode: 0644]
po/underlays/smiley/smileys.de.po [new file with mode: 0644]
po/vi.po
t/calculate_changed_links.t [new file with mode: 0755]
t/file_pruned.t
t/index.t
t/openiduser.t
t/pagespec_match.t
t/pagespec_match_list.t
t/preprocess.t
t/tag.t [new file with mode: 0755]
templates/atomitem.tmpl
templates/atompage.tmpl
templates/calendarmonth.tmpl
templates/change.tmpl
templates/page.tmpl
templates/rssitem.tmpl
underlays/basewiki/ikiwiki/pagespec/sorting.mdwn [changed from file to symlink]

index 8de36e2..e9ab152 100644 (file)
@@ -5,9 +5,10 @@ blib/*
 doc/.ikiwiki/*
 html/*
 ikiwiki.out
+ikiwiki-transition.out
+ikiwiki-calendar.out
 pm_to_blib
 *.man
-build-stamp
 po/po2wiki_stamp
 po/underlays/*/*.mdwn
 po/underlays/basewiki/*/*.mdwn
index 48bd127..5bbd7b8 100644 (file)
@@ -35,6 +35,7 @@ Text::WikiCreole
 Term::ReadLine::Gnu
 HTML::Tree
 Sort::Naturally
+YAML
 
 =head1 AUTHOR
 
index 966a3bb..2f26a16 100644 (file)
@@ -7,24 +7,25 @@ use strict;
 use Encode;
 use HTML::Entities;
 use URI::Escape q{uri_escape_utf8};
-use POSIX;
+use POSIX ();
 use Storable;
 use open qw{:utf8 :std};
 
 use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase
            %pagestate %wikistate %renderedfiles %oldrenderedfiles
            %pagesources %destsources %depends %depends_simple %hooks
-           %forcerebuild %loaded_plugins %autofiles %del_hash};
+           %forcerebuild %loaded_plugins %typedlinks %oldtypedlinks
+           %autofiles %del_hash};
 
 use Exporter q{import};
 our @EXPORT = qw(hook debug error template htmlpage deptype
                  add_depends pagespec_match pagespec_match_list bestlink
                 htmllink readfile writefile pagetype srcfile pagename
-                displaytime will_render gettext urlto targetpage
+                displaytime will_render gettext ngettext urlto targetpage
                 add_underlay pagetitle titlepage linkpage newpagefile
                 inject add_link add_autofile
                  %config %links %pagestate %wikistate %renderedfiles
-                 %pagesources %destsources);
+                 %pagesources %destsources %typedlinks);
 our $VERSION = 3.00; # plugin interface version, next is ikiwiki version
 our $version='unknown'; # VERSION_AUTOREPLACE done by Makefile, DNE
 our $installdir='/usr'; # INSTALLDIR_AUTOREPLACE done by Makefile, DNE
@@ -37,6 +38,7 @@ our $DEPEND_LINKS=4;
 # Optimisation.
 use Memoize;
 memoize("abs2rel");
+memoize("sortspec_translate");
 memoize("pagespec_translate");
 memoize("template_file");
 
@@ -334,11 +336,20 @@ sub getsetup () {
                safe => 0, # paranoia
                rebuild => 0,
        },
+       include => {
+               type => "string",
+               default => undef,
+               example => '^\.htaccess$',
+               description => "regexp of normally excluded files to include",
+               advanced => 1,
+               safe => 0, # regexp
+               rebuild => 1,
+       },
        exclude => {
                type => "string",
                default => undef,
-               example => '\.wav$',
-               description => "regexp of source files to ignore",
+               example => '^(*\.private|Makefile)$',
+               description => "regexp of files that should be skipped",
                advanced => 1,
                safe => 0, # regexp
                rebuild => 1,
@@ -409,6 +420,13 @@ sub getsetup () {
                safe => 0,
                rebuild => 0,
        },
+       clean => {
+               type => "internal",
+               default => 0,
+               description => "running in clean mode",
+               safe => 0,
+               rebuild => 0,
+       },
        refresh => {
                type => "internal",
                default => 0,
@@ -423,10 +441,9 @@ sub getsetup () {
                safe => 0,
                rebuild => 0,
        },
-       getctime => {
+       gettime => {
                type => "internal",
-               default => 0,
-               description => "running in getctime mode",
+               description => "running in gettime mode",
                safe => 0,
                rebuild => 0,
        },
@@ -451,6 +468,13 @@ sub getsetup () {
                safe => 0,
                rebuild => 0,
        },
+       setuptype => {
+               type => "internal",
+               default => "Standard",
+               description => "perl class to use to dump setup file",
+               safe => 0,
+               rebuild => 0,
+       },
        allow_symlinks_before_srcdir => {
                type => "boolean",
                default => 0,
@@ -941,7 +965,12 @@ sub linkpage ($) {
 sub cgiurl (@) {
        my %params=@_;
 
-       return $config{cgiurl}."?".
+       my $cgiurl=$config{cgiurl};
+       if (exists $params{cgiurl}) {
+               $cgiurl=$params{cgiurl};
+               delete $params{cgiurl};
+       }
+       return $cgiurl."?".
                join("&amp;", map $_."=".uri_escape_utf8($params{$_}), keys %params);
 }
 
@@ -1090,6 +1119,11 @@ sub htmllink ($$$;@) {
        return "<a href=\"$bestlink\"@attrs>$linktext</a>";
 }
 
+sub userpage ($) {
+       my $user=shift;
+       return length $config{userdir} ? "$config{userdir}/$user" : $user;
+}
+
 sub openiduser ($) {
        my $user=shift;
 
@@ -1098,11 +1132,10 @@ sub openiduser ($) {
                my $display;
 
                if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) {
-                       # this works in at least 2.x
                        $display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user);
                }
                else {
-                       # this only works in 1.x
+                       # backcompat with old version
                        my $oid=Net::OpenID::VerifiedIdentity->new(identity => $user);
                        $display=$oid->display;
                }
@@ -1115,7 +1148,7 @@ sub openiduser ($) {
                # Convert "http://somehost.com/user" to "user [somehost.com]".
                # (also "https://somehost.com/user/")
                if ($display !~ /\[/) {
-                       $display=~s/^https?:\/\/(.+)\/([^\/]+)\/?$/$2 [$1]/;
+                       $display=~s/^https?:\/\/(.+)\/([^\/#?]+)\/?(?:[#?].*)?$/$2 [$1]/;
                }
                $display=~s!^https?://!!; # make sure this is removed
                eval q{use CGI 'escapeHTML'};
@@ -1125,23 +1158,6 @@ sub openiduser ($) {
        return;
 }
 
-sub userlink ($) {
-       my $user=shift;
-
-       my $oiduser=eval { openiduser($user) };
-       if (defined $oiduser) {
-               return "<a href=\"$user\">$oiduser</a>";
-       }
-       else {
-               eval q{use CGI 'escapeHTML'};
-               error($@) if $@;
-
-               return htmllink("", "", escapeHTML(
-                       length $config{userdir} ? $config{userdir}."/".$user : $user
-               ), noimageinline => 1);
-       }
-}
-
 sub htmlize ($$$$) {
        my $page=shift;
        my $destpage=shift;
@@ -1149,7 +1165,7 @@ sub htmlize ($$$$) {
        my $content=shift;
        
        my $oneline = $content !~ /\n/;
-
+       
        if (exists $hooks{htmlize}{$type}) {
                $content=$hooks{htmlize}{$type}{call}->(
                        page => $page,
@@ -1170,10 +1186,9 @@ sub htmlize ($$$$) {
        
        if ($oneline) {
                # hack to get rid of enclosing junk added by markdown
-               # and other htmlizers
+               # and other htmlizers/sanitizers
                $content=~s/^<p>//i;
-               $content=~s/<\/p>$//i;
-               chomp $content;
+               $content=~s/<\/p>\n*$//i;
        }
 
        return $content;
@@ -1228,7 +1243,7 @@ sub preprocess ($$$;$$) {
                                (?:
                                        """(.*?)"""     # 2: triple-quoted value
                                |
-                                       "([^"]+)"       # 3: single-quoted value
+                                       "([^"]*?)"      # 3: single-quoted value
                                |
                                        (\S+)           # 4: unquoted value
                                )
@@ -1314,7 +1329,7 @@ sub preprocess ($$$;$$) {
                                        (?:
                                                """.*?"""       # triple-quoted value
                                                |
-                                               "[^"]+"         # single-quoted value
+                                               "[^"]*?"        # single-quoted value
                                                |
                                                [^"\s\]]+       # unquoted value
                                        )
@@ -1337,7 +1352,7 @@ sub preprocess ($$$;$$) {
                                        (?:
                                                """.*?"""       # triple-quoted value
                                                |
-                                               "[^"]+"         # single-quoted value
+                                               "[^"]*?"        # single-quoted value
                                                |
                                                [^"\s\]]+       # unquoted value
                                        )
@@ -1488,7 +1503,7 @@ sub loadindex () {
        if (! $config{rebuild}) {
                %pagesources=%pagemtime=%oldlinks=%links=%depends=
                %destsources=%renderedfiles=%pagecase=%pagestate=
-               %depends_simple=();
+               %depends_simple=%typedlinks=%oldtypedlinks=();
        }
        my $in;
        if (! open ($in, "<", "$config{wikistatedir}/indexdb")) {
@@ -1497,6 +1512,7 @@ sub loadindex () {
                        open ($in, "<", "$config{wikistatedir}/indexdb") || return;
                }
                else {
+                       $config{gettime}=1; # first build
                        return;
                }
        }
@@ -1554,6 +1570,14 @@ sub loadindex () {
                        if (exists $d->{state}) {
                                $pagestate{$page}=$d->{state};
                        }
+                       if (exists $d->{typedlinks}) {
+                               $typedlinks{$page}=$d->{typedlinks};
+
+                               while (my ($type, $links) = each %{$typedlinks{$page}}) {
+                                       next unless %$links;
+                                       $oldtypedlinks{$page}{$type} = {%$links};
+                               }
+                       }
                }
                $oldrenderedfiles{$page}=[@{$d->{dest}}];
        }
@@ -1602,6 +1626,10 @@ sub saveindex () {
                        $index{page}{$src}{depends_simple} = $depends_simple{$page};
                }
 
+               if (exists $typedlinks{$page} && %{$typedlinks{$page}}) {
+                       $index{page}{$src}{typedlinks} = $typedlinks{$page};
+               }
+
                if (exists $pagestate{$page}) {
                        foreach my $id (@hookids) {
                                foreach my $key (keys %{$pagestate{$page}{$id}}) {
@@ -1763,6 +1791,10 @@ sub rcs_getctime ($) {
        $hooks{rcs}{rcs_getctime}{call}->(@_);
 }
 
+sub rcs_getmtime ($) {
+       $hooks{rcs}{rcs_getmtime}{call}->(@_);
+}
+
 sub rcs_receive () {
        $hooks{rcs}{rcs_receive}{call}->();
 }
@@ -1781,7 +1813,7 @@ sub add_depends ($$;$) {
 
        # Add explicit dependencies for influences.
        my $sub=pagespec_translate($pagespec);
-       return if $@;
+       return unless defined $sub;
        foreach my $p (keys %pagesources) {
                my $r=$sub->($p, location => $page);
                my $i=$r->influences;
@@ -1811,6 +1843,7 @@ sub deptype (@) {
        return $deptype;
 }
 
+my $file_prune_regexp;
 sub file_pruned ($;$) {
        my $file=shift;
        if (@_) {
@@ -1821,34 +1854,52 @@ sub file_pruned ($;$) {
                $file =~ s#^\Q$base\E/+##;
        }
 
-       my $regexp='('.join('|', @{$config{wiki_file_prune_regexps}}).')';
-       return $file =~ m/$regexp/;
+       if (defined $config{include} && length $config{include}) {
+               return 0 if $file =~ m/$config{include}/;
+       }
+
+       if (! defined $file_prune_regexp) {
+               $file_prune_regexp='('.join('|', @{$config{wiki_file_prune_regexps}}).')';
+               $file_prune_regexp=qr/$file_prune_regexp/;
+       }
+       return $file =~ m/$file_prune_regexp/;
 }
 
 sub define_gettext () {
        # If translation is needed, redefine the gettext function to do it.
        # Otherwise, it becomes a quick no-op.
-       no warnings 'redefine';
+       my $gettext_obj;
+       my $getobj;
        if ((exists $ENV{LANG} && length $ENV{LANG}) ||
            (exists $ENV{LC_ALL} && length $ENV{LC_ALL}) ||
            (exists $ENV{LC_MESSAGES} && length $ENV{LC_MESSAGES})) {
-               *gettext=sub {
-                       my $gettext_obj=eval q{
+               $getobj=sub {
+                       $gettext_obj=eval q{
                                use Locale::gettext q{textdomain};
                                Locale::gettext->domain('ikiwiki')
                        };
-
-                       if ($gettext_obj) {
-                               $gettext_obj->get(shift);
-                       }
-                       else {
-                               return shift;
-                       }
                };
        }
-       else {
-               *gettext=sub { return shift };
-       }
+
+       no warnings 'redefine';
+       *gettext=sub {
+               $getobj->() if $getobj;
+               if ($gettext_obj) {
+                       $gettext_obj->get(shift);
+               }
+               else {
+                       return shift;
+               }
+       };
+       *ngettext=sub {
+               $getobj->() if $getobj;
+               if ($gettext_obj) {
+                       $gettext_obj->nget(@_);
+               }
+               else {
+                       return ($_[2] == 1 ? $_[0] : $_[1])
+               }
+       };
 }
 
 sub gettext {
@@ -1856,6 +1907,11 @@ sub gettext {
        gettext(@_);
 }
 
+sub ngettext {
+       define_gettext();
+       ngettext(@_);
+}
+
 sub yesno ($) {
        my $val=shift;
 
@@ -1887,12 +1943,82 @@ sub inject {
        use warnings;
 }
 
-sub add_link ($$) {
+sub add_link ($$;$) {
        my $page=shift;
        my $link=shift;
+       my $type=shift;
 
        push @{$links{$page}}, $link
                unless grep { $_ eq $link } @{$links{$page}};
+
+       if (defined $type) {
+               $typedlinks{$page}{$type}{$link} = 1;
+       }
+}
+
+sub sortspec_translate ($$) {
+       my $spec = shift;
+       my $reverse = shift;
+
+       my $code = "";
+       my @data;
+       while ($spec =~ m{
+               \s*
+               (-?)            # group 1: perhaps negated
+               \s*
+               (               # group 2: a word
+                       \w+\([^\)]*\)   # command(params)
+                       |
+                       [^\s]+          # or anything else
+               )
+               \s*
+       }gx) {
+               my $negated = $1;
+               my $word = $2;
+               my $params = undef;
+
+               if ($word =~ m/^(\w+)\((.*)\)$/) {
+                       # command with parameters
+                       $params = $2;
+                       $word = $1;
+               }
+               elsif ($word !~ m/^\w+$/) {
+                       error(sprintf(gettext("invalid sort type %s"), $word));
+               }
+
+               if (length $code) {
+                       $code .= " || ";
+               }
+
+               if ($negated) {
+                       $code .= "-";
+               }
+
+               if (exists $IkiWiki::SortSpec::{"cmp_$word"}) {
+                       if (defined $params) {
+                               push @data, $params;
+                               $code .= "IkiWiki::SortSpec::cmp_$word(\$data[$#data])";
+                       }
+                       else {
+                               $code .= "IkiWiki::SortSpec::cmp_$word(undef)";
+                       }
+               }
+               else {
+                       error(sprintf(gettext("unknown sort type %s"), $word));
+               }
+       }
+
+       if (! length $code) {
+               # undefined sorting method... sort arbitrarily
+               return sub { 0 };
+       }
+
+       if ($reverse) {
+               $code="-($code)";
+       }
+
+       no warnings;
+       return eval 'sub { '.$code.' }';
 }
 
 sub add_autofile ($$) {
@@ -1986,7 +2112,7 @@ sub pagespec_match ($$;@) {
 
        my $sub=pagespec_translate($spec);
        return IkiWiki::ErrorReason->new("syntax error in pagespec \"$spec\"")
-               if $@ || ! defined $sub;
+               if ! defined $sub;
        return $sub->($page, @params);
 }
 
@@ -2004,7 +2130,9 @@ sub pagespec_match_list ($$;@) {
 
        my $sub=pagespec_translate($pagespec);
        error "syntax error in pagespec \"$pagespec\""
-               if $@ || ! defined $sub;
+               if ! defined $sub;
+       my $sort=sortspec_translate($params{sort}, $params{reverse})
+               if defined $params{sort};
 
        my @candidates;
        if (exists $params{list}) {
@@ -2017,39 +2145,19 @@ sub pagespec_match_list ($$;@) {
                        ? grep { ! $params{filter}->($_) } keys %pagesources
                        : keys %pagesources;
        }
-
-       if (defined $params{sort}) {
-               my $f;
-               if ($params{sort} eq 'title') {
-                       $f=sub { pagetitle(basename($a)) cmp pagetitle(basename($b)) };
-               }
-               elsif ($params{sort} eq 'title_natural') {
-                       eval q{use Sort::Naturally};
-                       if ($@) {
-                               error(gettext("Sort::Naturally needed for title_natural sort"));
-                       }
-                       $f=sub { Sort::Naturally::ncmp(pagetitle(basename($a)), pagetitle(basename($b))) };
-                }
-               elsif ($params{sort} eq 'mtime') {
-                       $f=sub { $pagemtime{$b} <=> $pagemtime{$a} };
-               }
-               elsif ($params{sort} eq 'age') {
-                       $f=sub { $pagectime{$b} <=> $pagectime{$a} };
-               }
-               else {
-                       error sprintf(gettext("unknown sort type %s"), $params{sort});
-               }
-               @candidates = sort { &$f } @candidates;
-       }
-
-       @candidates=reverse(@candidates) if $params{reverse};
-       
-       $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT);
        
        # clear params, remainder is passed to pagespec
+       $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT);
        my $num=$params{num};
        delete @params{qw{num deptype reverse sort filter list}};
        
+       # when only the top matches will be returned, it's efficient to
+       # sort before matching to pagespec,
+       if (defined $num && defined $sort) {
+               @candidates=IkiWiki::SortSpec::sort_pages(
+                       $sort, @candidates);
+       }
+       
        my @matches;
        my $firstfail;
        my $count=0;
@@ -2071,14 +2179,21 @@ sub pagespec_match_list ($$;@) {
                $depends_simple{$page}{lc $k} |= $i->{$k};
        }
 
-       return @matches;
+       # when all matches will be returned, it's efficient to
+       # sort after matching
+       if (! defined $num && defined $sort) {
+               return IkiWiki::SortSpec::sort_pages(
+                       $sort, @matches);
+       }
+       else {
+               return @matches;
+       }
 }
 
 sub pagespec_valid ($) {
        my $spec=shift;
 
-       my $sub=pagespec_translate($spec);
-       return ! $@;
+       return defined pagespec_translate($spec);
 }
 
 sub glob2re ($) {
@@ -2198,26 +2313,34 @@ sub match_link ($$;@) {
 
        $link=derel($link, $params{location});
        my $from=exists $params{location} ? $params{location} : '';
+       my $linktype=$params{linktype};
+       my $qualifier='';
+       if (defined $linktype) {
+               $qualifier=" with type $linktype";
+       }
 
        my $links = $IkiWiki::links{$page};
-       return IkiWiki::FailReason->new("$page has no links", "" => 1)
+       return IkiWiki::FailReason->new("$page has no links", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
                unless $links && @{$links};
        my $bestlink = IkiWiki::bestlink($from, $link);
        foreach my $p (@{$links}) {
                if (length $bestlink) {
-                       return IkiWiki::SuccessReason->new("$page links to $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
-                               if $bestlink eq IkiWiki::bestlink($page, $p);
+                       if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p}) && $bestlink eq IkiWiki::bestlink($page, $p)) {
+                               return IkiWiki::SuccessReason->new("$page links to $link$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
+                       }
                }
                else {
-                       return IkiWiki::SuccessReason->new("$page links to page $p matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
-                               if match_glob($p, $link, %params);
+                       if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p}) && match_glob($p, $link, %params)) {
+                               return IkiWiki::SuccessReason->new("$page links to page $p$qualifier, matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
+                       }
                        my ($p_rel)=$p=~/^\/?(.*)/;
                        $link=~s/^\///;
-                       return IkiWiki::SuccessReason->new("$page links to page $p_rel matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
-                               if match_glob($p_rel, $link, %params);
+                       if ((!defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p_rel}) && match_glob($p_rel, $link, %params)) {
+                               return IkiWiki::SuccessReason->new("$page links to page $p_rel$qualifier, matching $link", $page => $IkiWiki::DEPEND_LINKS, "" => 1)
+                       }
                }
        }
-       return IkiWiki::FailReason->new("$page does not link to $link", "" => 1);
+       return IkiWiki::FailReason->new("$page does not link to $link$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1);
 }
 
 sub match_backlink ($$;@) {
@@ -2298,11 +2421,13 @@ sub match_user ($$;@) {
        my $user=shift;
        my %params=@_;
        
+       my $regexp=IkiWiki::glob2re($user);
+       
        if (! exists $params{user}) {
                return IkiWiki::ErrorReason->new("no user specified");
        }
 
-       if (defined $params{user} && lc $params{user} eq lc $user) {
+       if (defined $params{user} && $params{user}=~/^$regexp$/i) {
                return IkiWiki::SuccessReason->new("user is $user");
        }
        elsif (! defined $params{user}) {
@@ -2350,4 +2475,22 @@ sub match_ip ($$;@) {
        }
 }
 
+package IkiWiki::SortSpec;
+
+# This is in the SortSpec namespace so that the $a and $b that sort() uses
+# are easily available in this namespace, for cmp functions to use them.
+sub sort_pages {
+       my $f=shift;
+       sort $f @_
+}
+
+sub cmp_title {
+       IkiWiki::pagetitle(IkiWiki::basename($a))
+       cmp
+       IkiWiki::pagetitle(IkiWiki::basename($b))
+}
+
+sub cmp_mtime { $IkiWiki::pagemtime{$b} <=> $IkiWiki::pagemtime{$a} }
+sub cmp_age { $IkiWiki::pagectime{$b} <=> $IkiWiki::pagectime{$a} }
+
 1
index 85486e5..8adfd5d 100644 (file)
@@ -21,6 +21,7 @@ sub getsetup () {
                        # server admin action too
                        safe => 0,
                        rebuild => 0,
+                       section => "web",
                }
 }
 
index 3571c41..cfd8cd3 100644 (file)
@@ -132,6 +132,10 @@ sub getbucket {
                });
        }
 
+       if (! $bucket) {
+               # Try to use existing bucket.
+               $bucket=$s3->bucket($config{amazon_s3_bucket});
+       }
        if (! $bucket) {
                error(gettext("Failed to create S3 bucket: ").
                        $s3->err.": ".$s3->errstr."\n");
@@ -178,7 +182,7 @@ sub writefile ($$$;$$) {
 
        # First, write the file to disk.
        my $ret=$IkiWiki::Plugin::amazon_s3::subs{'IkiWiki::writefile'}->($file, $destdir, $content, $binary, $writer);
-               
+
        my @keys=IkiWiki::Plugin::amazon_s3::file2keys("$destdir/$file");
 
        # Store the data in S3.
index 243b989..0e74cbf 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                anonok_pagespec => {
                        type => "pagespec",
index cbe6efc..ad1dd9b 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                },
                allowed_attachments => {
                        type => "pagespec",
index 555856b..c71d733 100644 (file)
@@ -61,8 +61,16 @@ sub refresh () {
        }
        
        my %deleted;
-        if (ref $pagestate{index}{autoindex}{deleted}) {
-              %deleted=%{$pagestate{index}{autoindex}{deleted}};
+       if (ref $wikistate{autoindex}{deleted}) {
+               %deleted=%{$wikistate{autoindex}{deleted}};
+       }
+        elsif (ref $pagestate{index}{autoindex}{deleted}) {
+               # compatability code
+               %deleted=%{$pagestate{index}{autoindex}{deleted}};
+               delete $pagestate{index}{autoindex};
+       }
+
+       if (keys %deleted) {
                foreach my $dir (keys %deleted) {
                        # remove deleted page state if the deleted page is re-added,
                        # or if all its subpages are deleted
@@ -71,7 +79,7 @@ sub refresh () {
                                delete $deleted{$dir};
                        }
                }
-               $pagestate{index}{autoindex}{deleted}=\%deleted;
+               $wikistate{autoindex}{deleted}=\%deleted;
        }
 
        my @needed;
@@ -82,10 +90,10 @@ sub refresh () {
                                # This page must have just been deleted, so
                                # don't re-add it. And remember it was
                                # deleted.
-                               if (! ref $pagestate{index}{autoindex}{deleted}) {
-                                       $pagestate{index}{autoindex}{deleted}={};
+                               if (! ref $wikistate{autoindex}{deleted}) {
+                                       $wikistate{autoindex}{deleted}={};
                                }
-                               ${$pagestate{index}{autoindex}{deleted}}{$dir}=1;
+                               ${$wikistate{autoindex}{deleted}}{$dir}=1;
                        }
                        else {
                                push @needed, $dir;
index 626c8ec..c4e5cf3 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                blogspam_pagespec => {
                        type => 'pagespec',
index 8830073..f79ca7c 100644 (file)
@@ -20,6 +20,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub checkconfig () {
@@ -36,6 +37,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                bzr_wrapper => {
                        type => "string",
@@ -72,31 +74,40 @@ sub bzr_log ($) {
        my @infos = ();
        my $key = undef;
 
+       my %info;
        while (<$out>) {
                my $line = $_;
                my ($value);
                if ($line =~ /^message:/) {
                        $key = "message";
-                       $infos[$#infos]{$key} = "";
+                       $info{$key} = "";
                }
                elsif ($line =~ /^(modified|added|renamed|renamed and modified|removed):/) {
                        $key = "files";
-                       unless (defined($infos[$#infos]{$key})) { $infos[$#infos]{$key} = ""; }
+                       $info{$key} = "" unless defined $info{$key};
                }
                elsif (defined($key) and $line =~ /^  (.*)/) {
-                       $infos[$#infos]{$key} .= "$1\n";
+                       $info{$key} .= "$1\n";
                }
                elsif ($line eq "------------------------------------------------------------\n") {
+                       push @infos, {%info} if keys %info;
+                       %info = ();
                        $key = undef;
-                       push (@infos, {});
                }
-               else {
+               elsif ($line =~ /: /) {
                        chomp $line;
+                       if ($line =~ /^revno: (\d+)/) {
+                           $key = "revno";
+                           $value = $1;
+                       }
+                       else {
                                ($key, $value) = split /: +/, $line, 2;
-                       $infos[$#infos]{$key} = $value;
-               } 
+                       }
+                       $info{$key} = $value;
+               }
        }
        close $out;
+       push @infos, {%info} if keys %info;
 
        return @infos;
 }
@@ -212,7 +223,7 @@ sub rcs_recentchanges ($) {
        foreach my $info (bzr_log($out)) {
                my @pages = ();
                my @message = ();
-        
+
                foreach my $msgline (split(/\n/, $info->{message})) {
                        push @message, { line => $msgline };
                }
@@ -296,4 +307,8 @@ sub rcs_getctime ($) {
        return $ctime;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for bzr\n"; # TODO
+}
+
 1
index 2b87451..aeb5f3d 100644 (file)
@@ -22,7 +22,7 @@ use warnings;
 use strict;
 use IkiWiki 3.00;
 use Time::Local;
-use POSIX;
+use POSIX ();
 
 my $time=time;
 my @now=localtime($time);
@@ -38,6 +38,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
                archivebase => {
                        type => "string",
@@ -46,6 +47,14 @@ sub getsetup () {
                        safe => 1,
                        rebuild => 1,
                },
+               archive_pagespec => {
+                       type => "pagespec",
+                       example => "posts/* and !*/Discussion",
+                       description => "PageSpec of pages to include in the archives; used by ikiwiki-calendar command",
+                       link => 'ikiwiki/PageSpec',
+                       safe => 1,
+                       rebuild => 0,
+               },
 }
 
 sub is_leap_year (@) {
@@ -155,11 +164,11 @@ sub format_month (@) {
        # Start producing the month calendar
        $calendar=<<EOF;
 <table class="month-calendar">
-       <caption class="month-calendar-head">
-       $purl
-       $url
-       $nurl
-       </caption>
+       <tr>
+       <th class="month-calendar-arrow">$purl</th>
+       <th class="month-calendar-head" colspan="5">$url</th>
+       <th class="month-calendar-arrow">$nurl</th>
+       </tr>
        <tr>
 EOF
 
@@ -303,13 +312,14 @@ sub format_year (@) {
        add_depends($params{page}, "$archivebase/$nyear", deptype("presence"));
 
        # Start producing the year calendar
+       my $m=$params{months_per_row}-2;
        $calendar=<<EOF;
 <table class="year-calendar">
-       <caption class="year-calendar-head">
-       $purl
-       $url
-       $nurl
-       </caption>
+       <tr>
+       <th class="year-calendar-arrow">$purl</th>
+       <th class="year-calendar-head" colspan="$m">$url</th>
+       <th class="year-calendar-arrow">$nurl</th>
+       </tr>
        <tr>
                <th class="year-calendar-subhead" colspan="$params{months_per_row}">Months</th>
        </tr>
index 2050589..d550dd9 100644 (file)
@@ -10,6 +10,16 @@ use IkiWiki 3.00;
 sub import {
        hook(type => "preprocess", id => "color", call => \&preprocess);
        hook(type => "format",     id => "color", call => \&format);
+       hook(type => "getsetup",   id => "color", call => \&getsetup);
+}
+
+sub getsetup () {
+       return
+               plugin => {
+                       safe => 1,
+                       rebuild => undef,
+                       section => "widget",
+               },
 }
 
 sub preserve_style ($$$) {
index 5586cca..0aa0432 100644 (file)
@@ -38,6 +38,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "web",
                },
                comments_pagespec => {
                        type => 'pagespec',
@@ -171,9 +172,8 @@ sub preprocess {
                else {
                        $commentauthorurl = IkiWiki::cgiurl(
                                do => 'goto',
-                               page => (length $config{userdir}
-                                       ? "$config{userdir}/$commentuser"
-                                       : "$commentuser"));
+                               page => IkiWiki::userpage($commentuser)
+                       );
 
                        $commentauthor = $commentuser;
                }
@@ -221,7 +221,9 @@ sub preprocess {
        }
 
        if (defined $params{subject}) {
-               $pagestate{$page}{meta}{title} = $params{subject};
+               # decode title the same way meta does
+               eval q{use HTML::Entities};
+               $pagestate{$page}{meta}{title} = decode_entities($params{subject});
        }
 
        if ($params{page} =~ m/\/\Q$config{comments_pagename}\E\d+_/) {
@@ -736,39 +738,43 @@ sub pagetemplate (@) {
                }
 
                if ($shown && commentsopen($page)) {
-                       my $addcommenturl = IkiWiki::cgiurl(do => 'comment',
-                               page => $page);
-                       $template->param(addcommenturl => $addcommenturl);
+                       $template->param(addcommenturl => addcommenturl($page));
                }
        }
 
-       if ($template->query(name => 'commentsurl')) {
-               if ($shown) {
+       if ($shown) {
+               if ($template->query(name => 'commentsurl')) {
                        $template->param(commentsurl =>
                                urlto($page, undef, 1).'#comments');
                }
-       }
 
-       if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) {
-               if ($shown) {
+               if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) {
                        # This will 404 until there are some comments, but I
                        # think that's probably OK...
                        $template->param(atomcommentsurl =>
                                urlto($page, undef, 1).'comments.atom');
                }
-       }
 
-       if ($template->query(name => 'commentslink')) {
-               # XXX Would be nice to say how many comments there are in
-               # the link. But, to update the number, blog pages
-               # would have to update whenever comments of any inlines
-               # page are added, which is not currently done.
-               if ($shown) {
-                       $template->param(commentslink =>
-                               htmllink($page, $params{destpage}, $page,
-                                       linktext => gettext("Comments"),
+               if ($template->query(name => 'commentslink')) {
+                       my $num=num_comments($page, $config{srcdir});
+                       my $link;
+                       if ($num > 0) {
+                               $link = htmllink($page, $params{destpage}, $page,
+                                       linktext => sprintf(ngettext("%i comment", "%i comments", $num), $num),
                                        anchor => "comments",
-                                       noimageinline => 1));
+                                       noimageinline => 1
+                               );
+                       }
+                       elsif (commentsopen($page)) {
+                               $link = "<a href=\"".addcommenturl($page)."\">".
+                                       #translators: Here "Comment" is a verb;
+                                       #translators: the user clicks on it to
+                                       #translators: post a comment.
+                                       gettext("Comment").
+                                       "</a>";
+                       }
+                       $template->param(commentslink => $link)
+                               if defined $link;
                }
        }
 
@@ -816,6 +822,12 @@ sub pagetemplate (@) {
        }
 }
 
+sub addcommenturl ($) {
+       my $page=shift;
+
+       return IkiWiki::cgiurl(do => 'comment', page => $page);
+}
+
 sub num_comments ($$) {
        my $page=shift;
        my $dir=shift;
@@ -829,7 +841,7 @@ sub unique_comment_location ($$$) {
 
        eval q{use Digest::MD5 'md5_hex'};
        error($@) if $@;
-       my $content_md5=md5_hex(shift);
+       my $content_md5=md5_hex(Encode::encode_utf8(shift));
 
        my $dir=shift;
 
@@ -845,14 +857,14 @@ sub unique_comment_location ($$$) {
 
 sub page_to_id ($) {
        # Converts a comment page name into a unique, legal html id
-       # addtibute value, that can be used as an anchor to link to the
+       # attribute value, that can be used as an anchor to link to the
        # comment.
        my $page=shift;
 
        eval q{use Digest::MD5 'md5_hex'};
        error($@) if $@;
 
-       return "comment-".md5_hex($page);
+       return "comment-".md5_hex(Encode::encode_utf8(($page)));
 }
        
 package IkiWiki::PageSpec;
index aad6178..8a57961 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
@@ -29,7 +30,7 @@ sub preprocess_if (@) {
        }
 
        my $result=0;
-       if (! IkiWiki::yesno($params{all}) ||
+       if ((exists $params{all} && ! IkiWiki::yesno($params{all})) ||
            # An optimisation to avoid needless looping over every page
            # for simple uses of some of the tests.
            $params{test} =~ /^([\s\!()]*((enabled|sourcepage|destpage|included)\([^)]*\)|(and|or))[\s\!()]*)+$/) {
index 425e710..a1e4b31 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
index 417442f..01e9ce0 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index f6db8bc..360d972 100644 (file)
@@ -49,6 +49,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub genwrapper () {
@@ -85,6 +86,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                cvsrepo => {
                        type => "string",
@@ -484,4 +486,8 @@ sub rcs_getctime ($) {
        return $date;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for cvs\n"; # TODO
+}
+
 1
index 0d68f27..c1d6661 100644 (file)
@@ -18,6 +18,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub silentsystem (@) {
@@ -51,7 +52,7 @@ sub darcs_info ($$$) {
        return $_;
 }
 
-sub file_in_vc($$) {
+sub file_in_vc ($$) {
        my $repodir = shift;
        my $file = shift;
 
@@ -69,7 +70,7 @@ sub file_in_vc($$) {
        return $found;
 }
 
-sub darcs_rev($) {
+sub darcs_rev ($) {
        my $file = shift; # Relative to the repodir.
        my $repodir = $config{srcdir};
 
@@ -78,7 +79,7 @@ sub darcs_rev($) {
        return defined $hash ? $hash : "";
 }
 
-sub checkconfig() {
+sub checkconfig () {
        if (defined $config{darcs_wrapper} && length $config{darcs_wrapper}) {
                push @{$config{wrappers}}, {
                        wrapper => $config{darcs_wrapper},
@@ -87,11 +88,12 @@ sub checkconfig() {
        }
 }
 
-sub getsetup() {
+sub getsetup () {
        return
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                darcs_wrapper => {
                        type => "string",
@@ -237,7 +239,7 @@ sub rcs_commit ($$$;$$) {
        }
 }
 
-sub rcs_commit_staged($$$) {
+sub rcs_commit_staged ($$$) {
        my ($message, $user, $ipaddr) = @_;
 
        my $author;
@@ -426,4 +428,8 @@ sub rcs_getctime ($) {
        return $date;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for darcs\n"; # TODO
+}
+
 1
index 652e6df..ea5c9a9 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 7df6a9f..d8f53a4 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                },
 }
 
index fca970c..44fe551 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "core",
                },
 }
 
@@ -245,8 +246,9 @@ sub cgi_editpage ($$) {
                                        push @page_locs, $dir.$page;
                                }
                        
-                               push @page_locs, "$config{userdir}/$page"
-                                       if length $config{userdir};
+                               my $userpage=IkiWiki::userpage($page);
+                               push @page_locs, $userpage
+                                       if ! grep { $_ eq $userpage } @page_locs;
                        }
 
                        @page_locs = grep {
@@ -271,8 +273,10 @@ sub cgi_editpage ($$) {
                                check_canedit($_, $q, $session, 1)
                        } @page_locs;
                        if (! @editable_locs) {
-                               # let it throw an error this time
-                               map { check_canedit($_, $q, $session) } @page_locs;
+                               # now let it throw an error, or prompt for
+                               # login
+                               map { check_canedit($_, $q, $session) }
+                                       ($best_loc, @page_locs);
                        }
                        
                        my @page_types;
index a163b0d..5f0551d 100644 (file)
@@ -23,6 +23,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "web",
                },
 }
 
index 01d4909..1549b82 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 use strict;
 use IkiWiki 3.00;
 
-my %units=( #{{{       # size in bytes
+my %units=(            # size in bytes
        B               => 1,
        byte            => 1,
        KB              => 2 ** 10,
@@ -75,9 +75,9 @@ sub match_maxsize ($$;@) {
        }
 
        my %params=@_;
-       my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
+       my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
        if (! defined $file) {
-               return IkiWiki::ErrorReason->new("no file specified");
+               return IkiWiki::ErrorReason->new("file does not exist");
        }
 
        if (-s $file > $maxsize) {
@@ -96,9 +96,9 @@ sub match_minsize ($$;@) {
        }
 
        my %params=@_;
-       my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
+       my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
        if (! defined $file) {
-               return IkiWiki::ErrorReason->new("no file specified");
+               return IkiWiki::ErrorReason->new("file does not exist");
        }
 
        if (-s $file < $minsize) {
@@ -114,9 +114,9 @@ sub match_mimetype ($$;@) {
        my $wanted=shift;
 
        my %params=@_;
-       my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
+       my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
        if (! defined $file) {
-               return IkiWiki::ErrorReason->new("no file specified");
+               return IkiWiki::ErrorReason->new("file does not exist");
        }
 
        # Use ::magic to get the mime type, the idea is to only trust
@@ -147,9 +147,9 @@ sub match_virusfree ($$;@) {
        my $wanted=shift;
 
        my %params=@_;
-       my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
+       my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
        if (! defined $file) {
-               return IkiWiki::ErrorReason->new("no file specified");
+               return IkiWiki::ErrorReason->new("file does not exist");
        }
 
        if (! exists $IkiWiki::config{virus_checker} ||
index 1513cbe..d54e711 100644 (file)
@@ -7,6 +7,16 @@ use IkiWiki 3.00;
 
 sub import {
        hook(type => "preprocess", id => "format", call => \&preprocess);
+       hook(type => "getsetup",   id => "format", call => \&getsetup);
+}
+
+sub getsetup () {
+       return
+               plugin => {
+                       safe => 1,
+                       rebuild => undef,
+                       section => "widget",
+               },
 }
 
 sub preprocess (@) {
index 17e57de..f481c7e 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index d155543..b362de7 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "web",
                },
                getsource_mimetype => {
                        type => "string",
index 1eec6ae..aa402c0 100644 (file)
@@ -25,6 +25,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
        hook(type => "rcs", id => "rcs_receive", call => \&rcs_receive);
 }
 
@@ -68,6 +69,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                git_wrapper => {
                        type => "string",
@@ -614,23 +616,51 @@ sub rcs_diff ($) {
        }
 }
 
-sub rcs_getctime ($) {
+{
+my %time_cache;
+
+sub findtimes ($$) {
        my $file=shift;
+       my $id=shift; # 0 = mtime ; 1 = ctime
+
        # Remove srcdir prefix
        $file =~ s/^\Q$config{srcdir}\E\/?//;
 
-       my @raw_lines = run_or_die('git', 'log', 
-               '--follow', '--no-merges',
-               '--pretty=raw', '--raw', '--abbrev=40', '--always', '-c',
-               '-r', '--', $file);
-       my @ci;
-       while (my $parsed = parse_diff_tree("", \@raw_lines)) {
-               push @ci, $parsed;
+       if (! keys %time_cache) {
+               my $date;
+               foreach my $line (run_or_die('git', 'log',
+                               '--pretty=format:%ct',
+                               '--name-only', '--relative')) {
+                       if (! defined $date && $line =~ /^(\d+)$/) {
+                               $date=$line;
+                       }
+                       elsif (! length $line) {
+                               $date=undef;
+                       }
+                       else {
+                               if (! $time_cache{$line}) {
+                                       $time_cache{$line}[0]=$date; # mtime
+                               }
+                               $time_cache{$line}[1]=$date; # ctime
+                       }
+               }
        }
-       my $ctime = $ci[$#ci]->{'author_epoch'};
-       debug("ctime for '$file': ". localtime($ctime));
 
-       return $ctime;
+       return exists $time_cache{$file} ? $time_cache{$file}[$id] : 0;
+}
+
+}
+
+sub rcs_getctime ($) {
+       my $file=shift;
+
+       return findtimes($file, 1);
+}
+
+sub rcs_getmtime ($) {
+       my $file=shift;
+
+       return findtimes($file, 0);
 }
 
 sub rcs_receive () {
index 483fa17..48ad4c8 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "web",
                },
 }
 
index 439552f..03bd682 100644 (file)
@@ -14,6 +14,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                }
 }
 
index 32e994d..bec1220 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 5a062a2..bf23dce 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 9bdde85..e517ac5 100644 (file)
@@ -23,6 +23,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
                tohighlight => {
                        type => "string",
@@ -79,7 +80,7 @@ my %highlighters;
 
 # Parse highlight's config file to get extension => language mappings.
 sub read_filetypes () {
-       open (IN, $filetypes);
+       open (IN, $filetypes) || error("$filetypes: $!");
        while (<IN>) {
                chomp;
                if (/^\$ext\((.*)\)=(.*)$/) {
index bd2177a..32c9cf3 100644 (file)
@@ -23,6 +23,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
index a7d5e8c..4dbae08 100644 (file)
@@ -21,6 +21,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
index a249cdf..9f40c75 100644 (file)
@@ -30,9 +30,9 @@ sub import {
                "msnim", "notes", "rsync", "secondlife", "skype", "ssh",
                "sftp", "smb", "sms", "snews", "webcal", "ymsgr",
        );
-       # data is a special case. Allow data:image/*, but
-       # disallow data:text/javascript and everything else.
-       $safe_url_regexp=qr/^(?:(?:$uri_schemes):|data:image\/|[^:]+(?:$|\/))/i;
+       # data is a special case. Allow a few data:image/ types,
+       # but disallow data:text/javascript and everything else.
+       $safe_url_regexp=qr/^(?:(?:$uri_schemes):|data:image\/(?:png|jpeg|gif)|[^:]+(?:$|[\/\?]))/i;
 }
 
 sub getsetup () {
@@ -40,6 +40,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "core",
                },
                htmlscrubber_skip => {
                        type => "pagespec",
index 127c321..478f674 100644 (file)
@@ -9,6 +9,10 @@ use IkiWiki 3.00;
 sub import {
        hook(type => "getsetup", id => "httpauth", call => \&getsetup);
        hook(type => "auth", id => "httpauth", call => \&auth);
+       hook(type => "formbuilder_setup", id => "httpauth",
+               call => \&formbuilder_setup);
+       hook(type => "canedit", id => "httpauth", call => \&canedit,
+               first => 1);
 }
 
 sub getsetup () {
@@ -16,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                cgiauthurl => {
                        type => "string",
@@ -24,6 +29,23 @@ sub getsetup () {
                        safe => 1,
                        rebuild => 0,
                },
+               httpauth_pagespec => {
+                       type => "pagespec",
+                       example => "!*/Discussion",
+                       description => "PageSpec of pages where only httpauth will be used for authentication",
+                       safe => 0,
+                       rebuild => 0,
+               },
+}
+                       
+sub redir_cgiauthurl ($;@) {
+       my $cgi=shift;
+
+       IkiWiki::redirect($cgi, 
+               @_ > 1 ? IkiWiki::cgiurl(cgiurl => $config{cgiauthurl}, @_)
+                      : $config{cgiauthurl}."?@_"
+       );
+       exit;
 }
 
 sub auth ($$) {
@@ -33,9 +55,53 @@ sub auth ($$) {
        if (defined $cgi->remote_user()) {
                $session->param("name", $cgi->remote_user());
        }
-       elsif (defined $config{cgiauthurl}) {
-               IkiWiki::redirect($cgi, $config{cgiauthurl}.'?'.$cgi->query_string());
-               exit;
+}
+
+sub formbuilder_setup (@) {
+       my %params=@_;
+
+       my $form=$params{form};
+       my $session=$params{session};
+       my $cgi=$params{cgi};
+       my $buttons=$params{buttons};
+
+       if ($form->title eq "signin" &&
+           ! defined $cgi->remote_user() && defined $config{cgiauthurl}) {
+               my $button_text="Login with HTTP auth";
+               push @$buttons, $button_text;
+
+               if ($form->submitted && $form->submitted eq $button_text) {
+                       # bounce thru cgiauthurl and then back to
+                       # the stored postsignin action
+                       redir_cgiauthurl($cgi, do => "postsignin");
+               }
+       }
+}
+
+sub test_httpauth_pagespec ($) {
+       my $page=shift;
+
+       return (
+       );
+}
+
+sub canedit ($$$) {
+       my $page=shift;
+       my $cgi=shift;
+       my $session=shift;
+
+       if (! defined $cgi->remote_user() &&
+           defined $config{httpauth_pagespec} &&
+           length $config{httpauth_pagespec} &&
+           defined $config{cgiauthurl} &&
+           pagespec_match($page, $config{httpauth_pagespec})) {
+               return sub {
+                       # bounce thru cgiauthurl and back to edit action
+                       redir_cgiauthurl($cgi, $cgi->query_string());
+               };
+       }
+       else {
+               return undef;
        }
 }
 
index 82db15a..f061215 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 4018525..3359af3 100644 (file)
@@ -49,6 +49,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "core",
                },
                rss => {
                        type => "boolean",
@@ -159,7 +160,7 @@ sub preprocess_inline (@) {
        my $rss=(($config{rss} || $config{allowrss}) && exists $params{rss}) ? yesno($params{rss}) : $config{rss};
        my $atom=(($config{atom} || $config{allowatom}) && exists $params{atom}) ? yesno($params{atom}) : $config{atom};
        my $quick=exists $params{quick} ? yesno($params{quick}) : 0;
-       my $feeds=! $nested && (exists $params{feeds} ? yesno($params{feeds}) : !$quick);
+       my $feeds=! $nested && (exists $params{feeds} ? yesno($params{feeds}) : !$quick && ! $raw);
        my $emptyfeeds=exists $params{emptyfeeds} ? yesno($params{emptyfeeds}) : 1;
        my $feedonly=yesno($params{feedonly});
        if (! exists $params{show} && ! $archive) {
@@ -552,7 +553,8 @@ sub genfeed ($$$$$@) {
 
                if (exists $pagestate{$p}) {
                        if (exists $pagestate{$p}{meta}{guid}) {
-                               $itemtemplate->param(guid => $pagestate{$p}{meta}{guid});
+                               eval q{use HTML::Entities};
+                               $itemtemplate->param(guid => HTML::Entities::encode_numeric($pagestate{$p}{meta}{guid}));
                        }
 
                        if (exists $pagestate{$p}{meta}{updated}) {
index 4c1add9..3838aec 100644 (file)
@@ -20,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "core",
                },
 }
 
index 68eb6c8..ac26e07 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 09f08c5..8a67f71 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
                directive_description_dir => {
                        type => "string",
index 74ddbb1..1466e83 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                locked_pages => {
                        type => "pagespec",
index 788b968..ce3ac1d 100644 (file)
@@ -21,6 +21,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 3de59ef..b892eab 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
                multimarkdown => {
                        type => "boolean",
index 11fdec5..34e009c 100644 (file)
@@ -20,6 +20,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub checkconfig () {
@@ -36,6 +37,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                mercurial_wrapper => {
                        type => "string",
@@ -253,4 +255,8 @@ sub rcs_getctime ($) {
        return $ctime;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for mercurial\n"; # TODO
+}
+
 1
index 55c9ddb..128a634 100644 (file)
@@ -20,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "core",
                },
 }
 
@@ -87,15 +88,21 @@ sub preprocess (@) {
 
        # Metadata collection that needs to happen during the scan pass.
        if ($key eq 'title') {
-               $pagestate{$page}{meta}{title}=HTML::Entities::encode_numeric($value);
+               $pagestate{$page}{meta}{title}=$value;
+               if (exists $params{sortas}) {
+                       $pagestate{$page}{meta}{titlesort}=$params{sortas};
+               }
+               else {
+                       delete $pagestate{$page}{meta}{titlesort};
+               }
                return "";
        }
        elsif ($key eq 'description') {
-               $pagestate{$page}{meta}{description}=HTML::Entities::encode_numeric($value);
+               $pagestate{$page}{meta}{description}=$value;
                # fallthrough
        }
        elsif ($key eq 'guid') {
-               $pagestate{$page}{meta}{guid}=HTML::Entities::encode_numeric($value);
+               $pagestate{$page}{meta}{guid}=$value;
                # fallthrough
        }
        elsif ($key eq 'license') {
@@ -115,6 +122,12 @@ sub preprocess (@) {
        }
        elsif ($key eq 'author') {
                $pagestate{$page}{meta}{author}=$value;
+               if (exists $params{sortas}) {
+                       $pagestate{$page}{meta}{authorsort}=$params{sortas};
+               }
+               else {
+                       delete $pagestate{$page}{meta}{authorsort};
+               }
                # fallthorough
        }
        elsif ($key eq 'authorurl') {
@@ -263,15 +276,20 @@ sub pagetemplate (@) {
                $template->param(meta => join("\n", grep { (! $seen{$_}) && ($seen{$_}=1) } @{$metaheaders{$page}}));
        }
        if (exists $pagestate{$page}{meta}{title} && $template->query(name => "title")) {
-               $template->param(title => $pagestate{$page}{meta}{title});
+               $template->param(title => HTML::Entities::encode_numeric($pagestate{$page}{meta}{title}));
                $template->param(title_overridden => 1);
        }
 
-       foreach my $field (qw{author authorurl description permalink}) {
+       foreach my $field (qw{author authorurl permalink}) {
                $template->param($field => $pagestate{$page}{meta}{$field})
                        if exists $pagestate{$page}{meta}{$field} && $template->query(name => $field);
        }
 
+       foreach my $field (qw{description}) {
+               $template->param($field => HTML::Entities::encode_numeric($pagestate{$page}{meta}{$field}))
+                       if exists $pagestate{$page}{meta}{$field} && $template->query(name => $field);
+       }
+
        foreach my $field (qw{license copyright}) {
                if (exists $pagestate{$page}{meta}{$field} && $template->query(name => $field) &&
                    ($page eq $destpage || ! exists $pagestate{$destpage}{meta}{$field} ||
@@ -281,6 +299,33 @@ sub pagetemplate (@) {
        }
 }
 
+sub get_sort_key {
+       my $page = shift;
+       my $meta = shift;
+
+       # e.g. titlesort (also makes sense for author)
+       my $key = $pagestate{$page}{meta}{$meta . "sort"};
+       return $key if defined $key;
+
+       # e.g. title
+       $key = $pagestate{$page}{meta}{$meta};
+       return $key if defined $key;
+
+       # fall back to closer-to-core things
+       if ($meta eq 'title') {
+               return pagetitle(IkiWiki::basename($page));
+       }
+       elsif ($meta eq 'date') {
+               return $IkiWiki::pagectime{$page};
+       }
+       elsif ($meta eq 'updated') {
+               return $IkiWiki::pagemtime{$page};
+       }
+       else {
+               return '';
+       }
+}
+
 sub match {
        my $field=shift;
        my $page=shift;
@@ -331,4 +376,27 @@ sub match_copyright ($$;@) {
        IkiWiki::Plugin::meta::match("copyright", @_);
 }
 
+package IkiWiki::SortSpec;
+
+sub cmp_meta {
+       my $meta = shift;
+       error(gettext("sort=meta requires a parameter")) unless defined $meta;
+
+       if ($meta eq 'updated' || $meta eq 'date') {
+               return IkiWiki::Plugin::meta::get_sort_key($a, $meta)
+                       <=>
+                       IkiWiki::Plugin::meta::get_sort_key($b, $meta);
+       }
+
+       return IkiWiki::Plugin::meta::get_sort_key($a, $meta)
+               cmp
+               IkiWiki::Plugin::meta::get_sort_key($b, $meta);
+}
+
+# A prototype of how sort=title could behave in 4.0 or something
+sub cmp_meta_title {
+       $_[0] = 'title';
+       return cmp_meta(@_);
+}
+
 1
index d0a6107..92be791 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "web",
                },
                mirrorlist => {
                        type => "string",
index 2555927..b0a328a 100644 (file)
@@ -15,11 +15,13 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
-               moderate_users => {
-                       type => 'boolean',
-                       example => 1,
-                       description => 'Moderate comments of logged-in users?',
+               moderate_pagespec => {
+                       type => 'pagespec',
+                       example => 'user(http://*)',
+                       description => 'PageSpec matching users or comment locations to moderate',
+                       link => 'ikiwiki/PageSpec',
                        safe => 1,
                        rebuild => 0,
                },
@@ -31,14 +33,32 @@ sub checkcontent (@) {
        # only handle comments  
        return undef unless pagespec_match($params{page}, "postcomment(*)",
                                location => $params{page});
+       
+       # backwards compatability
+       if (exists $config{moderate_users} &&
+           ! exists $config{moderate_pagespec}) {
+               $config{moderate_pagespec} = $config{moderate_users}
+                       ? "!admin()"
+                       : "!user(*)";
+       }
+
+       # default is to moderate all except admins
+       if (! exists $config{moderate_pagespec}) {
+               $config{moderate_pagespec}="!admin()";
+       }
 
-       # admins and maybe users can comment w/o moderation
        my $session=$params{session};
        my $user=$session->param("name") if $session;
-       return undef if defined $user && (IkiWiki::is_admin($user) ||
-               (exists $config{moderate_users} && ! $config{moderate_users}));
-
-       return gettext("comment needs moderation");
+       if (pagespec_match($params{page}, $config{moderate_pagespec},
+                       location => $params{page},
+                       (defined $user ? (user => $user) : ()),
+                       (defined $ENV{REMOTE_ADDR} ? (ip => $ENV{REMOTE_ADDR}) : ()),
+       )) {
+               return gettext("comment needs moderation");
+       }
+       else {
+               return undef;
+       }
 }
 
 1
index 9502804..67d4abb 100644 (file)
@@ -23,6 +23,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub checkconfig () {
@@ -68,6 +69,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                mtn_wrapper => {
                        type => "string",
@@ -692,4 +694,8 @@ sub rcs_getctime ($) {
        return $date;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for monotone\n"; # TODO
+}
+
 1
index 77d5fb0..266c8e1 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index bfe84c0..053652a 100644 (file)
@@ -18,6 +18,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub getsetup () {
@@ -25,6 +26,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => 0,
+                       section => "rcs",
                },
 }
 
@@ -62,7 +64,11 @@ sub rcs_diff ($) {
 }
 
 sub rcs_getctime ($) {
-       error gettext("getctime not implemented");
+       return 0;
+}
+
+sub rcs_getmtime ($) {
+       return 0;
 }
 
 1
index 4517ff8..2805f60 100644 (file)
@@ -7,7 +7,8 @@ use IkiWiki 3.00;
 
 sub import {
        hook(type => "getsetup", id => "opendiscussion", call => \&getsetup);
-       hook(type => "canedit", id => "opendiscussion", call => \&canedit);
+       hook(type => "canedit", id => "opendiscussion", call => \&canedit,
+               first => 1);
 }
 
 sub getsetup () {
@@ -15,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
 }
 
@@ -24,6 +26,7 @@ sub canedit ($$) {
        my $session=shift;
 
        return "" if $page=~/(\/|^)\Q$config{discussionpage}\E$/i;
+       return "" if pagespec_match($page, "postcomment(*)");
        return undef;
 }
 
index dc0e0f4..7b1a178 100644 (file)
@@ -26,6 +26,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                openidsignup => {
                        type => "string",
@@ -56,7 +57,7 @@ sub formbuilder_setup (@) {
                # OpenID fieldset.
                $form->fieldsets("OpenID");
 
-               $form->field(
+               $form->field(
                        name => "openid_url",
                        label => gettext("Log in with")." ".htmllink("", "", "ikiwiki/OpenID", noimageinline => 1),
                        fieldset => "OpenID",
@@ -82,13 +83,14 @@ sub formbuilder_setup (@) {
                        }
                }
        }
-       elsif ($form->title eq "preferences") {
-               if (! defined $form->field(name => "name")) {
-                       $form->field(name => "OpenID", disabled => 1,
-                               value => $session->param("name"), 
-                               size => 50, force => 1,
-                               fieldset => "login");
-               }
+       elsif ($form->title eq "preferences" &&
+              IkiWiki::openiduser($session->param("name"))) {
+               $form->field(name => "openid_url", disabled => 1,
+                       label => htmllink("", "", "ikiwiki/OpenID", noimageinline => 1),
+                       value => $session->param("name"), 
+                       size => 50, force => 1,
+                       fieldset => "login");
+               $form->field(name => "email", type => "hidden");
        }
 }
 
@@ -112,6 +114,28 @@ sub validate ($$$;$) {
                }
        }
 
+       # Ask for client to provide a name and email, if possible.
+       # Try sreg and ax
+       if ($claimed_identity->can("set_extension_args")) {
+               $claimed_identity->set_extension_args(
+                       'http://openid.net/extensions/sreg/1.1',
+                       {
+                               optional => 'email,fullname,nickname',
+                       },
+               );
+               $claimed_identity->set_extension_args(
+                       'http://openid.net/srv/ax/1.0',
+                       {
+                               mode => 'fetch_request',
+                               'required' => 'email,fullname,nickname,firstname',
+                               'type.email' => "http://schema.openid.net/contact/email",
+                               'type.fullname' => "http://axschema.org/namePerson",
+                               'type.nickname' => "http://axschema.org/namePerson/friendly",
+                               'type.firstname' => "http://axschema.org/namePerson/first",
+                       },
+               );
+       }
+
        my $check_url = $claimed_identity->check_url(
                return_to => IkiWiki::cgiurl(do => "postsignin"),
                trust_root => $config{cgiurl},
@@ -138,6 +162,32 @@ sub auth ($$) {
                }
                elsif (my $vident = $csr->verified_identity) {
                        $session->param(name => $vident->url);
+
+                       my @extensions;
+                       if ($vident->can("signed_extension_fields")) {
+                               @extensions=grep { defined } (
+                                       $vident->signed_extension_fields('http://openid.net/extensions/sreg/1.1'),
+                                       $vident->signed_extension_fields('http://openid.net/srv/ax/1.0'),
+                               );
+                       }
+                       foreach my $ext (@extensions) {
+                               foreach my $field (qw{value.email email}) {
+                                       if (exists $ext->{$field} &&
+                                           defined $ext->{$field} &&
+                                           length $ext->{$field}) {
+                                               $session->param(email => $ext->{$field});
+                                               last;
+                                       }
+                               }
+                               foreach my $field (qw{value.nickname nickname value.fullname fullname value.firstname}) {
+                                       if (exists $ext->{$field} &&
+                                           defined $ext->{$field} &&
+                                           length $ext->{$field}) {
+                                               $session->param(username => $ext->{$field});
+                                               last;
+                                       }
+                               }
+                       }
                }
                else {
                        error("OpenID failure: ".$csr->err);
index 702943f..e3cc3c9 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 3ab2441..3801a6e 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
index 8d36f05..dd5de3c 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 4313aa2..17b26f7 100644 (file)
@@ -27,6 +27,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
@@ -74,7 +75,7 @@ sub preprocess (@) {
        }
 
        if ($style eq 'table') {
-               return "<table class='pageStats'>\n".
+               return "<table class='".(exists $params{class} ? $params{class} : "pageStats")."'>\n".
                        join("\n", map {
                                "<tr><td>".
                                htmllink($params{page}, $params{destpage}, $_, noimageinline => 1).
@@ -86,16 +87,31 @@ sub preprocess (@) {
        else {
                # In case of misspelling, default to a page cloud
 
-               my $res = "<div class='pagecloud'>\n";
+               my $res;
+               if ($style eq 'list') {
+                       $res = "<ul class='".(exists $params{class} ? $params{class} : "list")."'>\n";
+               }
+               else {
+                       $res = "<div class='".(exists $params{class} ? $params{class} : "pagecloud")."'>\n";
+               }
                foreach my $page (sort keys %counts) {
                        next unless $counts{$page} > 0;
 
                        my $class = $classes[$counts{$page} * scalar(@classes) / ($max + 1)];
+                       
+                       $res.="<li>" if $style eq 'list';
                        $res .= "<span class=\"$class\">".
                                htmllink($params{page}, $params{destpage}, $page).
                                "</span>\n";
+                       $res.="</li>" if $style eq 'list';
+
+               }
+               if ($style eq 'list') {
+                       $res .= "</ul>\n";
+               }
+               else {
+                       $res .= "</div>\n";
                }
-               $res .= "</div>\n";
 
                return $res;
        }
index e678a05..728bbc3 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "core",
                },
 }
 
index 8cf5af5..4848b47 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
                account_creation_password => {
                        type => "string",
@@ -104,11 +105,11 @@ sub formbuilder_setup (@) {
        my $session=$params{session};
        my $cgi=$params{cgi};
 
-       if ($form->title eq "signin" || $form->title eq "register") {
+       if ($form->title eq "signin" || $form->title eq "register" || $cgi->param("do") eq "register") {
                $form->field(name => "name", required => 0);
                $form->field(name => "password", type => "password", required => 0);
                
-               if ($form->submitted eq "Register" || $form->submitted eq "Create Account") {
+               if ($form->submitted eq "Register" || $form->submitted eq "Create Account" || $cgi->param("do") eq "register") {
                        $form->field(name => "confirm_password", type => "password");
                        $form->field(name => "account_creation_password", type => "password")
                                 if (defined $config{account_creation_password} &&
@@ -207,19 +208,34 @@ sub formbuilder_setup (@) {
                }
        }
        elsif ($form->title eq "preferences") {
-               $form->field(name => "name", disabled => 1, 
-                       value => $session->param("name"), force => 1,
-                       fieldset => "login");
-               $form->field(name => "password", type => "password",
-                       fieldset => "login",
-                       validate => sub {
-                               shift eq $form->field("confirm_password");
-                       }),
-               $form->field(name => "confirm_password", type => "password",
-                       fieldset => "login",
-                       validate => sub {
-                               shift eq $form->field("password");
-                       }),
+               my $user=$session->param("name");
+               if (! IkiWiki::openiduser($user)) {
+                       $form->field(name => "name", disabled => 1, 
+                               value => $user, force => 1,
+                               fieldset => "login");
+                       $form->field(name => "password", type => "password",
+                               fieldset => "login",
+                               validate => sub {
+                                       shift eq $form->field("confirm_password");
+                               });
+                       $form->field(name => "confirm_password", type => "password",
+                               fieldset => "login",
+                               validate => sub {
+                                       shift eq $form->field("password");
+                               });
+                       
+                       my $userpage=IkiWiki::userpage($user);
+                       if (exists $pagesources{$userpage}) {
+                               $form->text(gettext("Your user page: ").
+                                       htmllink("", "", $userpage,
+                                               noimageinline => 1));
+                       }
+                       else {
+                               $form->text("<a href=\"".
+                                       IkiWiki::cgiurl(do => "edit", page => $userpage).
+                                       "\">".gettext("Create your user page")."</a>");
+                       }
+               }
        }
 }
 
@@ -232,7 +248,7 @@ sub formbuilder (@) {
        my $buttons=$params{buttons};
 
        if ($form->title eq "signin" || $form->title eq "register") {
-               if ($form->submitted && $form->validate) {
+               if (($form->submitted && $form->validate) || $cgi->param("do") eq "register") {
                        if ($form->submitted eq 'Login') {
                                $session->param("name", $form->field("name"));
                                IkiWiki::cgi_postsignin($cgi, $session);
@@ -295,7 +311,7 @@ sub formbuilder (@) {
                                $form->field(name => "name", required => 0);
                                push @$buttons, "Reset Password";
                        }
-                       elsif ($form->submitted eq "Register") {
+                       elsif ($form->submitted eq "Register" || $cgi->param("do") eq "register") {
                                @$buttons="Create Account";
                        }
                }
@@ -336,6 +352,14 @@ sub sessioncgi ($$) {
                IkiWiki::cgi_prefs($q, $session);
                exit;
        }
+       elsif ($q->param("do") eq "register") {
+               # After registration, need to go somewhere, so show prefs page.
+               $session->param(postsignin => "do=prefs");
+               # Due to do=register, this will run in registration-only
+               # mode.
+               IkiWiki::cgi_signin($q, $session);
+               exit;
+       }
 }
 
 sub auth ($$) {
index f880118..759e812 100644 (file)
@@ -85,7 +85,8 @@ sub getsetup () {
        return
                plugin => {
                        safe => 0,
-                       rebuild => 1,
+                       rebuild => 1, # format plugin
+                       section => "format",
                },
                po_master_language => {
                        type => "string",
@@ -132,6 +133,7 @@ sub checkconfig () {
                                      $field, 'po'));
                }
        }
+       delete $config{po_slave_languages}{$config{po_master_language}{code}};;
 
        map {
                islanguagecode($_)
@@ -173,7 +175,8 @@ sub checkconfig () {
                if ($config{po_master_language}{code} ne 'en') {
                        # Add underlay containing translated source files
                        # for the master language.
-                       add_underlay("locale/$config{po_master_language}{code}/$underlay");
+                       add_underlay("locale/$config{po_master_language}{code}/$underlay")
+                               if -d "$config{underlaydirbase}/locale/$config{po_master_language}{code}/$underlay";
                }
        }
 }
@@ -307,7 +310,7 @@ sub pagetemplate (@) {
        if (ishomepage($page) && $template->query(name => "title")) {
                $template->param(title => $config{wikiname});
        }
-} # }}}
+}
 
 # Add the renamed page translations to the list of to-be-renamed pages.
 sub renamepages (@) {
index bc1e350..6bc4579 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index bc21d71..78e3611 100644 (file)
@@ -20,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 0d5a12e..2fae9c5 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index fe64b40..d27df5c 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index ad8a610..0838bcb 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # changes file types
+                       section => "format",
                },
 }
 
index fa851e4..5c7b71a 100644 (file)
@@ -124,7 +124,7 @@ sub store ($$$) {
        elsif (length $config{cgiurl}) {
                $change->{authorurl} = IkiWiki::cgiurl(
                        do => "goto",
-                       page => (length $config{userdir} ? "$config{userdir}/" : "").$change->{author},
+                       page => IkiWiki::userpage($change->{author}),
                );
        }
 
index 06df2ef..7f006af 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 no warnings 'redefine';
 use strict;
 use IkiWiki 3.00;
-use POSIX;
+use POSIX ();
 use Encode;
 
 sub import {
index 3c1e0c7..0fc180f 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                },
 }
 
@@ -48,7 +49,7 @@ sub check_canremove ($$$) {
        # This is sorta overkill, but better safe than sorry.
        if (! defined pagetype($pagesources{$page})) {
                if (IkiWiki::Plugin::attachment->can("check_canattach")) {
-                       IkiWiki::Plugin::attachment::check_canattach($session, $page, $file);
+                       IkiWiki::Plugin::attachment::check_canattach($session, $page, "$config{srcdir}/$file");
                }
                else {
                        error("removal of attachments is not allowed");
index 8213d21..1a9da63 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                },
 }
 
@@ -49,7 +50,7 @@ sub check_canrename ($$$$$$) {
        IkiWiki::check_canedit($src, $q, $session);
        if ($attachment) {
                if (IkiWiki::Plugin::attachment->can("check_canattach")) {
-                       IkiWiki::Plugin::attachment::check_canattach($session, $src, $srcfile);
+                       IkiWiki::Plugin::attachment::check_canattach($session, $src, "$config{srcdir}/$srcfile");
                }
                else {
                        error("renaming of attachments is not allowed");
@@ -84,7 +85,7 @@ sub check_canrename ($$$$$$) {
                if ($attachment) {
                        # Note that $srcfile is used here, not $destfile,
                        # because it wants the current file, to check it.
-                       IkiWiki::Plugin::attachment::check_canattach($session, $dest, $srcfile);
+                       IkiWiki::Plugin::attachment::check_canattach($session, $dest, "$config{srcdir}/$srcfile");
                }
        }
 
index f69ec39..ba7c5f0 100644 (file)
@@ -15,6 +15,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "web",
                },
                repositories => {
                        type => "string",
index 393c17e..c0e8703 100644 (file)
@@ -20,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1,
+                       section => "web",
                },
                omega_cgi => {
                        type => "string",
@@ -109,6 +110,7 @@ sub index (@) {
        # data used by omega
        # Decode html entities in it, since omega re-encodes them.
        eval q{use HTML::Entities};
+       error $@ if $@;
        $doc->set_data(
                "url=".urlto($params{page}, "")."\n".
                "sample=".decode_entities($sample)."\n".
@@ -213,9 +215,19 @@ sub setupfiles () {
                writefile("omega.conf", $config{wikistatedir}."/xapian",
                        "database_dir .\n".
                        "template_dir ./templates\n");
+               
+               # Avoid omega interpreting anything in the misctemplate
+               # as an omegascript command.
+               my $misctemplate=IkiWiki::misctemplate(gettext("search"), "\0");
+               eval q{use HTML::Entities};
+               error $@ if $@;
+               $misctemplate=encode_entities($misctemplate, '\$');
+
+               my $querytemplate=readfile(IkiWiki::template_file("searchquery.tmpl"));
+               $misctemplate=~s/\0/$querytemplate/;
+
                writefile("query", $config{wikistatedir}."/xapian/templates",
-                       IkiWiki::misctemplate(gettext("search"),
-                               readfile(IkiWiki::template_file("searchquery.tmpl"))));
+                       $misctemplate);
                $setup=1;
        }
 }
index 1840a57..0cedbe4 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 41812e1..0350f2b 100644 (file)
@@ -10,6 +10,7 @@ use IkiWiki 3.00;
 
 sub import {
        hook(type => "getsetup", id => "sidebar", call => \&getsetup);
+       hook(type => "preprocess", id => "sidebar", call => \&preprocess);
        hook(type => "pagetemplate", id => "sidebar", call => \&pagetemplate);
 }
 
@@ -19,11 +20,51 @@ sub getsetup () {
                        safe => 1,
                        rebuild => 1,
                },
+               global_sidebars => {
+                       type => "boolean",
+                       examples => 1,
+                       description => "show sidebar page on all pages?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+}
+
+my %pagesidebar;
+
+sub preprocess (@) {
+       my %params=@_;
+
+       my $page=$params{page};
+       return "" unless $page eq $params{destpage};
+       
+       if (! defined $params{content}) {
+               $pagesidebar{$page}=undef;
+       }
+       else {
+               my $file = $pagesources{$page};
+               my $type = pagetype($file);
+
+               $pagesidebar{$page}=
+                       IkiWiki::htmlize($page, $page, $type,
+                       IkiWiki::linkify($page, $page,
+                       IkiWiki::preprocess($page, $page,
+                       IkiWiki::filter($page, $page, $params{content}))));
+       }
+
+       return "";
 }
 
+my $oldfile;
+my $oldcontent;
+
 sub sidebar_content ($) {
        my $page=shift;
        
+       return delete $pagesidebar{$page} if defined $pagesidebar{$page};
+
+       return if ! exists $pagesidebar{$page} && 
+               defined $config{global_sidebars} && ! $config{global_sidebars};
+
        my $sidebar_page=bestlink($page, "sidebar") || return;
        my $sidebar_file=$pagesources{$sidebar_page} || return;
        my $sidebar_type=pagetype($sidebar_file);
@@ -34,7 +75,16 @@ sub sidebar_content ($) {
                # currently requires a wiki rebuild.
                add_depends($page, $sidebar_page);
 
-               my $content=readfile(srcfile($sidebar_file));
+               my $content;
+               if (defined $oldfile && $sidebar_file eq $oldfile) {
+                       $content=$oldcontent;
+               }
+               else {
+                       $content=readfile(srcfile($sidebar_file));
+                       $oldcontent=$content;
+                       $oldfile=$sidebar_file;
+               }
+
                return unless length $content;
                return IkiWiki::htmlize($sidebar_page, $page, $sidebar_type,
                       IkiWiki::linkify($sidebar_page, $page,
@@ -47,11 +97,10 @@ sub sidebar_content ($) {
 sub pagetemplate (@) {
        my %params=@_;
 
-       my $page=$params{page};
        my $template=$params{template};
-       
-       if ($template->query(name => "sidebar")) {
-               my $content=sidebar_content($page);
+       if ($params{destpage} eq $params{page} &&
+           $template->query(name => "sidebar")) {
+               my $content=sidebar_content($params{destpage});
                if (defined $content && length $content) {
                        $template->param(sidebar => $content);
                }
index 8b44a68..31160c0 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "auth",
                },
 }
 
diff --git a/IkiWiki/Plugin/sortnaturally.pm b/IkiWiki/Plugin/sortnaturally.pm
new file mode 100644 (file)
index 0000000..62e4276
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+# Sort::Naturally-powered title_natural sort order for IkiWiki
+package IkiWiki::Plugin::sortnaturally;
+
+use IkiWiki 3.00;
+no warnings;
+
+sub import {
+       hook(type => "getsetup", id => "sortnaturally", call => \&getsetup);
+}
+
+sub getsetup {
+       return
+               plugin => {
+                       safe => 1,
+                       rebuild => undef,
+               },
+}
+
+sub checkconfig () {
+       eval q{use Sort::Naturally};
+       error $@ if $@;
+}
+
+package IkiWiki::SortSpec;
+
+sub cmp_title_natural {
+       Sort::Naturally::ncmp(IkiWiki::pagetitle(IkiWiki::basename($a)),
+               IkiWiki::pagetitle(IkiWiki::basename($b)))
+}
+
+1;
index fb48494..42665ac 100644 (file)
@@ -24,6 +24,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 06b987f..d10b488 100644 (file)
@@ -19,6 +19,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub checkconfig () {
@@ -44,6 +45,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                svnrepo => {
                        type => "string",
@@ -348,9 +350,18 @@ sub rcs_diff ($) {
        return `svnlook diff $config{svnrepo} -r$rev --no-diff-deleted`;
 }
 
-sub rcs_getctime ($) {
+{
+
+my ($lastfile, $lastmtime, $lastctime);
+
+sub findtimes ($) {
        my $file=shift;
 
+       if (defined $lastfile && $lastfile eq $file) {
+               return $lastmtime, $lastctime;
+       }
+       $lastfile=$file;
+
        my $svn_log_infoline=qr/^r\d+\s+\|\s+[^\s]+\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/;
                
        my $child = open(SVNLOG, "-|");
@@ -358,24 +369,39 @@ sub rcs_getctime ($) {
                exec("svn", "log", $file) || error("svn log $file failed to run");
        }
 
-       my $date;
+       my ($cdate, $mdate);
        while (<SVNLOG>) {
                if (/$svn_log_infoline/) {
-                       $date=$1;
+                       $cdate=$1;
+                       $mdate=$1 unless defined $mdate;
                }
        }
-       close SVNLOG || warn "svn log $file exited $?";
+       close SVNLOG || error "svn log $file exited $?";
 
-       if (! defined $date) {
-               warn "failed to parse svn log for $file\n";
-               return 0;
+       if (! defined $cdate) {
+               error "failed to parse svn log for $file\n";
        }
                
        eval q{use Date::Parse};
        error($@) if $@;
-       $date=str2time($date);
-       debug("found ctime ".localtime($date)." for $file");
-       return $date;
+       
+       $lastctime=str2time($cdate);
+       $lastmtime=str2time($mdate);
+       return $lastmtime, $lastctime;
+}
+
+}
+
+sub rcs_getctime ($) {
+       my $file=shift;
+
+       return (findtimes($file))[1];
+}
+
+sub rcs_getmtime ($) {
+       my $file=shift;
+
+       return (findtimes($file))[0];
 }
 
 1
index 96d63f4..2edd1ea 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index fdd63d6..9e6f417 100644 (file)
@@ -6,8 +6,6 @@ use warnings;
 use strict;
 use IkiWiki 3.00;
 
-my %tags;
-
 sub import {
        hook(type => "getopt", id => "tag", call => \&getopt);
        hook(type => "getsetup", id => "tag", call => \&getsetup);
@@ -94,13 +92,12 @@ sub preprocess_tag (@) {
 
        foreach my $tag (keys %params) {
                $tag=linkpage($tag);
-               $tags{$page}{$tag}=1;
-
+               
+               # hidden WikiLink
+               add_link($page, tagpage($tag), 'tag');
+               
                # add tagpage if necessary
                gentag($tag);
-
-               # hidden WikiLink
-               add_link($page, tagpage($tag));
        }
                
        return "";
@@ -114,15 +111,13 @@ sub preprocess_taglink (@) {
        return join(" ", map {
                if (/(.*)\|(.*)/) {
                        my $tag=linkpage($2);
-                       $tags{$params{page}}{$tag}=1;
-                       add_link($params{page}, tagpage($tag));
+                       add_link($params{page}, tagpage($tag), 'tag');
                        return taglink($params{page}, $params{destpage}, $tag,
                                linktext => pagetitle($1));
                }
                else {
                        my $tag=linkpage($_);
-                       $tags{$params{page}}{$tag}=1;
-                       add_link($params{page}, tagpage($tag));
+                       add_link($params{page}, tagpage($tag), 'tag');
                        return taglink($params{page}, $params{destpage}, $tag);
                }
        }
@@ -137,17 +132,19 @@ sub pagetemplate (@) {
        my $destpage=$params{destpage};
        my $template=$params{template};
 
+       my $tags = $typedlinks{$page}{tag};
+
        $template->param(tags => [
                map { 
                        link => taglink($page, $destpage, $_, rel => "tag")
-               }, sort keys %{$tags{$page}}
-       ]) if exists $tags{$page} && %{$tags{$page}} && $template->query(name => "tags");
+               }, sort keys %$tags
+       ]) if defined $tags && %$tags && $template->query(name => "tags");
 
        if ($template->query(name => "categories")) {
                # It's an rss/atom template. Add any categories.
-               if (exists $tags{$page} && %{$tags{$page}}) {
+               if (defined $tags && %$tags) {
                        $template->param(categories => [map { category => $_ },
-                               sort keys %{$tags{$page}}]);
+                               sort keys %$tags]);
                }
        }
 }
@@ -155,9 +152,7 @@ sub pagetemplate (@) {
 package IkiWiki::PageSpec;
 
 sub match_tagged ($$;@) {
-       my $page = shift;
-       my $glob = shift;
-       return match_link($page, IkiWiki::Plugin::tag::tagpage($glob));
+       return match_link($_[0], IkiWiki::Plugin::tag::tagpage($_[1]), linktype => 'tag');
 }
 
 1
index 39d9667..98a13b5 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
@@ -36,7 +37,13 @@ sub preprocess (@) {
        my $template_page="templates/$params{id}";
        add_depends($params{page}, $template_page);
 
-       my $template_file=$pagesources{$template_page};
+       my $template_file;
+       if (exists $pagesources{$template_page}) {
+               $template_file=srcfile($pagesources{$template_page});
+       }
+       else {
+               $template_file=IkiWiki::template_file("$params{id}.tmpl")
+       }
        return sprintf(gettext("template %s not found"),
                htmllink($params{page}, $params{destpage}, "/".$template_page))
                        unless defined $template_file;
@@ -49,7 +56,7 @@ sub preprocess (@) {
                                $$text_ref=&Encode::decode_utf8($$text_ref);
                                chomp $$text_ref;
                        },
-                       filename => srcfile($template_file),
+                       filename => $template_file,
                                die_on_bad_params => 0,
                        no_includes => 1,
                        blind_cache => 1,
index f92ed01..521af49 100644 (file)
@@ -8,6 +8,7 @@ use strict;
 use Digest::MD5 qw(md5_hex);
 use File::Temp qw(tempdir);
 use HTML::Entities;
+use Encode;
 use IkiWiki 3.00;
 
 my $default_prefix = <<EOPREFIX;
@@ -31,6 +32,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
                teximg_dvipng => {
                        type => "boolean",
@@ -102,7 +104,7 @@ sub create ($$$) {
                $height = 12;
        }
 
-       my $digest = md5_hex($code, $height);
+       my $digest = md5_hex(Encode::encode_utf8($code), $height);
 
        my $imglink= $params->{page} . "/$digest.png";
        my $imglog =  $params->{page} .  "/$digest.log";
index 8cc5a79..56bb4bf 100644 (file)
@@ -19,6 +19,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
index f4b20a6..f5ad0cc 100644 (file)
@@ -18,6 +18,7 @@ sub import {
        hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
        hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
        hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
+       hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
 }
 
 sub checkconfig () {
@@ -34,6 +35,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # rcs plugin
                        rebuild => undef,
+                       section => "rcs",
                },
                tla_wrapper => {
                        type => "string",
@@ -161,7 +163,7 @@ sub rcs_remove ($) {
        error("rcs_remove not implemented for tla"); # TODO
 }
 
-sub rcs_rename ($$) { # {{{a
+sub rcs_rename ($$) {
        my ($src, $dest) = @_;
 
        error("rcs_rename not implemented for tla"); # TODO
@@ -283,4 +285,8 @@ sub rcs_getctime ($) {
        return $date;
 }
 
+sub rcs_getmtime ($) {
+       error "rcs_getmtime is not implemented for tla\n"; # TODO
+}
+
 1
index b8537d3..ac07b9a 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index ef066a4..f9c8995 100644 (file)
@@ -20,6 +20,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index 8599bdc..0d9a0b3 100644 (file)
@@ -29,6 +29,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 1, # format plugin
+                       section => "format",
                },
 }
 
@@ -38,7 +39,14 @@ sub filter (@) {
        my %params = @_;
        my $content = $params{content};
 
-       if (defined $pagesources{$params{page}} && $pagesources{$params{page}} =~ /\.txt$/) {
+       if (defined $pagesources{$params{page}} &&
+           $pagesources{$params{page}} =~ /\.txt$/) {
+               if ($pagesources{$params{page}} eq 'robots.txt' &&
+                   $params{page} eq $params{destpage}) {
+                       will_render($params{page}, 'robots.txt');
+                       writefile('robots.txt', $config{destdir}, $content);
+               }
+
                encode_entities($content, "<>&");
                if ($findurl) {
                        my $finder = URI::Find->new(sub {
index f62be82..9389b24 100644 (file)
@@ -9,7 +9,7 @@ use IkiWiki 3.00;
 sub import {
        hook(type => "getopt", id => "typography", call => \&getopt);
        hook(type => "getsetup", id => "typography", call => \&getsetup);
-       IkiWiki::hook(type => "sanitize", id => "typography", call => \&sanitize);
+       hook(type => "sanitize", id => "typography", call => \&sanitize);
 }
 
 sub getopt () {
index 587cd55..c136434 100644 (file)
@@ -17,6 +17,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => undef,
+                       section => "widget",
                },
 }
 
index e477bcc..d444c0a 100644 (file)
@@ -18,6 +18,7 @@ sub getsetup () {
                plugin => {
                        safe => 1,
                        rebuild => 0,
+                       section => "web",
                },
                websetup_force_plugins => {
                        type => "string",
@@ -26,6 +27,13 @@ sub getsetup () {
                        safe => 0,
                        rebuild => 0,
                },
+               websetup_unsafe => {
+                       type => "string",
+                       example => [],
+                       description => "list of additional setup field keys to treat as unsafe",
+                       safe => 0,
+                       rebuild => 0,
+               },
                websetup_show_unsafe => {
                        type => "boolean",
                        example => 1,
@@ -56,6 +64,12 @@ sub formatexample ($$) {
        }
 }
 
+sub issafe ($) {
+       my $key=shift;
+
+       return ! grep { $_ eq $key } @{$config{websetup_unsafe}};
+}
+
 sub showfields ($$$@) {
        my $form=shift;
        my $plugin=shift;
@@ -66,27 +80,30 @@ sub showfields ($$$@) {
        while (@_) {
                my $key=shift;
                my %info=%{shift()};
+               
+               if ($key eq 'plugin') {
+                       %plugininfo=%info;
+                       next;
+               }
 
                # skip internal settings
                next if defined $info{type} && $info{type} eq "internal";
                # XXX hashes not handled yet
                next if ref $config{$key} && ref $config{$key} eq 'HASH' || ref $info{example} eq 'HASH';
                # maybe skip unsafe settings
-               next if ! $info{safe} && ! ($config{websetup_show_unsafe} && $config{websetup_advanced});
+               next if ! ($config{websetup_show_unsafe} && $config{websetup_advanced}) &&
+                       (! $info{safe} || ! issafe($key));
                # maybe skip advanced settings
                next if $info{advanced} && ! $config{websetup_advanced};
                # these are handled specially, so don't show
                next if $key eq 'add_plugins' || $key eq 'disable_plugins';
 
-               if ($key eq 'plugin') {
-                       %plugininfo=%info;
-                       next;
-               }
-               
                push @show, $key, \%info;
        }
 
-       my $section=defined $plugin ? $plugin." ".gettext("plugin") : "main";
+       my $section=defined $plugin
+               ? sprintf(gettext("%s plugin:"), $plugininfo{section}).$plugin
+               : "main";
        my %enabledfields;
        my $shownfields=0;
        
@@ -97,6 +114,16 @@ sub showfields ($$$@) {
                @show=();
        }
 
+       my $section_fieldset;
+       if (defined $plugin) {
+               # Define the combined fieldset for the plugin's section.
+               # This ensures that this fieldset comes first.
+               $section_fieldset=sprintf(gettext("%s plugins"), $plugininfo{section});
+               $form->field(name => "placeholder.$plugininfo{section}",
+                       type => "hidden",
+                       fieldset => $section_fieldset);
+       }
+
        # show plugin toggle
        if (defined $plugin && (! $plugin_forced || $config{websetup_advanced})) {
                my $name="enable.$plugin";
@@ -137,10 +164,13 @@ sub showfields ($$$@) {
                my $name=defined $plugin ? $plugin.".".$key : $section.".".$key;
 
                my $value=$config{$key};
+               if (! defined $value) {
+                       $value="";
+               }
 
-               if ($info{safe} && (ref $value eq 'ARRAY' || ref $info{example} eq 'ARRAY')) {
-                       $value=[(ref $value eq 'ARRAY' ? map { Encode::encode_utf8($_) }  @{$value} : ""),
-                               "", ""]; # blank items for expansion
+               if (ref $value eq 'ARRAY' || ref $info{example} eq 'ARRAY') {
+                       $value=[(ref $value eq 'ARRAY' ? map { Encode::encode_utf8($_) }  @{$value} : "")];
+                       push @$value, "", "" if $info{safe} && issafe($key); # blank items for expansion
                }
                else {
                        $value=Encode::encode_utf8($value);
@@ -194,7 +224,7 @@ sub showfields ($$$@) {
                        }
                }
                
-               if (! $info{safe}) {
+               if (! $info{safe} || ! issafe($key)) {
                        $form->field(name => $name, disabled => 1);
                }
                else {
@@ -203,11 +233,11 @@ sub showfields ($$$@) {
                $shownfields++;
        }
        
-       # if no fields were shown for the plugin, drop it into the
-       # plugins fieldset
+       # if no fields were shown for the plugin, drop it into a combined
+       # fieldset for its section
        if (defined $plugin && (! $plugin_forced || $config{websetup_advanced}) &&
            ! $shownfields) {
-               $form->field(name => "enable.$plugin", fieldset => "plugins");
+               $form->field(name => "enable.$plugin", fieldset => $section_fieldset);
        }
 
        return %enabledfields;
@@ -258,7 +288,6 @@ sub showform ($$) {
                params => $cgi,
                fieldsets => [
                        [main => gettext("main")], 
-                       [plugins => gettext("plugins")]
                ],
                action => $config{cgiurl},
                template => {type => 'div'},
@@ -331,7 +360,7 @@ sub showform ($$) {
                                @value=0;
                        }
                
-                       if (! $info{safe}) {
+                       if (! $info{safe} || ! issafe($key)) {
                                error("unsafe field $key"); # should never happen
                        }
                
index accb03b..b24630b 100644 (file)
@@ -16,6 +16,7 @@ sub getsetup () {
                plugin => {
                        safe => 0, # format plugin
                        rebuild => undef,
+                       section => "format",
                },
 }
 
index 9ddd237..99b1362 100644 (file)
@@ -4,7 +4,6 @@ package IkiWiki::Plugin::wmd;
 use warnings;
 use strict;
 use IkiWiki 3.00;
-use POSIX;
 use Encode;
 
 sub import {
@@ -17,6 +16,8 @@ sub getsetup () {
        return
                plugin => {
                        safe => 1,
+                       rebuild => 0,
+                       section => "web",
                },
 }
 
index 0c21455..796af6a 100644 (file)
@@ -167,6 +167,7 @@ sub scan ($) {
                else {
                        $links{$page}=[];
                }
+               delete $typedlinks{$page};
 
                run_hooks(scan => sub {
                        shift->(
@@ -346,6 +347,8 @@ sub find_new_files ($) {
        my @new;
        my @internal_new;
 
+       my $times_noted;
+
        foreach my $file (@$files) {
                my $page=pagename($file);
                if (exists $pagesources{$page} && $pagesources{$page} ne $file) {
@@ -357,16 +360,33 @@ sub find_new_files ($) {
                        if (isinternal($page)) {
                                push @internal_new, $file;
                        }
-                       else {
+                       elsif ($config{rcs}) {
+                               if (! $times_noted) {
+                                       debug(sprintf(gettext("querying %s for file creation and modification times.."), $config{rcs}));
+                                       $times_noted=1;
+                               }
+
                                push @new, $file;
-                               if ($config{getctime} && -e "$config{srcdir}/$file") {
+                               if ($config{gettime} && -e "$config{srcdir}/$file") {
                                        eval {
-                                               my $time=rcs_getctime("$config{srcdir}/$file");
-                                               $pagectime{$page}=$time;
+                                               my $ctime=rcs_getctime("$config{srcdir}/$file");
+                                               if ($ctime > 0) {
+                                                       $pagectime{$page}=$ctime;
+                                               }
                                        };
                                        if ($@) {
                                                print STDERR $@;
                                        }
+                                       my $mtime;
+                                       eval {
+                                               $mtime=rcs_getmtime("$config{srcdir}/$file");
+                                       };
+                                       if ($@) {
+                                               print STDERR $@;
+                                       }
+                                       elsif ($mtime > 0) {
+                                               utime($mtime, $mtime, "$config{srcdir}/$file");
+                                       }
                                }
                        }
                        $pagecase{lc $page}=$page;
@@ -393,6 +413,7 @@ sub find_del_files ($) {
                                push @del, $pagesources{$page};
                        }
                        $links{$page}=[];
+                       delete $typedlinks{$page};
                        $renderedfiles{$page}=[];
                        $pagemtime{$page}=0;
                }
@@ -494,6 +515,29 @@ sub remove_unrendered () {
        }
 }
 
+sub link_types_changed ($$) {
+       # each is of the form { type => { link => 1 } }
+       my $new = shift;
+       my $old = shift;
+
+       return 0 if !defined $new && !defined $old;
+       return 1 if !defined $new || !defined $old;
+
+       while (my ($type, $links) = each %$new) {
+               foreach my $link (keys %$links) {
+                       return 1 unless exists $old->{$type}{$link};
+               }
+       }
+
+       while (my ($type, $links) = each %$old) {
+               foreach my $link (keys %$links) {
+                       return 1 unless exists $new->{$type}{$link};
+               }
+       }
+
+       return 0;
+}
+
 sub calculate_changed_links ($$$) {
        my ($changed, $del, $oldlink_targets)=@_;
 
@@ -520,6 +564,14 @@ sub calculate_changed_links ($$$) {
                        }
                        $linkchangers{lc($page)}=1;
                }
+
+               # we currently assume that changing the type of a link doesn't
+               # change backlinks
+               if (!exists $linkchangers{lc($page)}) {
+                       if (link_types_changed($typedlinks{$page}, $oldtypedlinks{$page})) {
+                               $linkchangers{lc($page)}=1;
+                       }
+               }
        }
 
        return \%backlinkchanged, \%linkchangers;
@@ -560,7 +612,7 @@ sub render_dependent ($$$$$$$) {
                if (exists $depends{$p} && ! defined $reason) {
                        foreach my $dep (keys %{$depends{$p}}) {
                                my $sub=pagespec_translate($dep);
-                               next if $@ || ! defined $sub;
+                               next unless defined $sub;
 
                                # only consider internal files
                                # if the page explicitly depends
@@ -695,6 +747,17 @@ sub refresh () {
        }
 }
 
+sub clean_rendered {
+       lockwiki();
+       loadindex();
+       remove_unrendered();
+       foreach my $page (keys %oldrenderedfiles) {
+               foreach my $file (@{$oldrenderedfiles{$page}}) {
+                       prune($config{destdir}."/".$file);
+               }
+       }
+}
+
 sub commandline_render () {
        lockwiki();
        loadindex();
index 8a25ecc..0610205 100644 (file)
@@ -1,6 +1,8 @@
 #!/usr/bin/perl
-# Ikiwiki setup files are perl files that 'use IkiWiki::Setup::foo',
-# passing it some sort of configuration data.
+# Ikiwiki setup files can be perl files that 'use IkiWiki::Setup::foo',
+# passing it some sort of configuration data. Or, they can contain
+# the module name at the top, without the 'use', and the whole file is
+# then fed into that module.
 
 package IkiWiki::Setup;
 
@@ -10,24 +12,58 @@ use IkiWiki;
 use open qw{:utf8 :std};
 use File::Spec;
 
-sub load ($) {
-       my $setup=IkiWiki::possibly_foolish_untaint(shift);
-       $config{setupfile}=File::Spec->rel2abs($setup);
+sub load ($;$) {
+       my $file=IkiWiki::possibly_foolish_untaint(shift);
+       my $safemode=shift;
+
+       $config{setupfile}=File::Spec->rel2abs($file);
 
        #translators: The first parameter is a filename, and the second
        #translators: is a (probably not translated) error message.
-       open (IN, $setup) || error(sprintf(gettext("cannot read %s: %s"), $setup, $!));
-       my $code;
+       open (IN, $file) || error(sprintf(gettext("cannot read %s: %s"), $file, $!));
+       my $content;
        {
                local $/=undef;
-               $code=<IN> || error("$setup: $!");
+               $content=<IN> || error("$file: $!");
        }
-       
-       ($code)=$code=~/(.*)/s;
        close IN;
 
-       eval $code;
-       error("$setup: ".$@) if $@;
+       if ($content=~/((?:use|require)\s+)?IkiWiki::Setup::(\w+)/) {
+               $config{setuptype}=$2;
+               if ($1) {
+                       error sprintf(gettext("cannot load %s in safe mode"), $file)
+                               if $safemode;
+                       eval IkiWiki::possibly_foolish_untaint($content);
+                       error("$file: ".$@) if $@;
+               }
+               else {
+                       eval qq{require IkiWiki::Setup::$config{setuptype}};
+                       error $@ if $@;
+                       "IkiWiki::Setup::$config{setuptype}"->loaddump(IkiWiki::possibly_foolish_untaint($content));
+               }
+       }
+       else {
+               error sprintf(gettext("failed to parse %s"), $file);
+       }
+}
+
+sub dump ($) {
+       my $file=IkiWiki::possibly_foolish_untaint(shift);
+       
+       eval qq{require IkiWiki::Setup::$config{setuptype}};
+       error $@ if $@;
+       my @dump="IkiWiki::Setup::$config{setuptype}"->gendump(
+               "Setup file for ikiwiki.",
+               "",
+               "Passing this to ikiwiki --setup will make ikiwiki generate",
+               "wrappers and build the wiki.",
+               "",
+               "Remember to re-run ikiwiki --setup any time you edit this file.",
+       );
+
+       open (OUT, ">", $file) || die "$file: $!";
+       print OUT "$_\n" foreach @dump;
+       close OUT;
 }
 
 sub merge ($) {
@@ -77,7 +113,6 @@ sub merge ($) {
 sub getsetup () {
        # Gets all available setup data from all plugins. Returns an
        # ordered list of [plugin, setup] pairs.
-       my @ret;
 
         # disable logging to syslog while dumping, broken plugins may
        # whine when loaded
@@ -85,38 +120,116 @@ sub getsetup () {
         $config{syslog}=undef;
 
        # Load all plugins, so that all setup options are available.
-       my @plugins=grep { $_ ne $config{rcs} } sort(IkiWiki::listplugins());
-       unshift @plugins, $config{rcs} if $config{rcs}; # rcs plugin 1st
+       my @plugins=IkiWiki::listplugins();
        foreach my $plugin (@plugins) {
                eval { IkiWiki::loadplugin($plugin) };
                if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) {
                        my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() };
                }
        }
-
+       
+       my %sections;
        foreach my $plugin (@plugins) {
                if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) {
                        # use an array rather than a hash, to preserve order
                        my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() };
                        next unless @s;
-                       push @ret, [ $plugin, \@s ],
+
+                       # set default section value (note use of shared
+                       # hashref between array and hash)
+                       my %s=@s;
+                       if (! exists $s{plugin} || ! $s{plugin}->{section}) {
+                               $s{plugin}->{section}="other";
+                       }
+
+                       # only the selected rcs plugin is included
+                       if ($config{rcs} && $plugin eq $config{rcs}) {
+                               $s{plugin}->{section}="core";
+                       }
+                       elsif ($s{plugin}->{section} eq "rcs") {
+                               next;
+                       }
+
+                       push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ];
                }
        }
        
         $config{syslog}=$syslog;
 
-       return @ret;
+       return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} }
+               sort { # core first, other last, otherwise alphabetical
+                       ($b eq "core") <=> ($a eq "core")
+                          ||
+                       ($a eq "other") <=> ($b eq "other")
+                          ||
+                       $a cmp $b
+               } keys %sections;
 }
 
-sub dump ($) {
-       my $file=IkiWiki::possibly_foolish_untaint(shift);
+sub commented_dump ($$) {
+       my $dumpline=shift;
+       my $indent=shift;
+
+       my %setup=(%config);
+       my @ret;
        
-       require IkiWiki::Setup::Standard;
-       my @dump=IkiWiki::Setup::Standard::gendump("Setup file for ikiwiki.");
+       # disable logging to syslog while dumping
+       $config{syslog}=undef;
 
-       open (OUT, ">", $file) || die "$file: $!";
-       print OUT "$_\n" foreach @dump;
-       close OUT;
+       eval q{use Text::Wrap};
+       die $@ if $@;
+
+       my %section_plugins;
+       push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup());
+       foreach my $pair (IkiWiki::Setup::getsetup()) {
+               my $plugin=$pair->[0];
+               my $setup=$pair->[1];
+               my %s=@{$setup};
+               my $section=$s{plugin}->{section};
+               push @{$section_plugins{$section}}, $plugin;
+               if (@{$section_plugins{$section}} == 1) {
+                       push @ret, "", $indent.("#" x 70), "$indent# $section plugins",
+                               sub {
+                                       wrap("$indent#   (", "$indent#    ",
+                                               join(", ", @{$section_plugins{$section}})).")"
+                               },
+                               $indent.("#" x 70);
+               }
+
+               my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup});
+               if (@values) {
+                       push @ret, "", "$indent# $plugin plugin", @values;
+               }
+       }
+
+       return map { ref $_ ? $_->() : $_ } @ret;
+}
+
+sub commented_dumpvalues ($$$@) {
+       my $dumpline=shift;
+       my $indent=shift;
+       my $setup=shift;
+       my @ret;
+       while (@_) {
+               my $key=shift;
+               my %info=%{shift()};
+
+               next if $key eq "plugin" || $info{type} eq "internal";
+               
+               push @ret, "$indent# ".$info{description} if exists $info{description};
+               
+               if (exists $setup->{$key} && defined $setup->{$key}) {
+                       push @ret, $dumpline->($key, $setup->{$key}, $info{type}, "");
+                       delete $setup->{$key};
+               }
+               elsif (exists $info{example}) {
+                       push @ret, $dumpline->($key, $info{example}, $info{type}, "#");
+               }
+               else {
+                       push @ret, $dumpline->($key, "", $info{type}, "#");
+               }
+       }
+       return @ret;
 }
 
 1
index 9fee4dc..2dcb424 100644 (file)
@@ -38,19 +38,22 @@ sub sanitize_wikiname ($) {
 
 sub import (@) {
        my $this=shift;
+       $config{setuptype}='Standard';
        IkiWiki::Setup::merge({@_});
 
-       # Avoid overwriting any existing files.
-       foreach my $key (qw{srcdir destdir repository dumpsetup}) {
-               next unless exists $config{$key};
-               my $add="";
-               my $dir=IkiWiki::dirname($config{$key})."/";
-               my $base=IkiWiki::basename($config{$key});
-               while (-e $dir.$add.$base) {
-                       $add=1 if ! $add;
-                       $add++;
+       if (! $config{force_overwrite}) {
+               # Avoid overwriting any existing files.
+               foreach my $key (qw{srcdir destdir repository dumpsetup}) {
+                       next unless exists $config{$key};
+                       my $add="";
+                       my $dir=IkiWiki::dirname($config{$key})."/";
+                       my $base=IkiWiki::basename($config{$key});
+                       while (-e $dir.$add.$base) {
+                               $add=1 if ! $add;
+                               $add++;
+                       }
+                       $config{$key}=$dir.$add.$base;
                }
-               $config{$key}=$dir.$add.$base;
        }
        
        # Set up wrapper
@@ -69,9 +72,15 @@ sub import (@) {
                }
                elsif ($config{rcs} eq 'bzr') {
                        # TODO
+                       print STDERR "warning: do not know how to set up the bzr_wrapper hook!\n";
                }
                elsif ($config{rcs} eq 'mercurial') {
                        # TODO
+                       print STDERR "warning: do not know how to set up the mercurial_wrapper hook!\n";
+               }
+               elsif ($config{rcs} eq 'tla') {
+                       # TODO
+                       print STDERR "warning: do not know how to set up the tla_wrapper hook!\n";
                }
                elsif ($config{rcs} eq 'cvs') {
                        $config{cvs_wrapper}=$config{repository}."/CVSROOT/post-commit";
@@ -121,9 +130,10 @@ sub import (@) {
                                IkiWiki::run_hooks(checkconfig => sub { shift->() });
                        };
                        if ($@) {
+                               my $err=$@;
                                print STDERR sprintf(gettext("** Disabling plugin %s, since it is failing with this message:"),
                                        $plugin)."\n";
-                               print STDERR "$@\n";
+                               print STDERR "$err\n";
                                push @{$bakconfig{disable_plugins}}, $plugin;
                        }
                }
@@ -142,7 +152,7 @@ sub import (@) {
 
        # Create admin user(s).
        foreach my $admin (@{$config{adminuser}}) {
-               next if $admin=~/^http\?:\/\//; # openid
+               next if defined IkiWiki::openiduser($admin);
                
                # Prompt for password w/o echo.
                my ($password, $password2);
index 951bcfc..c850693 100644 (file)
@@ -1,7 +1,4 @@
 #!/usr/bin/perl
-# Standard ikiwiki setup module.
-# Parameters to import should be all the standard ikiwiki config stuff,
-# plus an array of wrappers to set up.
 
 package IkiWiki::Setup::Standard;
 
@@ -9,10 +6,22 @@ use warnings;
 use strict;
 use IkiWiki;
 
+# Parameters to import should be all the standard ikiwiki config, in a hash.
 sub import {
        IkiWiki::Setup::merge($_[1]);
 }
 
+sub gendump ($@) {
+       my $class=shift;
+
+       "#!/usr/bin/perl",
+       "#",
+       (map { "# $_" } @_),
+       "use IkiWiki::Setup::Standard {",
+       IkiWiki::Setup::commented_dump(\&dumpline, "\t"),
+       "}"
+}
+
 sub dumpline ($$$$) {
        my $key=shift;
        my $value=shift;
@@ -57,61 +66,4 @@ sub dumpline ($$$$) {
        return "\t$prefix$key => $dumpedvalue,";
 }
 
-sub dumpvalues ($@) {
-       my $setup=shift;
-       my @ret;
-       while (@_) {
-               my $key=shift;
-               my %info=%{shift()};
-
-               next if $key eq "plugin" || $info{type} eq "internal";
-               
-               push @ret, "\t# ".$info{description} if exists $info{description};
-               
-               if (exists $setup->{$key} && defined $setup->{$key}) {
-                       push @ret, dumpline($key, $setup->{$key}, $info{type}, "");
-                       delete $setup->{$key};
-               }
-               elsif (exists $info{example}) {
-                       push @ret, dumpline($key, $info{example}, $info{type}, "#");
-               }
-               else {
-                       push @ret, dumpline($key, "", $info{type}, "#");
-               }
-       }
-       return @ret;
-}
-
-sub gendump ($) {
-       my $description=shift;
-       my %setup=(%config);
-       my @ret;
-       
-       # disable logging to syslog while dumping
-       $config{syslog}=undef;
-
-       push @ret, dumpvalues(\%setup, IkiWiki::getsetup());
-       foreach my $pair (IkiWiki::Setup::getsetup()) {
-               my $plugin=$pair->[0];
-               my $setup=$pair->[1];
-               my @values=dumpvalues(\%setup, @{$setup});
-               if (@values) {
-                       push @ret, "", "\t# $plugin plugin", @values;
-               }
-       }
-
-       unshift @ret,
-               "#!/usr/bin/perl",
-               "# $description",
-               "#",
-               "# Passing this to ikiwiki --setup will make ikiwiki generate",
-               "# wrappers and build the wiki.",
-               "#",
-               "# Remember to re-run ikiwiki --setup any time you edit this file.",
-               "use IkiWiki::Setup::Standard {";
-       push @ret, "}";
-
-       return @ret;
-}
-
 1
diff --git a/IkiWiki/Setup/Yaml.pm b/IkiWiki/Setup/Yaml.pm
new file mode 100644 (file)
index 0000000..9047847
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+
+package IkiWiki::Setup::Yaml;
+
+use warnings;
+use strict;
+use IkiWiki;
+
+sub loaddump ($$) {
+       my $class=shift;
+       my $content=shift;
+
+       eval q{use YAML::Any};
+       eval q{use YAML} if $@;
+       die $@ if $@;
+       $YAML::Syck::ImplicitUnicode=1;
+       IkiWiki::Setup::merge(Load($content));
+}
+
+sub gendump ($@) {
+       my $class=shift;
+       
+       "# IkiWiki::Setup::Yaml - YAML formatted setup file",
+       "#",
+       (map { "# $_" } @_),
+       "#",
+       IkiWiki::Setup::commented_dump(\&dumpline, "")
+}
+
+
+sub dumpline ($$$$) {
+       my $key=shift;
+       my $value=shift;
+       my $type=shift;
+       my $prefix=shift;
+       
+       eval q{use YAML::Old};
+       eval q{use YAML} if $@;
+       die $@ if $@;
+       $YAML::UseHeader=0;
+
+       my $dump=Dump({$key => $value});
+       chomp $dump;
+       if (length $prefix) {
+               $dump=join("\n", map { $prefix.$_ } split(/\n/, $dump));
+       }
+       return $dump;
+}
+
+1
index 5427a5c..3f3e63c 100644 (file)
@@ -76,8 +76,8 @@ EOF
        {
                int fd=open("$config{wikistatedir}/cgilock", O_CREAT | O_RDWR, 0666);
                if (fd != -1 && flock(fd, LOCK_EX) == 0) {
-                       char *fd_s;
-                       asprintf(&fd_s, "%i", fd);
+                       char *fd_s=malloc(8);
+                       sprintf(fd_s, "%i", fd);
                        setenv("IKIWIKI_CGILOCK_FD", fd_s, 1);
                }
        }
@@ -105,7 +105,7 @@ extern char **environ;
 char *newenviron[$#envsave+6];
 int i=0;
 
-addenv(char *var, char *val) {
+void addenv(char *var, char *val) {
        char *s=malloc(strlen(var)+1+strlen(val)+1);
        if (!s)
                perror("malloc");
@@ -121,8 +121,18 @@ $check_commit_hook
 $envsave
        newenviron[i++]="HOME=$ENV{HOME}";
        newenviron[i++]="WRAPPED_OPTIONS=$configstring";
+
+#ifdef __TINYC__
+       if (clearenv() != 0) {
+               perror("clearenv");
+               exit(1);
+       }
+       for (; i>0; i--)
+               putenv(newenviron[i-1]);
+#else
        newenviron[i]=NULL;
        environ=newenviron;
+#endif
 
        if (setregid(getegid(), -1) != 0 &&
            setregid(getegid(), -1) != 0) {
index 462f736..38db20d 100755 (executable)
@@ -28,15 +28,17 @@ W3M_CGI_BIN?=$(PREFIX)/lib/w3m/cgi-bin
 
 tflag=$(shell if [ -n "$$NOTAINT" ] && [ "$$NOTAINT" != 1 ]; then printf -- "-T"; fi)
 extramodules=$(shell if [ "$$PROFILE" = 1 ]; then printf -- "-d:NYTProf"; fi)
+outprogs=ikiwiki.out ikiwiki-transition.out ikiwiki-calendar.out
+scripts=ikiwiki-update-wikilist ikiwiki-makerepo
 
-ikiwiki.out: ikiwiki.in
-       ./pm_filter $(PREFIX) $(VER) $(PROBABLE_INST_LIB) < ikiwiki.in > ikiwiki.out
-       chmod +x ikiwiki.out
+%.out: %.in
+       ./pm_filter $(PREFIX) $(VER) $(PROBABLE_INST_LIB) < $< > $@
+       chmod +x $@
 
 ikiwiki.setup: ikiwiki.out
        HOME=/home/me $(PERL) -Iblib/lib $(extramodules) $(tflag) ikiwiki.out -libdir . -dumpsetup ikiwiki.setup
 
-extra_build: ikiwiki.out ikiwiki.setup docwiki
+extra_build: $(outprogs) ikiwiki.setup docwiki
        ./mdwn2man ikiwiki 1 doc/usage.mdwn > ikiwiki.man
        ./mdwn2man ikiwiki-mass-rebuild 8 doc/ikiwiki-mass-rebuild.mdwn > ikiwiki-mass-rebuild.man
        ./mdwn2man ikiwiki-makerepo 1 doc/ikiwiki-makerepo.mdwn > ikiwiki-makerepo.man
@@ -51,8 +53,8 @@ docwiki: ikiwiki.out
        $(PERL) -Iblib/lib $(extramodules) $(tflag) ikiwiki.out -libdir . -setup docwiki.setup -refresh
 
 extra_clean:
-       rm -rf html doc/.ikiwiki
-       rm -f *.man ikiwiki.out ikiwiki.setup plugins/*.pyc
+       $(PERL) -I. $(extramodules) $(tflag) ikiwiki.in -libdir . -setup docwiki.setup -clean
+       rm -f *.man $(outprogs) ikiwiki.setup plugins/*.pyc
        $(MAKE) -C po clean
 
 underlay_install:
@@ -115,8 +117,9 @@ extra_install: underlay_install
        install ikiwiki-w3m.cgi $(DESTDIR)$(W3M_CGI_BIN)
 
        install -d $(DESTDIR)$(PREFIX)/bin
-       install ikiwiki.out $(DESTDIR)$(PREFIX)/bin/ikiwiki
-       install ikiwiki-makerepo ikiwiki-transition ikiwiki-update-wikilist ikiwiki-calendar $(DESTDIR)$(PREFIX)/bin/
+       for prog in $(outprogs) $(scripts); do \
+               install $$prog $(DESTDIR)$(PREFIX)/bin/$$(echo $$prog | sed 's/\.out//'); \
+       done
 
        $(MAKE) -C po install DESTDIR=$(DESTDIR) PREFIX=$(PREFIX)
        
diff --git a/README b/README
index a4dc0d6..7efc59a 100644 (file)
--- a/README
+++ b/README
@@ -5,7 +5,7 @@ All other documentation is in the ikiwiki documentation wiki, which is also
 available online at <http://ikiwiki.info/>
 
 
-A few special variables you can set while using the Makefile:
+A few special variables you can set while using the Makefile.PL:
 
   PROFILE=1 turns on profiling for the build of the doc wiki.
   (Uses Devel::NYTProf)
index 82aa8d4..ef03295 100644 (file)
@@ -36,7 +36,7 @@ IkiWiki::Setup::Automator->import(
        cgiurl => "http://$domain/~$ENV{USER}/$wikiname_short/ikiwiki.cgi",
        cgi_wrapper => "$ENV{HOME}/public_html/$wikiname_short/ikiwiki.cgi",
        adminemail => "$ENV{USER}\@$domain",
-       add_plugins => [qw{goodstuff websetup comments blogspam}],
+       add_plugins => [qw{goodstuff&