From 1fc3f034191d3eec78b4d5da343e282092a221be Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 10 Jul 2013 21:49:23 -0400 Subject: [PATCH] ikiwiki (3.20130711) unstable; urgency=low * Deal with git behavior change in 1.7.2 and newer that broke support for commits with an empty commit message. * Pass --no-edit when used with git 1.7.8 and newer. # imported from the archive --- .perlcriticrc | 59 + Bundle/IkiWiki.pm | 37 + Bundle/IkiWiki/Extras.pm | 42 + CHANGELOG | 1 + IkiWiki.pm | 2854 ++++ IkiWiki/CGI.pm | 469 + IkiWiki/Plugin/404.pm | 81 + IkiWiki/Plugin/aggregate.pm | 792 ++ IkiWiki/Plugin/amazon_s3.pm | 257 + IkiWiki/Plugin/anonok.pm | 51 + IkiWiki/Plugin/attachment.pm | 393 + IkiWiki/Plugin/autoindex.pm | 139 + IkiWiki/Plugin/blogspam.pm | 130 + IkiWiki/Plugin/brokenlinks.pm | 56 + IkiWiki/Plugin/bzr.pm | 330 + IkiWiki/Plugin/calendar.pm | 525 + IkiWiki/Plugin/camelcase.pm | 73 + IkiWiki/Plugin/color.pm | 78 + IkiWiki/Plugin/comments.pm | 1067 ++ IkiWiki/Plugin/conditional.pm | 125 + IkiWiki/Plugin/creole.pm | 38 + IkiWiki/Plugin/cutpaste.pm | 90 + IkiWiki/Plugin/cvs.pm | 549 + IkiWiki/Plugin/darcs.pm | 438 + IkiWiki/Plugin/date.pm | 34 + IkiWiki/Plugin/ddate.pm | 41 + IkiWiki/Plugin/editdiff.pm | 78 + IkiWiki/Plugin/editpage.pm | 474 + IkiWiki/Plugin/edittemplate.pm | 143 + IkiWiki/Plugin/embed.pm | 77 + IkiWiki/Plugin/external.pm | 255 + IkiWiki/Plugin/favicon.pm | 33 + IkiWiki/Plugin/filecheck.pm | 222 + IkiWiki/Plugin/flattr.pm | 97 + IkiWiki/Plugin/format.pm | 54 + IkiWiki/Plugin/fortune.pm | 35 + IkiWiki/Plugin/getsource.pm | 94 + IkiWiki/Plugin/git.pm | 906 ++ IkiWiki/Plugin/goodstuff.pm | 43 + IkiWiki/Plugin/google.pm | 53 + IkiWiki/Plugin/goto.pm | 85 + IkiWiki/Plugin/graphviz.pm | 149 + IkiWiki/Plugin/haiku.pm | 60 + IkiWiki/Plugin/headinganchors.pm | 43 + IkiWiki/Plugin/highlight.pm | 207 + IkiWiki/Plugin/hnb.pm | 58 + IkiWiki/Plugin/html.pm | 33 + IkiWiki/Plugin/htmlbalance.pm | 58 + IkiWiki/Plugin/htmlscrubber.pm | 127 + IkiWiki/Plugin/htmltidy.pm | 71 + IkiWiki/Plugin/httpauth.pm | 117 + IkiWiki/Plugin/img.pm | 214 + IkiWiki/Plugin/inline.pm | 772 + IkiWiki/Plugin/link.pm | 176 + IkiWiki/Plugin/linkmap.pm | 100 + IkiWiki/Plugin/listdirectives.pm | 100 + IkiWiki/Plugin/localstyle.pm | 35 + IkiWiki/Plugin/lockedit.pm | 58 + IkiWiki/Plugin/map.pm | 163 + IkiWiki/Plugin/mdwn.pm | 117 + IkiWiki/Plugin/mercurial.pm | 400 + IkiWiki/Plugin/meta.pm | 453 + IkiWiki/Plugin/mirrorlist.pm | 71 + IkiWiki/Plugin/moderatedcomments.pm | 64 + IkiWiki/Plugin/monotone.pm | 772 + IkiWiki/Plugin/more.pm | 45 + IkiWiki/Plugin/norcs.pm | 72 + IkiWiki/Plugin/notifyemail.pm | 169 + IkiWiki/Plugin/opendiscussion.pm | 33 + IkiWiki/Plugin/openid.pm | 284 + IkiWiki/Plugin/orphans.pm | 68 + IkiWiki/Plugin/osm.pm | 595 + IkiWiki/Plugin/otl.pm | 93 + IkiWiki/Plugin/pagecount.pm | 40 + IkiWiki/Plugin/pagestats.pm | 120 + IkiWiki/Plugin/pagetemplate.pm | 50 + IkiWiki/Plugin/parentlinks.pm | 75 + IkiWiki/Plugin/passwordauth.pm | 450 + IkiWiki/Plugin/pingee.pm | 42 + IkiWiki/Plugin/pinger.pm | 121 + IkiWiki/Plugin/po.pm | 1376 ++ IkiWiki/Plugin/poll.pm | 183 + IkiWiki/Plugin/polygen.pm | 70 + IkiWiki/Plugin/postsparkline.pm | 149 + IkiWiki/Plugin/prettydate.pm | 127 + IkiWiki/Plugin/progress.pm | 76 + IkiWiki/Plugin/rawhtml.pm | 23 + IkiWiki/Plugin/recentchanges.pm | 251 + IkiWiki/Plugin/recentchangesdiff.pm | 80 + IkiWiki/Plugin/relativedate.pm | 71 + IkiWiki/Plugin/remove.pm | 297 + IkiWiki/Plugin/rename.pm | 617 + IkiWiki/Plugin/repolist.pm | 52 + IkiWiki/Plugin/rsync.pm | 45 + IkiWiki/Plugin/search.pm | 272 + IkiWiki/Plugin/shortcut.pm | 107 + IkiWiki/Plugin/sidebar.pm | 109 + IkiWiki/Plugin/signinedit.pm | 41 + IkiWiki/Plugin/skeleton.pm.example | 272 + IkiWiki/Plugin/smiley.pm | 107 + IkiWiki/Plugin/sortnaturally.pm | 38 + IkiWiki/Plugin/sparkline.pm | 173 + IkiWiki/Plugin/svn.pm | 380 + IkiWiki/Plugin/table.pm | 210 + IkiWiki/Plugin/tag.pm | 218 + IkiWiki/Plugin/template.pm | 71 + IkiWiki/Plugin/testpagespec.pm | 42 + IkiWiki/Plugin/teximg.pm | 191 + IkiWiki/Plugin/textile.pm | 35 + IkiWiki/Plugin/theme.pm | 75 + IkiWiki/Plugin/tla.pm | 299 + IkiWiki/Plugin/toc.pm | 142 + IkiWiki/Plugin/toggle.pm | 88 + IkiWiki/Plugin/trail.pm | 467 + IkiWiki/Plugin/transient.pm | 51 + IkiWiki/Plugin/txt.pm | 88 + IkiWiki/Plugin/typography.pm | 51 + IkiWiki/Plugin/underlay.pm | 41 + IkiWiki/Plugin/userlist.pm | 76 + IkiWiki/Plugin/version.pm | 48 + IkiWiki/Plugin/websetup.pm | 517 + IkiWiki/Plugin/wikitext.pm | 33 + IkiWiki/Plugin/wmd.pm | 51 + IkiWiki/Receive.pm | 91 + IkiWiki/Render.pm | 886 ++ IkiWiki/Setup.pm | 281 + IkiWiki/Setup/Automator.pm | 213 + IkiWiki/Setup/Standard.pm | 72 + IkiWiki/Setup/Yaml.pm | 49 + IkiWiki/UserInfo.pm | 77 + IkiWiki/Wrapper.pm | 291 + Makefile.PL | 191 + NEWS | 1 + README | 27 + auto-blog.setup | 54 + auto.setup | 44 + cpan/CPAN/MyConfig.pm | 44 + cpan/README | 7 + debian/NEWS | 572 + debian/README.Debian | 18 + debian/changelog | 4627 ++++++ debian/compat | 1 + debian/control | 59 + debian/copyright | 303 + debian/docs | 1 + debian/link | 2 + debian/postinst | 14 + debian/preinst | 20 + debian/rules | 19 + doc/GPL | 340 + doc/TourBusStop.mdwn | 30 + doc/anchor.mdwn | 11 + doc/backlinks.mdwn | 2 + doc/banned_users.mdwn | 10 + doc/banned_users/discussion.mdwn | 31 + doc/basewiki.mdwn | 26 + doc/basewiki/index.mdwn | 7 + doc/basewiki/sandbox.mdwn | 32 + doc/blog.mdwn | 4 + doc/branches.mdwn | 25 + doc/bugs.mdwn | 13 + doc/bugs/2.45_Compilation_error.mdwn | 198 + doc/bugs/404_plugin_and_lighttpd.mdwn | 45 + doc/bugs/404_plugin_should_handle_403.mdwn | 16 + doc/bugs/404_when_cancel_create_page.mdwn | 60 + doc/bugs/4_spaces_after_bullet.mdwn | 18 + ...r_div_on_all_pages_to_improve_theming.mdwn | 149 + ...s_for_suggesting__47__accepting_edits.mdwn | 15 + ...regated_Atom_feeds_are_double-encoded.mdwn | 22 + ...low_overriding_of_symlink_restriction.mdwn | 139 + doc/bugs/Another_UTF-8_problem.mdwn | 16 + ...tachment_plug-in_not_committing_files.mdwn | 18 + doc/bugs/Broken_URL_to_your_blog_script.mdwn | 10 + doc/bugs/Broken_access_to_Ikiwiki_gitweb.mdwn | 19 + ...ebar_does_not_regenerate_the_subpages.mdwn | 8 + ...lder__44___non-existent_field_address.mdwn | 59 + .../CGI_edit_and_slash_in_page_title.mdwn | 18 + .../CGI_problem_with_some_webservers.mdwn | 108 + doc/bugs/CGI_showed_HTML_when_perl_error.mdwn | 40 + ...t_store_PERL5LIB_environment_variable.mdwn | 28 + ..._Recent_Changes_create_spurious_Links.mdwn | 11 + doc/bugs/Can__39__t_build_2.49__63__.mdwn | 35 + ...nect_to_ikiwiki_git_repo_through_http.mdwn | 18 + doc/bugs/Can__39__t_create_root_page.mdwn | 69 + doc/bugs/Can__39__t_deplete_page__63__.mdwn | 8 + ..._rebuild_wiki_pages_with_ikiwiki_2.49.mdwn | 44 + ...nline_pages_with_apostrophes_in_title.mdwn | 7 + doc/bugs/Changing_language.mdwn | 9 + ...sum_errors_on_the_pristine-tar_branch.mdwn | 112 + ...d_override_settings_in_the_setup_file.mdwn | 32 + ...re_not_sorted_by_their_date_attribute.mdwn | 71 + doc/bugs/Comments_dissapeared.mdwn | 69 + ...ink_is_to_index.html_if_usedirs_is_on.mdwn | 5 + ...er__34___OpenID_at_RecentChanges_page.mdwn | 44 + doc/bugs/Disappearing_Pages.mdwn | 41 + ...link_not_translated_after_page_update.mdwn | 27 + ...iscussion_link_not_translated_in_post.mdwn | 67 + ...n_of_main_page_generates_invalid_link.mdwn | 3 + ...40____41___really_return_a_hash__63__.mdwn | 10 + ...e_entry_in_Bundle::IkiWiki::Extras.pm.mdwn | 5 + .../Encoding_problem_in_calendar_plugin.mdwn | 73 + .../Error:_OpenID_failure:_time_bad_sig:.mdwn | 83 + ...ror:_Your_login_session_has_expired._.mdwn | 46 + ...his_page_--_missing_page_dependencies.mdwn | 46 + ...n:_Unknown_function___96__this__39___.mdwn | 70 + ...st_nesting_in_map_output_for_subpages.mdwn | 3 + ...scussion_pages_appear_as_non-existing.mdwn | 5 + .../External_link:_underscore_conversion.mdwn | 25 + doc/bugs/External_links_with_Creole.mdwn | 5 + ...cy_characters_get_munged_on_page_save.mdwn | 29 + doc/bugs/Feeds_get_wrong_timezone..mdwn | 10 + ...nk_to_index.html_instead_of_directory.mdwn | 37 + ...lons_cause_problems_for_Windows_users.mdwn | 75 + ...t:_changed_behavior_w.r.t._timestamps.mdwn | 214 + .../Git:_web_commit_message_not_utf-8.mdwn | 17 + ...viz_plug-in_directive_changed_in_2.60.mdwn | 11 + ...ML_for_parentlinks_makes_theming_hard.mdwn | 45 + ...into_Atom_not_necessarily_well-formed.mdwn | 35 + ...created_when_editing_markdown_via_CGI.mdwn | 43 + ...light_extension_uses_hard_coded_paths.mdwn | 3 + ...rror_500_when_using_mercurial_backend.mdwn | 31 + ...yperestraier_search_plug-in_defective.mdwn | 55 + ...cation_not_set_correctly_in_make_test.mdwn | 24 + ...____41___broken_outside_ikiwiki__63__.mdwn | 20 + .../IkiWiki::Wrapper_should_use_destdir.mdwn | 23 + .../discussion.mdwn | 4 + ...use_file__39__s_mtime_for_Last_Edited.mdwn | 21 + .../Index_files_have_wrong_permissions.mdwn | 14 + ...Inline_doesn__39__t_wikilink_to_pages.mdwn | 96 + ..._in_eval_while_running_with_-T_switch.mdwn | 98 + doc/bugs/Insecure_dependency_in_mkdir.mdwn | 160 + doc/bugs/Insecure_dependency_in_utime.mdwn | 14 + ...rt_multiple_linkmaps_on_a_single_page.mdwn | 3 + ...missing_pages_should_always_be_styled.mdwn | 5 + ...nks_with_symbols_can__39__t_be_edited.mdwn | 22 + ...not_set_for_inline_or_archive_entries.mdwn | 22 + ...p_sorts_subtags_under_a_different_tag.mdwn | 49 + ...rl_should_read_r2__44___not_changeset.mdwn | 11 + ...__95__skip_setting.___40__patch__41__.mdwn | 11 + ...Missing_build-dep_on_perlmagick__63__.mdwn | 14 + ...onstant_domain_at_IkiWiki.pm_line_842.mdwn | 50 + doc/bugs/Monotone_rcs_support.mdwn | 58 + doc/bugs/More_permission_checking.mdwn | 17 + ...e_being_commented_on_while_commenting.mdwn | 11 + ...yed__59___need_page_refresh_to_appear.mdwn | 35 + ...rror:_Your_login_session_has_expired..mdwn | 39 + .../No_categories_in_RSS__47__Atom_feeds.mdwn | 7 + ..._items_when_filename_contains_a_colon.mdwn | 76 + ...inks_setting_for___34__no_limit__34__.mdwn | 4 + doc/bugs/No_progress_in_progress_bar.mdwn | 43 + ...e_listed_by___33__map_or___33__inline.mdwn | 68 + .../Obsolete_templates__47__estseek.conf.mdwn | 3 + .../OpenID_delegation_fails_on_my_server.mdwn | 53 + ...G_triggers_UTF-8_error_in_MimeInfo.pm.mdwn | 25 + .../PREFIX_not_honoured_for_underlaydir.mdwn | 44 + ...name_not_found_directly_after_commit.mdwn" | 145 + doc/bugs/Patch:_Fix_error_in_style.css.mdwn | 37 + ...epend_on___47__usr__47__bin__47__perl.mdwn | 6 + ...sing___39__cp_-a__39___in_Makefile.PL.mdwn | 77 + ...__39__t_refer_to_offsite_openid_image.mdwn | 19 + ...moves_page_location_drop-down_options.mdwn | 10 + ...ith_displaying_smileys_on_inline_page.mdwn | 25 + ...h_editing_page_after_first_SVN_commit.mdwn | 209 + doc/bugs/Problem_with_toc.pm_plug-in.mdwn | 37 + .../Problem_with_umlauts_and_friends.mdwn | 16 + doc/bugs/Problems_using_cygwin.mdwn | 20 + .../Problems_with_graphviz.pm_plug-in.mdwn | 43 + ...ems_with_graphviz.pm_plug-in_previews.mdwn | 54 + ...centChanges_broken_with_empty_svnpath.mdwn | 26 + .../RecentChanges_contains_invalid_XHTML.mdwn | 55 + .../RecentChanges_links_to_deleted_pages.mdwn | 15 + ...move_redirect_pages_from_inline_pages.mdwn | 15 + ..._web_is_failing_when_using_subversion.mdwn | 28 + .../Running_on_an_alternative_port_fails.mdwn | 93 + doc/bugs/SSI_include_stripped_from_mdwn.mdwn | 21 + .../SVG_files_not_recognized_as_images.mdwn | 39 + doc/bugs/Search_Help_doesn__39__t_exist.mdwn | 8 + ...l__44___when_use__95__dirs_is_enabled.mdwn | 15 + ...ludes_text_from_navigational_elements.mdwn | 22 + ..._not_clickable_while_adding_a_comment.mdwn | 9 + ...attachments___34__snails_it_all__34__.mdwn | 47 + .../discussion.mdwn | 141 + doc/bugs/Smileys_in_the_block_code.mdwn | 34 + ...Spaces_in_link_text_for_ikiwiki_links.mdwn | 53 + ...ements_added_to_tags_in_inliine_pages.mdwn | 43 + .../discussion.mdwn | 9 + ...roken___34__FormattingHelp__34___link.mdwn | 3 + ...linked_srcdir_requires_trailing_slash.mdwn | 81 + .../Tab_delimited_tables_don__39__t_work.mdwn | 22 + ...variable_not_passed_as-is__63____33__.mdwn | 23 + ..._are_lower-cased_when_creating_a_page.mdwn | 37 + ...ate_plugins_do_not_play_well_together.mdwn | 30 + doc/bugs/Trailing_slash_breaks_links.mdwn | 9 + ...URLs_with_parentheses_displayed_badly.mdwn | 19 + doc/bugs/UTF-16_and_UTF-32_are_unhandled.mdwn | 29 + ...-8_BOM_showing_up_inside_a_page__63__.mdwn | 38 + doc/bugs/UTF-8_in_attachment_filenames.mdwn | 25 + ...able_to_add_attachments_to_some_pages.mdwn | 31 + ...efined_subroutine_IkiWiki::escapeHTML.mdwn | 27 + ...Undefined_subroutine_IkiWiki::refresh.mdwn | 7 + ...nderscores_in_links_don__39__t_appear.mdwn | 18 + ..._cp__40__1__41___for_installing_files.mdwn | 32 + ...uses_http:__47____47__localhost__63__.mdwn | 34 + ..._a_directive_does_not_contain_a_space.mdwn | 19 + ..._between_toc_plugin_and_page_sections.mdwn | 40 + doc/bugs/Wrong_permissions_on_4_smileys.mdwn | 10 + ...needs_xmlns_attribute_on_html_element.mdwn | 5 + ...4__meta__40__date__41____34___ignored.mdwn | 41 + ...only_first_139_characters_of_each_key.mdwn | 12 + ..._deletion_does_not_refresh_front_page.mdwn | 6 + .../__34__more__34___doesn__39__t_work.mdwn | 17 + ...__error_when_src_path_contains_spaces.mdwn | 5 + ..._PATH__125___should_include_PREFIXbin.mdwn | 19 + ...kes_ikiwiki_not_un-escape_HTML_at_all.mdwn | 47 + ...are_removed_from_markdown_inline_HTML.mdwn | 31 + .../__63__Discussion_when_not_CGI_mode.mdwn | 9 + ...___Use_correct_perl_when_running_make.html | 17 + ...c_plugin_and_UTF-8:_IkiWiki_and_UTF-8.mdwn | 11 + .../discussion.mdwn | 23 + ...9___for___96__.page__42____39____63__.mdwn | 35 + ...setting_not_propagated_to_CGI_wrapper.mdwn | 28 + doc/bugs/absolute_sizes_in_default_CSS.mdwn | 39 + .../aggregate_generates_long_filenames.mdwn | 40 + doc/bugs/aggregate_global_feed_names.mdwn | 13 + doc/bugs/aggregate_plugin_errors.mdwn | 62 + .../aggregate_plugin_errors/discussion.mdwn | 6 + ...in_should_honour_a_post__39__s_mctime.mdwn | 15 + doc/bugs/aggregate_removed_feeds_linger.mdwn | 11 + ...einline_planets_wrongly_link_to_posts.mdwn | 17 + ...n__39__t_always_work_with_img_plugin_.mdwn | 7 + doc/bugs/anonok_vs._httpauth.mdwn | 118 + ...xing_templates_and_creation__95__date.mdwn | 62 + ...scaping_underscores_in_filename__63__.mdwn | 22 + .../attachment:_failed_to_get_filehandle.mdwn | 115 + ...hment_plugin_enabled_by_default__63__.mdwn | 19 + ...oad_does_not_work_for_windows_clients.mdwn | 34 + ...cklink__40__.__41___doesn__39__t_work.mdwn | 57 + .../backlinks_onhover_thing_can_go_weird.mdwn | 43 + ...a_change_removing_an_invalid_pagespec.mdwn | 48 + ...es_but_meta_is_not_enabled_by_default.mdwn | 5 + ....__47___even_if_it_is_already_present.mdwn | 3 + doc/bugs/bestlink_change_update_issue.mdwn | 32 + doc/bugs/bestlink_returns_deleted_pages.mdwn | 75 + ...log_posts_not_added_to_mercurial_repo.mdwn | 50 + ...in_not_allowing_non-ASCII_chars__63__.mdwn | 15 + ..._95__options_whitelist_vs._IPv6__63__.mdwn | 4 + .../blogspam_marks_me_as_spam_on_ipv6.mdwn | 8 + ...ment_create_elements_id__61__feedlink.mdwn | 15 + doc/bugs/broken_page_after_buggy_remove.mdwn | 4 + doc/bugs/broken_parentlinks.mdwn | 50 + ...okenlinks_accumulates_duplicate_items.mdwn | 27 + doc/bugs/brokenlinks_false_positives.mdwn | 6 + doc/bugs/bug_in_cgiurl_port.mdwn | 15 + .../bug_when_toggling_in_a_preview_page.mdwn | 29 + ...:_Broken_pipe__34_____40__patch__41__.mdwn | 24 + ...oddly_when_older_ikiwiki_is_installed.mdwn | 31 + doc/bugs/build_in_opensolaris.mdwn | 74 + doc/bugs/bzr-update-syntax-error.mdwn | 11 + doc/bugs/bzr_2.0_breaks_bzr_plugin.mdwn | 87 + ...r_RecentChanges_dates_start_from_1969.mdwn | 16 + ..._plugin_does_not_define_rcs__95__diff.mdwn | 27 + ...t_mix_template_vars_inside_directives.mdwn | 61 + .../cannot_clone_documented_git_repo.mdwn | 16 + doc/bugs/cannot_preview_shortcuts.mdwn | 17 + .../cannot_reliably_use_meta_in_template.mdwn | 18 + doc/bugs/cannot_revert_page_deletion.mdwn | 10 + doc/bugs/capitalized_attachment_names.mdwn | 14 + .../cgi_does_not_use_templatedir_overlay.mdwn | 26 + doc/bugs/cgi_wrapper_always_regenerated.mdwn | 16 + ...mg_directive_behave_not_as_documented.mdwn | 31 + .../clearenv_not_present_at_FreeBSD_.mdwn | 5 + .../discussion.mdwn | 1 + doc/bugs/clearing_email_in_prefs.mdwn | 5 + doc/bugs/colon:problem.mdwn | 12 + doc/bugs/colon:problem/discussion.mdwn | 1 + doc/bugs/comments_appear_two_times.mdwn | 24 + doc/bugs/comments_not_searchable.mdwn | 19 + ...s_preview_unsafe_with_allowdirectives.mdwn | 8 + ...produce_broken_links_in_RecentChanges.mdwn | 9 + ...1___in_wikilink-text_breaks_wikilinks.mdwn | 9 + .../conditional_preprocess_during_scan.mdwn | 57 + doc/bugs/conflicts.mdwn | 32 + ...pdated_time_information_for_the_feeds.mdwn | 113 + ...g_page_from_comment_creates_a_comment.mdwn | 9 + .../cutpaste.pm:_missing_filter_call.mdwn | 55 + ...e_timeformat__44___even_when_disabled.mdwn | 7 + ...ug_shortcut_should_expand_differently.mdwn | 17 + .../discussion.mdwn | 11 + ..._pull_in_packages_required_for_openid.mdwn | 9 + .../default__95__pageext_not_working.mdwn | 16 + doc/bugs/definition_lists_should_be_bold.mdwn | 27 + ...defintion_lists_appear_to_be_disabled.mdwn | 54 + doc/bugs/deletion_warnings.mdwn | 89 + doc/bugs/depends_simple_mixup.mdwn | 88 + doc/bugs/diff_links_to_backtrace.mdwn | 9 + doc/bugs/disable_sub-discussion_pages.mdwn | 63 + doc/bugs/disabling_backlinks.mdwn | 32 + doc/bugs/discussion.mdwn | 18 + doc/bugs/discussion_of_what__63__.mdwn | 7 + ...ak_the_detection_of_the_best_location.mdwn | 6 + doc/bugs/discussion_removal.mdwn | 16 + doc/bugs/done.mdwn | 3 + doc/bugs/dumpsetup_does_not_save_destdir.mdwn | 3 + ...esolves_links_differently_from_commit.mdwn | 23 + ...age_turned_off_in_web_interface__63__.mdwn | 10 + ...dits_not_showing_up_in_compiled_pages.mdwn | 20 + .../edittemplate_seems_not_to_be_working.mdwn | 7 + ...uld_not_be_considered_as_broken_links.mdwn | 12 + .../encoding_issue_in_blogspam_plugin.mdwn | 34 + ...gated_pagespec_matches_internal_pages.mdwn | 30 + ...ions_of_dates_not_formatted_correctly.mdwn | 43 + .../errors_with_ampersand_in_filename.mdwn | 21 + ...oryurl_doesn__39__t_show_file_history.mdwn | 17 + ...links_inside_headings_don__39__t_work.mdwn | 24 + ..._cannot_access_ARGV_needed_for_getopt.mdwn | 14 + ...does_the_wrong_thing_from_index.mdwn2.mdwn | 7 + ...ot_prevent_tags_from_being_aggregated.mdwn | 32 + ...eds_get_removed_in_strange_conditions.mdwn | 57 + doc/bugs/filecheck_failing_to_find_files.mdwn | 65 + ...ind:_invalid_predicate___96__-L__39__.mdwn | 26 + doc/bugs/find_gnuism.mdwn | 7 + ...to_load_updated_pages_at_ikiwiki.info.mdwn | 14 + doc/bugs/format_bug.mdwn | 25 + doc/bugs/formbuilder_3.0401_broken.mdwn | 73 + ...d_prune_remote_branches_when_fetching.mdwn | 14 + ...mmit_adds_files_that_were_not_tracked.mdwn | 19 + doc/bugs/git_fails_to_compile.mdwn | 32 + doc/bugs/git_mail_notification_race.mdwn | 57 + .../git_stderr_output_causes_problems.mdwn | 45 + doc/bugs/git_utf8.mdwn | 12 + ...es_script_picks_up_tags_from_anywhere.mdwn | 22 + ...gitweb_deficiency_w.r.t._log_messages.mdwn | 14 + ...deficiency_w.r.t._newly_created_pages.mdwn | 13 + doc/bugs/goto_with_bad_page_name.mdwn | 25 + .../graphviz_demo_generates_empty_graph.mdwn | 15 + ...hardcoded___34__Discussion__34___link.mdwn | 44 + .../helponformatting_link_disappears.mdwn | 5 + doc/bugs/html5_support.mdwn | 117 + ...ng_when_using_xhtml5___34__mode__34__.mdwn | 46 + doc/bugs/html_errors.mdwn | 5 + .../htmlbalance_fails_with_HTML-Tree_v4.mdwn | 18 + ...rubber_breaks_multimarkdown_footnotes.mdwn | 18 + ..._still_scrubbing_HTML_from_mdwn_pages.mdwn | 21 + ...s_email_obfuscation_by_Text::Markdown.mdwn | 37 + ...fig_file_which_may_break_other_usages.mdwn | 26 + doc/bugs/http_proxy_for_openid.mdwn | 86 + ...httpauth_conflicts_with_git_anon_push.mdwn | 25 + ...o_drop_privileges_and_execute_ikiwiki.mdwn | 30 + ...es_not_set_perl_moduels_path_properly.mdwn | 17 + ...0__eval_5__41___line_120__44___at_EOF.mdwn | 9 + ...require_blank_rcs_to_work_as_cgi_only.mdwn | 46 + doc/bugs/ikiwiki__39__s_ViewVC_down.mdwn | 3 + ...n_Solaris_due_to_missing_LOCK__95__EX.mdwn | 43 + .../ikiwiki_ignores_PATH_environment.mdwn | 24 + doc/bugs/ikiwiki_lacks_a_--quiet.mdwn | 29 + ...ly_honours_locks_when_asked_for_forms.mdwn | 34 + ...___if_external_plugins_return_nothing.mdwn | 12 + ...inlined_pages_have_wrong_relative_URL.mdwn | 15 + doc/bugs/img_plugin_and_class_attr.mdwn | 27 + .../img_plugin_and_missing_heigth_value.mdwn | 7 + doc/bugs/img_plugin_causes_taint_failure.mdwn | 20 + ...__tag_without_src_attribute_post-2.20.mdwn | 36 + ...n_should_pass_through_class_attribute.mdwn | 49 + doc/bugs/img_vs_align.mdwn | 38 + .../img_with_alt_has_extra_double_quote.mdwn | 32 + doc/bugs/index.html__63__updated.mdwn | 15 + ...ml_is_made_visible_by_various_actions.mdwn | 16 + doc/bugs/iniline_breaks_toc_plugin.mdwn | 64 + ...9__s_match__61____34____34___pagespec.mdwn | 15 + doc/bugs/inline_archive_crash.mdwn | 6 + .../inline_breaks_PERMALINK_variable.mdwn | 25 + ..._empty_if_rootpage_doesn__39__t_exist.mdwn | 20 + .../inline_page_not_updated_on_removal.mdwn | 9 + ...otpage_option_is_not_case_insensitive.mdwn | 9 + ...diturl_even_when_editpage_is_disabled.html | 16 + ...inline_raw_broken_on_unknown_pagetype.mdwn | 29 + doc/bugs/inline_skip_causes_empty_inline.mdwn | 10 + doc/bugs/inline_sort-by-title_issues.mdwn | 57 + ...inline_sort_order_and_meta_date_value.mdwn | 314 + doc/bugs/install_into_home_dir_fails.mdwn | 57 + ...ling_from_svn_copies_.svn_directories.mdwn | 28 + ...l_error:_smileys.mdwn_cannot_be_found.mdwn | 41 + doc/bugs/ipv6_address_in_comments.mdwn | 19 + ...y-ui.min.css_missing_some_image_files.mdwn | 14 + ...-xml-perl_0.69_breaks_XML-RPC_plugins.mdwn | 13 + ..._page_at_root_if_non-root_page_exists.mdwn | 6 + .../linkmap_displays_underscore_escapes.mdwn | 18 + .../the_patch.pl | 68 + doc/bugs/links_from_sidebars.mdwn | 14 + doc/bugs/links_from_sidebars/discussion.mdwn | 5 + doc/bugs/links_misparsed_in_CSV_files.mdwn | 27 + ...rectives_doesn__39__t_register_a_link.mdwn | 34 + ...ut_an_invalid_pagespec_in_preferences.mdwn | 21 + doc/bugs/locking_fun.mdwn | 105 + .../login_page_non-obvious_with_openid.mdwn | 47 + ...n_page_should_note_cookie_requirement.mdwn | 39 + doc/bugs/logout_in_ikiwiki.mdwn | 44 + ...t_properly_generated_in_rssatom_feeds.mdwn | 29 + ...irectory_for_which_a_file_also_exists.mdwn | 26 + .../discussion.mdwn | 3 + ...4__common__95__prefix__34___correctly.mdwn | 70 + ...ls_to_close_ul_element_for_empty_list.mdwn | 93 + doc/bugs/map_generates_malformed_HTML.mdwn | 36 + ...s_inconsistent_about_bare_directories.mdwn | 86 + ...ot_title_when_show__61__title_is_used.mdwn | 20 + ...directories_sometimes_make_ugly_lists.mdwn | 62 + ...ug:_email_escaping_and_plus_addresses.mdwn | 37 + doc/bugs/markdown_module_location.mdwn | 49 + doc/bugs/mercurial_fail_to_add.mdwn | 34 + ...o_basewiki_causes_odd_inconsistencies.mdwn | 6 + doc/bugs/messed_up_repository.mdwn | 21 + doc/bugs/meta_inline.mdwn | 4 + .../methodResponse_in_add__95__plugins.mdwn | 41 + doc/bugs/minor:_tiny_rendering_error.mdwn | 5 + ...t_the_current_page___40__if_any__41__.mdwn | 101 + ...end_does_not_support_srcdir_in_subdir.mdwn | 5 + doc/bugs/more_and_RSS_generation.mdwn | 20 + .../multiple_encoding_issues_in_atom.mdwn | 8 + doc/bugs/multiple_pages_with_same_name.mdwn | 76 + doc/bugs/multiple_rss_feeds_per_page.mdwn | 31 + ...re_uploading_more_than_one_attachment.mdwn | 44 + .../nested_inlines_produce_no_output.mdwn | 12 + doc/bugs/nested_raw_included_inlines.mdwn | 51 + doc/bugs/newfile-test.mdwn | 11 + doc/bugs/no_commit_mails_for_new_pages.mdwn | 10 + ...ntainer_around_a_set_of_inlined_pages.mdwn | 23 + ...9__t_do_search_in_w3m_at_ikiwiki.info.mdwn | 32 + ...ine_pagenames_do_not_add_a_dependency.mdwn | 44 + .../octal_umask_setting_is_unintuitive.mdwn | 55 + ..._should_respect_the_discussion_option.mdwn | 11 + .../discussion.mdwn | 26 + ...ed_with_simple_registration_extension.mdwn | 3 + ...penid_no_longer_pretty-prints_OpenIDs.mdwn | 17 + doc/bugs/openid_postsignin_failure.mdwn | 52 + ...o_not_display_properly_on_google_maps.mdwn | 14 + ..._maps_icon_path_have_a_trailing_slash.mdwn | 34 + ...40____41___usage_breaks_map_rendering.mdwn | 23 + doc/bugs/osm_sometimes_looses_some_nodes.mdwn | 5 + ..._list_the_full_path_to_affected_pages.mdwn | 14 + ...uild_fails_in_non-English_environment.mdwn | 11 + ...s_not_rebuilt_if_it_changes_extension.mdwn | 27 + ...oes_not_work_on_new_page_with_a_table.mdwn | 3 + doc/bugs/pagecount_is_broken.mdwn | 4 + doc/bugs/pagemtime_in_refresh_mode.mdwn | 28 + .../pages_missing_top-level_directory.mdwn | 78 + .../pages_under_templates_are_invalid.mdwn | 16 + ...ec:_tagged__40____41____44___globbing.mdwn | 36 + ...9__t_match___123__curly__125___braces.mdwn | 44 + ...spec_error_on_refresh_but_not_rebuild.mdwn | 32 + ...arsing_chokes_on_function__40____41__.mdwn | 64 + doc/bugs/pagestats_plugin_broken.mdwn | 29 + ...function_does_not_respect_meta_titles.mdwn | 289 + ...should_only_be_done_outside_html_tags.mdwn | 17 + doc/bugs/password_deletion.mdwn | 7 + doc/bugs/perl:_double_free_or_corruption.mdwn | 14 + doc/bugs/pipe-symbol_in_taglink_target.mdwn | 25 + doc/bugs/pipe_in_tables_as_characters.mdwn | 16 + ...__96__attachment__96___is_not_enabled.mdwn | 7 + ...edate_depends_on_locale_at_setup_file.mdwn | 16 + ...nfig_serves_index_directory_for_index.mdwn | 85 + ...che_config_serves_index.rss_for_index.mdwn | 36 + doc/bugs/po:_double_commits_of_po_files.mdwn | 22 + doc/bugs/po:_markdown_link_parse_bug.mdwn | 21 + ..._translated_versions_of_all_underlays.mdwn | 16 + doc/bugs/po:_new_pages_not_translatable.mdwn | 12 + ...ot_override_the_title_on_the_homepage.mdwn | 58 + .../po:_po4a_too_strict_on_html_pages.mdwn | 22 + .../po:_po_files_instead_of_html_files.mdwn | 30 + .../po:_ugly_messages_with_empty_files.mdwn | 6 + ...able_basewiki_pages_that_lack_po_fies.mdwn | 73 + doc/bugs/po_plugin_adds_new_dependency.mdwn | 38 + ...o_plugin_cannot_add_po_files_into_git.mdwn | 34 + doc/bugs/po_vs_templates.mdwn | 48 + ...can__39__t_vote_for_non-ascii_options.mdwn | 7 + doc/bugs/poll_plugin_uses_GET.mdwn | 8 + doc/bugs/possibly_po_related_error.mdwn | 20 + doc/bugs/post-commit_hangs.mdwn | 47 + ..._hook_can__39__t_be_compiled_with_tcc.mdwn | 19 + ...ydate_with_weekday-date_inconsistency.mdwn | 32 + .../preview_base_url_should_be_absolute.mdwn | 53 + doc/bugs/preview_pagestate.mdwn | 13 + ...ing_new_page_can_leave_files_dangling.mdwn | 53 + ...with_an_edittemplate_reverts_edit_box.mdwn | 5 + .../problem_adding_tag_from_template.mdwn | 10 + doc/bugs/proxy.py_utf8_troubles.mdwn | 35 + .../prune_causing_taint_mode_failures.mdwn | 35 + doc/bugs/pruning_is_too_strict.mdwn | 12 + doc/bugs/quieten_mercurial.mdwn | 34 + ..._91____91____33__included__93____93__.mdwn | 100 + ...hanging_the_underlaydir_config_option.mdwn | 12 + doc/bugs/recentchanges_escaping.mdwn | 5 + doc/bugs/recentchanges_feed_links.mdwn | 107 + ..._diffurl__61__1_when_diffurl_is_empty.mdwn | 18 + ...n_commits_which_remove_a_lot_of_files.mdwn | 46 + doc/bugs/relative_date_weird_results.mdwn | 4 + doc/bugs/removal_of_transient_pages.mdwn | 78 + ..._orphaned_sparkline-php_from_Suggests.mdwn | 22 + .../remove_plugin_and_untracked_files.mdwn | 6 + .../removing_pages_with_utf8_characters.mdwn | 51 + ...rename_fixup_not_attributed_to_author.mdwn | 12 + .../renaming_a_page_destroyed_some_links.mdwn | 12 + ...g_with_only_width_or_height_breaks_ie.mdwn | 9 + doc/bugs/rss_feed_cleanup_on_delete.mdwn | 6 + ..._encoding_of_entities_for_some_fields.mdwn | 52 + doc/bugs/rss_output_relative_links.mdwn | 3 + ...ails_on_file_containing_only_a_number.mdwn | 29 + doc/bugs/rst_plugin_hangs_on_utf-8.mdwn | 20 + ...n_has_python_hardcode_in_shebang_line.mdwn | 15 + ...impleXMLRPCDispatcher_from_pyhton_2.5.mdwn | 13 + doc/bugs/rst_tweak.mdwn | 52 + ...34___fields_are_incorrectly_specified.mdwn | 29 + ...iguration_files_many_times_on_rebuild.mdwn | 9 + ...locale_data_in_the_installed_location.mdwn | 25 + doc/bugs/search_plugin_and_CGI_preview.mdwn | 19 + ...in_finds_no_results_with_xapian_1.2.7.mdwn | 14 + .../search_plugin_uses_wrong_css_path.mdwn | 14 + doc/bugs/search_template_missing_dep.mdwn | 4 + ...__bugs_contain_colons_in_the_filename.mdwn | 15 + doc/bugs/shortcut_encoding.mdwn | 28 + ..._will_not_work_without_shortcuts.mdwn.mdwn | 33 + ...tcuts_don__39__t_escape_from_Markdown.mdwn | 7 + .../sidebar_is_obscured_by_recentchanges.mdwn | 59 + ...ebar_not_updated_in_unedited_subpages.mdwn | 9 + .../sitemap_includes_images_directly.mdwn | 8 + ...ot_all_meta_fields_are_stored_escaped.mdwn | 44 + ...ome_strings_are_not_internationalized.mdwn | 47 + ...nk__93____93___doesn__39__t_make_link.mdwn | 32 + ...ers_in_tag_names_need_manual_escaping.mdwn | 3 + ..._certificates_not_checked_with_openid.mdwn | 85 + doc/bugs/strange_hook_id_in_skeleton.pm.mdwn | 5 + doc/bugs/stray___60____47__p__62___tags.mdwn | 17 + doc/bugs/support_for_openid2_logins.mdwn | 24 + doc/bugs/svn+ssh_commit_fail.mdwn | 5 + doc/bugs/svn-commit-hanging.mdwn | 7 + ...ilures_interpreted_as_merge_conflicts.mdwn | 21 + doc/bugs/svn_fails_to_update.mdwn | 89 + ...9__t_find_IkiWiki.pm_if_not_installed.mdwn | 22 + doc/bugs/syntax_error_in_aggregate.mdwn | 11 + doc/bugs/table_external_file_links.mdwn | 9 + ...dle___92__r__92__n_lines_in_CSV_files.mdwn | 5 + ...nges_introduced_by_typed_link_feature.mdwn | 16 + ...ag_plugin:_autotag_vs._staged_changes.mdwn | 17 + ...tagged__40____41___matching_wikilinks.mdwn | 35 + doc/bugs/tags__44___backlinks_and_3.x.mdwn | 34 + ...e_dir_not_used_when_creating_new_tags.mdwn | 43 + doc/bugs/taint_and_-T.mdwn | 30 + .../taint_issue_with_regular_expressions.mdwn | 35 + .../tbasewiki__95__brokenlinks.t_broken.mdwn | 60 + .../discussion.mdwn | 2 + ...plateForRecentChangesMissingCloseSpan.mdwn | 26 + doc/bugs/template_creation_error.mdwn | 111 + doc/bugs/teximg_does_not_work_Preview.mdwn | 12 + ...if_same_tex_is_used_on_multiple_pages.mdwn | 24 + ...ies_if_input_has_a_non-utf8_character.mdwn | 14 + ...s_unclear_when_multiple_methods_exist.mdwn | 16 + ...ith_meta_enabled__44___causes_a_crash.mdwn | 3 + .../toc_displays_headings_from_sidebar.mdwn | 3 + doc/bugs/toc_in_sidebar.mdwn | 21 + ...pects_body_element_without_attributes.mdwn | 3 + doc/bugs/toggle_fails_on_Safari.mdwn | 58 + doc/bugs/trail_excess_dependencies.mdwn | 95 + doc/bugs/trail_shows_on_cgi_pages.mdwn | 12 + doc/bugs/trail_test_suite_failures.mdwn | 97 + ..._tagbase_is_not_transient_autoindexed.mdwn | 31 + doc/bugs/transitive_dependencies.mdwn | 94 + doc/bugs/trouble_with_base_in_search.mdwn | 60 + ..._having_problems_with_meta_directives.mdwn | 19 + doc/bugs/typo_in_ikiwiki.setup.mdwn | 9 + .../typo_in_skeleton.pm:_sessionncgi.mdwn | 5 + ...matched_tags_won__39__t_get_converted.mdwn | 46 + .../undefined_value_as_a_HASH_reference.mdwn | 68 + doc/bugs/underlaydir_file_expose.mdwn | 13 + .../unicode_chars_in_wikiname_break_auth.mdwn | 20 + ...nicode_encoded_urls_and_recentchanges.mdwn | 38 + ...CGI_parameter_when_creating_todo_item.mdwn | 18 + ..._discussion_links_on_discussion_pages.mdwn | 36 + ...PI_change_breaks_wikis_with_po_plugin.mdwn | 98 + ...__44___...__44___1__41___not_absolute.mdwn | 9 + ..._links_on_recentchanges_pages_problem.mdwn | 12 + doc/bugs/utf-8_bug_in_websetup.pm.mdwn | 22 + doc/bugs/utf8_html_templates.mdwn | 22 + doc/bugs/utf8_svn_log.mdwn | 11 + doc/bugs/web_reversion_on_ikiwiki.info.mdwn | 14 + ..._95__symlinks__95__before__95__srcdir.mdwn | 21 + ...re_in_match__95__included__40____41__.mdwn | 7 + doc/bugs/weird_syntax_in_aggregate.pm.mdwn | 9 + ...es_not_work_between_toc_and_an_inline.mdwn | 30 + ...ks_still_processed_inside_code_blocks.mdwn | 67 + ...aydir_or_templatedir_don__39__t_exist.mdwn | 15 + doc/bugs/wikilink_in_table.mdwn | 36 + doc/bugs/word_wrap.mdwn | 16 + ...pper_can__39__t_find_the_perl_modules.mdwn | 16 + doc/bugs/wrong_attachment_size.mdwn | 8 + doc/bugs/wrong_discussion_page_created.mdwn | 12 + ...reverting_an_ikiwiki_outside_git_root.mdwn | 8 + ...g_permissions_on_some_files_in_source.mdwn | 11 + ...rl_when_inside_another_blog-like_page.mdwn | 36 + doc/bugs/xgettext_issue.mdwn | 73 + doc/bugs/yaml:xs_codependency_not_listed.mdwn | 13 + ..._not_support_UTF-8_if_XS_is_installed.mdwn | 104 + doc/cgi.mdwn | 5 + doc/cgi/discussion.mdwn | 22 + doc/commit-internals.mdwn | 20 + doc/competition.mdwn | 19 + doc/consultants.mdwn | 9 + doc/contact.mdwn | 11 + doc/contact/discussion.mdwn | 14 + doc/convert.mdwn | 9 + doc/css.mdwn | 24 + doc/css/discussion.mdwn | 18 + doc/css_market.mdwn | 68 + doc/css_market/02_template.css | 307 + doc/css_market/02_template.tmpl | 20 + doc/css_market/bma.css | 108 + doc/css_market/cstamas.css | 69 + doc/css_market/discussion.mdwn | 37 + doc/css_market/embeddedmoose.css | 13 + doc/css_market/kirkambar.css | 142 + doc/css_market/zack.css | 193 + doc/download.mdwn | 52 + doc/examples.mdwn | 12 + doc/examples/blog.mdwn | 26 + doc/examples/blog/archives.mdwn | 8 + doc/examples/blog/comments.mdwn | 10 + doc/examples/blog/discussion.mdwn | 13 + doc/examples/blog/index.mdwn | 11 + doc/examples/blog/posts.mdwn | 3 + doc/examples/blog/posts/first_post.mdwn | 2 + doc/examples/blog/sidebar.mdwn | 10 + doc/examples/blog/tags.mdwn | 3 + doc/examples/softwaresite.mdwn | 19 + doc/examples/softwaresite/Makefile | 15 + doc/examples/softwaresite/bugs.mdwn | 4 + doc/examples/softwaresite/bugs/done.mdwn | 3 + .../bugs/fails_to_frobnicate.mdwn | 4 + doc/examples/softwaresite/bugs/hghg.mdwn | 1 + .../softwaresite/bugs/needs_more_bugs.mdwn | 3 + doc/examples/softwaresite/contact.mdwn | 7 + doc/examples/softwaresite/doc.mdwn | 5 + doc/examples/softwaresite/doc/faq.mdwn | 11 + doc/examples/softwaresite/doc/install.mdwn | 10 + doc/examples/softwaresite/doc/setup.mdwn | 4 + doc/examples/softwaresite/download.mdwn | 5 + doc/examples/softwaresite/index.mdwn | 13 + doc/examples/softwaresite/news.mdwn | 5 + .../softwaresite/news/version_1.0.mdwn | 1 + .../softwaresite/templates/release.mdwn | 7 + doc/favicon.ico | Bin 0 -> 371 bytes doc/features.mdwn | 181 + doc/forum.mdwn | 11 + doc/forum/404_-_not_found.mdwn | 22 + ..._3dea2600474f77fb986767da4d507d62._comment | 8 + ..._948e4678be6f82d9b541132405351a2c._comment | 31 + ..._4c7b1fa88776815bbc6aa286606214c2._comment | 8 + .../Accessing_meta_values_in_pages__63__.mdwn | 8 + doc/forum/Adding_new_markup_to_markdown.mdwn | 11 + .../Allow_only_specific_OpenIDs_to_login.mdwn | 7 + ...kiwiki_inline_feed_to_identi.ca__63__.mdwn | 3 + ..._8a5acbb6234104b607c8c4cf16124ae4._comment | 8 + ..._155e5823860a91989647ede8b5c9224a._comment | 16 + ..._317f1202a3da1bfc845d4becbac4bba8._comment | 10 + doc/forum/Apache_XBitHack.mdwn | 28 + ...accidentally_committed_jiberish__63__.mdwn | 12 + ..._b425823f800fba82ad2aaaa0dbe6686a._comment | 10 + doc/forum/Asciidoc_plugin.mdwn | 14 + doc/forum/Attachment_and_sub-directory.mdwn | 5 + doc/forum/Background_picture_and_css.mdwn | 8 + .../Blog_posting_times_and_ikiwiki_state.mdwn | 28 + ..._87304dfa2caea7e526cdb04917524e8c._comment | 8 + doc/forum/Broken_after_upgrading_Ikiwiki.mdwn | 10 + ..._3d0588a845c58b3aedc35970e8dcc811._comment | 14 + ..._fd65d4b87a735b67543bb0cf4053b652._comment | 10 + ..._7c8b46eabdb25cbc01c56c7b53ed3b91._comment | 8 + doc/forum/CGI_script_and_HTTPS.mdwn | 29 + ..._3f8ef438ca7de11635d4e40080e7baa9._comment | 43 + ...dar:_listing_multiple_entries_per_day.mdwn | 21 + ..._d3dd0b97c63d615e3dee22ceacaa5a30._comment | 83 + ..._2311b96483bb91dc25d5e3695bbca513._comment | 12 + ..._d23f0cedd0b9e937eaf200eef55ac457._comment | 166 + ..._4be39c2043821848d4b25d0bf946a718._comment | 15 + ..._de545ebb6376066674ef2aaae4757b9c._comment | 97 + ...I_change_the_default_menu_items__63__.mdwn | 6 + ..._eb56fed3b5fc19c8dd49af4444a049c5._comment | 31 + ...ferent_favicons_for_each_folder__63__.mdwn | 1 + ..._a01112ba235e2f44a7655c36ef680e7e._comment | 19 + ..._b8ccd3c29249eca73766f567bce12569._comment | 8 + ...cognize_multimarkdown_meta_tags__63__.mdwn | 4 + .../Can_OpenID_users_be_adminusers__63__.mdwn | 69 + ...an__39__t_get_comments_plugin_working.mdwn | 16 + ...ikiwiki_working_again_after_reinstall.mdwn | 16 + ..._87a360155ff0502fe08274911cc6a53f._comment | 8 + ...penID__44___but_can_use_Ikiwiki_login.mdwn | 5 + ..._79127e3c09a1d798146088dee5a67708._comment | 10 + ...es_live_somewhere_inside_srcdir__63__.mdwn | 8 + ..._d1e79825dfb5213d2d1cba2ace1707b1._comment | 8 + ..._8177ede5a586b1a573a13fd26f8d3cc0._comment | 8 + ...e_configured_as_multi_user_blog__63__.mdwn | 7 + ...t_page_of_results_using_search_plugin.mdwn | 26 + ...ge_is_added_rather_than_changed__63__.mdwn | 5 + ..._1397feebfb0fb7cc57af2f8b74ce047e._comment | 8 + ..._ad36c945f59fe525428fc30246911ff5._comment | 10 + doc/forum/Cannot_write_to_commitlock.mdwn | 28 + doc/forum/Chinese_file_name_corruption.mdwn | 5 + ..._765ac8b6f70083bb5aaaaac5beab461f._comment | 10 + doc/forum/Clarification_on_--cgi_option.mdwn | 4 + ..._deda457e4bff7dfe630dbc0192dfddea._comment | 11 + ...ed_comments_into_special_branch__63__.mdwn | 8 + ..._8403e8ff9c5c8dddb6d744632322f7bc._comment | 12 + doc/forum/Darcs_as_the_RCS___63__.mdwn | 13 + ..._IkiWiki__47__Setup__47__Automator.pm.mdwn | 19 + ..._aec4bf4ca7d04d580d2fa83fd3f7166f._comment | 8 + ..._c682ebb0e8e72088a8f92356dc31ef37._comment | 13 + ...20100815.7_-_update_recommended__63__.mdwn | 7 + ..._5e916c8fa90470909064ea73531f79d4._comment | 12 + ..._2fa15f0eaf8c860b82e366130c8563c7._comment | 8 + ..._c5af589dcdfe4f91dba50243762065e5._comment | 12 + ..._3090da7bafbf92a825edec8ffc45af20._comment | 12 + doc/forum/Define_custom_commands.mdwn | 1 + ..._7d82637bc8c706b69e4a55585677f6bf._comment | 11 + ...diting_with_editor_than_with_web-edit.mdwn | 24 + ..._ac6bda46ad00bfe980bc76c4a39aa796._comment | 9 + ..._10a46f8ee23c8935e20c70842671cee4._comment | 13 + ...____40__Blogging_and_Wiki_pages__41__.mdwn | 7 + ..._15651796492a6f04a19f4a481947c97c._comment | 16 + ...isable_account_creation_for_new_users.mdwn | 9 + ..._adafddb0aff7c2c1f4574101c4cf9073._comment | 8 + ..._865591f77966f1657a9a4b2426318c51._comment | 12 + ..._05193e563682f634f13691ee0a8359db._comment | 8 + doc/forum/Discussion_PageSpec__63__.mdwn | 3 + .../Doing_related_links_based_on_tags.mdwn | 31 + doc/forum/Dump_plugin.mdwn | 4 + ..._bfce80b3f5be78ec28692330843d4ae1._comment | 14 + ..._notifications_for_comment_moderation.mdwn | 3 + ..._668bf6a21310dcc8b882bc60a130ba06._comment | 12 + doc/forum/Empty_sha1sum_messages.mdwn | 11 + ..._b260b5e6b4c4f4c203b01183fee9fd69._comment | 10 + ..._d6a47838a3c81d0a75e6fc22e786c976._comment | 10 + ...oblem_in_french_with_ikiwiki-calendar.mdwn | 20 + ...iled_to_return_the_uploaded_file_name.mdwn | 11 + ..._66c321b9eb618d20872cee7d6ca9e44c._comment | 8 + ..._80296d67c7f1dd75b56b85c14f5efa3b._comment | 12 + ...ror:___34__do__34___parameter_missing.mdwn | 13 + ..._3a51c303ba1670f1567f323349b53837._comment | 16 + ..._c5f24a8c4d2de0267cf0de1908480e82._comment | 12 + doc/forum/Error:_bad_page_name.mdwn | 46 + ...iki__47__lockfile:_Permission_denied_.mdwn | 5 + ..._64146f306ec8c10614521359b6de4f82._comment | 10 + ..._ed2b4b8f7122b42bbde1189fbd2969dd._comment | 23 + doc/forum/Error_Code_1.mdwn | 7 + ..._0459afcc383aad382df67a19eaf2e731._comment | 8 + doc/forum/Everyone_can_remove_comments.mdwn | 1 + doc/forum/Flowplayer.mdwn | 1 + ..._75d13cd915a736422db47e00dbe46671._comment | 9 + ..._1b2d3891006a87a4773bd126baacddfc._comment | 8 + ...Forward_slashes_being_escaped_as_252F.mdwn | 33 + ..._7702cf6d354ab600d6643b075b9f09da._comment | 12 + ...s_of_ikiwiki.info_are_broken._:__40__.mdwn | 14 + ...me_for_new_pages_fetched_from_the_VCS.mdwn | 7 + ..._9572dd6f7a2f6f630b12f24bb5c4a8ce._comment | 8 + doc/forum/Help_with_tag__95__autocreate.mdwn | 9 + doc/forum/Hide_text.mdwn | 3 + ..._f21d21c130f97a7b21d8a317178e2e0c._comment | 8 + ..._5a878865f34f78a89c4ec91a9425a085._comment | 8 + ...manage_a_bilingual_ikiwiki_site__63__.mdwn | 15 + ..._4389d65b14fa1b7134098e0ffe3bf055._comment | 10 + doc/forum/How_can_I_prevent_spam__63__.mdwn | 17 + ..._fd26fb7f1569e8c44ba8262794f938db._comment | 19 + ..._d098124f005976ee815d25c883bc9106._comment | 16 + ..._deb434d01aaefa18d2791e48d6c824ae._comment | 8 + doc/forum/How_do_I_enable_OpenID__63__.mdwn | 1 + ...How_does_ikiwiki_remember_times__63__.mdwn | 98 + ...ted_in_inline_archive_templates__63__.mdwn | 11 + ...er_delay_newly_pushed_revisions__63__.mdwn | 10 + ...se-over_pop-up_label_for_a_text__63__.mdwn | 8 + ...rizontable_bar_under_page_title__63__.mdwn | 3 + ..._f2e52d38f60888c7d5142de853123540._comment | 8 + ...ous_and_next_blog_on_blog_pages__63__.mdwn | 14 + ..._aad510f45be505efaabcb6fb860665a4._comment | 23 + ..._ee65792a5b796caa216f4e7a653fc668._comment | 23 + ...lid_extensions_for_source_files__63__.mdwn | 1 + ...ly_a_background_color_to_a_page__63__.mdwn | 1 + .../How_to_change_registration_page.mdwn | 9 + ..._43758a232e4360561bc84f710862ff40._comment | 14 + ..._8176ef231cf901802fc60b6d414018e6._comment | 8 + .../How_to_configure_po_plugin__63__.mdwn | 21 + ..._5e0cc4cdfd126f2f4af64104f02102d6._comment | 9 + ...ink_to_a_page_in_a_subdirectory__63__.mdwn | 26 + ..._d20ee1d8d7a3e77a445f8b887e807119._comment | 11 + ...ranslation_page_using_po_plugin__63__.mdwn | 24 + .../How_to_customize_page_title__63__.mdwn | 6 + ..._403e1f866b5e04e5899021f54bbdd1ed._comment | 10 + ...not_map_to_Unicode__34___errors__63__.mdwn | 20 + ...obar__93____93___in_code_blocks__63__.mdwn | 9 + ..._ad000d39fd1dc05aa8ef6eb19d8d999b._comment | 8 + ...ubdir_but_not_ikiwiki_root_path__63__.mdwn | 26 + ...ne_a_page_from_another_git_repository.mdwn | 5 + ...st_all_pages_in_a_wiki_instance__63__.mdwn | 5 + ..._920bcc70fe6d081cf27aa2cc7c6136f4._comment | 8 + ..._to_list_new_pages__44___inline__63__.mdwn | 5 + ..._e989b18bade34a92a9c8fe7099036e15._comment | 13 + ...ions_and_comments_to_blog_posts__63__.mdwn | 11 + ..._e153beb17b6ada69c6ab09d1f491d112._comment | 8 + ...e_of_content_at_the_top_of_page__63__.mdwn | 3 + ..._6dedc31dd1145490bb5fa4ad14cc4c63._comment | 8 + ...rectory_into_a_new_subdirectory__63__.mdwn | 10 + ..._a83a1a33afbf245971733b4128809365._comment | 15 + ...eta_author_field_from_user_name__63__.mdwn | 3 + ..._0906e1f3eb8b826a7730233b95cb5ddd._comment | 10 + ...ow_to_set_up_a_page_as_internal__63__.mdwn | 5 + ..._to_set_up_git_repository_hook___63__.mdwn | 19 + ...nt_changes_for_individual_pages__63__.mdwn | 1 + ..._cd34affc6883f4e4bc5e7e7b711cc8ba._comment | 10 + ..._repository_is_on_a_remote_host__63__.mdwn | 3 + ..._0c71e17ae552cbab1056ac96fbd36c59._comment | 9 + ..._b309302a084fbd8bcd4cd9bd2509cf5a._comment | 10 + ...e_sidebar_differently_using_CSS__63__.mdwn | 13 + ...rkdown_instead_of_Text::MultiMarkdown.mdwn | 5 + ...t_labels_but_not_letter_in_toc_plugin.mdwn | 8 + doc/forum/Howto_add_tag_from_plugin_code.mdwn | 12 + ..._c61454825874a6fe1905cb549386deb0._comment | 77 + ...IWYG_ikiwiki_editor_for_Windows__63__.mdwn | 14 + ..._a66fd9d7ab4359784a5420cd899a1057._comment | 8 + ..._3351ff773fea3f640f4036bb8c7c7efd._comment | 10 + ..._273b2b63a9af2bc4eeb030e026436687._comment | 12 + ..._546771c13ea1b550301586e187d82cb5._comment | 8 + ...4___and_it__39__s_a_binary_file__63__.mdwn | 33 + ...iwiki_themes_for_mobile_devices__63__.mdwn | 7 + doc/forum/Include_attachment_in_a_page.mdwn | 9 + ..._275aad6ca3b2972749b7f6636b130035._comment | 12 + ...e_to_change_default_mdwn_suffix__63__.mdwn | 1 + ..._2a449c6017ecdb4f557963266fb4ec41._comment | 8 + ...reation_dates_relative_to_today__63__.mdwn | 26 + doc/forum/LaTeX_Error.mdwn | 66 + doc/forum/Last_visited_pages.mdwn | 1 + ..._e34650064dd645b35da98e80c0311df9._comment | 8 + ..._2a0c4e844da1deaa2c286e87c8eab84d._comment | 8 + doc/forum/Link_to_a_local_pdf_file.mdwn | 1 + ..._b6c57588042373f8e1f187041c1a8530._comment | 8 + doc/forum/Log_in_error.mdwn | 5 + ..._0ef13ea01a413160d81951636c15c3e6._comment | 10 + ...ike_to_add___63__updated_to_all_links.mdwn | 3 + ..._3fe4c5967e704355f9b594aed46baf67._comment | 13 + ..._to_new_host._Web_page_is_not_updated.mdwn | 27 + ..._b44a492c7f10395a31f3c0830ef33f0c._comment | 10 + ..._f9240b217b2d1ee8d51dada9cb1186b3._comment | 28 + ..._c3c5c41a4c220793c6d16f3fd6132272._comment | 15 + ..._1f6f9e3939a454c1eb8d2fb29bd519de._comment | 16 + ..._8611fc62797e70a0d2a61d94fcb03170._comment | 22 + ...epository_to_new_ikiwiki_system__63__.mdwn | 54 + ..._e5ce524c5d34b1d4218172296bd99100._comment | 8 + ..._65c4a4895f6541ff0ff2d094ff447bba._comment | 8 + doc/forum/Moving_wiki.git_folder__63__.mdwn | 17 + ..._05238461520613f4ed1b0d02ece663bd._comment | 11 + ..._72b2b842dfa0cfaf899fe7af12977519._comment | 10 + doc/forum/Multiple_urls.mdwn | 8 + ..._e4c1256346d5a421161c20e344d8bada._comment | 22 + .../Need_help_installing_h1title_plugin.mdwn | 5 + .../Need_help_setting_up_ikiwiki_CGI.mdwn | 16 + ..._0fc4573568711c56a0df4af620110c2f._comment | 12 + ..._89f2cd7d874a6257786478e4cae1e2bc._comment | 16 + ..._cbc20267fe5f0531f63db881d50596d1._comment | 20 + ..._2eaf53935eecd0a918755d728450a642._comment | 8 + ..._creating_pages_in_multiple_languages.mdwn | 6 + ..._something_more_powerful_than_Exclude.mdwn | 5 + ..._0019cd6b34c8d8678b2532de57a92d15._comment | 12 + ..._f577ab6beb9912471949d8d18c790267._comment | 11 + ..._1ed260b0083a290688425a006a83f603._comment | 8 + ..._c39bdaf38e1e20db74eb26f0560bd673._comment | 10 + ..._39b01857f7e0b388a6e7a3c1cf5388d5._comment | 9 + ..._1dccdfebad31446200213a2cae25f0e2._comment | 10 + ...tted_and_then_reviewed_before_posting.mdwn | 23 + ..._where_to_define_wiki__39__s_ID__63__.mdwn | 12 + ..._bf1bec748d6ab419276a73a7001024cf._comment | 8 + ..._14a1b269be6dbcc9b2068d3e18b55711._comment | 10 + ..._f581afcdb4481ea5d65bcc33bdbab99a._comment | 25 + ..._b0d39d30852bca1525ab9612a7532670._comment | 8 + ...IB__44___wrappers_and_homedir_install.mdwn | 38 + ...pec_results_from_independent_checkout.mdwn | 8 + doc/forum/Parent_Links_all_link_to_root.mdwn | 18 + ..._4b5ed25cceb7740f64ee08aba00a1d91._comment | 8 + ...g_-_tracking_non-post_files_in_a_blog.mdwn | 7 + ..._45ecaf6efa2065837fa54a42737f0a66._comment | 18 + ..._45ca7ef4190c281d703c8c7ca6979298._comment | 12 + ...use_meta_variables_in_templates__63__.mdwn | 11 + ..._556078a24041289d8f0b7ee756664690._comment | 20 + ..._e7e954218d39bc310015b95aa1a5212c._comment | 10 + ..._8b16c563c89eb6980ad6a5539d934d7a._comment | 8 + ..._76eadf93cce4e2168960131d4677c5fc._comment | 8 + ..._ddabe4a005042d19c7669038b49275c1._comment | 12 + doc/forum/Problem_with_gitweb.mdwn | 3 + ..._23cc0d87448d3cbdac20a005e9191589._comment | 10 + ..._697c6038009249e6a49d9e458a5ba271._comment | 47 + ..._6a5b96f7e0d6b169c090e3df7281d938._comment | 8 + ..._8a79b879205bd265d54e30f0eee2ac63._comment | 8 + doc/forum/Problem_with_local_git_commit.mdwn | 42 + doc/forum/Processing_non-pages.mdwn | 7 + ..._changes_on_main_site_or_on_a_sidebar.mdwn | 1 + ..._018b977ff7ee59fc53838e0c20c3a9a7._comment | 11 + ..._927c11f18315baa39f08ca4982ed2ab1._comment | 8 + .../Refresh_or_recreate_style.css__63__.mdwn | 40 + ..._3274be931d0b543c7f7cf641810817aa._comment | 8 + ...gex_for_Valid_Characters_in_Filenames.mdwn | 19 + ...n_one_dest_page_from_same_source_page.mdwn | 51 + .../Revision_history_for_single_pages.mdwn | 3 + ..._d509d5d726cd7eab9472d723013f5ec4._comment | 8 + ..._d39a6177fc4c1e3c3c2c4e2592be9e3d._comment | 8 + ..._aecf2b031ace001afaa2a0f2b5f50c82._comment | 8 + doc/forum/Run_script_on_markdown_source.mdwn | 1 + ...endered_old_revisions_via_pagehistory.mdwn | 1 + doc/forum/Setting_http__95__proxy.mdwn | 22 + ..._350a7c4834c9f422e107b646cdbae3b0._comment | 20 + ...plate_variable_from_config_file__63__.mdwn | 1 + ..._bb4b5a7a49f33d660b5116fc0ce3c92d._comment | 8 + .../Setting_up_a_development_environment.mdwn | 32 + ...ikiwiki_be_committed_and_pushed__63__.mdwn | 14 + ..._8e65d7d8298e3c31d2a16446a71c8049._comment | 10 + .../Should_not_create_an_existing_page.mdwn | 15 + doc/forum/Sidebar_with_links__63__.mdwn | 58 + doc/forum/Slow_ikiwiki_after_first_run.mdwn | 1 + doc/forum/Spaces_in_wikilinks.mdwn | 104 + doc/forum/Split_a_wiki.mdwn | 21 + ..._1599c26891b2071a2f1ca3fd90627fc4._comment | 8 + ..._1c54d3594f0350340f8dfb3e95c29ffd._comment | 20 + ..._9eac1d1b93df27d849acc574b1f0f26d._comment | 8 + ..._e193ba447c0188f72ba589180b5d529e._comment | 8 + doc/forum/TMPL__95__VAR_IS__95__ADMIN.mdwn | 1 + ..._3172568473e9b79ad7ab623afd19411a._comment | 13 + ..._4302d56a6fe68d17cc42d26e6f3566c2._comment | 8 + ..._4cc44e61b9c28a2d524fa874f115041a._comment | 14 + ..._33143bad68f3f6beae963a3d0ec5d0bd._comment | 53 + ..._ef790766456d723670f52cc9e3955e90._comment | 12 + ..._3db50264e01c8fad2e5567b5a9c7b6dc._comment | 23 + ..._bdc5c96022fdb8826b57d68a41ef6ca0._comment | 8 + ...iki.cgi_was_not_found_on_this_server..mdwn | 11 + ..._d36ce6fab90e0a086ac84369af38d205._comment | 16 + ..._5836bba08172d2ddf6a43da87ebb0332._comment | 7 + ..._4eec15c8c383275db5401c8e3c2d9242._comment | 9 + ..._43ac867621efb68affa6ae2b92740cad._comment | 10 + ..._e098723bb12adfb91ab561cae21b492b._comment | 8 + ..._101183817ca4394890bd56a7694bedd9._comment | 10 + ..._2f514e6ba78d43d90e7ff4ae387e65e0._comment | 10 + ..._098bb7a3112751a7e6167483dde626bb._comment | 10 + ..._fbf403255c38da93caa5b98589fbb285._comment | 8 + doc/forum/Translating_ikiwiki_interface.mdwn | 8 + ...from_RecentChanges_CGI_to_static_page.mdwn | 10 + ...s_ways_to_use_Subversion_with_ikiwiki.mdwn | 23 + ...ference_between_tag_and_taglink__63__.mdwn | 9 + ..._b3553d65d12af4c4a87f1f66f961c8d9._comment | 49 + ...rong_with_my_recentchange_page___63__.mdwn | 13 + ...sted_as_a__47__b_and_not_only_b__63__.mdwn | 6 + ..._cd5ea3aac8a59793ece5bf01a6190b53._comment | 9 + doc/forum/Wikilink_to_a_symbolic_link.mdwn | 1 + ..._e3ad5099491e0c84cd7729eba82ce552._comment | 8 + ..._46848020b1e3d0cd55bc1ec0ba382aad._comment | 8 + .../Wikilink_to_section_of_a_wikipage.mdwn | 1 + ..._c1409a3c07dfc4ed7274560c962aba75._comment | 11 + ..._8a04eb7b0d7f17b9e5bb4cd04ba45871._comment | 8 + .../Xapian_search:_empty_postlist_table.mdwn | 34 + ..._de9a7c94beec2707eda0924ca58be9df._comment | 8 + ..._55f191e4b1306a318a30319f01802229._comment | 15 + ..._0bd424a89c3a52ff393a1e7e00c806be._comment | 24 + ..._40479ac2cfbca609f5f423e539a20ee0._comment | 8 + ..._397443138da276e11c2e9b9fa7b51406._comment | 8 + ...___47__script__62___not_working__63__.mdwn | 13 + ..._953bd716373dcf51fa444ac098b7f971._comment | 8 + ..._c7360852f9bf069f28c193373333c9a8._comment | 8 + ..._6ffc30e27387366b48112198b66c01fa._comment | 8 + .../access_restrictions:_for_extranet.mdwn | 8 + ..._a0666c3c15661fb0fff70f313cd0d47d._comment | 29 + ..._563040aa099c9366dc5701eb4bc9c10d._comment | 20 + ...ternative_approach_to_structured_data.mdwn | 63 + ...ear_if_you_are_login_or_not_in_a_page.mdwn | 36 + doc/forum/attachments_fail_to_upload.mdwn | 8 + ..._577adde1dfa49463dfa8e169c462fc42._comment | 10 + ..._473f38c6d523496fac8dad13ac6d20c3._comment | 12 + ..._799a2f1b7b259157e97fd31ec76fb845._comment | 10 + ..._e37d1497acafd3fda547462f000636e3._comment | 8 + ..._da03f9c4917cb1ef52de984b8ba86b68._comment | 11 + ..._04498946a300ddb652dec73c2950f48f._comment | 19 + doc/forum/bashman.mdwn | 7 + ...ot_decode_string_with_wide_characters.mdwn | 12 + ..._83fbb415dd3ae6a19ed5ea5f82065c28._comment | 8 + ..._d258536c98538d4744f66eb3132439a9._comment | 20 + ..._d62173d0ae220ab7b063631952856587._comment | 10 + ..._d5d0174e09a94359c23fd9c006a22bbc._comment | 50 + ..._e652027a8f90ebef6f21613b5784ded2._comment | 8 + doc/forum/chinese_character_problem.mdwn | 21 + ...ning_up_discussion_pages_and_the_like.mdwn | 11 + ...___40__where_are_they_set__63____41__.mdwn | 13 + doc/forum/create_download_link.mdwn | 4 + ..._4797493157c569f8893b53b5e5a58e73._comment | 14 + .../cutpaste.pm_not_only_file-local.mdwn | 14 + ..._497c62f21fd1b87625b806407c72dbad._comment | 8 + ...ebian_backports_update_someone_please.mdwn | 18 + doc/forum/discussion.mdwn | 7 + ...__47____47____39___in_the_address_bar.mdwn | 3 + doc/forum/editing_a_comment.mdwn | 11 + doc/forum/editing_the_style_sheet.mdwn | 18 + ...2___40__Found__41___when_editing_page.mdwn | 59 + doc/forum/ever-growing_list_of_pages.mdwn | 29 + doc/forum/field__95__tags_not_linking.mdwn | 66 + ..._7c1540e6eb6aafd2e1c9c7016e6e6249._comment | 10 + ..._0c03cbaa4f748d2fb932fda08fe6e966._comment | 8 + ..._9f3a402173f9584d8a36bc61e5755f6d._comment | 8 + ..._455a2f921059f9ecca810bb8afed0fda._comment | 10 + ..._b82294c290a215d9aa6774ee20b5a552._comment | 10 + ..._57fb279ad50f8460341dc0f217acef06._comment | 10 + ..._8dae1024e80cf6ea765dee0318324d71._comment | 8 + ..._76a4fb4def8f13b906c848814de91660._comment | 12 + ..._64d51cc9ba953e7fed609c380e30bb7d._comment | 13 + ..._7a6eac4e216133f1cf6fc12336fc2496._comment | 8 + ..._e6941a0df00fb9f45563c30e01efa622._comment | 9 + ..._f08ded5a946458aeba59a2c4cec29b2f._comment | 8 + ..._6ea7de20c3db96589c05adbe97d57cfd._comment | 8 + ..._8ad385b61c46389d87c88b17430ab1f2._comment | 8 + ..._c3c5eced158babd8c3acb493a86b6ecb._comment | 8 + ..._9bd4b3df18a28a7ab3bbef5013856987._comment | 11 + doc/forum/field_and_forms.mdwn | 13 + ..._a0e976cb79f03dcff5e9a4511b90d160._comment | 19 + ...ating:_how_to_align_text_to_the_right.mdwn | 15 + ..._disabled___44___Is_it_possible__63__.mdwn | 6 + ..._747cc477584028ce2c7bc198070b1221._comment | 10 + ..._a230861b26dba6d61461862bfedbc09c._comment | 8 + ..._848b4801fc7887906a21a676e802023c._comment | 10 + doc/forum/google_openid_broken__63__.mdwn | 82 + ...e___39____47____39___as_tagbase__63__.mdwn | 13 + ..._e7897651ba8d9156526d36d6b7744eae._comment | 8 + ...tfile_from_metadata_in_multiple_pages.mdwn | 3 + ..._I_revert_edits_in_the_web_mode__63__.mdwn | 46 + ..._e4720e8e4fe74bd6cba746e8259832e6._comment | 8 + .../how_do_I_translate_a_TWiki_site.mdwn | 44 + ...add_post_titles_in_ikiwiki_blog__63__.mdwn | 28 + .../how_to_enable_multimarkdown__63__.mdwn | 9 + ..._037f858c4d0bcbb708c3efd264379500._comment | 14 + ..._b7d512a535490dabf8d6ce55439741c7._comment | 8 + ...get_nice_pdf_from_ikiwiki_pages__63__.mdwn | 3 + ..._332d32850c3dc0d45f5cc50434205f39._comment | 8 + ..._still_have_it_under_ikiwiki_template.mdwn | 3 + doc/forum/how_to_login_as_admin.mdwn | 18 + ..._295e130c6400a2d7336758e82bcd5647._comment | 10 + ...how_to_setup_ikiwiki_on_a_remote_host.mdwn | 35 + .../howto_install_the_pagedown_plugin.mdwn | 1 + ..._158fbcef24d20920c40968da8f10442a._comment | 8 + ...ml_source_pages_in_version_3.20100704.mdwn | 8 + doc/forum/ikiwiki_+_mathjax.mdwn | 1 + ..._8426a985ecfbb02d364116503ef3a0d4._comment | 8 + ..._ddb7a4d59bbe7145167d122a146e8f65._comment | 11 + ..._5a118654bc008bbb118285ff141eb6f1._comment | 8 + ...reates_tmp__47___directory_in_destdir.mdwn | 10 + doc/forum/ikiwiki__39__s_notion_of_time.mdwn | 35 + doc/forum/ikiwiki_and_big_files.mdwn | 102 + ..._df8a9f4249af435cc335f77768a3278d._comment | 8 + ..._2d996f1124aedc10f345139c3d8b11df._comment | 19 + ..._dfbd38e2b457ea3c4f70266dbf8fbeab._comment | 8 + .../ikiwiki_development_environment_tips.mdwn | 68 + ...rates_html_files_with_600_permission..mdwn | 8 + ..._6d73d412a9cc6f6ae426b62885c1f157._comment | 19 + ..._1392fcde369d11a264f31f6b8993ccec._comment | 8 + ..._962306f22ceb17afb4150e766e9a05b3._comment | 10 + ..._8b988d85cfde123798238d0348764c79._comment | 22 + doc/forum/ikiwiki_over_database__63__.wiki | 11 + doc/forum/ikiwiki_vim_integration.mdwn | 17 + doc/forum/ikiwiki_vim_syntaxfile.mdwn | 26 + doc/forum/index_attachments.mdwn | 9 + ..._18b9531d273292b45051eef6a306ca26._comment | 10 + .../index_attachments/comment_2._comment | 31 + ..._050e5847641a27e0c14232632f3e700a._comment | 10 + .../index_attachments/comment_4._comment | 10 + .../installation_and_setup_questions.mdwn | 52 + doc/forum/installation_as_non-root_user.mdwn | 7 + doc/forum/installation_of_selected_docs.mdwn | 29 + ...possible_to_NOT_add_openid2_meta_tags.mdwn | 67 + doc/forum/java_script_slideshow.mdwn | 11 + ..._3eba0b2f3c12acc991dc3069d2b83d49._comment | 8 + ..._59d90f42b2ca2a5cc71a4d9ba9b9ee9f._comment | 10 + ..._820a86db38231cff7239f0a88b1925fd._comment | 21 + ..._a68972e3dd20b65119211d4ab120b294._comment | 10 + doc/forum/link_autocompletion_in_vim.mdwn | 22 + ...e_inside_the_wiki_without_inlining_it.mdwn | 72 + .../links_to_diff_on_recentchanges__63__.mdwn | 1 + ..._1dbc723cc2794f6d45de9cbd2fc2e0fd._comment | 8 + ..._4349c85d92cf9c1acf2e7678371ab12a._comment | 10 + ...lockedit:_pages_don__39__t_get_locked.mdwn | 12 + ..._bacffb831e5ce7ece7e670c55ad9f3af._comment | 14 + ..._ad268d3f2cd3d529cfff281e0ecb2f16._comment | 8 + ..._da2fb41c5313763e4393cdd921a3f36e._comment | 10 + ..._d0de7964db26cb6f3e81d6e8c29d860d._comment | 16 + ..._d60727c53197d1c667b59bc7250afd9f._comment | 10 + doc/forum/managing_todo_lists.mdwn | 44 + ...ing_pages_redirected_to_search-SOLVED.mdwn | 36 + ..._aa03c337b31d7acb95761eb51caab1ef._comment | 44 + doc/forum/move_pages.mdwn | 1 + ..._3f1b9563af1e729a7311e869cf7a7787._comment | 11 + ..._22b1c238faacbf10df5f03f415223b49._comment | 8 + ...4___gitosis_and_apache2_in_Debian_Sid.mdwn | 96 + .../multi_domain_setup_possible__63__.mdwn | 15 + ..._43f5df30d09046ccc4f7c44703979a11._comment | 17 + ..._75d6581f81b71fb8acbe3561047ea759._comment | 16 + ...ki_pages_on_local_filesystem_with_vim.mdwn | 130 + doc/forum/nginx:_404_plugin_not_working.mdwn | 12 + ..._02a82e468676ae64374cc91ec87e39d6._comment | 15 + ..._ce6bd8e98e4be08316522182f5f85a11._comment | 11 + ..._52b05c3274455db7bee3c1765776fd52._comment | 8 + ..._5a8c2987f442106c68eb822c5bce3bf1._comment | 23 + ..._0720cd8842dc1cb338b74a0e6fdb2aac._comment | 8 + doc/forum/pandoc-iki_plugin.mdwn | 5 + ..._11eef903493378fd704a6bd92e968508._comment | 8 + ..._2c437577390cffe3401f5cc2f08a2ab1._comment | 8 + .../paths_to_files_outside_the_wiki_root.mdwn | 34 + doc/forum/perl5lib_and_wrappers.mdwn | 13 + ...create_po_files___40__only_pot__41__..mdwn | 11 + doc/forum/possible_utf-8_problem__63__.mdwn | 26 + .../postsignin_redirect_not_working.mdwn | 30 + ...em_with_git_after_a_commit_of_ikiwiki.mdwn | 4 + ..._2b9986717769419a8ae0f730c36b7e65._comment | 22 + ..._should_be_under_control_of_RCS__63__.mdwn | 105 + ...ng_original_title_with_meta_directive.mdwn | 1 + doc/forum/remove_css__63__.mdwn | 5 + doc/forum/report_pagination.mdwn | 18 + doc/forum/screenplay_plugin.mdwn | 1 + ..._6c353acfc80b972ee3a34c8bb09dede3._comment | 8 + ..._1868aeebebefae80531f2031ffba35d3._comment | 8 + doc/forum/section_editing.mdwn | 1 + ..._b193caa886a47c685ac7dafaf60c1761._comment | 12 + doc/forum/speeding_up_ikiwiki.mdwn | 90 + ..._interpreted_as_wikilinks__41____63__.html | 19 + ..._included_only_for_their_side_effects.mdwn | 16 + ..._plugin:_rebuilding_autocreated_pages.mdwn | 11 + ..._all_pages__44___not_populating_feeds.mdwn | 28 + ..._ec4ffab10e60510b53660b70908d1bd8._comment | 14 + ..._a47884ffd749df980cd62f4c1e3167ce._comment | 10 + ..._6c4affdbc637946506d0c28a8648dc6e._comment | 32 + doc/forum/teximg_not_working.mdwn | 26 + ..._35e2ebf3893fc0c7966490e1fef1e6cf._comment | 10 + ...tion_from_handwritten_html_to_ikiwiki.mdwn | 90 + ..._plugins:_newpage__44___jssearchfield.mdwn | 20 + doc/forum/understanding_filter_hooks.mdwn | 17 + doc/forum/upgrade_steps.mdwn | 147 + ...php-markdown-extra_with_ikiwiki__63__.mdwn | 3 + ..._66d48218361caa4c07bd714b82ed0021._comment | 8 + ..._f2ee0a4dce571d329f795e52139084c0._comment | 8 + ..._e388714f457ccb6ef73630179914558c._comment | 9 + ...edirs___38___indexpages_using_problem.mdwn | 17 + doc/forum/users/acodispo.mdwn | 2 + doc/forum/using_l10n__39__d_basewiki.mdwn | 7 + ..._eaab671848ee6129f6fe9399474eeac0._comment | 8 + ..._d907676a1db1210ca59506673c564359._comment | 10 + ..._5e9d5bc5ecaf63f9bfe3315b09a279aa._comment | 10 + doc/forum/using_svn+ssh_with_ikiwiki.mdwn | 11 + ...f8_warnings_for___34____92__xAB__34__.mdwn | 47 + ...___91__Save_Page__93___results_in_403.mdwn | 9 + ...web_service_API__44___fastcgi_support.mdwn | 18 + ...en__95__few_page_editing_policy__63__.mdwn | 3 + doc/forum/where_are_the_tags.mdwn | 9 + ..._6a559c3bfe72011c45b006d33176da3d._comment | 14 + ...--setup_is_processing_right_now__63__.mdwn | 4 + ..._4f52f8fc083982bd5a572742cf35c74f._comment | 7 + doc/forum/wiki_clones_on_dynamic_IPs.mdwn | 10 + doc/forum/wiki_name_in_page_titles.mdwn | 32 + ...n:_Editformular_showing_existing_tags.mdwn | 15 + ...ow_OpenID_users_to_set_a_display_name.mdwn | 3 + doc/forum/wishlist:_support_staging_area.mdwn | 12 + doc/forum/wmd_editor_double_preview.mdwn | 3 + ..._0d3acf67f3c35f8c4156228f96dcd975._comment | 8 + doc/freesoftware.mdwn | 11 + doc/freesoftware/discussion.mdwn | 3 + doc/git.mdwn | 84 + doc/ikiwiki-calendar.mdwn | 53 + doc/ikiwiki-calendar/discussion.mdwn | 36 + doc/ikiwiki-makerepo.mdwn | 44 + doc/ikiwiki-makerepo/discussion.mdwn | 1 + doc/ikiwiki-mass-rebuild.mdwn | 33 + doc/ikiwiki-mass-rebuild/discussion.mdwn | 1 + doc/ikiwiki-transition.mdwn | 75 + doc/ikiwiki-update-wikilist.mdwn | 33 + doc/ikiwiki.mdwn | 17 + doc/ikiwiki/directive.mdwn | 56 + doc/ikiwiki/directive/aggregate.mdwn | 57 + .../directive/aggregate/discussion.mdwn | 10 + doc/ikiwiki/directive/brokenlinks.mdwn | 14 + .../directive/brokenlinks/discussion.mdwn | 3 + doc/ikiwiki/directive/calendar.mdwn | 60 + doc/ikiwiki/directive/color.mdwn | 25 + doc/ikiwiki/directive/comment.mdwn | 40 + doc/ikiwiki/directive/commentmoderation.mdwn | 9 + doc/ikiwiki/directive/copy.mdwn | 3 + doc/ikiwiki/directive/cut.mdwn | 3 + doc/ikiwiki/directive/cutpaste.mdwn | 50 + doc/ikiwiki/directive/date.mdwn | 16 + doc/ikiwiki/directive/edittemplate.mdwn | 34 + doc/ikiwiki/directive/flattr.mdwn | 45 + doc/ikiwiki/directive/format.mdwn | 29 + doc/ikiwiki/directive/fortune.mdwn | 8 + doc/ikiwiki/directive/graph.mdwn | 32 + doc/ikiwiki/directive/graph/discussion.mdwn | 27 + doc/ikiwiki/directive/haiku.mdwn | 15 + doc/ikiwiki/directive/if.mdwn | 50 + doc/ikiwiki/directive/img.mdwn | 39 + doc/ikiwiki/directive/img/discussion.mdwn | 34 + doc/ikiwiki/directive/inline.mdwn | 126 + doc/ikiwiki/directive/inline/discussion.mdwn | 163 + doc/ikiwiki/directive/linkmap.mdwn | 29 + doc/ikiwiki/directive/listdirectives.mdwn | 20 + doc/ikiwiki/directive/map.mdwn | 21 + doc/ikiwiki/directive/map/discussion.mdwn | 99 + doc/ikiwiki/directive/meta.mdwn | 206 + doc/ikiwiki/directive/meta/discussion.mdwn | 69 + doc/ikiwiki/directive/more.mdwn | 21 + doc/ikiwiki/directive/orphans.mdwn | 15 + doc/ikiwiki/directive/osm.mdwn | 69 + doc/ikiwiki/directive/osm/discussion.mdwn | 13 + doc/ikiwiki/directive/pagecount.mdwn | 10 + doc/ikiwiki/directive/pagestats.mdwn | 40 + .../directive/pagestats/discussion.mdwn | 18 + doc/ikiwiki/directive/pagetemplate.mdwn | 13 + doc/ikiwiki/directive/paste.mdwn | 3 + doc/ikiwiki/directive/ping.mdwn | 18 + doc/ikiwiki/directive/poll.mdwn | 27 + doc/ikiwiki/directive/polygen.mdwn | 11 + doc/ikiwiki/directive/postsparkline.mdwn | 45 + doc/ikiwiki/directive/progress.mdwn | 18 + doc/ikiwiki/directive/shortcut.mdwn | 9 + doc/ikiwiki/directive/sidebar.mdwn | 20 + doc/ikiwiki/directive/sidebar/discussion.mdwn | 10 + doc/ikiwiki/directive/sparkline.mdwn | 52 + doc/ikiwiki/directive/table.mdwn | 53 + doc/ikiwiki/directive/table/discussion.mdwn | 1 + doc/ikiwiki/directive/tag.mdwn | 35 + doc/ikiwiki/directive/tag/discussion.mdwn | 13 + doc/ikiwiki/directive/taglink.mdwn | 3 + doc/ikiwiki/directive/template.mdwn | 91 + doc/ikiwiki/directive/testpagespec.mdwn | 24 + .../directive/testpagespec/discussion.mdwn | 6 + doc/ikiwiki/directive/teximg.mdwn | 23 + doc/ikiwiki/directive/toc.mdwn | 27 + doc/ikiwiki/directive/toggle.mdwn | 34 + doc/ikiwiki/directive/toggleable.mdwn | 3 + doc/ikiwiki/directive/trailitem.mdwn | 9 + doc/ikiwiki/directive/trailitems.mdwn | 25 + doc/ikiwiki/directive/traillink.mdwn | 16 + doc/ikiwiki/directive/trailoptions.mdwn | 18 + doc/ikiwiki/directive/version.mdwn | 12 + doc/ikiwiki/directive/waypoint.mdwn | 6 + doc/ikiwiki/formatting.mdwn | 106 + doc/ikiwiki/formatting/discussion.mdwn | 20 + doc/ikiwiki/markdown.mdwn | 11 + doc/ikiwiki/openid.mdwn | 28 + doc/ikiwiki/pagespec.mdwn | 86 + doc/ikiwiki/pagespec/attachment.mdwn | 38 + .../pagespec/attachment/discussion.mdwn | 15 + doc/ikiwiki/pagespec/discussion.mdwn | 170 + doc/ikiwiki/pagespec/po.mdwn | 23 + doc/ikiwiki/pagespec/sorting.mdwn | 30 + doc/ikiwiki/searching.mdwn | 20 + doc/ikiwiki/subpage.mdwn | 12 + doc/ikiwiki/subpage/linkingrules.mdwn | 33 + doc/ikiwiki/wikilink.mdwn | 29 + doc/ikiwiki/wikilink/discussion.mdwn | 93 + doc/ikiwikiusers.mdwn | 198 + doc/ikiwikiusers/discussion.mdwn | 39 + doc/index.mdwn | 29 + doc/index/discussion.mdwn | 1 + doc/index/openid/discussion.mdwn | 62 + doc/install.mdwn | 48 + doc/install/discussion.mdwn | 358 + doc/local.css | 3 + doc/logo.mdwn | 69 + doc/logo/discussion.mdwn | 1 + doc/logo/favicon.svgz | Bin 0 -> 1412 bytes doc/logo/ikiwiki.png | Bin 0 -> 1157 bytes doc/logo/ikiwiki.svgz | Bin 0 -> 6276 bytes doc/logo/ikiwiki_button.png | Bin 0 -> 974 bytes doc/logo/ikiwiki_large.png | Bin 0 -> 1952 bytes doc/logo/ikiwiki_old.png | Bin 0 -> 335 bytes doc/logo/ikiwiki_old2.png | Bin 0 -> 1275 bytes doc/logo/ikiwiki_old2.svgz | Bin 0 -> 28488 bytes doc/news.mdwn | 9 + doc/news/Article_on_Ikiwiki_as_a_BTS.mdwn | 1 + doc/news/code_swarm.mdwn | 34 + doc/news/code_swarm/code_swarm.config | 51 + doc/news/code_swarm/code_swarm_log.pl | 25 + doc/news/code_swarm/discussion.mdwn | 3 + doc/news/code_swarm/screenshot.png | Bin 0 -> 64320 bytes doc/news/consultant_list.mdwn | 17 + doc/news/discussion.mdwn | 35 + doc/news/donations.mdwn | 1 + doc/news/git_push_to_this_wiki.mdwn | 3 + .../git_push_to_this_wiki/discussion.mdwn | 37 + doc/news/ikiwiki-hosting.mdwn | 16 + .../ikiwiki_accepted_for_Summer_of_Code.mdwn | 5 + doc/news/ikiwiki_screencast.mdwn | 12 + doc/news/ikiwiki_screencast/discussion.mdwn | 8 + doc/news/ikiwiki_version_2.0.mdwn | 32 + doc/news/ikiwiki_version_3.0.mdwn | 42 + doc/news/irc_channel.mdwn | 6 + doc/news/moved_to_git.mdwn | 10 + doc/news/moved_to_git/discussion.mdwn | 43 + doc/news/new_domain_name.mdwn | 1 + doc/news/no_more_email_notifications.mdwn | 14 + doc/news/openid.mdwn | 13 + doc/news/openid/discussion.mdwn | 96 + doc/news/server_move.mdwn | 9 + doc/news/server_move_2009.mdwn | 6 + doc/news/server_speed.mdwn | 9 + doc/news/server_speed/discussion.mdwn | 1 + doc/news/stylesheets.mdwn | 16 + doc/news/stylesheets/discussion.mdwn | 3 + doc/news/version_3.20121212.mdwn | 6 + doc/news/version_3.20130212.mdwn | 18 + doc/news/version_3.20130504.mdwn | 11 + doc/news/version_3.20130518.mdwn | 9 + doc/news/version_3.20130710.mdwn | 23 + doc/pagehistory.mdwn | 8 + doc/patch.mdwn | 12 + doc/patch/core.mdwn | 7 + doc/plugins.mdwn | 17 + doc/plugins/404.mdwn | 24 + doc/plugins/404/discussion.mdwn | 3 + doc/plugins/aggregate.mdwn | 57 + doc/plugins/aggregate/discussion.mdwn | 137 + doc/plugins/amazon_s3.mdwn | 68 + doc/plugins/amazon_s3/discussion.mdwn | 18 + doc/plugins/anonok.mdwn | 19 + doc/plugins/attachment.mdwn | 27 + doc/plugins/autoindex.mdwn | 10 + doc/plugins/autoindex/discussion.mdwn | 84 + doc/plugins/blogspam.mdwn | 32 + doc/plugins/brokenlinks.mdwn | 9 + doc/plugins/calendar.mdwn | 36 + doc/plugins/calendar/discussion.mdwn | 23 + doc/plugins/camelcase.mdwn | 13 + doc/plugins/color.mdwn | 5 + doc/plugins/comments.mdwn | 55 + doc/plugins/comments/discussion.mdwn | 232 + doc/plugins/conditional.mdwn | 5 + doc/plugins/conditional/discussion.mdwn | 76 + doc/plugins/contrib.mdwn | 7 + doc/plugins/contrib/album.mdwn | 140 + doc/plugins/contrib/album/discussion.mdwn | 458 + doc/plugins/contrib/asymptote.mdwn | 141 + .../ikiwiki/directive/asymptote.mdwn | 27 + doc/plugins/contrib/attach.mdwn | 47 + doc/plugins/contrib/attach/discussion.mdwn | 18 + doc/plugins/contrib/bibtex.mdwn | 59 + doc/plugins/contrib/created_in_future.mdwn | 18 + ...pyright__42___and___42__license__42__.mdwn | 62 + doc/plugins/contrib/dynamiccookies.mdwn | 12 + doc/plugins/contrib/field.mdwn | 219 + doc/plugins/contrib/field/discussion.mdwn | 407 + doc/plugins/contrib/flattr.mdwn | 48 + doc/plugins/contrib/flattr/discussion.mdwn | 9 + doc/plugins/contrib/ftemplate.mdwn | 25 + doc/plugins/contrib/ftemplate/discussion.mdwn | 33 + .../ikiwiki/directive/ftemplate.mdwn | 106 + doc/plugins/contrib/gallery.mdwn | 39 + doc/plugins/contrib/gallery/discussion.mdwn | 45 + doc/plugins/contrib/getfield.mdwn | 137 + doc/plugins/contrib/getfield/discussion.mdwn | 79 + doc/plugins/contrib/googlemaps.mdwn | 21 + .../contrib/googlemaps/discussion.mdwn | 16 + doc/plugins/contrib/groupfile.mdwn | 105 + doc/plugins/contrib/highlightcode.mdwn | 10 + .../contrib/ikiwiki/directive/album.mdwn | 56 + .../contrib/ikiwiki/directive/albumimage.mdwn | 26 + .../ikiwiki/directive/albumsection.mdwn | 29 + .../ikiwiki/directive/jssearchfield.mdwn | 42 + .../contrib/ikiwiki/directive/ymlfront.mdwn | 17 + .../directive/ymlfront/discussion.mdwn | 37 + doc/plugins/contrib/imailhide.mdwn | 65 + doc/plugins/contrib/img.mdwn | 14 + doc/plugins/contrib/img/discussion.mdwn | 51 + doc/plugins/contrib/jscalendar.mdwn | 45 + doc/plugins/contrib/jssearchfield.mdwn | 35 + doc/plugins/contrib/justlogin.mdwn | 52 + doc/plugins/contrib/linguas.mdwn | 107 + doc/plugins/contrib/livefyre.mdwn | 14 + doc/plugins/contrib/localfavicon.mdwn | 7 + doc/plugins/contrib/mailbox.mdwn | 18 + doc/plugins/contrib/mailbox/discussion.mdwn | 8 + doc/plugins/contrib/mandoc.mdwn | 12 + doc/plugins/contrib/mathjax.mdwn | 13 + doc/plugins/contrib/mediawiki.mdwn | 10 + doc/plugins/contrib/mediawiki/discussion.mdwn | 9 + doc/plugins/contrib/monthcalendar.mdwn | 23 + doc/plugins/contrib/mscgen.mdwn | 52 + doc/plugins/contrib/navbar.mdwn | 40 + doc/plugins/contrib/navbar/discussion.mdwn | 2 + doc/plugins/contrib/newpage.mdwn | 29 + doc/plugins/contrib/newpage/discussion.mdwn | 10 + doc/plugins/contrib/opml.mdwn | 11 + doc/plugins/contrib/opml/discussion.mdwn | 4 + doc/plugins/contrib/pagespec_alias.mdwn | 28 + doc/plugins/contrib/pandoc.mdwn | 6 + doc/plugins/contrib/plusone.mdwn | 35 + doc/plugins/contrib/pod.mdwn | 38 + doc/plugins/contrib/pod/discussion.mdwn | 14 + doc/plugins/contrib/postal.mdwn | 35 + doc/plugins/contrib/postal/discussion.mdwn | 24 + doc/plugins/contrib/proxies.mdwn | 13 + doc/plugins/contrib/report.mdwn | 26 + doc/plugins/contrib/report/discussion.mdwn | 80 + .../report/ikiwiki/directive/report.mdwn | 175 + doc/plugins/contrib/sar.mdwn | 109 + doc/plugins/contrib/screenplay.pm.mdwn | 320 + doc/plugins/contrib/siterel2pagerel.mdwn | 30 + doc/plugins/contrib/sourcehighlight.mdwn | 30 + doc/plugins/contrib/syntax.mdwn | 65 + doc/plugins/contrib/syntax/discussion.mdwn | 23 + doc/plugins/contrib/tex4ht.mdwn | 15 + doc/plugins/contrib/texinfo.mdwn | 122 + doc/plugins/contrib/tracking.mdwn | 30 + doc/plugins/contrib/unixauth.mdwn | 21 + doc/plugins/contrib/unixauth/discussion.mdwn | 38 + doc/plugins/contrib/unixrelpagespec.mdwn | 42 + doc/plugins/contrib/video.mdwn | 25 + doc/plugins/contrib/video/discussion.mdwn | 3 + doc/plugins/contrib/wc.mdwn | 22 + doc/plugins/contrib/xslt.mdwn | 39 + doc/plugins/contrib/xslt/discussion.mdwn | 49 + doc/plugins/contrib/ymlfront.mdwn | 143 + doc/plugins/contrib/ymlfront/discussion.mdwn | 31 + doc/plugins/creole.mdwn | 22 + doc/plugins/creole/discussion.mdwn | 22 + doc/plugins/cutpaste.mdwn | 7 + doc/plugins/date.mdwn | 6 + doc/plugins/ddate.mdwn | 10 + doc/plugins/discussion.mdwn | 42 + doc/plugins/editdiff.mdwn | 13 + doc/plugins/editdiff/discussion.mdwn | 5 + doc/plugins/editpage.mdwn | 6 + doc/plugins/editpage/discussion.mdwn | 24 + doc/plugins/edittemplate.mdwn | 6 + doc/plugins/embed.mdwn | 53 + doc/plugins/favicon.mdwn | 7 + doc/plugins/favicon/discussion.mdwn | 19 + doc/plugins/filecheck.mdwn | 17 + doc/plugins/filecheck/discussion.mdwn | 85 + doc/plugins/flattr.mdwn | 9 + doc/plugins/format.mdwn | 9 + doc/plugins/format/discussion.mdwn | 15 + doc/plugins/fortune.mdwn | 14 + doc/plugins/getsource.mdwn | 14 + doc/plugins/getsource/discussion.mdwn | 3 + doc/plugins/goodstuff.mdwn | 29 + doc/plugins/goodstuff/discussion.mdwn | 8 + doc/plugins/google.mdwn | 11 + doc/plugins/google/discussion.mdwn | 25 + doc/plugins/goto.mdwn | 10 + doc/plugins/graphviz.mdwn | 25 + doc/plugins/haiku.mdwn | 12 + doc/plugins/haiku/discussion.mdwn | 5 + doc/plugins/headinganchors.mdwn | 7 + doc/plugins/headinganchors/discussion.mdwn | 49 + doc/plugins/highlight.mdwn | 77 + doc/plugins/highlight/discussion.mdwn | 23 + doc/plugins/hnb.mdwn | 6 + doc/plugins/hnb/discussion.mdwn | 28 + doc/plugins/html.mdwn | 11 + doc/plugins/htmlbalance.mdwn | 9 + doc/plugins/htmlbalance/discussion.mdwn | 10 + doc/plugins/htmlscrubber.mdwn | 51 + doc/plugins/htmlscrubber/discussion.mdwn | 18 + doc/plugins/htmltidy.mdwn | 11 + doc/plugins/httpauth.mdwn | 37 + doc/plugins/img.mdwn | 14 + doc/plugins/img/discussion.mdwn | 12 + doc/plugins/inline.mdwn | 6 + doc/plugins/install.mdwn | 19 + doc/plugins/link.mdwn | 5 + doc/plugins/linkmap.mdwn | 14 + doc/plugins/listdirectives.mdwn | 16 + doc/plugins/localstyle.mdwn | 12 + doc/plugins/lockedit.mdwn | 23 + doc/plugins/lockedit/discussion.mdwn | 18 + doc/plugins/map.mdwn | 11 + doc/plugins/map/discussion.mdwn | 49 + doc/plugins/mdwn.mdwn | 23 + doc/plugins/mdwn/discussion.mdwn | 7 + doc/plugins/meta.mdwn | 5 + doc/plugins/meta/discussion.mdwn | 18 + doc/plugins/mirrorlist.mdwn | 22 + doc/plugins/moderatedcomments.mdwn | 12 + doc/plugins/more.mdwn | 6 + doc/plugins/more/discussion.mdwn | 7 + doc/plugins/notifyemail.mdwn | 14 + doc/plugins/notifyemail/discussion.mdwn | 5 + doc/plugins/opendiscussion.mdwn | 11 + doc/plugins/openid.mdwn | 32 + doc/plugins/openid/discussion.mdwn | 26 + doc/plugins/orphans.mdwn | 16 + doc/plugins/orphans/discussion.mdwn | 22 + doc/plugins/osm.mdwn | 31 + doc/plugins/otl.mdwn | 6 + doc/plugins/pagecount.mdwn | 11 + doc/plugins/pagestats.mdwn | 6 + doc/plugins/pagetemplate.mdwn | 6 + doc/plugins/parentlinks.mdwn | 5 + doc/plugins/passwordauth.mdwn | 33 + doc/plugins/passwordauth/discussion.mdwn | 151 + doc/plugins/pingee.mdwn | 11 + doc/plugins/pingee/discussion.mdwn | 9 + doc/plugins/pinger.mdwn | 20 + doc/plugins/po.mdwn | 260 + doc/plugins/po/discussion.mdwn | 721 + doc/plugins/poll.mdwn | 5 + doc/plugins/poll/discussion.mdwn | 1 + doc/plugins/polygen.mdwn | 25 + doc/plugins/postsparkline.mdwn | 14 + doc/plugins/prettydate.mdwn | 20 + doc/plugins/progress.mdwn | 5 + doc/plugins/rawhtml.mdwn | 13 + doc/plugins/rawhtml/discussion.mdwn | 7 + doc/plugins/recentchanges.mdwn | 32 + doc/plugins/recentchanges/discussion.mdwn | 17 + doc/plugins/recentchangesdiff.mdwn | 9 + doc/plugins/relativedate.mdwn | 11 + doc/plugins/remove.mdwn | 7 + doc/plugins/rename.mdwn | 12 + doc/plugins/repolist.mdwn | 17 + doc/plugins/rst.mdwn | 18 + doc/plugins/rst/discussion.mdwn | 81 + doc/plugins/rsync.mdwn | 19 + doc/plugins/rsync/discussion.mdwn | 79 + doc/plugins/search.mdwn | 18 + doc/plugins/search/discussion.mdwn | 1 + doc/plugins/shortcut.mdwn | 9 + doc/plugins/shortcut/discussion.mdwn | 18 + doc/plugins/sidebar.mdwn | 28 + doc/plugins/sidebar/discussion.mdwn | 12 + doc/plugins/signinedit.mdwn | 5 + doc/plugins/smiley.mdwn | 9 + doc/plugins/sortnaturally.mdwn | 6 + doc/plugins/sparkline.mdwn | 22 + doc/plugins/table.mdwn | 12 + doc/plugins/table/discussion.mdwn | 73 + doc/plugins/tag.mdwn | 24 + doc/plugins/tag/discussion.mdwn | 31 + doc/plugins/template.mdwn | 7 + doc/plugins/testpagespec.mdwn | 6 + doc/plugins/teximg.mdwn | 15 + doc/plugins/teximg/discussion.mdwn | 5 + doc/plugins/textile.mdwn | 6 + doc/plugins/theme.mdwn | 18 + doc/plugins/theme/discussion.mdwn | 26 + doc/plugins/toc.mdwn | 5 + doc/plugins/toc/discussion.mdwn | 10 + doc/plugins/toggle.mdwn | 7 + doc/plugins/toggle/discussion.mdwn | 43 + doc/plugins/trail.mdwn | 76 + doc/plugins/trail/discussion.mdwn | 105 + doc/plugins/transient.mdwn | 24 + doc/plugins/txt.mdwn | 19 + doc/plugins/txt/discussion.mdwn | 33 + doc/plugins/type/auth.mdwn | 4 + doc/plugins/type/bundle.mdwn | 3 + doc/plugins/type/chrome.mdwn | 3 + doc/plugins/type/comments.mdwn | 3 + doc/plugins/type/core.mdwn | 3 + doc/plugins/type/date.mdwn | 3 + doc/plugins/type/format.mdwn | 3 + doc/plugins/type/fun.mdwn | 3 + doc/plugins/type/html.mdwn | 3 + doc/plugins/type/link.mdwn | 3 + doc/plugins/type/meta.mdwn | 3 + doc/plugins/type/slow.mdwn | 5 + doc/plugins/type/special-purpose.mdwn | 3 + doc/plugins/type/tags.mdwn | 3 + doc/plugins/type/web.mdwn | 3 + doc/plugins/type/widget.mdwn | 4 + doc/plugins/typography.mdwn | 12 + doc/plugins/underlay.mdwn | 14 + doc/plugins/userlist.mdwn | 6 + doc/plugins/version.mdwn | 7 + doc/plugins/websetup.mdwn | 27 + doc/plugins/wikitext.mdwn | 23 + doc/plugins/wmd.mdwn | 16 + doc/plugins/wmd/discussion.mdwn | 73 + doc/plugins/write.mdwn | 1396 ++ doc/plugins/write/discussion.mdwn | 46 + doc/plugins/write/external.mdwn | 146 + doc/plugins/write/tutorial.mdwn | 189 + doc/plugins/write/tutorial/discussion.mdwn | 20 + doc/post-commit.mdwn | 19 + doc/post-commit/discussion.mdwn | 123 + doc/quotes.mdwn | 3 + doc/quotes/pizza.mdwn | 4 + doc/quotes/pizza/discussion.mdwn | 1 + doc/quotes/sold.mdwn | 3 + doc/rcs.mdwn | 44 + doc/rcs/bzr.mdwn | 8 + doc/rcs/cvs.mdwn | 46 + doc/rcs/cvs/discussion.mdwn | 191 + doc/rcs/darcs.mdwn | 15 + doc/rcs/details.mdwn | 292 + doc/rcs/details/discussion.mdwn | 15 + doc/rcs/git.mdwn | 153 + doc/rcs/git/discussion.mdwn | 129 + doc/rcs/git/wiki_edit_flow.svg | 705 + doc/rcs/mercurial.mdwn | 18 + doc/rcs/monotone.mdwn | 24 + doc/rcs/svn.mdwn | 9 + doc/rcs/svn/discussion.mdwn | 13 + doc/rcs/tla.mdwn | 13 + doc/recentchanges.mdwn | 7 + doc/reviewed.mdwn | 7 + doc/roadmap.mdwn | 92 + doc/roadmap/discussion.mdwn | 32 + doc/robots.txt | 2 + doc/sandbox.mdwn | 84 + doc/sandbox/NewPage.mdwn | 1 + ...at_kind_of_a_blog_is_this__63____41__.mdwn | 3 + doc/security.mdwn | 499 + doc/security/discussion.mdwn | 33 + doc/setup.mdwn | 152 + doc/setup/byhand.mdwn | 202 + doc/setup/byhand/discussion.mdwn | 7 + doc/setup/discussion.mdwn | 271 + doc/shortcuts.mdwn | 83 + doc/shortcuts/discussion.mdwn | 21 + doc/sitemap.mdwn | 5 + doc/smileys.mdwn | 56 + doc/smileys/alert.png | Bin 0 -> 220 bytes doc/smileys/angry.png | Bin 0 -> 295 bytes doc/smileys/attention.png | Bin 0 -> 164 bytes doc/smileys/biggrin.png | Bin 0 -> 173 bytes doc/smileys/checkmark.png | Bin 0 -> 133 bytes doc/smileys/devil.png | Bin 0 -> 354 bytes doc/smileys/frown.png | Bin 0 -> 168 bytes doc/smileys/icon-error.png | Bin 0 -> 397 bytes doc/smileys/icon-info.png | Bin 0 -> 171 bytes doc/smileys/idea.png | Bin 0 -> 372 bytes doc/smileys/neutral.png | Bin 0 -> 239 bytes doc/smileys/ohwell.png | Bin 0 -> 167 bytes doc/smileys/prio1.png | Bin 0 -> 153 bytes doc/smileys/prio2.png | Bin 0 -> 158 bytes doc/smileys/prio3.png | Bin 0 -> 153 bytes doc/smileys/question.png | Bin 0 -> 302 bytes doc/smileys/redface.png | Bin 0 -> 306 bytes doc/smileys/sad.png | Bin 0 -> 182 bytes doc/smileys/smile.png | Bin 0 -> 356 bytes doc/smileys/smile2.png | Bin 0 -> 334 bytes doc/smileys/smile3.png | Bin 0 -> 326 bytes doc/smileys/smile4.png | Bin 0 -> 275 bytes doc/smileys/star_off.png | Bin 0 -> 297 bytes doc/smileys/star_on.png | Bin 0 -> 370 bytes doc/smileys/thumbs-up.png | Bin 0 -> 118 bytes doc/smileys/tired.png | Bin 0 -> 157 bytes doc/smileys/tongue.png | Bin 0 -> 176 bytes doc/soc.mdwn | 20 + doc/soc/application.mdwn | 96 + doc/soc/discussion.mdwn | 2 + doc/soc/ideas.mdwn | 8 + doc/style.css | 551 + doc/tags.mdwn | 26 + doc/tags/discussion.mdwn | 20 + doc/templates.mdwn | 94 + doc/templates/discussion.mdwn | 27 + doc/templates/gitbranch.mdwn | 16 + doc/templates/links.mdwn | 16 + doc/templates/note.mdwn | 11 + doc/templates/plugin.mdwn | 19 + doc/templates/popup.mdwn | 16 + doc/theme_market.mdwn | 13 + doc/themes.mdwn | 34 + doc/themes/actiontabs_small.png | Bin 0 -> 19202 bytes doc/themes/blueview_small.png | Bin 0 -> 18543 bytes doc/themes/discussion.mdwn | 20 + doc/themes/goldtype_small.png | Bin 0 -> 19240 bytes doc/themes/monochrome_small.png | Bin 0 -> 21054 bytes doc/themes/none_small.png | Bin 0 -> 18516 bytes doc/tipjar.mdwn | 25 + doc/tips.mdwn | 5 + doc/tips/Adding_Disqus_to_your_wiki.mdwn | 30 + .../discussion.mdwn | 1 + doc/tips/DreamHost.mdwn | 192 + doc/tips/DreamHost/discussion.mdwn | 18 + doc/tips/Emacs_and_markdown.html | 16 + ...ory_and_web_server_on_different_hosts.mdwn | 61 + doc/tips/Google_custom_search.mdwn | 12 + doc/tips/Importing_posts_from_Wordpress.mdwn | 102 + .../discussion.mdwn | 44 + ...ript_to_add_index.html_to_file:_links.mdwn | 63 + .../discusion.mdwn | 3 + .../discussion.mdwn | 2 + .../Make_calendar_start_week_on_Monday.mdwn | 9 + .../discussion.mdwn | 1 + doc/tips/add_chatterbox_to_blog.mdwn | 24 + .../add_chatterbox_to_blog/discussion.mdwn | 43 + doc/tips/blog_script.mdwn | 6 + doc/tips/comments_feed.mdwn | 17 + .../convert_blogger_blogs_to_ikiwiki.mdwn | 5 + doc/tips/convert_mediawiki_to_ikiwiki.mdwn | 286 + .../discussion.mdwn | 669 + doc/tips/convert_moinmoin_to_ikiwiki.mdwn | 109 + .../discussion.mdwn | 5 + doc/tips/distributed_wikis.mdwn | 46 + doc/tips/distributed_wikis/discussion.mdwn | 7 + doc/tips/dot_cgi.mdwn | 111 + doc/tips/dot_cgi/discussion.mdwn | 51 + doc/tips/emacs_syntax_highlighting.mdwn | 3 + doc/tips/embedding_content.mdwn | 35 + .../follow_wikilinks_from_inside_vim.mdwn | 47 + doc/tips/github.mdwn | 64 + .../howto_avoid_flooding_aggregators.mdwn | 28 + doc/tips/howto_limit_to_admin_users.mdwn | 9 + doc/tips/htaccess_file.mdwn | 27 + doc/tips/html5.mdwn | 27 + ...iki_as_a_requirements_management_tool.mdwn | 95 + .../discussion.mdwn | 21 + doc/tips/ikiwiki_on_mac_os_x.mdwn | 218 + doc/tips/ikiwiki_via_gopher.mdwn | 22 + doc/tips/ikiwiki_via_gopher/discussion.mdwn | 8 + doc/tips/importing_posts_from_typo.mdwn | 1 + .../ikiwiki-wordpress-import.mdwn | 468 + doc/tips/inside_dot_ikiwiki.mdwn | 91 + doc/tips/inside_dot_ikiwiki/discussion.mdwn | 66 + ...ntegrated_issue_tracking_with_ikiwiki.mdwn | 277 + .../discussion.mdwn | 32 + doc/tips/laptop_wiki_with_git.mdwn | 71 + doc/tips/laptop_wiki_with_git/discussion.mdwn | 15 + doc/tips/laptop_wiki_with_git_extended.mdwn | 43 + .../discussion.mdwn | 1 + doc/tips/mailman_subscription_form.mdwn | 10 + doc/tips/markdown_and_eclipse.mdwn | 4 + doc/tips/mathopd_permissions.mdwn | 15 + doc/tips/nearlyfreespeech.mdwn | 108 + doc/tips/nearlyfreespeech/discussion.mdwn | 22 + doc/tips/optimising_ikiwiki.mdwn | 188 + doc/tips/parentlinks_style.mdwn | 143 + doc/tips/psgi.mdwn | 21 + doc/tips/redirections_for_usedirs.mdwn | 39 + doc/tips/spam_and_softwaresites.mdwn | 87 + .../spam_and_softwaresites/discussion.mdwn | 8 + doc/tips/switching_to_usedirs.mdwn | 28 + doc/tips/switching_to_usedirs/discussion.mdwn | 24 + doc/tips/untrusted_git_push.mdwn | 114 + doc/tips/untrusted_git_push/discussion.mdwn | 33 + doc/tips/upgrade_to_3.0.mdwn | 95 + ...web_interface_with_a_real_text_editor.mdwn | 17 + .../discussion.mdwn | 2 + doc/tips/vim_and_ikiwiki.mdwn | 28 + doc/tips/vim_syntax_highlighting.mdwn | 20 + .../vim_syntax_highlighting/discussion.mdwn | 8 + doc/tips/vim_syntax_highlighting/ikiwiki.vim | 71 + doc/tips/wikiannounce.mdwn | 8 + doc/tips/yaml_setup_files.mdwn | 12 + doc/todo.mdwn | 21 + doc/todo/ACL.mdwn | 98 + ...esults_in_unnecessary_feed_generation.mdwn | 80 + doc/todo/Account-creation_password.mdwn | 6 + doc/todo/Account_moderation.mdwn | 12 + ...d_DATE_parameter_for_use_in_templates.mdwn | 86 + doc/todo/Add_HTML_support_to_po_plugin.mdwn | 9 + ...list_available_pre-processor_commands.mdwn | 141 + doc/todo/Add_basename_in_edittemplate.mdwn | 8 + doc/todo/Add_camelcase_exclusions.mdwn | 23 + ...mmit_messages_for_add__47__edit_pages.mdwn | 43 + ...ve_commit_messages_for_removing_pages.mdwn | 32 + .../Add_label_to_search_form_input_field.mdwn | 56 + doc/todo/Add_nicer_math_formatting.mdwn | 28 + .../Add_showdown_GUI_input__47__edit.mdwn | 31 + ...dd_space_before_slash_in_parent_links.mdwn | 156 + ...atest_Text::Markdown_as_found_on_CPAN.mdwn | 45 + doc/todo/Adjust_goodstuff.mdwn | 12 + ..._the_path_in_addition_to_the_basename.mdwn | 79 + doc/todo/Allow_change_of_wiki_file_types.mdwn | 85 + ..._disabling_edit_and_preferences_links.mdwn | 81 + .../Allow_edittemplate_to_set_file_type.mdwn | 44 + .../Allow_filenames_that_are_all_type.mdwn | 41 + .../Allow_per-page_template_selection.mdwn | 43 + ...it_form_comment_field_to_be_mandatory.mdwn | 22 + ...t_to_extend_Mercurial_backend_support.mdwn | 258 + ..._and_maintain_Mercurial_wrapper_hooks.mdwn | 240 + .../Auto-setup_should_default_to_YAML.mdwn | 3 + ...etup_from_wikilist_in_Debian_package_.mdwn | 29 + doc/todo/BTS_integration.mdwn | 11 + ...dir_along_with_bestlink_in_IkiWiki.pm.mdwn | 51 + .../discussion.mdwn | 6 + doc/todo/Better_bug_tracking_support.mdwn | 71 + ...Better_reporting_of_validation_errors.mdwn | 2 + doc/todo/BibTeX.mdwn | 74 + doc/todo/BrowserID.mdwn | 25 + doc/todo/CGI_method_to_pullrefresh.mdwn | 7 + doc/todo/CSS_classes_for_links.mdwn | 138 + doc/todo/CVS_backend.mdwn | 16 + ...ar:_listing_multiple_entries_per_day_.mdwn | 94 + doc/todo/Case.mdwn | 4 + doc/todo/Commit_emails:_ones_own_changes.mdwn | 9 + ...m_length_of_log_message_for_web_edits.mdwn | 5 + .../Configureable_separator_of_page_name.mdwn | 12 + ...mend_gcc_+_libc6-dev__44___not_Depend.mdwn | 22 + doc/todo/Default_text_for_new_pages.mdwn | 104 + doc/todo/Does_not_support_non-UTF8_files.mdwn | 7 + doc/todo/Editing_po_files.mdwn | 5 + ...filtering_of_files_indexed_for_search.mdwn | 7 + doc/todo/Extensible_inlining.mdwn | 263 + doc/todo/Feature_parity_with_Trac.mdwn | 22 + ...__from_GitHub_Flavored_Markdown__41__.mdwn | 44 + ...o_not_put_a_border_around_image_links.mdwn | 7 + doc/todo/Fix_selflink_in_po_plugin.mdwn | 21 + .../FormBuilder__95__Template__95__patch.mdwn | 10 + ...FormattingHelp_should_open_new_window.mdwn | 1 + doc/todo/Gallery.mdwn | 83 + ...lates_inserted_by_the_template_plugin.mdwn | 111 + doc/todo/Google_Analytics_support.mdwn | 31 + doc/todo/Google_Sitemap_protocol.mdwn | 60 + ..._pdf__44___openoffice__44___documents.mdwn | 5 + doc/todo/IRC_topic.mdwn | 10 + doc/todo/Improve_display_of_OpenIDs.mdwn | 5 + doc/todo/Improve_markdown_speed.mdwn | 33 + doc/todo/Improve_signin_form_layout.mdwn | 44 + ...ing_the_efficiency_of_match__95__glob.mdwn | 228 + ..._plugin_option_to_show_full_page_path.mdwn | 30 + ..._36__tagbase_should_be_in__by_default.mdwn | 15 + doc/todo/Mailing_list.mdwn | 36 + .../Make_example_setup_file_consistent.mdwn | 33 + doc/todo/Mercurial_backend_update.mdwn | 969 ++ doc/todo/Modern_standard_layout.mdwn | 39 + ...re_flexible_po-plugin_for_translation.mdwn | 5 + ..._teximg_latex_preamble_to_config_file.mdwn | 156 + doc/todo/Moving_Pages.mdwn | 222 + .../Multiple_categorization_namespaces.mdwn | 103 + .../New_preprocessor_directive_syntax.mdwn | 21 + .../discussion.mdwn | 19 + doc/todo/OpenSearch.mdwn | 38 + ...on_to_disable_date_footer_for_inlines.mdwn | 31 + .../Option_to_make_title_an_h1__63__.mdwn | 14 + .../Overlay_directory_for_pagetemplates.mdwn | 9 + doc/todo/Pagination_next_prev_links.mdwn | 68 + ...__34___links_for_popular_feed_readers.mdwn | 6 + ...-compilation_inclusion_of_the_sidebar.mdwn | 67 + doc/todo/Print_link.mdwn | 73 + doc/todo/RSS_fields.mdwn | 25 + doc/todo/RSS_links.mdwn | 17 + doc/todo/Raw_view_link.mdwn | 19 + ...hanges_page_links_without_cgi_wrapper.mdwn | 26 + ...multiple_destinations_from_one_source.mdwn | 85 + ...StructuredText_links_to_ikiwiki_pages.mdwn | 333 + ...Restrict_formats_allowed_for_comments.mdwn | 99 + doc/todo/Restrict_page_viewing.mdwn | 42 + doc/todo/Separate_OpenIDs_and_usernames.mdwn | 55 + ...ry_date_to_be_used_by_calendar_plugin.mdwn | 210 + .../discussion.mdwn | 44 + ...plates_for_whole_sections_of_the_site.mdwn | 41 + doc/todo/Short_wikilinks.mdwn | 104 + doc/todo/Shorter_feeds.mdwn | 11 + doc/todo/Silence_monotone_warning.mdwn | 17 + ...dencies_into_separate_Debian_packages.mdwn | 40 + ...n_should_be_subpage_if_siblings_exist.mdwn | 26 + doc/todo/Support_MultiMarkdown_3.X.mdwn | 10 + doc/todo/Support_XML-RPC-based_blogging.mdwn | 17 + .../Support__47__Switch_to_MultiMarkdown.mdwn | 35 + doc/todo/Support_preprocessing_CSS.mdwn | 1 + .../Support_subdirectory_of_a_git_repo.mdwn | 9 + .../Support_tab_insertion_in_textarea.mdwn | 15 + ...of_link__40____41___within_a_pagespec.mdwn | 45 + ...ags_list_in_page_footer_uses_basename.mdwn | 11 + ...rack_Markdown_Standardisation_Efforts.mdwn | 7 + doc/todo/Unit_tests.mdwn | 10 + doc/todo/Untrusted_push_in_Monotone.mdwn | 28 + doc/todo/Updated_bug_tracking_example.mdwn | 136 + .../Using_page_titles_in_internal_links.mdwn | 3 + doc/todo/Wikilink_to_a_symbolic_link.mdwn | 5 + .../Wrapper_config_with_multiline_regexp.mdwn | 36 + doc/todo/Zoned_ikiwiki.mdwn | 64 + ...this_page__34___checkbox_on_edit_form.mdwn | 10 + ...ing_functionality_for_the_meta_plugin.mdwn | 70 + ..._47___should_point_to_top-level_index.mdwn | 3 + .../a_navbar_based_on_page_properties.mdwn | 48 + doc/todo/abbreviation.mdwn | 7 + ..._force_particular_UUIDs_on_blog_posts.mdwn | 24 + doc/todo/absolute_urls_in_wikilinks.mdwn | 20 + doc/todo/access_keys.mdwn | 286 + doc/todo/ad-hoc_plugins.mdwn | 66 + ..._forward_age_sorting_option_to_inline.mdwn | 34 + ..._new_pages_by_using_the_web_interface.mdwn | 79 + ...ommit_message_for_rename__44___remove.mdwn | 5 + doc/todo/aggregate_401_handling.mdwn | 20 + doc/todo/aggregate_locking.mdwn | 64 + doc/todo/aggregate_to_internal_pages.mdwn | 59 + doc/todo/aggregation.mdwn | 3 + doc/todo/alias_directive.mdwn | 72 + .../allow_CGI_to_create_dynamic_pages.mdwn | 3 + ...TMPL__95__LOOP_in_template_directives.mdwn | 278 + ...ning_a_user_when_moderating_a_comment.mdwn | 1 + .../allow_creation_of_non-existent_pages.mdwn | 13 + doc/todo/allow_disabling_backlinks.mdwn | 18 + .../allow_displaying_number_of_comments.mdwn | 30 + ...m_the___34__add_a_new_post__34___form.mdwn | 12 + .../allow_plugins_to_add_sorting_methods.mdwn | 304 + .../allow_site-wide_meta_definitions.mdwn | 169 + .../allow_wiki_syntax_in_commit_messages.mdwn | 21 + doc/todo/anon_push_of_comments.mdwn | 14 + doc/todo/anti-spam_protection.mdwn | 30 + .../apache_404_ErrorDocument_handler.mdwn | 25 + doc/todo/applydiff_plugin.mdwn | 110 + doc/todo/assumes_system_perl.mdwn | 20 + doc/todo/attachments.mdwn | 22 + ...ate_tag_pages_according_to_a_template.mdwn | 270 + doc/todo/auto_getctime_on_fresh_build.mdwn | 13 + doc/todo/auto_publish_expire.mdwn | 33 + doc/todo/auto_rebuild_on_template_change.mdwn | 78 + ...utoindex_should_use_add__95__autofile.mdwn | 120 + .../automatic_rebuilding_of_html_pages.mdwn | 5 + ...of_syntax_plugin_on_source_code_files.mdwn | 17 + .../discussion.mdwn | 215 + doc/todo/avatar.mdwn | 31 + doc/todo/avatar/discussion.mdwn | 1 + ..._attachement_ui_if_upload_not_allowed.mdwn | 25 + doc/todo/avoid_thrashing.mdwn | 22 + doc/todo/backlinks_result_is_lossy.mdwn | 12 + .../basewiki_should_be_self_documenting.mdwn | 40 + ...be_more_selective_about_running_hooks.mdwn | 68 + ...idebar_to_allow_for_multiple_sidebars.mdwn | 125 + doc/todo/beef_up_signin_page.mdwn | 17 + doc/todo/block_external_links.mdwn | 16 + doc/todo/blocking_ip_ranges.mdwn | 7 + doc/todo/blogging.mdwn | 137 + doc/todo/blogpost_plugin.mdwn | 156 + doc/todo/blogs.mdwn | 4 + doc/todo/blogspam_training.mdwn | 31 + .../break_up_page_template_into_subfiles.mdwn | 36 + ...kenlinks_should_group_links_to_a_page.mdwn | 21 + doc/todo/bzr.mdwn | 194 + doc/todo/cache_backlinks.mdwn | 25 + ...hive_browsing_via_a_calendar_frontend.mdwn | 124 + ...alendar_with___34__create__34___links.mdwn | 10 + .../incomplete_patch.pl | 36 + ...ate-server-info_from_post-udpate_hook.mdwn | 15 + doc/todo/canonical_feed_location.mdwn | 16 + doc/todo/capitalize_title.mdwn | 31 + doc/todo/cas_authentication.mdwn | 184 + ...ate_and_mdate_available_for_templates.mdwn | 15 + doc/todo/cgi_hooks_get_session_objects.mdwn | 5 + doc/todo/clear_page_to_delete.mdwn | 33 + doc/todo/clickable-openid-urls-in-logs.mdwn | 23 + doc/todo/color_plugin.mdwn | 231 + doc/todo/comment_by_mail.mdwn | 3 + doc/todo/comment_by_mail/discussion.mdwn | 25 + doc/todo/comment_moderation_feed.mdwn | 16 + doc/todo/comments.mdwn | 170 + ...tional_text_based_on_ikiwiki_features.mdwn | 128 + doc/todo/conditional_underlay_files.mdwn | 29 + doc/todo/configurable_markdown_path.mdwn | 64 + ...onfigurable_tidy_command_for_htmltidy.mdwn | 8 + doc/todo/configurable_timezones.mdwn | 7 + doc/todo/conflict_free_comment_merges.mdwn | 23 + doc/todo/consistent_smileys.mdwn | 22 + doc/todo/copyright_based_on_pagespec.mdwn | 10 + ...pdated_time_information_for_the_feeds.mdwn | 113 + doc/todo/countdown_directive.mdwn | 5 + doc/todo/credentials_page.mdwn | 33 + doc/todo/ctime_on_blog_post_pages_.mdwn | 11 + doc/todo/custom_location_for_openlayers.mdwn | 17 + doc/todo/darcs.mdwn | 53 + doc/todo/datearchives-plugin.mdwn | 77 + doc/todo/default_content_for_new_post.mdwn | 66 + doc/todo/default_name_for_new_post.mdwn | 3 + doc/todo/dependency_types.mdwn | 579 + ...iption_meta_param_passed_to_templates.mdwn | 10 + doc/todo/different_search_engine.mdwn | 332 + doc/todo/directive_docs.mdwn | 79 + doc/todo/discuss_without_login.mdwn | 19 + doc/todo/discussion_page_as_blog.mdwn | 33 + .../discussion/castle.mdwn | 3 + .../discussion/castle/discussion.mdwn | 1 + .../discussion/Don__39__t_like_foo.mdwn | 3 + .../Don__39__t_like_foo/how_about_bar.mdwn | 1 + .../discussion/Don__39__t_like_foo/sdf.mdwn | 5 + .../castle/discussion/foo_is_ok.mdwn | 1 + .../discussion/castle/discussion/test.mdwn | 1 + doc/todo/do_not_make_links_backwards.mdwn | 95 + doc/todo/done.mdwn | 3 + ...ble-click_protection_for_form_buttons.mdwn | 5 + doc/todo/doxygen_support.mdwn | 7 + doc/todo/dynamic_rootpage.mdwn | 35 + doc/todo/ease_archivepage_styling.mdwn | 59 + ...edit_form:_no_fixed_size_for_textarea.mdwn | 52 + ...ook_in_templates_directory_by_default.mdwn | 8 + ...uld_support_uuid__44___date_variables.mdwn | 19 + doc/todo/else_parameter_for_map_plugin.mdwn | 56 + doc/todo/enable-htaccess-files.mdwn | 80 + ...nable_arbitrary_markup_for_directives.mdwn | 47 + doc/todo/etherpad_support.mdwn | 22 + doc/todo/excluding_commit_mails.mdwn | 19 + doc/todo/fancypodcast.mdwn | 330 + ..._or_modperl_installation_instructions.mdwn | 18 + .../feed_enhancements_for_inline_pages.mdwn | 132 + doc/todo/fileupload.mdwn | 63 + doc/todo/fileupload/discussion.mdwn | 45 + doc/todo/fileupload/soc-proposal.mdwn | 71 + .../fileupload/soc-proposal/discussion.mdwn | 46 + doc/todo/filtering_content_when_inlining.mdwn | 16 + ...ntrol_over___60__object___47____62__s.mdwn | 98 + doc/todo/firm_up_plugin_interface.mdwn | 96 + ..._amazon_s3_pre-gzip-encode_safe_files.mdwn | 17 + doc/todo/format_escape.mdwn | 292 + ...rtune:_select_options_via_environment.mdwn | 34 + doc/todo/friendly_markup_names.mdwn | 13 + ...generated_po_stuff_not_ignored_by_git.mdwn | 6 + ...eric___39__do__61__goto__39___for_CGI.mdwn | 35 + doc/todo/generic_insert_links.mdwn | 24 + doc/todo/geotagging.mdwn | 7 + ...tive_path___40__fixes_git_ctime__41__.mdwn | 24 + doc/todo/git_attribution.mdwn | 9 + doc/todo/git_attribution/discussion.mdwn | 98 + ..._recentchanges_should_not_show_merges.mdwn | 20 + doc/todo/graphviz.mdwn | 19 + ...ion_for_man_pages_and_w3m_cgi_wrapper.mdwn | 94 + doc/todo/headless_git_branches.mdwn | 113 + doc/todo/hidden_links__47__tags.mdwn | 13 + ...o_detect_markdown_links_to_wiki_pages.mdwn | 1 + doc/todo/html.mdwn | 6 + doc/todo/htmlvalidation.mdwn | 47 + doc/todo/htpasswd_mirror_of_the_userdb.mdwn | 29 + doc/todo/http_bl_support.mdwn | 67 + doc/todo/httpauth_example.mdwn | 8 + doc/todo/httpauth_example/discussion.mdwn | 1 + ...auth_feature_parity_with_passwordauth.mdwn | 28 + doc/todo/hyphenation.mdwn | 32 + doc/todo/ikibot.mdwn | 9 + doc/todo/improve_globlists.mdwn | 8 + doc/todo/improved_mediawiki_support.mdwn | 9 + doc/todo/improved_parentlinks_styling.mdwn | 9 + doc/todo/index.html_allowed.mdwn | 126 + .../inline:_numerical_ordering_by_title.mdwn | 254 + ...e_directive_should_support_pagination.mdwn | 8 + ...ion_for_pagespec-specific_show__61__N.mdwn | 3 + ...plugin:_ability_to_override_feed_name.mdwn | 29 + ...ne_plugin:_hide_feed_buttons_if_empty.mdwn | 7 + ...plugin:_specifying_ordered_page_names.mdwn | 19 + doc/todo/inline_postform_autotitles.mdwn | 67 + doc/todo/inline_raw_files.mdwn | 115 + doc/todo/inlines_inheriting_links.mdwn | 39 + ...Iceweasel_feed_subscription_mechanism.mdwn | 13 + doc/todo/interactive_todo_lists.mdwn | 49 + .../internal_definition_list_support.mdwn | 54 + doc/todo/l10n.mdwn | 84 + ...nguage_definition_for_the_meta_plugin.mdwn | 118 + doc/todo/latex.mdwn | 244 + doc/todo/latex/discussion.mdwn | 6 + .../let_inline_plugin_use_pagetemplates.mdwn | 5 + ..._markup_formats_available_for_editing.mdwn | 8 + doc/todo/link_map.mdwn | 6 + ...link_plugin_perhaps_too_general__63__.mdwn | 25 + doc/todo/linkbase.mdwn | 16 + .../linkify_and_preprocessor_ordering.mdwn | 24 + doc/todo/linktitle.mdwn | 19 + doc/todo/lists.mdwn | 3 + doc/todo/location_of_external_plugins.mdwn | 24 + doc/todo/location_of_ikiwiki-w3m.cgi.mdwn | 3 + doc/todo/logo.mdwn | 4 + doc/todo/lucene_search_engine.mdwn | 1 + doc/todo/mailnotification.mdwn | 59 + doc/todo/mailnotification/discussion.mdwn | 14 + ...ml-parser_use_encode_entities_numeric.mdwn | 19 + ...k_target_search_all_paths_as_fallback.mdwn | 27 + doc/todo/manpages.mdwn | 4 + ...entify__47__filter_on_trivial_changes.mdwn | 11 + .../matching_different_kinds_of_links.mdwn | 196 + doc/todo/mbox.mdwn | 18 + doc/todo/mdwn_itex.mdwn | 22 + doc/todo/mdwn_preview.mdwn | 339 + doc/todo/mdwn_preview/discussion.mdwn | 1 + doc/todo/mercurial.mdwn | 129 + doc/todo/mercurial/discussion.mdwn | 9 + doc/todo/meta_rcsid.mdwn | 51 + doc/todo/metadata.mdwn | 19 + ...documentation_for_recentchanges_feeds.mdwn | 28 + ...list_with_per-mirror_usedirs_settings.mdwn | 103 + doc/todo/missingparents.pm.mdwn | 261 + doc/todo/modify_page_filename_in_plugin.mdwn | 35 + doc/todo/monochrome_theme.mdwn | 48 + .../more_class__61____34____34___for_css.mdwn | 83 + .../more_customisable_titlepage_function.mdwn | 42 + doc/todo/more_flexible_inline_postform.mdwn | 23 + doc/todo/mtime.mdwn | 16 + doc/todo/multi-thread_ikiwiki.mdwn | 89 + doc/todo/multiple_output_formats.mdwn | 17 + doc/todo/multiple_repository_support.mdwn | 15 + doc/todo/multiple_simultaneous_rcs.mdwn | 26 + .../multiple_simultaneous_rcs/discussion.mdwn | 15 + doc/todo/multiple_template_directories.mdwn | 73 + doc/todo/multiple_templates.mdwn | 13 + doc/todo/natural_sorting.mdwn | 21 + doc/todo/need_global_renamepage_hook.mdwn | 115 + doc/todo/nested_preprocessor_directives.mdwn | 69 + doc/todo/online_configuration.mdwn | 28 + doc/todo/openid_enable_cache.mdwn | 4 + doc/todo/openid_user_filtering.mdwn | 13 + doc/todo/optimisations.mdwn | 15 + doc/todo/optimize_simple_dependencies.mdwn | 95 + doc/todo/optional_underlaydir_prefix.mdwn | 46 + doc/todo/org_mode.mdwn | 36 + doc/todo/org_mode/Discussion.mdwn | 7 + ...95__optimisations__95__and__95__fixes.mdwn | 27 + doc/todo/osm_arbitrary_layers.mdwn | 43 + ...verriding_displayed_modification_time.mdwn | 27 + doc/todo/page_edit_disable.mdwn | 53 + doc/todo/pagedeletion.mdwn | 3 + doc/todo/pagedown_plugin.mdwn | 5 + doc/todo/pageindexes.mdwn | 5 + doc/todo/pagespec_aliases.mdwn | 169 + doc/todo/pagespec_aliases/discussion.mdwn | 13 + doc/todo/pagespec_expansions.mdwn | 151 + doc/todo/pagespec_relative_to_a_target.mdwn | 101 + ...agespec_to_disable_ikiwiki_directives.mdwn | 5 + .../pagestats_among_a_subset_of_pages.mdwn | 28 + doc/todo/pal_plugin.mdwn | 9 + doc/todo/parse_debian_packages.mdwn | 70 + .../passwordauth:_sendmail_interface.mdwn | 61 + doc/todo/paste_plugin.mdwn | 36 + doc/todo/pastebin.mdwn | 11 + doc/todo/pdf_output.mdwn | 22 + doc/todo/pdfshare_plugin.mdwn | 1 + doc/todo/pedigree_plugin.mdwn | 194 + doc/todo/per_page_ACLs.mdwn | 18 + doc/todo/pingback_support.mdwn | 41 + doc/todo/please_add_some_table_styles.mdwn | 8 + doc/todo/pluggablerenderers.mdwn | 3 + doc/todo/plugin.mdwn | 118 + doc/todo/plugin_data_storage.mdwn | 94 + doc/todo/plugin_dependency_calulation.mdwn | 24 + ...lang_name_and_code_template_variables.mdwn | 7 + ...:_avoid_rebuilding_to_fix_meta_titles.mdwn | 60 + doc/todo/po:_better_documentation.mdwn | 3 + doc/todo/po:_better_links.mdwn | 12 + .../po:_better_translation_interface.mdwn | 5 + ...remove_po_files_when_disabling_plugin.mdwn | 13 + doc/todo/po:_rethink_pagespecs.mdwn | 40 + doc/todo/po:_should_cleanup_.pot_files.mdwn | 8 + doc/todo/po:_transifex_integration.mdwn | 13 + doc/todo/po:_translation_of_directives.mdwn | 8 + doc/todo/po_needstranslation_pagespec.mdwn | 12 + ...cessor_directive_for_proposed_changes.mdwn | 60 + ...tty-print_OpenIDs_even_if_not_enabled.mdwn | 29 + doc/todo/preview_changes.mdwn | 14 + .../preview_changes_before_git_commit.mdwn | 17 + doc/todo/progressbar_plugin.mdwn | 132 + doc/todo/provide_a_mailing_list.mdwn | 40 + ...provide_inline_diffs_in_recentchanges.mdwn | 27 + doc/todo/provide_sha1_for_git_diffurl.mdwn | 26 + doc/todo/publishing_in_the_future.mdwn | 127 + doc/todo/quieten-bzr.mdwn | 28 + doc/todo/rcs.mdwn | 25 + ...al_backend__44___based_on_Git_backend.mdwn | 40 + ...al_backend__44___based_on_Git_backend.mdwn | 157 + doc/todo/rcs_updates_needed.mdwn | 10 + doc/todo/recentchanges.mdwn | 144 + doc/todo/recentchanges_feed_with_comment.mdwn | 5 + doc/todo/recentchanges_path.mdwn | 9 + ...arkdown-discount_instead_of_depending.mdwn | 25 + .../redirect_automatically_after_rename.mdwn | 10 + doc/todo/refreshing_recentchanges_page.mdwn | 20 + .../rel__61__nofollow_on_external_links.mdwn | 4 + doc/todo/rel_attribute_for_links.mdwn | 19 + doc/todo/relative_pagespec_deficiency.mdwn | 8 + doc/todo/remove_basewiki_redir_pages.mdwn | 4 + ..._HTML::Template_with_Template_Toolkit.mdwn | 115 + doc/todo/require_CAPTCHA_to_edit.mdwn | 327 + doc/todo/review_mechanism.mdwn | 35 + doc/todo/rewrite_ikiwiki_in_haskell.mdwn | 65 + .../discussion.mdwn | 61 + doc/todo/rss_title_description.mdwn | 35 + doc/todo/rst_plugin_python_rewrite.mdwn | 7 + .../salmon_protocol_for_comment_sharing.mdwn | 21 + doc/todo/search.mdwn | 5 + doc/todo/search_terms.mdwn | 7 + doc/todo/section-numbering.mdwn | 7 + doc/todo/selective_more_directive.mdwn | 28 + doc/todo/shortcut_link_text.mdwn | 19 + doc/todo/shortcut_optional_parameters.mdwn | 46 + .../shortcut_with_different_link_text.mdwn | 67 + ...with_no_url_parameter__44___only_desc.mdwn | 23 + doc/todo/should_optimise_pagespecs.mdwn | 313 + ...d_encoding_for_utf_chars_in_filenames.mdwn | 61 + doc/todo/sigs.mdwn | 25 + doc/todo/sigs/discussion.mdwn | 1 + ...sing_or_regex_in_template_or_shortcut.mdwn | 32 + doc/todo/skip_option_for_inline_plugin.mdwn | 8 + doc/todo/smarter_sorting.mdwn | 141 + ...rk_in_PreprocessorDirective_arguments.mdwn | 18 + doc/todo/softlinks.mdwn | 14 + ...arameter_for_map_plugin_and_directive.mdwn | 53 + .../incomplete_patch.pl.pl | 77 + .../python_algorithms.py | 86 + doc/todo/sortable_tables.mdwn | 1 + doc/todo/sortbylastcomment_plugin.mdwn | 13 + doc/todo/sorting_by_path.mdwn | 18 + doc/todo/source_link.mdwn | 135 + doc/todo/spell_check_plug-in.mdwn | 12 + doc/todo/strftime.mdwn | 4 + doc/todo/structured_page_data.mdwn | 633 + doc/todo/structured_page_data/discussion.mdwn | 1 + ...sheet_suggestion_for_verbatim_content.mdwn | 33 + doc/todo/submodule_support.mdwn | 15 + doc/todo/support_creole_markup.mdwn | 18 + doc/todo/support_dicts_in_setup.mdwn | 26 + doc/todo/support_for_SDF_documents.mdwn | 8 + ...or_plugins_written_in_other_languages.mdwn | 56 + doc/todo/support_includes_in_setup_files.mdwn | 10 + ...support_link__40__.__41___in_pagespec.mdwn | 21 + doc/todo/support_multiple_perl_libraries.mdwn | 11 + ...pporting_comments_via_disussion_pages.mdwn | 222 + doc/todo/svg.mdwn | 77 + doc/todo/syntax_highlighting.mdwn | 120 + doc/todo/syntax_highlighting/discussion.mdwn | 28 + doc/todo/syslog_should_show_wiki_name.mdwn | 8 + doc/todo/table_with_header_column.mdwn | 7 + doc/todo/tag_pagespec_function.mdwn | 41 + doc/todo/tagging_with_a_publication_date.mdwn | 71 + doc/todo/tags.mdwn | 12 + doc/todo/target_filter_for_brokenlinks.mdwn | 9 + doc/todo/terminalclient.mdwn | 10 + doc/todo/test_coverage.mdwn | 24 + .../themes_should_ship_with_templates.mdwn | 19 + .../tidy_git__39__s_ctime_debug_output.mdwn | 15 + doc/todo/tla.mdwn | 7 + doc/todo/tmplvars_plugin.mdwn | 75 + doc/todo/tmplvars_plugin/discussion.mdwn | 1 + doc/todo/toc-with-human-readable-anchors.mdwn | 7 + ...___40__opposite_of_levels__61____41__.mdwn | 48 + doc/todo/toc_plugin_to_skip_one_level.mdwn | 23 + doc/todo/toggle_initial_state.mdwn | 6 + doc/todo/toplevel_index.mdwn | 37 + doc/todo/tracking_bugs_with_dependencies.mdwn | 680 + doc/todo/transient_pages.mdwn | 318 + doc/todo/translation_links.mdwn | 46 + ...edittemplate_verbosity_off_by_default.mdwn | 34 + doc/todo/two-way_convert_of_wikis.mdwn | 18 + doc/todo/typography_plugin_configuration.mdwn | 6 + .../unaccent_url_instead_of_encoding.mdwn | 24 + doc/todo/underlay.mdwn | 13 + ...emporary_file__47__directory_handling.mdwn | 19 + doc/todo/untrusted_git_push_hooks.mdwn | 12 + doc/todo/upgradehooks.mdwn | 8 + .../use_secure_cookies_for_ssl_logins.mdwn | 36 + .../use_templates_for_the_img_plugin.mdwn | 29 + ..._95__redir_proposed_additional_module.mdwn | 8 + ...er-defined_templates_outside_the_wiki.mdwn | 10 + ...r_mechanism_like_etc_ikiwiki_wikilist.mdwn | 3 + doc/todo/userdir_links.mdwn | 5 + doc/todo/utf8.mdwn | 18 + ...res_for_values__41___in_ikiwiki.setup.mdwn | 272 + ...r_https_in_urls_to_allow_serving_both.mdwn | 376 + doc/todo/wanted_pages_plugin.mdwn | 3 + doc/todo/wdiffs_in_recentchanges.mdwn | 1 + doc/todo/web-based_image_editing.mdwn | 3 + doc/todo/web_gui_for_managing_tags.mdwn | 12 + doc/todo/web_reversion.mdwn | 73 + ...up_should_link_to_plugin_descriptions.mdwn | 3 + ...formatted_comments_with_syntax_plugin.mdwn | 9 + doc/todo/wikilink_titles.mdwn | 4 + doc/todo/wikilinkfeatures.mdwn | 4 + doc/todo/wikitrails.mdwn | 49 + doc/todo/wikitrails/discussion.mdwn | 84 + doc/todo/wikiwyg.mdwn | 71 + doc/todo/wikiwyg/discussion.mdwn | 181 + doc/todo/wmd_editor_live_preview.mdwn | 11 + doc/todo/wrapperuser.mdwn | 7 + doc/translation.mdwn | 46 + doc/translation/discussion.mdwn | 121 + doc/usage.mdwn | 389 + doc/usage/discussion.mdwn | 1 + doc/users.mdwn | 11 + doc/users/BerndZeimetz.mdwn | 8 + doc/users/Christine_Spang.mdwn | 1 + doc/users/DamianSmall.mdwn | 1 + doc/users/Daniel_Andersson.mdwn | 3 + doc/users/DavidBremner.mdwn | 1 + doc/users/David_Riebenbauer.mdwn | 8 + doc/users/Edward_Betts.mdwn | 4 + doc/users/Erkan_Yilmaz.mdwn | 2 + doc/users/Gianpaolo_Macario.mdwn | 14 + doc/users/GiuseppeBilotta.mdwn | 6 + doc/users/HenrikBrixAndersen.mdwn | 3 + doc/users/Jamie.mdwn | 1 + doc/users/JeremieKoenig.mdwn | 3 + doc/users/Jimmy_Tang.mdwn | 1 + doc/users/JoshBBall.mdwn | 3 + doc/users/Kai_Hendry.mdwn | 5 + doc/users/KarlMW.mdwn | 3 + doc/users/KarlMW/discussion.mdwn | 27 + doc/users/KathrynAndersen.mdwn | 8 + doc/users/KathrynAndersen/discussion.mdwn | 20 + doc/users/Larry_Clapp.mdwn | 3 + doc/users/LucaCapello.mdwn | 5 + doc/users/MatthiasIhrke.mdwn | 4 + doc/users/Mick_Pollard.mdwn | 1 + doc/users/NeilSmithline.mdwn | 1 + doc/users/NicolasLimare.mdwn | 1 + doc/users/Oblomov.mdwn | 1 + doc/users/Olea.mdwn | 4 + doc/users/OscarMorante.mdwn | 3 + doc/users/Perry.mdwn | 1 + doc/users/Ramsey.mdwn | 3 + doc/users/Remy.mdwn | 1 + doc/users/RickOwens.mdwn | 1 + doc/users/Simon_Michael.mdwn | 8 + doc/users/Stefano_Zacchiroli.mdwn | 1 + doc/users/StevenBlack.mdwn | 5 + doc/users/TaylorKillian.mdwn | 9 + doc/users/TaylorKillian/discussion.mdwn | 5 + doc/users/The_TOVA_Company.mdwn | 32 + doc/users/TimBosse.mdwn | 1 + doc/users/Tim_Lavoie.mdwn | 1 + doc/users/Will.mdwn | 28 + doc/users/acathur.mdwn | 3 + doc/users/adamshand.mdwn | 7 + doc/users/ajt.mdwn | 20 + doc/users/aland.mdwn | 1 + doc/users/alexander.mdwn | 1 + doc/users/alexandredupas.mdwn | 7 + doc/users/anarcat.mdwn | 31 + doc/users/anarcat.wiki | 1 + doc/users/arpitjain.mdwn | 7 + doc/users/bartmassey.mdwn | 5 + doc/users/bbb.mdwn | 5 + doc/users/blipvert.mdwn | 1 + doc/users/bstpierre.mdwn | 1 + doc/users/cfm.mdwn | 1 + doc/users/chris.mdwn | 7 + doc/users/chrismgray.mdwn | 4 + doc/users/chrysn.mdwn | 4 + doc/users/cord.mdwn | 1 + doc/users/cstamas.mdwn | 4 + doc/users/dark.mdwn | 3 + doc/users/dato.mdwn | 3 + doc/users/dirk.mdwn | 1 + doc/users/dom.mdwn | 3 + doc/users/donmarti.mdwn | 2 + doc/users/emptty.mdwn | 2 + doc/users/ericdrechsel.mdwn | 1 + doc/users/fil.mdwn | 1 + doc/users/fmarier.mdwn | 6 + doc/users/harishcm.mdwn | 1 + doc/users/harningt.mdwn | 11 + doc/users/hb.mdwn | 11 + doc/users/hb/discussion.mdwn | 6 + doc/users/hendry.mdwn | 1 + doc/users/intrigeri.mdwn | 4 + doc/users/iustin.mdwn | 1 + doc/users/ivan_shmakov.mdwn | 54 + doc/users/jasonblevins.mdwn | 89 + doc/users/jasonriedy.mdwn | 1 + doc/users/jaywalk.mdwn | 5 + doc/users/jcorneli.mdwn | 3 + doc/users/jeanprivat.mdwn | 1 + doc/users/jelmer.mdwn | 1 + doc/users/jeremyreed.mdwn | 3 + doc/users/jerojasro.mdwn | 3 + doc/users/jmtd.mdwn | 1 + doc/users/joey.mdwn | 8 + doc/users/jogo.mdwn | 5 + doc/users/jon.mdwn | 65 + doc/users/jonassmedegaard.mdwn | 5 + doc/users/josephturian.mdwn | 10 + doc/users/joshtriplett.mdwn | 16 + doc/users/joshtriplett/discussion.mdwn | 68 + doc/users/jrblevin.mdwn | 1 + doc/users/justint.mdwn | 1 + doc/users/jwalzer.mdwn | 3 + doc/users/kyle.mdwn | 2 + doc/users/madduck.mdwn | 9 + doc/users/marcelomagallon.mdwn | 3 + doc/users/mathdesc.mdwn | 190 + doc/users/michaelrasmussen.wiki | 1 + doc/users/neale.mdwn | 10 + doc/users/nil.mdwn | 8 + doc/users/nolan.mdwn | 1 + doc/users/patrickwinnertz.mdwn | 10 + doc/users/pdurbin.mdwn | 1 + doc/users/pelle.mdwn | 1 + doc/users/perolofsson.mdwn | 7 + doc/users/peteg.mdwn | 7 + doc/users/peter_woodman.mdwn | 1 + doc/users/ptecza.mdwn | 21 + doc/users/rubykat.mdwn | 1 + doc/users/sabr.mdwn | 32 + doc/users/sabr/sub1.mdwn | 1 + doc/users/sabr/sub2.mdwn | 1 + doc/users/schmonz-web-ikiwiki.mdwn | 1 + doc/users/schmonz.mdwn | 32 + doc/users/seanh.mdwn | 1 + doc/users/simonraven.mdwn | 7 + doc/users/smcv.mdwn | 10 + doc/users/smcv/gallery.mdwn | 4 + doc/users/smcv/gallery/discussion.mdwn | 18 + doc/users/solofo.mdwn | 1 + doc/users/sphynkx.mdwn | 1 + doc/users/sunny256.mdwn | 15 + doc/users/svend.mdwn | 4 + doc/users/tbm.mdwn | 3 + doc/users/tjgolubi.mdwn | 3 + doc/users/tschwinge.mdwn | 151 + doc/users/ttw.mdwn | 1 + doc/users/tupyakov_vladimir.mdwn | 1 + doc/users/tychoish.mdwn | 10 + doc/users/ulrik.mdwn | 3 + doc/users/undx.mdwn | 7 + doc/users/victormoral.mdwn | 6 + doc/users/weakish.mdwn | 3 + doc/users/weakishjiang.mdwn | 4 + doc/users/wentasah.mdwn | 9 + doc/users/wiebel.mdwn | 5 + doc/users/wtk.mdwn | 6 + doc/users/xma/discussion.mdwn | 18 + doc/users/xtaran.mdwn | 5 + doc/users/yds.mdwn | 1 + doc/w3mmode.mdwn | 11 + doc/w3mmode/ikiwiki.setup | 31 + doc/whyikiwiki.mdwn | 15 + doc/wikiicons/diff.png | Bin 0 -> 219 bytes doc/wikiicons/openidlogin-bg.gif | Bin 0 -> 336 bytes doc/wikiicons/revert.png | Bin 0 -> 397 bytes doc/wikiicons/search-bg.gif | Bin 0 -> 74 bytes doc/wishlist.mdwn | 6 + doc/wishlist/watched_pages.mdwn | 1 + docwiki.setup | 36 + gitremotes | 26 + ikiwiki-calendar.in | 66 + ikiwiki-makerepo | 175 + ikiwiki-mass-rebuild | 90 + ikiwiki-transition.in | 349 + ikiwiki-update-wikilist | 65 + ikiwiki-w3m.cgi | 15 + ikiwiki.in | 229 + ikiwiki.spec | 129 + mdwn2man | 43 + plugins/externaldemo | 144 + plugins/proxy.py | 330 + plugins/pythondemo | 382 + plugins/rst | 86 + pm_filter | 33 + po/Makefile | 97 + po/bg.po | 1468 ++ po/cs.po | 1454 ++ po/da.po | 1376 ++ po/de.po | 1437 ++ po/es.po | 1458 ++ po/fr.po | 1447 ++ po/gu.po | 1452 ++ po/ikiwiki.pot | 1343 ++ po/it.po | 1424 ++ po/pl.po | 1479 ++ po/po2wiki | 42 + po/sv.po | 1457 ++ po/tr.po | 1344 ++ po/underlay.setup | 32 + po/underlays/basewiki/ikiwiki.cs.po | 74 + po/underlays/basewiki/ikiwiki.da.po | 79 + po/underlays/basewiki/ikiwiki.de.po | 73 + po/underlays/basewiki/ikiwiki.es.po | 75 + po/underlays/basewiki/ikiwiki.fr.po | 73 + po/underlays/basewiki/ikiwiki/directive.cs.po | 130 + po/underlays/basewiki/ikiwiki/directive.da.po | 136 + po/underlays/basewiki/ikiwiki/directive.de.po | 137 + po/underlays/basewiki/ikiwiki/directive.es.po | 133 + po/underlays/basewiki/ikiwiki/directive.fr.po | 132 + .../basewiki/ikiwiki/formatting.cs.po | 288 + .../basewiki/ikiwiki/formatting.da.po | 299 + .../basewiki/ikiwiki/formatting.de.po | 299 + .../basewiki/ikiwiki/formatting.es.po | 295 + .../basewiki/ikiwiki/formatting.fr.po | 291 + po/underlays/basewiki/ikiwiki/markdown.cs.po | 48 + po/underlays/basewiki/ikiwiki/markdown.da.po | 55 + po/underlays/basewiki/ikiwiki/markdown.de.po | 52 + po/underlays/basewiki/ikiwiki/markdown.es.po | 50 + po/underlays/basewiki/ikiwiki/markdown.fr.po | 49 + po/underlays/basewiki/ikiwiki/openid.cs.po | 121 + po/underlays/basewiki/ikiwiki/openid.da.po | 95 + po/underlays/basewiki/ikiwiki/openid.de.po | 118 + po/underlays/basewiki/ikiwiki/openid.es.po | 123 + po/underlays/basewiki/ikiwiki/openid.fr.po | 122 + po/underlays/basewiki/ikiwiki/pagespec.cs.po | 278 + po/underlays/basewiki/ikiwiki/pagespec.da.po | 224 + po/underlays/basewiki/ikiwiki/pagespec.de.po | 279 + po/underlays/basewiki/ikiwiki/pagespec.es.po | 289 + po/underlays/basewiki/ikiwiki/pagespec.fr.po | 311 + .../ikiwiki/pagespec/attachment.cs.po | 120 + .../ikiwiki/pagespec/attachment.da.po | 123 + .../ikiwiki/pagespec/attachment.de.po | 124 + .../ikiwiki/pagespec/attachment.es.po | 127 + .../ikiwiki/pagespec/attachment.fr.po | 122 + .../basewiki/ikiwiki/pagespec/po.cs.po | 75 + .../basewiki/ikiwiki/pagespec/po.da.po | 84 + .../basewiki/ikiwiki/pagespec/po.de.po | 75 + .../basewiki/ikiwiki/pagespec/po.es.po | 77 + .../basewiki/ikiwiki/pagespec/po.fr.po | 75 + .../basewiki/ikiwiki/pagespec/sorting.de.po | 48 + po/underlays/basewiki/ikiwiki/searching.cs.po | 80 + po/underlays/basewiki/ikiwiki/searching.da.po | 91 + po/underlays/basewiki/ikiwiki/searching.de.po | 92 + po/underlays/basewiki/ikiwiki/searching.es.po | 84 + po/underlays/basewiki/ikiwiki/searching.fr.po | 87 + po/underlays/basewiki/ikiwiki/subpage.cs.po | 59 + po/underlays/basewiki/ikiwiki/subpage.da.po | 57 + po/underlays/basewiki/ikiwiki/subpage.de.po | 59 + po/underlays/basewiki/ikiwiki/subpage.es.po | 59 + po/underlays/basewiki/ikiwiki/subpage.fr.po | 52 + .../ikiwiki/subpage/linkingrules.cs.po | 100 + .../ikiwiki/subpage/linkingrules.da.po | 108 + .../ikiwiki/subpage/linkingrules.de.po | 109 + .../ikiwiki/subpage/linkingrules.es.po | 102 + .../ikiwiki/subpage/linkingrules.fr.po | 103 + po/underlays/basewiki/ikiwiki/wikilink.cs.po | 115 + po/underlays/basewiki/ikiwiki/wikilink.da.po | 96 + po/underlays/basewiki/ikiwiki/wikilink.de.po | 115 + po/underlays/basewiki/ikiwiki/wikilink.es.po | 118 + po/underlays/basewiki/ikiwiki/wikilink.fr.po | 112 + po/underlays/basewiki/index.cs.po | 27 + po/underlays/basewiki/index.da.po | 35 + po/underlays/basewiki/index.de.po | 29 + po/underlays/basewiki/index.es.po | 31 + po/underlays/basewiki/index.fr.po | 26 + po/underlays/basewiki/recentchanges.cs.po | 38 + po/underlays/basewiki/recentchanges.da.po | 44 + po/underlays/basewiki/recentchanges.de.po | 38 + po/underlays/basewiki/recentchanges.es.po | 40 + po/underlays/basewiki/recentchanges.fr.po | 37 + po/underlays/basewiki/sandbox.cs.po | 91 + po/underlays/basewiki/sandbox.da.po | 97 + po/underlays/basewiki/sandbox.de.po | 91 + po/underlays/basewiki/sandbox.es.po | 90 + po/underlays/basewiki/sandbox.fr.po | 88 + po/underlays/basewiki/shortcuts.cs.po | 269 + po/underlays/basewiki/shortcuts.da.po | 246 + po/underlays/basewiki/shortcuts.de.po | 287 + po/underlays/basewiki/shortcuts.es.po | 269 + po/underlays/basewiki/shortcuts.fr.po | 266 + po/underlays/basewiki/templates.cs.po | 339 + po/underlays/basewiki/templates.da.po | 254 + po/underlays/basewiki/templates.de.po | 346 + po/underlays/basewiki/templates.es.po | 345 + po/underlays/basewiki/templates.fr.po | 396 + po/underlays/basewiki/templates/note.cs.po | 55 + po/underlays/basewiki/templates/note.da.po | 48 + po/underlays/basewiki/templates/note.de.po | 53 + po/underlays/basewiki/templates/note.es.po | 56 + po/underlays/basewiki/templates/note.fr.po | 53 + po/underlays/basewiki/templates/popup.cs.po | 69 + po/underlays/basewiki/templates/popup.da.po | 57 + po/underlays/basewiki/templates/popup.de.po | 68 + po/underlays/basewiki/templates/popup.es.po | 71 + po/underlays/basewiki/templates/popup.fr.po | 64 + .../ikiwiki/directive/aggregate.da.po | 150 + .../ikiwiki/directive/aggregate.de.po | 189 + .../ikiwiki/directive/aggregate.fr.po | 177 + .../ikiwiki/directive/brokenlinks.da.po | 54 + .../ikiwiki/directive/brokenlinks.de.po | 57 + .../ikiwiki/directive/brokenlinks.fr.po | 51 + .../ikiwiki/directive/calendar.da.po | 150 + .../ikiwiki/directive/calendar.de.po | 205 + .../ikiwiki/directive/calendar.fr.po | 200 + .../directives/ikiwiki/directive/color.da.po | 81 + .../directives/ikiwiki/directive/color.de.po | 97 + .../directives/ikiwiki/directive/color.fr.po | 74 + .../ikiwiki/directive/comment.da.po | 123 + .../ikiwiki/directive/comment.de.po | 138 + .../ikiwiki/directive/comment.fr.po | 134 + .../directives/ikiwiki/directive/copy.da.po | 31 + .../directives/ikiwiki/directive/copy.de.po | 25 + .../directives/ikiwiki/directive/copy.fr.po | 24 + .../directives/ikiwiki/directive/cut.da.po | 31 + .../directives/ikiwiki/directive/cut.de.po | 25 + .../directives/ikiwiki/directive/cut.fr.po | 25 + .../ikiwiki/directive/cutpaste.da.po | 135 + .../ikiwiki/directive/cutpaste.de.po | 153 + .../ikiwiki/directive/cutpaste.fr.po | 139 + .../directives/ikiwiki/directive/date.de.po | 61 + .../ikiwiki/directive/edittemplate.da.po | 91 + .../ikiwiki/directive/edittemplate.de.po | 98 + .../ikiwiki/directive/edittemplate.fr.po | 109 + .../directives/ikiwiki/directive/format.da.po | 86 + .../directives/ikiwiki/directive/format.de.po | 93 + .../directives/ikiwiki/directive/format.fr.po | 90 + .../ikiwiki/directive/fortune.da.po | 43 + .../ikiwiki/directive/fortune.de.po | 42 + .../ikiwiki/directive/fortune.fr.po | 36 + .../directives/ikiwiki/directive/graph.da.po | 77 + .../directives/ikiwiki/directive/graph.de.po | 88 + .../directives/ikiwiki/directive/graph.fr.po | 70 + .../directives/ikiwiki/directive/haiku.da.po | 56 + .../directives/ikiwiki/directive/haiku.de.po | 60 + .../directives/ikiwiki/directive/haiku.fr.po | 49 + .../directives/ikiwiki/directive/if.da.po | 132 + .../directives/ikiwiki/directive/if.de.po | 145 + .../directives/ikiwiki/directive/if.fr.po | 138 + .../directives/ikiwiki/directive/img.da.po | 99 + .../directives/ikiwiki/directive/img.de.po | 124 + .../directives/ikiwiki/directive/img.fr.po | 123 + .../directives/ikiwiki/directive/inline.da.po | 247 + .../directives/ikiwiki/directive/inline.de.po | 385 + .../directives/ikiwiki/directive/inline.fr.po | 398 + .../ikiwiki/directive/linkmap.da.po | 83 + .../ikiwiki/directive/linkmap.de.po | 99 + .../ikiwiki/directive/linkmap.fr.po | 93 + .../ikiwiki/directive/listdirectives.da.po | 64 + .../ikiwiki/directive/listdirectives.de.po | 72 + .../ikiwiki/directive/listdirectives.fr.po | 69 + .../directives/ikiwiki/directive/map.da.po | 71 + .../directives/ikiwiki/directive/map.de.po | 77 + .../directives/ikiwiki/directive/map.fr.po | 77 + .../directives/ikiwiki/directive/meta.da.po | 448 + .../directives/ikiwiki/directive/meta.de.po | 470 + .../directives/ikiwiki/directive/meta.fr.po | 447 + .../directives/ikiwiki/directive/more.da.po | 67 + .../directives/ikiwiki/directive/more.de.po | 63 + .../directives/ikiwiki/directive/more.fr.po | 63 + .../ikiwiki/directive/orphans.da.po | 56 + .../ikiwiki/directive/orphans.de.po | 60 + .../ikiwiki/directive/orphans.fr.po | 49 + .../ikiwiki/directive/pagecount.da.po | 47 + .../ikiwiki/directive/pagecount.de.po | 48 + .../ikiwiki/directive/pagecount.fr.po | 40 + .../ikiwiki/directive/pagestats.da.po | 113 + .../ikiwiki/directive/pagestats.de.po | 117 + .../ikiwiki/directive/pagestats.fr.po | 120 + .../ikiwiki/directive/pagetemplate.da.po | 51 + .../ikiwiki/directive/pagetemplate.de.po | 66 + .../ikiwiki/directive/pagetemplate.fr.po | 66 + .../directives/ikiwiki/directive/paste.da.po | 31 + .../directives/ikiwiki/directive/paste.de.po | 25 + .../directives/ikiwiki/directive/paste.fr.po | 26 + .../directives/ikiwiki/directive/ping.da.po | 62 + .../directives/ikiwiki/directive/ping.de.po | 69 + .../directives/ikiwiki/directive/ping.fr.po | 55 + .../directives/ikiwiki/directive/poll.da.po | 77 + .../directives/ikiwiki/directive/poll.de.po | 87 + .../directives/ikiwiki/directive/poll.fr.po | 70 + .../ikiwiki/directive/polygen.da.po | 49 + .../ikiwiki/directive/polygen.de.po | 50 + .../ikiwiki/directive/polygen.fr.po | 42 + .../ikiwiki/directive/postsparkline.da.po | 128 + .../ikiwiki/directive/postsparkline.de.po | 149 + .../ikiwiki/directive/postsparkline.fr.po | 136 + .../ikiwiki/directive/progress.da.po | 59 + .../ikiwiki/directive/progress.de.po | 68 + .../ikiwiki/directive/progress.fr.po | 52 + .../ikiwiki/directive/shortcut.da.po | 44 + .../ikiwiki/directive/shortcut.de.po | 46 + .../ikiwiki/directive/shortcut.fr.po | 37 + .../ikiwiki/directive/sparkline.da.po | 145 + .../ikiwiki/directive/sparkline.de.po | 178 + .../ikiwiki/directive/sparkline.fr.po | 162 + .../directives/ikiwiki/directive/table.da.po | 135 + .../directives/ikiwiki/directive/table.de.po | 153 + .../directives/ikiwiki/directive/table.fr.po | 149 + .../directives/ikiwiki/directive/tag.da.po | 99 + .../directives/ikiwiki/directive/tag.de.po | 121 + .../directives/ikiwiki/directive/tag.fr.po | 118 + .../ikiwiki/directive/taglink.da.po | 31 + .../ikiwiki/directive/taglink.de.po | 25 + .../ikiwiki/directive/taglink.fr.po | 24 + .../ikiwiki/directive/template.da.po | 198 + .../ikiwiki/directive/template.de.po | 204 + .../ikiwiki/directive/template.fr.po | 239 + .../ikiwiki/directive/testpagespec.da.po | 76 + .../ikiwiki/directive/testpagespec.de.po | 84 + .../ikiwiki/directive/testpagespec.fr.po | 69 + .../directives/ikiwiki/directive/teximg.da.po | 77 + .../directives/ikiwiki/directive/teximg.de.po | 84 + .../directives/ikiwiki/directive/teximg.fr.po | 72 + .../directives/ikiwiki/directive/toc.da.po | 79 + .../directives/ikiwiki/directive/toc.de.po | 85 + .../directives/ikiwiki/directive/toc.fr.po | 76 + .../directives/ikiwiki/directive/toggle.da.po | 96 + .../directives/ikiwiki/directive/toggle.de.po | 113 + .../directives/ikiwiki/directive/toggle.fr.po | 108 + .../ikiwiki/directive/toggleable.da.po | 31 + .../ikiwiki/directive/toggleable.de.po | 25 + .../ikiwiki/directive/toggleable.fr.po | 24 + .../ikiwiki/directive/version.da.po | 51 + .../ikiwiki/directive/version.de.po | 50 + .../ikiwiki/directive/version.fr.po | 44 + po/underlays/smiley/smileys.da.po | 215 + po/underlays/smiley/smileys.de.po | 211 + po/underlays/smiley/smileys.fr.po | 209 + po/vi.po | 1455 ++ t/404.t | 44 + t/add_depends.t | 70 + t/autoindex.t | 134 + t/basename.t | 12 + t/basewiki_brokenlinks.t | 29 + t/basewiki_brokenlinks/index.mdwn | 1 + t/bazaar.t | 116 + t/beautify_urlpath.t | 17 + t/bestlink.t | 33 + t/calculate_changed_links.t | 58 + t/cmp_path.t | 48 + t/comments.t | 57 + t/conflicts.t | 131 + t/crazy-badass-perl-bug.t | 20 + t/cvs.t | 708 + t/dirname.t | 12 + t/file_pruned.t | 40 + t/find_src_files.t | 97 + t/git.t | 97 + t/html.t | 30 + t/htmlbalance.t | 23 + t/htmlize.t | 85 + t/index.t | 161 + t/linkify.t | 112 + t/linkpage.t | 13 + t/map.t | 242 + t/mercurial.t | 75 + t/openiduser.t | 42 + t/pagename.t | 35 + t/pagespec_match.t | 147 + t/pagespec_match_list.t | 174 + t/pagespec_match_result.t | 84 + t/pagetitle.t | 13 + t/parentlinks.t | 81 + t/parentlinks/templates/parentlinks.tmpl | 4 + t/permalink.t | 14 + t/po.t | 250 + t/preprocess.t | 83 + t/prune.t | 23 + t/readfile.t | 12 + t/renamepage.t | 51 + t/rssurls.t | 37 + t/rst.t | 22 + t/svn.t | 78 + t/syntax.t | 20 + t/tag.t | 88 + t/template_syntax.t | 15 + t/templates_documented.t | 14 + t/test1.mdwn | 2 + t/test2.mdwn | 5 + t/test3.mdwn | 1 + t/tinyblog/index.mdwn | 1 + t/tinyblog/post.mdwn | 1 + t/titlepage.t | 13 + t/trail.t | 292 + t/urlto.t | 51 + t/yesno.t | 23 + templates/aggregatepost.tmpl | 15 + templates/archivepage.tmpl | 19 + templates/atomitem.tmpl | 48 + templates/atompage.tmpl | 39 + templates/autoindex.tmpl | 1 + templates/autotag.tmpl | 4 + templates/blogpost.tmpl | 16 + templates/calendarmonth.tmpl | 5 + templates/calendaryear.tmpl | 1 + templates/change.tmpl | 57 + templates/comment.tmpl | 66 + templates/commentmoderation.tmpl | 32 + templates/editcomment.tmpl | 43 + templates/editconflict.tmpl | 7 + templates/editcreationconflict.tmpl | 9 + templates/editfailedsave.tmpl | 10 + templates/editpage.tmpl | 91 + templates/editpagegone.tmpl | 7 + templates/feedlink.tmpl | 8 + templates/googleform.tmpl | 8 + templates/inlinepage.tmpl | 72 + templates/microblog.tmpl | 22 + templates/notifyemail.tmpl | 9 + templates/openid-selector.tmpl | 43 + templates/page.tmpl | 219 + templates/passwordmail.tmpl | 15 + templates/pocreatepage.tmpl | 3 + templates/recentchanges.tmpl | 7 + templates/renamesummary.tmpl | 26 + templates/revert.tmpl | 21 + templates/rssitem.tmpl | 29 + templates/rsspage.tmpl | 11 + templates/searchform.tmpl | 6 + templates/searchquery.tmpl | 113 + templates/titlepage.tmpl | 7 + templates/trails.tmpl | 23 + themes/actiontabs/style.css | 149 + themes/blueview/background_darkness.png | Bin 0 -> 165 bytes themes/blueview/header_background.png | Bin 0 -> 53693 bytes themes/blueview/style.css | 281 + themes/goldtype/background_darkness.png | Bin 0 -> 165 bytes themes/goldtype/base.css | 1 + themes/goldtype/header_background.png | Bin 0 -> 196261 bytes themes/goldtype/style.css | 23 + themes/monochrome/gradient.png | Bin 0 -> 2466 bytes themes/monochrome/style.css | 53 + .../attachment/ikiwiki/images/pbar-ani.gif | Bin 0 -> 3323 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../attachment/ikiwiki/jquery-ui.full.css | 568 + .../attachment/ikiwiki/jquery-ui.full.js | 11729 ++++++++++++++++ .../attachment/ikiwiki/jquery-ui.min.css | 1 + underlays/attachment/ikiwiki/jquery-ui.min.js | 33 + .../ikiwiki/jquery.fileupload-ui.js | 357 + .../attachment/ikiwiki/jquery.fileupload.js | 720 + .../ikiwiki/jquery.iframe-transport.js | 133 + .../attachment/ikiwiki/jquery.tmpl.full.js | 486 + .../attachment/ikiwiki/jquery.tmpl.min.js | 1 + underlays/basewiki/favicon.ico | 1 + underlays/basewiki/ikiwiki.mdwn | 1 + underlays/basewiki/ikiwiki/directive.mdwn | 1 + underlays/basewiki/ikiwiki/formatting.mdwn | 1 + underlays/basewiki/ikiwiki/markdown.mdwn | 1 + underlays/basewiki/ikiwiki/openid.mdwn | 1 + underlays/basewiki/ikiwiki/pagespec.mdwn | 1 + .../basewiki/ikiwiki/pagespec/attachment.mdwn | 1 + underlays/basewiki/ikiwiki/pagespec/po.mdwn | 1 + .../basewiki/ikiwiki/pagespec/sorting.mdwn | 1 + underlays/basewiki/ikiwiki/searching.mdwn | 1 + underlays/basewiki/ikiwiki/subpage.mdwn | 1 + .../ikiwiki/subpage/linkingrules.mdwn | 1 + underlays/basewiki/ikiwiki/wikilink.mdwn | 1 + underlays/basewiki/index.mdwn | 1 + underlays/basewiki/local.css | 1 + underlays/basewiki/recentchanges.mdwn | 1 + underlays/basewiki/sandbox.mdwn | 1 + underlays/basewiki/shortcuts.mdwn | 1 + underlays/basewiki/style.css | 1 + underlays/basewiki/templates.mdwn | 1 + underlays/basewiki/templates/note.mdwn | 1 + underlays/basewiki/templates/popup.mdwn | 1 + underlays/basewiki/wikiicons | 1 + underlays/javascript/ikiwiki/ikiwiki.js | 54 + underlays/javascript/ikiwiki/relativedate.js | 75 + underlays/javascript/ikiwiki/toggle.js | 29 + underlays/jquery/ikiwiki/jquery.full.js | 8981 ++++++++++++ underlays/jquery/ikiwiki/jquery.min.js | 23 + .../ikiwiki/openid/openid-jquery.js | 272 + underlays/osm/ikiwiki/images/osm.png | Bin 0 -> 2982 bytes underlays/osm/ikiwiki/osm.js | 166 + underlays/smiley/smileys | 1 + underlays/smiley/smileys.mdwn | 1 + wikilist | 12 + 2940 files changed, 193153 insertions(+) create mode 100644 .perlcriticrc create mode 100644 Bundle/IkiWiki.pm create mode 100644 Bundle/IkiWiki/Extras.pm create mode 120000 CHANGELOG create mode 100644 IkiWiki.pm create mode 100644 IkiWiki/CGI.pm create mode 100644 IkiWiki/Plugin/404.pm create mode 100644 IkiWiki/Plugin/aggregate.pm create mode 100644 IkiWiki/Plugin/amazon_s3.pm create mode 100644 IkiWiki/Plugin/anonok.pm create mode 100644 IkiWiki/Plugin/attachment.pm create mode 100644 IkiWiki/Plugin/autoindex.pm create mode 100644 IkiWiki/Plugin/blogspam.pm create mode 100644 IkiWiki/Plugin/brokenlinks.pm create mode 100644 IkiWiki/Plugin/bzr.pm create mode 100644 IkiWiki/Plugin/calendar.pm create mode 100644 IkiWiki/Plugin/camelcase.pm create mode 100644 IkiWiki/Plugin/color.pm create mode 100644 IkiWiki/Plugin/comments.pm create mode 100644 IkiWiki/Plugin/conditional.pm create mode 100644 IkiWiki/Plugin/creole.pm create mode 100644 IkiWiki/Plugin/cutpaste.pm create mode 100644 IkiWiki/Plugin/cvs.pm create mode 100644 IkiWiki/Plugin/darcs.pm create mode 100644 IkiWiki/Plugin/date.pm create mode 100644 IkiWiki/Plugin/ddate.pm create mode 100644 IkiWiki/Plugin/editdiff.pm create mode 100644 IkiWiki/Plugin/editpage.pm create mode 100644 IkiWiki/Plugin/edittemplate.pm create mode 100644 IkiWiki/Plugin/embed.pm create mode 100644 IkiWiki/Plugin/external.pm create mode 100644 IkiWiki/Plugin/favicon.pm create mode 100644 IkiWiki/Plugin/filecheck.pm create mode 100644 IkiWiki/Plugin/flattr.pm create mode 100644 IkiWiki/Plugin/format.pm create mode 100644 IkiWiki/Plugin/fortune.pm create mode 100644 IkiWiki/Plugin/getsource.pm create mode 100644 IkiWiki/Plugin/git.pm create mode 100644 IkiWiki/Plugin/goodstuff.pm create mode 100644 IkiWiki/Plugin/google.pm create mode 100644 IkiWiki/Plugin/goto.pm create mode 100644 IkiWiki/Plugin/graphviz.pm create mode 100644 IkiWiki/Plugin/haiku.pm create mode 100644 IkiWiki/Plugin/headinganchors.pm create mode 100644 IkiWiki/Plugin/highlight.pm create mode 100644 IkiWiki/Plugin/hnb.pm create mode 100644 IkiWiki/Plugin/html.pm create mode 100644 IkiWiki/Plugin/htmlbalance.pm create mode 100644 IkiWiki/Plugin/htmlscrubber.pm create mode 100644 IkiWiki/Plugin/htmltidy.pm create mode 100644 IkiWiki/Plugin/httpauth.pm create mode 100644 IkiWiki/Plugin/img.pm create mode 100644 IkiWiki/Plugin/inline.pm create mode 100644 IkiWiki/Plugin/link.pm create mode 100644 IkiWiki/Plugin/linkmap.pm create mode 100644 IkiWiki/Plugin/listdirectives.pm create mode 100644 IkiWiki/Plugin/localstyle.pm create mode 100644 IkiWiki/Plugin/lockedit.pm create mode 100644 IkiWiki/Plugin/map.pm create mode 100644 IkiWiki/Plugin/mdwn.pm create mode 100644 IkiWiki/Plugin/mercurial.pm create mode 100644 IkiWiki/Plugin/meta.pm create mode 100644 IkiWiki/Plugin/mirrorlist.pm create mode 100644 IkiWiki/Plugin/moderatedcomments.pm create mode 100644 IkiWiki/Plugin/monotone.pm create mode 100644 IkiWiki/Plugin/more.pm create mode 100644 IkiWiki/Plugin/norcs.pm create mode 100644 IkiWiki/Plugin/notifyemail.pm create mode 100644 IkiWiki/Plugin/opendiscussion.pm create mode 100644 IkiWiki/Plugin/openid.pm create mode 100644 IkiWiki/Plugin/orphans.pm create mode 100644 IkiWiki/Plugin/osm.pm create mode 100644 IkiWiki/Plugin/otl.pm create mode 100644 IkiWiki/Plugin/pagecount.pm create mode 100644 IkiWiki/Plugin/pagestats.pm create mode 100644 IkiWiki/Plugin/pagetemplate.pm create mode 100644 IkiWiki/Plugin/parentlinks.pm create mode 100644 IkiWiki/Plugin/passwordauth.pm create mode 100644 IkiWiki/Plugin/pingee.pm create mode 100644 IkiWiki/Plugin/pinger.pm create mode 100644 IkiWiki/Plugin/po.pm create mode 100644 IkiWiki/Plugin/poll.pm create mode 100644 IkiWiki/Plugin/polygen.pm create mode 100644 IkiWiki/Plugin/postsparkline.pm create mode 100644 IkiWiki/Plugin/prettydate.pm create mode 100644 IkiWiki/Plugin/progress.pm create mode 100644 IkiWiki/Plugin/rawhtml.pm create mode 100644 IkiWiki/Plugin/recentchanges.pm create mode 100644 IkiWiki/Plugin/recentchangesdiff.pm create mode 100644 IkiWiki/Plugin/relativedate.pm create mode 100644 IkiWiki/Plugin/remove.pm create mode 100644 IkiWiki/Plugin/rename.pm create mode 100644 IkiWiki/Plugin/repolist.pm create mode 100644 IkiWiki/Plugin/rsync.pm create mode 100644 IkiWiki/Plugin/search.pm create mode 100644 IkiWiki/Plugin/shortcut.pm create mode 100644 IkiWiki/Plugin/sidebar.pm create mode 100644 IkiWiki/Plugin/signinedit.pm create mode 100644 IkiWiki/Plugin/skeleton.pm.example create mode 100644 IkiWiki/Plugin/smiley.pm create mode 100644 IkiWiki/Plugin/sortnaturally.pm create mode 100644 IkiWiki/Plugin/sparkline.pm create mode 100644 IkiWiki/Plugin/svn.pm create mode 100644 IkiWiki/Plugin/table.pm create mode 100644 IkiWiki/Plugin/tag.pm create mode 100644 IkiWiki/Plugin/template.pm create mode 100644 IkiWiki/Plugin/testpagespec.pm create mode 100644 IkiWiki/Plugin/teximg.pm create mode 100644 IkiWiki/Plugin/textile.pm create mode 100644 IkiWiki/Plugin/theme.pm create mode 100644 IkiWiki/Plugin/tla.pm create mode 100644 IkiWiki/Plugin/toc.pm create mode 100644 IkiWiki/Plugin/toggle.pm create mode 100644 IkiWiki/Plugin/trail.pm create mode 100644 IkiWiki/Plugin/transient.pm create mode 100644 IkiWiki/Plugin/txt.pm create mode 100644 IkiWiki/Plugin/typography.pm create mode 100644 IkiWiki/Plugin/underlay.pm create mode 100644 IkiWiki/Plugin/userlist.pm create mode 100644 IkiWiki/Plugin/version.pm create mode 100644 IkiWiki/Plugin/websetup.pm create mode 100644 IkiWiki/Plugin/wikitext.pm create mode 100644 IkiWiki/Plugin/wmd.pm create mode 100644 IkiWiki/Receive.pm create mode 100644 IkiWiki/Render.pm create mode 100644 IkiWiki/Setup.pm create mode 100644 IkiWiki/Setup/Automator.pm create mode 100644 IkiWiki/Setup/Standard.pm create mode 100644 IkiWiki/Setup/Yaml.pm create mode 100644 IkiWiki/UserInfo.pm create mode 100644 IkiWiki/Wrapper.pm create mode 100755 Makefile.PL create mode 120000 NEWS create mode 100644 README create mode 100644 auto-blog.setup create mode 100644 auto.setup create mode 100644 cpan/CPAN/MyConfig.pm create mode 100644 cpan/README create mode 100644 debian/NEWS create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/link create mode 100755 debian/postinst create mode 100755 debian/preinst create mode 100755 debian/rules create mode 100644 doc/GPL create mode 100644 doc/TourBusStop.mdwn create mode 100644 doc/anchor.mdwn create mode 100644 doc/backlinks.mdwn create mode 100644 doc/banned_users.mdwn create mode 100644 doc/banned_users/discussion.mdwn create mode 100644 doc/basewiki.mdwn create mode 100644 doc/basewiki/index.mdwn create mode 100644 doc/basewiki/sandbox.mdwn create mode 100644 doc/blog.mdwn create mode 100644 doc/branches.mdwn create mode 100644 doc/bugs.mdwn create mode 100644 doc/bugs/2.45_Compilation_error.mdwn create mode 100644 doc/bugs/404_plugin_and_lighttpd.mdwn create mode 100644 doc/bugs/404_plugin_should_handle_403.mdwn create mode 100644 doc/bugs/404_when_cancel_create_page.mdwn create mode 100644 doc/bugs/4_spaces_after_bullet.mdwn create mode 100644 doc/bugs/Add_a_footer_div_on_all_pages_to_improve_theming.mdwn create mode 100644 doc/bugs/Add_permissions_for_suggesting__47__accepting_edits.mdwn create mode 100644 doc/bugs/Aggregated_Atom_feeds_are_double-encoded.mdwn create mode 100644 doc/bugs/Allow_overriding_of_symlink_restriction.mdwn create mode 100644 doc/bugs/Another_UTF-8_problem.mdwn create mode 100644 doc/bugs/Attachment_plug-in_not_committing_files.mdwn create mode 100644 doc/bugs/Broken_URL_to_your_blog_script.mdwn create mode 100644 doc/bugs/Broken_access_to_Ikiwiki_gitweb.mdwn create mode 100644 doc/bugs/Building_a_sidebar_does_not_regenerate_the_subpages.mdwn create mode 100644 doc/bugs/CGI__44___formbuilder__44___non-existent_field_address.mdwn create mode 100644 doc/bugs/CGI_edit_and_slash_in_page_title.mdwn create mode 100644 doc/bugs/CGI_problem_with_some_webservers.mdwn create mode 100644 doc/bugs/CGI_showed_HTML_when_perl_error.mdwn create mode 100644 doc/bugs/CGI_wrapper_doesn__39__t_store_PERL5LIB_environment_variable.mdwn create mode 100644 doc/bugs/CamelCase_and_Recent_Changes_create_spurious_Links.mdwn create mode 100644 doc/bugs/Can__39__t_build_2.49__63__.mdwn create mode 100644 doc/bugs/Can__39__t_connect_to_ikiwiki_git_repo_through_http.mdwn create mode 100644 doc/bugs/Can__39__t_create_root_page.mdwn create mode 100644 doc/bugs/Can__39__t_deplete_page__63__.mdwn create mode 100644 doc/bugs/Can__39__t_rebuild_wiki_pages_with_ikiwiki_2.49.mdwn create mode 100644 doc/bugs/Cannot_inline_pages_with_apostrophes_in_title.mdwn create mode 100644 doc/bugs/Changing_language.mdwn create mode 100644 doc/bugs/Checksum_errors_on_the_pristine-tar_branch.mdwn create mode 100644 doc/bugs/Command-line_arguments_should_override_settings_in_the_setup_file.mdwn create mode 100644 doc/bugs/Comments_are_not_sorted_by_their_date_attribute.mdwn create mode 100644 doc/bugs/Comments_dissapeared.mdwn create mode 100644 doc/bugs/Comments_link_is_to_index.html_if_usedirs_is_on.mdwn create mode 100644 doc/bugs/Convert___34__somehost.com/user__34___OpenID_at_RecentChanges_page.mdwn create mode 100644 doc/bugs/Disappearing_Pages.mdwn create mode 100644 doc/bugs/Discussion_link_not_translated_after_page_update.mdwn create mode 100644 doc/bugs/Discussion_link_not_translated_in_post.mdwn create mode 100644 doc/bugs/Discussion_of_main_page_generates_invalid_link.mdwn create mode 100644 doc/bugs/Does_IkiWiki::Setup::load__40____41___really_return_a_hash__63__.mdwn create mode 100644 doc/bugs/Dupe_entry_in_Bundle::IkiWiki::Extras.pm.mdwn create mode 100644 doc/bugs/Encoding_problem_in_calendar_plugin.mdwn create mode 100644 doc/bugs/Error:_OpenID_failure:_time_bad_sig:.mdwn create mode 100644 doc/bugs/Error:_Your_login_session_has_expired._.mdwn create mode 100644 doc/bugs/Error:_no_text_was_copied_in_this_page_--_missing_page_dependencies.mdwn create mode 100644 doc/bugs/Exception:_Unknown_function___96__this__39___.mdwn create mode 100644 doc/bugs/Excessive_list_nesting_in_map_output_for_subpages.mdwn create mode 100644 doc/bugs/Existing_Discussion_pages_appear_as_non-existing.mdwn create mode 100644 doc/bugs/External_link:_underscore_conversion.mdwn create mode 100644 doc/bugs/External_links_with_Creole.mdwn create mode 100644 doc/bugs/Fancy_characters_get_munged_on_page_save.mdwn create mode 100644 doc/bugs/Feeds_get_wrong_timezone..mdwn create mode 100644 doc/bugs/Feeds_link_to_index.html_instead_of_directory.mdwn create mode 100644 doc/bugs/Filenames_with_colons_cause_problems_for_Windows_users.mdwn create mode 100644 doc/bugs/Git:_changed_behavior_w.r.t._timestamps.mdwn create mode 100644 doc/bugs/Git:_web_commit_message_not_utf-8.mdwn create mode 100644 doc/bugs/Graphviz_plug-in_directive_changed_in_2.60.mdwn create mode 100644 doc/bugs/HTML_for_parentlinks_makes_theming_hard.mdwn create mode 100644 doc/bugs/HTML_inlined_into_Atom_not_necessarily_well-formed.mdwn create mode 100644 doc/bugs/HTML_is_not_update_nor_created_when_editing_markdown_via_CGI.mdwn create mode 100644 doc/bugs/Highlight_extension_uses_hard_coded_paths.mdwn create mode 100644 doc/bugs/Http_error_500_when_using_mercurial_backend.mdwn create mode 100644 doc/bugs/Hyperestraier_search_plug-in_defective.mdwn create mode 100644 doc/bugs/INC_location_not_set_correctly_in_make_test.mdwn create mode 100644 doc/bugs/IkiWiki::Setup::load__40____41___broken_outside_ikiwiki__63__.mdwn create mode 100644 doc/bugs/IkiWiki::Wrapper_should_use_destdir.mdwn create mode 100644 doc/bugs/IkiWiki::Wrapper_should_use_destdir/discussion.mdwn create mode 100644 doc/bugs/IkiWiki_does_not_use_file__39__s_mtime_for_Last_Edited.mdwn create mode 100644 doc/bugs/Index_files_have_wrong_permissions.mdwn create mode 100644 doc/bugs/Inline_doesn__39__t_wikilink_to_pages.mdwn create mode 100644 doc/bugs/Insecure_dependency_in_eval_while_running_with_-T_switch.mdwn create mode 100644 doc/bugs/Insecure_dependency_in_mkdir.mdwn create mode 100644 doc/bugs/Insecure_dependency_in_utime.mdwn create mode 100644 doc/bugs/Linkmap_doesn__39__t_support_multiple_linkmaps_on_a_single_page.mdwn create mode 100644 doc/bugs/Links_to_missing_pages_should_always_be_styled.mdwn create mode 100644 doc/bugs/Links_with_symbols_can__39__t_be_edited.mdwn create mode 100644 doc/bugs/MTIME_not_set_for_inline_or_archive_entries.mdwn create mode 100644 doc/bugs/Map_sorts_subtags_under_a_different_tag.mdwn create mode 100644 doc/bugs/Mercurial_example_diffurl_should_read_r2__44___not_changeset.mdwn create mode 100644 doc/bugs/Meta_plugin_does_not_respect_htmlscrubber__95__skip_setting.___40__patch__41__.mdwn create mode 100644 doc/bugs/Missing_build-dep_on_perlmagick__63__.mdwn create mode 100644 doc/bugs/Missing_constant_domain_at_IkiWiki.pm_line_842.mdwn create mode 100644 doc/bugs/Monotone_rcs_support.mdwn create mode 100644 doc/bugs/More_permission_checking.mdwn create mode 100644 doc/bugs/Navbar_does_not_link_to_page_being_commented_on_while_commenting.mdwn create mode 100644 doc/bugs/New_comments_are_not_always_displayed__59___need_page_refresh_to_appear.mdwn create mode 100644 doc/bugs/No___34__sid__34___in_forms_resulting_in_Error:_Your_login_session_has_expired..mdwn create mode 100644 doc/bugs/No_categories_in_RSS__47__Atom_feeds.mdwn create mode 100644 doc/bugs/No_link_for_blog_items_when_filename_contains_a_colon.mdwn create mode 100644 doc/bugs/No_numbacklinks_setting_for___34__no_limit__34__.mdwn create mode 100644 doc/bugs/No_progress_in_progress_bar.mdwn create mode 100644 doc/bugs/Not_all_comments_are_listed_by___33__map_or___33__inline.mdwn create mode 100644 doc/bugs/Obsolete_templates__47__estseek.conf.mdwn create mode 100644 doc/bugs/OpenID_delegation_fails_on_my_server.mdwn create mode 100644 doc/bugs/PNG_triggers_UTF-8_error_in_MimeInfo.pm.mdwn create mode 100644 doc/bugs/PREFIX_not_honoured_for_underlaydir.mdwn create mode 100644 "doc/bugs/Pages_with_non-ascii_characters_like_\303\266\303\244\303\245_in_name_not_found_directly_after_commit.mdwn" create mode 100644 doc/bugs/Patch:_Fix_error_in_style.css.mdwn create mode 100644 doc/bugs/Perl_scripts_depend_on___47__usr__47__bin__47__perl.mdwn create mode 100644 doc/bugs/Please_avoid_using___39__cp_-a__39___in_Makefile.PL.mdwn create mode 100644 doc/bugs/Please_don__39__t_refer_to_offsite_openid_image.mdwn create mode 100644 doc/bugs/Preview_removes_page_location_drop-down_options.mdwn create mode 100644 doc/bugs/Problem_with_displaying_smileys_on_inline_page.mdwn create mode 100644 doc/bugs/Problem_with_editing_page_after_first_SVN_commit.mdwn create mode 100644 doc/bugs/Problem_with_toc.pm_plug-in.mdwn create mode 100644 doc/bugs/Problem_with_umlauts_and_friends.mdwn create mode 100644 doc/bugs/Problems_using_cygwin.mdwn create mode 100644 doc/bugs/Problems_with_graphviz.pm_plug-in.mdwn create mode 100644 doc/bugs/Problems_with_graphviz.pm_plug-in_previews.mdwn create mode 100644 doc/bugs/RecentChanges_broken_with_empty_svnpath.mdwn create mode 100644 doc/bugs/RecentChanges_contains_invalid_XHTML.mdwn create mode 100644 doc/bugs/RecentChanges_links_to_deleted_pages.mdwn create mode 100644 doc/bugs/Remove_redirect_pages_from_inline_pages.mdwn create mode 100644 doc/bugs/Renaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn create mode 100644 doc/bugs/Running_on_an_alternative_port_fails.mdwn create mode 100644 doc/bugs/SSI_include_stripped_from_mdwn.mdwn create mode 100644 doc/bugs/SVG_files_not_recognized_as_images.mdwn create mode 100644 doc/bugs/Search_Help_doesn__39__t_exist.mdwn create mode 100644 doc/bugs/Search_results_should_point_to_dir__44___not_index.html__44___when_use__95__dirs_is_enabled.mdwn create mode 100644 doc/bugs/Search_summary_includes_text_from_navigational_elements.mdwn create mode 100644 doc/bugs/Site_title_not_clickable_while_adding_a_comment.mdwn create mode 100644 doc/bugs/Slow_Filecheck_attachments___34__snails_it_all__34__.mdwn create mode 100644 doc/bugs/Slow_Filecheck_attachments___34__snails_it_all__34__/discussion.mdwn create mode 100644 doc/bugs/Smileys_in_the_block_code.mdwn create mode 100644 doc/bugs/Spaces_in_link_text_for_ikiwiki_links.mdwn create mode 100644 doc/bugs/Spurious___60__p__62___elements_added_to_tags_in_inliine_pages.mdwn create mode 100644 doc/bugs/Spurious___60__p__62___elements_added_to_tags_in_inliine_pages/discussion.mdwn create mode 100644 doc/bugs/Sub-Discussion_pages_have_a_broken___34__FormattingHelp__34___link.mdwn create mode 100644 doc/bugs/Symlinked_srcdir_requires_trailing_slash.mdwn create mode 100644 doc/bugs/Tab_delimited_tables_don__39__t_work.mdwn create mode 100644 doc/bugs/Template_variable_not_passed_as-is__63____33__.mdwn create mode 100644 doc/bugs/Titles_are_lower-cased_when_creating_a_page.mdwn create mode 100644 doc/bugs/Toc_map_and_template_plugins_do_not_play_well_together.mdwn create mode 100644 doc/bugs/Trailing_slash_breaks_links.mdwn create mode 100644 doc/bugs/URLs_with_parentheses_displayed_badly.mdwn create mode 100644 doc/bugs/UTF-16_and_UTF-32_are_unhandled.mdwn create mode 100644 doc/bugs/UTF-8_BOM_showing_up_inside_a_page__63__.mdwn create mode 100644 doc/bugs/UTF-8_in_attachment_filenames.mdwn create mode 100644 doc/bugs/Unable_to_add_attachments_to_some_pages.mdwn create mode 100644 doc/bugs/Undefined_subroutine_IkiWiki::escapeHTML.mdwn create mode 100644 doc/bugs/Undefined_subroutine_IkiWiki::refresh.mdwn create mode 100644 doc/bugs/Underscores_in_links_don__39__t_appear.mdwn create mode 100644 doc/bugs/Use_install__40__1__41___instead_of_cp__40__1__41___for_installing_files.mdwn create mode 100644 doc/bugs/W3MMode_still_uses_http:__47____47__localhost__63__.mdwn create mode 100644 doc/bugs/Warns_about_use_of_uninitialized_value_if_prefix__95__directives_is_on_and_a_directive_does_not_contain_a_space.mdwn create mode 100644 doc/bugs/Weird_interaction_between_toc_plugin_and_page_sections.mdwn create mode 100644 doc/bugs/Wrong_permissions_on_4_smileys.mdwn create mode 100644 doc/bugs/XHTML_needs_xmlns_attribute_on_html_element.mdwn create mode 100644 doc/bugs/__33__inline_sort__61____34__meta__40__date__41____34___ignored.mdwn create mode 100644 doc/bugs/__34__Currently_enabled_SSH_keys:__34___shows_only_first_139_characters_of_each_key.mdwn create mode 100644 doc/bugs/__34__First_post__34___deletion_does_not_refresh_front_page.mdwn create mode 100644 doc/bugs/__34__more__34___doesn__39__t_work.mdwn create mode 100644 doc/bugs/__34__skipping_bad_filename__34___error_when_src_path_contains_spaces.mdwn create mode 100644 doc/bugs/__36__ENV__123__PATH__125___should_include_PREFIXbin.mdwn create mode 100644 doc/bugs/__38__uuml__59___in_markup_makes_ikiwiki_not_un-escape_HTML_at_all.mdwn create mode 100644 doc/bugs/__60__br__62___tags_are_removed_from_markdown_inline_HTML.mdwn create mode 100644 doc/bugs/__63__Discussion_when_not_CGI_mode.mdwn create mode 100644 doc/bugs/__91__PATCH__93___Use_correct_perl_when_running_make.html create mode 100644 doc/bugs/__91__SOLVED__93___Pandoc_plugin_and_UTF-8:_IkiWiki_and_UTF-8.mdwn create mode 100644 doc/bugs/__91__SOLVED__93___Pandoc_plugin_and_UTF-8:_IkiWiki_and_UTF-8/discussion.mdwn create mode 100644 doc/bugs/__96____96__clear:_both__39____39___for___96__.page__42____39____63__.mdwn create mode 100644 doc/bugs/__96__wiki__95__file__95__chars__96___setting_not_propagated_to_CGI_wrapper.mdwn create mode 100644 doc/bugs/absolute_sizes_in_default_CSS.mdwn create mode 100644 doc/bugs/aggregate_generates_long_filenames.mdwn create mode 100644 doc/bugs/aggregate_global_feed_names.mdwn create mode 100644 doc/bugs/aggregate_plugin_errors.mdwn create mode 100644 doc/bugs/aggregate_plugin_errors/discussion.mdwn create mode 100644 doc/bugs/aggregate_plugin_should_honour_a_post__39__s_mctime.mdwn create mode 100644 doc/bugs/aggregate_removed_feeds_linger.mdwn create mode 100644 doc/bugs/aggregateinline_planets_wrongly_link_to_posts.mdwn create mode 100644 doc/bugs/align_doesn__39__t_always_work_with_img_plugin_.mdwn create mode 100644 doc/bugs/anonok_vs._httpauth.mdwn create mode 100644 doc/bugs/argument_isn__39__t_numeric:_mixing_templates_and_creation__95__date.mdwn create mode 100644 doc/bugs/attachment:_escaping_underscores_in_filename__63__.mdwn create mode 100644 doc/bugs/attachment:_failed_to_get_filehandle.mdwn create mode 100644 doc/bugs/attachment_plugin_enabled_by_default__63__.mdwn create mode 100644 doc/bugs/attachment_upload_does_not_work_for_windows_clients.mdwn create mode 100644 doc/bugs/backlink__40__.__41___doesn__39__t_work.mdwn create mode 100644 doc/bugs/backlinks_onhover_thing_can_go_weird.mdwn create mode 100644 doc/bugs/barfs_on_recentchange_entry_for_a_change_removing_an_invalid_pagespec.mdwn create mode 100644 doc/bugs/basewiki_uses_meta_directives_but_meta_is_not_enabled_by_default.mdwn create mode 100644 doc/bugs/beautify__95__urlpath_will_add_.__47___even_if_it_is_already_present.mdwn create mode 100644 doc/bugs/bestlink_change_update_issue.mdwn create mode 100644 doc/bugs/bestlink_returns_deleted_pages.mdwn create mode 100644 doc/bugs/blog_posts_not_added_to_mercurial_repo.mdwn create mode 100644 doc/bugs/blog_spam_plugin_not_allowing_non-ASCII_chars__63__.mdwn create mode 100644 doc/bugs/blogspam__95__options_whitelist_vs._IPv6__63__.mdwn create mode 100644 doc/bugs/blogspam_marks_me_as_spam_on_ipv6.mdwn create mode 100644 doc/bugs/both_inline_and_comment_create_elements_id__61__feedlink.mdwn create mode 100644 doc/bugs/broken_page_after_buggy_remove.mdwn create mode 100644 doc/bugs/broken_parentlinks.mdwn create mode 100644 doc/bugs/brokenlinks_accumulates_duplicate_items.mdwn create mode 100644 doc/bugs/brokenlinks_false_positives.mdwn create mode 100644 doc/bugs/bug_in_cgiurl_port.mdwn create mode 100644 doc/bugs/bug_when_toggling_in_a_preview_page.mdwn create mode 100644 doc/bugs/bugfix_for:___34__mtn:_operation_canceled:_Broken_pipe__34_____40__patch__41__.mdwn create mode 100644 doc/bugs/build_fails_oddly_when_older_ikiwiki_is_installed.mdwn create mode 100644 doc/bugs/build_in_opensolaris.mdwn create mode 100644 doc/bugs/bzr-update-syntax-error.mdwn create mode 100644 doc/bugs/bzr_2.0_breaks_bzr_plugin.mdwn create mode 100644 doc/bugs/bzr_RecentChanges_dates_start_from_1969.mdwn create mode 100644 doc/bugs/bzr_plugin_does_not_define_rcs__95__diff.mdwn create mode 100644 doc/bugs/can__39__t_mix_template_vars_inside_directives.mdwn create mode 100644 doc/bugs/cannot_clone_documented_git_repo.mdwn create mode 100644 doc/bugs/cannot_preview_shortcuts.mdwn create mode 100644 doc/bugs/cannot_reliably_use_meta_in_template.mdwn create mode 100644 doc/bugs/cannot_revert_page_deletion.mdwn create mode 100644 doc/bugs/capitalized_attachment_names.mdwn create mode 100644 doc/bugs/cgi_does_not_use_templatedir_overlay.mdwn create mode 100644 doc/bugs/cgi_wrapper_always_regenerated.mdwn create mode 100644 doc/bugs/class_parameter_of_img_directive_behave_not_as_documented.mdwn create mode 100644 doc/bugs/clearenv_not_present_at_FreeBSD_.mdwn create mode 100644 doc/bugs/clearenv_not_present_at_FreeBSD_/discussion.mdwn create mode 100644 doc/bugs/clearing_email_in_prefs.mdwn create mode 100644 doc/bugs/colon:problem.mdwn create mode 100644 doc/bugs/colon:problem/discussion.mdwn create mode 100644 doc/bugs/comments_appear_two_times.mdwn create mode 100644 doc/bugs/comments_not_searchable.mdwn create mode 100644 doc/bugs/comments_preview_unsafe_with_allowdirectives.mdwn create mode 100644 doc/bugs/comments_produce_broken_links_in_RecentChanges.mdwn create mode 100644 doc/bugs/complex_wiki-code___40__braces__41___in_wikilink-text_breaks_wikilinks.mdwn create mode 100644 doc/bugs/conditional_preprocess_during_scan.mdwn create mode 100644 doc/bugs/conflicts.mdwn create mode 100644 doc/bugs/correct_published_and_updated_time_information_for_the_feeds.mdwn create mode 100644 doc/bugs/creating_page_from_comment_creates_a_comment.mdwn create mode 100644 doc/bugs/cutpaste.pm:_missing_filter_call.mdwn create mode 100644 doc/bugs/ddate_plugin_causes_websetup_to_change_timeformat__44___even_when_disabled.mdwn create mode 100644 doc/bugs/debbug_shortcut_should_expand_differently.mdwn create mode 100644 doc/bugs/debbug_shortcut_should_expand_differently/discussion.mdwn create mode 100644 doc/bugs/debian_package_doesn__39__t_pull_in_packages_required_for_openid.mdwn create mode 100644 doc/bugs/default__95__pageext_not_working.mdwn create mode 100644 doc/bugs/definition_lists_should_be_bold.mdwn create mode 100644 doc/bugs/defintion_lists_appear_to_be_disabled.mdwn create mode 100644 doc/bugs/deletion_warnings.mdwn create mode 100644 doc/bugs/depends_simple_mixup.mdwn create mode 100644 doc/bugs/diff_links_to_backtrace.mdwn create mode 100644 doc/bugs/disable_sub-discussion_pages.mdwn create mode 100644 doc/bugs/disabling_backlinks.mdwn create mode 100644 doc/bugs/discussion.mdwn create mode 100644 doc/bugs/discussion_of_what__63__.mdwn create mode 100644 doc/bugs/discussion_pages_with_uppercase_characters_break_the_detection_of_the_best_location.mdwn create mode 100644 doc/bugs/discussion_removal.mdwn create mode 100644 doc/bugs/done.mdwn create mode 100644 doc/bugs/dumpsetup_does_not_save_destdir.mdwn create mode 100644 doc/bugs/edit_preview_resolves_links_differently_from_commit.mdwn create mode 100644 doc/bugs/editmessage_turned_off_in_web_interface__63__.mdwn create mode 100644 doc/bugs/edits_not_showing_up_in_compiled_pages.mdwn create mode 100644 doc/bugs/edittemplate_seems_not_to_be_working.mdwn create mode 100644 doc/bugs/emails_should_not_be_considered_as_broken_links.mdwn create mode 100644 doc/bugs/encoding_issue_in_blogspam_plugin.mdwn create mode 100644 doc/bugs/entirely_negated_pagespec_matches_internal_pages.mdwn create mode 100644 doc/bugs/enumerations_of_dates_not_formatted_correctly.mdwn create mode 100644 doc/bugs/errors_with_ampersand_in_filename.mdwn create mode 100644 doc/bugs/example_Mercurial_historyurl_doesn__39__t_show_file_history.mdwn create mode 100644 doc/bugs/external_links_inside_headings_don__39__t_work.mdwn create mode 100644 doc/bugs/external_plugins_cannot_access_ARGV_needed_for_getopt.mdwn create mode 100644 doc/bugs/feedfile_does_the_wrong_thing_from_index.mdwn2.mdwn create mode 100644 doc/bugs/feedpages_does_not_prevent_tags_from_being_aggregated.mdwn create mode 100644 doc/bugs/feeds_get_removed_in_strange_conditions.mdwn create mode 100644 doc/bugs/filecheck_failing_to_find_files.mdwn create mode 100644 doc/bugs/find:_invalid_predicate___96__-L__39__.mdwn create mode 100644 doc/bugs/find_gnuism.mdwn create mode 100644 doc/bugs/firefox_doesn__39__t_want_to_load_updated_pages_at_ikiwiki.info.mdwn create mode 100644 doc/bugs/format_bug.mdwn create mode 100644 doc/bugs/formbuilder_3.0401_broken.mdwn create mode 100644 doc/bugs/git.pm_should_prune_remote_branches_when_fetching.mdwn create mode 100644 doc/bugs/git_commit_adds_files_that_were_not_tracked.mdwn create mode 100644 doc/bugs/git_fails_to_compile.mdwn create mode 100644 doc/bugs/git_mail_notification_race.mdwn create mode 100644 doc/bugs/git_stderr_output_causes_problems.mdwn create mode 100644 doc/bugs/git_utf8.mdwn create mode 100644 doc/bugs/gitremotes_script_picks_up_tags_from_anywhere.mdwn create mode 100644 doc/bugs/gitweb_deficiency_w.r.t._log_messages.mdwn create mode 100644 doc/bugs/gitweb_deficiency_w.r.t._newly_created_pages.mdwn create mode 100644 doc/bugs/goto_with_bad_page_name.mdwn create mode 100644 doc/bugs/graphviz_demo_generates_empty_graph.mdwn create mode 100644 doc/bugs/hardcoded___34__Discussion__34___link.mdwn create mode 100644 doc/bugs/helponformatting_link_disappears.mdwn create mode 100644 doc/bugs/html5_support.mdwn create mode 100644 doc/bugs/html5_time_element__39__s_pubdate_wrong_when_using_xhtml5___34__mode__34__.mdwn create mode 100644 doc/bugs/html_errors.mdwn create mode 100644 doc/bugs/htmlbalance_fails_with_HTML-Tree_v4.mdwn create mode 100644 doc/bugs/htmlscrubber_breaks_multimarkdown_footnotes.mdwn create mode 100644 doc/bugs/htmlscrubber_still_scrubbing_HTML_from_mdwn_pages.mdwn create mode 100644 doc/bugs/htmlscrubber_undoes_email_obfuscation_by_Text::Markdown.mdwn create mode 100644 doc/bugs/htmltidy_has_no_possibilty_to_use_an_alternative_config_file_which_may_break_other_usages.mdwn create mode 100644 doc/bugs/http_proxy_for_openid.mdwn create mode 100644 doc/bugs/httpauth_conflicts_with_git_anon_push.mdwn create mode 100644 doc/bugs/ikiwiki-mass-rebuild_fails_to_drop_privileges_and_execute_ikiwiki.mdwn create mode 100644 doc/bugs/ikiwiki-transition_does_not_set_perl_moduels_path_properly.mdwn create mode 100644 doc/bugs/ikiwiki.setup:_syntax_error_at___40__eval_5__41___line_120__44___at_EOF.mdwn create mode 100644 doc/bugs/ikiwiki.setup_require_blank_rcs_to_work_as_cgi_only.mdwn create mode 100644 doc/bugs/ikiwiki__39__s_ViewVC_down.mdwn create mode 100644 doc/bugs/ikiwiki_cgi_fails_to_build_on_Solaris_due_to_missing_LOCK__95__EX.mdwn create mode 100644 doc/bugs/ikiwiki_ignores_PATH_environment.mdwn create mode 100644 doc/bugs/ikiwiki_lacks_a_--quiet.mdwn create mode 100644 doc/bugs/ikiwiki_overzealously_honours_locks_when_asked_for_forms.mdwn create mode 100644 doc/bugs/ikiwiki_renders___39__28__39___if_external_plugins_return_nothing.mdwn create mode 100644 doc/bugs/images_in_inlined_pages_have_wrong_relative_URL.mdwn create mode 100644 doc/bugs/img_plugin_and_class_attr.mdwn create mode 100644 doc/bugs/img_plugin_and_missing_heigth_value.mdwn create mode 100644 doc/bugs/img_plugin_causes_taint_failure.mdwn create mode 100644 doc/bugs/img_plugin_renders___60__img__62___tag_without_src_attribute_post-2.20.mdwn create mode 100644 doc/bugs/img_plugin_should_pass_through_class_attribute.mdwn create mode 100644 doc/bugs/img_vs_align.mdwn create mode 100644 doc/bugs/img_with_alt_has_extra_double_quote.mdwn create mode 100644 doc/bugs/index.html__63__updated.mdwn create mode 100644 doc/bugs/index.html_is_made_visible_by_various_actions.mdwn create mode 100644 doc/bugs/iniline_breaks_toc_plugin.mdwn create mode 100644 doc/bugs/inline_action_buttons_circumvent_exclude_criteria_from_edittemplate__39__s_match__61____34____34___pagespec.mdwn create mode 100644 doc/bugs/inline_archive_crash.mdwn create mode 100644 doc/bugs/inline_breaks_PERMALINK_variable.mdwn create mode 100644 doc/bugs/inline_from_field_empty_if_rootpage_doesn__39__t_exist.mdwn create mode 100644 doc/bugs/inline_page_not_updated_on_removal.mdwn create mode 100644 doc/bugs/inline_plugin_rootpage_option_is_not_case_insensitive.mdwn create mode 100644 doc/bugs/inline_plugin_sets_editurl_even_when_editpage_is_disabled.html create mode 100644 doc/bugs/inline_raw_broken_on_unknown_pagetype.mdwn create mode 100644 doc/bugs/inline_skip_causes_empty_inline.mdwn create mode 100644 doc/bugs/inline_sort-by-title_issues.mdwn create mode 100644 doc/bugs/inline_sort_order_and_meta_date_value.mdwn create mode 100644 doc/bugs/install_into_home_dir_fails.mdwn create mode 100644 doc/bugs/installing_from_svn_copies_.svn_directories.mdwn create mode 100644 doc/bugs/internal_error:_smileys.mdwn_cannot_be_found.mdwn create mode 100644 doc/bugs/ipv6_address_in_comments.mdwn create mode 100644 doc/bugs/jquery-ui.min.css_missing_some_image_files.mdwn create mode 100644 doc/bugs/librpc-xml-perl_0.69_breaks_XML-RPC_plugins.mdwn create mode 100644 doc/bugs/linkingrules_should_document_how_to_link_to_page_at_root_if_non-root_page_exists.mdwn create mode 100644 doc/bugs/linkmap_displays_underscore_escapes.mdwn create mode 100644 doc/bugs/linkmap_displays_underscore_escapes/the_patch.pl create mode 100644 doc/bugs/links_from_sidebars.mdwn create mode 100644 doc/bugs/links_from_sidebars/discussion.mdwn create mode 100644 doc/bugs/links_misparsed_in_CSV_files.mdwn create mode 100644 doc/bugs/listdirectives_doesn__39__t_register_a_link.mdwn create mode 100644 doc/bugs/lockedit_plugin_should_alert_user_about_an_invalid_pagespec_in_preferences.mdwn create mode 100644 doc/bugs/locking_fun.mdwn create mode 100644 doc/bugs/login_page_non-obvious_with_openid.mdwn create mode 100644 doc/bugs/login_page_should_note_cookie_requirement.mdwn create mode 100644 doc/bugs/logout_in_ikiwiki.mdwn create mode 100644 doc/bugs/mailto:_links_not_properly_generated_in_rssatom_feeds.mdwn create mode 100644 doc/bugs/map_does_not_link_directory_for_which_a_file_also_exists.mdwn create mode 100644 doc/bugs/map_does_not_link_directory_for_which_a_file_also_exists/discussion.mdwn create mode 100644 doc/bugs/map_doesn__39__t_calculate___34__common__95__prefix__34___correctly.mdwn create mode 100644 doc/bugs/map_fails_to_close_ul_element_for_empty_list.mdwn create mode 100644 doc/bugs/map_generates_malformed_HTML.mdwn create mode 100644 doc/bugs/map_is_inconsistent_about_bare_directories.mdwn create mode 100644 doc/bugs/map_sorts_by_pagename_and_not_title_when_show__61__title_is_used.mdwn create mode 100644 doc/bugs/maps_with_nested_directories_sometimes_make_ugly_lists.mdwn create mode 100644 doc/bugs/markdown_bug:_email_escaping_and_plus_addresses.mdwn create mode 100644 doc/bugs/markdown_module_location.mdwn create mode 100644 doc/bugs/mercurial_fail_to_add.mdwn create mode 100644 doc/bugs/merging_to_basewiki_causes_odd_inconsistencies.mdwn create mode 100644 doc/bugs/messed_up_repository.mdwn create mode 100644 doc/bugs/meta_inline.mdwn create mode 100644 doc/bugs/methodResponse_in_add__95__plugins.mdwn create mode 100644 doc/bugs/minor:_tiny_rendering_error.mdwn create mode 100644 doc/bugs/misctemplate_does_not_respect_the_current_page___40__if_any__41__.mdwn create mode 100644 doc/bugs/monotone_backend_does_not_support_srcdir_in_subdir.mdwn create mode 100644 doc/bugs/more_and_RSS_generation.mdwn create mode 100644 doc/bugs/multiple_encoding_issues_in_atom.mdwn create mode 100644 doc/bugs/multiple_pages_with_same_name.mdwn create mode 100644 doc/bugs/multiple_rss_feeds_per_page.mdwn create mode 100644 doc/bugs/must_save_before_uploading_more_than_one_attachment.mdwn create mode 100644 doc/bugs/nested_inlines_produce_no_output.mdwn create mode 100644 doc/bugs/nested_raw_included_inlines.mdwn create mode 100644 doc/bugs/newfile-test.mdwn create mode 100644 doc/bugs/no_commit_mails_for_new_pages.mdwn create mode 100644 doc/bugs/no_easy_way_to_wrap_HTML_container_around_a_set_of_inlined_pages.mdwn create mode 100644 doc/bugs/no_search_button__44___hence_can__39__t_do_search_in_w3m_at_ikiwiki.info.mdwn create mode 100644 doc/bugs/nonexistent_pages_in_inline_pagenames_do_not_add_a_dependency.mdwn create mode 100644 doc/bugs/octal_umask_setting_is_unintuitive.mdwn create mode 100644 doc/bugs/opendiscussion_should_respect_the_discussion_option.mdwn create mode 100644 doc/bugs/opendiscussion_should_respect_the_discussion_option/discussion.mdwn create mode 100644 doc/bugs/openid_incompatability_with_pyblosxom_openid_server_plugin_when_used_with_simple_registration_extension.mdwn create mode 100644 doc/bugs/openid_no_longer_pretty-prints_OpenIDs.mdwn create mode 100644 doc/bugs/openid_postsignin_failure.mdwn create mode 100644 doc/bugs/osm_KML_maps_do_not_display_properly_on_google_maps.mdwn create mode 100644 doc/bugs/osm_KML_maps_icon_path_have_a_trailing_slash.mdwn create mode 100644 doc/bugs/osm_linkto__40____41___usage_breaks_map_rendering.mdwn create mode 100644 doc/bugs/osm_sometimes_looses_some_nodes.mdwn create mode 100644 doc/bugs/output_of_successful_rename_should_list_the_full_path_to_affected_pages.mdwn create mode 100644 doc/bugs/package_build_fails_in_non-English_environment.mdwn create mode 100644 doc/bugs/page_is_not_rebuilt_if_it_changes_extension.mdwn create mode 100644 doc/bugs/page_preview_does_not_work_on_new_page_with_a_table.mdwn create mode 100644 doc/bugs/pagecount_is_broken.mdwn create mode 100644 doc/bugs/pagemtime_in_refresh_mode.mdwn create mode 100644 doc/bugs/pages_missing_top-level_directory.mdwn create mode 100644 doc/bugs/pages_under_templates_are_invalid.mdwn create mode 100644 doc/bugs/pagespec:_tagged__40____41____44___globbing.mdwn create mode 100644 doc/bugs/pagespec_can__39__t_match___123__curly__125___braces.mdwn create mode 100644 doc/bugs/pagespec_error_on_refresh_but_not_rebuild.mdwn create mode 100644 doc/bugs/pagespec_parsing_chokes_on_function__40____41__.mdwn create mode 100644 doc/bugs/pagestats_plugin_broken.mdwn create mode 100644 doc/bugs/pagetitle_function_does_not_respect_meta_titles.mdwn create mode 100644 doc/bugs/parsing_for_WikiWords_should_only_be_done_outside_html_tags.mdwn create mode 100644 doc/bugs/password_deletion.mdwn create mode 100644 doc/bugs/perl:_double_free_or_corruption.mdwn create mode 100644 doc/bugs/pipe-symbol_in_taglink_target.mdwn create mode 100644 doc/bugs/pipe_in_tables_as_characters.mdwn create mode 100644 doc/bugs/plugin___96__rename__96___fails_if___96__attachment__96___is_not_enabled.mdwn create mode 100644 doc/bugs/plugins__47__relativedate_depends_on_locale_at_setup_file.mdwn create mode 100644 doc/bugs/po:__apache_config_serves_index_directory_for_index.mdwn create mode 100644 doc/bugs/po:_apache_config_serves_index.rss_for_index.mdwn create mode 100644 doc/bugs/po:_double_commits_of_po_files.mdwn create mode 100644 doc/bugs/po:_markdown_link_parse_bug.mdwn create mode 100644 doc/bugs/po:_might_not_add_translated_versions_of_all_underlays.mdwn create mode 100644 doc/bugs/po:_new_pages_not_translatable.mdwn create mode 100644 doc/bugs/po:_plugin_should_not_override_the_title_on_the_homepage.mdwn create mode 100644 doc/bugs/po:_po4a_too_strict_on_html_pages.mdwn create mode 100644 doc/bugs/po:_po_files_instead_of_html_files.mdwn create mode 100644 doc/bugs/po:_ugly_messages_with_empty_files.mdwn create mode 100644 doc/bugs/po:broken_links_to_translatable_basewiki_pages_that_lack_po_fies.mdwn create mode 100644 doc/bugs/po_plugin_adds_new_dependency.mdwn create mode 100644 doc/bugs/po_plugin_cannot_add_po_files_into_git.mdwn create mode 100644 doc/bugs/po_vs_templates.mdwn create mode 100644 doc/bugs/poll_plugin:_can__39__t_vote_for_non-ascii_options.mdwn create mode 100644 doc/bugs/poll_plugin_uses_GET.mdwn create mode 100644 doc/bugs/possibly_po_related_error.mdwn create mode 100644 doc/bugs/post-commit_hangs.mdwn create mode 100644 doc/bugs/post-update_hook_can__39__t_be_compiled_with_tcc.mdwn create mode 100644 doc/bugs/prettydate_with_weekday-date_inconsistency.mdwn create mode 100644 doc/bugs/preview_base_url_should_be_absolute.mdwn create mode 100644 doc/bugs/preview_pagestate.mdwn create mode 100644 doc/bugs/previewing_new_page_can_leave_files_dangling.mdwn create mode 100644 doc/bugs/previewing_with_an_edittemplate_reverts_edit_box.mdwn create mode 100644 doc/bugs/problem_adding_tag_from_template.mdwn create mode 100644 doc/bugs/proxy.py_utf8_troubles.mdwn create mode 100644 doc/bugs/prune_causing_taint_mode_failures.mdwn create mode 100644 doc/bugs/pruning_is_too_strict.mdwn create mode 100644 doc/bugs/quieten_mercurial.mdwn create mode 100644 doc/bugs/raw_html_in-page_and___91____91____33__included__93____93__.mdwn create mode 100644 doc/bugs/rebuild_after_changing_the_underlaydir_config_option.mdwn create mode 100644 doc/bugs/recentchanges_escaping.mdwn create mode 100644 doc/bugs/recentchanges_feed_links.mdwn create mode 100644 doc/bugs/recentchanges_sets_has__95__diffurl__61__1_when_diffurl_is_empty.mdwn create mode 100644 doc/bugs/recentchangesdiff_crashes_on_commits_which_remove_a_lot_of_files.mdwn create mode 100644 doc/bugs/relative_date_weird_results.mdwn create mode 100644 doc/bugs/removal_of_transient_pages.mdwn create mode 100644 doc/bugs/remove_orphaned_sparkline-php_from_Suggests.mdwn create mode 100644 doc/bugs/remove_plugin_and_untracked_files.mdwn create mode 100644 doc/bugs/removing_pages_with_utf8_characters.mdwn create mode 100644 doc/bugs/rename_fixup_not_attributed_to_author.mdwn create mode 100644 doc/bugs/renaming_a_page_destroyed_some_links.mdwn create mode 100644 doc/bugs/resized_img_with_only_width_or_height_breaks_ie.mdwn create mode 100644 doc/bugs/rss_feed_cleanup_on_delete.mdwn create mode 100644 doc/bugs/rss_feeds_do_not_use_recommended_encoding_of_entities_for_some_fields.mdwn create mode 100644 doc/bugs/rss_output_relative_links.mdwn create mode 100644 doc/bugs/rst_fails_on_file_containing_only_a_number.mdwn create mode 100644 doc/bugs/rst_plugin_hangs_on_utf-8.mdwn create mode 100644 doc/bugs/rst_plugin_has_python_hardcode_in_shebang_line.mdwn create mode 100644 doc/bugs/rst_plugin_traceback_with_SimpleXMLRPCDispatcher_from_pyhton_2.5.mdwn create mode 100644 doc/bugs/rst_tweak.mdwn create mode 100644 doc/bugs/search:___34__link__34___and___34__title__34___fields_are_incorrectly_specified.mdwn create mode 100644 doc/bugs/search_creates_configuration_files_many_times_on_rebuild.mdwn create mode 100644 doc/bugs/search_for_locale_data_in_the_installed_location.mdwn create mode 100644 doc/bugs/search_plugin_and_CGI_preview.mdwn create mode 100644 doc/bugs/search_plugin_finds_no_results_with_xapian_1.2.7.mdwn create mode 100644 doc/bugs/search_plugin_uses_wrong_css_path.mdwn create mode 100644 doc/bugs/search_template_missing_dep.mdwn create mode 100644 doc/bugs/several_entries_in_docs__47__bugs_contain_colons_in_the_filename.mdwn create mode 100644 doc/bugs/shortcut_encoding.mdwn create mode 100644 doc/bugs/shortcut_plugin_will_not_work_without_shortcuts.mdwn.mdwn create mode 100644 doc/bugs/shortcuts_don__39__t_escape_from_Markdown.mdwn create mode 100644 doc/bugs/sidebar_is_obscured_by_recentchanges.mdwn create mode 100644 doc/bugs/sidebar_not_updated_in_unedited_subpages.mdwn create mode 100644 doc/bugs/sitemap_includes_images_directly.mdwn create mode 100644 doc/bugs/some_but_not_all_meta_fields_are_stored_escaped.mdwn create mode 100644 doc/bugs/some_strings_are_not_internationalized.mdwn create mode 100644 doc/bugs/space_in_a___91____91__page_link__93____93___doesn__39__t_make_link.mdwn create mode 100644 doc/bugs/special_characters_in_tag_names_need_manual_escaping.mdwn create mode 100644 doc/bugs/ssl_certificates_not_checked_with_openid.mdwn create mode 100644 doc/bugs/strange_hook_id_in_skeleton.pm.mdwn create mode 100644 doc/bugs/stray___60____47__p__62___tags.mdwn create mode 100644 doc/bugs/support_for_openid2_logins.mdwn create mode 100644 doc/bugs/svn+ssh_commit_fail.mdwn create mode 100644 doc/bugs/svn-commit-hanging.mdwn create mode 100644 doc/bugs/svn_commit_failures_interpreted_as_merge_conflicts.mdwn create mode 100644 doc/bugs/svn_fails_to_update.mdwn create mode 100644 doc/bugs/svn_post-commit_wrapper_can__39__t_find_IkiWiki.pm_if_not_installed.mdwn create mode 100644 doc/bugs/syntax_error_in_aggregate.mdwn create mode 100644 doc/bugs/table_external_file_links.mdwn create mode 100644 doc/bugs/table_plugin_does_not_handle___92__r__92__n_lines_in_CSV_files.mdwn create mode 100644 doc/bugs/tag_behavior_changes_introduced_by_typed_link_feature.mdwn create mode 100644 doc/bugs/tag_plugin:_autotag_vs._staged_changes.mdwn create mode 100644 doc/bugs/tagged__40____41___matching_wikilinks.mdwn create mode 100644 doc/bugs/tags__44___backlinks_and_3.x.mdwn create mode 100644 doc/bugs/tags_base_dir_not_used_when_creating_new_tags.mdwn create mode 100644 doc/bugs/taint_and_-T.mdwn create mode 100644 doc/bugs/taint_issue_with_regular_expressions.mdwn create mode 100644 doc/bugs/tbasewiki__95__brokenlinks.t_broken.mdwn create mode 100644 doc/bugs/tbasewiki__95__brokenlinks.t_broken/discussion.mdwn create mode 100644 doc/bugs/templateForRecentChangesMissingCloseSpan.mdwn create mode 100644 doc/bugs/template_creation_error.mdwn create mode 100644 doc/bugs/teximg_does_not_work_Preview.mdwn create mode 100644 doc/bugs/teximg_fails_if_same_tex_is_used_on_multiple_pages.mdwn create mode 100644 doc/bugs/textile_plugin_dies_if_input_has_a_non-utf8_character.mdwn create mode 100644 doc/bugs/the_login_page_is_unclear_when_multiple_methods_exist.mdwn create mode 100644 doc/bugs/title__40____41___in_a_PageSpec__44___with_meta_enabled__44___causes_a_crash.mdwn create mode 100644 doc/bugs/toc_displays_headings_from_sidebar.mdwn create mode 100644 doc/bugs/toc_in_sidebar.mdwn create mode 100644 doc/bugs/toggle_expects_body_element_without_attributes.mdwn create mode 100644 doc/bugs/toggle_fails_on_Safari.mdwn create mode 100644 doc/bugs/trail_excess_dependencies.mdwn create mode 100644 doc/bugs/trail_shows_on_cgi_pages.mdwn create mode 100644 doc/bugs/trail_test_suite_failures.mdwn create mode 100644 doc/bugs/transient_autocreated_tagbase_is_not_transient_autoindexed.mdwn create mode 100644 doc/bugs/transitive_dependencies.mdwn create mode 100644 doc/bugs/trouble_with_base_in_search.mdwn create mode 100644 doc/bugs/txt_plugin_having_problems_with_meta_directives.mdwn create mode 100644 doc/bugs/typo_in_ikiwiki.setup.mdwn create mode 100644 doc/bugs/typo_in_skeleton.pm:_sessionncgi.mdwn create mode 100644 doc/bugs/undefined_tags_or_mismatched_tags_won__39__t_get_converted.mdwn create mode 100644 doc/bugs/undefined_value_as_a_HASH_reference.mdwn create mode 100644 doc/bugs/underlaydir_file_expose.mdwn create mode 100644 doc/bugs/unicode_chars_in_wikiname_break_auth.mdwn create mode 100644 doc/bugs/unicode_encoded_urls_and_recentchanges.mdwn create mode 100644 doc/bugs/unrecognized___34__do__61__blog__34___CGI_parameter_when_creating_todo_item.mdwn create mode 100644 doc/bugs/unwanted_discussion_links_on_discussion_pages.mdwn create mode 100644 doc/bugs/urlto_API_change_breaks_wikis_with_po_plugin.mdwn create mode 100644 doc/bugs/urlto__40____34____34____44___...__44___1__41___not_absolute.mdwn create mode 100644 doc/bugs/user_links_on_recentchanges_pages_problem.mdwn create mode 100644 doc/bugs/utf-8_bug_in_websetup.pm.mdwn create mode 100644 doc/bugs/utf8_html_templates.mdwn create mode 100644 doc/bugs/utf8_svn_log.mdwn create mode 100644 doc/bugs/web_reversion_on_ikiwiki.info.mdwn create mode 100644 doc/bugs/websetup_eats_setupconf_and_allow__95__symlinks__95__before__95__srcdir.mdwn create mode 100644 doc/bugs/weird_signature_in_match__95__included__40____41__.mdwn create mode 100644 doc/bugs/weird_syntax_in_aggregate.pm.mdwn create mode 100644 doc/bugs/wiki_formatting_does_not_work_between_toc_and_an_inline.mdwn create mode 100644 doc/bugs/wiki_links_still_processed_inside_code_blocks.mdwn create mode 100644 doc/bugs/wiki_rebuild_should_throw_errors_if_the_configured_underlaydir_or_templatedir_don__39__t_exist.mdwn create mode 100644 doc/bugs/wikilink_in_table.mdwn create mode 100644 doc/bugs/word_wrap.mdwn create mode 100644 doc/bugs/wrapper_can__39__t_find_the_perl_modules.mdwn create mode 100644 doc/bugs/wrong_attachment_size.mdwn create mode 100644 doc/bugs/wrong_discussion_page_created.mdwn create mode 100644 doc/bugs/wrong_link_in_recentchanges_when_reverting_an_ikiwiki_outside_git_root.mdwn create mode 100644 doc/bugs/wrong_permissions_on_some_files_in_source.mdwn create mode 100644 doc/bugs/wrong_rss_url_when_inside_another_blog-like_page.mdwn create mode 100644 doc/bugs/xgettext_issue.mdwn create mode 100644 doc/bugs/yaml:xs_codependency_not_listed.mdwn create mode 100644 doc/bugs/yaml_setup_file_does_not_support_UTF-8_if_XS_is_installed.mdwn create mode 100644 doc/cgi.mdwn create mode 100644 doc/cgi/discussion.mdwn create mode 100644 doc/commit-internals.mdwn create mode 100644 doc/competition.mdwn create mode 100644 doc/consultants.mdwn create mode 100644 doc/contact.mdwn create mode 100644 doc/contact/discussion.mdwn create mode 100644 doc/convert.mdwn create mode 100644 doc/css.mdwn create mode 100644 doc/css/discussion.mdwn create mode 100644 doc/css_market.mdwn create mode 100644 doc/css_market/02_template.css create mode 100644 doc/css_market/02_template.tmpl create mode 100644 doc/css_market/bma.css create mode 100644 doc/css_market/cstamas.css create mode 100644 doc/css_market/discussion.mdwn create mode 100644 doc/css_market/embeddedmoose.css create mode 100644 doc/css_market/kirkambar.css create mode 100644 doc/css_market/zack.css create mode 100644 doc/download.mdwn create mode 100644 doc/examples.mdwn create mode 100644 doc/examples/blog.mdwn create mode 100644 doc/examples/blog/archives.mdwn create mode 100644 doc/examples/blog/comments.mdwn create mode 100644 doc/examples/blog/discussion.mdwn create mode 100644 doc/examples/blog/index.mdwn create mode 100644 doc/examples/blog/posts.mdwn create mode 100644 doc/examples/blog/posts/first_post.mdwn create mode 100644 doc/examples/blog/sidebar.mdwn create mode 100644 doc/examples/blog/tags.mdwn create mode 100644 doc/examples/softwaresite.mdwn create mode 100644 doc/examples/softwaresite/Makefile create mode 100644 doc/examples/softwaresite/bugs.mdwn create mode 100644 doc/examples/softwaresite/bugs/done.mdwn create mode 100644 doc/examples/softwaresite/bugs/fails_to_frobnicate.mdwn create mode 100644 doc/examples/softwaresite/bugs/hghg.mdwn create mode 100644 doc/examples/softwaresite/bugs/needs_more_bugs.mdwn create mode 100644 doc/examples/softwaresite/contact.mdwn create mode 100644 doc/examples/softwaresite/doc.mdwn create mode 100644 doc/examples/softwaresite/doc/faq.mdwn create mode 100644 doc/examples/softwaresite/doc/install.mdwn create mode 100644 doc/examples/softwaresite/doc/setup.mdwn create mode 100644 doc/examples/softwaresite/download.mdwn create mode 100644 doc/examples/softwaresite/index.mdwn create mode 100644 doc/examples/softwaresite/news.mdwn create mode 100644 doc/examples/softwaresite/news/version_1.0.mdwn create mode 100644 doc/examples/softwaresite/templates/release.mdwn create mode 100644 doc/favicon.ico create mode 100644 doc/features.mdwn create mode 100644 doc/forum.mdwn create mode 100644 doc/forum/404_-_not_found.mdwn create mode 100644 doc/forum/404_-_not_found/comment_1_3dea2600474f77fb986767da4d507d62._comment create mode 100644 doc/forum/404_-_not_found/comment_2_948e4678be6f82d9b541132405351a2c._comment create mode 100644 doc/forum/404_-_not_found/comment_3_4c7b1fa88776815bbc6aa286606214c2._comment create mode 100644 doc/forum/Accessing_meta_values_in_pages__63__.mdwn create mode 100644 doc/forum/Adding_new_markup_to_markdown.mdwn create mode 100644 doc/forum/Allow_only_specific_OpenIDs_to_login.mdwn create mode 100644 doc/forum/Anyone_mirroring_ikiwiki_inline_feed_to_identi.ca__63__.mdwn create mode 100644 doc/forum/Anyone_mirroring_ikiwiki_inline_feed_to_identi.ca__63__/comment_1_8a5acbb6234104b607c8c4cf16124ae4._comment create mode 100644 doc/forum/Anyone_mirroring_ikiwiki_inline_feed_to_identi.ca__63__/comment_2_155e5823860a91989647ede8b5c9224a._comment create mode 100644 doc/forum/Anyone_mirroring_ikiwiki_inline_feed_to_identi.ca__63__/comment_3_317f1202a3da1bfc845d4becbac4bba8._comment create mode 100644 doc/forum/Apache_XBitHack.mdwn create mode 100644 doc/forum/Are_these_revisions_legit_or_accidentally_committed_jiberish__63__.mdwn create mode 100644 doc/forum/Are_these_revisions_legit_or_accidentally_committed_jiberish__63__/comment_1_b425823f800fba82ad2aaaa0dbe6686a._comment create mode 100644 doc/forum/Asciidoc_plugin.mdwn create mode 100644 doc/forum/Attachment_and_sub-directory.mdwn create mode 100644 doc/forum/Background_picture_and_css.mdwn create mode 100644 doc/forum/Blog_posting_times_and_ikiwiki_state.mdwn create mode 100644 doc/forum/Blog_posting_times_and_ikiwiki_state/comment_1_87304dfa2caea7e526cdb04917524e8c._comment create mode 100644 doc/forum/Broken_after_upgrading_Ikiwiki.mdwn create mode 100644 doc/forum/Broken_after_upgrading_Ikiwiki/comment_1_3d0588a845c58b3aedc35970e8dcc811._comment create mode 100644 doc/forum/Broken_after_upgrading_Ikiwiki/comment_2_fd65d4b87a735b67543bb0cf4053b652._comment create mode 100644 doc/forum/Broken_after_upgrading_Ikiwiki/comment_3_7c8b46eabdb25cbc01c56c7b53ed3b91._comment create mode 100644 doc/forum/CGI_script_and_HTTPS.mdwn create mode 100644 doc/forum/CGI_script_and_HTTPS/comment_1_3f8ef438ca7de11635d4e40080e7baa9._comment create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day.mdwn create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day/comment_1_d3dd0b97c63d615e3dee22ceacaa5a30._comment create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day/comment_2_2311b96483bb91dc25d5e3695bbca513._comment create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day/comment_3_d23f0cedd0b9e937eaf200eef55ac457._comment create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day/comment_4_4be39c2043821848d4b25d0bf946a718._comment create mode 100644 doc/forum/Calendar:_listing_multiple_entries_per_day/comment_5_de545ebb6376066674ef2aaae4757b9c._comment create mode 100644 doc/forum/Can_I_change_the_default_menu_items__63__.mdwn create mode 100644 doc/forum/Can_I_change_the_default_menu_items__63__/comment_2_eb56fed3b5fc19c8dd49af4444a049c5._comment create mode 100644 doc/forum/Can_I_have_different_favicons_for_each_folder__63__.mdwn create mode 100644 doc/forum/Can_I_have_different_favicons_for_each_folder__63__/comment_1_a01112ba235e2f44a7655c36ef680e7e._comment create mode 100644 doc/forum/Can_I_have_different_favicons_for_each_folder__63__/comment_2_b8ccd3c29249eca73766f567bce12569._comment create mode 100644 doc/forum/Can_Ikiwiki_recognize_multimarkdown_meta_tags__63__.mdwn create mode 100644 doc/forum/Can_OpenID_users_be_adminusers__63__.mdwn create mode 100644 doc/forum/Can__39__t_get_comments_plugin_working.mdwn create mode 100644 doc/forum/Can__39__t_get_ikiwiki_working_again_after_reinstall.mdwn create mode 100644 doc/forum/Can__39__t_get_ikiwiki_working_again_after_reinstall/comment_1_87a360155ff0502fe08274911cc6a53f._comment create mode 100644 doc/forum/Can__39__t_login_using_Google__44___or_openID__44___but_can_use_Ikiwiki_login.mdwn create mode 100644 doc/forum/Can__39__t_login_using_Google__44___or_openID__44___but_can_use_Ikiwiki_login/comment_1_79127e3c09a1d798146088dee5a67708._comment create mode 100644 doc/forum/Can_custom_themes_live_somewhere_inside_srcdir__63__.mdwn create mode 100644 doc/forum/Can_custom_themes_live_somewhere_inside_srcdir__63__/comment_1_d1e79825dfb5213d2d1cba2ace1707b1._comment create mode 100644 doc/forum/Can_custom_themes_live_somewhere_inside_srcdir__63__/comment_2_8177ede5a586b1a573a13fd26f8d3cc0._comment create mode 100644 doc/forum/Can_ikiwiki_be_configured_as_multi_user_blog__63__.mdwn create mode 100644 doc/forum/Can_not_advance_past_first_page_of_results_using_search_plugin.mdwn create mode 100644 doc/forum/Can_one_tell_if_a_page_is_added_rather_than_changed__63__.mdwn create mode 100644 doc/forum/Can_one_tell_if_a_page_is_added_rather_than_changed__63__/comment_1_1397feebfb0fb7cc57af2f8b74ce047e._comment create mode 100644 doc/forum/Can_one_tell_if_a_page_is_added_rather_than_changed__63__/comment_2_ad36c945f59fe525428fc30246911ff5._comment create mode 100644 doc/forum/Cannot_write_to_commitlock.mdwn create mode 100644 doc/forum/Chinese_file_name_corruption.mdwn create mode 100644 doc/forum/Chinese_file_name_corruption/comment_1_765ac8b6f70083bb5aaaaac5beab461f._comment create mode 100644 doc/forum/Clarification_on_--cgi_option.mdwn create mode 100644 doc/forum/Clarification_on_--cgi_option/comment_1_deda457e4bff7dfe630dbc0192dfddea._comment create mode 100644 doc/forum/Commiting_all_moderated_comments_into_special_branch__63__.mdwn create mode 100644 doc/forum/Commiting_all_moderated_comments_into_special_branch__63__/comment_1_8403e8ff9c5c8dddb6d744632322f7bc._comment create mode 100644 doc/forum/Darcs_as_the_RCS___63__.mdwn create mode 100644 doc/forum/Debian_5.0.7:_Can__39__t_locate_IkiWiki__47__Setup__47__Automator.pm.mdwn create mode 100644 doc/forum/Debian_5.0.7:_Can__39__t_locate_IkiWiki__47__Setup__47__Automator.pm/comment_1_aec4bf4ca7d04d580d2fa83fd3f7166f._comment create mode 100644 doc/forum/Debian_5.0.7:_Can__39__t_locate_IkiWiki__47__Setup__47__Automator.pm/comment_4_c682ebb0e8e72088a8f92356dc31ef37._comment create mode 100644 doc/forum/Debian_squeeze_still_on_3.20100815.7_-_update_recommended__63__.mdwn create mode 100644 doc/forum/Debian_squeeze_still_on_3.20100815.7_-_update_recommended__63__/comment_1_5e916c8fa90470909064ea73531f79d4._comment create mode 100644 doc/forum/Debian_squeeze_still_on_3.20100815.7_-_update_recommended__63__/comment_2_2fa15f0eaf8c860b82e366130c8563c7._comment create mode 100644 doc/forum/Debian_squeeze_still_on_3.20100815.7_-_update_recommended__63__/comment_3_c5af589dcdfe4f91dba50243762065e5._comment create mode 100644 doc/forum/Debian_squeeze_still_on_3.20100815.7_-_update_recommended__63__/comment_4_3090da7bafbf92a825edec8ffc45af20._comment create mode 100644 doc/forum/Define_custom_commands.mdwn create mode 100644 doc/forum/Define_custom_commands/comment_1_7d82637bc8c706b69e4a55585677f6bf._comment create mode 100644 doc/forum/Different_behaviour_when_editing_with_editor_than_with_web-edit.mdwn create mode 100644 doc/forum/Different_behaviour_when_editing_with_editor_than_with_web-edit/comment_1_ac6bda46ad00bfe980bc76c4a39aa796._comment create mode 100644 doc/forum/Different_behaviour_when_editing_with_editor_than_with_web-edit/comment_3_10a46f8ee23c8935e20c70842671cee4._comment create mode 100644 doc/forum/Different_templates_for_subdirectories__63_____40__Blogging_and_Wiki_pages__41__.mdwn create mode 100644 doc/forum/Different_templates_for_subdirectories__63_____40__Blogging_and_Wiki_pages__41__/comment_1_15651796492a6f04a19f4a481947c97c._comment create mode 100644 doc/forum/Disable_account_creation_for_new_users.mdwn create mode 100644 doc/forum/Disable_account_creation_for_new_users/comment_1_adafddb0aff7c2c1f4574101c4cf9073._comment create mode 100644 doc/forum/Disable_account_creation_for_new_users/comment_2_865591f77966f1657a9a4b2426318c51._comment create mode 100644 doc/forum/Disable_account_creation_for_new_users/comment_3_05193e563682f634f13691ee0a8359db._comment create mode 100644 doc/forum/Discussion_PageSpec__63__.mdwn create mode 100644 doc/forum/Doing_related_links_based_on_tags.mdwn create mode 100644 doc/forum/Dump_plugin.mdwn create mode 100644 doc/forum/Dump_plugin/comment_1_bfce80b3f5be78ec28692330843d4ae1._comment create mode 100644 doc/forum/Email_notifications_for_comment_moderation.mdwn create mode 100644 doc/forum/Email_notifications_for_comment_moderation/comment_1_668bf6a21310dcc8b882bc60a130ba06._comment create mode 100644 doc/forum/Empty_sha1sum_messages.mdwn create mode 100644 doc/forum/Empty_sha1sum_messages/comment_1_b260b5e6b4c4f4c203b01183fee9fd69._comment create mode 100644 doc/forum/Empty_sha1sum_messages/comment_2_d6a47838a3c81d0a75e6fc22e786c976._comment create mode 100644 doc/forum/Encoding_problem_in_french_with_ikiwiki-calendar.mdwn create mode 100644 doc/forum/Error:_CGI::tmpFileName_failed_to_return_the_uploaded_file_name.mdwn create mode 100644 doc/forum/Error:_CGI::tmpFileName_failed_to_return_the_uploaded_file_name/comment_1_66c321b9eb618d20872cee7d6ca9e44c._comment create mode 100644 doc/forum/Error:_CGI::tmpFileName_failed_to_return_the_uploaded_file_name/comment_2_80296d67c7f1dd75b56b85c14f5efa3b._comment create mode 100644 doc/forum/Error:___34__do__34___parameter_missing.mdwn create mode 100644 doc/forum/Error:___34__do__34___parameter_missing/comment_1_3a51c303ba1670f1567f323349b53837._comment create mode 100644 doc/forum/Error:___34__do__34___parameter_missing/comment_2_c5f24a8c4d2de0267cf0de1908480e82._comment create mode 100644 doc/forum/Error:_bad_page_name.mdwn create mode 100644 doc/forum/Error:_cannot_write_to___47__home__47__user__47__myiki2__47__.ikiwiki__47__lockfile:_Permission_denied_.mdwn create mode 100644 doc/forum/Error:_cannot_write_to___47__home__47__user__47__myiki2__47__.ikiwiki__47__lockfile:_Permission_denied_/comment_1_64146f306ec8c10614521359b6de4f82._comment create mode 100644 doc/forum/Error:_cannot_write_to___47__home__47__user__47__myiki2__47__.ikiwiki__47__lockfile:_Permission_denied_/comment_2_ed2b4b8f7122b42bbde1189fbd2969dd._comment create mode 100644 doc/forum/Error_Code_1.mdwn create mode 100644 doc/forum/Error_Code_1/comment_1_0459afcc383aad382df67a19eaf2e731._comment create mode 100644 doc/forum/Everyone_can_remove_comments.mdwn create mode 100644 doc/forum/Flowplayer.mdwn create mode 100644 doc/forum/Flowplayer/comment_1_75d13cd915a736422db47e00dbe46671._comment create mode 100644 doc/forum/Flowplayer/comment_2_1b2d3891006a87a4773bd126baacddfc._comment create mode 100644 doc/forum/Forward_slashes_being_escaped_as_252F.mdwn create mode 100644 doc/forum/Forward_slashes_being_escaped_as_252F/comment_1_7702cf6d354ab600d6643b075b9f09da._comment create mode 100644 doc/forum/Google_searches_of_ikiwiki.info_are_broken._:__40__.mdwn create mode 100644 doc/forum/Have_creation_time_for_new_pages_fetched_from_the_VCS.mdwn create mode 100644 doc/forum/Have_creation_time_for_new_pages_fetched_from_the_VCS/comment_1_9572dd6f7a2f6f630b12f24bb5c4a8ce._comment create mode 100644 doc/forum/Help_with_tag__95__autocreate.mdwn create mode 100644 doc/forum/Hide_text.mdwn create mode 100644 doc/forum/Hide_text/comment_1_f21d21c130f97a7b21d8a317178e2e0c._comment create mode 100644 doc/forum/Hide_text/comment_2_5a878865f34f78a89c4ec91a9425a085._comment create mode 100644 doc/forum/How_best_to_manage_a_bilingual_ikiwiki_site__63__.mdwn create mode 100644 doc/forum/How_best_to_manage_a_bilingual_ikiwiki_site__63__/comment_1_4389d65b14fa1b7134098e0ffe3bf055._comment create mode 100644 doc/forum/How_can_I_prevent_spam__63__.mdwn create mode 100644 doc/forum/How_can_I_prevent_spam__63__/comment_1_fd26fb7f1569e8c44ba8262794f938db._comment create mode 100644 doc/forum/How_can_I_prevent_spam__63__/comment_2_d098124f005976ee815d25c883bc9106._comment create mode 100644 doc/forum/How_can_I_prevent_spam__63__/comment_3_deb434d01aaefa18d2791e48d6c824ae._comment create mode 100644 doc/forum/How_do_I_enable_OpenID__63__.mdwn create mode 100644 doc/forum/How_does_ikiwiki_remember_times__63__.mdwn create mode 100644 doc/forum/How_is_TITLE_evaluated_in_inline_archive_templates__63__.mdwn create mode 100644 doc/forum/How_long_does_server_delay_newly_pushed_revisions__63__.mdwn create mode 100644 doc/forum/How_to_add_a_mouse-over_pop-up_label_for_a_text__63__.mdwn create mode 100644 doc/forum/How_to_add_additional_links_to_the_gray_horizontable_bar_under_page_title__63__.mdwn create mode 100644 doc/forum/How_to_add_additional_links_to_the_gray_horizontable_bar_under_page_title__63__/comment_1_f2e52d38f60888c7d5142de853123540._comment create mode 100644 doc/forum/How_to_add_link_to_previous_and_next_blog_on_blog_pages__63__.mdwn create mode 100644 doc/forum/How_to_add_link_to_previous_and_next_blog_on_blog_pages__63__/comment_1_aad510f45be505efaabcb6fb860665a4._comment create mode 100644 doc/forum/How_to_add_link_to_previous_and_next_blog_on_blog_pages__63__/comment_2_ee65792a5b796caa216f4e7a653fc668._comment create mode 100644 doc/forum/How_to_allow_.markdown_and_.md_at_the_same_time_as_valid_extensions_for_source_files__63__.mdwn create mode 100644 doc/forum/How_to_apply_a_background_color_to_a_page__63__.mdwn create mode 100644 doc/forum/How_to_change_registration_page.mdwn create mode 100644 doc/forum/How_to_change_registration_page/comment_1_43758a232e4360561bc84f710862ff40._comment create mode 100644 doc/forum/How_to_change_registration_page/comment_2_8176ef231cf901802fc60b6d414018e6._comment create mode 100644 doc/forum/How_to_configure_po_plugin__63__.mdwn create mode 100644 doc/forum/How_to_configure_po_plugin__63__/comment_1_5e0cc4cdfd126f2f4af64104f02102d6._comment create mode 100644 doc/forum/How_to_create_a_WikiLink_to_a_page_in_a_subdirectory__63__.mdwn create mode 100644 doc/forum/How_to_create_a_WikiLink_to_a_page_in_a_subdirectory__63__/comment_1_d20ee1d8d7a3e77a445f8b887e807119._comment create mode 100644 doc/forum/How_to_create_first_translation_page_using_po_plugin__63__.mdwn create mode 100644 doc/forum/How_to_customize_page_title__63__.mdwn create mode 100644 doc/forum/How_to_customize_page_title__63__/comment_1_403e1f866b5e04e5899021f54bbdd1ed._comment create mode 100644 doc/forum/How_to_fix___34__does_not_map_to_Unicode__34___errors__63__.mdwn create mode 100644 doc/forum/How_to_format___91____91__foobar__93____93___in_code_blocks__63__.mdwn create mode 100644 doc/forum/How_to_format___91____91__foobar__93____93___in_code_blocks__63__/comment_1_ad000d39fd1dc05aa8ef6eb19d8d999b._comment create mode 100644 doc/forum/How_to_generate_blog_archive_pages_in___47__blog_subdir_but_not_ikiwiki_root_path__63__.mdwn create mode 100644 doc/forum/How_to_inline_a_page_from_another_git_repository.mdwn create mode 100644 doc/forum/How_to_list_all_pages_in_a_wiki_instance__63__.mdwn create mode 100644 doc/forum/How_to_list_all_pages_in_a_wiki_instance__63__/comment_1_920bcc70fe6d081cf27aa2cc7c6136f4._comment create mode 100644 doc/forum/How_to_list_new_pages__44___inline__63__.mdwn create mode 100644 doc/forum/How_to_list_new_pages__44___inline__63__/comment_1_e989b18bade34a92a9c8fe7099036e15._comment create mode 100644 doc/forum/How_to_lock_all_pages_except_discussions_and_comments_to_blog_posts__63__.mdwn create mode 100644 doc/forum/How_to_lock_all_pages_except_discussions_and_comments_to_blog_posts__63__/comment_1_e153beb17b6ada69c6ab09d1f491d112._comment create mode 100644 doc/forum/How_to_make_a_table_of_content_at_the_top_of_page__63__.mdwn create mode 100644 doc/forum/How_to_make_a_table_of_content_at_the_top_of_page__63__/comment_1_6dedc31dd1145490bb5fa4ad14cc4c63._comment create mode 100644 doc/forum/How_to_migrate_multiple_pages_in_root_directory_into_a_new_subdirectory__63__.mdwn create mode 100644 doc/forum/How_to_migrate_multiple_pages_in_root_directory_into_a_new_subdirectory__63__/comment_1_a83a1a33afbf245971733b4128809365._comment create mode 100644 doc/forum/How_to_set_the_meta_author_field_from_user_name__63__.mdwn create mode 100644 doc/forum/How_to_set_the_meta_author_field_from_user_name__63__/comment_1_0906e1f3eb8b826a7730233b95cb5ddd._comment create mode 100644 doc/forum/How_to_set_up_a_page_as_internal__63__.mdwn create mode 100644 doc/forum/How_to_set_up_git_repository_hook___63__.mdwn create mode 100644 doc/forum/How_to_show_recent_changes_for_individual_pages__63__.mdwn create mode 100644 doc/forum/How_to_show_recent_changes_for_individual_pages__63__/comment_1_cd34affc6883f4e4bc5e7e7b711cc8ba._comment create mode 100644 doc/forum/How_to_specify_repository_is_on_a_remote_host__63__.mdwn create mode 100644 doc/forum/How_to_specify_repository_is_on_a_remote_host__63__/comment_1_0c71e17ae552cbab1056ac96fbd36c59._comment create mode 100644 doc/forum/How_to_specify_repository_is_on_a_remote_host__63__/comment_2_b309302a084fbd8bcd4cd9bd2509cf5a._comment create mode 100644 doc/forum/How_to_style_main_sidebar_and_SubPage_sidebar_differently_using_CSS__63__.mdwn create mode 100644 doc/forum/How_to_use___126____47__bin__47__multimarkdown_instead_of_Text::MultiMarkdown.mdwn create mode 100644 doc/forum/How_to_use_number_as_bullet_labels_but_not_letter_in_toc_plugin.mdwn create mode 100644 doc/forum/Howto_add_tag_from_plugin_code.mdwn create mode 100644 doc/forum/Howto_add_tag_from_plugin_code/comment_1_c61454825874a6fe1905cb549386deb0._comment create mode 100644 doc/forum/If_there__39__s_no_Windows_ikiwiki__44___how_about_a_WYSIWYG_ikiwiki_editor_for_Windows__63__.mdwn create mode 100644 doc/forum/If_there__39__s_no_Windows_ikiwiki__44___how_about_a_WYSIWYG_ikiwiki_editor_for_Windows__63__/comment_1_a66fd9d7ab4359784a5420cd899a1057._comment create mode 100644 doc/forum/If_there__39__s_no_Windows_ikiwiki__44___how_about_a_WYSIWYG_ikiwiki_editor_for_Windows__63__/comment_2_3351ff773fea3f640f4036bb8c7c7efd._comment create mode 100644 doc/forum/If_there__39__s_no_Windows_ikiwiki__44___how_about_a_WYSIWYG_ikiwiki_editor_for_Windows__63__/comment_3_273b2b63a9af2bc4eeb030e026436687._comment create mode 100644 doc/forum/If_there__39__s_no_Windows_ikiwiki__44___how_about_a_WYSIWYG_ikiwiki_editor_for_Windows__63__/comment_4_546771c13ea1b550301586e187d82cb5._comment create mode 100644 doc/forum/Ikiwiki_CGI_not_working_on_my_server__44___and_it__39__s_a_binary_file__63__.mdwn create mode 100644 doc/forum/Ikiwiki_themes_for_mobile_devices__63__.mdwn create mode 100644 doc/forum/Include_attachment_in_a_page.mdwn create mode 100644 doc/forum/Include_attachment_in_a_page/comment_1_275aad6ca3b2972749b7f6636b130035._comment create mode 100644 doc/forum/Is_it_possible_to_change_default_mdwn_suffix__63__.mdwn create mode 100644 doc/forum/Is_it_possible_to_change_default_mdwn_suffix__63__/comment_1_2a449c6017ecdb4f557963266fb4ec41._comment create mode 100644 doc/forum/Is_there_a_pagespec_for_creation_dates_relative_to_today__63__.mdwn create mode 100644 doc/forum/LaTeX_Error.mdwn create mode 100644 doc/forum/Last_visited_pages.mdwn create mode 100644 doc/forum/Last_visited_pages/comment_1_e34650064dd645b35da98e80c0311df9._comment create mode 100644 doc/forum/Last_visited_pages/comment_2_2a0c4e844da1deaa2c286e87c8eab84d._comment create mode 100644 doc/forum/Link_to_a_local_pdf_file.mdwn create mode 100644 doc/forum/Link_to_a_local_pdf_file/comment_1_b6c57588042373f8e1f187041c1a8530._comment create mode 100644 doc/forum/Log_in_error.mdwn create mode 100644 doc/forum/Log_in_error/comment_1_0ef13ea01a413160d81951636c15c3e6._comment create mode 100644 doc/forum/Map_Plugin__44___would_like_to_add___63__updated_to_all_links.mdwn create mode 100644 doc/forum/Map_Plugin__44___would_like_to_add___63__updated_to_all_links/comment_1_3fe4c5967e704355f9b594aed46baf67._comment create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated.mdwn create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated/comment_2_b44a492c7f10395a31f3c0830ef33f0c._comment create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated/comment_2_f9240b217b2d1ee8d51dada9cb1186b3._comment create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated/comment_3_c3c5c41a4c220793c6d16f3fd6132272._comment create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated/comment_4_1f6f9e3939a454c1eb8d2fb29bd519de._comment create mode 100644 doc/forum/Migrated_ikiwiki_to_new_host._Web_page_is_not_updated/comment_5_8611fc62797e70a0d2a61d94fcb03170._comment create mode 100644 doc/forum/Migrating_old_repository_to_new_ikiwiki_system__63__.mdwn create mode 100644 doc/forum/Migrating_old_repository_to_new_ikiwiki_system__63__/comment_1_e5ce524c5d34b1d4218172296bd99100._comment create mode 100644 doc/forum/Migrating_old_repository_to_new_ikiwiki_system__63__/comment_3_65c4a4895f6541ff0ff2d094ff447bba._comment create mode 100644 doc/forum/Moving_wiki.git_folder__63__.mdwn create mode 100644 doc/forum/Moving_wiki.git_folder__63__/comment_1_05238461520613f4ed1b0d02ece663bd._comment create mode 100644 doc/forum/Moving_wiki.git_folder__63__/comment_2_72b2b842dfa0cfaf899fe7af12977519._comment create mode 100644 doc/forum/Multiple_urls.mdwn create mode 100644 doc/forum/Multiple_urls/comment_1_e4c1256346d5a421161c20e344d8bada._comment create mode 100644 doc/forum/Need_help_installing_h1title_plugin.mdwn create mode 100644 doc/forum/Need_help_setting_up_ikiwiki_CGI.mdwn create mode 100644 doc/forum/Need_help_setting_up_ikiwiki_CGI/comment_1_0fc4573568711c56a0df4af620110c2f._comment create mode 100644 doc/forum/Need_help_setting_up_ikiwiki_CGI/comment_3_89f2cd7d874a6257786478e4cae1e2bc._comment create mode 100644 doc/forum/Need_help_setting_up_ikiwiki_CGI/comment_3_cbc20267fe5f0531f63db881d50596d1._comment create mode 100644 doc/forum/Need_help_setting_up_ikiwiki_CGI/comment_4_2eaf53935eecd0a918755d728450a642._comment create mode 100644 doc/forum/Need_some_help_on_starting_to_use_po_plugin_for_creating_pages_in_multiple_languages.mdwn create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude.mdwn create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_2_0019cd6b34c8d8678b2532de57a92d15._comment create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_2_f577ab6beb9912471949d8d18c790267._comment create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_3_1ed260b0083a290688425a006a83f603._comment create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_4_c39bdaf38e1e20db74eb26f0560bd673._comment create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_5_39b01857f7e0b388a6e7a3c1cf5388d5._comment create mode 100644 doc/forum/Need_something_more_powerful_than_Exclude/comment_6_1dccdfebad31446200213a2cae25f0e2._comment create mode 100644 doc/forum/News_site_where_articles_are_submitted_and_then_reviewed_before_posting.mdwn create mode 100644 doc/forum/OpenID_not_working___47___where_to_define_wiki__39__s_ID__63__.mdwn create mode 100644 doc/forum/OpenID_not_working___47___where_to_define_wiki__39__s_ID__63__/comment_1_bf1bec748d6ab419276a73a7001024cf._comment create mode 100644 doc/forum/OpenID_not_working___47___where_to_define_wiki__39__s_ID__63__/comment_2_14a1b269be6dbcc9b2068d3e18b55711._comment create mode 100644 doc/forum/OpenID_not_working___47___where_to_define_wiki__39__s_ID__63__/comment_3_f581afcdb4481ea5d65bcc33bdbab99a._comment create mode 100644 doc/forum/OpenID_not_working___47___where_to_define_wiki__39__s_ID__63__/comment_4_b0d39d30852bca1525ab9612a7532670._comment create mode 100644 doc/forum/PERL5LIB__44___wrappers_and_homedir_install.mdwn create mode 100644 doc/forum/PageSpec_results_from_independent_checkout.mdwn create mode 100644 doc/forum/Parent_Links_all_link_to_root.mdwn create mode 100644 doc/forum/Parent_Links_all_link_to_root/comment_1_4b5ed25cceb7740f64ee08aba00a1d91._comment create mode 100644 doc/forum/Perhaps_I__39__m_doing_it_wrong_-_tracking_non-post_files_in_a_blog.mdwn create mode 100644 doc/forum/Perhaps_I__39__m_doing_it_wrong_-_tracking_non-post_files_in_a_blog/comment_1_45ecaf6efa2065837fa54a42737f0a66._comment create mode 100644 doc/forum/Perhaps_I__39__m_doing_it_wrong_-_tracking_non-post_files_in_a_blog/comment_2_45ca7ef4190c281d703c8c7ca6979298._comment create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__.mdwn create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__/comment_1_556078a24041289d8f0b7ee756664690._comment create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__/comment_2_e7e954218d39bc310015b95aa1a5212c._comment create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__/comment_3_8b16c563c89eb6980ad6a5539d934d7a._comment create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__/comment_4_76eadf93cce4e2168960131d4677c5fc._comment create mode 100644 doc/forum/Possible_to_use_meta_variables_in_templates__63__/comment_5_ddabe4a005042d19c7669038b49275c1._comment create mode 100644 doc/forum/Problem_with_gitweb.mdwn create mode 100644 doc/forum/Problem_with_gitweb/comment_2_23cc0d87448d3cbdac20a005e9191589._comment create mode 100644 doc/forum/Problem_with_gitweb/comment_3_697c6038009249e6a49d9e458a5ba271._comment create mode 100644 doc/forum/Problem_with_gitweb/comment_3_6a5b96f7e0d6b169c090e3df7281d938._comment create mode 100644 doc/forum/Problem_with_gitweb/comment_5_8a79b879205bd265d54e30f0eee2ac63._comment create mode 100644 doc/forum/Problem_with_local_git_commit.mdwn create mode 100644 doc/forum/Processing_non-pages.mdwn create mode 100644 doc/forum/Recent_changes_on_main_site_or_on_a_sidebar.mdwn create mode 100644 doc/forum/Recent_changes_on_main_site_or_on_a_sidebar/comment_1_018b977ff7ee59fc53838e0c20c3a9a7._comment create mode 100644 doc/forum/Recent_changes_on_main_site_or_on_a_sidebar/comment_2_927c11f18315baa39f08ca4982ed2ab1._comment create mode 100644 doc/forum/Refresh_or_recreate_style.css__63__.mdwn create mode 100644 doc/forum/Refresh_or_recreate_style.css__63__/comment_1_3274be931d0b543c7f7cf641810817aa._comment create mode 100644 doc/forum/Regex_for_Valid_Characters_in_Filenames.mdwn create mode 100644 doc/forum/Render_more_than_one_dest_page_from_same_source_page.mdwn create mode 100644 doc/forum/Revision_history_for_single_pages.mdwn create mode 100644 doc/forum/Revision_history_for_single_pages/comment_1_d509d5d726cd7eab9472d723013f5ec4._comment create mode 100644 doc/forum/Revision_history_for_single_pages/comment_2_d39a6177fc4c1e3c3c2c4e2592be9e3d._comment create mode 100644 doc/forum/Revision_history_for_single_pages/comment_3_aecf2b031ace001afaa2a0f2b5f50c82._comment create mode 100644 doc/forum/Run_script_on_markdown_source.mdwn create mode 100644 doc/forum/See_rendered_old_revisions_via_pagehistory.mdwn create mode 100644 doc/forum/Setting_http__95__proxy.mdwn create mode 100644 doc/forum/Setting_http__95__proxy/comment_1_350a7c4834c9f422e107b646cdbae3b0._comment create mode 100644 doc/forum/Setting_template_variable_from_config_file__63__.mdwn create mode 100644 doc/forum/Setting_template_variable_from_config_file__63__/comment_1_bb4b5a7a49f33d660b5116fc0ce3c92d._comment create mode 100644 doc/forum/Setting_up_a_development_environment.mdwn create mode 100644 doc/forum/Should_files_in_.ikiwiki_be_committed_and_pushed__63__.mdwn create mode 100644 doc/forum/Should_files_in_.ikiwiki_be_committed_and_pushed__63__/comment_1_8e65d7d8298e3c31d2a16446a71c8049._comment create mode 100644 doc/forum/Should_not_create_an_existing_page.mdwn create mode 100644 doc/forum/Sidebar_with_links__63__.mdwn create mode 100644 doc/forum/Slow_ikiwiki_after_first_run.mdwn create mode 100644 doc/forum/Spaces_in_wikilinks.mdwn create mode 100644 doc/forum/Split_a_wiki.mdwn create mode 100644 doc/forum/Split_a_wiki/comment_1_1599c26891b2071a2f1ca3fd90627fc4._comment create mode 100644 doc/forum/Split_a_wiki/comment_2_1c54d3594f0350340f8dfb3e95c29ffd._comment create mode 100644 doc/forum/Split_a_wiki/comment_3_9eac1d1b93df27d849acc574b1f0f26d._comment create mode 100644 doc/forum/Split_a_wiki/comment_4_e193ba447c0188f72ba589180b5d529e._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN.mdwn create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_1_3172568473e9b79ad7ab623afd19411a._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_2_4302d56a6fe68d17cc42d26e6f3566c2._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_3_4cc44e61b9c28a2d524fa874f115041a._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_4_33143bad68f3f6beae963a3d0ec5d0bd._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_5_ef790766456d723670f52cc9e3955e90._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_6_3db50264e01c8fad2e5567b5a9c7b6dc._comment create mode 100644 doc/forum/TMPL__95__VAR_IS__95__ADMIN/comment_7_bdc5c96022fdb8826b57d68a41ef6ca0._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server..mdwn create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_1_d36ce6fab90e0a086ac84369af38d205._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_2_5836bba08172d2ddf6a43da87ebb0332._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_3_4eec15c8c383275db5401c8e3c2d9242._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_4_43ac867621efb68affa6ae2b92740cad._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_5_e098723bb12adfb91ab561cae21b492b._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_6_101183817ca4394890bd56a7694bedd9._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_7_2f514e6ba78d43d90e7ff4ae387e65e0._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_8_098bb7a3112751a7e6167483dde626bb._comment create mode 100644 doc/forum/The_requested_URL___47____126__jean__47__blog__47__ikiwiki.cgi_was_not_found_on_this_server./comment_9_fbf403255c38da93caa5b98589fbb285._comment create mode 100644 doc/forum/Translating_ikiwiki_interface.mdwn create mode 100644 doc/forum/Upgrade_steps_from_RecentChanges_CGI_to_static_page.mdwn create mode 100644 doc/forum/Various_ways_to_use_Subversion_with_ikiwiki.mdwn create mode 100644 doc/forum/What__39__s_the_difference_between_tag_and_taglink__63__.mdwn create mode 100644 doc/forum/What__39__s_the_difference_between_tag_and_taglink__63__/comment_1_b3553d65d12af4c4a87f1f66f961c8d9._comment create mode 100644 doc/forum/What_is_wrong_with_my_recentchange_page___63__.mdwn create mode 100644 doc/forum/When_do_tags_like_a__47__b_get_listed_as_a__47__b_and_not_only_b__63__.mdwn create mode 100644 doc/forum/When_do_tags_like_a__47__b_get_listed_as_a__47__b_and_not_only_b__63__/comment_1_cd5ea3aac8a59793ece5bf01a6190b53._comment create mode 100644 doc/forum/Wikilink_to_a_symbolic_link.mdwn create mode 100644 doc/forum/Wikilink_to_a_symbolic_link/comment_1_e3ad5099491e0c84cd7729eba82ce552._comment create mode 100644 doc/forum/Wikilink_to_a_symbolic_link/comment_2_46848020b1e3d0cd55bc1ec0ba382aad._comment create mode 100644 doc/forum/Wikilink_to_section_of_a_wikipage.mdwn create mode 100644 doc/forum/Wikilink_to_section_of_a_wikipage/comment_1_c1409a3c07dfc4ed7274560c962aba75._comment create mode 100644 doc/forum/Wikilink_to_section_of_a_wikipage/comment_2_8a04eb7b0d7f17b9e5bb4cd04ba45871._comment create mode 100644 doc/forum/Xapian_search:_empty_postlist_table.mdwn create mode 100644 doc/forum/Xapian_search:_empty_postlist_table/comment_1_de9a7c94beec2707eda0924ca58be9df._comment create mode 100644 doc/forum/Xapian_search:_empty_postlist_table/comment_2_55f191e4b1306a318a30319f01802229._comment create mode 100644 doc/forum/Xapian_search:_empty_postlist_table/comment_3_0bd424a89c3a52ff393a1e7e00c806be._comment create mode 100644 doc/forum/Xapian_search:_empty_postlist_table/comment_4_40479ac2cfbca609f5f423e539a20ee0._comment create mode 100644 doc/forum/Xapian_search:_empty_postlist_table/comment_5_397443138da276e11c2e9b9fa7b51406._comment create mode 100644 doc/forum/__42__.html_source_file_containing___60__script__62__...__60____47__script__62___not_working__63__.mdwn create mode 100644 doc/forum/__42__.html_source_file_containing___60__script__62__...__60____47__script__62___not_working__63__/comment_1_953bd716373dcf51fa444ac098b7f971._comment create mode 100644 doc/forum/__42__.html_source_file_containing___60__script__62__...__60____47__script__62___not_working__63__/comment_2_c7360852f9bf069f28c193373333c9a8._comment create mode 100644 doc/forum/__42__.html_source_file_containing___60__script__62__...__60____47__script__62___not_working__63__/comment_3_6ffc30e27387366b48112198b66c01fa._comment create mode 100644 doc/forum/access_restrictions:_for_extranet.mdwn create mode 100644 doc/forum/access_restrictions:_for_extranet/comment_1_a0666c3c15661fb0fff70f313cd0d47d._comment create mode 100644 doc/forum/access_restrictions:_for_extranet/comment_2_563040aa099c9366dc5701eb4bc9c10d._comment create mode 100644 doc/forum/an_alternative_approach_to_structured_data.mdwn create mode 100644 doc/forum/appear_if_you_are_login_or_not_in_a_page.mdwn create mode 100644 doc/forum/attachments_fail_to_upload.mdwn create mode 100644 doc/forum/attachments_fail_to_upload/comment_1_577adde1dfa49463dfa8e169c462fc42._comment create mode 100644 doc/forum/attachments_fail_to_upload/comment_2_473f38c6d523496fac8dad13ac6d20c3._comment create mode 100644 doc/forum/attachments_fail_to_upload/comment_3_799a2f1b7b259157e97fd31ec76fb845._comment create mode 100644 doc/forum/attachments_fail_to_upload/comment_4_e37d1497acafd3fda547462f000636e3._comment create mode 100644 doc/forum/attachments_fail_to_upload/comment_5_da03f9c4917cb1ef52de984b8ba86b68._comment create mode 100644 doc/forum/attachments_fail_to_upload/comment_6_04498946a300ddb652dec73c2950f48f._comment create mode 100644 doc/forum/bashman.mdwn create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters.mdwn create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters/comment_1_83fbb415dd3ae6a19ed5ea5f82065c28._comment create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters/comment_2_d258536c98538d4744f66eb3132439a9._comment create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters/comment_3_d62173d0ae220ab7b063631952856587._comment create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters/comment_4_d5d0174e09a94359c23fd9c006a22bbc._comment create mode 100644 doc/forum/build_error:_Cannot_decode_string_with_wide_characters/comment_5_e652027a8f90ebef6f21613b5784ded2._comment create mode 100644 doc/forum/chinese_character_problem.mdwn create mode 100644 doc/forum/cleaning_up_discussion_pages_and_the_like.mdwn create mode 100644 doc/forum/copyright_and_license_template_variables___40__where_are_they_set__63____41__.mdwn create mode 100644 doc/forum/create_download_link.mdwn create mode 100644 doc/forum/create_download_link/comment_1_4797493157c569f8893b53b5e5a58e73._comment create mode 100644 doc/forum/cutpaste.pm_not_only_file-local.mdwn create mode 100644 doc/forum/cutpaste.pm_not_only_file-local/comment_1_497c62f21fd1b87625b806407c72dbad._comment create mode 100644 doc/forum/debian_backports_update_someone_please.mdwn create mode 100644 doc/forum/discussion.mdwn create mode 100644 doc/forum/double_forward_slash___39____47____47____39___in_the_address_bar.mdwn create mode 100644 doc/forum/editing_a_comment.mdwn create mode 100644 doc/forum/editing_the_style_sheet.mdwn create mode 100644 doc/forum/error_302___40__Found__41___when_editing_page.mdwn create mode 100644 doc/forum/ever-growing_list_of_pages.mdwn create mode 100644 doc/forum/field__95__tags_not_linking.mdwn create mode 100644 doc/forum/field__95__tags_not_linking/comment_10_7c1540e6eb6aafd2e1c9c7016e6e6249._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_11_0c03cbaa4f748d2fb932fda08fe6e966._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_12_9f3a402173f9584d8a36bc61e5755f6d._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_13_455a2f921059f9ecca810bb8afed0fda._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_14_b82294c290a215d9aa6774ee20b5a552._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_15_57fb279ad50f8460341dc0f217acef06._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_16_8dae1024e80cf6ea765dee0318324d71._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_1_76a4fb4def8f13b906c848814de91660._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_2_64d51cc9ba953e7fed609c380e30bb7d._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_3_7a6eac4e216133f1cf6fc12336fc2496._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_4_e6941a0df00fb9f45563c30e01efa622._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_5_f08ded5a946458aeba59a2c4cec29b2f._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_6_6ea7de20c3db96589c05adbe97d57cfd._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_7_8ad385b61c46389d87c88b17430ab1f2._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_8_c3c5eced158babd8c3acb493a86b6ecb._comment create mode 100644 doc/forum/field__95__tags_not_linking/comment_9_9bd4b3df18a28a7ab3bbef5013856987._comment create mode 100644 doc/forum/field_and_forms.mdwn create mode 100644 doc/forum/field_and_forms/comment_1_a0e976cb79f03dcff5e9a4511b90d160._comment create mode 100644 doc/forum/formating:_how_to_align_text_to_the_right.mdwn create mode 100644 doc/forum/getsource_from_gitweb_with_cgi_disabled___44___Is_it_possible__63__.mdwn create mode 100644 doc/forum/getsource_from_gitweb_with_cgi_disabled___44___Is_it_possible__63__/comment_1_747cc477584028ce2c7bc198070b1221._comment create mode 100644 doc/forum/getsource_from_gitweb_with_cgi_disabled___44___Is_it_possible__63__/comment_2_a230861b26dba6d61461862bfedbc09c._comment create mode 100644 doc/forum/getsource_from_gitweb_with_cgi_disabled___44___Is_it_possible__63__/comment_3_848b4801fc7887906a21a676e802023c._comment create mode 100644 doc/forum/google_openid_broken__63__.mdwn create mode 100644 doc/forum/how_can_I_use___39____47____39___as_tagbase__63__.mdwn create mode 100644 doc/forum/how_can_I_use___39____47____39___as_tagbase__63__/comment_1_e7897651ba8d9156526d36d6b7744eae._comment create mode 100644 doc/forum/how_could_i_generate_a_flat_textfile_from_metadata_in_multiple_pages.mdwn create mode 100644 doc/forum/how_do_I_revert_edits_in_the_web_mode__63__.mdwn create mode 100644 doc/forum/how_do_I_revert_edits_in_the_web_mode__63__/comment_1_e4720e8e4fe74bd6cba746e8259832e6._comment create mode 100644 doc/forum/how_do_I_translate_a_TWiki_site.mdwn create mode 100644 doc/forum/how_to_add_post_titles_in_ikiwiki_blog__63__.mdwn create mode 100644 doc/forum/how_to_enable_multimarkdown__63__.mdwn create mode 100644 doc/forum/how_to_enable_multimarkdown__63__/comment_1_037f858c4d0bcbb708c3efd264379500._comment create mode 100644 doc/forum/how_to_enable_multimarkdown__63__/comment_2_b7d512a535490dabf8d6ce55439741c7._comment create mode 100644 doc/forum/how_to_get_nice_pdf_from_ikiwiki_pages__63__.mdwn create mode 100644 doc/forum/how_to_get_nice_pdf_from_ikiwiki_pages__63__/comment_1_332d32850c3dc0d45f5cc50434205f39._comment create mode 100644 doc/forum/how_to_load_an_external_page_and_still_have_it_under_ikiwiki_template.mdwn create mode 100644 doc/forum/how_to_login_as_admin.mdwn create mode 100644 doc/forum/how_to_login_as_admin/comment_1_295e130c6400a2d7336758e82bcd5647._comment create mode 100644 doc/forum/how_to_setup_ikiwiki_on_a_remote_host.mdwn create mode 100644 doc/forum/howto_install_the_pagedown_plugin.mdwn create mode 100644 doc/forum/howto_install_the_pagedown_plugin/comment_1_158fbcef24d20920c40968da8f10442a._comment create mode 100644 doc/forum/html_source_pages_in_version_3.20100704.mdwn create mode 100644 doc/forum/ikiwiki_+_mathjax.mdwn create mode 100644 doc/forum/ikiwiki_+_mathjax/comment_1_8426a985ecfbb02d364116503ef3a0d4._comment create mode 100644 doc/forum/ikiwiki_+_mathjax/comment_2_ddb7a4d59bbe7145167d122a146e8f65._comment create mode 100644 doc/forum/ikiwiki_+_mathjax/comment_3_5a118654bc008bbb118285ff141eb6f1._comment create mode 100644 doc/forum/ikiwiki_--setup_creates_tmp__47___directory_in_destdir.mdwn create mode 100644 doc/forum/ikiwiki__39__s_notion_of_time.mdwn create mode 100644 doc/forum/ikiwiki_and_big_files.mdwn create mode 100644 doc/forum/ikiwiki_and_big_files/comment_1_df8a9f4249af435cc335f77768a3278d._comment create mode 100644 doc/forum/ikiwiki_and_big_files/comment_2_2d996f1124aedc10f345139c3d8b11df._comment create mode 100644 doc/forum/ikiwiki_and_big_files/comment_3_dfbd38e2b457ea3c4f70266dbf8fbeab._comment create mode 100644 doc/forum/ikiwiki_development_environment_tips.mdwn create mode 100644 doc/forum/ikiwiki_generates_html_files_with_600_permission..mdwn create mode 100644 doc/forum/ikiwiki_generates_html_files_with_600_permission./comment_1_6d73d412a9cc6f6ae426b62885c1f157._comment create mode 100644 doc/forum/ikiwiki_generates_html_files_with_600_permission./comment_2_1392fcde369d11a264f31f6b8993ccec._comment create mode 100644 doc/forum/ikiwiki_generates_html_files_with_600_permission./comment_3_962306f22ceb17afb4150e766e9a05b3._comment create mode 100644 doc/forum/ikiwiki_generates_html_files_with_600_permission./comment_4_8b988d85cfde123798238d0348764c79._comment create mode 100644 doc/forum/ikiwiki_over_database__63__.wiki create mode 100644 doc/forum/ikiwiki_vim_integration.mdwn create mode 100644 doc/forum/ikiwiki_vim_syntaxfile.mdwn create mode 100644 doc/forum/index_attachments.mdwn create mode 100644 doc/forum/index_attachments/comment_1_18b9531d273292b45051eef6a306ca26._comment create mode 100644 doc/forum/index_attachments/comment_2._comment create mode 100644 doc/forum/index_attachments/comment_3_050e5847641a27e0c14232632f3e700a._comment create mode 100644 doc/forum/index_attachments/comment_4._comment create mode 100644 doc/forum/installation_and_setup_questions.mdwn create mode 100644 doc/forum/installation_as_non-root_user.mdwn create mode 100644 doc/forum/installation_of_selected_docs.mdwn create mode 100644 doc/forum/is_it_possible_to_NOT_add_openid2_meta_tags.mdwn create mode 100644 doc/forum/java_script_slideshow.mdwn create mode 100644 doc/forum/java_script_slideshow/comment_1_3eba0b2f3c12acc991dc3069d2b83d49._comment create mode 100644 doc/forum/java_script_slideshow/comment_2_59d90f42b2ca2a5cc71a4d9ba9b9ee9f._comment create mode 100644 doc/forum/java_script_slideshow/comment_3_820a86db38231cff7239f0a88b1925fd._comment create mode 100644 doc/forum/java_script_slideshow/comment_4_a68972e3dd20b65119211d4ab120b294._comment create mode 100644 doc/forum/link_autocompletion_in_vim.mdwn create mode 100644 doc/forum/link_to_an_image_inside_the_wiki_without_inlining_it.mdwn create mode 100644 doc/forum/links_to_diff_on_recentchanges__63__.mdwn create mode 100644 doc/forum/links_to_diff_on_recentchanges__63__/comment_1_1dbc723cc2794f6d45de9cbd2fc2e0fd._comment create mode 100644 doc/forum/links_to_diff_on_recentchanges__63__/comment_2_4349c85d92cf9c1acf2e7678371ab12a._comment create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked.mdwn create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked/comment_1_bacffb831e5ce7ece7e670c55ad9f3af._comment create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked/comment_2_ad268d3f2cd3d529cfff281e0ecb2f16._comment create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked/comment_3_da2fb41c5313763e4393cdd921a3f36e._comment create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked/comment_4_d0de7964db26cb6f3e81d6e8c29d860d._comment create mode 100644 doc/forum/lockedit:_pages_don__39__t_get_locked/comment_5_d60727c53197d1c667b59bc7250afd9f._comment create mode 100644 doc/forum/managing_todo_lists.mdwn create mode 100644 doc/forum/missing_pages_redirected_to_search-SOLVED.mdwn create mode 100644 doc/forum/missing_pages_redirected_to_search-SOLVED/comment_1_aa03c337b31d7acb95761eb51caab1ef._comment create mode 100644 doc/forum/move_pages.mdwn create mode 100644 doc/forum/move_pages/comment_1_3f1b9563af1e729a7311e869cf7a7787._comment create mode 100644 doc/forum/move_pages/comment_2_22b1c238faacbf10df5f03f415223b49._comment create mode 100644 doc/forum/multi-user_setup_of_ikiwiki__44___gitosis_and_apache2_in_Debian_Sid.mdwn create mode 100644 doc/forum/multi_domain_setup_possible__63__.mdwn create mode 100644 doc/forum/multi_domain_setup_possible__63__/comment_1_43f5df30d09046ccc4f7c44703979a11._comment create mode 100644 doc/forum/multi_domain_setup_possible__63__/comment_2_75d6581f81b71fb8acbe3561047ea759._comment create mode 100644 doc/forum/navigation_of_wiki_pages_on_local_filesystem_with_vim.mdwn create mode 100644 doc/forum/nginx:_404_plugin_not_working.mdwn create mode 100644 doc/forum/nginx:_404_plugin_not_working/comment_1_02a82e468676ae64374cc91ec87e39d6._comment create mode 100644 doc/forum/nginx:_404_plugin_not_working/comment_2_ce6bd8e98e4be08316522182f5f85a11._comment create mode 100644 doc/forum/nginx:_404_plugin_not_working/comment_3_52b05c3274455db7bee3c1765776fd52._comment create mode 100644 doc/forum/nginx:_404_plugin_not_working/comment_4_5a8c2987f442106c68eb822c5bce3bf1._comment create mode 100644 doc/forum/nginx:_404_plugin_not_working/comment_5_0720cd8842dc1cb338b74a0e6fdb2aac._comment create mode 100644 doc/forum/pandoc-iki_plugin.mdwn create mode 100644 doc/forum/pandoc-iki_plugin/comment_1_11eef903493378fd704a6bd92e968508._comment create mode 100644 doc/forum/pandoc-iki_plugin/comment_2_2c437577390cffe3401f5cc2f08a2ab1._comment create mode 100644 doc/forum/paths_to_files_outside_the_wiki_root.mdwn create mode 100644 doc/forum/perl5lib_and_wrappers.mdwn create mode 100644 doc/forum/po_plugin_doesn__39__t_create_po_files___40__only_pot__41__..mdwn create mode 100644 doc/forum/possible_utf-8_problem__63__.mdwn create mode 100644 doc/forum/postsignin_redirect_not_working.mdwn create mode 100644 doc/forum/problem_with_git_after_a_commit_of_ikiwiki.mdwn create mode 100644 doc/forum/problem_with_git_after_a_commit_of_ikiwiki/comment_1_2b9986717769419a8ae0f730c36b7e65._comment create mode 100644 doc/forum/recentchanges_dir_should_be_under_control_of_RCS__63__.mdwn create mode 100644 doc/forum/recovering_original_title_with_meta_directive.mdwn create mode 100644 doc/forum/remove_css__63__.mdwn create mode 100644 doc/forum/report_pagination.mdwn create mode 100644 doc/forum/screenplay_plugin.mdwn create mode 100644 doc/forum/screenplay_plugin/comment_1_6c353acfc80b972ee3a34c8bb09dede3._comment create mode 100644 doc/forum/screenplay_plugin/comment_2_1868aeebebefae80531f2031ffba35d3._comment create mode 100644 doc/forum/section_editing.mdwn create mode 100644 doc/forum/section_editing/comment_1_b193caa886a47c685ac7dafaf60c1761._comment create mode 100644 doc/forum/speeding_up_ikiwiki.mdwn create mode 100644 doc/forum/square_brackets_inside_backticks_generates_incorrect_html___40__interpreted_as_wikilinks__41____63__.html create mode 100644 doc/forum/suppressing_output_of_pages_included_only_for_their_side_effects.mdwn create mode 100644 doc/forum/tag_plugin:_rebuilding_autocreated_pages.mdwn create mode 100644 doc/forum/tags_acting_strangely:_not_picking_up_all_pages__44___not_populating_feeds.mdwn create mode 100644 doc/forum/tags_acting_strangely:_not_picking_up_all_pages__44___not_populating_feeds/comment_1_ec4ffab10e60510b53660b70908d1bd8._comment create mode 100644 doc/forum/tags_acting_strangely:_not_picking_up_all_pages__44___not_populating_feeds/comment_2_a47884ffd749df980cd62f4c1e3167ce._comment create mode 100644 doc/forum/tags_acting_strangely:_not_picking_up_all_pages__44___not_populating_feeds/comment_3_6c4affdbc637946506d0c28a8648dc6e._comment create mode 100644 doc/forum/teximg_not_working.mdwn create mode 100644 doc/forum/teximg_not_working/comment_2_35e2ebf3893fc0c7966490e1fef1e6cf._comment create mode 100644 doc/forum/transition_from_handwritten_html_to_ikiwiki.mdwn create mode 100644 doc/forum/two_new_contrib_plugins:_newpage__44___jssearchfield.mdwn create mode 100644 doc/forum/understanding_filter_hooks.mdwn create mode 100644 doc/forum/upgrade_steps.mdwn create mode 100644 doc/forum/use_php-markdown-extra_with_ikiwiki__63__.mdwn create mode 100644 doc/forum/use_php-markdown-extra_with_ikiwiki__63__/comment_1_66d48218361caa4c07bd714b82ed0021._comment create mode 100644 doc/forum/use_php-markdown-extra_with_ikiwiki__63__/comment_2_f2ee0a4dce571d329f795e52139084c0._comment create mode 100644 doc/forum/use_php-markdown-extra_with_ikiwiki__63__/comment_3_e388714f457ccb6ef73630179914558c._comment create mode 100644 doc/forum/usedirs___38___indexpages_using_problem.mdwn create mode 100644 doc/forum/users/acodispo.mdwn create mode 100644 doc/forum/using_l10n__39__d_basewiki.mdwn create mode 100644 doc/forum/using_l10n__39__d_basewiki/comment_1_eaab671848ee6129f6fe9399474eeac0._comment create mode 100644 doc/forum/using_l10n__39__d_basewiki/comment_2_d907676a1db1210ca59506673c564359._comment create mode 100644 doc/forum/using_l10n__39__d_basewiki/comment_3_5e9d5bc5ecaf63f9bfe3315b09a279aa._comment create mode 100644 doc/forum/using_svn+ssh_with_ikiwiki.mdwn create mode 100644 doc/forum/utf8_warnings_for___34____92__xAB__34__.mdwn create mode 100644 doc/forum/w3mmode___91__Save_Page__93___results_in_403.mdwn create mode 100644 doc/forum/web_service_API__44___fastcgi_support.mdwn create mode 100644 doc/forum/what_is_the_easiest_way_to_implement_order:_disallow_all__44___allow_chosen__95__few_page_editing_policy__63__.mdwn create mode 100644 doc/forum/where_are_the_tags.mdwn create mode 100644 doc/forum/where_are_the_tags/comment_1_6a559c3bfe72011c45b006d33176da3d._comment create mode 100644 doc/forum/which_file_ikiwiki_--setup_is_processing_right_now__63__.mdwn create mode 100644 doc/forum/which_file_ikiwiki_--setup_is_processing_right_now__63__/comment_1_4f52f8fc083982bd5a572742cf35c74f._comment create mode 100644 doc/forum/wiki_clones_on_dynamic_IPs.mdwn create mode 100644 doc/forum/wiki_name_in_page_titles.mdwn create mode 100644 doc/forum/wishlist-discussion:_Editformular_showing_existing_tags.mdwn create mode 100644 doc/forum/wishlist:_Allow_OpenID_users_to_set_a_display_name.mdwn create mode 100644 doc/forum/wishlist:_support_staging_area.mdwn create mode 100644 doc/forum/wmd_editor_double_preview.mdwn create mode 100644 doc/forum/wmd_editor_double_preview/comment_1_0d3acf67f3c35f8c4156228f96dcd975._comment create mode 100644 doc/freesoftware.mdwn create mode 100644 doc/freesoftware/discussion.mdwn create mode 100644 doc/git.mdwn create mode 100644 doc/ikiwiki-calendar.mdwn create mode 100644 doc/ikiwiki-calendar/discussion.mdwn create mode 100644 doc/ikiwiki-makerepo.mdwn create mode 100644 doc/ikiwiki-makerepo/discussion.mdwn create mode 100644 doc/ikiwiki-mass-rebuild.mdwn create mode 100644 doc/ikiwiki-mass-rebuild/discussion.mdwn create mode 100644 doc/ikiwiki-transition.mdwn create mode 100644 doc/ikiwiki-update-wikilist.mdwn create mode 100644 doc/ikiwiki.mdwn create mode 100644 doc/ikiwiki/directive.mdwn create mode 100644 doc/ikiwiki/directive/aggregate.mdwn create mode 100644 doc/ikiwiki/directive/aggregate/discussion.mdwn create mode 100644 doc/ikiwiki/directive/brokenlinks.mdwn create mode 100644 doc/ikiwiki/directive/brokenlinks/discussion.mdwn create mode 100644 doc/ikiwiki/directive/calendar.mdwn create mode 100644 doc/ikiwiki/directive/color.mdwn create mode 100644 doc/ikiwiki/directive/comment.mdwn create mode 100644 doc/ikiwiki/directive/commentmoderation.mdwn create mode 100644 doc/ikiwiki/directive/copy.mdwn create mode 100644 doc/ikiwiki/directive/cut.mdwn create mode 100644 doc/ikiwiki/directive/cutpaste.mdwn create mode 100644 doc/ikiwiki/directive/date.mdwn create mode 100644 doc/ikiwiki/directive/edittemplate.mdwn create mode 100644 doc/ikiwiki/directive/flattr.mdwn create mode 100644 doc/ikiwiki/directive/format.mdwn create mode 100644 doc/ikiwiki/directive/fortune.mdwn create mode 100644 doc/ikiwiki/directive/graph.mdwn create mode 100644 doc/ikiwiki/directive/graph/discussion.mdwn create mode 100644 doc/ikiwiki/directive/haiku.mdwn create mode 100644 doc/ikiwiki/directive/if.mdwn create mode 100644 doc/ikiwiki/directive/img.mdwn create mode 100644 doc/ikiwiki/directive/img/discussion.mdwn create mode 100644 doc/ikiwiki/directive/inline.mdwn create mode 100644 doc/ikiwiki/directive/inline/discussion.mdwn create mode 100644 doc/ikiwiki/directive/linkmap.mdwn create mode 100644 doc/ikiwiki/directive/listdirectives.mdwn create mode 100644 doc/ikiwiki/directive/map.mdwn create mode 100644 doc/ikiwiki/directive/map/discussion.mdwn create mode 100644 doc/ikiwiki/directive/meta.mdwn create mode 100644 doc/ikiwiki/directive/meta/discussion.mdwn create mode 100644 doc/ikiwiki/directive/more.mdwn create mode 100644 doc/ikiwiki/directive/orphans.mdwn create mode 100644 doc/ikiwiki/directive/osm.mdwn create mode 100644 doc/ikiwiki/directive/osm/discussion.mdwn create mode 100644 doc/ikiwiki/directive/pagecount.mdwn create mode 100644 doc/ikiwiki/directive/pagestats.mdwn create mode 100644 doc/ikiwiki/directive/pagestats/discussion.mdwn create mode 100644 doc/ikiwiki/directive/pagetemplate.mdwn create mode 100644 doc/ikiwiki/directive/paste.mdwn create mode 100644 doc/ikiwiki/directive/ping.mdwn create mode 100644 doc/ikiwiki/directive/poll.mdwn create mode 100644 doc/ikiwiki/directive/polygen.mdwn create mode 100644 doc/ikiwiki/directive/postsparkline.mdwn create mode 100644 doc/ikiwiki/directive/progress.mdwn create mode 100644 doc/ikiwiki/directive/shortcut.mdwn create mode 100644 doc/ikiwiki/directive/sidebar.mdwn create mode 100644 doc/ikiwiki/directive/sidebar/discussion.mdwn create mode 100644 doc/ikiwiki/directive/sparkline.mdwn create mode 100644 doc/ikiwiki/directive/table.mdwn create mode 100644 doc/ikiwiki/directive/table/discussion.mdwn create mode 100644 doc/ikiwiki/directive/tag.mdwn create mode 100644 doc/ikiwiki/directive/tag/discussion.mdwn create mode 100644 doc/ikiwiki/directive/taglink.mdwn create mode 100644 doc/ikiwiki/directive/template.mdwn create mode 100644 doc/ikiwiki/directive/testpagespec.mdwn create mode 100644 doc/ikiwiki/directive/testpagespec/discussion.mdwn create mode 100644 doc/ikiwiki/directive/teximg.mdwn create mode 100644 doc/ikiwiki/directive/toc.mdwn create mode 100644 doc/ikiwiki/directive/toggle.mdwn create mode 100644 doc/ikiwiki/directive/toggleable.mdwn create mode 100644 doc/ikiwiki/directive/trailitem.mdwn create mode 100644 doc/ikiwiki/directive/trailitems.mdwn create mode 100644 doc/ikiwiki/directive/traillink.mdwn create mode 100644 doc/ikiwiki/directive/trailoptions.mdwn create mode 100644 doc/ikiwiki/directive/version.mdwn create mode 100644 doc/ikiwiki/directive/waypoint.mdwn create mode 100644 doc/ikiwiki/formatting.mdwn create mode 100644 doc/ikiwiki/formatting/discussion.mdwn create mode 100644 doc/ikiwiki/markdown.mdwn create mode 100644 doc/ikiwiki/openid.mdwn create mode 100644 doc/ikiwiki/pagespec.mdwn create mode 100644 doc/ikiwiki/pagespec/attachment.mdwn create mode 100644 doc/ikiwiki/pagespec/attachment/discussion.mdwn create mode 100644 doc/ikiwiki/pagespec/discussion.mdwn create mode 100644 doc/ikiwiki/pagespec/po.mdwn create mode 100644 doc/ikiwiki/pagespec/sorting.mdwn create mode 100644 doc/ikiwiki/searching.mdwn create mode 100644 doc/ikiwiki/subpage.mdwn create mode 100644 doc/ikiwiki/subpage/linkingrules.mdwn create mode 100644 doc/ikiwiki/wikilink.mdwn create mode 100644 doc/ikiwiki/wikilink/discussion.mdwn create mode 100644 doc/ikiwikiusers.mdwn create mode 100644 doc/ikiwikiusers/discussion.mdwn create mode 100644 doc/index.mdwn create mode 100644 doc/index/discussion.mdwn create mode 100644 doc/index/openid/discussion.mdwn create mode 100644 doc/install.mdwn create mode 100644 doc/install/discussion.mdwn create mode 100644 doc/local.css create mode 100644 doc/logo.mdwn create mode 100644 doc/logo/discussion.mdwn create mode 100644 doc/logo/favicon.svgz create mode 100644 doc/logo/ikiwiki.png create mode 100644 doc/logo/ikiwiki.svgz create mode 100644 doc/logo/ikiwiki_button.png create mode 100644 doc/logo/ikiwiki_large.png create mode 100644 doc/logo/ikiwiki_old.png create mode 100644 doc/logo/ikiwiki_old2.png create mode 100644 doc/logo/ikiwiki_old2.svgz create mode 100644 doc/news.mdwn create mode 100644 doc/news/Article_on_Ikiwiki_as_a_BTS.mdwn create mode 100644 doc/news/code_swarm.mdwn create mode 100644 doc/news/code_swarm/code_swarm.config create mode 100755 doc/news/code_swarm/code_swarm_log.pl create mode 100644 doc/news/code_swarm/discussion.mdwn create mode 100644 doc/news/code_swarm/screenshot.png create mode 100644 doc/news/consultant_list.mdwn create mode 100644 doc/news/discussion.mdwn create mode 100644 doc/news/donations.mdwn create mode 100644 doc/news/git_push_to_this_wiki.mdwn create mode 100644 doc/news/git_push_to_this_wiki/discussion.mdwn create mode 100644 doc/news/ikiwiki-hosting.mdwn create mode 100644 doc/news/ikiwiki_accepted_for_Summer_of_Code.mdwn create mode 100644 doc/news/ikiwiki_screencast.mdwn create mode 100644 doc/news/ikiwiki_screencast/discussion.mdwn create mode 100644 doc/news/ikiwiki_version_2.0.mdwn create mode 100644 doc/news/ikiwiki_version_3.0.mdwn create mode 100644 doc/news/irc_channel.mdwn create mode 100644 doc/news/moved_to_git.mdwn create mode 100644 doc/news/moved_to_git/discussion.mdwn create mode 100644 doc/news/new_domain_name.mdwn create mode 100644 doc/news/no_more_email_notifications.mdwn create mode 100644 doc/news/openid.mdwn create mode 100644 doc/news/openid/discussion.mdwn create mode 100644 doc/news/server_move.mdwn create mode 100644 doc/news/server_move_2009.mdwn create mode 100644 doc/news/server_speed.mdwn create mode 100644 doc/news/server_speed/discussion.mdwn create mode 100644 doc/news/stylesheets.mdwn create mode 100644 doc/news/stylesheets/discussion.mdwn create mode 100644 doc/news/version_3.20121212.mdwn create mode 100644 doc/news/version_3.20130212.mdwn create mode 100644 doc/news/version_3.20130504.mdwn create mode 100644 doc/news/version_3.20130518.mdwn create mode 100644 doc/news/version_3.20130710.mdwn create mode 100644 doc/pagehistory.mdwn create mode 100644 doc/patch.mdwn create mode 100644 doc/patch/core.mdwn create mode 100644 doc/plugins.mdwn create mode 100644 doc/plugins/404.mdwn create mode 100644 doc/plugins/404/discussion.mdwn create mode 100644 doc/plugins/aggregate.mdwn create mode 100644 doc/plugins/aggregate/discussion.mdwn create mode 100644 doc/plugins/amazon_s3.mdwn create mode 100644 doc/plugins/amazon_s3/discussion.mdwn create mode 100644 doc/plugins/anonok.mdwn create mode 100644 doc/plugins/attachment.mdwn create mode 100644 doc/plugins/autoindex.mdwn create mode 100644 doc/plugins/autoindex/discussion.mdwn create mode 100644 doc/plugins/blogspam.mdwn create mode 100644 doc/plugins/brokenlinks.mdwn create mode 100644 doc/plugins/calendar.mdwn create mode 100644 doc/plugins/calendar/discussion.mdwn create mode 100644 doc/plugins/camelcase.mdwn create mode 100644 doc/plugins/color.mdwn create mode 100644 doc/plugins/comments.mdwn create mode 100644 doc/plugins/comments/discussion.mdwn create mode 100644 doc/plugins/conditional.mdwn create mode 100644 doc/plugins/conditional/discussion.mdwn create mode 100644 doc/plugins/contrib.mdwn create mode 100644 doc/plugins/contrib/album.mdwn create mode 100644 doc/plugins/contrib/album/discussion.mdwn create mode 100644 doc/plugins/contrib/asymptote.mdwn create mode 100644 doc/plugins/contrib/asymptote/ikiwiki/directive/asymptote.mdwn create mode 100644 doc/plugins/contrib/attach.mdwn create mode 100644 doc/plugins/contrib/attach/discussion.mdwn create mode 100644 doc/plugins/contrib/bibtex.mdwn create mode 100644 doc/plugins/contrib/created_in_future.mdwn create mode 100644 doc/plugins/contrib/default_content_for___42__copyright__42___and___42__license__42__.mdwn create mode 100644 doc/plugins/contrib/dynamiccookies.mdwn create mode 100644 doc/plugins/contrib/field.mdwn create mode 100644 doc/plugins/contrib/field/discussion.mdwn create mode 100644 doc/plugins/contrib/flattr.mdwn create mode 100644 doc/plugins/contrib/flattr/discussion.mdwn create mode 100644 doc/plugins/contrib/ftemplate.mdwn create mode 100644 doc/plugins/contrib/ftemplate/discussion.mdwn create mode 100644 doc/plugins/contrib/ftemplate/ikiwiki/directive/ftemplate.mdwn create mode 100644 doc/plugins/contrib/gallery.mdwn create mode 100644 doc/plugins/contrib/gallery/discussion.mdwn create mode 100644 doc/plugins/contrib/getfield.mdwn create mode 100644 doc/plugins/contrib/getfield/discussion.mdwn create mode 100644 doc/plugins/contrib/googlemaps.mdwn create mode 100644 doc/plugins/contrib/googlemaps/discussion.mdwn create mode 100644 doc/plugins/contrib/groupfile.mdwn create mode 100644 doc/plugins/contrib/highlightcode.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/album.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/albumimage.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/albumsection.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/jssearchfield.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/ymlfront.mdwn create mode 100644 doc/plugins/contrib/ikiwiki/directive/ymlfront/discussion.mdwn create mode 100644 doc/plugins/contrib/imailhide.mdwn create mode 100644 doc/plugins/contrib/img.mdwn create mode 100644 doc/plugins/contrib/img/discussion.mdwn create mode 100644 doc/plugins/contrib/jscalendar.mdwn create mode 100644 doc/plugins/contrib/jssearchfield.mdwn create mode 100644 doc/plugins/contrib/justlogin.mdwn create mode 100644 doc/plugins/contrib/linguas.mdwn create mode 100644 doc/plugins/contrib/livefyre.mdwn create mode 100644 doc/plugins/contrib/localfavicon.mdwn create mode 100644 doc/plugins/contrib/mailbox.mdwn create mode 100644 doc/plugins/contrib/mailbox/discussion.mdwn create mode 100644 doc/plugins/contrib/mandoc.mdwn create mode 100644 doc/plugins/contrib/mathjax.mdwn create mode 100644 doc/plugins/contrib/mediawiki.mdwn create mode 100644 doc/plugins/contrib/mediawiki/discussion.mdwn create mode 100644 doc/plugins/contrib/monthcalendar.mdwn create mode 100644 doc/plugins/contrib/mscgen.mdwn create mode 100644 doc/plugins/contrib/navbar.mdwn create mode 100644 doc/plugins/contrib/navbar/discussion.mdwn create mode 100644 doc/plugins/contrib/newpage.mdwn create mode 100644 doc/plugins/contrib/newpage/discussion.mdwn create mode 100644 doc/plugins/contrib/opml.mdwn create mode 100644 doc/plugins/contrib/opml/discussion.mdwn create mode 100644 doc/plugins/contrib/pagespec_alias.mdwn create mode 100644 doc/plugins/contrib/pandoc.mdwn create mode 100644 doc/plugins/contrib/plusone.mdwn create mode 100644 doc/plugins/contrib/pod.mdwn create mode 100644 doc/plugins/contrib/pod/discussion.mdwn create mode 100644 doc/plugins/contrib/postal.mdwn create mode 100644 doc/plugins/contrib/postal/discussion.mdwn create mode 100644 doc/plugins/contrib/proxies.mdwn create mode 100644 doc/plugins/contrib/report.mdwn create mode 100644 doc/plugins/contrib/report/discussion.mdwn create mode 100644 doc/plugins/contrib/report/ikiwiki/directive/report.mdwn create mode 100644 doc/plugins/contrib/sar.mdwn create mode 100644 doc/plugins/contrib/screenplay.pm.mdwn create mode 100644 doc/plugins/contrib/siterel2pagerel.mdwn create mode 100644 doc/plugins/contrib/sourcehighlight.mdwn create mode 100644 doc/plugins/contrib/syntax.mdwn create mode 100644 doc/plugins/contrib/syntax/discussion.mdwn create mode 100644 doc/plugins/contrib/tex4ht.mdwn create mode 100644 doc/plugins/contrib/texinfo.mdwn create mode 100644 doc/plugins/contrib/tracking.mdwn create mode 100644 doc/plugins/contrib/unixauth.mdwn create mode 100644 doc/plugins/contrib/unixauth/discussion.mdwn create mode 100644 doc/plugins/contrib/unixrelpagespec.mdwn create mode 100644 doc/plugins/contrib/video.mdwn create mode 100644 doc/plugins/contrib/video/discussion.mdwn create mode 100644 doc/plugins/contrib/wc.mdwn create mode 100644 doc/plugins/contrib/xslt.mdwn create mode 100644 doc/plugins/contrib/xslt/discussion.mdwn create mode 100644 doc/plugins/contrib/ymlfront.mdwn create mode 100644 doc/plugins/contrib/ymlfront/discussion.mdwn create mode 100644 doc/plugins/creole.mdwn create mode 100644 doc/plugins/creole/discussion.mdwn create mode 100644 doc/plugins/cutpaste.mdwn create mode 100644 doc/plugins/date.mdwn create mode 100644 doc/plugins/ddate.mdwn create mode 100644 doc/plugins/discussion.mdwn create mode 100644 doc/plugins/editdiff.mdwn create mode 100644 doc/plugins/editdiff/discussion.mdwn create mode 100644 doc/plugins/editpage.mdwn create mode 100644 doc/plugins/editpage/discussion.mdwn create mode 100644 doc/plugins/edittemplate.mdwn create mode 100644 doc/plugins/embed.mdwn create mode 100644 doc/plugins/favicon.mdwn create mode 100644 doc/plugins/favicon/discussion.mdwn create mode 100644 doc/plugins/filecheck.mdwn create mode 100644 doc/plugins/filecheck/discussion.mdwn create mode 100644 doc/plugins/flattr.mdwn create mode 100644 doc/plugins/format.mdwn create mode 100644 doc/plugins/format/discussion.mdwn create mode 100644 doc/plugins/fortune.mdwn create mode 100644 doc/plugins/getsource.mdwn create mode 100644 doc/plugins/getsource/discussion.mdwn create mode 100644 doc/plugins/goodstuff.mdwn create mode 100644 doc/plugins/goodstuff/discussion.mdwn create mode 100644 doc/plugins/google.mdwn create mode 100644 doc/plugins/google/discussion.mdwn create mode 100644 doc/plugins/goto.mdwn create mode 100644 doc/plugins/graphviz.mdwn create mode 100644 doc/plugins/haiku.mdwn create mode 100644 doc/plugins/haiku/discussion.mdwn create mode 100644 doc/plugins/headinganchors.mdwn create mode 100644 doc/plugins/headinganchors/discussion.mdwn create mode 100644 doc/plugins/highlight.mdwn create mode 100644 doc/plugins/highlight/discussion.mdwn create mode 100644 doc/plugins/hnb.mdwn create mode 100644 doc/plugins/hnb/discussion.mdwn create mode 100644 doc/plugins/html.mdwn create mode 100644 doc/plugins/htmlbalance.mdwn create mode 100644 doc/plugins/htmlbalance/discussion.mdwn create mode 100644 doc/plugins/htmlscrubber.mdwn create mode 100644 doc/plugins/htmlscrubber/discussion.mdwn create mode 100644 doc/plugins/htmltidy.mdwn create mode 100644 doc/plugins/httpauth.mdwn create mode 100644 doc/plugins/img.mdwn create mode 100644 doc/plugins/img/discussion.mdwn create mode 100644 doc/plugins/inline.mdwn create mode 100644 doc/plugins/install.mdwn create mode 100644 doc/plugins/link.mdwn create mode 100644 doc/plugins/linkmap.mdwn create mode 100644 doc/plugins/listdirectives.mdwn create mode 100644 doc/plugins/localstyle.mdwn create mode 100644 doc/plugins/lockedit.mdwn create mode 100644 doc/plugins/lockedit/discussion.mdwn create mode 100644 doc/plugins/map.mdwn create mode 100644 doc/plugins/map/discussion.mdwn create mode 100644 doc/plugins/mdwn.mdwn create mode 100644 doc/plugins/mdwn/discussion.mdwn create mode 100644 doc/plugins/meta.mdwn create mode 100644 doc/plugins/meta/discussion.mdwn create mode 100644 doc/plugins/mirrorlist.mdwn create mode 100644 doc/plugins/moderatedcomments.mdwn create mode 100644 doc/plugins/more.mdwn create mode 100644 doc/plugins/more/discussion.mdwn create mode 100644 doc/plugins/notifyemail.mdwn create mode 100644 doc/plugins/notifyemail/discussion.mdwn create mode 100644 doc/plugins/opendiscussion.mdwn create mode 100644 doc/plugins/openid.mdwn create mode 100644 doc/plugins/openid/discussion.mdwn create mode 100644 doc/plugins/orphans.mdwn create mode 100644 doc/plugins/orphans/discussion.mdwn create mode 100644 doc/plugins/osm.mdwn create mode 100644 doc/plugins/otl.mdwn create mode 100644 doc/plugins/pagecount.mdwn create mode 100644 doc/plugins/pagestats.mdwn create mode 100644 doc/plugins/pagetemplate.mdwn create mode 100644 doc/plugins/parentlinks.mdwn create mode 100644 doc/plugins/passwordauth.mdwn create mode 100644 doc/plugins/passwordauth/discussion.mdwn create mode 100644 doc/plugins/pingee.mdwn create mode 100644 doc/plugins/pingee/discussion.mdwn create mode 100644 doc/plugins/pinger.mdwn create mode 100644 doc/plugins/po.mdwn create mode 100644 doc/plugins/po/discussion.mdwn create mode 100644 doc/plugins/poll.mdwn create mode 100644 doc/plugins/poll/discussion.mdwn create mode 100644 doc/plugins/polygen.mdwn create mode 100644 doc/plugins/postsparkline.mdwn create mode 100644 doc/plugins/prettydate.mdwn create mode 100644 doc/plugins/progress.mdwn create mode 100644 doc/plugins/rawhtml.mdwn create mode 100644 doc/plugins/rawhtml/discussion.mdwn create mode 100644 doc/plugins/recentchanges.mdwn create mode 100644 doc/plugins/recentchanges/discussion.mdwn create mode 100644 doc/plugins/recentchangesdiff.mdwn create mode 100644 doc/plugins/relativedate.mdwn create mode 100644 doc/plugins/remove.mdwn create mode 100644 doc/plugins/rename.mdwn create mode 100644 doc/plugins/repolist.mdwn create mode 100644 doc/plugins/rst.mdwn create mode 100644 doc/plugins/rst/discussion.mdwn create mode 100644 doc/plugins/rsync.mdwn create mode 100644 doc/plugins/rsync/discussion.mdwn create mode 100644 doc/plugins/search.mdwn create mode 100644 doc/plugins/search/discussion.mdwn create mode 100644 doc/plugins/shortcut.mdwn create mode 100644 doc/plugins/shortcut/discussion.mdwn create mode 100644 doc/plugins/sidebar.mdwn create mode 100644 doc/plugins/sidebar/discussion.mdwn create mode 100644 doc/plugins/signinedit.mdwn create mode 100644 doc/plugins/smiley.mdwn create mode 100644 doc/plugins/sortnaturally.mdwn create mode 100644 doc/plugins/sparkline.mdwn create mode 100644 doc/plugins/table.mdwn create mode 100644 doc/plugins/table/discussion.mdwn create mode 100644 doc/plugins/tag.mdwn create mode 100644 doc/plugins/tag/discussion.mdwn create mode 100644 doc/plugins/template.mdwn create mode 100644 doc/plugins/testpagespec.mdwn create mode 100644 doc/plugins/teximg.mdwn create mode 100644 doc/plugins/teximg/discussion.mdwn create mode 100644 doc/plugins/textile.mdwn create mode 100644 doc/plugins/theme.mdwn create mode 100644 doc/plugins/theme/discussion.mdwn create mode 100644 doc/plugins/toc.mdwn create mode 100644 doc/plugins/toc/discussion.mdwn create mode 100644 doc/plugins/toggle.mdwn create mode 100644 doc/plugins/toggle/discussion.mdwn create mode 100644 doc/plugins/trail.mdwn create mode 100644 doc/plugins/trail/discussion.mdwn create mode 100644 doc/plugins/transient.mdwn create mode 100644 doc/plugins/txt.mdwn create mode 100644 doc/plugins/txt/discussion.mdwn create mode 100644 doc/plugins/type/auth.mdwn create mode 100644 doc/plugins/type/bundle.mdwn create mode 100644 doc/plugins/type/chrome.mdwn create mode 100644 doc/plugins/type/comments.mdwn create mode 100644 doc/plugins/type/core.mdwn create mode 100644 doc/plugins/type/date.mdwn create mode 100644 doc/plugins/type/format.mdwn create mode 100644 doc/plugins/type/fun.mdwn create mode 100644 doc/plugins/type/html.mdwn create mode 100644 doc/plugins/type/link.mdwn create mode 100644 doc/plugins/type/meta.mdwn create mode 100644 doc/plugins/type/slow.mdwn create mode 100644 doc/plugins/type/special-purpose.mdwn create mode 100644 doc/plugins/type/tags.mdwn create mode 100644 doc/plugins/type/web.mdwn create mode 100644 doc/plugins/type/widget.mdwn create mode 100644 doc/plugins/typography.mdwn create mode 100644 doc/plugins/underlay.mdwn create mode 100644 doc/plugins/userlist.mdwn create mode 100644 doc/plugins/version.mdwn create mode 100644 doc/plugins/websetup.mdwn create mode 100644 doc/plugins/wikitext.mdwn create mode 100644 doc/plugins/wmd.mdwn create mode 100644 doc/plugins/wmd/discussion.mdwn create mode 100644 doc/plugins/write.mdwn create mode 100644 doc/plugins/write/discussion.mdwn create mode 100644 doc/plugins/write/external.mdwn create mode 100644 doc/plugins/write/tutorial.mdwn create mode 100644 doc/plugins/write/tutorial/discussion.mdwn create mode 100644 doc/post-commit.mdwn create mode 100644 doc/post-commit/discussion.mdwn create mode 100644 doc/quotes.mdwn create mode 100644 doc/quotes/pizza.mdwn create mode 100644 doc/quotes/pizza/discussion.mdwn create mode 100644 doc/quotes/sold.mdwn create mode 100644 doc/rcs.mdwn create mode 100644 doc/rcs/bzr.mdwn create mode 100644 doc/rcs/cvs.mdwn create mode 100644 doc/rcs/cvs/discussion.mdwn create mode 100644 doc/rcs/darcs.mdwn create mode 100644 doc/rcs/details.mdwn create mode 100644 doc/rcs/details/discussion.mdwn create mode 100644 doc/rcs/git.mdwn create mode 100644 doc/rcs/git/discussion.mdwn create mode 100644 doc/rcs/git/wiki_edit_flow.svg create mode 100644 doc/rcs/mercurial.mdwn create mode 100644 doc/rcs/monotone.mdwn create mode 100644 doc/rcs/svn.mdwn create mode 100644 doc/rcs/svn/discussion.mdwn create mode 100644 doc/rcs/tla.mdwn create mode 100644 doc/recentchanges.mdwn create mode 100644 doc/reviewed.mdwn create mode 100644 doc/roadmap.mdwn create mode 100644 doc/roadmap/discussion.mdwn create mode 100644 doc/robots.txt create mode 100644 doc/sandbox.mdwn create mode 100644 doc/sandbox/NewPage.mdwn create mode 100644 doc/sandbox/hmm__44___what_kind_of_a_blog_is_this__63____41__.mdwn create mode 100644 doc/security.mdwn create mode 100644 doc/security/discussion.mdwn create mode 100644 doc/setup.mdwn create mode 100644 doc/setup/byhand.mdwn create mode 100644 doc/setup/byhand/discussion.mdwn create mode 100644 doc/setup/discussion.mdwn create mode 100644 doc/shortcuts.mdwn create mode 100644 doc/shortcuts/discussion.mdwn create mode 100644 doc/sitemap.mdwn create mode 100644 doc/smileys.mdwn create mode 100644 doc/smileys/alert.png create mode 100644 doc/smileys/angry.png create mode 100644 doc/smileys/attention.png create mode 100644 doc/smileys/biggrin.png create mode 100644 doc/smileys/checkmark.png create mode 100644 doc/smileys/devil.png create mode 100644 doc/smileys/frown.png create mode 100644 doc/smileys/icon-error.png create mode 100644 doc/smileys/icon-info.png create mode 100644 doc/smileys/idea.png create mode 100644 doc/smileys/neutral.png create mode 100644 doc/smileys/ohwell.png create mode 100644 doc/smileys/prio1.png create mode 100644 doc/smileys/prio2.png create mode 100644 doc/smileys/prio3.png create mode 100644 doc/smileys/question.png create mode 100644 doc/smileys/redface.png create mode 100644 doc/smileys/sad.png create mode 100644 doc/smileys/smile.png create mode 100644 doc/smileys/smile2.png create mode 100644 doc/smileys/smile3.png create mode 100644 doc/smileys/smile4.png create mode 100644 doc/smileys/star_off.png create mode 100644 doc/smileys/star_on.png create mode 100644 doc/smileys/thumbs-up.png create mode 100644 doc/smileys/tired.png create mode 100644 doc/smileys/tongue.png create mode 100644 doc/soc.mdwn create mode 100644 doc/soc/application.mdwn create mode 100644 doc/soc/discussion.mdwn create mode 100644 doc/soc/ideas.mdwn create mode 100644 doc/style.css create mode 100644 doc/tags.mdwn create mode 100644 doc/tags/discussion.mdwn create mode 100644 doc/templates.mdwn create mode 100644 doc/templates/discussion.mdwn create mode 100644 doc/templates/gitbranch.mdwn create mode 100644 doc/templates/links.mdwn create mode 100644 doc/templates/note.mdwn create mode 100644 doc/templates/plugin.mdwn create mode 100644 doc/templates/popup.mdwn create mode 100644 doc/theme_market.mdwn create mode 100644 doc/themes.mdwn create mode 100644 doc/themes/actiontabs_small.png create mode 100644 doc/themes/blueview_small.png create mode 100644 doc/themes/discussion.mdwn create mode 100644 doc/themes/goldtype_small.png create mode 100644 doc/themes/monochrome_small.png create mode 100644 doc/themes/none_small.png create mode 100644 doc/tipjar.mdwn create mode 100644 doc/tips.mdwn create mode 100644 doc/tips/Adding_Disqus_to_your_wiki.mdwn create mode 100644 doc/tips/Adding_Disqus_to_your_wiki/discussion.mdwn create mode 100644 doc/tips/DreamHost.mdwn create mode 100644 doc/tips/DreamHost/discussion.mdwn create mode 100644 doc/tips/Emacs_and_markdown.html create mode 100644 doc/tips/Git_repository_and_web_server_on_different_hosts.mdwn create mode 100644 doc/tips/Google_custom_search.mdwn create mode 100644 doc/tips/Importing_posts_from_Wordpress.mdwn create mode 100644 doc/tips/Importing_posts_from_Wordpress/discussion.mdwn create mode 100644 doc/tips/JavaScript_to_add_index.html_to_file:_links.mdwn create mode 100644 doc/tips/JavaScript_to_add_index.html_to_file:_links/discusion.mdwn create mode 100644 doc/tips/JavaScript_to_add_index.html_to_file:_links/discussion.mdwn create mode 100644 doc/tips/Make_calendar_start_week_on_Monday.mdwn create mode 100644 doc/tips/Make_calendar_start_week_on_Monday/discussion.mdwn create mode 100644 doc/tips/add_chatterbox_to_blog.mdwn create mode 100644 doc/tips/add_chatterbox_to_blog/discussion.mdwn create mode 100644 doc/tips/blog_script.mdwn create mode 100644 doc/tips/comments_feed.mdwn create mode 100644 doc/tips/convert_blogger_blogs_to_ikiwiki.mdwn create mode 100644 doc/tips/convert_mediawiki_to_ikiwiki.mdwn create mode 100644 doc/tips/convert_mediawiki_to_ikiwiki/discussion.mdwn create mode 100644 doc/tips/convert_moinmoin_to_ikiwiki.mdwn create mode 100644 doc/tips/convert_moinmoin_to_ikiwiki/discussion.mdwn create mode 100644 doc/tips/distributed_wikis.mdwn create mode 100644 doc/tips/distributed_wikis/discussion.mdwn create mode 100644 doc/tips/dot_cgi.mdwn create mode 100644 doc/tips/dot_cgi/discussion.mdwn create mode 100644 doc/tips/emacs_syntax_highlighting.mdwn create mode 100644 doc/tips/embedding_content.mdwn create mode 100644 doc/tips/follow_wikilinks_from_inside_vim.mdwn create mode 100644 doc/tips/github.mdwn create mode 100644 doc/tips/howto_avoid_flooding_aggregators.mdwn create mode 100644 doc/tips/howto_limit_to_admin_users.mdwn create mode 100644 doc/tips/htaccess_file.mdwn create mode 100644 doc/tips/html5.mdwn create mode 100644 doc/tips/ikiwiki_as_a_requirements_management_tool.mdwn create mode 100644 doc/tips/ikiwiki_as_a_requirements_management_tool/discussion.mdwn create mode 100644 doc/tips/ikiwiki_on_mac_os_x.mdwn create mode 100644 doc/tips/ikiwiki_via_gopher.mdwn create mode 100644 doc/tips/ikiwiki_via_gopher/discussion.mdwn create mode 100644 doc/tips/importing_posts_from_typo.mdwn create mode 100644 doc/tips/importing_posts_from_wordpress/ikiwiki-wordpress-import.mdwn create mode 100644 doc/tips/inside_dot_ikiwiki.mdwn create mode 100644 doc/tips/inside_dot_ikiwiki/discussion.mdwn create mode 100644 doc/tips/integrated_issue_tracking_with_ikiwiki.mdwn create mode 100644 doc/tips/integrated_issue_tracking_with_ikiwiki/discussion.mdwn create mode 100644 doc/tips/laptop_wiki_with_git.mdwn create mode 100644 doc/tips/laptop_wiki_with_git/discussion.mdwn create mode 100644 doc/tips/laptop_wiki_with_git_extended.mdwn create mode 100644 doc/tips/laptop_wiki_with_git_extended/discussion.mdwn create mode 100644 doc/tips/mailman_subscription_form.mdwn create mode 100644 doc/tips/markdown_and_eclipse.mdwn create mode 100644 doc/tips/mathopd_permissions.mdwn create mode 100644 doc/tips/nearlyfreespeech.mdwn create mode 100644 doc/tips/nearlyfreespeech/discussion.mdwn create mode 100644 doc/tips/optimising_ikiwiki.mdwn create mode 100644 doc/tips/parentlinks_style.mdwn create mode 100644 doc/tips/psgi.mdwn create mode 100644 doc/tips/redirections_for_usedirs.mdwn create mode 100644 doc/tips/spam_and_softwaresites.mdwn create mode 100644 doc/tips/spam_and_softwaresites/discussion.mdwn create mode 100644 doc/tips/switching_to_usedirs.mdwn create mode 100644 doc/tips/switching_to_usedirs/discussion.mdwn create mode 100644 doc/tips/untrusted_git_push.mdwn create mode 100644 doc/tips/untrusted_git_push/discussion.mdwn create mode 100644 doc/tips/upgrade_to_3.0.mdwn create mode 100644 doc/tips/using_the_web_interface_with_a_real_text_editor.mdwn create mode 100644 doc/tips/using_the_web_interface_with_a_real_text_editor/discussion.mdwn create mode 100644 doc/tips/vim_and_ikiwiki.mdwn create mode 100644 doc/tips/vim_syntax_highlighting.mdwn create mode 100644 doc/tips/vim_syntax_highlighting/discussion.mdwn create mode 100644 doc/tips/vim_syntax_highlighting/ikiwiki.vim create mode 100644 doc/tips/wikiannounce.mdwn create mode 100644 doc/tips/yaml_setup_files.mdwn create mode 100644 doc/todo.mdwn create mode 100644 doc/todo/ACL.mdwn create mode 100644 doc/todo/A_page_that_inlines_pages__61____34____42____34___results_in_unnecessary_feed_generation.mdwn create mode 100644 doc/todo/Account-creation_password.mdwn create mode 100644 doc/todo/Account_moderation.mdwn create mode 100644 doc/todo/Add_DATE_parameter_for_use_in_templates.mdwn create mode 100644 doc/todo/Add_HTML_support_to_po_plugin.mdwn create mode 100644 doc/todo/Add_a_plugin_to_list_available_pre-processor_commands.mdwn create mode 100644 doc/todo/Add_basename_in_edittemplate.mdwn create mode 100644 doc/todo/Add_camelcase_exclusions.mdwn create mode 100644 doc/todo/Add_instructive_commit_messages_for_add__47__edit_pages.mdwn create mode 100644 doc/todo/Add_instructive_commit_messages_for_removing_pages.mdwn create mode 100644 doc/todo/Add_label_to_search_form_input_field.mdwn create mode 100644 doc/todo/Add_nicer_math_formatting.mdwn create mode 100644 doc/todo/Add_showdown_GUI_input__47__edit.mdwn create mode 100644 doc/todo/Add_space_before_slash_in_parent_links.mdwn create mode 100644 doc/todo/Add_support_for_latest_Text::Markdown_as_found_on_CPAN.mdwn create mode 100644 doc/todo/Adjust_goodstuff.mdwn create mode 100644 doc/todo/Allow_TITLE_to_include_part_of_the_path_in_addition_to_the_basename.mdwn create mode 100644 doc/todo/Allow_change_of_wiki_file_types.mdwn create mode 100644 doc/todo/Allow_disabling_edit_and_preferences_links.mdwn create mode 100644 doc/todo/Allow_edittemplate_to_set_file_type.mdwn create mode 100644 doc/todo/Allow_filenames_that_are_all_type.mdwn create mode 100644 doc/todo/Allow_per-page_template_selection.mdwn create mode 100644 doc/todo/Allow_web_edit_form_comment_field_to_be_mandatory.mdwn create mode 100644 doc/todo/Attempt_to_extend_Mercurial_backend_support.mdwn create mode 100644 doc/todo/Auto-setup_and_maintain_Mercurial_wrapper_hooks.mdwn create mode 100644 doc/todo/Auto-setup_should_default_to_YAML.mdwn create mode 100644 doc/todo/Automatic_aggregate_setup_from_wikilist_in_Debian_package_.mdwn create mode 100644 doc/todo/BTS_integration.mdwn create mode 100644 doc/todo/Bestdir_along_with_bestlink_in_IkiWiki.pm.mdwn create mode 100644 doc/todo/Bestdir_along_with_bestlink_in_IkiWiki.pm/discussion.mdwn create mode 100644 doc/todo/Better_bug_tracking_support.mdwn create mode 100644 doc/todo/Better_reporting_of_validation_errors.mdwn create mode 100644 doc/todo/BibTeX.mdwn create mode 100644 doc/todo/BrowserID.mdwn create mode 100644 doc/todo/CGI_method_to_pullrefresh.mdwn create mode 100644 doc/todo/CSS_classes_for_links.mdwn create mode 100644 doc/todo/CVS_backend.mdwn create mode 100644 doc/todo/Calendar:_listing_multiple_entries_per_day_.mdwn create mode 100644 doc/todo/Case.mdwn create mode 100644 doc/todo/Commit_emails:_ones_own_changes.mdwn create mode 100644 doc/todo/Configurable_minimum_length_of_log_message_for_web_edits.mdwn create mode 100644 doc/todo/Configureable_separator_of_page_name.mdwn create mode 100644 doc/todo/Debian_package_could_Recommend_gcc_+_libc6-dev__44___not_Depend.mdwn create mode 100644 doc/todo/Default_text_for_new_pages.mdwn create mode 100644 doc/todo/Does_not_support_non-UTF8_files.mdwn create mode 100644 doc/todo/Editing_po_files.mdwn create mode 100644 doc/todo/Enable_filtering_of_files_indexed_for_search.mdwn create mode 100644 doc/todo/Extensible_inlining.mdwn create mode 100644 doc/todo/Feature_parity_with_Trac.mdwn create mode 100644 doc/todo/Fenced_code_blocks___40__from_GitHub_Flavored_Markdown__41__.mdwn create mode 100644 doc/todo/Fix_CSS_to_not_put_a_border_around_image_links.mdwn create mode 100644 doc/todo/Fix_selflink_in_po_plugin.mdwn create mode 100644 doc/todo/FormBuilder__95__Template__95__patch.mdwn create mode 100644 doc/todo/FormattingHelp_should_open_new_window.mdwn create mode 100644 doc/todo/Gallery.mdwn create mode 100644 doc/todo/Give_access_to_more_TMPL__95__VAR_variables_in_templates_inserted_by_the_template_plugin.mdwn create mode 100644 doc/todo/Google_Analytics_support.mdwn create mode 100644 doc/todo/Google_Sitemap_protocol.mdwn create mode 100644 doc/todo/Have_xapian_index_pdf__44___openoffice__44___documents.mdwn create mode 100644 doc/todo/IRC_topic.mdwn create mode 100644 doc/todo/Improve_display_of_OpenIDs.mdwn create mode 100644 doc/todo/Improve_markdown_speed.mdwn create mode 100644 doc/todo/Improve_signin_form_layout.mdwn create mode 100644 doc/todo/Improving_the_efficiency_of_match__95__glob.mdwn create mode 100644 doc/todo/Inline_plugin_option_to_show_full_page_path.mdwn create mode 100644 doc/todo/Location_of_pages_starting_with___36__tagbase_should_be_in__by_default.mdwn create mode 100644 doc/todo/Mailing_list.mdwn create mode 100644 doc/todo/Make_example_setup_file_consistent.mdwn create mode 100644 doc/todo/Mercurial_backend_update.mdwn create mode 100644 doc/todo/Modern_standard_layout.mdwn create mode 100644 doc/todo/More_flexible_po-plugin_for_translation.mdwn create mode 100644 doc/todo/Move_teximg_latex_preamble_to_config_file.mdwn create mode 100644 doc/todo/Moving_Pages.mdwn create mode 100644 doc/todo/Multiple_categorization_namespaces.mdwn create mode 100644 doc/todo/New_preprocessor_directive_syntax.mdwn create mode 100644 doc/todo/New_preprocessor_directive_syntax/discussion.mdwn create mode 100644 doc/todo/OpenSearch.mdwn create mode 100644 doc/todo/Option_to_disable_date_footer_for_inlines.mdwn create mode 100644 doc/todo/Option_to_make_title_an_h1__63__.mdwn create mode 100644 doc/todo/Overlay_directory_for_pagetemplates.mdwn create mode 100644 doc/todo/Pagination_next_prev_links.mdwn create mode 100644 doc/todo/Plugins_to_provide___34__add_to__34___links_for_popular_feed_readers.mdwn create mode 100644 doc/todo/Post-compilation_inclusion_of_the_sidebar.mdwn create mode 100644 doc/todo/Print_link.mdwn create mode 100644 doc/todo/RSS_fields.mdwn create mode 100644 doc/todo/RSS_links.mdwn create mode 100644 doc/todo/Raw_view_link.mdwn create mode 100644 doc/todo/RecentChanges_page_links_without_cgi_wrapper.mdwn create mode 100644 doc/todo/Render_multiple_destinations_from_one_source.mdwn create mode 100644 doc/todo/Resolve_native_reStructuredText_links_to_ikiwiki_pages.mdwn create mode 100644 doc/todo/Restrict_formats_allowed_for_comments.mdwn create mode 100644 doc/todo/Restrict_page_viewing.mdwn create mode 100644 doc/todo/Separate_OpenIDs_and_usernames.mdwn create mode 100644 doc/todo/Set_arbitrary_date_to_be_used_by_calendar_plugin.mdwn create mode 100644 doc/todo/Set_arbitrary_date_to_be_used_by_calendar_plugin/discussion.mdwn create mode 100644 doc/todo/Set_templates_for_whole_sections_of_the_site.mdwn create mode 100644 doc/todo/Short_wikilinks.mdwn create mode 100644 doc/todo/Shorter_feeds.mdwn create mode 100644 doc/todo/Silence_monotone_warning.mdwn create mode 100644 doc/todo/Split_plugins_with_external_dependencies_into_separate_Debian_packages.mdwn create mode 100644 doc/todo/Suggested_location_should_be_subpage_if_siblings_exist.mdwn create mode 100644 doc/todo/Support_MultiMarkdown_3.X.mdwn create mode 100644 doc/todo/Support_XML-RPC-based_blogging.mdwn create mode 100644 doc/todo/Support__47__Switch_to_MultiMarkdown.mdwn create mode 100644 doc/todo/Support_preprocessing_CSS.mdwn create mode 100644 doc/todo/Support_subdirectory_of_a_git_repo.mdwn create mode 100644 doc/todo/Support_tab_insertion_in_textarea.mdwn create mode 100644 doc/todo/Support_wildcard_inside_of_link__40____41___within_a_pagespec.mdwn create mode 100644 doc/todo/Tags_list_in_page_footer_uses_basename.mdwn create mode 100644 doc/todo/Track_Markdown_Standardisation_Efforts.mdwn create mode 100644 doc/todo/Unit_tests.mdwn create mode 100644 doc/todo/Untrusted_push_in_Monotone.mdwn create mode 100644 doc/todo/Updated_bug_tracking_example.mdwn create mode 100644 doc/todo/Using_page_titles_in_internal_links.mdwn create mode 100644 doc/todo/Wikilink_to_a_symbolic_link.mdwn create mode 100644 doc/todo/Wrapper_config_with_multiline_regexp.mdwn create mode 100644 doc/todo/Zoned_ikiwiki.mdwn create mode 100644 doc/todo/__34__subscribe_to_this_page__34___checkbox_on_edit_form.mdwn create mode 100644 doc/todo/__42__forward__42__ing_functionality_for_the_meta_plugin.mdwn create mode 100644 doc/todo/__47___should_point_to_top-level_index.mdwn create mode 100644 doc/todo/a_navbar_based_on_page_properties.mdwn create mode 100644 doc/todo/abbreviation.mdwn create mode 100644 doc/todo/ability_to_force_particular_UUIDs_on_blog_posts.mdwn create mode 100644 doc/todo/absolute_urls_in_wikilinks.mdwn create mode 100644 doc/todo/access_keys.mdwn create mode 100644 doc/todo/ad-hoc_plugins.mdwn create mode 100644 doc/todo/add_forward_age_sorting_option_to_inline.mdwn create mode 100644 doc/todo/adding_new_pages_by_using_the_web_interface.mdwn create mode 100644 doc/todo/adjust_commit_message_for_rename__44___remove.mdwn create mode 100644 doc/todo/aggregate_401_handling.mdwn create mode 100644 doc/todo/aggregate_locking.mdwn create mode 100644 doc/todo/aggregate_to_internal_pages.mdwn create mode 100644 doc/todo/aggregation.mdwn create mode 100644 doc/todo/alias_directive.mdwn create mode 100644 doc/todo/allow_CGI_to_create_dynamic_pages.mdwn create mode 100644 doc/todo/allow_TMPL__95__LOOP_in_template_directives.mdwn create mode 100644 doc/todo/allow_banning_a_user_when_moderating_a_comment.mdwn create mode 100644 doc/todo/allow_creation_of_non-existent_pages.mdwn create mode 100644 doc/todo/allow_disabling_backlinks.mdwn create mode 100644 doc/todo/allow_displaying_number_of_comments.mdwn create mode 100644 doc/todo/allow_full_post_from_the___34__add_a_new_post__34___form.mdwn create mode 100644 doc/todo/allow_plugins_to_add_sorting_methods.mdwn create mode 100644 doc/todo/allow_site-wide_meta_definitions.mdwn create mode 100644 doc/todo/allow_wiki_syntax_in_commit_messages.mdwn create mode 100644 doc/todo/anon_push_of_comments.mdwn create mode 100644 doc/todo/anti-spam_protection.mdwn create mode 100644 doc/todo/apache_404_ErrorDocument_handler.mdwn create mode 100644 doc/todo/applydiff_plugin.mdwn create mode 100644 doc/todo/assumes_system_perl.mdwn create mode 100644 doc/todo/attachments.mdwn create mode 100644 doc/todo/auto-create_tag_pages_according_to_a_template.mdwn create mode 100644 doc/todo/auto_getctime_on_fresh_build.mdwn create mode 100644 doc/todo/auto_publish_expire.mdwn create mode 100644 doc/todo/auto_rebuild_on_template_change.mdwn create mode 100644 doc/todo/autoindex_should_use_add__95__autofile.mdwn create mode 100644 doc/todo/automatic_rebuilding_of_html_pages.mdwn create mode 100644 doc/todo/automatic_use_of_syntax_plugin_on_source_code_files.mdwn create mode 100644 doc/todo/automatic_use_of_syntax_plugin_on_source_code_files/discussion.mdwn create mode 100644 doc/todo/avatar.mdwn create mode 100644 doc/todo/avatar/discussion.mdwn create mode 100644 doc/todo/avoid_attachement_ui_if_upload_not_allowed.mdwn create mode 100644 doc/todo/avoid_thrashing.mdwn create mode 100644 doc/todo/backlinks_result_is_lossy.mdwn create mode 100644 doc/todo/basewiki_should_be_self_documenting.mdwn create mode 100644 doc/todo/be_more_selective_about_running_hooks.mdwn create mode 100644 doc/todo/beef_up_sidebar_to_allow_for_multiple_sidebars.mdwn create mode 100644 doc/todo/beef_up_signin_page.mdwn create mode 100644 doc/todo/block_external_links.mdwn create mode 100644 doc/todo/blocking_ip_ranges.mdwn create mode 100644 doc/todo/blogging.mdwn create mode 100644 doc/todo/blogpost_plugin.mdwn create mode 100644 doc/todo/blogs.mdwn create mode 100644 doc/todo/blogspam_training.mdwn create mode 100644 doc/todo/break_up_page_template_into_subfiles.mdwn create mode 100644 doc/todo/brokenlinks_should_group_links_to_a_page.mdwn create mode 100644 doc/todo/bzr.mdwn create mode 100644 doc/todo/cache_backlinks.mdwn create mode 100644 doc/todo/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn create mode 100644 doc/todo/calendar_with___34__create__34___links.mdwn create mode 100644 doc/todo/calendar_with___34__create__34___links/incomplete_patch.pl create mode 100644 doc/todo/call_git-update-server-info_from_post-udpate_hook.mdwn create mode 100644 doc/todo/canonical_feed_location.mdwn create mode 100644 doc/todo/capitalize_title.mdwn create mode 100644 doc/todo/cas_authentication.mdwn create mode 100644 doc/todo/cdate_and_mdate_available_for_templates.mdwn create mode 100644 doc/todo/cgi_hooks_get_session_objects.mdwn create mode 100644 doc/todo/clear_page_to_delete.mdwn create mode 100644 doc/todo/clickable-openid-urls-in-logs.mdwn create mode 100644 doc/todo/color_plugin.mdwn create mode 100644 doc/todo/comment_by_mail.mdwn create mode 100644 doc/todo/comment_by_mail/discussion.mdwn create mode 100644 doc/todo/comment_moderation_feed.mdwn create mode 100644 doc/todo/comments.mdwn create mode 100644 doc/todo/conditional_text_based_on_ikiwiki_features.mdwn create mode 100644 doc/todo/conditional_underlay_files.mdwn create mode 100644 doc/todo/configurable_markdown_path.mdwn create mode 100644 doc/todo/configurable_tidy_command_for_htmltidy.mdwn create mode 100644 doc/todo/configurable_timezones.mdwn create mode 100644 doc/todo/conflict_free_comment_merges.mdwn create mode 100644 doc/todo/consistent_smileys.mdwn create mode 100644 doc/todo/copyright_based_on_pagespec.mdwn create mode 100644 doc/todo/correct_published_and_updated_time_information_for_the_feeds.mdwn create mode 100644 doc/todo/countdown_directive.mdwn create mode 100644 doc/todo/credentials_page.mdwn create mode 100644 doc/todo/ctime_on_blog_post_pages_.mdwn create mode 100644 doc/todo/custom_location_for_openlayers.mdwn create mode 100644 doc/todo/darcs.mdwn create mode 100644 doc/todo/datearchives-plugin.mdwn create mode 100644 doc/todo/default_content_for_new_post.mdwn create mode 100644 doc/todo/default_name_for_new_post.mdwn create mode 100644 doc/todo/dependency_types.mdwn create mode 100644 doc/todo/description_meta_param_passed_to_templates.mdwn create mode 100644 doc/todo/different_search_engine.mdwn create mode 100644 doc/todo/directive_docs.mdwn create mode 100644 doc/todo/discuss_without_login.mdwn create mode 100644 doc/todo/discussion_page_as_blog.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion/Don__39__t_like_foo.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion/Don__39__t_like_foo/how_about_bar.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion/Don__39__t_like_foo/sdf.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion/foo_is_ok.mdwn create mode 100644 doc/todo/discussion_page_as_blog/discussion/castle/discussion/test.mdwn create mode 100644 doc/todo/do_not_make_links_backwards.mdwn create mode 100644 doc/todo/done.mdwn create mode 100644 doc/todo/double-click_protection_for_form_buttons.mdwn create mode 100644 doc/todo/doxygen_support.mdwn create mode 100644 doc/todo/dynamic_rootpage.mdwn create mode 100644 doc/todo/ease_archivepage_styling.mdwn create mode 100644 doc/todo/edit_form:_no_fixed_size_for_textarea.mdwn create mode 100644 doc/todo/edittemplate_should_look_in_templates_directory_by_default.mdwn create mode 100644 doc/todo/edittemplate_should_support_uuid__44___date_variables.mdwn create mode 100644 doc/todo/else_parameter_for_map_plugin.mdwn create mode 100644 doc/todo/enable-htaccess-files.mdwn create mode 100644 doc/todo/enable_arbitrary_markup_for_directives.mdwn create mode 100644 doc/todo/etherpad_support.mdwn create mode 100644 doc/todo/excluding_commit_mails.mdwn create mode 100644 doc/todo/fancypodcast.mdwn create mode 100644 doc/todo/fastcgi_or_modperl_installation_instructions.mdwn create mode 100644 doc/todo/feed_enhancements_for_inline_pages.mdwn create mode 100644 doc/todo/fileupload.mdwn create mode 100644 doc/todo/fileupload/discussion.mdwn create mode 100644 doc/todo/fileupload/soc-proposal.mdwn create mode 100644 doc/todo/fileupload/soc-proposal/discussion.mdwn create mode 100644 doc/todo/filtering_content_when_inlining.mdwn create mode 100644 doc/todo/finer_control_over___60__object___47____62__s.mdwn create mode 100644 doc/todo/firm_up_plugin_interface.mdwn create mode 100644 doc/todo/for_amazon_s3_pre-gzip-encode_safe_files.mdwn create mode 100644 doc/todo/format_escape.mdwn create mode 100644 doc/todo/fortune:_select_options_via_environment.mdwn create mode 100644 doc/todo/friendly_markup_names.mdwn create mode 100644 doc/todo/generated_po_stuff_not_ignored_by_git.mdwn create mode 100644 doc/todo/generic___39__do__61__goto__39___for_CGI.mdwn create mode 100644 doc/todo/generic_insert_links.mdwn create mode 100644 doc/todo/geotagging.mdwn create mode 100644 doc/todo/git-rev-list_requires_relative_path___40__fixes_git_ctime__41__.mdwn create mode 100644 doc/todo/git_attribution.mdwn create mode 100644 doc/todo/git_attribution/discussion.mdwn create mode 100644 doc/todo/git_recentchanges_should_not_show_merges.mdwn create mode 100644 doc/todo/graphviz.mdwn create mode 100644 doc/todo/hard-coded_location_for_man_pages_and_w3m_cgi_wrapper.mdwn create mode 100644 doc/todo/headless_git_branches.mdwn create mode 100644 doc/todo/hidden_links__47__tags.mdwn create mode 100644 doc/todo/hook_to_detect_markdown_links_to_wiki_pages.mdwn create mode 100644 doc/todo/html.mdwn create mode 100644 doc/todo/htmlvalidation.mdwn create mode 100644 doc/todo/htpasswd_mirror_of_the_userdb.mdwn create mode 100644 doc/todo/http_bl_support.mdwn create mode 100644 doc/todo/httpauth_example.mdwn create mode 100644 doc/todo/httpauth_example/discussion.mdwn create mode 100644 doc/todo/httpauth_feature_parity_with_passwordauth.mdwn create mode 100644 doc/todo/hyphenation.mdwn create mode 100644 doc/todo/ikibot.mdwn create mode 100644 doc/todo/improve_globlists.mdwn create mode 100644 doc/todo/improved_mediawiki_support.mdwn create mode 100644 doc/todo/improved_parentlinks_styling.mdwn create mode 100644 doc/todo/index.html_allowed.mdwn create mode 100644 doc/todo/inline:_numerical_ordering_by_title.mdwn create mode 100644 doc/todo/inline_directive_should_support_pagination.mdwn create mode 100644 doc/todo/inline_option_for_pagespec-specific_show__61__N.mdwn create mode 100644 doc/todo/inline_plugin:_ability_to_override_feed_name.mdwn create mode 100644 doc/todo/inline_plugin:_hide_feed_buttons_if_empty.mdwn create mode 100644 doc/todo/inline_plugin:_specifying_ordered_page_names.mdwn create mode 100644 doc/todo/inline_postform_autotitles.mdwn create mode 100644 doc/todo/inline_raw_files.mdwn create mode 100644 doc/todo/inlines_inheriting_links.mdwn create mode 100644 doc/todo/integration_with_Firefox_and_Iceweasel_feed_subscription_mechanism.mdwn create mode 100644 doc/todo/interactive_todo_lists.mdwn create mode 100644 doc/todo/internal_definition_list_support.mdwn create mode 100644 doc/todo/l10n.mdwn create mode 100644 doc/todo/language_definition_for_the_meta_plugin.mdwn create mode 100644 doc/todo/latex.mdwn create mode 100644 doc/todo/latex/discussion.mdwn create mode 100644 doc/todo/let_inline_plugin_use_pagetemplates.mdwn create mode 100644 doc/todo/limit_the_markup_formats_available_for_editing.mdwn create mode 100644 doc/todo/link_map.mdwn create mode 100644 doc/todo/link_plugin_perhaps_too_general__63__.mdwn create mode 100644 doc/todo/linkbase.mdwn create mode 100644 doc/todo/linkify_and_preprocessor_ordering.mdwn create mode 100644 doc/todo/linktitle.mdwn create mode 100644 doc/todo/lists.mdwn create mode 100644 doc/todo/location_of_external_plugins.mdwn create mode 100644 doc/todo/location_of_ikiwiki-w3m.cgi.mdwn create mode 100644 doc/todo/logo.mdwn create mode 100644 doc/todo/lucene_search_engine.mdwn create mode 100644 doc/todo/mailnotification.mdwn create mode 100644 doc/todo/mailnotification/discussion.mdwn create mode 100644 doc/todo/make_html-parser_use_encode_entities_numeric.mdwn create mode 100644 doc/todo/make_link_target_search_all_paths_as_fallback.mdwn create mode 100644 doc/todo/manpages.mdwn create mode 100644 doc/todo/mark_edit_as_trivial__44___identify__47__filter_on_trivial_changes.mdwn create mode 100644 doc/todo/matching_different_kinds_of_links.mdwn create mode 100644 doc/todo/mbox.mdwn create mode 100644 doc/todo/mdwn_itex.mdwn create mode 100644 doc/todo/mdwn_preview.mdwn create mode 100644 doc/todo/mdwn_preview/discussion.mdwn create mode 100644 doc/todo/mercurial.mdwn create mode 100644 doc/todo/mercurial/discussion.mdwn create mode 100644 doc/todo/meta_rcsid.mdwn create mode 100644 doc/todo/metadata.mdwn create mode 100644 doc/todo/minor_adjustment_to_setup_documentation_for_recentchanges_feeds.mdwn create mode 100644 doc/todo/mirrorlist_with_per-mirror_usedirs_settings.mdwn create mode 100644 doc/todo/missingparents.pm.mdwn create mode 100644 doc/todo/modify_page_filename_in_plugin.mdwn create mode 100644 doc/todo/monochrome_theme.mdwn create mode 100644 doc/todo/more_class__61____34____34___for_css.mdwn create mode 100644 doc/todo/more_customisable_titlepage_function.mdwn create mode 100644 doc/todo/more_flexible_inline_postform.mdwn create mode 100644 doc/todo/mtime.mdwn create mode 100644 doc/todo/multi-thread_ikiwiki.mdwn create mode 100644 doc/todo/multiple_output_formats.mdwn create mode 100644 doc/todo/multiple_repository_support.mdwn create mode 100644 doc/todo/multiple_simultaneous_rcs.mdwn create mode 100644 doc/todo/multiple_simultaneous_rcs/discussion.mdwn create mode 100644 doc/todo/multiple_template_directories.mdwn create mode 100644 doc/todo/multiple_templates.mdwn create mode 100644 doc/todo/natural_sorting.mdwn create mode 100644 doc/todo/need_global_renamepage_hook.mdwn create mode 100644 doc/todo/nested_preprocessor_directives.mdwn create mode 100644 doc/todo/online_configuration.mdwn create mode 100644 doc/todo/openid_enable_cache.mdwn create mode 100644 doc/todo/openid_user_filtering.mdwn create mode 100644 doc/todo/optimisations.mdwn create mode 100644 doc/todo/optimize_simple_dependencies.mdwn create mode 100644 doc/todo/optional_underlaydir_prefix.mdwn create mode 100644 doc/todo/org_mode.mdwn create mode 100644 doc/todo/org_mode/Discussion.mdwn create mode 100644 doc/todo/osm__95__optimisations__95__and__95__fixes.mdwn create mode 100644 doc/todo/osm_arbitrary_layers.mdwn create mode 100644 doc/todo/overriding_displayed_modification_time.mdwn create mode 100644 doc/todo/page_edit_disable.mdwn create mode 100644 doc/todo/pagedeletion.mdwn create mode 100644 doc/todo/pagedown_plugin.mdwn create mode 100644 doc/todo/pageindexes.mdwn create mode 100644 doc/todo/pagespec_aliases.mdwn create mode 100644 doc/todo/pagespec_aliases/discussion.mdwn create mode 100644 doc/todo/pagespec_expansions.mdwn create mode 100644 doc/todo/pagespec_relative_to_a_target.mdwn create mode 100644 doc/todo/pagespec_to_disable_ikiwiki_directives.mdwn create mode 100644 doc/todo/pagestats_among_a_subset_of_pages.mdwn create mode 100644 doc/todo/pal_plugin.mdwn create mode 100644 doc/todo/parse_debian_packages.mdwn create mode 100644 doc/todo/passwordauth:_sendmail_interface.mdwn create mode 100644 doc/todo/paste_plugin.mdwn create mode 100644 doc/todo/pastebin.mdwn create mode 100644 doc/todo/pdf_output.mdwn create mode 100644 doc/todo/pdfshare_plugin.mdwn create mode 100644 doc/todo/pedigree_plugin.mdwn create mode 100644 doc/todo/per_page_ACLs.mdwn create mode 100644 doc/todo/pingback_support.mdwn create mode 100644 doc/todo/please_add_some_table_styles.mdwn create mode 100644 doc/todo/pluggablerenderers.mdwn create mode 100644 doc/todo/plugin.mdwn create mode 100644 doc/todo/plugin_data_storage.mdwn create mode 100644 doc/todo/plugin_dependency_calulation.mdwn create mode 100644 doc/todo/po:_add_lang_name_and_code_template_variables.mdwn create mode 100644 doc/todo/po:_avoid_rebuilding_to_fix_meta_titles.mdwn create mode 100644 doc/todo/po:_better_documentation.mdwn create mode 100644 doc/todo/po:_better_links.mdwn create mode 100644 doc/todo/po:_better_translation_interface.mdwn create mode 100644 doc/todo/po:_remove_po_files_when_disabling_plugin.mdwn create mode 100644 doc/todo/po:_rethink_pagespecs.mdwn create mode 100644 doc/todo/po:_should_cleanup_.pot_files.mdwn create mode 100644 doc/todo/po:_transifex_integration.mdwn create mode 100644 doc/todo/po:_translation_of_directives.mdwn create mode 100644 doc/todo/po_needstranslation_pagespec.mdwn create mode 100644 doc/todo/preprocessor_directive_for_proposed_changes.mdwn create mode 100644 doc/todo/pretty-print_OpenIDs_even_if_not_enabled.mdwn create mode 100644 doc/todo/preview_changes.mdwn create mode 100644 doc/todo/preview_changes_before_git_commit.mdwn create mode 100644 doc/todo/progressbar_plugin.mdwn create mode 100644 doc/todo/provide_a_mailing_list.mdwn create mode 100644 doc/todo/provide_inline_diffs_in_recentchanges.mdwn create mode 100644 doc/todo/provide_sha1_for_git_diffurl.mdwn create mode 100644 doc/todo/publishing_in_the_future.mdwn create mode 100644 doc/todo/quieten-bzr.mdwn create mode 100644 doc/todo/rcs.mdwn create mode 100644 doc/todo/rcs__95__diff_implementation_for_Mercurial_backend__44___based_on_Git_backend.mdwn create mode 100644 doc/todo/rcs__95__get__123__c__44__m__125__time_implementation_for_Mercurial_backend__44___based_on_Git_backend.mdwn create mode 100644 doc/todo/rcs_updates_needed.mdwn create mode 100644 doc/todo/recentchanges.mdwn create mode 100644 doc/todo/recentchanges_feed_with_comment.mdwn create mode 100644 doc/todo/recentchanges_path.mdwn create mode 100644 doc/todo/recommend_libtext-markdown-discount_instead_of_depending.mdwn create mode 100644 doc/todo/redirect_automatically_after_rename.mdwn create mode 100644 doc/todo/refreshing_recentchanges_page.mdwn create mode 100644 doc/todo/rel__61__nofollow_on_external_links.mdwn create mode 100644 doc/todo/rel_attribute_for_links.mdwn create mode 100644 doc/todo/relative_pagespec_deficiency.mdwn create mode 100644 doc/todo/remove_basewiki_redir_pages.mdwn create mode 100644 doc/todo/replace_HTML::Template_with_Template_Toolkit.mdwn create mode 100644 doc/todo/require_CAPTCHA_to_edit.mdwn create mode 100644 doc/todo/review_mechanism.mdwn create mode 100644 doc/todo/rewrite_ikiwiki_in_haskell.mdwn create mode 100644 doc/todo/rewrite_ikiwiki_in_haskell/discussion.mdwn create mode 100644 doc/todo/rss_title_description.mdwn create mode 100644 doc/todo/rst_plugin_python_rewrite.mdwn create mode 100644 doc/todo/salmon_protocol_for_comment_sharing.mdwn create mode 100644 doc/todo/search.mdwn create mode 100644 doc/todo/search_terms.mdwn create mode 100644 doc/todo/section-numbering.mdwn create mode 100644 doc/todo/selective_more_directive.mdwn create mode 100644 doc/todo/shortcut_link_text.mdwn create mode 100644 doc/todo/shortcut_optional_parameters.mdwn create mode 100644 doc/todo/shortcut_with_different_link_text.mdwn create mode 100644 doc/todo/shortcut_with_no_url_parameter__44___only_desc.mdwn create mode 100644 doc/todo/should_optimise_pagespecs.mdwn create mode 100644 doc/todo/should_use_a_standard_encoding_for_utf_chars_in_filenames.mdwn create mode 100644 doc/todo/sigs.mdwn create mode 100644 doc/todo/sigs/discussion.mdwn create mode 100644 doc/todo/simple_text_parsing_or_regex_in_template_or_shortcut.mdwn create mode 100644 doc/todo/skip_option_for_inline_plugin.mdwn create mode 100644 doc/todo/smarter_sorting.mdwn create mode 100644 doc/todo/smileys_do_not_work_in_PreprocessorDirective_arguments.mdwn create mode 100644 doc/todo/softlinks.mdwn create mode 100644 doc/todo/sort_parameter_for_map_plugin_and_directive.mdwn create mode 100644 doc/todo/sort_parameter_for_map_plugin_and_directive/incomplete_patch.pl.pl create mode 100644 doc/todo/sort_parameter_for_map_plugin_and_directive/python_algorithms.py create mode 100644 doc/todo/sortable_tables.mdwn create mode 100644 doc/todo/sortbylastcomment_plugin.mdwn create mode 100644 doc/todo/sorting_by_path.mdwn create mode 100644 doc/todo/source_link.mdwn create mode 100644 doc/todo/spell_check_plug-in.mdwn create mode 100644 doc/todo/strftime.mdwn create mode 100644 doc/todo/structured_page_data.mdwn create mode 100644 doc/todo/structured_page_data/discussion.mdwn create mode 100644 doc/todo/stylesheet_suggestion_for_verbatim_content.mdwn create mode 100644 doc/todo/submodule_support.mdwn create mode 100644 doc/todo/support_creole_markup.mdwn create mode 100644 doc/todo/support_dicts_in_setup.mdwn create mode 100644 doc/todo/support_for_SDF_documents.mdwn create mode 100644 doc/todo/support_for_plugins_written_in_other_languages.mdwn create mode 100644 doc/todo/support_includes_in_setup_files.mdwn create mode 100644 doc/todo/support_link__40__.__41___in_pagespec.mdwn create mode 100644 doc/todo/support_multiple_perl_libraries.mdwn create mode 100644 doc/todo/supporting_comments_via_disussion_pages.mdwn create mode 100644 doc/todo/svg.mdwn create mode 100644 doc/todo/syntax_highlighting.mdwn create mode 100644 doc/todo/syntax_highlighting/discussion.mdwn create mode 100644 doc/todo/syslog_should_show_wiki_name.mdwn create mode 100644 doc/todo/table_with_header_column.mdwn create mode 100644 doc/todo/tag_pagespec_function.mdwn create mode 100644 doc/todo/tagging_with_a_publication_date.mdwn create mode 100644 doc/todo/tags.mdwn create mode 100644 doc/todo/target_filter_for_brokenlinks.mdwn create mode 100644 doc/todo/terminalclient.mdwn create mode 100644 doc/todo/test_coverage.mdwn create mode 100644 doc/todo/themes_should_ship_with_templates.mdwn create mode 100644 doc/todo/tidy_git__39__s_ctime_debug_output.mdwn create mode 100644 doc/todo/tla.mdwn create mode 100644 doc/todo/tmplvars_plugin.mdwn create mode 100644 doc/todo/tmplvars_plugin/discussion.mdwn create mode 100644 doc/todo/toc-with-human-readable-anchors.mdwn create mode 100644 doc/todo/toc_plugin:_set_a_header_ceiling___40__opposite_of_levels__61____41__.mdwn create mode 100644 doc/todo/toc_plugin_to_skip_one_level.mdwn create mode 100644 doc/todo/toggle_initial_state.mdwn create mode 100644 doc/todo/toplevel_index.mdwn create mode 100644 doc/todo/tracking_bugs_with_dependencies.mdwn create mode 100644 doc/todo/transient_pages.mdwn create mode 100644 doc/todo/translation_links.mdwn create mode 100644 doc/todo/turn_edittemplate_verbosity_off_by_default.mdwn create mode 100644 doc/todo/two-way_convert_of_wikis.mdwn create mode 100644 doc/todo/typography_plugin_configuration.mdwn create mode 100644 doc/todo/unaccent_url_instead_of_encoding.mdwn create mode 100644 doc/todo/underlay.mdwn create mode 100644 doc/todo/unified_temporary_file__47__directory_handling.mdwn create mode 100644 doc/todo/untrusted_git_push_hooks.mdwn create mode 100644 doc/todo/upgradehooks.mdwn create mode 100644 doc/todo/use_secure_cookies_for_ssl_logins.mdwn create mode 100644 doc/todo/use_templates_for_the_img_plugin.mdwn create mode 100644 doc/todo/usedirs__95__redir_proposed_additional_module.mdwn create mode 100644 doc/todo/user-defined_templates_outside_the_wiki.mdwn create mode 100644 doc/todo/user-subdir_mechanism_like_etc_ikiwiki_wikilist.mdwn create mode 100644 doc/todo/userdir_links.mdwn create mode 100644 doc/todo/utf8.mdwn create mode 100644 doc/todo/varioki_--_add_template_variables___40__with_closures_for_values__41___in_ikiwiki.setup.mdwn create mode 100644 doc/todo/want_to_avoid_ikiwiki_using_http_or_https_in_urls_to_allow_serving_both.mdwn create mode 100644 doc/todo/wanted_pages_plugin.mdwn create mode 100644 doc/todo/wdiffs_in_recentchanges.mdwn create mode 100644 doc/todo/web-based_image_editing.mdwn create mode 100644 doc/todo/web_gui_for_managing_tags.mdwn create mode 100644 doc/todo/web_reversion.mdwn create mode 100644 doc/todo/websetup_should_link_to_plugin_descriptions.mdwn create mode 100644 doc/todo/wiki-formatted_comments_with_syntax_plugin.mdwn create mode 100644 doc/todo/wikilink_titles.mdwn create mode 100644 doc/todo/wikilinkfeatures.mdwn create mode 100644 doc/todo/wikitrails.mdwn create mode 100644 doc/todo/wikitrails/discussion.mdwn create mode 100644 doc/todo/wikiwyg.mdwn create mode 100644 doc/todo/wikiwyg/discussion.mdwn create mode 100644 doc/todo/wmd_editor_live_preview.mdwn create mode 100644 doc/todo/wrapperuser.mdwn create mode 100644 doc/translation.mdwn create mode 100644 doc/translation/discussion.mdwn create mode 100644 doc/usage.mdwn create mode 100644 doc/usage/discussion.mdwn create mode 100644 doc/users.mdwn create mode 100644 doc/users/BerndZeimetz.mdwn create mode 100644 doc/users/Christine_Spang.mdwn create mode 100644 doc/users/DamianSmall.mdwn create mode 100644 doc/users/Daniel_Andersson.mdwn create mode 100644 doc/users/DavidBremner.mdwn create mode 100644 doc/users/David_Riebenbauer.mdwn create mode 100644 doc/users/Edward_Betts.mdwn create mode 100644 doc/users/Erkan_Yilmaz.mdwn create mode 100644 doc/users/Gianpaolo_Macario.mdwn create mode 100644 doc/users/GiuseppeBilotta.mdwn create mode 100644 doc/users/HenrikBrixAndersen.mdwn create mode 100644 doc/users/Jamie.mdwn create mode 100644 doc/users/JeremieKoenig.mdwn create mode 100644 doc/users/Jimmy_Tang.mdwn create mode 100644 doc/users/JoshBBall.mdwn create mode 100644 doc/users/Kai_Hendry.mdwn create mode 100644 doc/users/KarlMW.mdwn create mode 100644 doc/users/KarlMW/discussion.mdwn create mode 100644 doc/users/KathrynAndersen.mdwn create mode 100644 doc/users/KathrynAndersen/discussion.mdwn create mode 100644 doc/users/Larry_Clapp.mdwn create mode 100644 doc/users/LucaCapello.mdwn create mode 100644 doc/users/MatthiasIhrke.mdwn create mode 100644 doc/users/Mick_Pollard.mdwn create mode 100644 doc/users/NeilSmithline.mdwn create mode 100644 doc/users/NicolasLimare.mdwn create mode 100644 doc/users/Oblomov.mdwn create mode 100644 doc/users/Olea.mdwn create mode 100644 doc/users/OscarMorante.mdwn create mode 100644 doc/users/Perry.mdwn create mode 100644 doc/users/Ramsey.mdwn create mode 100644 doc/users/Remy.mdwn create mode 100644 doc/users/RickOwens.mdwn create mode 100644 doc/users/Simon_Michael.mdwn create mode 100644 doc/users/Stefano_Zacchiroli.mdwn create mode 100644 doc/users/StevenBlack.mdwn create mode 100644 doc/users/TaylorKillian.mdwn create mode 100644 doc/users/TaylorKillian/discussion.mdwn create mode 100644 doc/users/The_TOVA_Company.mdwn create mode 100644 doc/users/TimBosse.mdwn create mode 100644 doc/users/Tim_Lavoie.mdwn create mode 100644 doc/users/Will.mdwn create mode 100644 doc/users/acathur.mdwn create mode 100644 doc/users/adamshand.mdwn create mode 100644 doc/users/ajt.mdwn create mode 100644 doc/users/aland.mdwn create mode 100644 doc/users/alexander.mdwn create mode 100644 doc/users/alexandredupas.mdwn create mode 100644 doc/users/anarcat.mdwn create mode 100644 doc/users/anarcat.wiki create mode 100644 doc/users/arpitjain.mdwn create mode 100644 doc/users/bartmassey.mdwn create mode 100644 doc/users/bbb.mdwn create mode 100644 doc/users/blipvert.mdwn create mode 100644 doc/users/bstpierre.mdwn create mode 100644 doc/users/cfm.mdwn create mode 100644 doc/users/chris.mdwn create mode 100644 doc/users/chrismgray.mdwn create mode 100644 doc/users/chrysn.mdwn create mode 100644 doc/users/cord.mdwn create mode 100644 doc/users/cstamas.mdwn create mode 100644 doc/users/dark.mdwn create mode 100644 doc/users/dato.mdwn create mode 100644 doc/users/dirk.mdwn create mode 100644 doc/users/dom.mdwn create mode 100644 doc/users/donmarti.mdwn create mode 100644 doc/users/emptty.mdwn create mode 100644 doc/users/ericdrechsel.mdwn create mode 100644 doc/users/fil.mdwn create mode 100644 doc/users/fmarier.mdwn create mode 100644 doc/users/harishcm.mdwn create mode 100644 doc/users/harningt.mdwn create mode 100644 doc/users/hb.mdwn create mode 100644 doc/users/hb/discussion.mdwn create mode 100644 doc/users/hendry.mdwn create mode 100644 doc/users/intrigeri.mdwn create mode 100644 doc/users/iustin.mdwn create mode 100644 doc/users/ivan_shmakov.mdwn create mode 100644 doc/users/jasonblevins.mdwn create mode 100644 doc/users/jasonriedy.mdwn create mode 100644 doc/users/jaywalk.mdwn create mode 100644 doc/users/jcorneli.mdwn create mode 100644 doc/users/jeanprivat.mdwn create mode 100644 doc/users/jelmer.mdwn create mode 100644 doc/users/jeremyreed.mdwn create mode 100644 doc/users/jerojasro.mdwn create mode 100644 doc/users/jmtd.mdwn create mode 100644 doc/users/joey.mdwn create mode 100644 doc/users/jogo.mdwn create mode 100644 doc/users/jon.mdwn create mode 100644 doc/users/jonassmedegaard.mdwn create mode 100644 doc/users/josephturian.mdwn create mode 100644 doc/users/joshtriplett.mdwn create mode 100644 doc/users/joshtriplett/discussion.mdwn create mode 100644 doc/users/jrblevin.mdwn create mode 100644 doc/users/justint.mdwn create mode 100644 doc/users/jwalzer.mdwn create mode 100644 doc/users/kyle.mdwn create mode 100644 doc/users/madduck.mdwn create mode 100644 doc/users/marcelomagallon.mdwn create mode 100644 doc/users/mathdesc.mdwn create mode 100644 doc/users/michaelrasmussen.wiki create mode 100644 doc/users/neale.mdwn create mode 100644 doc/users/nil.mdwn create mode 100644 doc/users/nolan.mdwn create mode 100644 doc/users/patrickwinnertz.mdwn create mode 100644 doc/users/pdurbin.mdwn create mode 100644 doc/users/pelle.mdwn create mode 100644 doc/users/perolofsson.mdwn create mode 100644 doc/users/peteg.mdwn create mode 100644 doc/users/peter_woodman.mdwn create mode 100644 doc/users/ptecza.mdwn create mode 100644 doc/users/rubykat.mdwn create mode 100644 doc/users/sabr.mdwn create mode 100644 doc/users/sabr/sub1.mdwn create mode 100644 doc/users/sabr/sub2.mdwn create mode 100644 doc/users/schmonz-web-ikiwiki.mdwn create mode 100644 doc/users/schmonz.mdwn create mode 100644 doc/users/seanh.mdwn create mode 100644 doc/users/simonraven.mdwn create mode 100644 doc/users/smcv.mdwn create mode 100644 doc/users/smcv/gallery.mdwn create mode 100644 doc/users/smcv/gallery/discussion.mdwn create mode 100644 doc/users/solofo.mdwn create mode 100644 doc/users/sphynkx.mdwn create mode 100644 doc/users/sunny256.mdwn create mode 100644 doc/users/svend.mdwn create mode 100644 doc/users/tbm.mdwn create mode 100644 doc/users/tjgolubi.mdwn create mode 100644 doc/users/tschwinge.mdwn create mode 100644 doc/users/ttw.mdwn create mode 100644 doc/users/tupyakov_vladimir.mdwn create mode 100644 doc/users/tychoish.mdwn create mode 100644 doc/users/ulrik.mdwn create mode 100644 doc/users/undx.mdwn create mode 100644 doc/users/victormoral.mdwn create mode 100644 doc/users/weakish.mdwn create mode 100644 doc/users/weakishjiang.mdwn create mode 100644 doc/users/wentasah.mdwn create mode 100644 doc/users/wiebel.mdwn create mode 100644 doc/users/wtk.mdwn create mode 100644 doc/users/xma/discussion.mdwn create mode 100644 doc/users/xtaran.mdwn create mode 100644 doc/users/yds.mdwn create mode 100644 doc/w3mmode.mdwn create mode 100644 doc/w3mmode/ikiwiki.setup create mode 100644 doc/whyikiwiki.mdwn create mode 100644 doc/wikiicons/diff.png create mode 100644 doc/wikiicons/openidlogin-bg.gif create mode 100644 doc/wikiicons/revert.png create mode 100644 doc/wikiicons/search-bg.gif create mode 100644 doc/wishlist.mdwn create mode 100644 doc/wishlist/watched_pages.mdwn create mode 100644 docwiki.setup create mode 100755 gitremotes create mode 100755 ikiwiki-calendar.in create mode 100755 ikiwiki-makerepo create mode 100755 ikiwiki-mass-rebuild create mode 100755 ikiwiki-transition.in create mode 100755 ikiwiki-update-wikilist create mode 100755 ikiwiki-w3m.cgi create mode 100755 ikiwiki.in create mode 100644 ikiwiki.spec create mode 100755 mdwn2man create mode 100755 plugins/externaldemo create mode 100755 plugins/proxy.py create mode 100755 plugins/pythondemo create mode 100755 plugins/rst create mode 100755 pm_filter create mode 100644 po/Makefile create mode 100644 po/bg.po create mode 100644 po/cs.po create mode 100644 po/da.po create mode 100644 po/de.po create mode 100644 po/es.po create mode 100644 po/fr.po create mode 100644 po/gu.po create mode 100644 po/ikiwiki.pot create mode 100644 po/it.po create mode 100644 po/pl.po create mode 100755 po/po2wiki create mode 100644 po/sv.po create mode 100644 po/tr.po create mode 100644 po/underlay.setup create mode 100644 po/underlays/basewiki/ikiwiki.cs.po create mode 100644 po/underlays/basewiki/ikiwiki.da.po create mode 100644 po/underlays/basewiki/ikiwiki.de.po create mode 100644 po/underlays/basewiki/ikiwiki.es.po create mode 100644 po/underlays/basewiki/ikiwiki.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/directive.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/directive.da.po create mode 100644 po/underlays/basewiki/ikiwiki/directive.de.po create mode 100644 po/underlays/basewiki/ikiwiki/directive.es.po create mode 100644 po/underlays/basewiki/ikiwiki/directive.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/formatting.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/formatting.da.po create mode 100644 po/underlays/basewiki/ikiwiki/formatting.de.po create mode 100644 po/underlays/basewiki/ikiwiki/formatting.es.po create mode 100644 po/underlays/basewiki/ikiwiki/formatting.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/markdown.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/markdown.da.po create mode 100644 po/underlays/basewiki/ikiwiki/markdown.de.po create mode 100644 po/underlays/basewiki/ikiwiki/markdown.es.po create mode 100644 po/underlays/basewiki/ikiwiki/markdown.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/openid.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/openid.da.po create mode 100644 po/underlays/basewiki/ikiwiki/openid.de.po create mode 100644 po/underlays/basewiki/ikiwiki/openid.es.po create mode 100644 po/underlays/basewiki/ikiwiki/openid.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec.da.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec.de.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec.es.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/attachment.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/attachment.da.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/attachment.de.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/attachment.es.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/attachment.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/po.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/po.da.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/po.de.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/po.es.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/po.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/pagespec/sorting.de.po create mode 100644 po/underlays/basewiki/ikiwiki/searching.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/searching.da.po create mode 100644 po/underlays/basewiki/ikiwiki/searching.de.po create mode 100644 po/underlays/basewiki/ikiwiki/searching.es.po create mode 100644 po/underlays/basewiki/ikiwiki/searching.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage.da.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage.de.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage.es.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage/linkingrules.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage/linkingrules.da.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage/linkingrules.de.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage/linkingrules.es.po create mode 100644 po/underlays/basewiki/ikiwiki/subpage/linkingrules.fr.po create mode 100644 po/underlays/basewiki/ikiwiki/wikilink.cs.po create mode 100644 po/underlays/basewiki/ikiwiki/wikilink.da.po create mode 100644 po/underlays/basewiki/ikiwiki/wikilink.de.po create mode 100644 po/underlays/basewiki/ikiwiki/wikilink.es.po create mode 100644 po/underlays/basewiki/ikiwiki/wikilink.fr.po create mode 100644 po/underlays/basewiki/index.cs.po create mode 100644 po/underlays/basewiki/index.da.po create mode 100644 po/underlays/basewiki/index.de.po create mode 100644 po/underlays/basewiki/index.es.po create mode 100644 po/underlays/basewiki/index.fr.po create mode 100644 po/underlays/basewiki/recentchanges.cs.po create mode 100644 po/underlays/basewiki/recentchanges.da.po create mode 100644 po/underlays/basewiki/recentchanges.de.po create mode 100644 po/underlays/basewiki/recentchanges.es.po create mode 100644 po/underlays/basewiki/recentchanges.fr.po create mode 100644 po/underlays/basewiki/sandbox.cs.po create mode 100644 po/underlays/basewiki/sandbox.da.po create mode 100644 po/underlays/basewiki/sandbox.de.po create mode 100644 po/underlays/basewiki/sandbox.es.po create mode 100644 po/underlays/basewiki/sandbox.fr.po create mode 100644 po/underlays/basewiki/shortcuts.cs.po create mode 100644 po/underlays/basewiki/shortcuts.da.po create mode 100644 po/underlays/basewiki/shortcuts.de.po create mode 100644 po/underlays/basewiki/shortcuts.es.po create mode 100644 po/underlays/basewiki/shortcuts.fr.po create mode 100644 po/underlays/basewiki/templates.cs.po create mode 100644 po/underlays/basewiki/templates.da.po create mode 100644 po/underlays/basewiki/templates.de.po create mode 100644 po/underlays/basewiki/templates.es.po create mode 100644 po/underlays/basewiki/templates.fr.po create mode 100644 po/underlays/basewiki/templates/note.cs.po create mode 100644 po/underlays/basewiki/templates/note.da.po create mode 100644 po/underlays/basewiki/templates/note.de.po create mode 100644 po/underlays/basewiki/templates/note.es.po create mode 100644 po/underlays/basewiki/templates/note.fr.po create mode 100644 po/underlays/basewiki/templates/popup.cs.po create mode 100644 po/underlays/basewiki/templates/popup.da.po create mode 100644 po/underlays/basewiki/templates/popup.de.po create mode 100644 po/underlays/basewiki/templates/popup.es.po create mode 100644 po/underlays/basewiki/templates/popup.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/aggregate.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/aggregate.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/aggregate.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/brokenlinks.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/brokenlinks.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/brokenlinks.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/calendar.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/calendar.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/calendar.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/color.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/color.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/color.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/comment.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/comment.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/comment.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/copy.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/copy.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/copy.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/cut.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/cut.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/cut.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/cutpaste.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/cutpaste.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/cutpaste.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/date.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/edittemplate.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/edittemplate.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/edittemplate.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/format.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/format.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/format.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/fortune.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/fortune.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/fortune.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/graph.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/graph.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/graph.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/haiku.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/haiku.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/haiku.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/if.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/if.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/if.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/img.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/img.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/img.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/inline.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/inline.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/inline.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/linkmap.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/linkmap.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/linkmap.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/listdirectives.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/listdirectives.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/listdirectives.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/map.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/map.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/map.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/meta.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/meta.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/meta.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/more.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/more.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/more.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/orphans.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/orphans.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/orphans.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagecount.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagecount.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagecount.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagestats.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagestats.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagestats.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagetemplate.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagetemplate.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/pagetemplate.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/paste.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/paste.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/paste.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/ping.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/ping.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/ping.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/poll.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/poll.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/poll.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/polygen.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/polygen.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/polygen.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/postsparkline.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/postsparkline.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/postsparkline.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/progress.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/progress.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/progress.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/shortcut.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/shortcut.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/shortcut.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/sparkline.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/sparkline.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/sparkline.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/table.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/table.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/table.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/tag.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/tag.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/tag.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/taglink.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/taglink.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/taglink.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/template.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/template.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/template.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/testpagespec.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/testpagespec.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/testpagespec.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/teximg.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/teximg.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/teximg.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/toc.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/toc.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/toc.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggle.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggle.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggle.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggleable.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggleable.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/toggleable.fr.po create mode 100644 po/underlays/directives/ikiwiki/directive/version.da.po create mode 100644 po/underlays/directives/ikiwiki/directive/version.de.po create mode 100644 po/underlays/directives/ikiwiki/directive/version.fr.po create mode 100644 po/underlays/smiley/smileys.da.po create mode 100644 po/underlays/smiley/smileys.de.po create mode 100644 po/underlays/smiley/smileys.fr.po create mode 100644 po/vi.po create mode 100755 t/404.t create mode 100755 t/add_depends.t create mode 100755 t/autoindex.t create mode 100755 t/basename.t create mode 100755 t/basewiki_brokenlinks.t create mode 100644 t/basewiki_brokenlinks/index.mdwn create mode 100755 t/bazaar.t create mode 100755 t/beautify_urlpath.t create mode 100755 t/bestlink.t create mode 100755 t/calculate_changed_links.t create mode 100755 t/cmp_path.t create mode 100755 t/comments.t create mode 100755 t/conflicts.t create mode 100755 t/crazy-badass-perl-bug.t create mode 100755 t/cvs.t create mode 100755 t/dirname.t create mode 100755 t/file_pruned.t create mode 100755 t/find_src_files.t create mode 100755 t/git.t create mode 100755 t/html.t create mode 100755 t/htmlbalance.t create mode 100755 t/htmlize.t create mode 100755 t/index.t create mode 100755 t/linkify.t create mode 100755 t/linkpage.t create mode 100755 t/map.t create mode 100755 t/mercurial.t create mode 100755 t/openiduser.t create mode 100755 t/pagename.t create mode 100755 t/pagespec_match.t create mode 100755 t/pagespec_match_list.t create mode 100755 t/pagespec_match_result.t create mode 100755 t/pagetitle.t create mode 100755 t/parentlinks.t create mode 100644 t/parentlinks/templates/parentlinks.tmpl create mode 100755 t/permalink.t create mode 100755 t/po.t create mode 100755 t/preprocess.t create mode 100755 t/prune.t create mode 100755 t/readfile.t create mode 100755 t/renamepage.t create mode 100755 t/rssurls.t create mode 100755 t/rst.t create mode 100755 t/svn.t create mode 100755 t/syntax.t create mode 100755 t/tag.t create mode 100755 t/template_syntax.t create mode 100755 t/templates_documented.t create mode 100644 t/test1.mdwn create mode 100644 t/test2.mdwn create mode 100644 t/test3.mdwn create mode 100644 t/tinyblog/index.mdwn create mode 100644 t/tinyblog/post.mdwn create mode 100755 t/titlepage.t create mode 100755 t/trail.t create mode 100755 t/urlto.t create mode 100755 t/yesno.t create mode 100644 templates/aggregatepost.tmpl create mode 100644 templates/archivepage.tmpl create mode 100644 templates/atomitem.tmpl create mode 100644 templates/atompage.tmpl create mode 100644 templates/autoindex.tmpl create mode 100644 templates/autotag.tmpl create mode 100644 templates/blogpost.tmpl create mode 100644 templates/calendarmonth.tmpl create mode 100644 templates/calendaryear.tmpl create mode 100644 templates/change.tmpl create mode 100644 templates/comment.tmpl create mode 100644 templates/commentmoderation.tmpl create mode 100644 templates/editcomment.tmpl create mode 100644 templates/editconflict.tmpl create mode 100644 templates/editcreationconflict.tmpl create mode 100644 templates/editfailedsave.tmpl create mode 100644 templates/editpage.tmpl create mode 100644 templates/editpagegone.tmpl create mode 100644 templates/feedlink.tmpl create mode 100644 templates/googleform.tmpl create mode 100644 templates/inlinepage.tmpl create mode 100644 templates/microblog.tmpl create mode 100644 templates/notifyemail.tmpl create mode 100644 templates/openid-selector.tmpl create mode 100644 templates/page.tmpl create mode 100644 templates/passwordmail.tmpl create mode 100644 templates/pocreatepage.tmpl create mode 100644 templates/recentchanges.tmpl create mode 100644 templates/renamesummary.tmpl create mode 100644 templates/revert.tmpl create mode 100644 templates/rssitem.tmpl create mode 100644 templates/rsspage.tmpl create mode 100644 templates/searchform.tmpl create mode 100644 templates/searchquery.tmpl create mode 100644 templates/titlepage.tmpl create mode 100644 templates/trails.tmpl create mode 100644 themes/actiontabs/style.css create mode 100644 themes/blueview/background_darkness.png create mode 100644 themes/blueview/header_background.png create mode 100644 themes/blueview/style.css create mode 100644 themes/goldtype/background_darkness.png create mode 120000 themes/goldtype/base.css create mode 100644 themes/goldtype/header_background.png create mode 100644 themes/goldtype/style.css create mode 100644 themes/monochrome/gradient.png create mode 100644 themes/monochrome/style.css create mode 100644 underlays/attachment/ikiwiki/images/pbar-ani.gif create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 underlays/attachment/ikiwiki/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 underlays/attachment/ikiwiki/images/ui-icons_222222_256x240.png create mode 100644 underlays/attachment/ikiwiki/images/ui-icons_2e83ff_256x240.png create mode 100644 underlays/attachment/ikiwiki/images/ui-icons_454545_256x240.png create mode 100644 underlays/attachment/ikiwiki/images/ui-icons_888888_256x240.png create mode 100644 underlays/attachment/ikiwiki/images/ui-icons_cd0a0a_256x240.png create mode 100644 underlays/attachment/ikiwiki/jquery-ui.full.css create mode 100644 underlays/attachment/ikiwiki/jquery-ui.full.js create mode 100644 underlays/attachment/ikiwiki/jquery-ui.min.css create mode 100644 underlays/attachment/ikiwiki/jquery-ui.min.js create mode 100644 underlays/attachment/ikiwiki/jquery.fileupload-ui.js create mode 100644 underlays/attachment/ikiwiki/jquery.fileupload.js create mode 100644 underlays/attachment/ikiwiki/jquery.iframe-transport.js create mode 100644 underlays/attachment/ikiwiki/jquery.tmpl.full.js create mode 100644 underlays/attachment/ikiwiki/jquery.tmpl.min.js create mode 120000 underlays/basewiki/favicon.ico create mode 120000 underlays/basewiki/ikiwiki.mdwn create mode 120000 underlays/basewiki/ikiwiki/directive.mdwn create mode 120000 underlays/basewiki/ikiwiki/formatting.mdwn create mode 120000 underlays/basewiki/ikiwiki/markdown.mdwn create mode 120000 underlays/basewiki/ikiwiki/openid.mdwn create mode 120000 underlays/basewiki/ikiwiki/pagespec.mdwn create mode 120000 underlays/basewiki/ikiwiki/pagespec/attachment.mdwn create mode 120000 underlays/basewiki/ikiwiki/pagespec/po.mdwn create mode 120000 underlays/basewiki/ikiwiki/pagespec/sorting.mdwn create mode 120000 underlays/basewiki/ikiwiki/searching.mdwn create mode 120000 underlays/basewiki/ikiwiki/subpage.mdwn create mode 120000 underlays/basewiki/ikiwiki/subpage/linkingrules.mdwn create mode 120000 underlays/basewiki/ikiwiki/wikilink.mdwn create mode 120000 underlays/basewiki/index.mdwn create mode 120000 underlays/basewiki/local.css create mode 120000 underlays/basewiki/recentchanges.mdwn create mode 120000 underlays/basewiki/sandbox.mdwn create mode 120000 underlays/basewiki/shortcuts.mdwn create mode 120000 underlays/basewiki/style.css create mode 120000 underlays/basewiki/templates.mdwn create mode 120000 underlays/basewiki/templates/note.mdwn create mode 120000 underlays/basewiki/templates/popup.mdwn create mode 120000 underlays/basewiki/wikiicons create mode 100644 underlays/javascript/ikiwiki/ikiwiki.js create mode 100644 underlays/javascript/ikiwiki/relativedate.js create mode 100644 underlays/javascript/ikiwiki/toggle.js create mode 100644 underlays/jquery/ikiwiki/jquery.full.js create mode 100644 underlays/jquery/ikiwiki/jquery.min.js create mode 100644 underlays/openid-selector/ikiwiki/openid/openid-jquery.js create mode 100644 underlays/osm/ikiwiki/images/osm.png create mode 100644 underlays/osm/ikiwiki/osm.js create mode 120000 underlays/smiley/smileys create mode 120000 underlays/smiley/smileys.mdwn create mode 100644 wikilist diff --git a/.perlcriticrc b/.perlcriticrc new file mode 100644 index 000000000..1e099736f --- /dev/null +++ b/.perlcriticrc @@ -0,0 +1,59 @@ +theme = core + pbp + cosmetic + bugs + maintenance + complexity + security + +# While there's good reason to not use subroutine prototypes, ikiwiki does +# use them, and changing away from them could lead to subtle bugs in stuff +# using the library. So for now, demote errors about them. +[Subroutines::ProhibitSubroutinePrototypes] +severity = 1 + +# Nice to have, but low priority. I do it for the hairy regexps. +[RegularExpressions::RequireExtendedFormatting] +severity = 1 + +# ProhibitStringyEval doesn't take into account that eval q{use Foo}; +# defers the use until the eval runs, which is often a useful optimisation. +# While eval {use Foo}; does not defer the use at all. +[-BuiltinFunctions::ProhibitStringyEval] + +# ikiwiki uses the method of switching other files to the IkiWiki package +# when they are part of the core program. I don't plan to have more than +# the one exporting module in IkiWiki, so let's ignore this test. +[-Modules::RequireFilenameMatchesPackage] +# IkiWiki also switches _out_ of the core package when a package namespace +# is a good way to group a set of functions. This doesn't mean I want it +# loading up a separate file though, so it's in the same file. +[-Modules::ProhibitMultiplePackages] + +# ikiwiki uses this when it makes sense, ie, for conditional variable +# localisation. +[-Variables::ProhibitConditionalDeclarations] + +# IkiWiki exports symbols, and uses globals, if it's bad form, that's too +# bad. :-) +[-Modules::ProhibitAutomaticExportation] +[-Variables::ProhibitPackageVars] + +# Stylistic checks that I don't agree with. Larry put both forms there for +# a reason; both forms can be abused. +[-BuiltinFunctions::RequireBlockGrep] +[-BuiltinFunctions::RequireBlockMap] +[-Variables::ProhibitPunctuationVars] +[-ControlStructures::ProhibitPostfixControls] + +# Sadly doesn't match my coding style. +[-CodeLayout::ProhibitHardTabs] + +# Sillyness. +[-Miscellanea::RequireRcsKeywords] + +# Sadly, perl doesn't offer a builtin better way in many cases. +[-ControlStructures::ProhibitCascadingIfElse] + +# Good god, man, it's perl. Get over it! +[-ValuesAndExpressions::ProhibitNoisyQuotes] +[-ValuesAndExpressions::ProhibitEmptyQuotes] +[-RegularExpressions::RequireLineBoundaryMatching] + +# When I use local vars, I have a damn good reason. +# (A shower after with lots of strong soap is also a nice thing.) +[-Variables::ProhibitLocalVars] diff --git a/Bundle/IkiWiki.pm b/Bundle/IkiWiki.pm new file mode 100644 index 000000000..005936250 --- /dev/null +++ b/Bundle/IkiWiki.pm @@ -0,0 +1,37 @@ +package Bundle::IkiWiki; + +$VERSION = '0.01'; + +1; + +__END__ + +=head1 NAME + +Bundle::IkiWiki - core modules that ikiwiki needs + +=head1 SYNOPSIS + +perl -MCPAN -e 'install Bundle::IkiWiki' + +=head1 CONTENTS + +Text::Markdown::Discount +HTML::Scrubber +HTML::Template +HTML::Parser +URI 1.36 +XML::Simple +Date::Parse +CGI::FormBuilder +CGI::Session +Mail::Sendmail +CGI +Data::Dumper +YAML::XS +JSON +RPC::XML + +=head1 AUTHOR + +Joey Hess diff --git a/Bundle/IkiWiki/Extras.pm b/Bundle/IkiWiki/Extras.pm new file mode 100644 index 000000000..0a7cd3ae3 --- /dev/null +++ b/Bundle/IkiWiki/Extras.pm @@ -0,0 +1,42 @@ +package Bundle::IkiWiki::Extras; + +$VERSION = '0.01'; + +1; + +__END__ + +=head1 NAME + +Bundle::IkiWiki - modules used by ikiwiki plugins + +=head1 SYNOPSIS + +perl -MCPAN -e 'install Bundle::IkiWiki::Extras' + +=head1 CONTENTS + +Authen::Passphrase +Search::Xapian +File::MimeInfo +Locale::gettext +Net::OpenID::Consumer +LWPx::ParanoidAgent +Crypt::SSLeay +Text::CSV +Text::Typography +Text::Textile +Text::WikiFormat +XML::Feed +Net::Amazon::S3 +Text::WikiCreole +Term::ReadLine::Gnu +HTML::Tree +Sort::Naturally +Gravatar::URL +Net::INET6Glue +XML::Writer + +=head1 AUTHOR + +Joey Hess diff --git a/CHANGELOG b/CHANGELOG new file mode 120000 index 000000000..d526672ce --- /dev/null +++ b/CHANGELOG @@ -0,0 +1 @@ +debian/changelog \ No newline at end of file diff --git a/IkiWiki.pm b/IkiWiki.pm new file mode 100644 index 000000000..c497dd38f --- /dev/null +++ b/IkiWiki.pm @@ -0,0 +1,2854 @@ +#!/usr/bin/perl + +package IkiWiki; + +use warnings; +use strict; +use Encode; +use URI::Escape q{uri_escape_utf8}; +use POSIX (); +use Storable; +use open qw{:utf8 :std}; + +use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase + %pagestate %wikistate %renderedfiles %oldrenderedfiles + %pagesources %delpagesources %destsources %depends %depends_simple + @mass_depends %hooks %forcerebuild %loaded_plugins %typedlinks + %oldtypedlinks %autofiles}; + +use Exporter q{import}; +our @EXPORT = qw(hook debug error htmlpage template template_depends + deptype add_depends pagespec_match pagespec_match_list bestlink + htmllink readfile writefile pagetype srcfile pagename + displaytime strftime_utf8 will_render gettext ngettext urlto targetpage + add_underlay pagetitle titlepage linkpage newpagefile + inject add_link add_autofile + %config %links %pagestate %wikistate %renderedfiles + %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 + +# Page dependency types. +our $DEPEND_CONTENT=1; +our $DEPEND_PRESENCE=2; +our $DEPEND_LINKS=4; + +# Optimisation. +use Memoize; +memoize("abs2rel"); +memoize("sortspec_translate"); +memoize("pagespec_translate"); +memoize("template_file"); + +sub getsetup () { + wikiname => { + type => "string", + default => "wiki", + description => "name of the wiki", + safe => 1, + rebuild => 1, + }, + adminemail => { + type => "string", + default => undef, + example => 'me@example.com', + description => "contact email for wiki", + safe => 1, + rebuild => 0, + }, + adminuser => { + type => "string", + default => [], + description => "users who are wiki admins", + safe => 1, + rebuild => 0, + }, + banned_users => { + type => "string", + default => [], + description => "users who are banned from the wiki", + safe => 1, + rebuild => 0, + }, + srcdir => { + type => "string", + default => undef, + example => "$ENV{HOME}/wiki", + description => "where the source of the wiki is located", + safe => 0, # path + rebuild => 1, + }, + destdir => { + type => "string", + default => undef, + example => "/var/www/wiki", + description => "where to build the wiki", + safe => 0, # path + rebuild => 1, + }, + url => { + type => "string", + default => '', + example => "http://example.com/wiki", + description => "base url to the wiki", + safe => 1, + rebuild => 1, + }, + cgiurl => { + type => "string", + default => '', + example => "http://example.com/wiki/ikiwiki.cgi", + description => "url to the ikiwiki.cgi", + safe => 1, + rebuild => 1, + }, + cgi_wrapper => { + type => "string", + default => '', + example => "/var/www/wiki/ikiwiki.cgi", + description => "filename of cgi wrapper to generate", + safe => 0, # file + rebuild => 0, + }, + cgi_wrappermode => { + type => "string", + default => '06755', + description => "mode for cgi_wrapper (can safely be made suid)", + safe => 0, + rebuild => 0, + }, + cgi_overload_delay => { + type => "string", + default => '', + example => "10", + description => "number of seconds to delay CGI requests when overloaded", + safe => 1, + rebuild => 0, + }, + cgi_overload_message => { + type => "string", + default => '', + example => "Please wait", + description => "message to display when overloaded (may contain html)", + safe => 1, + rebuild => 0, + }, + rcs => { + type => "string", + default => '', + description => "rcs backend to use", + safe => 0, # don't allow overriding + rebuild => 0, + }, + default_plugins => { + type => "internal", + default => [qw{mdwn link inline meta htmlscrubber passwordauth + openid signinedit lockedit conditional + recentchanges parentlinks editpage}], + description => "plugins to enable by default", + safe => 0, + rebuild => 1, + }, + add_plugins => { + type => "string", + default => [], + description => "plugins to add to the default configuration", + safe => 1, + rebuild => 1, + }, + disable_plugins => { + type => "string", + default => [], + description => "plugins to disable", + safe => 1, + rebuild => 1, + }, + templatedir => { + type => "string", + default => "$installdir/share/ikiwiki/templates", + description => "additional directory to search for template files", + advanced => 1, + safe => 0, # path + rebuild => 1, + }, + underlaydir => { + type => "string", + default => "$installdir/share/ikiwiki/basewiki", + description => "base wiki source location", + advanced => 1, + safe => 0, # path + rebuild => 0, + }, + underlaydirbase => { + type => "internal", + default => "$installdir/share/ikiwiki", + description => "parent directory containing additional underlays", + safe => 0, + rebuild => 0, + }, + wrappers => { + type => "internal", + default => [], + description => "wrappers to generate", + safe => 0, + rebuild => 0, + }, + underlaydirs => { + type => "internal", + default => [], + description => "additional underlays to use", + safe => 0, + rebuild => 0, + }, + verbose => { + type => "boolean", + example => 1, + description => "display verbose messages?", + safe => 1, + rebuild => 0, + }, + syslog => { + type => "boolean", + example => 1, + description => "log to syslog?", + safe => 1, + rebuild => 0, + }, + usedirs => { + type => "boolean", + default => 1, + description => "create output files named page/index.html?", + safe => 0, # changing requires manual transition + rebuild => 1, + }, + prefix_directives => { + type => "boolean", + default => 1, + description => "use '!'-prefixed preprocessor directives?", + safe => 0, # changing requires manual transition + rebuild => 1, + }, + indexpages => { + type => "boolean", + default => 0, + description => "use page/index.mdwn source files", + safe => 1, + rebuild => 1, + }, + discussion => { + type => "boolean", + default => 1, + description => "enable Discussion pages?", + safe => 1, + rebuild => 1, + }, + discussionpage => { + type => "string", + default => gettext("Discussion"), + description => "name of Discussion pages", + safe => 1, + rebuild => 1, + }, + html5 => { + type => "boolean", + default => 0, + description => "generate HTML5?", + advanced => 0, + safe => 1, + rebuild => 1, + }, + sslcookie => { + type => "boolean", + default => 0, + description => "only send cookies over SSL connections?", + advanced => 1, + safe => 1, + rebuild => 0, + }, + default_pageext => { + type => "string", + default => "mdwn", + description => "extension to use for new pages", + safe => 0, # not sanitized + rebuild => 0, + }, + htmlext => { + type => "string", + default => "html", + description => "extension to use for html files", + safe => 0, # not sanitized + rebuild => 1, + }, + timeformat => { + type => "string", + default => '%c', + description => "strftime format string to display date", + advanced => 1, + safe => 1, + rebuild => 1, + }, + locale => { + type => "string", + default => undef, + example => "en_US.UTF-8", + description => "UTF-8 locale to use", + advanced => 1, + safe => 0, + rebuild => 1, + }, + userdir => { + type => "string", + default => "", + example => "users", + description => "put user pages below specified page", + safe => 1, + rebuild => 1, + }, + numbacklinks => { + type => "integer", + default => 10, + description => "how many backlinks to show before hiding excess (0 to show all)", + safe => 1, + rebuild => 1, + }, + hardlink => { + type => "boolean", + default => 0, + description => "attempt to hardlink source files? (optimisation for large files)", + advanced => 1, + safe => 0, # paranoia + rebuild => 0, + }, + umask => { + type => "string", + example => "public", + description => "force ikiwiki to use a particular umask (keywords public, group or private, or a number)", + advanced => 1, + safe => 0, # paranoia + rebuild => 0, + }, + wrappergroup => { + type => "string", + example => "ikiwiki", + description => "group for wrappers to run in", + advanced => 1, + safe => 0, # paranoia + rebuild => 0, + }, + libdir => { + type => "string", + default => "", + example => "$ENV{HOME}/.ikiwiki/", + description => "extra library and plugin directory", + advanced => 1, + safe => 0, # directory + rebuild => 0, + }, + ENV => { + type => "string", + default => {}, + description => "environment variables", + safe => 0, # paranoia + rebuild => 0, + }, + timezone => { + type => "string", + default => "", + example => "US/Eastern", + description => "time zone name", + safe => 1, + rebuild => 1, + }, + 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 => '^(*\.private|Makefile)$', + description => "regexp of files that should be skipped", + advanced => 1, + safe => 0, # regexp + rebuild => 1, + }, + wiki_file_prune_regexps => { + type => "internal", + default => [qr/(^|\/)\.\.(\/|$)/, qr/^\//, qr/^\./, qr/\/\./, + qr/\.x?html?$/, qr/\.ikiwiki-new$/, + qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//, + qr/(^|\/)_MTN\//, qr/(^|\/)_darcs\//, + qr/(^|\/)CVS\//, qr/\.dpkg-tmp$/], + description => "regexps of source files to ignore", + safe => 0, + rebuild => 1, + }, + wiki_file_chars => { + type => "string", + description => "specifies the characters that are allowed in source filenames", + default => "-[:alnum:]+/.:_", + safe => 0, + rebuild => 1, + }, + wiki_file_regexp => { + type => "internal", + description => "regexp of legal source files", + safe => 0, + rebuild => 1, + }, + web_commit_regexp => { + type => "internal", + default => qr/^web commit (by (.*?(?=: |$))|from ([0-9a-fA-F:.]+[0-9a-fA-F])):?(.*)/, + description => "regexp to parse web commits from logs", + safe => 0, + rebuild => 0, + }, + cgi => { + type => "internal", + default => 0, + description => "run as a cgi", + safe => 0, + rebuild => 0, + }, + cgi_disable_uploads => { + type => "internal", + default => 1, + description => "whether CGI should accept file uploads", + safe => 0, + rebuild => 0, + }, + post_commit => { + type => "internal", + default => 0, + description => "run as a post-commit hook", + safe => 0, + rebuild => 0, + }, + rebuild => { + type => "internal", + default => 0, + description => "running in rebuild mode", + safe => 0, + rebuild => 0, + }, + setup => { + type => "internal", + default => undef, + description => "running in setup mode", + safe => 0, + rebuild => 0, + }, + clean => { + type => "internal", + default => 0, + description => "running in clean mode", + safe => 0, + rebuild => 0, + }, + refresh => { + type => "internal", + default => 0, + description => "running in refresh mode", + safe => 0, + rebuild => 0, + }, + test_receive => { + type => "internal", + default => 0, + description => "running in receive test mode", + safe => 0, + rebuild => 0, + }, + wrapper_background_command => { + type => "internal", + default => '', + description => "background shell command to run", + safe => 0, + rebuild => 0, + }, + gettime => { + type => "internal", + description => "running in gettime mode", + safe => 0, + rebuild => 0, + }, + w3mmode => { + type => "internal", + default => 0, + description => "running in w3mmode", + safe => 0, + rebuild => 0, + }, + wikistatedir => { + type => "internal", + default => undef, + description => "path to the .ikiwiki directory holding ikiwiki state", + safe => 0, + rebuild => 0, + }, + setupfile => { + type => "internal", + default => undef, + description => "path to setup file", + safe => 0, + rebuild => 0, + }, + setuptype => { + type => "internal", + default => "Yaml", + description => "perl class to use to dump setup file", + safe => 0, + rebuild => 0, + }, + allow_symlinks_before_srcdir => { + type => "boolean", + default => 0, + description => "allow symlinks in the path leading to the srcdir (potentially insecure)", + safe => 0, + rebuild => 0, + }, +} + +sub defaultconfig () { + my %s=getsetup(); + my @ret; + foreach my $key (keys %s) { + push @ret, $key, $s{$key}->{default}; + } + return @ret; +} + +# URL to top of wiki as a path starting with /, valid from any wiki page or +# the CGI; if that's not possible, an absolute URL. Either way, it ends with / +my $local_url; +# URL to CGI script, similar to $local_url +my $local_cgiurl; + +sub checkconfig () { + # locale stuff; avoid LC_ALL since it overrides everything + if (defined $ENV{LC_ALL}) { + $ENV{LANG} = $ENV{LC_ALL}; + delete $ENV{LC_ALL}; + } + if (defined $config{locale}) { + if (POSIX::setlocale(&POSIX::LC_ALL, $config{locale})) { + $ENV{LANG}=$config{locale}; + define_gettext(); + } + } + + if (! defined $config{wiki_file_regexp}) { + $config{wiki_file_regexp}=qr/(^[$config{wiki_file_chars}]+$)/; + } + + if (ref $config{ENV} eq 'HASH') { + foreach my $val (keys %{$config{ENV}}) { + $ENV{$val}=$config{ENV}{$val}; + } + } + if (defined $config{timezone} && length $config{timezone}) { + $ENV{TZ}=$config{timezone}; + } + else { + $config{timezone}=$ENV{TZ}; + } + + if ($config{w3mmode}) { + eval q{use Cwd q{abs_path}}; + error($@) if $@; + $config{srcdir}=possibly_foolish_untaint(abs_path($config{srcdir})); + $config{destdir}=possibly_foolish_untaint(abs_path($config{destdir})); + $config{cgiurl}="file:///\$LIB/ikiwiki-w3m.cgi/".$config{cgiurl} + unless $config{cgiurl} =~ m!file:///!; + $config{url}="file://".$config{destdir}; + } + + if ($config{cgi} && ! length $config{url}) { + error(gettext("Must specify url to wiki with --url when using --cgi")); + } + + if (defined $config{url} && length $config{url}) { + eval q{use URI}; + my $baseurl = URI->new($config{url}); + + $local_url = $baseurl->path . "/"; + $local_cgiurl = undef; + + if (length $config{cgiurl}) { + my $cgiurl = URI->new($config{cgiurl}); + + $local_cgiurl = $cgiurl->path; + + if ($cgiurl->scheme ne $baseurl->scheme or + $cgiurl->authority ne $baseurl->authority) { + # too far apart, fall back to absolute URLs + $local_url = "$config{url}/"; + $local_cgiurl = $config{cgiurl}; + } + } + + $local_url =~ s{//$}{/}; + } + else { + $local_cgiurl = $config{cgiurl}; + } + + $config{wikistatedir}="$config{srcdir}/.ikiwiki" + unless exists $config{wikistatedir} && defined $config{wikistatedir}; + + if (defined $config{umask}) { + my $u = possibly_foolish_untaint($config{umask}); + + if ($u =~ m/^\d+$/) { + umask($u); + } + elsif ($u eq 'private') { + umask(077); + } + elsif ($u eq 'group') { + umask(027); + } + elsif ($u eq 'public') { + umask(022); + } + else { + error(sprintf(gettext("unsupported umask setting %s"), $u)); + } + } + + run_hooks(checkconfig => sub { shift->() }); + + return 1; +} + +sub listplugins () { + my %ret; + + foreach my $dir (@INC, $config{libdir}) { + next unless defined $dir && length $dir; + foreach my $file (glob("$dir/IkiWiki/Plugin/*.pm")) { + my ($plugin)=$file=~/.*\/(.*)\.pm$/; + $ret{$plugin}=1; + } + } + foreach my $dir ($config{libdir}, "$installdir/lib/ikiwiki") { + next unless defined $dir && length $dir; + foreach my $file (glob("$dir/plugins/*")) { + $ret{basename($file)}=1 if -x $file; + } + } + + return keys %ret; +} + +sub loadplugins () { + if (defined $config{libdir} && length $config{libdir}) { + unshift @INC, possibly_foolish_untaint($config{libdir}); + } + + foreach my $plugin (@{$config{default_plugins}}, @{$config{add_plugins}}) { + loadplugin($plugin); + } + + if ($config{rcs}) { + if (exists $hooks{rcs}) { + error(gettext("cannot use multiple rcs plugins")); + } + loadplugin($config{rcs}); + } + if (! exists $hooks{rcs}) { + loadplugin("norcs"); + } + + run_hooks(getopt => sub { shift->() }); + if (grep /^-/, @ARGV) { + print STDERR "Unknown option (or missing parameter): $_\n" + foreach grep /^-/, @ARGV; + usage(); + } + + return 1; +} + +sub loadplugin ($;$) { + my $plugin=shift; + my $force=shift; + + return if ! $force && grep { $_ eq $plugin} @{$config{disable_plugins}}; + + foreach my $dir (defined $config{libdir} ? possibly_foolish_untaint($config{libdir}) : undef, + "$installdir/lib/ikiwiki") { + if (defined $dir && -x "$dir/plugins/$plugin") { + eval { require IkiWiki::Plugin::external }; + if ($@) { + my $reason=$@; + error(sprintf(gettext("failed to load external plugin needed for %s plugin: %s"), $plugin, $reason)); + } + import IkiWiki::Plugin::external "$dir/plugins/$plugin"; + $loaded_plugins{$plugin}=1; + return 1; + } + } + + my $mod="IkiWiki::Plugin::".possibly_foolish_untaint($plugin); + eval qq{use $mod}; + if ($@) { + error("Failed to load plugin $mod: $@"); + } + $loaded_plugins{$plugin}=1; + return 1; +} + +sub error ($;$) { + my $message=shift; + my $cleaner=shift; + log_message('err' => $message) if $config{syslog}; + if (defined $cleaner) { + $cleaner->(); + } + die $message."\n"; +} + +sub debug ($) { + return unless $config{verbose}; + return log_message(debug => @_); +} + +my $log_open=0; +sub log_message ($$) { + my $type=shift; + + if ($config{syslog}) { + require Sys::Syslog; + if (! $log_open) { + Sys::Syslog::setlogsock('unix'); + Sys::Syslog::openlog('ikiwiki', '', 'user'); + $log_open=1; + } + return eval { + Sys::Syslog::syslog($type, "[$config{wikiname}] %s", join(" ", @_)); + }; + } + elsif (! $config{cgi}) { + return print "@_\n"; + } + else { + return print STDERR "@_\n"; + } +} + +sub possibly_foolish_untaint ($) { + my $tainted=shift; + my ($untainted)=$tainted=~/(.*)/s; + return $untainted; +} + +sub basename ($) { + my $file=shift; + + $file=~s!.*/+!!; + return $file; +} + +sub dirname ($) { + my $file=shift; + + $file=~s!/*[^/]+$!!; + return $file; +} + +sub isinternal ($) { + my $page=shift; + return exists $pagesources{$page} && + $pagesources{$page} =~ /\._([^.]+)$/; +} + +sub pagetype ($) { + my $file=shift; + + if ($file =~ /\.([^.]+)$/) { + return $1 if exists $hooks{htmlize}{$1}; + } + my $base=basename($file); + if (exists $hooks{htmlize}{$base} && + $hooks{htmlize}{$base}{noextension}) { + return $base; + } + return; +} + +my %pagename_cache; + +sub pagename ($) { + my $file=shift; + + if (exists $pagename_cache{$file}) { + return $pagename_cache{$file}; + } + + my $type=pagetype($file); + my $page=$file; + $page=~s/\Q.$type\E*$// + if defined $type && !$hooks{htmlize}{$type}{keepextension} + && !$hooks{htmlize}{$type}{noextension}; + if ($config{indexpages} && $page=~/(.*)\/index$/) { + $page=$1; + } + + $pagename_cache{$file} = $page; + return $page; +} + +sub newpagefile ($$) { + my $page=shift; + my $type=shift; + + if (! $config{indexpages} || $page eq 'index') { + return $page.".".$type; + } + else { + return $page."/index.".$type; + } +} + +sub targetpage ($$;$) { + my $page=shift; + my $ext=shift; + my $filename=shift; + + if (defined $filename) { + return $page."/".$filename.".".$ext; + } + elsif (! $config{usedirs} || $page eq 'index') { + return $page.".".$ext; + } + else { + return $page."/index.".$ext; + } +} + +sub htmlpage ($) { + my $page=shift; + + return targetpage($page, $config{htmlext}); +} + +sub srcfile_stat { + my $file=shift; + my $nothrow=shift; + + return "$config{srcdir}/$file", stat(_) if -e "$config{srcdir}/$file"; + foreach my $dir (@{$config{underlaydirs}}, $config{underlaydir}) { + return "$dir/$file", stat(_) if -e "$dir/$file"; + } + error("internal error: $file cannot be found in $config{srcdir} or underlay") unless $nothrow; + return; +} + +sub srcfile ($;$) { + return (srcfile_stat(@_))[0]; +} + +sub add_literal_underlay ($) { + my $dir=shift; + + if (! grep { $_ eq $dir } @{$config{underlaydirs}}) { + unshift @{$config{underlaydirs}}, $dir; + } +} + +sub add_underlay ($) { + my $dir = shift; + + if ($dir !~ /^\//) { + $dir="$config{underlaydirbase}/$dir"; + } + + add_literal_underlay($dir); + # why does it return 1? we just don't know + return 1; +} + +sub readfile ($;$$) { + my $file=shift; + my $binary=shift; + my $wantfd=shift; + + if (-l $file) { + error("cannot read a symlink ($file)"); + } + + local $/=undef; + open (my $in, "<", $file) || error("failed to read $file: $!"); + binmode($in) if ($binary); + return \*$in if $wantfd; + my $ret=<$in>; + # check for invalid utf-8, and toss it back to avoid crashes + if (! utf8::valid($ret)) { + $ret=encode_utf8($ret); + } + close $in || error("failed to read $file: $!"); + return $ret; +} + +sub prep_writefile ($$) { + my $file=shift; + my $destdir=shift; + + my $test=$file; + while (length $test) { + if (-l "$destdir/$test") { + error("cannot write to a symlink ($test)"); + } + if (-f _ && $test ne $file) { + # Remove conflicting file. + foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) { + foreach my $f (@{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) { + if ($f eq $test) { + unlink("$destdir/$test"); + last; + } + } + } + } + $test=dirname($test); + } + + my $dir=dirname("$destdir/$file"); + if (! -d $dir) { + my $d=""; + foreach my $s (split(m!/+!, $dir)) { + $d.="$s/"; + if (! -d $d) { + mkdir($d) || error("failed to create directory $d: $!"); + } + } + } + + return 1; +} + +sub writefile ($$$;$$) { + my $file=shift; # can include subdirs + my $destdir=shift; # directory to put file in + my $content=shift; + my $binary=shift; + my $writer=shift; + + prep_writefile($file, $destdir); + + my $newfile="$destdir/$file.ikiwiki-new"; + if (-l $newfile) { + error("cannot write to a symlink ($newfile)"); + } + + my $cleanup = sub { unlink($newfile) }; + open (my $out, '>', $newfile) || error("failed to write $newfile: $!", $cleanup); + binmode($out) if ($binary); + if ($writer) { + $writer->(\*$out, $cleanup); + } + else { + print $out $content or error("failed writing to $newfile: $!", $cleanup); + } + close $out || error("failed saving $newfile: $!", $cleanup); + rename($newfile, "$destdir/$file") || + error("failed renaming $newfile to $destdir/$file: $!", $cleanup); + + return 1; +} + +my %cleared; +sub will_render ($$;$) { + my $page=shift; + my $dest=shift; + my $clear=shift; + + # Important security check for independently created files. + if (-e "$config{destdir}/$dest" && ! $config{rebuild} && + ! grep { $_ eq $dest } (@{$renderedfiles{$page}}, @{$oldrenderedfiles{$page}}, @{$wikistate{editpage}{previews}})) { + my $from_other_page=0; + # Expensive, but rarely runs. + foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) { + if (grep { + $_ eq $dest || + dirname($_) eq $dest + } @{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) { + $from_other_page=1; + last; + } + } + + error("$config{destdir}/$dest independently created, not overwriting with version from $page") + unless $from_other_page; + } + + # If $dest exists as a directory, remove conflicting files in it + # rendered from other pages. + if (-d _) { + foreach my $p (keys %renderedfiles, keys %oldrenderedfiles) { + foreach my $f (@{$renderedfiles{$p}}, @{$oldrenderedfiles{$p}}) { + if (dirname($f) eq $dest) { + unlink("$config{destdir}/$f"); + rmdir(dirname("$config{destdir}/$f")); + } + } + } + } + + if (! $clear || $cleared{$page}) { + $renderedfiles{$page}=[$dest, grep { $_ ne $dest } @{$renderedfiles{$page}}]; + } + else { + foreach my $old (@{$renderedfiles{$page}}) { + delete $destsources{$old}; + } + $renderedfiles{$page}=[$dest]; + $cleared{$page}=1; + } + $destsources{$dest}=$page; + + return 1; +} + +sub bestlink ($$) { + my $page=shift; + my $link=shift; + + my $cwd=$page; + if ($link=~s/^\/+//) { + # absolute links + $cwd=""; + } + $link=~s/\/$//; + + do { + my $l=$cwd; + $l.="/" if length $l; + $l.=$link; + + if (exists $pagesources{$l}) { + return $l; + } + elsif (exists $pagecase{lc $l}) { + return $pagecase{lc $l}; + } + } while $cwd=~s{/?[^/]+$}{}; + + if (length $config{userdir}) { + my $l = "$config{userdir}/".lc($link); + if (exists $pagesources{$l}) { + return $l; + } + elsif (exists $pagecase{lc $l}) { + return $pagecase{lc $l}; + } + } + + #print STDERR "warning: page $page, broken link: $link\n"; + return ""; +} + +sub isinlinableimage ($) { + my $file=shift; + + return $file =~ /\.(png|gif|jpg|jpeg|svg)$/i; +} + +sub pagetitle ($;$) { + my $page=shift; + my $unescaped=shift; + + if ($unescaped) { + $page=~s/(__(\d+)__|_)/$1 eq '_' ? ' ' : chr($2)/eg; + } + else { + $page=~s/(__(\d+)__|_)/$1 eq '_' ? ' ' : "&#$2;"/eg; + } + + return $page; +} + +sub titlepage ($) { + my $title=shift; + # support use w/o %config set + my $chars = defined $config{wiki_file_chars} ? $config{wiki_file_chars} : "-[:alnum:]+/.:_"; + $title=~s/([^$chars]|_)/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg; + return $title; +} + +sub linkpage ($) { + my $link=shift; + my $chars = defined $config{wiki_file_chars} ? $config{wiki_file_chars} : "-[:alnum:]+/.:_"; + $link=~s/([^$chars])/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg; + return $link; +} + +sub cgiurl (@) { + my %params=@_; + + my $cgiurl=$local_cgiurl; + + if (exists $params{cgiurl}) { + $cgiurl=$params{cgiurl}; + delete $params{cgiurl}; + } + + unless (%params) { + return $cgiurl; + } + + return $cgiurl."?". + join("&", map $_."=".uri_escape_utf8($params{$_}), keys %params); +} + +sub cgiurl_abs (@) { + eval q{use URI}; + URI->new_abs(cgiurl(@_), $config{cgiurl}); +} + +sub baseurl (;$) { + my $page=shift; + + return $local_url if ! defined $page; + + $page=htmlpage($page); + $page=~s/[^\/]+$//; + $page=~s/[^\/]+\//..\//g; + return $page; +} + +sub urlabs ($$) { + my $url=shift; + my $urlbase=shift; + + return $url unless defined $urlbase && length $urlbase; + + eval q{use URI}; + URI->new_abs($url, $urlbase)->as_string; +} + +sub abs2rel ($$) { + # Work around very innefficient behavior in File::Spec if abs2rel + # is passed two relative paths. It's much faster if paths are + # absolute! (Debian bug #376658; fixed in debian unstable now) + my $path="/".shift; + my $base="/".shift; + + require File::Spec; + my $ret=File::Spec->abs2rel($path, $base); + $ret=~s/^// if defined $ret; + return $ret; +} + +sub displaytime ($;$$) { + # Plugins can override this function to mark up the time to + # display. + my $time=formattime($_[0], $_[1]); + if ($config{html5}) { + return ''; + } + else { + return ''.$time.''; + } +} + +sub formattime ($;$) { + # Plugins can override this function to format the time. + my $time=shift; + my $format=shift; + if (! defined $format) { + $format=$config{timeformat}; + } + + return strftime_utf8($format, localtime($time)); +} + +my $strftime_encoding; +sub strftime_utf8 { + # strftime doesn't know about encodings, so make sure + # its output is properly treated as utf8. + # Note that this does not handle utf-8 in the format string. + ($strftime_encoding) = POSIX::setlocale(&POSIX::LC_TIME) =~ m#\.([^@]+)# + unless defined $strftime_encoding; + $strftime_encoding + ? Encode::decode($strftime_encoding, POSIX::strftime(@_)) + : POSIX::strftime(@_); +} + +sub date_3339 ($) { + my $time=shift; + + my $lc_time=POSIX::setlocale(&POSIX::LC_TIME); + POSIX::setlocale(&POSIX::LC_TIME, "C"); + my $ret=POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", gmtime($time)); + POSIX::setlocale(&POSIX::LC_TIME, $lc_time); + return $ret; +} + +sub beautify_urlpath ($) { + my $url=shift; + + # Ensure url is not an empty link, and if necessary, + # add ./ to avoid colon confusion. + if ($url !~ /^\// && $url !~ /^\.\.?\//) { + $url="./$url"; + } + + if ($config{usedirs}) { + $url =~ s!/index.$config{htmlext}$!/!; + } + + return $url; +} + +sub urlto ($;$$) { + my $to=shift; + my $from=shift; + my $absolute=shift; + + if (! length $to) { + $to = 'index'; + } + + if (! $destsources{$to}) { + $to=htmlpage($to); + } + + if ($absolute) { + return $config{url}.beautify_urlpath("/".$to); + } + + if (! defined $from) { + my $u = $local_url || ''; + $u =~ s{/$}{}; + return $u.beautify_urlpath("/".$to); + } + + my $link = abs2rel($to, dirname(htmlpage($from))); + + return beautify_urlpath($link); +} + +sub isselflink ($$) { + # Plugins can override this function to support special types + # of selflinks. + my $page=shift; + my $link=shift; + + return $page eq $link; +} + +sub htmllink ($$$;@) { + my $lpage=shift; # the page doing the linking + my $page=shift; # the page that will contain the link (different for inline) + my $link=shift; + my %opts=@_; + + $link=~s/\/$//; + + my $bestlink; + if (! $opts{forcesubpage}) { + $bestlink=bestlink($lpage, $link); + } + else { + $bestlink="$lpage/".lc($link); + } + + my $linktext; + if (defined $opts{linktext}) { + $linktext=$opts{linktext}; + } + else { + $linktext=pagetitle(basename($link)); + } + + return "$linktext" + if length $bestlink && isselflink($page, $bestlink) && + ! defined $opts{anchor}; + + if (! $destsources{$bestlink}) { + $bestlink=htmlpage($bestlink); + + if (! $destsources{$bestlink}) { + my $cgilink = ""; + if (length $config{cgiurl}) { + $cgilink = " "create", + page => $link, + from => $lpage + )."\" rel=\"nofollow\">?"; + } + return "$cgilink$linktext" + } + } + + $bestlink=abs2rel($bestlink, dirname(htmlpage($page))); + $bestlink=beautify_urlpath($bestlink); + + if (! $opts{noimageinline} && isinlinableimage($bestlink)) { + return "\"$linktext\""; + } + + if (defined $opts{anchor}) { + $bestlink.="#".$opts{anchor}; + } + + my @attrs; + foreach my $attr (qw{rel class title}) { + if (defined $opts{$attr}) { + push @attrs, " $attr=\"$opts{$attr}\""; + } + } + + return "$linktext"; +} + +sub userpage ($) { + my $user=shift; + return length $config{userdir} ? "$config{userdir}/$user" : $user; +} + +sub openiduser ($) { + my $user=shift; + + if (defined $user && $user =~ m!^https?://! && + eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) { + my $display; + + if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) { + $display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user); + } + else { + # backcompat with old version + my $oid=Net::OpenID::VerifiedIdentity->new(identity => $user); + $display=$oid->display; + } + + # Convert "user.somehost.com" to "user [somehost.com]" + # (also "user.somehost.co.uk") + if ($display !~ /\[/) { + $display=~s/^([-a-zA-Z0-9]+?)\.([-.a-zA-Z0-9]+\.[a-z]+)$/$1 [$2]/; + } + # Convert "http://somehost.com/user" to "user [somehost.com]". + # (also "https://somehost.com/user/") + if ($display !~ /\[/) { + $display=~s/^https?:\/\/(.+)\/([^\/#?]+)\/?(?:[#?].*)?$/$2 [$1]/; + } + $display=~s!^https?://!!; # make sure this is removed + eval q{use CGI 'escapeHTML'}; + error($@) if $@; + return escapeHTML($display); + } + return; +} + +sub htmlize ($$$$) { + my $page=shift; + my $destpage=shift; + my $type=shift; + my $content=shift; + + my $oneline = $content !~ /\n/; + + if (exists $hooks{htmlize}{$type}) { + $content=$hooks{htmlize}{$type}{call}->( + page => $page, + content => $content, + ); + } + else { + error("htmlization of $type not supported"); + } + + run_hooks(sanitize => sub { + $content=shift->( + page => $page, + destpage => $destpage, + content => $content, + ); + }); + + if ($oneline) { + # hack to get rid of enclosing junk added by markdown + # and other htmlizers/sanitizers + $content=~s/^

//i; + $content=~s/<\/p>\n*$//i; + } + + return $content; +} + +sub linkify ($$$) { + my $page=shift; + my $destpage=shift; + my $content=shift; + + run_hooks(linkify => sub { + $content=shift->( + page => $page, + destpage => $destpage, + content => $content, + ); + }); + + return $content; +} + +our %preprocessing; +our $preprocess_preview=0; +sub preprocess ($$$;$$) { + my $page=shift; # the page the data comes from + my $destpage=shift; # the page the data will appear in (different for inline) + my $content=shift; + my $scan=shift; + my $preview=shift; + + # Using local because it needs to be set within any nested calls + # of this function. + local $preprocess_preview=$preview if defined $preview; + + my $handle=sub { + my $escape=shift; + my $prefix=shift; + my $command=shift; + my $params=shift; + $params="" if ! defined $params; + + if (length $escape) { + return "[[$prefix$command $params]]"; + } + elsif (exists $hooks{preprocess}{$command}) { + return "" if $scan && ! $hooks{preprocess}{$command}{scan}; + # Note: preserve order of params, some plugins may + # consider it significant. + my @params; + while ($params =~ m{ + (?:([-.\w]+)=)? # 1: named parameter key? + (?: + """(.*?)""" # 2: triple-quoted value + | + "([^"]*?)" # 3: single-quoted value + | + '''(.*?)''' # 4: triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (.*?)\n\5 # 6: heredoc value + | + (\S+) # 7: unquoted value + ) + (?:\s+|$) # delimiter to next param + }msgx) { + my $key=$1; + my $val; + if (defined $2) { + $val=$2; + $val=~s/\r\n/\n/mg; + $val=~s/^\n+//g; + $val=~s/\n+$//g; + } + elsif (defined $3) { + $val=$3; + } + elsif (defined $4) { + $val=$4; + } + elsif (defined $7) { + $val=$7; + } + elsif (defined $6) { + $val=$6; + } + + if (defined $key) { + push @params, $key, $val; + } + else { + push @params, $val, ''; + } + } + if ($preprocessing{$page}++ > 3) { + # Avoid loops of preprocessed pages preprocessing + # other pages that preprocess them, etc. + return "[[!$command ". + sprintf(gettext("preprocessing loop detected on %s at depth %i"), + $page, $preprocessing{$page}). + "]]"; + } + my $ret; + if (! $scan) { + $ret=eval { + $hooks{preprocess}{$command}{call}->( + @params, + page => $page, + destpage => $destpage, + preview => $preprocess_preview, + ); + }; + if ($@) { + my $error=$@; + chomp $error; + $ret="[[!$command ". + gettext("Error").": $error"."]]"; + } + } + else { + # use void context during scan pass + eval { + $hooks{preprocess}{$command}{call}->( + @params, + page => $page, + destpage => $destpage, + preview => $preprocess_preview, + ); + }; + $ret=""; + } + $preprocessing{$page}--; + return $ret; + } + else { + return "[[$prefix$command $params]]"; + } + }; + + my $regex; + if ($config{prefix_directives}) { + $regex = qr{ + (\\?) # 1: escape? + \[\[(!) # directive open; 2: prefix + ([-\w]+) # 3: command + ( # 4: the parameters.. + \s+ # Must have space if parameters present + (?: + (?:[-.\w]+=)? # named parameter key? + (?: + """.*?""" # triple-quoted value + | + "[^"]*?" # single-quoted value + | + '''.*?''' # triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (?:.*?)\n\5 # heredoc value + | + [^"\s\]]+ # unquoted value + ) + \s* # whitespace or end + # of directive + ) + *)? # 0 or more parameters + \]\] # directive closed + }sx; + } + else { + $regex = qr{ + (\\?) # 1: escape? + \[\[(!?) # directive open; 2: optional prefix + ([-\w]+) # 3: command + \s+ + ( # 4: the parameters.. + (?: + (?:[-.\w]+=)? # named parameter key? + (?: + """.*?""" # triple-quoted value + | + "[^"]*?" # single-quoted value + | + '''.*?''' # triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (?:.*?)\n\5 # heredoc value + | + [^"\s\]]+ # unquoted value + ) + \s* # whitespace or end + # of directive + ) + *) # 0 or more parameters + \]\] # directive closed + }sx; + } + + $content =~ s{$regex}{$handle->($1, $2, $3, $4)}eg; + return $content; +} + +sub filter ($$$) { + my $page=shift; + my $destpage=shift; + my $content=shift; + + run_hooks(filter => sub { + $content=shift->(page => $page, destpage => $destpage, + content => $content); + }); + + return $content; +} + +sub check_canedit ($$$;$) { + my $page=shift; + my $q=shift; + my $session=shift; + my $nonfatal=shift; + + my $canedit; + run_hooks(canedit => sub { + return if defined $canedit; + my $ret=shift->($page, $q, $session); + if (defined $ret) { + if ($ret eq "") { + $canedit=1; + } + elsif (ref $ret eq 'CODE') { + $ret->() unless $nonfatal; + $canedit=0; + } + elsif (defined $ret) { + error($ret) unless $nonfatal; + $canedit=0; + } + } + }); + return defined $canedit ? $canedit : 1; +} + +sub check_content (@) { + my %params=@_; + + return 1 if ! exists $hooks{checkcontent}; # optimisation + + if (exists $pagesources{$params{page}}) { + my @diff; + my %old=map { $_ => 1 } + split("\n", readfile(srcfile($pagesources{$params{page}}))); + foreach my $line (split("\n", $params{content})) { + push @diff, $line if ! exists $old{$line}; + } + $params{diff}=join("\n", @diff); + } + + my $ok; + run_hooks(checkcontent => sub { + return if defined $ok; + my $ret=shift->(%params); + if (defined $ret) { + if ($ret eq "") { + $ok=1; + } + elsif (ref $ret eq 'CODE') { + $ret->() unless $params{nonfatal}; + $ok=0; + } + elsif (defined $ret) { + error($ret) unless $params{nonfatal}; + $ok=0; + } + } + + }); + return defined $ok ? $ok : 1; +} + +sub check_canchange (@) { + my %params = @_; + my $cgi = $params{cgi}; + my $session = $params{session}; + my @changes = @{$params{changes}}; + + my %newfiles; + foreach my $change (@changes) { + # This untaint is safe because we check file_pruned and + # wiki_file_regexp. + my ($file)=$change->{file}=~/$config{wiki_file_regexp}/; + $file=possibly_foolish_untaint($file); + if (! defined $file || ! length $file || + file_pruned($file)) { + error(gettext("bad file name %s"), $file); + } + + my $type=pagetype($file); + my $page=pagename($file) if defined $type; + + if ($change->{action} eq 'add') { + $newfiles{$file}=1; + } + + if ($change->{action} eq 'change' || + $change->{action} eq 'add') { + if (defined $page) { + check_canedit($page, $cgi, $session); + next; + } + else { + if (IkiWiki::Plugin::attachment->can("check_canattach")) { + IkiWiki::Plugin::attachment::check_canattach($session, $file, $change->{path}); + check_canedit($file, $cgi, $session); + next; + } + } + } + elsif ($change->{action} eq 'remove') { + # check_canremove tests to see if the file is present + # on disk. This will fail when a single commit adds a + # file and then removes it again. Avoid the problem + # by not testing the removal in such pairs of changes. + # (The add is still tested, just to make sure that + # no data is added to the repo that a web edit + # could not add.) + next if $newfiles{$file}; + + if (IkiWiki::Plugin::remove->can("check_canremove")) { + IkiWiki::Plugin::remove::check_canremove(defined $page ? $page : $file, $cgi, $session); + check_canedit(defined $page ? $page : $file, $cgi, $session); + next; + } + } + else { + error "unknown action ".$change->{action}; + } + + error sprintf(gettext("you are not allowed to change %s"), $file); + } +} + + +my $wikilock; + +sub lockwiki () { + # Take an exclusive lock on the wiki to prevent multiple concurrent + # run issues. The lock will be dropped on program exit. + if (! -d $config{wikistatedir}) { + mkdir($config{wikistatedir}); + } + open($wikilock, '>', "$config{wikistatedir}/lockfile") || + error ("cannot write to $config{wikistatedir}/lockfile: $!"); + if (! flock($wikilock, 2)) { # LOCK_EX + error("failed to get lock"); + } + return 1; +} + +sub unlockwiki () { + POSIX::close($ENV{IKIWIKI_CGILOCK_FD}) if exists $ENV{IKIWIKI_CGILOCK_FD}; + return close($wikilock) if $wikilock; + return; +} + +my $commitlock; + +sub commit_hook_enabled () { + open($commitlock, '+>', "$config{wikistatedir}/commitlock") || + error("cannot write to $config{wikistatedir}/commitlock: $!"); + if (! flock($commitlock, 1 | 4)) { # LOCK_SH | LOCK_NB to test + close($commitlock) || error("failed closing commitlock: $!"); + return 0; + } + close($commitlock) || error("failed closing commitlock: $!"); + return 1; +} + +sub disable_commit_hook () { + open($commitlock, '>', "$config{wikistatedir}/commitlock") || + error("cannot write to $config{wikistatedir}/commitlock: $!"); + if (! flock($commitlock, 2)) { # LOCK_EX + error("failed to get commit lock"); + } + return 1; +} + +sub enable_commit_hook () { + return close($commitlock) if $commitlock; + return; +} + +sub loadindex () { + %oldrenderedfiles=%pagectime=(); + if (! $config{rebuild}) { + %pagesources=%pagemtime=%oldlinks=%links=%depends= + %destsources=%renderedfiles=%pagecase=%pagestate= + %depends_simple=%typedlinks=%oldtypedlinks=(); + } + my $in; + if (! open ($in, "<", "$config{wikistatedir}/indexdb")) { + if (-e "$config{wikistatedir}/index") { + system("ikiwiki-transition", "indexdb", $config{srcdir}); + open ($in, "<", "$config{wikistatedir}/indexdb") || return; + } + else { + $config{gettime}=1; # first build + return; + } + } + + my $index=Storable::fd_retrieve($in); + if (! defined $index) { + return 0; + } + + my $pages; + if (exists $index->{version} && ! ref $index->{version}) { + $pages=$index->{page}; + %wikistate=%{$index->{state}}; + # Handle plugins that got disabled by loading a new setup. + if (exists $config{setupfile}) { + require IkiWiki::Setup; + IkiWiki::Setup::disabled_plugins( + grep { ! $loaded_plugins{$_} } keys %wikistate); + } + } + else { + $pages=$index; + %wikistate=(); + } + + foreach my $src (keys %$pages) { + my $d=$pages->{$src}; + my $page=pagename($src); + $pagectime{$page}=$d->{ctime}; + $pagesources{$page}=$src; + if (! $config{rebuild}) { + $pagemtime{$page}=$d->{mtime}; + $renderedfiles{$page}=$d->{dest}; + if (exists $d->{links} && ref $d->{links}) { + $links{$page}=$d->{links}; + $oldlinks{$page}=[@{$d->{links}}]; + } + if (ref $d->{depends_simple} eq 'ARRAY') { + # old format + $depends_simple{$page}={ + map { $_ => 1 } @{$d->{depends_simple}} + }; + } + elsif (exists $d->{depends_simple}) { + $depends_simple{$page}=$d->{depends_simple}; + } + if (exists $d->{dependslist}) { + # old format + $depends{$page}={ + map { $_ => $DEPEND_CONTENT } + @{$d->{dependslist}} + }; + } + elsif (exists $d->{depends} && ! ref $d->{depends}) { + # old format + $depends{$page}={$d->{depends} => $DEPEND_CONTENT }; + } + elsif (exists $d->{depends}) { + $depends{$page}=$d->{depends}; + } + 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}}]; + } + foreach my $page (keys %pagesources) { + $pagecase{lc $page}=$page; + } + foreach my $page (keys %renderedfiles) { + $destsources{$_}=$page foreach @{$renderedfiles{$page}}; + } + return close($in); +} + +sub saveindex () { + run_hooks(savestate => sub { shift->() }); + + my @plugins=keys %loaded_plugins; + + if (! -d $config{wikistatedir}) { + mkdir($config{wikistatedir}); + } + my $newfile="$config{wikistatedir}/indexdb.new"; + my $cleanup = sub { unlink($newfile) }; + open (my $out, '>', $newfile) || error("cannot write to $newfile: $!", $cleanup); + + my %index; + foreach my $page (keys %pagemtime) { + next unless $pagemtime{$page}; + my $src=$pagesources{$page}; + + $index{page}{$src}={ + ctime => $pagectime{$page}, + mtime => $pagemtime{$page}, + dest => $renderedfiles{$page}, + links => $links{$page}, + }; + + if (exists $depends{$page}) { + $index{page}{$src}{depends} = $depends{$page}; + } + + if (exists $depends_simple{$page}) { + $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 (@plugins) { + foreach my $key (keys %{$pagestate{$page}{$id}}) { + $index{page}{$src}{state}{$id}{$key}=$pagestate{$page}{$id}{$key}; + } + } + } + } + + $index{state}={}; + foreach my $id (@plugins) { + $index{state}{$id}={}; # used to detect disabled plugins + foreach my $key (keys %{$wikistate{$id}}) { + $index{state}{$id}{$key}=$wikistate{$id}{$key}; + } + } + + $index{version}="3"; + my $ret=Storable::nstore_fd(\%index, $out); + return if ! defined $ret || ! $ret; + close $out || error("failed saving to $newfile: $!", $cleanup); + rename($newfile, "$config{wikistatedir}/indexdb") || + error("failed renaming $newfile to $config{wikistatedir}/indexdb", $cleanup); + + return 1; +} + +sub template_file ($) { + my $name=shift; + + my $tpage=($name =~ s/^\///) ? $name : "templates/$name"; + my $template; + if ($name !~ /\.tmpl$/ && exists $pagesources{$tpage}) { + $template=srcfile($pagesources{$tpage}, 1); + $name.=".tmpl"; + } + else { + $template=srcfile($tpage, 1); + } + + if (defined $template) { + return $template, $tpage, 1 if wantarray; + return $template; + } + else { + $name=~s:/::; # avoid path traversal + foreach my $dir ($config{templatedir}, + "$installdir/share/ikiwiki/templates") { + if (-e "$dir/$name") { + $template="$dir/$name"; + last; + } + } + if (defined $template) { + return $template, $tpage if wantarray; + return $template; + } + } + + return; +} + +sub template_depends ($$;@) { + my $name=shift; + my $page=shift; + + my ($filename, $tpage, $untrusted)=template_file($name); + if (! defined $filename) { + error(sprintf(gettext("template %s not found"), $name)) + } + + if (defined $page && defined $tpage) { + add_depends($page, $tpage); + } + + my @opts=( + filter => sub { + my $text_ref = shift; + ${$text_ref} = decode_utf8(${$text_ref}); + }, + loop_context_vars => 1, + die_on_bad_params => 0, + parent_global_vars => 1, + filename => $filename, + @_, + ($untrusted ? (no_includes => 1) : ()), + ); + return @opts if wantarray; + + require HTML::Template; + return HTML::Template->new(@opts); +} + +sub template ($;@) { + template_depends(shift, undef, @_); +} + +sub templateactions ($$) { + my $template=shift; + my $page=shift; + + my $have_actions=0; + my @actions; + run_hooks(pageactions => sub { + push @actions, map { { action => $_ } } + grep { defined } shift->(page => $page); + }); + $template->param(actions => \@actions); + + if ($config{cgiurl} && exists $hooks{auth}) { + $template->param(prefsurl => cgiurl(do => "prefs")); + $have_actions=1; + } + + if ($have_actions || @actions) { + $template->param(have_actions => 1); + } +} + +sub hook (@) { + my %param=@_; + + if (! exists $param{type} || ! ref $param{call} || ! exists $param{id}) { + error 'hook requires type, call, and id parameters'; + } + + return if $param{no_override} && exists $hooks{$param{type}}{$param{id}}; + + $hooks{$param{type}}{$param{id}}=\%param; + return 1; +} + +sub run_hooks ($$) { + # Calls the given sub for each hook of the given type, + # passing it the hook function to call. + my $type=shift; + my $sub=shift; + + if (exists $hooks{$type}) { + my (@first, @middle, @last); + foreach my $id (keys %{$hooks{$type}}) { + if ($hooks{$type}{$id}{first}) { + push @first, $id; + } + elsif ($hooks{$type}{$id}{last}) { + push @last, $id; + } + else { + push @middle, $id; + } + } + foreach my $id (@first, @middle, @last) { + $sub->($hooks{$type}{$id}{call}); + } + } + + return 1; +} + +sub rcs_update () { + $hooks{rcs}{rcs_update}{call}->(@_); +} + +sub rcs_prepedit ($) { + $hooks{rcs}{rcs_prepedit}{call}->(@_); +} + +sub rcs_commit (@) { + $hooks{rcs}{rcs_commit}{call}->(@_); +} + +sub rcs_commit_staged (@) { + $hooks{rcs}{rcs_commit_staged}{call}->(@_); +} + +sub rcs_add ($) { + $hooks{rcs}{rcs_add}{call}->(@_); +} + +sub rcs_remove ($) { + $hooks{rcs}{rcs_remove}{call}->(@_); +} + +sub rcs_rename ($$) { + $hooks{rcs}{rcs_rename}{call}->(@_); +} + +sub rcs_recentchanges ($) { + $hooks{rcs}{rcs_recentchanges}{call}->(@_); +} + +sub rcs_diff ($;$) { + $hooks{rcs}{rcs_diff}{call}->(@_); +} + +sub rcs_getctime ($) { + $hooks{rcs}{rcs_getctime}{call}->(@_); +} + +sub rcs_getmtime ($) { + $hooks{rcs}{rcs_getmtime}{call}->(@_); +} + +sub rcs_receive () { + $hooks{rcs}{rcs_receive}{call}->(); +} + +sub add_depends ($$;$) { + my $page=shift; + my $pagespec=shift; + my $deptype=shift || $DEPEND_CONTENT; + + # Is the pagespec a simple page name? + if ($pagespec =~ /$config{wiki_file_regexp}/ && + $pagespec !~ /[\s*?()!]/) { + $depends_simple{$page}{lc $pagespec} |= $deptype; + return 1; + } + + # Add explicit dependencies for influences. + my $sub=pagespec_translate($pagespec); + return unless defined $sub; + foreach my $p (keys %pagesources) { + my $r=$sub->($p, location => $page); + my $i=$r->influences; + my $static=$r->influences_static; + foreach my $k (keys %$i) { + next unless $r || $static || $k eq $page; + $depends_simple{$page}{lc $k} |= $i->{$k}; + } + last if $static; + } + + $depends{$page}{$pagespec} |= $deptype; + return 1; +} + +sub deptype (@) { + my $deptype=0; + foreach my $type (@_) { + if ($type eq 'presence') { + $deptype |= $DEPEND_PRESENCE; + } + elsif ($type eq 'links') { + $deptype |= $DEPEND_LINKS; + } + elsif ($type eq 'content') { + $deptype |= $DEPEND_CONTENT; + } + } + return $deptype; +} + +my $file_prune_regexp; +sub file_pruned ($) { + my $file=shift; + + 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. + 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})) { + $getobj=sub { + $gettext_obj=eval q{ + use Locale::gettext q{textdomain}; + Locale::gettext->domain('ikiwiki') + }; + }; + } + + 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 { + define_gettext(); + gettext(@_); +} + +sub ngettext { + define_gettext(); + ngettext(@_); +} + +sub yesno ($) { + my $val=shift; + + return (defined $val && (lc($val) eq gettext("yes") || lc($val) eq "yes" || $val eq "1")); +} + +sub inject { + # Injects a new function into the symbol table to replace an + # exported function. + my %params=@_; + + # This is deep ugly perl foo, beware. + no strict; + no warnings; + if (! defined $params{parent}) { + $params{parent}='::'; + $params{old}=\&{$params{name}}; + $params{name}=~s/.*:://; + } + my $parent=$params{parent}; + foreach my $ns (grep /^\w+::/, keys %{$parent}) { + $ns = $params{parent} . $ns; + inject(%params, parent => $ns) unless $ns eq '::main::'; + *{$ns . $params{name}} = $params{call} + if exists ${$ns}{$params{name}} && + \&{${$ns}{$params{name}}} == $params{old}; + } + use strict; + use warnings; +} + +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 add_autofile ($$$) { + my $file=shift; + my $plugin=shift; + my $generator=shift; + + $autofiles{$file}{plugin}=$plugin; + $autofiles{$file}{generator}=$generator; +} + +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 pagespec_translate ($) { + my $spec=shift; + + # Convert spec to perl code. + my $code=""; + my @data; + while ($spec=~m{ + \s* # ignore whitespace + ( # 1: match a single word + \! # ! + | + \( # ( + | + \) # ) + | + \w+\([^\)]*\) # command(params) + | + [^\s()]+ # any other text + ) + \s* # ignore whitespace + }gx) { + my $word=$1; + if (lc $word eq 'and') { + $code.=' &'; + } + elsif (lc $word eq 'or') { + $code.=' |'; + } + elsif ($word eq "(" || $word eq ")" || $word eq "!") { + $code.=' '.$word; + } + elsif ($word =~ /^(\w+)\((.*)\)$/) { + if (exists $IkiWiki::PageSpec::{"match_$1"}) { + push @data, $2; + $code.="IkiWiki::PageSpec::match_$1(\$page, \$data[$#data], \@_)"; + } + else { + push @data, qq{unknown function in pagespec "$word"}; + $code.="IkiWiki::ErrorReason->new(\$data[$#data])"; + } + } + else { + push @data, $word; + $code.=" IkiWiki::PageSpec::match_glob(\$page, \$data[$#data], \@_)"; + } + } + + if (! length $code) { + $code="IkiWiki::FailReason->new('empty pagespec')"; + } + + no warnings; + return eval 'sub { my $page=shift; '.$code.' }'; +} + +sub pagespec_match ($$;@) { + my $page=shift; + my $spec=shift; + my @params=@_; + + # Backwards compatability with old calling convention. + if (@params == 1) { + unshift @params, 'location'; + } + + my $sub=pagespec_translate($spec); + return IkiWiki::ErrorReason->new("syntax error in pagespec \"$spec\"") + if ! defined $sub; + return $sub->($page, @params); +} + +sub pagespec_match_list ($$;@) { + my $page=shift; + my $pagespec=shift; + my %params=@_; + + # Backwards compatability with old calling convention. + if (ref $page) { + print STDERR "warning: a plugin (".caller().") is using pagespec_match_list in an obsolete way, and needs to be updated\n"; + $params{list}=$page; + $page=$params{location}; # ugh! + } + + my $sub=pagespec_translate($pagespec); + error "syntax error in pagespec \"$pagespec\"" + if ! defined $sub; + my $sort=sortspec_translate($params{sort}, $params{reverse}) + if defined $params{sort}; + + my @candidates; + if (exists $params{list}) { + @candidates=exists $params{filter} + ? grep { ! $params{filter}->($_) } @{$params{list}} + : @{$params{list}}; + } + else { + @candidates=exists $params{filter} + ? grep { ! $params{filter}->($_) } keys %pagesources + : keys %pagesources; + } + + # 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; + my $accum=IkiWiki::SuccessReason->new(); + foreach my $p (@candidates) { + my $r=$sub->($p, %params, location => $page); + error(sprintf(gettext("cannot match pages: %s"), $r)) + if $r->isa("IkiWiki::ErrorReason"); + unless ($r || $r->influences_static) { + $r->remove_influence($p); + } + $accum |= $r; + if ($r) { + push @matches, $p; + last if defined $num && ++$count == $num; + } + } + + # Add simple dependencies for accumulated influences. + my $i=$accum->influences; + foreach my $k (keys %$i) { + $depends_simple{$page}{lc $k} |= $i->{$k}; + } + + # 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; + + return defined pagespec_translate($spec); +} + +sub glob2re ($) { + my $re=quotemeta(shift); + $re=~s/\\\*/.*/g; + $re=~s/\\\?/./g; + return qr/^$re$/i; +} + +package IkiWiki::FailReason; + +use overload ( + '""' => sub { $_[0][0] }, + '0+' => sub { 0 }, + '!' => sub { bless $_[0], 'IkiWiki::SuccessReason'}, + '&' => sub { $_[0]->merge_influences($_[1], 1); $_[0] }, + '|' => sub { $_[1]->merge_influences($_[0]); $_[1] }, + fallback => 1, +); + +our @ISA = 'IkiWiki::SuccessReason'; + +package IkiWiki::SuccessReason; + +use overload ( + '""' => sub { $_[0][0] }, + '0+' => sub { 1 }, + '!' => sub { bless $_[0], 'IkiWiki::FailReason'}, + '&' => sub { $_[1]->merge_influences($_[0], 1); $_[1] }, + '|' => sub { $_[0]->merge_influences($_[1]); $_[0] }, + fallback => 1, +); + +sub new { + my $class = shift; + my $value = shift; + return bless [$value, {@_}], $class; +} + +sub influences { + my $this=shift; + $this->[1]={@_} if @_; + my %i=%{$this->[1]}; + delete $i{""}; + return \%i; +} + +sub influences_static { + return ! $_[0][1]->{""}; +} + +sub merge_influences { + my $this=shift; + my $other=shift; + my $anded=shift; + + if (! $anded || (($this || %{$this->[1]}) && + ($other || %{$other->[1]}))) { + foreach my $influence (keys %{$other->[1]}) { + $this->[1]{$influence} |= $other->[1]{$influence}; + } + } + else { + # influence blocker + $this->[1]={}; + } +} + +sub remove_influence { + my $this=shift; + my $torm=shift; + + delete $this->[1]{$torm}; +} + +package IkiWiki::ErrorReason; + +our @ISA = 'IkiWiki::FailReason'; + +package IkiWiki::PageSpec; + +sub derel ($$) { + my $path=shift; + my $from=shift; + + if ($path =~ m!^\.(/|$)!) { + if ($1) { + $from=~s#/?[^/]+$## if defined $from; + $path=~s#^\./##; + $path="$from/$path" if defined $from && length $from; + } + else { + $path = $from; + $path = "" unless defined $path; + } + } + + return $path; +} + +my %glob_cache; + +sub match_glob ($$;@) { + my $page=shift; + my $glob=shift; + my %params=@_; + + $glob=derel($glob, $params{location}); + + # Instead of converting the glob to a regex every time, + # cache the compiled regex to save time. + my $re=$glob_cache{$glob}; + unless (defined $re) { + $glob_cache{$glob} = $re = IkiWiki::glob2re($glob); + } + if ($page =~ $re) { + if (! IkiWiki::isinternal($page) || $params{internal}) { + return IkiWiki::SuccessReason->new("$glob matches $page"); + } + else { + return IkiWiki::FailReason->new("$glob matches $page, but the page is an internal page"); + } + } + else { + return IkiWiki::FailReason->new("$glob does not match $page"); + } +} + +sub match_internal ($$;@) { + return match_glob(shift, shift, @_, internal => 1) +} + +sub match_page ($$;@) { + my $page=shift; + my $match=match_glob($page, shift, @_); + if ($match) { + my $source=exists $IkiWiki::pagesources{$page} ? + $IkiWiki::pagesources{$page} : + $IkiWiki::delpagesources{$page}; + my $type=defined $source ? IkiWiki::pagetype($source) : undef; + if (! defined $type) { + return IkiWiki::FailReason->new("$page is not a page"); + } + } + return $match; +} + +sub match_link ($$;@) { + my $page=shift; + my $link=lc(shift); + my %params=@_; + + $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", $page => $IkiWiki::DEPEND_LINKS, "" => 1) + unless $links && @{$links}; + my $bestlink = IkiWiki::bestlink($from, $link); + foreach my $p (@{$links}) { + next unless (! defined $linktype || exists $IkiWiki::typedlinks{$page}{$linktype}{$p}); + + if (length $bestlink) { + if ($bestlink eq IkiWiki::bestlink($page, $p)) { + return IkiWiki::SuccessReason->new("$page links to $link$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1) + } + } + else { + if (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/^\///; + if (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$qualifier", $page => $IkiWiki::DEPEND_LINKS, "" => 1); +} + +sub match_backlink ($$;@) { + my $page=shift; + my $testpage=shift; + my %params=@_; + if ($testpage eq '.') { + $testpage = $params{'location'} + } + my $ret=match_link($testpage, $page, @_); + $ret->influences($testpage => $IkiWiki::DEPEND_LINKS); + return $ret; +} + +sub match_created_before ($$;@) { + my $page=shift; + my $testpage=shift; + my %params=@_; + + $testpage=derel($testpage, $params{location}); + + if (exists $IkiWiki::pagectime{$testpage}) { + if ($IkiWiki::pagectime{$page} < $IkiWiki::pagectime{$testpage}) { + return IkiWiki::SuccessReason->new("$page created before $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); + } + else { + return IkiWiki::FailReason->new("$page not created before $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); + } + } + else { + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage => $IkiWiki::DEPEND_PRESENCE); + } +} + +sub match_created_after ($$;@) { + my $page=shift; + my $testpage=shift; + my %params=@_; + + $testpage=derel($testpage, $params{location}); + + if (exists $IkiWiki::pagectime{$testpage}) { + if ($IkiWiki::pagectime{$page} > $IkiWiki::pagectime{$testpage}) { + return IkiWiki::SuccessReason->new("$page created after $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); + } + else { + return IkiWiki::FailReason->new("$page not created after $testpage", $testpage => $IkiWiki::DEPEND_PRESENCE); + } + } + else { + return IkiWiki::ErrorReason->new("$testpage does not exist", $testpage => $IkiWiki::DEPEND_PRESENCE); + } +} + +sub match_creation_day ($$;@) { + my $page=shift; + my $d=shift; + if ($d !~ /^\d+$/) { + return IkiWiki::ErrorReason->new("invalid day $d"); + } + if ((localtime($IkiWiki::pagectime{$page}))[3] == $d) { + return IkiWiki::SuccessReason->new('creation_day matched'); + } + else { + return IkiWiki::FailReason->new('creation_day did not match'); + } +} + +sub match_creation_month ($$;@) { + my $page=shift; + my $m=shift; + if ($m !~ /^\d+$/) { + return IkiWiki::ErrorReason->new("invalid month $m"); + } + if ((localtime($IkiWiki::pagectime{$page}))[4] + 1 == $m) { + return IkiWiki::SuccessReason->new('creation_month matched'); + } + else { + return IkiWiki::FailReason->new('creation_month did not match'); + } +} + +sub match_creation_year ($$;@) { + my $page=shift; + my $y=shift; + if ($y !~ /^\d+$/) { + return IkiWiki::ErrorReason->new("invalid year $y"); + } + if ((localtime($IkiWiki::pagectime{$page}))[5] + 1900 == $y) { + return IkiWiki::SuccessReason->new('creation_year matched'); + } + else { + return IkiWiki::FailReason->new('creation_year did not match'); + } +} + +sub match_user ($$;@) { + shift; + my $user=shift; + my %params=@_; + + if (! exists $params{user}) { + return IkiWiki::ErrorReason->new("no user specified"); + } + + my $regexp=IkiWiki::glob2re($user); + + if (defined $params{user} && $params{user}=~$regexp) { + return IkiWiki::SuccessReason->new("user is $user"); + } + elsif (! defined $params{user}) { + return IkiWiki::FailReason->new("not logged in"); + } + else { + return IkiWiki::FailReason->new("user is $params{user}, not $user"); + } +} + +sub match_admin ($$;@) { + shift; + shift; + my %params=@_; + + if (! exists $params{user}) { + return IkiWiki::ErrorReason->new("no user specified"); + } + + if (defined $params{user} && IkiWiki::is_admin($params{user})) { + return IkiWiki::SuccessReason->new("user is an admin"); + } + elsif (! defined $params{user}) { + return IkiWiki::FailReason->new("not logged in"); + } + else { + return IkiWiki::FailReason->new("user is not an admin"); + } +} + +sub match_ip ($$;@) { + shift; + my $ip=shift; + my %params=@_; + + if (! exists $params{ip}) { + return IkiWiki::ErrorReason->new("no IP specified"); + } + + my $regexp=IkiWiki::glob2re(lc $ip); + + if (defined $params{ip} && lc $params{ip}=~$regexp) { + return IkiWiki::SuccessReason->new("IP is $ip"); + } + else { + return IkiWiki::FailReason->new("IP is $params{ip}, not $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_path { IkiWiki::pagetitle($a) cmp IkiWiki::pagetitle($b) } +sub cmp_mtime { $IkiWiki::pagemtime{$b} <=> $IkiWiki::pagemtime{$a} } +sub cmp_age { $IkiWiki::pagectime{$b} <=> $IkiWiki::pagectime{$a} } + +1 diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm new file mode 100644 index 000000000..5baa6c179 --- /dev/null +++ b/IkiWiki/CGI.pm @@ -0,0 +1,469 @@ +#!/usr/bin/perl + +package IkiWiki; + +use warnings; +use strict; +use IkiWiki; +use IkiWiki::UserInfo; +use open qw{:utf8 :std}; +use Encode; + +sub printheader ($) { + my $session=shift; + + if (($ENV{HTTPS} && lc $ENV{HTTPS} ne "off") || $config{sslcookie}) { + print $session->header(-charset => 'utf-8', + -cookie => $session->cookie(-httponly => 1, -secure => 1)); + } + else { + print $session->header(-charset => 'utf-8', + -cookie => $session->cookie(-httponly => 1)); + } +} + +sub prepform { + my $form=shift; + my $buttons=shift; + my $session=shift; + my $cgi=shift; + + if (exists $hooks{formbuilder}) { + run_hooks(formbuilder => sub { + shift->(form => $form, cgi => $cgi, session => $session, + buttons => $buttons); + }); + } + + return $form; +} + +sub showform ($$$$;@) { + my $form=prepform(@_); + shift; + my $buttons=shift; + my $session=shift; + my $cgi=shift; + + printheader($session); + print cgitemplate($cgi, $form->title, + $form->render(submit => $buttons), @_); +} + +sub cgitemplate ($$$;@) { + my $cgi=shift; + my $title=shift; + my $content=shift; + my %params=@_; + + my $template=template("page.tmpl"); + + my $topurl = defined $cgi ? $cgi->url : $config{url}; + + my $page=""; + if (exists $params{page}) { + $page=delete $params{page}; + $params{forcebaseurl}=urlabs(urlto($page), $topurl); + } + run_hooks(pagetemplate => sub { + shift->( + page => $page, + destpage => $page, + template => $template, + ); + }); + templateactions($template, ""); + + $template->param( + dynamic => 1, + title => $title, + wikiname => $config{wikiname}, + content => $content, + baseurl => urlabs(baseurl(), $topurl), + html5 => $config{html5}, + %params, + ); + + return $template->output; +} + +sub redirect ($$) { + my $q=shift; + eval q{use URI}; + my $url=URI->new(urlabs(shift, $q->url)); + if (! $config{w3mmode}) { + print $q->redirect($url); + } + else { + print "Content-type: text/plain\n"; + print "W3m-control: GOTO $url\n\n"; + } +} + +sub decode_cgi_utf8 ($) { + # decode_form_utf8 method is needed for 5.01 + if ($] < 5.01) { + my $cgi = shift; + foreach my $f ($cgi->param) { + $cgi->param($f, map { decode_utf8 $_ } $cgi->param($f)); + } + } +} + +sub decode_form_utf8 ($) { + if ($] >= 5.01) { + my $form = shift; + foreach my $f ($form->field) { + my @value=map { decode_utf8($_) } $form->field($f); + $form->field(name => $f, + value => \@value, + force => 1, + ); + } + } +} + +# Check if the user is signed in. If not, redirect to the signin form and +# save their place to return to later. +sub needsignin ($$) { + my $q=shift; + my $session=shift; + + if (! defined $session->param("name") || + ! userinfo_get($session->param("name"), "regdate")) { + $session->param(postsignin => $q->query_string); + cgi_signin($q, $session); + cgi_savesession($session); + exit; + } +} + +sub cgi_signin ($$;$) { + my $q=shift; + my $session=shift; + my $returnhtml=shift; + + decode_cgi_utf8($q); + eval q{use CGI::FormBuilder}; + error($@) if $@; + my $form = CGI::FormBuilder->new( + title => "signin", + name => "signin", + charset => "utf-8", + method => 'POST', + required => 'NONE', + javascript => 0, + params => $q, + action => cgiurl(), + header => 0, + template => {type => 'div'}, + stylesheet => 1, + ); + my $buttons=["Login"]; + + $form->field(name => "do", type => "hidden", value => "signin", + force => 1); + + decode_form_utf8($form); + run_hooks(formbuilder_setup => sub { + shift->(form => $form, cgi => $q, session => $session, + buttons => $buttons); + }); + decode_form_utf8($form); + + if ($form->submitted) { + $form->validate; + } + + if ($returnhtml) { + $form=prepform($form, $buttons, $session, $q); + return $form->render(submit => $buttons); + } + + showform($form, $buttons, $session, $q); +} + +sub cgi_postsignin ($$) { + my $q=shift; + my $session=shift; + + # Continue with whatever was being done before the signin process. + if (defined $session->param("postsignin")) { + my $postsignin=CGI->new($session->param("postsignin")); + $session->clear("postsignin"); + cgi($postsignin, $session); + cgi_savesession($session); + exit; + } + else { + if ($config{sslcookie} && ! $q->https()) { + error(gettext("probable misconfiguration: sslcookie is set, but you are attempting to login via http, not https")); + } + else { + error(gettext("login failed, perhaps you need to turn on cookies?")); + } + } +} + +sub cgi_prefs ($$) { + my $q=shift; + my $session=shift; + + needsignin($q, $session); + decode_cgi_utf8($q); + + # The session id is stored on the form and checked to + # guard against CSRF. + my $sid=$q->param('sid'); + if (! defined $sid) { + $q->delete_all; + } + elsif ($sid ne $session->id) { + error(gettext("Your login session has expired.")); + } + + eval q{use CGI::FormBuilder}; + error($@) if $@; + my $form = CGI::FormBuilder->new( + title => "preferences", + name => "preferences", + header => 0, + charset => "utf-8", + method => 'POST', + validate => { + email => 'EMAIL', + }, + required => 'NONE', + javascript => 0, + params => $q, + action => cgiurl(), + template => {type => 'div'}, + stylesheet => 1, + fieldsets => [ + [login => gettext("Login")], + [preferences => gettext("Preferences")], + [admin => gettext("Admin")] + ], + ); + my $buttons=["Save Preferences", "Logout", "Cancel"]; + + decode_form_utf8($form); + run_hooks(formbuilder_setup => sub { + shift->(form => $form, cgi => $q, session => $session, + buttons => $buttons); + }); + decode_form_utf8($form); + + $form->field(name => "do", type => "hidden", value => "prefs", + force => 1); + $form->field(name => "sid", type => "hidden", value => $session->id, + force => 1); + $form->field(name => "email", size => 50, fieldset => "preferences"); + + my $user_name=$session->param("name"); + + if (! $form->submitted) { + $form->field(name => "email", force => 1, + value => userinfo_get($user_name, "email")); + } + + if ($form->submitted eq 'Logout') { + $session->delete(); + redirect($q, baseurl(undef)); + return; + } + elsif ($form->submitted eq 'Cancel') { + redirect($q, baseurl(undef)); + return; + } + elsif ($form->submitted eq 'Save Preferences' && $form->validate) { + if (defined $form->field('email')) { + userinfo_set($user_name, 'email', $form->field('email')) || + error("failed to set email"); + } + + $form->text(gettext("Preferences saved.")); + } + + showform($form, $buttons, $session, $q, + prefsurl => "", # avoid showing the preferences link + ); +} + +sub cgi_custom_failure ($$$) { + my $q=shift; + my $httpstatus=shift; + my $message=shift; + + print $q->header( + -status => $httpstatus, + -charset => 'utf-8', + ); + print $message; + + # Internet Explod^Hrer won't show custom 404 responses + # unless they're >= 512 bytes + print ' ' x 512; + + exit; +} + +sub check_banned ($$) { + my $q=shift; + my $session=shift; + + my $banned=0; + my $name=$session->param("name"); + if (defined $name && + grep { $name eq $_ } @{$config{banned_users}}) { + $banned=1; + } + + foreach my $b (@{$config{banned_users}}) { + if (pagespec_match("", $b, + ip => $session->remote_addr(), + name => defined $name ? $name : "", + )) { + $banned=1; + last; + } + } + + if ($banned) { + $session->delete(); + cgi_savesession($session); + cgi_custom_failure( + $q, "403 Forbidden", + gettext("You are banned.")); + } +} + +sub cgi_getsession ($) { + my $q=shift; + + eval q{use CGI::Session; use HTML::Entities}; + error($@) if $@; + CGI::Session->name("ikiwiki_session_".encode_entities($config{wikiname})); + + my $oldmask=umask(077); + my $session = eval { + CGI::Session->new("driver:DB_File", $q, + { FileName => "$config{wikistatedir}/sessions.db" }) + }; + if (! $session || $@) { + error($@." ".CGI::Session->errstr()); + } + + umask($oldmask); + + return $session; +} + +# To guard against CSRF, the user's session id (sid) +# can be stored on a form. This function will check +# (for logged in users) that the sid on the form matches +# the session id in the cookie. +sub checksessionexpiry ($$) { + my $q=shift; + my $session = shift; + + if (defined $session->param("name")) { + my $sid=$q->param('sid'); + if (! defined $sid || $sid ne $session->id) { + error(gettext("Your login session has expired.")); + } + } +} + +sub cgi_savesession ($) { + my $session=shift; + + # Force session flush with safe umask. + my $oldmask=umask(077); + $session->flush; + umask($oldmask); +} + +sub cgi (;$$) { + my $q=shift; + my $session=shift; + + eval q{use CGI}; + error($@) if $@; + $CGI::DISABLE_UPLOADS=$config{cgi_disable_uploads}; + + if (! $q) { + binmode(STDIN); + $q=CGI->new; + binmode(STDIN, ":utf8"); + + run_hooks(cgi => sub { shift->($q) }); + } + + my $do=$q->param('do'); + if (! defined $do || ! length $do) { + my $error = $q->cgi_error; + if ($error) { + error("Request not processed: $error"); + } + else { + error("\"do\" parameter missing"); + } + } + + # Need to lock the wiki before getting a session. + lockwiki(); + loadindex(); + + if (! $session) { + $session=cgi_getsession($q); + } + + # Auth hooks can sign a user in. + if ($do ne 'signin' && ! defined $session->param("name")) { + run_hooks(auth => sub { + shift->($q, $session) + }); + if (defined $session->param("name")) { + # Make sure whatever user was authed is in the + # userinfo db. + if (! userinfo_get($session->param("name"), "regdate")) { + userinfo_setall($session->param("name"), { + email => defined $session->param("email") ? $session->param("email") : "", + password => "", + regdate => time, + }) || error("failed adding user"); + } + } + } + + check_banned($q, $session); + + run_hooks(sessioncgi => sub { shift->($q, $session) }); + + if ($do eq 'signin') { + cgi_signin($q, $session); + cgi_savesession($session); + } + elsif ($do eq 'prefs') { + cgi_prefs($q, $session); + } + elsif (defined $session->param("postsignin") || $do eq 'postsignin') { + cgi_postsignin($q, $session); + } + else { + error("unknown do parameter"); + } +} + +# Does not need to be called directly; all errors will go through here. +sub cgierror ($) { + my $message=shift; + + print "Content-type: text/html\n\n"; + print cgitemplate(undef, gettext("Error"), + "

".gettext("Error").": $message

"); + die $@; +} + +1 diff --git a/IkiWiki/Plugin/404.pm b/IkiWiki/Plugin/404.pm new file mode 100644 index 000000000..42cfa9e8a --- /dev/null +++ b/IkiWiki/Plugin/404.pm @@ -0,0 +1,81 @@ +#!/usr/bin/perl +# Copyright © 2009 Simon McVittie +# Licensed under the GNU GPL, version 2, or any later version published by the +# Free Software Foundation +package IkiWiki::Plugin::404; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "cgi", id => '404', call => \&cgi); + hook(type => "getsetup", id => '404', call => \&getsetup); + IkiWiki::loadplugin("goto"); +} + +sub getsetup () { + return + plugin => { + # not really a matter of safety, but enabling/disabling + # through a web interface is useless - it needs web + # server admin action too + safe => 0, + rebuild => 0, + section => "web", + } +} + +sub cgi_page_from_404 ($$$) { + my $path = shift; + my $baseurl = shift; + my $usedirs = shift; + + # fail if missing from environment or whatever + return undef unless defined $path; + return undef unless defined $baseurl; + + # with usedirs on, path is like /~fred/foo/bar/ or /~fred/foo/bar or + # /~fred/foo/bar/index.html + # with usedirs off, path is like /~fred/foo/bar.html + # baseurl is like 'http://people.example.com/~fred' + + # convert baseurl to ~fred + unless ($baseurl =~ s{^https?://[^/]+/?}{}) { + return undef; + } + + # convert path to /~fred/foo/bar + if ($usedirs) { + $path =~ s/\/*(?:index\.$config{htmlext})?$//; + } + else { + $path =~ s/\.$config{htmlext}$//; + } + + # remove /~fred/ + unless ($path =~ s{^/*\Q$baseurl\E/*}{}) { + return undef; + } + + # special case for the index + unless ($path) { + return 'index'; + } + + return $path; +} + +sub cgi ($) { + my $cgi=shift; + + if (exists $ENV{REDIRECT_STATUS} && + $ENV{REDIRECT_STATUS} eq '404') { + my $page = cgi_page_from_404( + Encode::decode_utf8($ENV{REDIRECT_URL}), + $config{url}, $config{usedirs}); + IkiWiki::Plugin::goto::cgi_goto($cgi, $page); + } +} + +1; diff --git a/IkiWiki/Plugin/aggregate.pm b/IkiWiki/Plugin/aggregate.pm new file mode 100644 index 000000000..89da5c453 --- /dev/null +++ b/IkiWiki/Plugin/aggregate.pm @@ -0,0 +1,792 @@ +#!/usr/bin/perl +# Feed aggregation plugin. +package IkiWiki::Plugin::aggregate; + +use warnings; +use strict; +use IkiWiki 3.00; +use HTML::Parser; +use HTML::Tagset; +use HTML::Entities; +use open qw{:utf8 :std}; + +my %feeds; +my %guids; + +sub import { + hook(type => "getopt", id => "aggregate", call => \&getopt); + hook(type => "getsetup", id => "aggregate", call => \&getsetup); + hook(type => "checkconfig", id => "aggregate", call => \&checkconfig, + last => 1); + hook(type => "needsbuild", id => "aggregate", call => \&needsbuild); + hook(type => "preprocess", id => "aggregate", call => \&preprocess); + hook(type => "delete", id => "aggregate", call => \&delete); + hook(type => "savestate", id => "aggregate", call => \&savestate); + hook(type => "htmlize", id => "_aggregated", call => \&htmlize); + if (exists $config{aggregate_webtrigger} && $config{aggregate_webtrigger}) { + hook(type => "cgi", id => "aggregate", call => \&cgi); + } +} + +sub getopt () { + eval q{use Getopt::Long}; + error($@) if $@; + Getopt::Long::Configure('pass_through'); + GetOptions( + "aggregate" => \$config{aggregate}, + "aggregateinternal!" => \$config{aggregateinternal}, + ); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, + aggregateinternal => { + type => "boolean", + example => 1, + description => "enable aggregation to internal pages?", + safe => 0, # enabling needs manual transition + rebuild => 0, + }, + aggregate_webtrigger => { + type => "boolean", + example => 0, + description => "allow aggregation to be triggered via the web?", + safe => 1, + rebuild => 0, + }, + cookiejar => { + type => "string", + example => { file => "$ENV{HOME}/.ikiwiki/cookies" }, + safe => 0, # hooks into perl module internals + description => "cookie control", + }, +} + +sub checkconfig () { + if (! defined $config{aggregateinternal}) { + $config{aggregateinternal}=1; + } + if (! defined $config{cookiejar}) { + $config{cookiejar}={ file => "$ENV{HOME}/.ikiwiki/cookies" }; + } + + # This is done here rather than in a refresh hook because it + # needs to run before the wiki is locked. + if ($config{aggregate} && ! ($config{post_commit} && + IkiWiki::commit_hook_enabled())) { + launchaggregation(); + } +} + +sub cgi ($) { + my $cgi=shift; + + if (defined $cgi->param('do') && + $cgi->param("do") eq "aggregate_webtrigger") { + $|=1; + print "Content-Type: text/plain\n\n"; + $config{cgi}=0; + $config{verbose}=1; + $config{syslog}=0; + print gettext("Aggregation triggered via web.")."\n\n"; + if (launchaggregation()) { + IkiWiki::lockwiki(); + IkiWiki::loadindex(); + require IkiWiki::Render; + IkiWiki::refresh(); + IkiWiki::saveindex(); + } + else { + print gettext("Nothing to do right now, all feeds are up-to-date!")."\n"; + } + exit 0; + } +} + +sub launchaggregation () { + # See if any feeds need aggregation. + loadstate(); + my @feeds=needsaggregate(); + return unless @feeds; + if (! lockaggregate()) { + error("an aggregation process is already running"); + } + # force a later rebuild of source pages + $IkiWiki::forcerebuild{$_->{sourcepage}}=1 + foreach @feeds; + + # Fork a child process to handle the aggregation. + # The parent process will then handle building the + # result. This avoids messy code to clear state + # accumulated while aggregating. + defined(my $pid = fork) or error("Can't fork: $!"); + if (! $pid) { + IkiWiki::loadindex(); + # Aggregation happens without the main wiki lock + # being held. This allows editing pages etc while + # aggregation is running. + aggregate(@feeds); + + IkiWiki::lockwiki; + # Merge changes, since aggregation state may have + # changed on disk while the aggregation was happening. + mergestate(); + expire(); + savestate(); + IkiWiki::unlockwiki; + exit 0; + } + waitpid($pid,0); + if ($?) { + error "aggregation failed with code $?"; + } + + clearstate(); + unlockaggregate(); + + return 1; +} + +# Pages with extension _aggregated have plain html markup, pass through. +sub htmlize (@) { + my %params=@_; + return $params{content}; +} + +# Used by ikiwiki-transition aggregateinternal. +sub migrate_to_internal { + if (! lockaggregate()) { + error("an aggregation process is currently running"); + } + + IkiWiki::lockwiki(); + loadstate(); + $config{verbose}=1; + + foreach my $data (values %guids) { + next unless $data->{page}; + next if $data->{expired}; + + $config{aggregateinternal} = 0; + my $oldname = "$config{srcdir}/".htmlfn($data->{page}); + if (! -e $oldname) { + $oldname = $IkiWiki::Plugin::transient::transientdir."/".htmlfn($data->{page}); + } + + my $oldoutput = $config{destdir}."/".IkiWiki::htmlpage($data->{page}); + + $config{aggregateinternal} = 1; + my $newname = $IkiWiki::Plugin::transient::transientdir."/".htmlfn($data->{page}); + + debug "moving $oldname -> $newname"; + if (-e $newname) { + if (-e $oldname) { + error("$newname already exists"); + } + else { + debug("already renamed to $newname?"); + } + } + elsif (-e $oldname) { + rename($oldname, $newname) || error("$!"); + } + else { + debug("$oldname not found"); + } + if (-e $oldoutput) { + require IkiWiki::Render; + debug("removing output file $oldoutput"); + IkiWiki::prune($oldoutput, $config{destdir}); + } + } + + savestate(); + IkiWiki::unlockwiki; + + unlockaggregate(); +} + +sub needsbuild (@) { + my $needsbuild=shift; + + loadstate(); + + foreach my $feed (values %feeds) { + if (exists $pagesources{$feed->{sourcepage}} && + grep { $_ eq $pagesources{$feed->{sourcepage}} } @$needsbuild) { + # Mark all feeds originating on this page as + # not yet seen; preprocess will unmark those that + # still exist. + markunseen($feed->{sourcepage}); + } + } + + return $needsbuild; +} + +sub preprocess (@) { + my %params=@_; + + foreach my $required (qw{name url}) { + if (! exists $params{$required}) { + error sprintf(gettext("missing %s parameter"), $required) + } + } + + my $feed={}; + my $name=$params{name}; + if (exists $feeds{$name}) { + $feed=$feeds{$name}; + } + else { + $feeds{$name}=$feed; + } + $feed->{name}=$name; + $feed->{sourcepage}=$params{page}; + $feed->{url}=$params{url}; + my $dir=exists $params{dir} ? $params{dir} : $params{page}."/".titlepage($params{name}); + $dir=~s/^\/+//; + ($dir)=$dir=~/$config{wiki_file_regexp}/; + $feed->{dir}=$dir; + $feed->{feedurl}=defined $params{feedurl} ? $params{feedurl} : ""; + $feed->{updateinterval}=defined $params{updateinterval} ? $params{updateinterval} * 60 : 15 * 60; + $feed->{expireage}=defined $params{expireage} ? $params{expireage} : 0; + $feed->{expirecount}=defined $params{expirecount} ? $params{expirecount} : 0; + if (exists $params{template}) { + $params{template}=~s/[^-_a-zA-Z0-9]+//g; + } + else { + $params{template} = "aggregatepost" + } + $feed->{template}=$params{template} . ".tmpl"; + delete $feed->{unseen}; + $feed->{lastupdate}=0 unless defined $feed->{lastupdate}; + $feed->{lasttry}=$feed->{lastupdate} unless defined $feed->{lasttry}; + $feed->{numposts}=0 unless defined $feed->{numposts}; + $feed->{newposts}=0 unless defined $feed->{newposts}; + $feed->{message}=gettext("new feed") unless defined $feed->{message}; + $feed->{error}=0 unless defined $feed->{error}; + $feed->{tags}=[]; + while (@_) { + my $key=shift; + my $value=shift; + if ($key eq 'tag') { + push @{$feed->{tags}}, $value; + } + } + + return "{url}."\">".$feed->{name}.": ". + ($feed->{error} ? "" : "").$feed->{message}. + ($feed->{error} ? "" : ""). + " (".$feed->{numposts}." ".gettext("posts"). + ($feed->{newposts} ? "; ".$feed->{newposts}. + " ".gettext("new") : ""). + ")"; +} + +sub delete (@) { + my @files=@_; + + # Remove feed data for removed pages. + foreach my $file (@files) { + my $page=pagename($file); + markunseen($page); + } +} + +sub markunseen ($) { + my $page=shift; + + foreach my $id (keys %feeds) { + if ($feeds{$id}->{sourcepage} eq $page) { + $feeds{$id}->{unseen}=1; + } + } +} + +my $state_loaded=0; + +sub loadstate () { + return if $state_loaded; + $state_loaded=1; + if (-e "$config{wikistatedir}/aggregate") { + open(IN, "<", "$config{wikistatedir}/aggregate") || + die "$config{wikistatedir}/aggregate: $!"; + while () { + $_=IkiWiki::possibly_foolish_untaint($_); + chomp; + my $data={}; + foreach my $i (split(/ /, $_)) { + my ($field, $val)=split(/=/, $i, 2); + if ($field eq "name" || $field eq "feed" || + $field eq "guid" || $field eq "message") { + $data->{$field}=decode_entities($val, " \t\n"); + } + elsif ($field eq "tag") { + push @{$data->{tags}}, $val; + } + else { + $data->{$field}=$val; + } + } + + if (exists $data->{name}) { + $feeds{$data->{name}}=$data; + } + elsif (exists $data->{guid}) { + $guids{$data->{guid}}=$data; + } + } + + close IN; + } +} + +sub savestate () { + return unless $state_loaded; + garbage_collect(); + my $newfile="$config{wikistatedir}/aggregate.new"; + my $cleanup = sub { unlink($newfile) }; + open (OUT, ">", $newfile) || error("open $newfile: $!", $cleanup); + foreach my $data (values %feeds, values %guids) { + my @line; + foreach my $field (keys %$data) { + if ($field eq "name" || $field eq "feed" || + $field eq "guid" || $field eq "message") { + push @line, "$field=".encode_entities($data->{$field}, " \t\n"); + } + elsif ($field eq "tags") { + push @line, "tag=$_" foreach @{$data->{tags}}; + } + else { + push @line, "$field=".$data->{$field} + if defined $data->{$field}; + } + } + print OUT join(" ", @line)."\n" || error("write $newfile: $!", $cleanup); + } + close OUT || error("save $newfile: $!", $cleanup); + rename($newfile, "$config{wikistatedir}/aggregate") || + error("rename $newfile: $!", $cleanup); + + my $timestamp=undef; + foreach my $feed (keys %feeds) { + my $t=$feeds{$feed}->{lastupdate}+$feeds{$feed}->{updateinterval}; + if (! defined $timestamp || $timestamp > $t) { + $timestamp=$t; + } + } + $newfile=~s/\.new$/time/; + open (OUT, ">", $newfile) || error("open $newfile: $!", $cleanup); + if (defined $timestamp) { + print OUT $timestamp."\n"; + } + close OUT || error("save $newfile: $!", $cleanup); +} + +sub garbage_collect () { + foreach my $name (keys %feeds) { + # remove any feeds that were not seen while building the pages + # that used to contain them + if ($feeds{$name}->{unseen}) { + delete $feeds{$name}; + } + } + + foreach my $guid (values %guids) { + # any guid whose feed is gone should be removed + if (! exists $feeds{$guid->{feed}}) { + if (exists $guid->{page}) { + unlink $IkiWiki::Plugin::transient::transientdir."/".htmlfn($guid->{page}) + || unlink "$config{srcdir}/".htmlfn($guid->{page}); + } + delete $guids{$guid->{guid}}; + } + # handle expired guids + elsif ($guid->{expired} && exists $guid->{page}) { + unlink "$config{srcdir}/".htmlfn($guid->{page}); + unlink $IkiWiki::Plugin::transient::transientdir."/".htmlfn($guid->{page}); + delete $guid->{page}; + delete $guid->{md5}; + } + } +} + +sub mergestate () { + # Load the current state in from disk, and merge into it + # values from the state in memory that might have changed + # during aggregation. + my %myfeeds=%feeds; + my %myguids=%guids; + clearstate(); + loadstate(); + + # All that can change in feed state during aggregation is a few + # fields. + foreach my $name (keys %myfeeds) { + if (exists $feeds{$name}) { + foreach my $field (qw{message lastupdate lasttry + numposts newposts error}) { + $feeds{$name}->{$field}=$myfeeds{$name}->{$field}; + } + } + } + + # New guids can be created during aggregation. + # Guids have a few fields that may be updated during aggregation. + # It's also possible that guids were removed from the on-disk state + # while the aggregation was in process. That would only happen if + # their feed was also removed, so any removed guids added back here + # will be garbage collected later. + foreach my $guid (keys %myguids) { + if (! exists $guids{$guid}) { + $guids{$guid}=$myguids{$guid}; + } + else { + foreach my $field (qw{md5}) { + $guids{$guid}->{$field}=$myguids{$guid}->{$field}; + } + } + } +} + +sub clearstate () { + %feeds=(); + %guids=(); + $state_loaded=0; +} + +sub expire () { + foreach my $feed (values %feeds) { + next unless $feed->{expireage} || $feed->{expirecount}; + my $count=0; + my %seen; + foreach my $item (sort { ($IkiWiki::pagectime{$b->{page}} || 0) <=> ($IkiWiki::pagectime{$a->{page}} || 0) } + grep { exists $_->{page} && $_->{feed} eq $feed->{name} } + values %guids) { + if ($feed->{expireage}) { + my $days_old = (time - ($IkiWiki::pagectime{$item->{page}} || 0)) / 60 / 60 / 24; + if ($days_old > $feed->{expireage}) { + debug(sprintf(gettext("expiring %s (%s days old)"), + $item->{page}, int($days_old))); + $item->{expired}=1; + } + } + elsif ($feed->{expirecount} && + $count >= $feed->{expirecount}) { + debug(sprintf(gettext("expiring %s"), $item->{page})); + $item->{expired}=1; + } + else { + if (! $seen{$item->{page}}) { + $seen{$item->{page}}=1; + $count++; + } + } + } + } +} + +sub needsaggregate () { + return values %feeds if $config{rebuild}; + return grep { time - $_->{lastupdate} >= $_->{updateinterval} } values %feeds; +} + +sub aggregate (@) { + eval q{use Net::INET6Glue::INET_is_INET6}; # may not be available + eval q{use XML::Feed}; + error($@) if $@; + eval q{use URI::Fetch}; + error($@) if $@; + + foreach my $feed (@_) { + $feed->{lasttry}=time; + $feed->{newposts}=0; + $feed->{message}=sprintf(gettext("last checked %s"), + displaytime($feed->{lasttry})); + $feed->{error}=0; + + debug(sprintf(gettext("checking feed %s ..."), $feed->{name})); + + if (! length $feed->{feedurl}) { + my @urls=XML::Feed->find_feeds($feed->{url}); + if (! @urls) { + $feed->{message}=sprintf(gettext("could not find feed at %s"), $feed->{url}); + $feed->{error}=1; + debug($feed->{message}); + next; + } + $feed->{feedurl}=pop @urls; + } + my $res=URI::Fetch->fetch($feed->{feedurl}, + UserAgent => LWP::UserAgent->new( + cookie_jar => $config{cookiejar}, + ), + ); + if (! $res) { + $feed->{message}=URI::Fetch->errstr; + $feed->{error}=1; + debug($feed->{message}); + next; + } + + # lastupdate is only set if we were able to contact the server + $feed->{lastupdate}=$feed->{lasttry}; + + if ($res->status == URI::Fetch::URI_GONE()) { + $feed->{message}=gettext("feed not found"); + $feed->{error}=1; + debug($feed->{message}); + next; + } + my $content=$res->content; + my $f=eval{XML::Feed->parse(\$content)}; + if ($@) { + # One common cause of XML::Feed crashing is a feed + # that contains invalid UTF-8 sequences. Convert + # feed to ascii to try to work around. + $feed->{message}.=" ".sprintf(gettext("(invalid UTF-8 stripped from feed)")); + $f=eval { + $content=Encode::decode_utf8($content, 0); + XML::Feed->parse(\$content) + }; + } + if ($@) { + # Another possibility is badly escaped entities. + $feed->{message}.=" ".sprintf(gettext("(feed entities escaped)")); + $content=~s/\&(?!amp)(\w+);/&$1;/g; + $f=eval { + $content=Encode::decode_utf8($content, 0); + XML::Feed->parse(\$content) + }; + } + if ($@) { + $feed->{message}=gettext("feed crashed XML::Feed!")." ($@)"; + $feed->{error}=1; + debug($feed->{message}); + next; + } + if (! $f) { + $feed->{message}=XML::Feed->errstr; + $feed->{error}=1; + debug($feed->{message}); + next; + } + + foreach my $entry ($f->entries) { + # XML::Feed doesn't work around XML::Atom's bizarre + # API, so we will. Real unicode strings? Yes please. + # See [[bugs/Aggregated_Atom_feeds_are_double-encoded]] + local $XML::Atom::ForceUnicode = 1; + + my $c=$entry->content; + # atom feeds may have no content, only a summary + if (! defined $c && ref $entry->summary) { + $c=$entry->summary; + } + + add_page( + feed => $feed, + copyright => $f->copyright, + title => defined $entry->title ? decode_entities($entry->title) : "untitled", + link => $entry->link, + content => (defined $c && defined $c->body) ? $c->body : "", + guid => defined $entry->id ? $entry->id : time."_".$feed->{name}, + ctime => $entry->issued ? ($entry->issued->epoch || time) : time, + base => (defined $c && $c->can("base")) ? $c->base : undef, + ); + } + } +} + +sub add_page (@) { + my %params=@_; + + my $feed=$params{feed}; + my $guid={}; + my $mtime; + if (exists $guids{$params{guid}}) { + # updating an existing post + $guid=$guids{$params{guid}}; + return if $guid->{expired}; + write_page($feed, $guid, $mtime, \%params); + } + else { + # new post + $guid->{guid}=$params{guid}; + $guids{$params{guid}}=$guid; + $mtime=$params{ctime}; + $feed->{numposts}++; + $feed->{newposts}++; + + # assign it an unused page + my $page=titlepage($params{title}); + # escape slashes and periods in title so it doesn't specify + # directory name or trigger ".." disallowing code. + $page=~s!([/.])!"__".ord($1)."__"!eg; + $page=$feed->{dir}."/".$page; + ($page)=$page=~/$config{wiki_file_regexp}/; + if (! defined $page || ! length $page) { + $page=$feed->{dir}."/item"; + } + my $c=""; + while (exists $IkiWiki::pagecase{lc $page.$c} || + -e $IkiWiki::Plugin::transient::transientdir."/".htmlfn($page.$c) || + -e "$config{srcdir}/".htmlfn($page.$c)) { + $c++ + } + + $guid->{page}=$page; + eval { write_page($feed, $guid, $mtime, \%params) }; + if ($@) { + # assume failure was due to a too long filename + # (or o + $c=""; + $page=$feed->{dir}."/item"; + while (exists $IkiWiki::pagecase{lc $page.$c} || + -e $IkiWiki::Plugin::transient::transientdir."/".htmlfn($page.$c) || + -e "$config{srcdir}/".htmlfn($page.$c)) { + $c++ + } + + $guid->{page}=$page; + write_page($feed, $guid, $mtime, \%params); + } + + debug(sprintf(gettext("creating new page %s"), $page)); + } +} + +sub write_page ($$$$$) { + my $feed=shift; + my $guid=shift; + my $mtime=shift; + my %params=%{shift()}; + + $guid->{feed}=$feed->{name}; + + # To write or not to write? Need to avoid writing unchanged pages + # to avoid unneccessary rebuilding. The mtime from rss cannot be + # trusted; let's use a digest. + eval q{use Digest::MD5 'md5_hex'}; + error($@) if $@; + require Encode; + my $digest=md5_hex(Encode::encode_utf8($params{content})); + return unless ! exists $guid->{md5} || $guid->{md5} ne $digest || $config{rebuild}; + $guid->{md5}=$digest; + + # Create the page. + my $template; + eval { + $template=template($feed->{template}, blind_cache => 1); + }; + if ($@) { + print STDERR gettext("failed to process template:")." $@"; + return; + } + $template->param(title => $params{title}) + if defined $params{title} && length($params{title}); + $template->param(content => wikiescape(htmlabs($params{content}, + defined $params{base} ? $params{base} : $feed->{feedurl}))); + $template->param(name => $feed->{name}); + $template->param(url => $feed->{url}); + $template->param(copyright => $params{copyright}) + if defined $params{copyright} && length $params{copyright}; + $template->param(permalink => IkiWiki::urlabs($params{link}, $feed->{feedurl})) + if defined $params{link}; + if (ref $feed->{tags}) { + $template->param(tags => [map { tag => $_ }, @{$feed->{tags}}]); + } + writefile(htmlfn($guid->{page}), + $IkiWiki::Plugin::transient::transientdir, $template->output); + + if (defined $mtime && $mtime <= time) { + # Set the mtime, this lets the build process get the right + # creation time on record for the new page. + utime $mtime, $mtime, + $IkiWiki::Plugin::transient::transientdir."/".htmlfn($guid->{page}); + # Store it in pagectime for expiry code to use also. + $IkiWiki::pagectime{$guid->{page}}=$mtime + unless exists $IkiWiki::pagectime{$guid->{page}}; + } + else { + # Dummy value for expiry code. + $IkiWiki::pagectime{$guid->{page}}=time + unless exists $IkiWiki::pagectime{$guid->{page}}; + } +} + +sub wikiescape ($) { + # escape accidental wikilinks and preprocessor stuff + return encode_entities(shift, '\[\]'); +} + +sub htmlabs ($$) { + # Convert links in html from relative to absolute. + # Note that this is a heuristic, which is not specified by the rss + # spec and may not be right for all feeds. Also, see Debian + # bug #381359. + my $html=shift; + my $urlbase=shift; + + my $ret=""; + my $p = HTML::Parser->new(api_version => 3); + $p->handler(default => sub { $ret.=join("", @_) }, "text"); + $p->handler(start => sub { + my ($tagname, $pos, $text) = @_; + if (ref $HTML::Tagset::linkElements{$tagname}) { + while (4 <= @$pos) { + # use attribute sets from right to left + # to avoid invalidating the offsets + # when replacing the values + my($k_offset, $k_len, $v_offset, $v_len) = + splice(@$pos, -4); + my $attrname = lc(substr($text, $k_offset, $k_len)); + next unless grep { $_ eq $attrname } @{$HTML::Tagset::linkElements{$tagname}}; + next unless $v_offset; # 0 v_offset means no value + my $v = substr($text, $v_offset, $v_len); + $v =~ s/^([\'\"])(.*)\1$/$2/; + my $new_v=IkiWiki::urlabs($v, $urlbase); + $new_v =~ s/\"/"/g; # since we quote with "" + substr($text, $v_offset, $v_len) = qq("$new_v"); + } + } + $ret.=$text; + }, "tagname, tokenpos, text"); + $p->parse($html); + $p->eof; + + return $ret; +} + +sub htmlfn ($) { + return shift().".".($config{aggregateinternal} ? "_aggregated" : $config{htmlext}); +} + +my $aggregatelock; + +sub lockaggregate () { + # Take an exclusive lock to prevent multiple concurrent aggregators. + # Returns true if the lock was aquired. + if (! -d $config{wikistatedir}) { + mkdir($config{wikistatedir}); + } + open($aggregatelock, '>', "$config{wikistatedir}/aggregatelock") || + error ("cannot open to $config{wikistatedir}/aggregatelock: $!"); + if (! flock($aggregatelock, 2 | 4)) { # LOCK_EX | LOCK_NB + close($aggregatelock) || error("failed closing aggregatelock: $!"); + return 0; + } + return 1; +} + +sub unlockaggregate () { + return close($aggregatelock) if $aggregatelock; + return; +} + +1 diff --git a/IkiWiki/Plugin/amazon_s3.pm b/IkiWiki/Plugin/amazon_s3.pm new file mode 100644 index 000000000..a9da6bf12 --- /dev/null +++ b/IkiWiki/Plugin/amazon_s3.pm @@ -0,0 +1,257 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::amazon_s3; + +use warnings; +no warnings 'redefine'; +use strict; +use IkiWiki 3.00; +use IkiWiki::Render; +use Net::Amazon::S3; + +# Store references to real subs before overriding them. +our %subs; +BEGIN { + foreach my $sub (qw{IkiWiki::writefile IkiWiki::prune}) { + $subs{$sub}=\&$sub; + } +}; + +sub import { + hook(type => "getopt", id => "amazon_s3", call => \&getopt); + hook(type => "getsetup", id => "amazon_s3", call => \&getsetup); + hook(type => "checkconfig", id => "amazon_s3", call => \&checkconfig); +} + +sub getopt () { + eval q{use Getopt::Long}; + error($@) if $@; + Getopt::Long::Configure('pass_through'); + GetOptions("delete-bucket" => sub { + my $bucket=getbucket(); + debug(gettext("deleting bucket..")); + my $resp = $bucket->list_all or die $bucket->err . ": " . $bucket->errstr; + foreach my $key (@{$resp->{keys}}) { + debug("\t".$key->{key}); + $bucket->delete_key($key->{key}) or die $bucket->err . ": " . $bucket->errstr; + } + $bucket->delete_bucket or die $bucket->err . ": " . $bucket->errstr; + debug(gettext("done")); + exit(0); + }); +} + +sub getsetup () { + return + plugin => { + safe => 0, + rebuild => 0, + }, + amazon_s3_key_id => { + type => "string", + example => "XXXXXXXXXXXXXXXXXXXX", + description => "public access key id", + safe => 1, + rebuild => 0, + }, + amazon_s3_key_id => { + type => "string", + example => "$ENV{HOME}/.s3_key", + description => "file holding secret key (must not be readable by others!)", + safe => 0, # ikiwiki reads this file + rebuild => 0, + }, + amazon_s3_bucket => { + type => "string", + example => "mywiki", + description => "globally unique name of bucket to store wiki in", + safe => 1, + rebuild => 1, + }, + amazon_s3_prefix => { + type => "string", + example => "wiki/", + description => "a prefix to prepend to each page name", + safe => 1, + rebuild => 1, + }, + amazon_s3_location => { + type => "string", + example => "EU", + description => "which S3 datacenter to use (leave blank for default)", + safe => 1, + rebuild => 1, + }, + amazon_s3_dupindex => { + type => "boolean", + example => 0, + description => "store each index file twice? (allows urls ending in \"/index.html\" and \"/\")", + safe => 1, + rebuild => 1, + }, +} + +sub checkconfig { + foreach my $field (qw{amazon_s3_key_id amazon_s3_key_file + amazon_s3_bucket}) { + if (! exists $config{$field} || ! defined $config{$field}) { + error(sprintf(gettext("Must specify %s"), $field)); + } + } + if (! exists $config{amazon_s3_prefix} || + ! defined $config{amazon_s3_prefix}) { + $config{amazon_s3_prefix}="wiki/"; + } +} + +{ +my $bucket; +sub getbucket { + return $bucket if defined $bucket; + + open(IN, "<", $config{amazon_s3_key_file}) || error($config{amazon_s3_key_file}.": ".$!); + my $key=; + chomp $key; + close IN; + + my $s3=Net::Amazon::S3->new({ + aws_access_key_id => $config{amazon_s3_key_id}, + aws_secret_access_key => $key, + retry => 1, + }); + + # make sure the bucket exists + if (exists $config{amazon_s3_location}) { + $bucket=$s3->add_bucket({ + bucket => $config{amazon_s3_bucket}, + location_constraint => $config{amazon_s3_location}, + }); + } + else { + $bucket=$s3->add_bucket({ + bucket => $config{amazon_s3_bucket}, + }); + } + + 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"); + } + + return $bucket; +} +} + +# Given a file, return any S3 keys associated with it. +sub file2keys ($) { + my $file=shift; + + my @keys; + if ($file =~ /^\Q$config{destdir}\/\E(.*)/) { + push @keys, $config{amazon_s3_prefix}.$1; + + # Munge foo/index.html to foo/ + if ($keys[0]=~/(^|.*\/)index.$config{htmlext}$/) { + # A duplicate might need to be stored under the + # unmunged name too. + if (!$config{usedirs} || $config{amazon_s3_dupindex}) { + push @keys, $1; + } + else { + @keys=($1); + } + } + } + return @keys; +} + +package IkiWiki; +use File::MimeInfo; +use Encode; + +# This is a wrapper around the real writefile. +sub writefile ($$$;$$) { + my $file=shift; + my $destdir=shift; + my $content=shift; + my $binary=shift; + my $writer=shift; + + # 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. + if (@keys) { + my $bucket=IkiWiki::Plugin::amazon_s3::getbucket(); + + # The http layer tries to downgrade utf-8 + # content, but that can fail (see + # http://rt.cpan.org/Ticket/Display.html?id=35710), + # so force convert it to bytes. + $content=encode_utf8($content) if defined $content; + + my %opts=( + acl_short => 'public-read', + content_type => mimetype("$destdir/$file"), + ); + + # If there are multiple keys to write, data is sent + # multiple times. + # TODO: investigate using the new copy operation. + # (It may not be robust enough.) + foreach my $key (@keys) { + my $res; + if (! $writer) { + $res=$bucket->add_key($key, $content, \%opts); + } + else { + # This test for empty files is a workaround + # for this bug: + # http://rt.cpan.org//Ticket/Display.html?id=35731 + if (-z "$destdir/$file") { + $res=$bucket->add_key($key, "", \%opts); + } + else { + # read back in the file that the writer emitted + $res=$bucket->add_key_filename($key, "$destdir/$file", \%opts); + } + } + if (! $res) { + error(gettext("Failed to save file to S3: "). + $bucket->err.": ".$bucket->errstr."\n"); + } + } + } + + return $ret; +} + +# This is a wrapper around the real prune. +sub prune ($;$) { + my $file=shift; + my $up_to=shift; + + my @keys=IkiWiki::Plugin::amazon_s3::file2keys($file); + + # Prune files out of S3 too. + if (@keys) { + my $bucket=IkiWiki::Plugin::amazon_s3::getbucket(); + + foreach my $key (@keys) { + my $res=$bucket->delete_key($key); + if (! $res) { + error(gettext("Failed to delete file from S3: "). + $bucket->err.": ".$bucket->errstr."\n"); + } + } + } + + return $IkiWiki::Plugin::amazon_s3::subs{'IkiWiki::prune'}->($file, $up_to); +} + +1 diff --git a/IkiWiki/Plugin/anonok.pm b/IkiWiki/Plugin/anonok.pm new file mode 100644 index 000000000..0e74cbfad --- /dev/null +++ b/IkiWiki/Plugin/anonok.pm @@ -0,0 +1,51 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::anonok; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "anonok", call => \&getsetup); + hook(type => "canedit", id => "anonok", call => \&canedit); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "auth", + }, + anonok_pagespec => { + type => "pagespec", + example => "*/discussion", + description => "PageSpec to limit which pages anonymous users can edit", + link => "ikiwiki/PageSpec", + safe => 1, + rebuild => 0, + }, +} + +sub canedit ($$$) { + my $page=shift; + my $cgi=shift; + my $session=shift; + + my $ret; + + if (exists $config{anonok_pagespec} && length $config{anonok_pagespec}) { + if (pagespec_match($page, $config{anonok_pagespec}, + location => $page)) { + return ""; + } + else { + return undef; + } + } + else { + return ""; + } +} + +1 diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm new file mode 100644 index 000000000..83dd120f6 --- /dev/null +++ b/IkiWiki/Plugin/attachment.pm @@ -0,0 +1,393 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::attachment; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + add_underlay("attachment"); + add_underlay("javascript"); + add_underlay("jquery"); + hook(type => "getsetup", id => "attachment", call => \&getsetup); + hook(type => "checkconfig", id => "attachment", call => \&checkconfig); + hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); + hook(type => "formbuilder", id => "attachment", call => \&formbuilder, last => 1); + IkiWiki::loadplugin("filecheck"); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "web", + }, + allowed_attachments => { + type => "pagespec", + example => "virusfree() and mimetype(image/*) and maxsize(50kb)", + description => "enhanced PageSpec specifying what attachments are allowed", + link => "ikiwiki/PageSpec/attachment", + safe => 1, + rebuild => 0, + }, + virus_checker => { + type => "string", + example => "clamdscan -", + description => "virus checker program (reads STDIN, returns nonzero if virus found)", + safe => 0, # executed + rebuild => 0, + }, +} + +sub check_canattach ($$;$) { + my $session=shift; + my $dest=shift; # where it's going to be put, under the srcdir + my $file=shift; # the path to the attachment currently + + # Don't allow an attachment to be uploaded with the same name as an + # existing page. + if (exists $IkiWiki::pagesources{$dest} && + $IkiWiki::pagesources{$dest} ne $dest) { + error(sprintf(gettext("there is already a page named %s"), $dest)); + } + + # Use a special pagespec to test that the attachment is valid. + my $allowed=1; + if (defined $config{allowed_attachments} && + length $config{allowed_attachments}) { + $allowed=pagespec_match($dest, + $config{allowed_attachments}, + file => $file, + user => $session->param("name"), + ip => $session->remote_addr(), + ); + } + + if (! $allowed) { + error(gettext("prohibited by allowed_attachments")." ($allowed)"); + } + else { + return 1; + } +} + +sub checkconfig () { + $config{cgi_disable_uploads}=0; +} + +sub formbuilder_setup (@) { + my %params=@_; + my $form=$params{form}; + my $q=$params{cgi}; + + if (defined $form->field("do") && ($form->field("do") eq "edit" || + $form->field("do") eq "create")) { + # Add attachment field, set type to multipart. + $form->enctype(&CGI::MULTIPART); + $form->field(name => 'attachment', type => 'file'); + # These buttons are not put in the usual place, so + # are not added to the normal formbuilder button list. + $form->tmpl_param("field-upload" => ''); + $form->tmpl_param("field-link" => ''); + + # Add all the javascript used by the attachments interface. + require IkiWiki::Plugin::toggle; + my $js=IkiWiki::Plugin::toggle::include_javascript($params{page}); + $js.=''."\n"; + my @jsfiles=qw{jquery.min jquery-ui.min + jquery.tmpl.min jquery.iframe-transport + jquery.fileupload jquery.fileupload-ui + }; + foreach my $file (@jsfiles) { + $js.=''."\n"; + } + $form->tmpl_param("javascript" => $js); + + # Start with the attachments interface toggled invisible, + # but if it was used, keep it open. + if ($form->submitted ne "Upload Attachment" && + (! defined $q->param("attachment_select") || + ! length $q->param("attachment_select"))) { + $form->tmpl_param("attachments-class" => "toggleable"); + } + else { + $form->tmpl_param("attachments-class" => "toggleable-open"); + } + + # Save attachments in holding area before previewing and + # saving. + if ($form->submitted eq "Preview" || + $form->submitted eq "Save Page") { + attachments_save($form, $params{session}); + } + } +} + +sub formbuilder (@) { + my %params=@_; + my $form=$params{form}; + my $q=$params{cgi}; + + return if ! defined $form->field("do") || ($form->field("do") ne "edit" && $form->field("do") ne "create") ; + + my $filename=Encode::decode_utf8($q->param('attachment')); + if (defined $filename && length $filename) { + attachment_store($filename, $form, $q, $params{session}); + } + + if ($form->submitted eq "Save Page") { + attachments_save($form, $params{session}); + } + + if ($form->submitted eq "Insert Links") { + my $page=quotemeta(Encode::decode_utf8($q->param("page"))); + my $add=""; + foreach my $f ($q->param("attachment_select")) { + $f=Encode::decode_utf8($f); + $f=~s/^$page\///; + if (IkiWiki::isinlinableimage($f) && + IkiWiki::Plugin::img->can("import")) { + $add.='[[!img '.$f.' align="right" size="" alt=""]]'; + } + else { + $add.="[[$f]]"; + } + $add.="\n"; + } + $form->field(name => 'editcontent', + value => $form->field('editcontent')."\n\n".$add, + force => 1) if length $add; + } + + # Generate the attachment list only after having added any new + # attachments. + $form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]); +} + +sub attachment_holding_location { + my $page=attachment_location(shift); + + my $dir=$config{wikistatedir}."/attachments/". + IkiWiki::possibly_foolish_untaint(linkpage($page)); + $dir=~s/\/$//; + return $dir; +} + +sub is_held_attachment { + my $attachment=shift; + + my $f=attachment_holding_location($attachment); + if (-f $f) { + return $f + } + else { + return undef; + } +} + +# Stores the attachment in a holding area, not yet in the wiki proper. +sub attachment_store { + my $filename=shift; + my $form=shift; + my $q=shift; + my $session=shift; + + # This is an (apparently undocumented) way to get the name + # of the temp file that CGI writes the upload to. + my $tempfile=$q->tmpFileName($filename); + if (! defined $tempfile || ! length $tempfile) { + # perl 5.8 needs an alternative, awful method + if ($q =~ /HASH/ && exists $q->{'.tmpfiles'}) { + foreach my $key (keys(%{$q->{'.tmpfiles'}})) { + $tempfile=$q->tmpFileName(\$key); + last if defined $tempfile && length $tempfile; + } + } + if (! defined $tempfile || ! length $tempfile) { + error("CGI::tmpFileName failed to return the uploaded file name"); + } + } + + $filename=IkiWiki::basename($filename); + $filename=~s/.*\\+(.+)/$1/; # hello, windows + $filename=IkiWiki::possibly_foolish_untaint(linkpage($filename)); + my $dest=attachment_holding_location($form->field('page')); + + # Check that the user is allowed to edit the attachment. + my $final_filename= + linkpage(IkiWiki::possibly_foolish_untaint( + attachment_location($form->field('page')))). + $filename; + eval { + if (IkiWiki::file_pruned($final_filename)) { + error(gettext("bad attachment filename")); + } + IkiWiki::check_canedit($final_filename, $q, $session); + # And that the attachment itself is acceptable. + check_canattach($session, $final_filename, $tempfile); + }; + if ($@) { + json_response($q, $form, $dest."/".$filename, $@); + error $@; + } + + # Move the attachment into holding directory. + # Try to use a fast rename; fall back to copying. + IkiWiki::prep_writefile($filename, $dest); + unlink($dest."/".$filename); + if (rename($tempfile, $dest."/".$filename)) { + # The temp file has tight permissions; loosen up. + chmod(0666 & ~umask, $dest."/".$filename); + } + else { + my $fh=$q->upload('attachment'); + if (! defined $fh || ! ref $fh) { + # needed by old CGI versions + $fh=$q->param('attachment'); + if (! defined $fh || ! ref $fh) { + # even that doesn't always work, + # fall back to opening the tempfile + $fh=undef; + open($fh, "<", $tempfile) || error("failed to open \"$tempfile\": $!"); + } + } + binmode($fh); + require IkiWiki::Render; + writefile($filename, $dest, undef, 1, sub { + IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_); + }); + } + + json_response($q, $form, $dest."/".$filename, stored_msg()); +} + +# Save all stored attachments for a page. +sub attachments_save { + my $form=shift; + my $session=shift; + + # Move attachments out of holding directory. + my @attachments; + my $dir=attachment_holding_location($form->field('page')); + foreach my $filename (glob("$dir/*")) { + $filename=Encode::decode_utf8($filename); + next unless -f $filename; + my $destdir=linkpage(IkiWiki::possibly_foolish_untaint( + attachment_location($form->field('page')))); + my $absdestdir=$config{srcdir}."/".$destdir; + my $destfile=IkiWiki::basename($filename); + my $dest=$absdestdir.$destfile; + unlink($dest); + IkiWiki::prep_writefile($destfile, $absdestdir); + rename($filename, $dest); + push @attachments, $destdir.$destfile; + } + return unless @attachments; + require IkiWiki::Render; + IkiWiki::prune($dir, $config{wikistatedir}."/attachments"); + + # Check the attachments in and trigger a wiki refresh. + if ($config{rcs}) { + IkiWiki::rcs_add($_) foreach @attachments; + IkiWiki::disable_commit_hook(); + IkiWiki::rcs_commit_staged( + message => gettext("attachment upload"), + session => $session, + ); + IkiWiki::enable_commit_hook(); + IkiWiki::rcs_update(); + } + IkiWiki::refresh(); + IkiWiki::saveindex(); +} + +sub attachment_location ($) { + my $page=shift; + + # Put the attachment in a subdir of the page it's attached + # to, unless that page is the "index" page. + return "" if $page eq 'index'; + $page.="/" if length $page; + + return $page; +} + +sub attachment_list ($) { + my $page=shift; + my $loc=attachment_location($page); + + my $std=sub { + my $file=shift; + my $mtime=shift; + my $date=shift; + my $size=shift; + + name => $file, + size => IkiWiki::Plugin::filecheck::humansize($size), + mtime => $date, + mtime_raw => $mtime, + }; + + # attachments already in the wiki + my %attachments; + foreach my $f (values %pagesources) { + if (! defined pagetype($f) && + $f=~m/^\Q$loc\E[^\/]+$/) { + $attachments{$f}={ + $std->($f, $IkiWiki::pagemtime{$f}, displaytime($IkiWiki::pagemtime{$f}), (stat($f))[7]), + link => htmllink($page, $page, $f, noimageinline => 1), + }; + } + } + + # attachments in holding directory + my $dir=attachment_holding_location($page); + my $heldmsg=gettext("this attachment is not yet saved"); + foreach my $file (glob("$dir/*")) { + $file=Encode::decode_utf8($file); + next unless -f $file; + my $base=IkiWiki::basename($file); + my $f=$loc.$base; + $attachments{$f}={ + $std->($f, (stat($file))[9]*2, stored_msg(), (stat(_))[7]), + link => $base, + } + } + + # Sort newer attachments to the end of the list. + return sort { $a->{mtime_raw} <=> $b->{mtime_raw} || $a->{link} cmp $b->{link} } + values %attachments; +} + +sub stored_msg { + gettext("just uploaded"); +} + +sub json_response ($$$$) { + my $q=shift; + my $form=shift; + my $filename=shift; + my $stored_msg=shift; + + if (! defined $form->submitted || + $form->submitted ne "Upload Attachment") { + eval q{use JSON}; + error $@ if $@; + print "Content-type: text/html\n\n"; + my $size=-s $filename; + print to_json([ + { + name => IkiWiki::basename($filename), + size => $size, + humansize => IkiWiki::Plugin::filecheck::humansize($size), + stored_msg => $stored_msg, + + } + ]); + exit 0; + } +} + +1 diff --git a/IkiWiki/Plugin/autoindex.pm b/IkiWiki/Plugin/autoindex.pm new file mode 100644 index 000000000..78571b276 --- /dev/null +++ b/IkiWiki/Plugin/autoindex.pm @@ -0,0 +1,139 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::autoindex; + +use warnings; +use strict; +use IkiWiki 3.00; +use Encode; + +sub import { + hook(type => "checkconfig", id => "autoindex", call => \&checkconfig); + hook(type => "getsetup", id => "autoindex", call => \&getsetup); + hook(type => "refresh", id => "autoindex", call => \&refresh); + IkiWiki::loadplugin("transient"); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + }, + autoindex_commit => { + type => "boolean", + example => 1, + default => 1, + description => "commit autocreated index pages", + safe => 1, + rebuild => 0, + }, +} + +sub checkconfig () { + if (! defined $config{autoindex_commit}) { + $config{autoindex_commit} = 1; + } +} + +sub genindex ($) { + my $page=shift; + my $file=newpagefile($page, $config{default_pageext}); + + add_autofile($file, "autoindex", sub { + my $message = sprintf(gettext("creating index page %s"), + $page); + debug($message); + + my $dir = $config{srcdir}; + if (! $config{autoindex_commit}) { + $dir = $IkiWiki::Plugin::transient::transientdir; + } + + my $template = template("autoindex.tmpl"); + $template->param(page => $page); + writefile($file, $dir, $template->output); + + if ($config{rcs} && $config{autoindex_commit}) { + IkiWiki::disable_commit_hook(); + IkiWiki::rcs_add($file); + IkiWiki::rcs_commit_staged(message => $message); + IkiWiki::enable_commit_hook(); + } + }); +} + +sub refresh () { + eval q{use File::Find}; + error($@) if $@; + eval q{use Cwd}; + error($@) if $@; + my $origdir=getcwd(); + + my (%pages, %dirs); + foreach my $dir ($config{srcdir}, @{$config{underlaydirs}}, $config{underlaydir}) { + next if $dir eq $IkiWiki::Plugin::transient::transientdir; + chdir($dir) || next; + + find({ + no_chdir => 1, + wanted => sub { + my $file=decode_utf8($_); + $file=~s/^\.\/?//; + return unless length $file; + if (IkiWiki::file_pruned($file)) { + $File::Find::prune=1; + } + elsif (! -l $_) { + my ($f) = $file =~ /$config{wiki_file_regexp}/; # untaint + return unless defined $f; + return if $f =~ /\._([^.]+)$/; # skip internal page + if (! -d _) { + $pages{pagename($f)}=1; + } + elsif ($dir eq $config{srcdir}) { + $dirs{$f}=1; + } + } + } + }, '.'); + + chdir($origdir) || die "chdir $origdir: $!"; + } + + # Compatibility code. + # + # {deleted} contains pages that have been deleted at some point. + # This plugin used to delete from the hash sometimes, but no longer + # does; in [[todo/autoindex_should_use_add__95__autofile]] Joey + # thought the old behaviour was probably a bug. + # + # The effect of listing a page in {deleted} was to avoid re-creating + # it; we migrate these pages to {autofile} which has the same effect. + # However, {autofile} contains source filenames whereas {deleted} + # contains page names. + my %deleted; + if (ref $wikistate{autoindex}{deleted}) { + %deleted=%{$wikistate{autoindex}{deleted}}; + delete $wikistate{autoindex}{deleted}; + } + elsif (ref $pagestate{index}{autoindex}{deleted}) { + # an even older version + %deleted=%{$pagestate{index}{autoindex}{deleted}}; + delete $pagestate{index}{autoindex}; + } + + if (keys %deleted) { + foreach my $dir (keys %deleted) { + my $file=newpagefile($dir, $config{default_pageext}); + $wikistate{autoindex}{autofile}{$file} = 1; + } + } + + foreach my $dir (keys %dirs) { + if (! exists $pages{$dir} && grep /^$dir\/.*/, keys %pages) { + genindex($dir); + } + } +} + +1 diff --git a/IkiWiki/Plugin/blogspam.pm b/IkiWiki/Plugin/blogspam.pm new file mode 100644 index 000000000..e48ed729f --- /dev/null +++ b/IkiWiki/Plugin/blogspam.pm @@ -0,0 +1,130 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::blogspam; + +use warnings; +use strict; +use IkiWiki 3.00; +use Encode; + +my $defaulturl='http://test.blogspam.net:8888/'; + +sub import { + hook(type => "getsetup", id => "blogspam", call => \&getsetup); + hook(type => "checkconfig", id => "blogspam", call => \&checkconfig); + hook(type => "checkcontent", id => "blogspam", call => \&checkcontent); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "auth", + }, + blogspam_pagespec => { + type => 'pagespec', + example => 'postcomment(*)', + description => 'PageSpec of pages to check for spam', + link => 'ikiwiki/PageSpec', + safe => 1, + rebuild => 0, + }, + blogspam_options => { + type => "string", + example => "blacklist=1.2.3.4,blacklist=8.7.6.5,max-links=10", + description => "options to send to blogspam server", + link => "http://blogspam.net/api/testComment.html#options", + safe => 1, + rebuild => 0, + }, + blogspam_server => { + type => "string", + default => $defaulturl, + description => "blogspam server XML-RPC url", + safe => 1, + rebuild => 0, + }, +} + +sub checkconfig () { + # This is done at checkconfig time because printing an error + # if the module is missing when a spam is posted would not + # let the admin know about the problem. + eval q{ + use RPC::XML; + use RPC::XML::Client; + $RPC::XML::ENCODING = 'utf-8'; + }; + error $@ if $@; +} + +sub checkcontent (@) { + my %params=@_; + my $session=$params{session}; + + my $spec='!admin()'; + if (exists $config{blogspam_pagespec} && + length $config{blogspam_pagespec}) { + $spec.=" and (".$config{blogspam_pagespec}.")"; + } + + my $user=$session->param("name"); + return undef unless pagespec_match($params{page}, $spec, + (defined $user ? (user => $user) : ()), + (defined $session->remote_addr() ? (ip => $session->remote_addr()) : ()), + location => $params{page}); + + my $url=$defaulturl; + $url = $config{blogspam_server} if exists $config{blogspam_server}; + + my $client = RPC::XML::Client->new($url); + + my @options = split(",", $config{blogspam_options}) + if exists $config{blogspam_options}; + + # Allow short comments and whitespace-only edits, unless the user + # has overridden min-words themselves. + push @options, "min-words=0" + unless grep /^min-words=/i, @options; + # Wiki pages can have a lot of urls, unless the user specifically + # wants to limit them. + push @options, "exclude=lotsaurls" + unless grep /^max-links/i, @options; + # Unless the user specified a size check, disable such checking. + push @options, "exclude=size" + unless grep /^(?:max|min)-size/i, @options; + # This test has absurd false positives on words like "alpha" + # and "buy". + push @options, "exclude=stopwords"; + + my %req=( + ip => $session->remote_addr(), + comment => encode_utf8(defined $params{diff} ? $params{diff} : $params{content}), + subject => encode_utf8(defined $params{subject} ? $params{subject} : ""), + name => encode_utf8(defined $params{author} ? $params{author} : ""), + link => encode_utf8(exists $params{url} ? $params{url} : ""), + options => join(",", @options), + site => encode_utf8($config{url}), + version => "ikiwiki ".$IkiWiki::version, + ); + my $res = $client->send_request('testComment', \%req); + + if (! ref $res || ! defined $res->value) { + debug("failed to get response from blogspam server ($url)"); + return undef; + } + elsif ($res->value =~ /^SPAM:(.*)/) { + eval q{use Data::Dumper}; + debug("blogspam server reports ".$res->value.": ".Dumper(\%req)); + return gettext("Sorry, but that looks like spam to blogspam: ").$1; + } + elsif ($res->value ne 'OK') { + debug("blogspam server failure: ".$res->value); + return undef; + } + else { + return undef; + } +} + +1 diff --git a/IkiWiki/Plugin/brokenlinks.pm b/IkiWiki/Plugin/brokenlinks.pm new file mode 100644 index 000000000..8ee734bf9 --- /dev/null +++ b/IkiWiki/Plugin/brokenlinks.pm @@ -0,0 +1,56 @@ +#!/usr/bin/perl +# Provides a list of broken links. +package IkiWiki::Plugin::brokenlinks; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "brokenlinks", call => \&getsetup); + hook(type => "preprocess", id => "brokenlinks", call => \&preprocess); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +sub preprocess (@) { + my %params=@_; + $params{pages}="*" unless defined $params{pages}; + + my @broken; + foreach my $link (keys %IkiWiki::brokenlinks) { + next if $link =~ /.*\/\Q$config{discussionpage}\E/i && $config{discussion}; + + my @pages=pagespec_match_list($params{page}, $params{pages}, + list => $IkiWiki::brokenlinks{$link}, + # needs to update when links on a page change + deptype => deptype("links") + ); + next unless @pages; + + my $page=$IkiWiki::brokenlinks{$link}->[0]; + push @broken, sprintf(gettext("%s from %s"), + htmllink($page, $params{destpage}, $link, noimageinline => 1), + join(", ", map { + htmllink($params{page}, $params{destpage}, $_, noimageinline => 1) + } @pages) + ); + } + + return gettext("There are no broken links!") unless @broken; + return "
    \n" + .join("\n", + map { + "
  • $_
  • " + } + sort @broken) + ."
\n"; +} + +1 diff --git a/IkiWiki/Plugin/bzr.pm b/IkiWiki/Plugin/bzr.pm new file mode 100644 index 000000000..99a07d2c0 --- /dev/null +++ b/IkiWiki/Plugin/bzr.pm @@ -0,0 +1,330 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::bzr; + +use warnings; +use strict; +use IkiWiki; +use Encode; +use URI::Escape q{uri_escape_utf8}; +use open qw{:utf8 :std}; + +sub import { + hook(type => "checkconfig", id => "bzr", call => \&checkconfig); + hook(type => "getsetup", id => "bzr", call => \&getsetup); + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); + hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); + hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); + hook(type => "rcs", id => "rcs_commit_staged", call => \&rcs_commit_staged); + hook(type => "rcs", id => "rcs_add", call => \&rcs_add); + hook(type => "rcs", id => "rcs_remove", call => \&rcs_remove); + hook(type => "rcs", id => "rcs_rename", call => \&rcs_rename); + 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 () { + if (defined $config{bzr_wrapper} && length $config{bzr_wrapper}) { + push @{$config{wrappers}}, { + wrapper => $config{bzr_wrapper}, + wrappermode => (defined $config{bzr_wrappermode} ? $config{bzr_wrappermode} : "06755"), + }; + } +} + +sub getsetup () { + return + plugin => { + safe => 0, # rcs plugin + rebuild => undef, + section => "rcs", + }, + bzr_wrapper => { + type => "string", + #example => "", # FIXME add example + description => "bzr post-commit hook to generate", + safe => 0, # file + rebuild => 0, + }, + bzr_wrappermode => { + type => "string", + example => '06755', + description => "mode for bzr_wrapper (can safely be made suid)", + safe => 0, + rebuild => 0, + }, + historyurl => { + type => "string", + #example => "", # FIXME add example + description => "url to show file history, using loggerhead ([[file]] substituted)", + safe => 1, + rebuild => 1, + }, + diffurl => { + type => "string", + example => "http://example.com/revision?start_revid=[[r2]]#[[file]]-s", + description => "url to view a diff, using loggerhead ([[file]] and [[r2]] substituted)", + safe => 1, + rebuild => 1, + }, +} + +sub bzr_log ($) { + my $out = shift; + my @infos = (); + my $key = undef; + + my %info; + while (<$out>) { + my $line = $_; + my ($value); + if ($line =~ /^message:/) { + $key = "message"; + $info{$key} = ""; + } + elsif ($line =~ /^(modified|added|renamed|renamed and modified|removed):/) { + $key = "files"; + $info{$key} = "" unless defined $info{$key}; + } + elsif (defined($key) and $line =~ /^ (.*)/) { + $info{$key} .= "$1\n"; + } + elsif ($line eq "------------------------------------------------------------\n") { + push @infos, {%info} if keys %info; + %info = (); + $key = undef; + } + elsif ($line =~ /: /) { + chomp $line; + if ($line =~ /^revno: (\d+)/) { + $key = "revno"; + $value = $1; + } + else { + ($key, $value) = split /: +/, $line, 2; + } + $info{$key} = $value; + } + } + close $out; + push @infos, {%info} if keys %info; + + return @infos; +} + +sub rcs_update () { + my @cmdline = ("bzr", "update", "--quiet", $config{srcdir}); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } +} + +sub rcs_prepedit ($) { + return ""; +} + +sub bzr_author ($) { + my $session=shift; + + return unless defined $session; + + my $user=$session->param("name"); + my $ipaddr=$session->remote_addr(); + + if (defined $user) { + return IkiWiki::possibly_foolish_untaint($user); + } + elsif (defined $ipaddr) { + return "Anonymous from ".IkiWiki::possibly_foolish_untaint($ipaddr); + } + else { + return "Anonymous"; + } +} + +sub rcs_commit (@) { + my %params=@_; + + my $user=bzr_author($params{session}); + + $params{message} = IkiWiki::possibly_foolish_untaint($params{message}); + if (! length $params{message}) { + $params{message} = "no message given"; + } + + my @cmdline = ("bzr", "commit", "--quiet", "-m", $params{message}, + (defined $user ? ("--author", $user) : ()), + $config{srcdir}."/".$params{file}); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } + + return undef; # success +} + +sub rcs_commit_staged (@) { + my %params=@_; + + my $user=bzr_author($params{session}); + + $params{message} = IkiWiki::possibly_foolish_untaint($params{message}); + if (! length $params{message}) { + $params{message} = "no message given"; + } + + my @cmdline = ("bzr", "commit", "--quiet", "-m", $params{message}, + (defined $user ? ("--author", $user) : ()), + $config{srcdir}); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } + + return undef; # success +} + +sub rcs_add ($) { + my ($file) = @_; + + my @cmdline = ("bzr", "add", "--quiet", "$config{srcdir}/$file"); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } +} + +sub rcs_remove ($) { + my ($file) = @_; + + my @cmdline = ("bzr", "rm", "--quiet", "$config{srcdir}/$file"); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } +} + +sub rcs_rename ($$) { + my ($src, $dest) = @_; + + my $parent = IkiWiki::dirname($dest); + if (system("bzr", "add", "--quiet", "$config{srcdir}/$parent") != 0) { + warn("bzr add $parent failed\n"); + } + + my @cmdline = ("bzr", "mv", "--quiet", "$config{srcdir}/$src", "$config{srcdir}/$dest"); + if (system(@cmdline) != 0) { + warn "'@cmdline' failed: $!"; + } +} + +sub rcs_recentchanges ($) { + my ($num) = @_; + + my @cmdline = ("bzr", "log", "-v", "--show-ids", "--limit", $num, + $config{srcdir}); + open (my $out, "@cmdline |"); + + eval q{use Date::Parse}; + error($@) if $@; + + my @ret; + foreach my $info (bzr_log($out)) { + my @pages = (); + my @message = (); + + foreach my $msgline (split(/\n/, $info->{message})) { + push @message, { line => $msgline }; + } + + foreach my $file (split(/\n/, $info->{files})) { + my ($filename, $fileid) = ($file =~ /^(.*?) +([^ ]+)$/); + + # Skip directories + next if ($filename =~ /\/$/); + + # Skip source name in renames + $filename =~ s/^.* => //; + + my $efilename = uri_escape_utf8($filename); + + my $diffurl = defined $config{'diffurl'} ? $config{'diffurl'} : ""; + $diffurl =~ s/\[\[file\]\]/$efilename/go; + $diffurl =~ s/\[\[file-id\]\]/$fileid/go; + $diffurl =~ s/\[\[r2\]\]/$info->{revno}/go; + + push @pages, { + page => pagename($filename), + diffurl => $diffurl, + }; + } + + my $user = $info->{"committer"}; + if (defined($info->{"author"})) { $user = $info->{"author"}; } + $user =~ s/\s*<.*>\s*$//; + $user =~ s/^\s*//; + + push @ret, { + rev => $info->{"revno"}, + user => $user, + committype => "bzr", + when => str2time($info->{"timestamp"}), + message => [@message], + pages => [@pages], + }; + } + + return @ret; +} + +sub rcs_diff ($;$) { + my $taintedrev=shift; + my $maxlines=shift; + my ($rev) = $taintedrev =~ /^(\d+(\.\d+)*)$/; # untaint + + my $prevspec = "before:" . $rev; + my $revspec = "revno:" . $rev; + my @cmdline = ("bzr", "diff", "--old", $config{srcdir}, + "--new", $config{srcdir}, + "-r", $prevspec . ".." . $revspec); + open (my $out, "@cmdline |"); + my @lines; + while (my $line=<$out>) { + last if defined $maxlines && @lines == $maxlines; + push @lines, $line; + } + if (wantarray) { + return @lines; + } + else { + return join("", @lines); + } +} + +sub extract_timestamp (@) { + open (my $out, "-|", @_); + my @log = bzr_log($out); + + if (length @log < 1) { + return 0; + } + + eval q{use Date::Parse}; + error($@) if $@; + + my $time = str2time($log[0]->{"timestamp"}); + return $time; +} + +sub rcs_getctime ($) { + my ($file) = @_; + + my @cmdline = ("bzr", "log", "--forward", "--limit", '1', "$config{srcdir}/$file"); + return extract_timestamp(@cmdline); +} + +sub rcs_getmtime ($) { + my ($file) = @_; + + my @cmdline = ("bzr", "log", "--limit", '1', "$config{srcdir}/$file"); + return extract_timestamp(@cmdline); +} + +1 diff --git a/IkiWiki/Plugin/calendar.pm b/IkiWiki/Plugin/calendar.pm new file mode 100644 index 000000000..8ca6dd826 --- /dev/null +++ b/IkiWiki/Plugin/calendar.pm @@ -0,0 +1,525 @@ +#! /usr/bin/perl +# Copyright (c) 2006, 2007 Manoj Srivastava +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +require 5.002; +package IkiWiki::Plugin::calendar; + +use warnings; +use strict; +use IkiWiki 3.00; +use Time::Local; + +my $time=time; +my @now=localtime($time); + +sub import { + hook(type => "getsetup", id => "calendar", call => \&getsetup); + hook(type => "needsbuild", id => "calendar", call => \&needsbuild); + hook(type => "preprocess", id => "calendar", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, + archivebase => { + type => "string", + example => "archives", + description => "base of the archives hierarchy", + safe => 1, + rebuild => 1, + }, + archive_pagespec => { + type => "pagespec", + example => "page(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 (@) { + my %params=@_; + return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 == 0)); +} + +sub month_days { + my %params=@_; + my $days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)[$params{month}-1]; + if ($params{month} == 2 && is_leap_year(%params)) { + $days_in_month++; + } + return $days_in_month; +} + +sub format_month (@) { + my %params=@_; + + my %linkcache; + foreach my $p (pagespec_match_list($params{page}, + "creation_year($params{year}) and creation_month($params{month}) and ($params{pages})", + # add presence dependencies to update + # month calendar when pages are added/removed + deptype => deptype("presence"))) { + my $mtime = $IkiWiki::pagectime{$p}; + my @date = localtime($mtime); + my $mday = $date[3]; + my $month = $date[4] + 1; + my $year = $date[5] + 1900; + my $mtag = sprintf("%02d", $month); + + if (! $linkcache{"$year/$mtag/$mday"}) { + $linkcache{"$year/$mtag/$mday"} = []; + } + push(@{$linkcache{"$year/$mtag/$mday"}}, $p); + } + + my $pmonth = $params{month} - 1; + my $nmonth = $params{month} + 1; + my $pyear = $params{year}; + my $nyear = $params{year}; + + # Adjust for January and December + if ($params{month} == 1) { + $pmonth = 12; + $pyear--; + } + if ($params{month} == 12) { + $nmonth = 1; + $nyear++; + } + + # Add padding. + $pmonth=sprintf("%02d", $pmonth); + $nmonth=sprintf("%02d", $nmonth); + + my $calendar="\n"; + + # When did this month start? + my @monthstart = localtime(timelocal(0,0,0,1,$params{month}-1,$params{year}-1900)); + + my $future_dom = 0; + my $today = 0; + if ($params{year} == $now[5]+1900 && $params{month} == $now[4]+1) { + $future_dom = $now[3]+1; + $today = $now[3]; + } + + # Find out month names for this, next, and previous months + my $monthabbrev=strftime_utf8("%b", @monthstart); + my $monthname=strftime_utf8("%B", @monthstart); + my $pmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900))); + my $nmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900))); + + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + $archivebase = $params{archivebase} if defined $params{archivebase}; + + # Calculate URL's for monthly archives. + my ($url, $purl, $nurl)=("$monthname $params{year}",'',''); + if (exists $pagesources{"$archivebase/$params{year}/$params{month}"}) { + $url = htmllink($params{page}, $params{destpage}, + "$archivebase/$params{year}/".$params{month}, + noimageinline => 1, + linktext => "$monthabbrev $params{year}", + title => $monthname); + } + add_depends($params{page}, "$archivebase/$params{year}/$params{month}", + deptype("presence")); + if (exists $pagesources{"$archivebase/$pyear/$pmonth"}) { + $purl = htmllink($params{page}, $params{destpage}, + "$archivebase/$pyear/$pmonth", + noimageinline => 1, + linktext => "\←", + title => $pmonthname); + } + add_depends($params{page}, "$archivebase/$pyear/$pmonth", + deptype("presence")); + if (exists $pagesources{"$archivebase/$nyear/$nmonth"}) { + $nurl = htmllink($params{page}, $params{destpage}, + "$archivebase/$nyear/$nmonth", + noimageinline => 1, + linktext => "\→", + title => $nmonthname); + } + add_depends($params{page}, "$archivebase/$nyear/$nmonth", + deptype("presence")); + + # Start producing the month calendar + $calendar=< + + $purl + $url + $nurl + + +EOF + + # Suppose we want to start the week with day $week_start_day + # If $monthstart[6] == 1 + my $week_start_day = $params{week_start_day}; + + my $start_day = 1 + (7 - $monthstart[6] + $week_start_day) % 7; + my %downame; + my %dowabbr; + for my $dow ($week_start_day..$week_start_day+6) { + my @day=localtime(timelocal(0,0,0,$start_day++,$params{month}-1,$params{year}-1900)); + my $downame = strftime_utf8("%A", @day); + my $dowabbr = substr($downame, 0, 1); + $downame{$dow % 7}=$downame; + $dowabbr{$dow % 7}=$dowabbr; + $calendar.= qq{\t\t$dowabbr\n}; + } + + $calendar.=< +EOF + + my $wday; + # we start with a week_start_day, and skip until we get to the first + for ($wday=$week_start_day; $wday != $monthstart[6]; $wday++, $wday %= 7) { + $calendar.=qq{\t\n} if $wday == $week_start_day; + $calendar.=qq{\t\t \n}; + } + + # At this point, either the first is a week_start_day, in which case + # nothing has been printed, or else we are in the middle of a row. + for (my $day = 1; $day <= month_days(year => $params{year}, month => $params{month}); + $day++, $wday++, $wday %= 7) { + # At this point, on a week_start_day, we close out a row, + # and start a new one -- unless it is week_start_day on the + # first, where we do not close a row -- since none was started. + if ($wday == $week_start_day) { + $calendar.=qq{\t\n} unless $day == 1; + $calendar.=qq{\t\n}; + } + + my $tag; + my $key="$params{year}/$params{month}/$day"; + if (defined $linkcache{$key}) { + if ($day == $today) { + $tag='month-calendar-day-this-day'; + } + else { + $tag='month-calendar-day-link'; + } + $calendar.=qq{\t\t}; + if (scalar(@{$linkcache{$key}}) == 1) { + # Only one posting on this page + my $page = $linkcache{$key}[0]; + $calendar.=htmllink($params{page}, $params{destpage}, + $page, + noimageinline => 1, + linktext => $day, + title => pagetitle(IkiWiki::basename($page))); + } + else { + $calendar.=qq{}; + } + $calendar.=qq{\n}; + } + else { + if ($day == $today) { + $tag='month-calendar-day-this-day'; + } + elsif ($day == $future_dom) { + $tag='month-calendar-day-future'; + } + else { + $tag='month-calendar-day-nolink'; + } + $calendar.=qq{\t\t$day\n}; + } + } + + # finish off the week + for (; $wday != $week_start_day; $wday++, $wday %= 7) { + $calendar.=qq{\t\t \n}; + } + $calendar.=< + +EOF + + return $calendar; +} + +sub format_year (@) { + my %params=@_; + + my @post_months; + foreach my $p (pagespec_match_list($params{page}, + "creation_year($params{year}) and ($params{pages})", + # add presence dependencies to update + # year calendar's links to months when + # pages are added/removed + deptype => deptype("presence"))) { + my $mtime = $IkiWiki::pagectime{$p}; + my @date = localtime($mtime); + my $month = $date[4] + 1; + + $post_months[$month]++; + } + + my $calendar="\n"; + + my $pyear = $params{year} - 1; + my $nyear = $params{year} + 1; + + my $thisyear = $now[5]+1900; + my $future_month = 0; + $future_month = $now[4]+1 if $params{year} == $thisyear; + + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + $archivebase = $params{archivebase} if defined $params{archivebase}; + + # calculate URL's for previous and next years + my ($url, $purl, $nurl)=("$params{year}",'',''); + if (exists $pagesources{"$archivebase/$params{year}"}) { + $url = htmllink($params{page}, $params{destpage}, + "$archivebase/$params{year}", + noimageinline => 1, + linktext => $params{year}, + title => $params{year}); + } + add_depends($params{page}, "$archivebase/$params{year}", deptype("presence")); + if (exists $pagesources{"$archivebase/$pyear"}) { + $purl = htmllink($params{page}, $params{destpage}, + "$archivebase/$pyear", + noimageinline => 1, + linktext => "\←", + title => $pyear); + } + add_depends($params{page}, "$archivebase/$pyear", deptype("presence")); + if (exists $pagesources{"$archivebase/$nyear"}) { + $nurl = htmllink($params{page}, $params{destpage}, + "$archivebase/$nyear", + noimageinline => 1, + linktext => "\→", + title => $nyear); + } + add_depends($params{page}, "$archivebase/$nyear", deptype("presence")); + + # Start producing the year calendar + my $m=$params{months_per_row}-2; + $calendar=< + + $purl + $url + $nurl + + + Months + +EOF + + for (my $month = 1; $month <= 12; $month++) { + my @day=localtime(timelocal(0,0,0,15,$month-1,$params{year}-1900)); + my $murl; + my $monthname = strftime_utf8("%B", @day); + my $monthabbr = strftime_utf8("%b", @day); + $calendar.=qq{\t\n} if ($month % $params{months_per_row} == 1); + my $tag; + my $mtag=sprintf("%02d", $month); + if ($month == $params{month} && $thisyear == $params{year}) { + $tag = 'year-calendar-this-month'; + } + elsif ($pagesources{"$archivebase/$params{year}/$mtag"}) { + $tag = 'year-calendar-month-link'; + } + elsif ($future_month && $month >= $future_month) { + $tag = 'year-calendar-month-future'; + } + else { + $tag = 'year-calendar-month-nolink'; + } + + if ($pagesources{"$archivebase/$params{year}/$mtag"} && + $post_months[$mtag]) { + $murl = htmllink($params{page}, $params{destpage}, + "$archivebase/$params{year}/$mtag", + noimageinline => 1, + linktext => $monthabbr, + title => $monthname); + $calendar.=qq{\t}; + $calendar.=$murl; + $calendar.=qq{\t\n}; + } + else { + $calendar.=qq{\t$monthabbr\n}; + } + add_depends($params{page}, "$archivebase/$params{year}/$mtag", + deptype("presence")); + + $calendar.=qq{\t\n} if ($month % $params{months_per_row} == 0); + } + + $calendar.=< +EOF + + return $calendar; +} + +sub setnextchange ($$) { + my $page=shift; + my $timestamp=shift; + + if (! exists $pagestate{$page}{calendar}{nextchange} || + $pagestate{$page}{calendar}{nextchange} > $timestamp) { + $pagestate{$page}{calendar}{nextchange}=$timestamp; + } +} + +sub preprocess (@) { + my %params=@_; + + my $thisyear=1900 + $now[5]; + my $thismonth=1 + $now[4]; + + $params{pages} = "*" unless defined $params{pages}; + $params{type} = "month" unless defined $params{type}; + $params{week_start_day} = 0 unless defined $params{week_start_day}; + $params{months_per_row} = 3 unless defined $params{months_per_row}; + $params{year} = $thisyear unless defined $params{year}; + $params{month} = $thismonth unless defined $params{month}; + + my $relativeyear=0; + if ($params{year} < 1) { + $relativeyear=1; + $params{year}=$thisyear+$params{year}; + } + my $relativemonth=0; + if ($params{month} < 1) { + $relativemonth=1; + my $monthoff=$params{month}; + $params{month}=($thismonth+$monthoff) % 12; + $params{month}=12 if $params{month}==0; + my $yearoff=POSIX::ceil(($thismonth-$params{month}) / -12) + - int($monthoff / 12); + $params{year}-=$yearoff; + } + + $params{month} = sprintf("%02d", $params{month}); + + if ($params{type} eq 'month' && $params{year} == $thisyear + && $params{month} == $thismonth) { + # calendar for current month, updates next midnight + setnextchange($params{destpage}, ($time + + (60 - $now[0]) # seconds + + (59 - $now[1]) * 60 # minutes + + (23 - $now[2]) * 60 * 60 # hours + )); + } + elsif ($params{type} eq 'month' && + (($params{year} == $thisyear && $params{month} > $thismonth) || + $params{year} > $thisyear)) { + # calendar for upcoming month, updates 1st of that month + setnextchange($params{destpage}, + timelocal(0, 0, 0, 1, $params{month}-1, $params{year})); + } + elsif (($params{type} eq 'year' && $params{year} == $thisyear) || + $relativemonth) { + # Calendar for current year updates 1st of next month. + # Any calendar relative to the current month also updates + # then. + if ($thismonth < 12) { + setnextchange($params{destpage}, + timelocal(0, 0, 0, 1, $thismonth+1-1, $params{year})); + } + else { + setnextchange($params{destpage}, + timelocal(0, 0, 0, 1, 1-1, $params{year}+1)); + } + } + elsif ($relativeyear) { + # Any calendar relative to the current year updates 1st + # of next year. + setnextchange($params{destpage}, + timelocal(0, 0, 0, 1, 1-1, $thisyear+1)); + } + elsif ($params{type} eq 'year' && $params{year} > $thisyear) { + # calendar for upcoming year, updates 1st of that year + setnextchange($params{destpage}, + timelocal(0, 0, 0, 1, 1-1, $params{year})); + } + else { + # calendar for past month or year, does not need + # to update any more + delete $pagestate{$params{destpage}}{calendar}; + } + + my $calendar=""; + if ($params{type} eq 'month') { + $calendar=format_month(%params); + } + elsif ($params{type} eq 'year') { + $calendar=format_year(%params); + } + + return "\n
$calendar
\n"; +} #}} + +sub needsbuild (@) { + my $needsbuild=shift; + foreach my $page (keys %pagestate) { + if (exists $pagestate{$page}{calendar}{nextchange}) { + if ($pagestate{$page}{calendar}{nextchange} <= $time) { + # force a rebuild so the calendar shows + # the current day + push @$needsbuild, $pagesources{$page}; + } + if (exists $pagesources{$page} && + grep { $_ eq $pagesources{$page} } @$needsbuild) { + # remove state, will be re-added if + # the calendar is still there during the + # rebuild + delete $pagestate{$page}{calendar}; + } + } + } + return $needsbuild; +} + +1 diff --git a/IkiWiki/Plugin/camelcase.pm b/IkiWiki/Plugin/camelcase.pm new file mode 100644 index 000000000..088447d6b --- /dev/null +++ b/IkiWiki/Plugin/camelcase.pm @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# CamelCase links +package IkiWiki::Plugin::camelcase; + +use warnings; +use strict; +use IkiWiki 3.00; + +# This regexp is based on the one in Text::WikiFormat. +my $link_regexp=qr{ + (? "getsetup", id => "camelcase", call => \&getsetup); + hook(type => "linkify", id => "camelcase", call => \&linkify); + hook(type => "scan", id => "camelcase", call => \&scan); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, + camelcase_ignore => { + type => "string", + example => [], + description => "list of words to not turn into links", + safe => 1, + rebuild => undef, # might change links + }, +} + +sub linkify (@) { + my %params=@_; + my $page=$params{page}; + my $destpage=$params{destpage}; + + $params{content}=~s{$link_regexp}{ + ignored($1) ? $1 : htmllink($page, $destpage, linkpage($1)) + }eg; + + return $params{content}; +} + +sub scan (@) { + my %params=@_; + my $page=$params{page}; + my $content=$params{content}; + + while ($content =~ /$link_regexp/g) { + add_link($page, linkpage($1)) unless ignored($1) + } +} + +sub ignored ($) { + my $word=lc shift; + grep { $word eq lc $_ } @{$config{'camelcase_ignore'}} +} + +1 diff --git a/IkiWiki/Plugin/color.pm b/IkiWiki/Plugin/color.pm new file mode 100644 index 000000000..9bb2359ce --- /dev/null +++ b/IkiWiki/Plugin/color.pm @@ -0,0 +1,78 @@ +#!/usr/bin/perl +# Ikiwiki text colouring plugin +# Paweł‚ Tęcza +package IkiWiki::Plugin::color; + +use warnings; +use strict; +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 ($$$) { + my $foreground = shift; + my $background = shift; + my $text = shift; + + $foreground = defined $foreground ? lc($foreground) : ''; + $background = defined $background ? lc($background) : ''; + $text = '' unless (defined $text); + + # Validate colors. Only color name or color code are valid. + $foreground = '' unless ($foreground && + ($foreground =~ /^[a-z]+$/ || $foreground =~ /^#[0-9a-f]{3,6}$/)); + $background = '' unless ($background && + ($background =~ /^[a-z]+$/ || $background =~ /^#[0-9a-f]{3,6}$/)); + + my $preserved = ''; + $preserved .= ''; + $preserved .= 'color: '.$foreground if ($foreground); + $preserved .= '; ' if ($foreground && $background); + $preserved .= 'background-color: '.$background if ($background); + $preserved .= ''; + $preserved .= ''.$text.''; + + return $preserved; + +} + +sub replace_preserved_style ($) { + my $content = shift; + + $content =~ s!((color: ([a-z]+|\#[0-9a-f]{3,6})?)?((; )?(background-color: ([a-z]+|\#[0-9a-f]{3,6})?)?)?)!!g; + $content =~ s!!!g; + + return $content; +} + +sub preprocess (@) { + my %params = @_; + + return preserve_style($params{foreground}, $params{background}, + # Preprocess the text to expand any preprocessor directives + # embedded inside it. + IkiWiki::preprocess($params{page}, $params{destpage}, + $params{text})); +} + +sub format (@) { + my %params = @_; + + $params{content} = replace_preserved_style($params{content}); + return $params{content}; +} + +1 diff --git a/IkiWiki/Plugin/comments.pm b/IkiWiki/Plugin/comments.pm new file mode 100644 index 000000000..1ef79a27a --- /dev/null +++ b/IkiWiki/Plugin/comments.pm @@ -0,0 +1,1067 @@ +#!/usr/bin/perl +# Copyright © 2006-2008 Joey Hess +# Copyright © 2008 Simon McVittie +# Licensed under the GNU GPL, version 2, or any later version published by the +# Free Software Foundation +package IkiWiki::Plugin::comments; + +use warnings; +use strict; +use IkiWiki 3.00; +use Encode; + +use constant PREVIEW => "Preview"; +use constant POST_COMMENT => "Post comment"; +use constant CANCEL => "Cancel"; + +my $postcomment; +my %commentstate; + +sub import { + hook(type => "checkconfig", id => 'comments', call => \&checkconfig); + hook(type => "getsetup", id => 'comments', call => \&getsetup); + hook(type => "preprocess", id => 'comment', call => \&preprocess, + scan => 1); + hook(type => "preprocess", id => 'commentmoderation', call => \&preprocess_moderation); + # here for backwards compatability with old comments + hook(type => "preprocess", id => '_comment', call => \&preprocess); + hook(type => "sessioncgi", id => 'comment', call => \&sessioncgi); + hook(type => "htmlize", id => "_comment", call => \&htmlize); + hook(type => "htmlize", id => "_comment_pending", + call => \&htmlize_pending); + hook(type => "pagetemplate", id => "comments", call => \&pagetemplate); + hook(type => "formbuilder_setup", id => "comments", + call => \&formbuilder_setup); + # Load goto to fix up user page links for logged-in commenters + IkiWiki::loadplugin("goto"); + IkiWiki::loadplugin("inline"); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + section => "web", + }, + comments_pagespec => { + type => 'pagespec', + example => 'blog/* and !*/Discussion', + description => 'PageSpec of pages where comments are allowed', + link => 'ikiwiki/PageSpec', + safe => 1, + rebuild => 1, + }, + comments_closed_pagespec => { + type => 'pagespec', + example => 'blog/controversial or blog/flamewar', + description => 'PageSpec of pages where posting new comments is not allowed', + link => 'ikiwiki/PageSpec', + safe => 1, + rebuild => 1, + }, + comments_pagename => { + type => 'string', + default => 'comment_', + description => 'Base name for comments, e.g. "comment_" for pages like "sandbox/comment_12"', + safe => 0, # manual page moving required + rebuild => undef, + }, + comments_allowdirectives => { + type => 'boolean', + example => 0, + description => 'Interpret directives in comments?', + safe => 1, + rebuild => 0, + }, + comments_allowauthor => { + type => 'boolean', + example => 0, + description => 'Allow anonymous commenters to set an author name?', + safe => 1, + rebuild => 0, + }, + comments_commit => { + type => 'boolean', + example => 1, + description => 'commit comments to the VCS', + # old uncommitted comments are likely to cause + # confusion if this is changed + safe => 0, + rebuild => 0, + }, + comments_allowformats => { + type => 'string', + default => '', + example => 'mdwn txt', + description => 'Restrict formats for comments to (no restriction if empty)', + safe => 1, + rebuild => 0, + }, + +} + +sub checkconfig () { + $config{comments_commit} = 1 + unless defined $config{comments_commit}; + $config{comments_pagespec} = '' + unless defined $config{comments_pagespec}; + $config{comments_closed_pagespec} = '' + unless defined $config{comments_closed_pagespec}; + $config{comments_pagename} = 'comment_' + unless defined $config{comments_pagename}; + $config{comments_allowformats} = '' + unless defined $config{comments_allowformats}; +} + +sub htmlize { + my %params = @_; + return $params{content}; +} + +sub htmlize_pending { + my %params = @_; + return sprintf(gettext("this comment needs %s"), + ''. + gettext("moderation").''); +} + +# FIXME: copied verbatim from meta +sub safeurl ($) { + my $url=shift; + if (exists $IkiWiki::Plugin::htmlscrubber::{safe_url_regexp} && + defined $IkiWiki::Plugin::htmlscrubber::safe_url_regexp) { + return $url=~/$IkiWiki::Plugin::htmlscrubber::safe_url_regexp/; + } + else { + return 1; + } +} + +sub isallowed ($) { + my $format = shift; + return ! $config{comments_allowformats} || $config{comments_allowformats} =~ /\b$format\b/; +} + +sub preprocess { + my %params = @_; + my $page = $params{page}; + + my $format = $params{format}; + if (defined $format && (! exists $IkiWiki::hooks{htmlize}{$format} || + ! isallowed($format))) { + error(sprintf(gettext("unsupported page format %s"), $format)); + } + + my $content = $params{content}; + if (! defined $content) { + error(gettext("comment must have content")); + } + $content =~ s/\\"/"/g; + + if (defined wantarray) { + if ($config{comments_allowdirectives}) { + $content = IkiWiki::preprocess($page, $params{destpage}, + $content); + } + + # no need to bother with htmlize if it's just HTML + $content = IkiWiki::htmlize($page, $params{destpage}, $format, $content) + if defined $format; + + IkiWiki::run_hooks(sanitize => sub { + $content = shift->( + page => $page, + destpage => $params{destpage}, + content => $content, + ); + }); + } + else { + IkiWiki::preprocess($page, $params{destpage}, $content, 1); + } + + # set metadata, possibly overriding [[!meta]] directives from the + # comment itself + + my $commentuser; + my $commentip; + my $commentauthor; + my $commentauthorurl; + my $commentopenid; + if (defined $params{username}) { + $commentuser = $params{username}; + + my $oiduser = eval { IkiWiki::openiduser($commentuser) }; + + if (defined $oiduser) { + # looks like an OpenID + $commentauthorurl = $commentuser; + $commentauthor = (defined $params{nickname} && length $params{nickname}) ? $params{nickname} : $oiduser; + $commentopenid = $commentuser; + } + else { + $commentauthorurl = IkiWiki::cgiurl( + do => 'goto', + page => IkiWiki::userpage($commentuser) + ); + + $commentauthor = $commentuser; + } + } + else { + if (defined $params{ip}) { + $commentip = $params{ip}; + } + $commentauthor = gettext("Anonymous"); + } + + $commentstate{$page}{commentuser} = $commentuser; + $commentstate{$page}{commentopenid} = $commentopenid; + $commentstate{$page}{commentip} = $commentip; + $commentstate{$page}{commentauthor} = $commentauthor; + $commentstate{$page}{commentauthorurl} = $commentauthorurl; + $commentstate{$page}{commentauthoravatar} = $params{avatar}; + if (! defined $pagestate{$page}{meta}{author}) { + $pagestate{$page}{meta}{author} = $commentauthor; + } + if (! defined $pagestate{$page}{meta}{authorurl}) { + $pagestate{$page}{meta}{authorurl} = $commentauthorurl; + } + + if ($config{comments_allowauthor}) { + if (defined $params{claimedauthor}) { + $pagestate{$page}{meta}{author} = $params{claimedauthor}; + } + + if (defined $params{url}) { + my $url=$params{url}; + + eval q{use URI::Heuristic}; + if (! $@) { + $url=URI::Heuristic::uf_uristr($url); + } + + if (safeurl($url)) { + $pagestate{$page}{meta}{authorurl} = $url; + } + } + } + else { + $pagestate{$page}{meta}{author} = $commentauthor; + $pagestate{$page}{meta}{authorurl} = $commentauthorurl; + } + + if (defined $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+_/) { + $pagestate{$page}{meta}{permalink} = urlto(IkiWiki::dirname($params{page})). + "#".page_to_id($params{page}); + } + + eval q{use Date::Parse}; + if (! $@) { + my $time = str2time($params{date}); + $IkiWiki::pagectime{$page} = $time if defined $time; + } + + return $content; +} + +sub preprocess_moderation { + my %params = @_; + + $params{desc}=gettext("Comment Moderation") + unless defined $params{desc}; + + if (length $config{cgiurl}) { + return ''.$params{desc}.''; + } + else { + return $params{desc}; + } +} + +sub sessioncgi ($$) { + my $cgi=shift; + my $session=shift; + + my $do = $cgi->param('do'); + if ($do eq 'comment') { + editcomment($cgi, $session); + } + elsif ($do eq 'commentmoderation') { + commentmoderation($cgi, $session); + } + elsif ($do eq 'commentsignin') { + IkiWiki::cgi_signin($cgi, $session); + exit; + } +} + +# Mostly cargo-culted from IkiWiki::plugin::editpage +sub editcomment ($$) { + my $cgi=shift; + my $session=shift; + + IkiWiki::decode_cgi_utf8($cgi); + + eval q{use CGI::FormBuilder}; + error($@) if $@; + + my @buttons = (POST_COMMENT, PREVIEW, CANCEL); + my $form = CGI::FormBuilder->new( + fields => [qw{do sid page subject editcontent type author + email url subscribe anonsubscribe}], + charset => 'utf-8', + method => 'POST', + required => [qw{editcontent}], + javascript => 0, + params => $cgi, + action => IkiWiki::cgiurl(), + header => 0, + table => 0, + template => { template('editcomment.tmpl') }, + ); + + IkiWiki::decode_form_utf8($form); + IkiWiki::run_hooks(formbuilder_setup => sub { + shift->(title => "comment", form => $form, cgi => $cgi, + session => $session, buttons => \@buttons); + }); + IkiWiki::decode_form_utf8($form); + + my $type = $form->param('type'); + if (defined $type && length $type && $IkiWiki::hooks{htmlize}{$type}) { + $type = IkiWiki::possibly_foolish_untaint($type); + } + else { + $type = $config{default_pageext}; + } + + + my @page_types; + if (exists $IkiWiki::hooks{htmlize}) { + foreach my $key (grep { !/^_/ && isallowed($_) } keys %{$IkiWiki::hooks{htmlize}}) { + push @page_types, [$key, $IkiWiki::hooks{htmlize}{$key}{longname} || $key]; + } + } + @page_types=sort @page_types; + + $form->field(name => 'do', type => 'hidden'); + $form->field(name => 'sid', type => 'hidden', value => $session->id, + force => 1); + $form->field(name => 'page', type => 'hidden'); + $form->field(name => 'subject', type => 'text', size => 72); + $form->field(name => 'editcontent', type => 'textarea', rows => 10); + $form->field(name => "type", value => $type, force => 1, + type => 'select', options => \@page_types); + + my $username=$session->param('name'); + $form->tmpl_param(username => $username); + + $form->field(name => "subscribe", type => 'hidden'); + $form->field(name => "anonsubscribe", type => 'hidden'); + if (IkiWiki::Plugin::notifyemail->can("subscribe")) { + if (defined $username) { + $form->field(name => "subscribe", type => "checkbox", + options => [gettext("email replies to me")]); + } + elsif (IkiWiki::Plugin::passwordauth->can("anonuser")) { + $form->field(name => "anonsubscribe", type => "checkbox", + options => [gettext("email replies to me")]); + } + } + + if ($config{comments_allowauthor} and + ! defined $session->param('name')) { + $form->tmpl_param(allowauthor => 1); + $form->field(name => 'author', type => 'text', size => '40'); + $form->field(name => 'email', type => 'text', size => '40'); + $form->field(name => 'url', type => 'text', size => '40'); + } + else { + $form->tmpl_param(allowauthor => 0); + $form->field(name => 'author', type => 'hidden', value => '', + force => 1); + $form->field(name => 'email', type => 'hidden', value => '', + force => 1); + $form->field(name => 'url', type => 'hidden', value => '', + force => 1); + } + + if (! defined $session->param('name')) { + # Make signinurl work and return here. + $form->tmpl_param(signinurl => IkiWiki::cgiurl(do => 'commentsignin')); + $session->param(postsignin => $ENV{QUERY_STRING}); + IkiWiki::cgi_savesession($session); + } + + # The untaint is OK (as in editpage) because we're about to pass + # it to file_pruned and wiki_file_regexp anyway. + my ($page) = $form->field('page')=~/$config{wiki_file_regexp}/; + $page = IkiWiki::possibly_foolish_untaint($page); + if (! defined $page || ! length $page || + IkiWiki::file_pruned($page)) { + error(gettext("bad page name")); + } + + $form->title(sprintf(gettext("commenting on %s"), + IkiWiki::pagetitle(IkiWiki::basename($page)))); + + $form->tmpl_param('helponformattinglink', + htmllink($page, $page, 'ikiwiki/formatting', + noimageinline => 1, + linktext => 'FormattingHelp'), + allowdirectives => $config{allow_directives}); + + if ($form->submitted eq CANCEL) { + # bounce back to the page they wanted to comment on, and exit. + IkiWiki::redirect($cgi, urlto($page)); + exit; + } + + if (not exists $pagesources{$page}) { + error(sprintf(gettext( + "page '%s' doesn't exist, so you can't comment"), + $page)); + } + + if (pagespec_match($page, $config{comments_closed_pagespec}, + location => $page)) { + error(sprintf(gettext( + "comments on page '%s' are closed"), + $page)); + } + + # Set a flag to indicate that we're posting a comment, + # so that postcomment() can tell it should match. + $postcomment=1; + IkiWiki::check_canedit($page, $cgi, $session); + $postcomment=0; + + my $content = "[[!comment format=$type\n"; + + if (defined $session->param('name')) { + my $username = $session->param('name'); + $username =~ s/"/"/g; + $content .= " username=\"$username\"\n"; + } + if (defined $session->param('nickname')) { + my $nickname = $session->param('nickname'); + $nickname =~ s/"/"/g; + $content .= " nickname=\"$nickname\"\n"; + } + elsif (defined $session->remote_addr()) { + $content .= " ip=\"".$session->remote_addr()."\"\n"; + } + + if ($config{comments_allowauthor}) { + my $author = $form->field('author'); + if (defined $author && length $author) { + $author =~ s/"/"/g; + $content .= " claimedauthor=\"$author\"\n"; + } + my $url = $form->field('url'); + if (defined $url && length $url) { + $url =~ s/"/"/g; + $content .= " url=\"$url\"\n"; + } + } + + my $avatar=getavatar($session->param('name')); + if (defined $avatar && length $avatar) { + $avatar =~ s/"/"/g; + $content .= " avatar=\"$avatar\"\n"; + } + + my $subject = $form->field('subject'); + if (defined $subject && length $subject) { + $subject =~ s/"/"/g; + } + else { + $subject = "comment ".(num_comments($page, $config{srcdir}) + 1); + } + $content .= " subject=\"$subject\"\n"; + + $content .= " date=\"" . strftime_utf8('%Y-%m-%dT%H:%M:%SZ', gmtime) . "\"\n"; + + my $editcontent = $form->field('editcontent'); + $editcontent="" if ! defined $editcontent; + $editcontent =~ s/\r\n/\n/g; + $editcontent =~ s/\r/\n/g; + $editcontent =~ s/"/\\"/g; + $content .= " content=\"\"\"\n$editcontent\n\"\"\"]]\n"; + + my $location=unique_comment_location($page, $content, $config{srcdir}); + + # This is essentially a simplified version of editpage: + # - the user does not control the page that's created, only the parent + # - it's always a create operation, never an edit + # - this means that conflicts should never happen + # - this means that if they do, rocks fall and everyone dies + + if ($form->submitted eq PREVIEW) { + my $preview=previewcomment($content, $location, $page, time); + IkiWiki::run_hooks(format => sub { + $preview = shift->(page => $page, + content => $preview); + }); + $form->tmpl_param(page_preview => $preview); + } + else { + $form->tmpl_param(page_preview => ""); + } + + if ($form->submitted eq POST_COMMENT && $form->validate) { + IkiWiki::checksessionexpiry($cgi, $session); + + if (IkiWiki::Plugin::notifyemail->can("subscribe")) { + my $subspec="comment($page)"; + if (defined $username && + length $form->field("subscribe")) { + IkiWiki::Plugin::notifyemail::subscribe( + $username, $subspec); + } + elsif (length $form->field("email") && + length $form->field("anonsubscribe")) { + IkiWiki::Plugin::notifyemail::anonsubscribe( + $form->field("email"), $subspec); + } + } + + $postcomment=1; + my $ok=IkiWiki::check_content(content => $form->field('editcontent'), + subject => $form->field('subject'), + $config{comments_allowauthor} ? ( + author => $form->field('author'), + url => $form->field('url'), + ) : (), + page => $location, + cgi => $cgi, + session => $session, + nonfatal => 1, + ); + $postcomment=0; + + if (! $ok) { + $location=unique_comment_location($page, $content, $config{srcdir}, "._comment_pending"); + writefile("$location._comment_pending", $config{srcdir}, $content); + + # Refresh so anything that deals with pending + # comments can be updated. + require IkiWiki::Render; + IkiWiki::refresh(); + IkiWiki::saveindex(); + + IkiWiki::printheader($session); + print IkiWiki::cgitemplate($cgi, gettext(gettext("comment stored for moderation")), + "

". + gettext("Your comment will be posted after moderator review"). + "

"); + exit; + } + + # FIXME: could probably do some sort of graceful retry + # on error? Would require significant unwinding though + my $file = "$location._comment"; + writefile($file, $config{srcdir}, $content); + + my $conflict; + + if ($config{rcs} and $config{comments_commit}) { + my $message = gettext("Added a comment"); + if (defined $form->field('subject') && + length $form->field('subject')) { + $message = sprintf( + gettext("Added a comment: %s"), + $form->field('subject')); + } + + IkiWiki::rcs_add($file); + IkiWiki::disable_commit_hook(); + $conflict = IkiWiki::rcs_commit_staged( + message => $message, + session => $session, + ); + IkiWiki::enable_commit_hook(); + IkiWiki::rcs_update(); + } + + # Now we need a refresh + require IkiWiki::Render; + IkiWiki::refresh(); + IkiWiki::saveindex(); + + # this should never happen, unless a committer deliberately + # breaks it or something + error($conflict) if defined $conflict; + + # Jump to the new comment on the page. + # The trailing question mark tries to avoid broken + # caches and get the most recent version of the page. + IkiWiki::redirect($cgi, urlto($page). + "?updated#".page_to_id($location)); + + } + else { + IkiWiki::showform($form, \@buttons, $session, $cgi, + page => $page); + } + + exit; +} + +sub getavatar ($) { + my $user=shift; + return undef unless defined $user; + + my $avatar; + eval q{use Libravatar::URL}; + if (! $@) { + my $oiduser = eval { IkiWiki::openiduser($user) }; + my $https=defined $config{url} && $config{url}=~/^https:/; + + if (defined $oiduser) { + eval { + $avatar = libravatar_url(openid => $user, https => $https); + } + } + if (! defined $avatar && + (my $email = IkiWiki::userinfo_get($user, 'email'))) { + eval { + $avatar = libravatar_url(email => $email, https => $https); + } + } + } + return $avatar; +} + + +sub commentmoderation ($$) { + my $cgi=shift; + my $session=shift; + + IkiWiki::needsignin($cgi, $session); + if (! IkiWiki::is_admin($session->param("name"))) { + error(gettext("you are not logged in as an admin")); + } + + IkiWiki::decode_cgi_utf8($cgi); + + if (defined $cgi->param('sid')) { + IkiWiki::checksessionexpiry($cgi, $session); + + my $rejectalldefer=$cgi->param('rejectalldefer'); + + my %vars=$cgi->Vars; + my $added=0; + foreach my $id (keys %vars) { + if ($id =~ /(.*)\._comment(?:_pending)?$/) { + $id=decode_utf8($id); + my $action=$cgi->param($id); + next if $action eq 'Defer' && ! $rejectalldefer; + + # Make sure that the id is of a legal + # pending comment. + my ($f) = $id =~ /$config{wiki_file_regexp}/; + if (! defined $f || ! length $f || + IkiWiki::file_pruned($f)) { + error("illegal file"); + } + + my $page=IkiWiki::dirname($f); + my $file="$config{srcdir}/$f"; + my $filedir=$config{srcdir}; + if (! -e $file) { + # old location + $file="$config{wikistatedir}/comments_pending/".$f; + $filedir="$config{wikistatedir}/comments_pending"; + } + + if ($action eq 'Accept') { + my $content=eval { readfile($file) }; + next if $@; # file vanished since form was displayed + my $dest=unique_comment_location($page, $content, $config{srcdir})."._comment"; + writefile($dest, $config{srcdir}, $content); + if ($config{rcs} and $config{comments_commit}) { + IkiWiki::rcs_add($dest); + } + $added++; + } + + require IkiWiki::Render; + IkiWiki::prune($file, $filedir); + } + } + + if ($added) { + my $conflict; + if ($config{rcs} and $config{comments_commit}) { + my $message = gettext("Comment moderation"); + IkiWiki::disable_commit_hook(); + $conflict=IkiWiki::rcs_commit_staged( + message => $message, + session => $session, + ); + IkiWiki::enable_commit_hook(); + IkiWiki::rcs_update(); + } + + # Now we need a refresh + require IkiWiki::Render; + IkiWiki::refresh(); + IkiWiki::saveindex(); + + error($conflict) if defined $conflict; + } + } + + my @comments=map { + my ($id, $dir, $ctime)=@{$_}; + my $content=readfile("$dir/$id"); + my $preview=previewcomment($content, $id, + $id, $ctime); + { + id => $id, + view => $preview, + } + } sort { $b->[2] <=> $a->[2] } comments_pending(); + + my $template=template("commentmoderation.tmpl"); + $template->param( + sid => $session->id, + comments => \@comments, + cgiurl => IkiWiki::cgiurl(), + ); + IkiWiki::printheader($session); + my $out=$template->output; + IkiWiki::run_hooks(format => sub { + $out = shift->(page => "", content => $out); + }); + print IkiWiki::cgitemplate($cgi, gettext("comment moderation"), $out); + exit; +} + +sub formbuilder_setup (@) { + my %params=@_; + + my $form=$params{form}; + if ($form->title eq "preferences" && + IkiWiki::is_admin($params{session}->param("name"))) { + push @{$params{buttons}}, "Comment Moderation"; + if ($form->submitted && $form->submitted eq "Comment Moderation") { + commentmoderation($params{cgi}, $params{session}); + } + } +} + +sub comments_pending () { + my @ret; + + eval q{use File::Find}; + error($@) if $@; + eval q{use Cwd}; + error($@) if $@; + my $origdir=getcwd(); + + my $find_comments=sub { + my $dir=shift; + my $extension=shift; + return unless -d $dir; + + chdir($dir) || die "chdir $dir: $!"; + + find({ + no_chdir => 1, + wanted => sub { + my $file=decode_utf8($_); + $file=~s/^\.\///; + return if ! length $file || IkiWiki::file_pruned($file) + || -l $_ || -d _ || $file !~ /\Q$extension\E$/; + my ($f) = $file =~ /$config{wiki_file_regexp}/; # untaint + if (defined $f) { + my $ctime=(stat($_))[10]; + push @ret, [$f, $dir, $ctime]; + } + } + }, "."); + + chdir($origdir) || die "chdir $origdir: $!"; + }; + + $find_comments->($config{srcdir}, "._comment_pending"); + # old location + $find_comments->("$config{wikistatedir}/comments_pending/", + "._comment"); + + return @ret; +} + +sub previewcomment ($$$) { + my $content=shift; + my $location=shift; + my $page=shift; + my $time=shift; + + # Previewing a comment should implicitly enable comment posting mode. + my $oldpostcomment=$postcomment; + $postcomment=1; + + my $preview = IkiWiki::htmlize($location, $page, '_comment', + IkiWiki::linkify($location, $page, + IkiWiki::preprocess($location, $page, + IkiWiki::filter($location, $page, $content), 0, 1))); + + my $template = template("comment.tmpl"); + $template->param(content => $preview); + $template->param(ctime => displaytime($time, undef, 1)); + $template->param(html5 => $config{html5}); + + IkiWiki::run_hooks(pagetemplate => sub { + shift->(page => $location, + destpage => $page, + template => $template); + }); + + $template->param(have_actions => 0); + + $postcomment=$oldpostcomment; + + return $template->output; +} + +sub commentsshown ($) { + my $page=shift; + + return pagespec_match($page, $config{comments_pagespec}, + location => $page); +} + +sub commentsopen ($) { + my $page = shift; + + return length $config{cgiurl} > 0 && + (! length $config{comments_closed_pagespec} || + ! pagespec_match($page, $config{comments_closed_pagespec}, + location => $page)); +} + +sub pagetemplate (@) { + my %params = @_; + + my $page = $params{page}; + my $template = $params{template}; + my $shown = ($template->query(name => 'commentslink') || + $template->query(name => 'commentsurl') || + $template->query(name => 'atomcommentsurl') || + $template->query(name => 'comments')) && + commentsshown($page); + + if ($template->query(name => 'comments')) { + my $comments = undef; + if ($shown) { + $comments = IkiWiki::preprocess_inline( + pages => "comment($page) and !comment($page/*)", + template => 'comment', + show => 0, + reverse => 'yes', + page => $page, + destpage => $params{destpage}, + feedfile => 'comments', + emptyfeeds => 'no', + ); + } + + if (defined $comments && length $comments) { + $template->param(comments => $comments); + } + + if ($shown && commentsopen($page)) { + $template->param(addcommenturl => addcommenturl($page)); + } + } + + if ($shown) { + if ($template->query(name => 'commentsurl')) { + $template->param(commentsurl => + urlto($page).'#comments'); + } + + 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).'comments.atom'); + } + + 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 + ); + } + elsif (commentsopen($page)) { + $link = "". + #translators: Here "Comment" is a verb; + #translators: the user clicks on it to + #translators: post a comment. + gettext("Comment"). + ""; + } + $template->param(commentslink => $link) + if defined $link; + } + } + + # everything below this point is only relevant to the comments + # themselves + if (!exists $commentstate{$page}) { + return; + } + + if ($template->query(name => 'commentid')) { + $template->param(commentid => page_to_id($page)); + } + + if ($template->query(name => 'commentuser')) { + $template->param(commentuser => + $commentstate{$page}{commentuser}); + } + + if ($template->query(name => 'commentopenid')) { + $template->param(commentopenid => + $commentstate{$page}{commentopenid}); + } + + if ($template->query(name => 'commentip')) { + $template->param(commentip => + $commentstate{$page}{commentip}); + } + + if ($template->query(name => 'commentauthor')) { + $template->param(commentauthor => + $commentstate{$page}{commentauthor}); + } + + if ($template->query(name => 'commentauthorurl')) { + $template->param(commentauthorurl => + $commentstate{$page}{commentauthorurl}); + } + + if ($template->query(name => 'commentauthoravatar')) { + $template->param(commentauthoravatar => + $commentstate{$page}{commentauthoravatar}); + } + + if ($template->query(name => 'removeurl') && + IkiWiki::Plugin::remove->can("check_canremove") && + length $config{cgiurl}) { + $template->param(removeurl => IkiWiki::cgiurl(do => 'remove', + page => $page)); + $template->param(have_actions => 1); + } +} + +sub addcommenturl ($) { + my $page=shift; + + return IkiWiki::cgiurl(do => 'comment', page => $page); +} + +sub num_comments ($$) { + my $page=shift; + my $dir=shift; + + my @comments=glob("$dir/$page/$config{comments_pagename}*._comment"); + return int @comments; +} + +sub unique_comment_location ($$$$) { + my $page=shift; + eval q{use Digest::MD5 'md5_hex'}; + error($@) if $@; + my $content_md5=md5_hex(Encode::encode_utf8(shift)); + my $dir=shift; + my $ext=shift || "._comment"; + + my $location; + my $i = num_comments($page, $dir); + do { + $i++; + $location = "$page/$config{comments_pagename}${i}_${content_md5}"; + } while (-e "$dir/$location$ext"); + + return $location; +} + +sub page_to_id ($) { + # Converts a comment page name into a unique, legal html id + # 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(Encode::encode_utf8(($page))); +} + +package IkiWiki::PageSpec; + +sub match_postcomment ($$;@) { + my $page = shift; + my $glob = shift; + + if (! $postcomment) { + return IkiWiki::FailReason->new("not posting a comment"); + } + return match_glob($page, $glob, @_); +} + +sub match_comment ($$;@) { + my $page = shift; + my $glob = shift; + + if (! $postcomment) { + # To see if it's a comment, check the source file type. + # Deal with comments that were just deleted. + my $source=exists $IkiWiki::pagesources{$page} ? + $IkiWiki::pagesources{$page} : + $IkiWiki::delpagesources{$page}; + my $type=defined $source ? IkiWiki::pagetype($source) : undef; + if (! defined $type || $type ne "_comment") { + return IkiWiki::FailReason->new("$page is not a comment"); + } + } + + return match_glob($page, "$glob/*", internal => 1, @_); +} + +sub match_comment_pending ($$;@) { + my $page = shift; + my $glob = shift; + + my $source=exists $IkiWiki::pagesources{$page} ? + $IkiWiki::pagesources{$page} : + $IkiWiki::delpagesources{$page}; + my $type=defined $source ? IkiWiki::pagetype($source) : undef; + if (! defined $type || $type ne "_comment_pending") { + return IkiWiki::FailReason->new("$page is not a pending comment"); + } + + return match_glob($page, "$glob/*", internal => 1, @_); +} + +1 diff --git a/IkiWiki/Plugin/conditional.pm b/IkiWiki/Plugin/conditional.pm new file mode 100644 index 000000000..0a3d7fb4c --- /dev/null +++ b/IkiWiki/Plugin/conditional.pm @@ -0,0 +1,125 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::conditional; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "conditional", call => \&getsetup); + hook(type => "preprocess", id => "if", call => \&preprocess_if); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess_if (@) { + my %params=@_; + + foreach my $param (qw{test then}) { + if (! exists $params{$param}) { + error sprintf(gettext('%s parameter is required'), $param); + } + } + + my $result=0; + 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\!()]*)+$/) { + add_depends($params{page}, "($params{test}) and $params{page}"); + $result=pagespec_match($params{page}, $params{test}, + location => $params{page}, + sourcepage => $params{page}, + destpage => $params{destpage}); + } + else { + $result=pagespec_match_list($params{page}, $params{test}, + # stop after first match + num => 1, + sourcepage => $params{page}, + destpage => $params{destpage}, + ); + } + + my $ret; + if ($result) { + $ret=$params{then}; + } + elsif (exists $params{else}) { + $ret=$params{else}; + } + else { + $ret=""; + } + return IkiWiki::preprocess($params{page}, $params{destpage}, $ret); +} + +package IkiWiki::PageSpec; + +sub match_enabled ($$;@) { + shift; + my $plugin=shift; + + # test if the plugin is enabled + if (UNIVERSAL::can("IkiWiki::Plugin::".$plugin, "import")) { + return IkiWiki::SuccessReason->new("$plugin is enabled"); + } + else { + return IkiWiki::FailReason->new("$plugin is not enabled"); + } +} + +sub match_sourcepage ($$;@) { + shift; + my $glob=shift; + my %params=@_; + + $glob=derel($glob, $params{location}); + + return IkiWiki::FailReason->new("cannot match sourcepage") unless exists $params{sourcepage}; + if (match_glob($params{sourcepage}, $glob, @_)) { + return IkiWiki::SuccessReason->new("sourcepage matches $glob"); + } + else { + return IkiWiki::FailReason->new("sourcepage does not match $glob"); + } +} + +sub match_destpage ($$;@) { + shift; + my $glob=shift; + my %params=@_; + + $glob=derel($glob, $params{location}); + + return IkiWiki::FailReason->new("cannot match destpage") unless exists $params{destpage}; + if (match_glob($params{destpage}, $glob, @_)) { + return IkiWiki::SuccessReason->new("destpage matches $glob"); + } + else { + return IkiWiki::FailReason->new("destpage does not match $glob"); + } +} + +sub match_included ($$;@) { + shift; + shift; + my %params=@_; + + return IkiWiki::FailReason->new("cannot match included") unless exists $params{sourcepage} && exists $params{destpage}; + if ($params{sourcepage} ne $params{destpage}) { + return IkiWiki::SuccessReason->new("page $params{sourcepage} is included"); + } + else { + return IkiWiki::FailReason->new("page $params{sourcepage} is not included"); + } +} + +1 diff --git a/IkiWiki/Plugin/creole.pm b/IkiWiki/Plugin/creole.pm new file mode 100644 index 000000000..a1e4b31d3 --- /dev/null +++ b/IkiWiki/Plugin/creole.pm @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# WikiCreole markup +# based on the WikiText plugin. +package IkiWiki::Plugin::creole; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "creole", call => \&getsetup); + hook(type => "htmlize", id => "creole", call => \&htmlize); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => 1, # format plugin + section => "format", + }, +} + +sub htmlize (@) { + my %params=@_; + my $content = $params{content}; + + eval q{use Text::WikiCreole}; + return $content if $@; + + # don't parse WikiLinks, ikiwiki already does + creole_customlinks(); + creole_custombarelinks(); + + return creole_parse($content); +} + +1 diff --git a/IkiWiki/Plugin/cutpaste.pm b/IkiWiki/Plugin/cutpaste.pm new file mode 100644 index 000000000..0f6ea0b1f --- /dev/null +++ b/IkiWiki/Plugin/cutpaste.pm @@ -0,0 +1,90 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::cutpaste; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "cutpaste", call => \&getsetup); + hook(type => "needsbuild", id => "cutpaste", call => \&needsbuild); + hook(type => "preprocess", id => "cut", call => \&preprocess_cut, scan => 1); + hook(type => "preprocess", id => "copy", call => \&preprocess_copy, scan => 1); + hook(type => "preprocess", id => "paste", call => \&preprocess_paste); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub needsbuild (@) { + my $needsbuild=shift; + foreach my $page (keys %pagestate) { + if (exists $pagestate{$page}{cutpaste}) { + if (exists $pagesources{$page} && + grep { $_ eq $pagesources{$page} } @$needsbuild) { + # remove state, will be re-added if + # the cut/copy directive is still present + # on rebuild. + delete $pagestate{$page}{cutpaste}; + } + } + } + return $needsbuild; +} + +sub preprocess_cut (@) { + my %params=@_; + + foreach my $param (qw{id text}) { + if (! exists $params{$param}) { + error sprintf(gettext('%s parameter is required'), $param); + } + } + + $pagestate{$params{page}}{cutpaste}{$params{id}} = $params{text}; + + return "" if defined wantarray; +} + +sub preprocess_copy (@) { + my %params=@_; + + foreach my $param (qw{id text}) { + if (! exists $params{$param}) { + error sprintf(gettext('%s parameter is required'), $param); + } + } + + $pagestate{$params{page}}{cutpaste}{$params{id}} = $params{text}; + + return IkiWiki::preprocess($params{page}, $params{destpage}, $params{text}) + if defined wantarray; +} + +sub preprocess_paste (@) { + my %params=@_; + + foreach my $param (qw{id}) { + if (! exists $params{$param}) { + error sprintf(gettext('%s parameter is required'), $param); + } + } + + if (! exists $pagestate{$params{page}}{cutpaste}) { + error gettext('no text was copied in this page'); + } + if (! exists $pagestate{$params{page}}{cutpaste}{$params{id}}) { + error sprintf(gettext('no text was copied in this page with id %s'), $params{id}); + } + + return IkiWiki::preprocess($params{page}, $params{destpage}, + $pagestate{$params{page}}{cutpaste}{$params{id}}); +} + +1; diff --git a/IkiWiki/Plugin/cvs.pm b/IkiWiki/Plugin/cvs.pm new file mode 100644 index 000000000..841aec914 --- /dev/null +++ b/IkiWiki/Plugin/cvs.pm @@ -0,0 +1,549 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::cvs; + +# Copyright (c) 2009 Amitai Schlair +# All rights reserved. +# +# This code is derived from software contributed to ikiwiki +# by Amitai Schlair. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY IKIWIKI AND CONTRIBUTORS ``AS IS'' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +use warnings; +use strict; +use IkiWiki; + +use URI::Escape q{uri_escape_utf8}; +use File::chdir; + + +# GENERAL PLUGIN API CALLS + +sub import { + hook(type => "checkconfig", id => "cvs", call => \&checkconfig); + hook(type => "getsetup", id => "cvs", call => \&getsetup); + hook(type => "genwrapper", id => "cvs", call => \&genwrapper); + + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); + hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); + hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); + hook(type => "rcs", id => "rcs_commit_staged", call => \&rcs_commit_staged); + hook(type => "rcs", id => "rcs_add", call => \&rcs_add); + hook(type => "rcs", id => "rcs_remove", call => \&rcs_remove); + hook(type => "rcs", id => "rcs_rename", call => \&rcs_rename); + 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 () { + if (! defined $config{cvspath}) { + $config{cvspath}="ikiwiki"; + } + if (exists $config{cvspath}) { + # code depends on the path not having extraneous slashes + $config{cvspath}=~tr#/#/#s; + $config{cvspath}=~s/\/$//; + $config{cvspath}=~s/^\///; + } + if (defined $config{cvs_wrapper} && length $config{cvs_wrapper}) { + push @{$config{wrappers}}, { + wrapper => $config{cvs_wrapper}, + wrappermode => (defined $config{cvs_wrappermode} ? $config{cvs_wrappermode} : "04755"), + }; + } +} + +sub getsetup () { + return + plugin => { + safe => 0, # rcs plugin + rebuild => undef, + section => "rcs", + }, + cvsrepo => { + type => "string", + example => "/cvs/wikirepo", + description => "cvs repository location", + safe => 0, # path + rebuild => 0, + }, + cvspath => { + type => "string", + example => "ikiwiki", + description => "path inside repository where the wiki is located", + safe => 0, # paranoia + rebuild => 0, + }, + cvs_wrapper => { + type => "string", + example => "/cvs/wikirepo/CVSROOT/post-commit", + description => "cvs post-commit hook to generate (triggered by CVSROOT/loginfo entry)", + safe => 0, # file + rebuild => 0, + }, + cvs_wrappermode => { + type => "string", + example => '04755', + description => "mode for cvs_wrapper (can safely be made suid)", + safe => 0, + rebuild => 0, + }, + historyurl => { + type => "string", + example => "http://cvs.example.org/cvsweb.cgi/ikiwiki/[[file]]", + description => "cvsweb url to show file history ([[file]] substituted)", + safe => 1, + rebuild => 1, + }, + diffurl => { + type => "string", + example => "http://cvs.example.org/cvsweb.cgi/ikiwiki/[[file]].diff?r1=text&tr1=[[r1]]&r2=text&tr2=[[r2]]", + description => "cvsweb url to show a diff ([[file]], [[r1]], and [[r2]] substituted)", + safe => 1, + rebuild => 1, + }, +} + +sub genwrapper () { + return <$tmpfile"); + if ($? == -1) { + error "couldn't run cvsps: $!\n"; + } + elsif (($? >> 8) != 0) { + error "cvsps exited " . ($? >> 8) . ": $!\n"; + } + + tie(*SPSVC, 'File::ReadBackwards', $tmpfile) + || error "couldn't open $tmpfile for read: $!\n"; + + while (my $line = ) { + $line =~ /^$/ || error "expected blank line, got $line"; + + my ($rev, $user, $committype, $when); + my (@message, @pages); + + # We're reading backwards. + # Forwards, an entry looks like so: + # --------------------- + # PatchSet $rev + # Date: $when + # Author: $user (or user CGI runs as, for web commits) + # Branch: branch + # Tag: tag + # Log: + # @message_lines + # Members: + # @pages (and revisions) + # + + while ($line = ) { + last if ($line =~ /^Members:/); + for ($line) { + s/^\s+//; + s/\s+$//; + } + my ($page, $revs) = split(/:/, $line); + my ($oldrev, $newrev) = split(/->/, $revs); + $oldrev =~ s/INITIAL/0/; + $newrev =~ s/\(DEAD\)//; + my $diffurl = defined $config{diffurl} ? $config{diffurl} : ""; + my $epage = join('/', + map { uri_escape_utf8($_) } split('/', $page) + ); + $diffurl=~s/\[\[file\]\]/$epage/g; + $diffurl=~s/\[\[r1\]\]/$oldrev/g; + $diffurl=~s/\[\[r2\]\]/$newrev/g; + unshift @pages, { + page => pagename($page), + diffurl => $diffurl, + } if length $page; + } + + while ($line = ) { + last if ($line =~ /^Log:$/); + chomp $line; + unshift @message, { line => $line }; + } + $committype = "web"; + if (defined $message[0] && + $message[0]->{line}=~/$config{web_commit_regexp}/) { + $user=defined $2 ? "$2" : "$3"; + $message[0]->{line}=$4; + } + else { + $committype="cvs"; + } + + $line = ; # Tag + $line = ; # Branch + + $line = ; + if ($line =~ /^Author: (.*)$/) { + $user = $1 unless defined $user && length $user; + } + else { + error "expected Author, got $line"; + } + + $line = ; + if ($line =~ /^Date: (.*)$/) { + $when = str2time($1, 'UTC'); + } + else { + error "expected Date, got $line"; + } + + $line = ; + if ($line =~ /^PatchSet (.*)$/) { + $rev = $1; + } + else { + error "expected PatchSet, got $line"; + } + + $line = ; # --------------------- + + push @ret, { + rev => $rev, + user => $user, + committype => $committype, + when => $when, + message => [@message], + pages => [@pages], + } if @pages; + last if @ret >= $num; + } + + unlink($tmpfile) || error "couldn't unlink $tmpfile: $!\n"; + + return @ret; +} + +sub rcs_diff ($;$) { + my $rev=IkiWiki::possibly_foolish_untaint(int(shift)); + my $maxlines=shift; + + local $CWD = $config{srcdir}; + + # diff output is unavoidably preceded by the cvsps PatchSet entry + my @cvsps = `env TZ=UTC cvsps -q --cvs-direct -z 30 -g -s $rev`; + my $blank_lines_seen = 0; + + # skip log, get to the diff + while (my $line = shift @cvsps) { + $blank_lines_seen++ if ($line =~ /^$/); + last if $blank_lines_seen == 2; + } + + @cvsps = @cvsps[0..$maxlines-1] + if defined $maxlines && @cvsps > $maxlines; + + if (wantarray) { + return @cvsps; + } + else { + return join("", @cvsps); + } +} + +sub rcs_getctime ($) { + my $file=shift; + + local $CWD = $config{srcdir}; + + my $cvs_log_infoline=qr/^date: (.+);\s+author/; + + open CVSLOG, "cvs -Q log -r1.1 '$file' |" + || error "couldn't get cvs log output: $!\n"; + + my $date; + while () { + if (/$cvs_log_infoline/) { + $date=$1; + } + } + close CVSLOG || warn "cvs log $file exited $?"; + + if (! defined $date) { + warn "failed to parse cvs log for $file\n"; + return 0; + } + + eval q{use Date::Parse}; + error($@) if $@; + $date=str2time($date, 'UTC'); + debug("found ctime ".localtime($date)." for $file"); + return $date; +} + +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for cvs\n"; # TODO +} + + +# INTERNAL SUPPORT ROUTINES + +sub commitmessage (@) { + my %params=@_; + + if (defined $params{session}) { + if (defined $params{session}->param("name")) { + return "web commit by ". + $params{session}->param("name"). + (length $params{message} ? ": $params{message}" : ""); + } + elsif (defined $params{session}->remote_addr()) { + return "web commit from ". + $params{session}->remote_addr(). + (length $params{message} ? ": $params{message}" : ""); + } + } + return $params{message}; +} + +sub cvs_info ($$) { + my $field=shift; + my $file=shift; + + local $CWD = $config{srcdir}; + + my $info=`cvs status $file`; + my ($ret)=$info=~/^\s*$field:\s*(\S+)/m; + return $ret; +} + +sub cvs_is_controlling { + my $dir=shift; + $dir=$config{srcdir} unless defined($dir); + return (-d "$dir/CVS") ? 1 : 0; +} + +sub cvs_keyword_subst_args ($) { + my $file = shift; + + local $CWD = $config{srcdir}; + + eval q{use File::MimeInfo}; + error($@) if $@; + my $filemime = File::MimeInfo::default($file); + # if (-T $file) { + + defined($filemime) && $filemime eq 'text/plain' + ? return ('-kkv', $file) + : return ('-kb', $file); +} + +sub cvs_runcvs(@) { + my @cmd = @_; + unshift @cmd, 'cvs', '-Q'; + + # CVS can't operate outside a srcdir, so we're always setting $CWD. + # "local $CWD" restores the previous value when we go out of scope. + # Usually that's correct. But if we're removing the last file from + # a directory, the post-commit hook will exec in a working directory + # that's about to not exist (CVS will prune it). + # + # chdir() manually here, so we can selectively not chdir() back. + + my $oldcwd = $CWD; + chdir($config{srcdir}); + + eval q{ + use IPC::Open3; + use Symbol qw(gensym); + use IO::File; + }; + error($@) if $@; + + my $cvsout = ''; + my $cvserr = ''; + local *CATCHERR = IO::File->new_tmpfile; + my $pid = open3(gensym(), \*CATCHOUT, ">&CATCHERR", @cmd); + while (my $l = ) { + $cvsout .= $l + unless 1; + } + waitpid($pid, 0); + my $ret = $? >> 8; + seek CATCHERR, 0, 0; + while (my $l = ) { + $cvserr .= $l + unless $l =~ /^cvs commit: changing keyword expansion /; + } + + print STDOUT $cvsout; + print STDERR $cvserr; + + chdir($oldcwd) if -d $oldcwd; + + return ($ret == 0) ? 1 : 0; +} + +1 diff --git a/IkiWiki/Plugin/darcs.pm b/IkiWiki/Plugin/darcs.pm new file mode 100644 index 000000000..646f65df1 --- /dev/null +++ b/IkiWiki/Plugin/darcs.pm @@ -0,0 +1,438 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::darcs; + +use warnings; +use strict; +use URI::Escape q{uri_escape_utf8}; +use IkiWiki; + +sub import { + hook(type => "checkconfig", id => "darcs", call => \&checkconfig); + hook(type => "getsetup", id => "darcs", call => \&getsetup); + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); + hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); + hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); + hook(type => "rcs", id => "rcs_commit_staged", call => \&rcs_commit_staged); + hook(type => "rcs", id => "rcs_add", call => \&rcs_add); + hook(type => "rcs", id => "rcs_remove", call => \&rcs_remove); + hook(type => "rcs", id => "rcs_rename", call => \&rcs_rename); + 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 (@) { + open(SAVED_STDOUT, ">&STDOUT"); + open(STDOUT, ">/dev/null"); + my $ret = system @_; + open(STDOUT, ">&SAVED_STDOUT"); + return $ret; +} + +sub darcs_info ($$$) { + my $field = shift; + my $repodir = shift; + my $file = shift; # Relative to the repodir. + + my $child = open(DARCS_CHANGES, "-|"); + if (! $child) { + exec('darcs', 'changes', '--repodir', $repodir, '--xml-output', $file) or + error("failed to run 'darcs changes'"); + } + + # Brute force for now. :-/ + while () { + last if /^<\/created_as>$/; + } + ($_) = =~ /$field=\'([^\']+)/; + $field eq 'hash' and s/\.gz//; # Strip away the '.gz' from 'hash'es. + + close(DARCS_CHANGES); + + return $_; +} + +sub file_in_vc ($$) { + my $repodir = shift; + my $file = shift; + + my $child = open(DARCS_MANIFEST, "-|"); + if (! $child) { + exec('darcs', 'query', 'manifest', '--repodir', $repodir) or + error("failed to run 'darcs query manifest'"); + } + my $found=0; + while () { + $found = 1 if /^(\.\/)?$file$/; + } + close(DARCS_MANIFEST) or error("'darcs query manifest' exited " . $?); + + return $found; +} + +sub darcs_rev ($) { + my $file = shift; # Relative to the repodir. + my $repodir = $config{srcdir}; + + return "" unless file_in_vc($repodir, $file); + my $hash = darcs_info('hash', $repodir, $file); + return defined $hash ? $hash : ""; +} + +sub checkconfig () { + if (defined $config{darcs_wrapper} && length $config{darcs_wrapper}) { + push @{$config{wrappers}}, { + wrapper => $config{darcs_wrapper}, + wrappermode => (defined $config{darcs_wrappermode} ? $config{darcs_wrappermode} : "06755"), + }; + } +} + +sub getsetup () { + return + plugin => { + safe => 0, # rcs plugin + rebuild => undef, + section => "rcs", + }, + darcs_wrapper => { + type => "string", + example => "/darcs/repo/_darcs/ikiwiki-wrapper", + description => "wrapper to generate (set as master repo apply hook)", + safe => 0, # file + rebuild => 0, + }, + darcs_wrappermode => { + type => "string", + example => '06755', + description => "mode for darcs_wrapper (can safely be made suid)", + safe => 0, + rebuild => 0, + }, + historyurl => { + type => "string", + example => "http://darcs.example.com/darcsweb.cgi?r=wiki;a=filehistory;f=[[file]]", + description => "darcsweb url to show file history ([[file]] substituted)", + safe => 1, + rebuild => 1, + }, + diffurl => { + type => "string", + example => "http://darcs.example.com/darcsweb.cgi?r=wiki;a=filediff;h=[[hash]];f=[[file]]", + description => "darcsweb url to show a diff ([[hash]] and [[file]] substituted)", + safe => 1, + rebuild => 1, + }, +} + +sub rcs_update () { + silentsystem('darcs', "pull", "--repodir", $config{srcdir}, "-qa") +} + +sub rcs_prepedit ($) { + # Prepares to edit a file under revision control. Returns a token that + # must be passed to rcs_commit() when the file is to be commited. For us, + # this token the hash value of the latest patch that modifies the file, + # i.e. something like its current revision. + + my $file = shift; # Relative to the repodir. + my $rev = darcs_rev($file); + return $rev; +} + +sub commitauthor (@) { + my %params=@_; + + my $author="anon\@web"; + if (defined $params{session}) { + if (defined $params{session}->param("name")) { + return $params{session}->param("name").'@web'; + } + elsif (defined $params{session}->remote_addr()) { + return $params{session}->remote_addr().'@web'; + } + } + return 'anon@web'; +} + +sub rcs_commit (@) { + # Commit the page. Returns 'undef' on success and a version of the page + # with conflict markers on failure. + my %params=@_; + + my ($file, $message, $token) = + ($params{file}, $params{message}, $params{token}); + + # Compute if the "revision" of $file changed. + my $changed = darcs_rev($file) ne $token; + + # Yes, the following is a bit convoluted. + if ($changed) { + # TODO. Invent a better, non-conflicting name. + rename("$config{srcdir}/$file", "$config{srcdir}/$file.save") or + error("failed to rename $file to $file.save: $!"); + + # Roll the repository back to $token. + + # TODO. Can we be sure that no changes are lost? I think that + # we can, if we make sure that the 'darcs push' below will always + # succeed. + + # We need to revert everything as 'darcs obliterate' might choke + # otherwise. + # TODO: 'yes | ...' needed? Doesn't seem so. + silentsystem('darcs', "revert", "--repodir", $config{srcdir}, "--all") == 0 || + error("'darcs revert' failed"); + # Remove all patches starting at $token. + my $child = open(DARCS_OBLITERATE, "|-"); + if (! $child) { + open(STDOUT, ">/dev/null"); + exec('darcs', "obliterate", "--repodir", $config{srcdir}, + "--match", "hash " . $token) and + error("'darcs obliterate' failed"); + } + 1 while print DARCS_OBLITERATE "y"; + close(DARCS_OBLITERATE); + # Restore the $token one. + silentsystem('darcs', "pull", "--quiet", "--repodir", $config{srcdir}, + "--match", "hash " . $token, "--all") == 0 || + error("'darcs pull' failed"); + + # We're back at $token. Re-install the modified file. + rename("$config{srcdir}/$file.save", "$config{srcdir}/$file") or + error("failed to rename $file.save to $file: $!"); + } + + # Record the changes. + my $author=commitauthor(%params); + if (!defined $message || !length($message)) { + $message = "empty message"; + } + silentsystem('darcs', 'record', '--repodir', $config{srcdir}, '--all', + '-m', $message, '--author', $author, $file) == 0 || + error("'darcs record' failed"); + + # Update the repository by pulling from the default repository, which is + # master repository. + silentsystem('darcs', "pull", "--quiet", "--repodir", $config{srcdir}, + "--all") == 0 || error("'darcs pull' failed"); + + # If this updating yields any conflicts, we'll record them now to resolve + # them. If nothing is recorded, there are no conflicts. + $token = darcs_rev($file); + # TODO: Use only the first line here, i.e. only the patch name? + writefile("$file.log", $config{srcdir}, 'resolve conflicts: ' . $message); + silentsystem('darcs', 'record', '--repodir', $config{srcdir}, '--all', + '-m', 'resolve conflicts: ' . $message, '--author', $author, $file) == 0 || + error("'darcs record' failed"); + my $conflicts = darcs_rev($file) ne $token; + unlink("$config{srcdir}/$file.log") or + error("failed to remove '$file.log'"); + + # Push the changes to the main repository. + silentsystem('darcs', 'push', '--quiet', '--repodir', $config{srcdir}, '--all') == 0 || + error("'darcs push' failed"); + # TODO: darcs send? + + if ($conflicts) { + my $document = readfile("$config{srcdir}/$file"); + # Try to leave everything in a consistent state. + # TODO: 'yes | ...' needed? Doesn't seem so. + silentsystem('darcs', "revert", "--repodir", $config{srcdir}, "--all") == 0 || + warn("'darcs revert' failed"); + return $document; + } + else { + return undef; + } +} + +sub rcs_commit_staged (@) { + my %params=@_; + + my $author=commitauthor(%params); + if (!defined $params{message} || !length($params{message})) { + $params{message} = "empty message"; + } + + silentsystem('darcs', "record", "--repodir", $config{srcdir}, + "-a", "-A", $author, + "-m", $params{message}, + ) == 0 || error("'darcs record' failed"); + + # Push the changes to the main repository. + silentsystem('darcs', 'push', '--quiet', '--repodir', $config{srcdir}, '--all') == 0 || + error("'darcs push' failed"); + # TODO: darcs send? + + return undef; +} + +sub rcs_add ($) { + my $file = shift; # Relative to the repodir. + + if(! file_in_vc($config{srcdir}, $file)) { + # Intermediate directories will be added automagically. + system('darcs', 'add', '--quiet', '--repodir', $config{srcdir}, + '--boring', $file) == 0 || error("'darcs add' failed"); + } +} + +sub rcs_remove ($) { + my $file = shift; # Relative to the repodir. + + unlink($config{srcdir}.'/'.$file); +} + +sub rcs_rename ($$) { + my $a = shift; # Relative to the repodir. + my $b = shift; # Relative to the repodir. + + system('darcs', 'mv', '--repodir', $config{srcdir}, $a, $b) == 0 || + error("'darcs mv' failed"); +} + +sub rcs_recentchanges ($) { + my $num=shift; + my @ret; + + eval q{use Date::Parse}; + eval q{use XML::Simple}; + + my $repodir=$config{srcdir}; + + my $child = open(LOG, "-|"); + if (! $child) { + $ENV{"DARCS_DONT_ESCAPE_ANYTHING"}=1; + exec("darcs", "changes", "--xml", + "--summary", + "--repodir", "$repodir", + "--last", "$num") + || error("'darcs changes' failed to run"); + } + my $data; + $data .= $_ while(); + close LOG; + + my $log = XMLin($data, ForceArray => 1); + + foreach my $patch (@{$log->{patch}}) { + my $date=$patch->{local_date}; + my $hash=$patch->{hash}; + my $when=str2time($date); + my (@pages, @files, @pg); + push @pages, $_ foreach (@{$patch->{summary}->[0]->{modify_file}}); + push @pages, $_ foreach (@{$patch->{summary}->[0]->{add_file}}); + push @pages, $_ foreach (@{$patch->{summary}->[0]->{remove_file}}); + foreach my $f (@pages) { + $f = $f->{content} if ref $f; + $f =~ s,^\s+,,; $f =~ s,\s+$,,; # cut whitespace + + push @files, $f; + } + foreach my $p (@{$patch->{summary}->[0]->{move}}) { + push @files, $p->{from}; + } + + foreach my $f (@files) { + my $d = defined $config{'diffurl'} ? $config{'diffurl'} : ""; + my $ef = uri_escape_utf8($f); + $d =~ s/\[\[file\]\]/$ef/go; + $d =~ s/\[\[hash\]\]/$hash/go; + + push @pg, { + page => pagename($f), + diffurl => $d, + }; + } + next unless (scalar @pg > 0); + + my @message; + push @message, { line => $_ } foreach (@{$patch->{name}}); + + my $committype; + my $author; + if ($patch->{author} =~ /(.*)\@web$/) { + $author = $1; + $committype = "web"; + } + else { + $author=$patch->{author}; + $committype = "darcs"; + } + + push @ret, { + rev => $patch->{hash}, + user => $author, + committype => $committype, + when => $when, + message => [@message], + pages => [@pg], + }; + } + + return @ret; +} + +sub rcs_diff ($;$) { + my $rev=shift; + my $maxlines=shift; + my @lines; + my $repodir=$config{srcdir}; + foreach my $line (`darcs diff --repodir $repodir --match 'hash $rev'`) { + if (@lines || $line=~/^diff/) { + last if defined $maxlines && @lines == $maxlines; + push @lines, $line."\n"; + } + } + if (wantarray) { + return @lines; + } + else { + return join("", @lines); + } +} + +sub rcs_getctime ($) { + my $file=shift; + + eval q{use Date::Parse}; + eval q{use XML::Simple}; + local $/=undef; + + my $child = open(LOG, "-|"); + if (! $child) { + exec("darcs", "changes", "--xml", "--reverse", + "--repodir", $config{srcdir}, $file) + || error("'darcs changes $file' failed to run"); + } + + my $data; + { + local $/=undef; + $data = ; + } + close LOG; + + my $log = XMLin($data, ForceArray => 1); + + my $datestr = $log->{patch}[0]->{local_date}; + + if (! defined $datestr) { + warn "failed to get ctime for $file"; + return 0; + } + + my $date = str2time($datestr); + + debug("ctime for '$file': ". localtime($date)); + + return $date; +} + +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for darcs\n"; # TODO +} + +1 diff --git a/IkiWiki/Plugin/date.pm b/IkiWiki/Plugin/date.pm new file mode 100644 index 000000000..ea5c9a9c5 --- /dev/null +++ b/IkiWiki/Plugin/date.pm @@ -0,0 +1,34 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::date; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "date", call => \&getsetup); + hook(type => "preprocess", id => "date", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess (@) { + my $str=shift; + + eval q{use Date::Parse}; + error $@ if $@; + my $time = str2time($str); + if (! defined $time) { + error("unable to parse $str"); + } + return displaytime($time); +} + +1 diff --git a/IkiWiki/Plugin/ddate.pm b/IkiWiki/Plugin/ddate.pm new file mode 100644 index 000000000..bb77ce59f --- /dev/null +++ b/IkiWiki/Plugin/ddate.pm @@ -0,0 +1,41 @@ +#!/usr/bin/perl +# Discordian date support fnord ikiwiki. +package IkiWiki::Plugin::ddate; + +use IkiWiki 3.00; +no warnings; + +sub import { + hook(type => "getsetup", id => "ddate", call => \&getsetup); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => 1, + }, +} + +sub IkiWiki::formattime ($;$) { + my $time=shift; + my $format=shift; + if (! defined $format) { + $format=$config{timeformat}; + if ($format eq '%c') { + $format='on %A, the %e of %B, %Y. %N%nCelebrate %H'; + } + } + eval q{ + use DateTime; + use DateTime::Calendar::Discordian; + }; + if ($@) { + return "some time or other ($@ -- hail Eris!)"; + } + my $dt = DateTime->from_epoch(epoch => $time); + my $dd = DateTime::Calendar::Discordian->from_object(object => $dt); + return $dd->strftime($format); +} + +5 diff --git a/IkiWiki/Plugin/editdiff.pm b/IkiWiki/Plugin/editdiff.pm new file mode 100644 index 000000000..015ce9c14 --- /dev/null +++ b/IkiWiki/Plugin/editdiff.pm @@ -0,0 +1,78 @@ +#!/usr/bin/perl +# This plugin adds a "Diff" button to the page edit form. +package IkiWiki::Plugin::editdiff; + +use warnings; +use strict; +use IkiWiki 3.00; +use HTML::Entities; +use IPC::Open2; + +sub import { + hook(type => "getsetup", id => "editdiff", call => \&getsetup); + hook(type => "formbuilder_setup", id => "editdiff", + call => \&formbuilder_setup); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "web", + }, +} + +sub diff ($$) { + my $orig=shift; + my $content=shift; + + my $sigpipe=0; + $SIG{PIPE} = sub { $sigpipe=1; }; + + my $pid = open2(*DIFFOUT, *DIFFIN, 'diff', '-u', $orig, '-'); + binmode($_, ':utf8') foreach (*DIFFIN, *DIFFOUT); + + print DIFFIN $content; + close DIFFIN; + my $ret=''; + while () { + if (defined $ret) { + $ret.=$_; + } + elsif (/^\@\@/) { + $ret=$_; + } + } + close DIFFOUT; + waitpid $pid, 0; + + $SIG{PIPE}="default"; + return "couldn't run diff\n" if $sigpipe; + + return "
".encode_entities($ret)."
"; +} + +sub formbuilder_setup { + my %params=@_; + my $form=$params{form}; + + return if ! defined $form->field("do") || $form->field("do") ne "edit"; + + my $page=$form->field("page"); + $page = IkiWiki::possibly_foolish_untaint($page); + return unless exists $pagesources{$page}; + + push @{$params{buttons}}, "Diff"; + + if ($form->submitted eq "Diff") { + my $content=$form->field('editcontent'); + $content=~s/\r\n/\n/g; + $content=~s/\r/\n/g; + + my $diff = diff(srcfile($pagesources{$page}), $content); + $form->tmpl_param("page_diff", $diff); + } +} + +1 diff --git a/IkiWiki/Plugin/editpage.pm b/IkiWiki/Plugin/editpage.pm new file mode 100644 index 000000000..d15607990 --- /dev/null +++ b/IkiWiki/Plugin/editpage.pm @@ -0,0 +1,474 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::editpage; + +use warnings; +use strict; +use IkiWiki; +use open qw{:utf8 :std}; + +sub import { + hook(type => "getsetup", id => "editpage", call => \&getsetup); + hook(type => "refresh", id => "editpage", call => \&refresh); + hook(type => "sessioncgi", id => "editpage", call => \&IkiWiki::cgi_editpage); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + section => "core", + }, +} + +sub refresh () { + if (exists $wikistate{editpage} && exists $wikistate{editpage}{previews}) { + # Expire old preview files after one hour. + my $expire=time - (60 * 60); + + my @previews; + foreach my $file (@{$wikistate{editpage}{previews}}) { + my $mtime=(stat("$config{destdir}/$file"))[9]; + if (defined $mtime && $mtime <= $expire) { + # Avoid deleting a preview that was later saved. + my $delete=1; + foreach my $page (keys %renderedfiles) { + if (grep { $_ eq $file } @{$renderedfiles{$page}}) { + $delete=0; + } + } + if ($delete) { + debug(sprintf(gettext("removing old preview %s"), $file)); + IkiWiki::prune("$config{destdir}/$file", $config{destdir}); + } + } + elsif (defined $mtime) { + push @previews, $file; + } + } + $wikistate{editpage}{previews}=\@previews; + } +} + +# Back to ikiwiki namespace for the rest, this code is very much +# internal to ikiwiki even though it's separated into a plugin, +# and other plugins use the function below. +package IkiWiki; + +sub cgi_editpage ($$) { + my $q=shift; + my $session=shift; + + my $do=$q->param('do'); + return unless $do eq 'create' || $do eq 'edit'; + + decode_cgi_utf8($q); + + my @fields=qw(do rcsinfo subpage from page type editcontent + editmessage subscribe); + my @buttons=("Save Page", "Preview", "Cancel"); + eval q{use CGI::FormBuilder}; + error($@) if $@; + my $form = CGI::FormBuilder->new( + fields => \@fields, + charset => "utf-8", + method => 'POST', + required => [qw{editcontent}], + javascript => 0, + params => $q, + action => IkiWiki::cgiurl(), + header => 0, + table => 0, + template => { template("editpage.tmpl") }, + ); + + decode_form_utf8($form); + run_hooks(formbuilder_setup => sub { + shift->(form => $form, cgi => $q, session => $session, + buttons => \@buttons); + }); + decode_form_utf8($form); + + # This untaint is safe because we check file_pruned and + # wiki_file_regexp. + my ($page)=$form->field('page')=~/$config{wiki_file_regexp}/; + if (! defined $page) { + error(gettext("bad page name")); + } + $page=possibly_foolish_untaint($page); + my $absolute=($page =~ s#^/+##); # absolute name used to force location + if (! defined $page || ! length $page || + file_pruned($page)) { + error(gettext("bad page name")); + } + + my $baseurl = urlto($page); + + my $from; + if (defined $form->field('from')) { + ($from)=$form->field('from')=~/$config{wiki_file_regexp}/; + } + + my $file; + my $type; + if (exists $pagesources{$page} && $form->field("do") ne "create") { + $file=$pagesources{$page}; + $type=pagetype($file); + if (! defined $type || $type=~/^_/) { + error(sprintf(gettext("%s is not an editable page"), $page)); + } + if (! $form->submitted) { + $form->field(name => "rcsinfo", + value => rcs_prepedit($file), force => 1); + } + $form->field(name => "editcontent", validate => '/.*/'); + } + else { + $type=$form->param('type'); + if (defined $type && length $type && $hooks{htmlize}{$type}) { + $type=possibly_foolish_untaint($type); + } + elsif (defined $from && exists $pagesources{$from}) { + # favor the type of linking page + $type=pagetype($pagesources{$from}); + } + $type=$config{default_pageext} + if ! defined $type || $type=~/^_/; # not internal type + $file=newpagefile($page, $type); + if (! $form->submitted) { + $form->field(name => "rcsinfo", value => "", force => 1); + } + $form->field(name => "editcontent", validate => '/.+/'); + } + + $form->field(name => "do", type => 'hidden'); + $form->field(name => "sid", type => "hidden", value => $session->id, + force => 1); + $form->field(name => "from", type => 'hidden'); + $form->field(name => "rcsinfo", type => 'hidden'); + $form->field(name => "subpage", type => 'hidden'); + $form->field(name => "page", value => $page, force => 1); + $form->field(name => "type", value => $type, force => 1); + $form->field(name => "editmessage", type => "text", size => 80); + $form->field(name => "editcontent", type => "textarea", rows => 20, + cols => 80); + $form->tmpl_param("can_commit", $config{rcs}); + $form->tmpl_param("helponformattinglink", + htmllink($page, $page, "ikiwiki/formatting", + noimageinline => 1, + linktext => "FormattingHelp")); + + my $cansubscribe=IkiWiki::Plugin::notifyemail->can("subscribe") + && IkiWiki::Plugin::comments->can("import") + && defined $session->param('name'); + if ($cansubscribe) { + $form->field(name => "subscribe", type => "checkbox", + options => [gettext("email comments to me")]); + } + else { + $form->field(name => "subscribe", type => 'hidden'); + } + + my $previewing=0; + if ($form->submitted eq "Cancel") { + if ($form->field("do") eq "create" && defined $from) { + redirect($q, urlto($from)); + } + elsif ($form->field("do") eq "create") { + redirect($q, baseurl(undef)); + } + else { + redirect($q, $baseurl); + } + exit; + } + elsif ($form->submitted eq "Preview") { + $previewing=1; + + my $new=not exists $pagesources{$page}; + # temporarily record its type + $pagesources{$page}=$page.".".$type if $new; + my %wasrendered=map { $_ => 1 } @{$renderedfiles{$page}}; + + my $content=$form->field('editcontent'); + + run_hooks(editcontent => sub { + $content=shift->( + content => $content, + page => $page, + cgi => $q, + session => $session, + ); + }); + my $preview=htmlize($page, $page, $type, + linkify($page, $page, + preprocess($page, $page, + filter($page, $page, $content), 0, 1))); + run_hooks(format => sub { + $preview=shift->( + page => $page, + content => $preview, + ); + }); + $form->tmpl_param("page_preview", $preview); + + # Previewing may have created files on disk. + # Keep a list of these to be deleted later. + my %previews = map { $_ => 1 } @{$wikistate{editpage}{previews}}; + foreach my $f (@{$renderedfiles{$page}}) { + $previews{$f}=1 unless $wasrendered{$f}; + } + + # Throw out any other state changes made during previewing, + # and save the previews list. + loadindex(); + @{$wikistate{editpage}{previews}} = keys %previews; + saveindex(); + } + elsif ($form->submitted eq "Save Page") { + $form->tmpl_param("page_preview", ""); + } + + if ($form->submitted ne "Save Page" || ! $form->validate) { + if ($form->field("do") eq "create") { + my @page_locs; + my $best_loc; + if (! defined $from || ! length $from || + $from ne $form->field('from') || + file_pruned($from) || + $absolute || + $form->submitted) { + @page_locs=$best_loc=$page; + unshift @page_locs, lc($page) + if ! $form->submitted && lc($page) ne $page; + } + elsif (lc $page eq lc $config{discussionpage}) { + @page_locs=$best_loc="$from/".lc($page); + } + else { + my $dir=$from."/"; + $dir=~s![^/]+/+$!!; + + if ((defined $form->field('subpage') && + length $form->field('subpage'))) { + $best_loc="$from/$page"; + } + else { + $best_loc=$dir.$page; + } + + my $mixedcase=lc($page) ne $page; + + push @page_locs, $dir.lc($page) if $mixedcase; + push @page_locs, $dir.$page; + push @page_locs, $from."/".lc($page) if $mixedcase; + push @page_locs, $from."/".$page; + while (length $dir) { + $dir=~s![^/]+/+$!!; + push @page_locs, $dir.lc($page) if $mixedcase; + push @page_locs, $dir.$page; + } + + my $userpage=IkiWiki::userpage($page); + push @page_locs, $userpage + if ! grep { $_ eq $userpage } @page_locs; + } + + @page_locs = grep { + ! exists $pagecase{lc $_} + } @page_locs; + if (! @page_locs) { + # hmm, someone else made the page in the + # meantime? + if ($previewing) { + # let them go ahead with the edit + # and resolve the conflict at save + # time + @page_locs=$page; + } + else { + redirect($q, $baseurl); + exit; + } + } + + my @editable_locs = grep { + check_canedit($_, $q, $session, 1) + } @page_locs; + if (! @editable_locs) { + # now let it throw an error, or prompt for + # login + map { check_canedit($_, $q, $session) } + ($best_loc, @page_locs); + } + + my @page_types; + if (exists $hooks{htmlize}) { + foreach my $key (grep { !/^_/ } keys %{$hooks{htmlize}}) { + push @page_types, [$key, $hooks{htmlize}{$key}{longname} || $key]; + } + } + @page_types=sort @page_types; + + $form->tmpl_param("page_select", 1); + $form->field(name => "page", type => 'select', + options => [ map { [ $_, pagetitle($_, 1) ] } @editable_locs ], + value => $best_loc); + $form->field(name => "type", type => 'select', + options => \@page_types); + $form->title(sprintf(gettext("creating %s"), pagetitle(basename($page)))); + + } + elsif ($form->field("do") eq "edit") { + check_canedit($page, $q, $session); + if (! defined $form->field('editcontent') || + ! length $form->field('editcontent')) { + my $content=""; + if (exists $pagesources{$page}) { + $content=readfile(srcfile($pagesources{$page})); + $content=~s/\n/\r\n/g; + } + $form->field(name => "editcontent", value => $content, + force => 1); + } + $form->tmpl_param("page_select", 0); + $form->field(name => "page", type => 'hidden'); + $form->field(name => "type", type => 'hidden'); + $form->title(sprintf(gettext("editing %s"), pagetitle(basename($page)))); + } + + showform($form, \@buttons, $session, $q, page => $page); + } + else { + # save page + check_canedit($page, $q, $session); + checksessionexpiry($q, $session, $q->param('sid')); + + my $exists=-e "$config{srcdir}/$file"; + + if ($form->field("do") ne "create" && ! $exists && + ! defined srcfile($file, 1)) { + $form->tmpl_param("message", template("editpagegone.tmpl")->output); + $form->field(name => "do", value => "create", force => 1); + $form->tmpl_param("page_select", 0); + $form->field(name => "page", type => 'hidden'); + $form->field(name => "type", type => 'hidden'); + $form->title(sprintf(gettext("editing %s"), $page)); + showform($form, \@buttons, $session, $q, + page => $page); + exit; + } + elsif ($form->field("do") eq "create" && $exists) { + $form->tmpl_param("message", template("editcreationconflict.tmpl")->output); + $form->field(name => "do", value => "edit", force => 1); + $form->tmpl_param("page_select", 0); + $form->field(name => "page", type => 'hidden'); + $form->field(name => "type", type => 'hidden'); + $form->title(sprintf(gettext("editing %s"), $page)); + $form->field("editcontent", + value => readfile("$config{srcdir}/$file"). + "\n\n\n".$form->field("editcontent"), + force => 1); + showform($form, \@buttons, $session, $q, + page => $page); + exit; + } + + my $message=""; + if (defined $form->field('editmessage') && + length $form->field('editmessage')) { + $message=$form->field('editmessage'); + } + + my $content=$form->field('editcontent'); + check_content(content => $content, page => $page, + cgi => $q, session => $session, + subject => $message); + run_hooks(editcontent => sub { + $content=shift->( + content => $content, + page => $page, + cgi => $q, + session => $session, + ); + }); + $content=~s/\r\n/\n/g; + $content=~s/\r/\n/g; + $content.="\n" if $content !~ /\n$/; + + $config{cgi}=0; # avoid cgi error message + eval { writefile($file, $config{srcdir}, $content) }; + $config{cgi}=1; + if ($@) { + $form->field(name => "rcsinfo", value => rcs_prepedit($file), + force => 1); + my $mtemplate=template("editfailedsave.tmpl"); + $mtemplate->param(error_message => $@); + $form->tmpl_param("message", $mtemplate->output); + $form->field("editcontent", value => $content, force => 1); + $form->tmpl_param("page_select", 0); + $form->field(name => "page", type => 'hidden'); + $form->field(name => "type", type => 'hidden'); + $form->title(sprintf(gettext("editing %s"), $page)); + showform($form, \@buttons, $session, $q, + page => $page); + exit; + } + + my $conflict; + if ($config{rcs}) { + if (! $exists) { + rcs_add($file); + } + + # Prevent deadlock with post-commit hook by + # signaling to it that it should not try to + # do anything. + disable_commit_hook(); + $conflict=rcs_commit( + file => $file, + message => $message, + token => $form->field("rcsinfo"), + session => $session, + ); + enable_commit_hook(); + rcs_update(); + } + + # Refresh even if there was a conflict, since other changes + # may have been committed while the post-commit hook was + # disabled. + require IkiWiki::Render; + refresh(); + saveindex(); + + if (defined $conflict) { + $form->field(name => "rcsinfo", value => rcs_prepedit($file), + force => 1); + $form->tmpl_param("message", template("editconflict.tmpl")->output); + $form->field("editcontent", value => $conflict, force => 1); + $form->field("do", "edit", force => 1); + $form->tmpl_param("page_select", 0); + $form->field(name => "page", type => 'hidden'); + $form->field(name => "type", type => 'hidden'); + $form->title(sprintf(gettext("editing %s"), $page)); + showform($form, \@buttons, $session, $q, + page => $page); + } + else { + # The trailing question mark tries to avoid broken + # caches and get the most recent version of the page. + redirect($q, $baseurl."?updated"); + } + + if ($cansubscribe && length $form->field("subscribe")) { + my $subspec="comment($page)"; + IkiWiki::Plugin::notifyemail::subscribe( + $session->param('name'), $subspec); + } + } + + exit; +} + +1 diff --git a/IkiWiki/Plugin/edittemplate.pm b/IkiWiki/Plugin/edittemplate.pm new file mode 100644 index 000000000..c7f1e4fa7 --- /dev/null +++ b/IkiWiki/Plugin/edittemplate.pm @@ -0,0 +1,143 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::edittemplate; + +use warnings; +use strict; +use IkiWiki 3.00; +use HTML::Template; +use Encode; + +sub import { + hook(type => "getsetup", id => "edittemplate", + call => \&getsetup); + hook(type => "needsbuild", id => "edittemplate", + call => \&needsbuild); + hook(type => "preprocess", id => "edittemplate", + call => \&preprocess); + hook(type => "formbuilder", id => "edittemplate", + call => \&formbuilder); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "web", + }, +} + +sub needsbuild (@) { + my $needsbuild=shift; + + foreach my $page (keys %pagestate) { + if (exists $pagestate{$page}{edittemplate}) { + if (exists $pagesources{$page} && + grep { $_ eq $pagesources{$page} } @$needsbuild) { + # remove state, it will be re-added + # if the preprocessor directive is still + # there during the rebuild + delete $pagestate{$page}{edittemplate}; + } + } + } + + return $needsbuild; +} + +sub preprocess (@) { + my %params=@_; + + return "" if $params{page} ne $params{destpage}; + + if (! exists $params{template} || ! length($params{template})) { + error gettext("template not specified") + } + if (! exists $params{match} || ! length($params{match})) { + error gettext("match not specified") + } + + my $link=linkpage($params{template}); + add_depends($params{page}, $link, deptype("presence")); + my $bestlink=bestlink($params{page}, $link); + if (! length $bestlink) { + add_depends($params{page}, "templates/$link", deptype("presence")); + $link="/templates/".$link; + $bestlink=bestlink($params{page}, $link); + } + $pagestate{$params{page}}{edittemplate}{$params{match}}=$bestlink; + + return "" if ($params{silent} && IkiWiki::yesno($params{silent})) && + length $bestlink; + return sprintf(gettext("edittemplate %s registered for %s"), + htmllink($params{page}, $params{destpage}, $link), + $params{match}); +} + +sub formbuilder (@) { + my %params=@_; + my $form=$params{form}; + + return if $form->field("do") ne "create" || + (defined $form->field("editcontent") && length $form->field("editcontent")); + + my $page=$form->field("page"); + + # The tricky bit here is that $page is probably just the base + # page name, without any subdir, but the pagespec for a template + # probably does include the subdir (ie, "bugs/*"). We don't know + # what subdir the user will pick to put the page in. So, try them + # all, starting with the one that was made default. + my @page_locs=$page; + foreach my $field ($form->field) { + if ($field eq 'page') { + @page_locs=$field->def_value; + + # FormBuilder is on the bad crack. See #551499 + my @options=map { ref $_ ? @$_ : $_ } $field->options; + + push @page_locs, @options; + } + } + foreach my $p (@page_locs) { + foreach my $registering_page (keys %pagestate) { + if (exists $pagestate{$registering_page}{edittemplate}) { + foreach my $pagespec (sort keys %{$pagestate{$registering_page}{edittemplate}}) { + if (pagespec_match($p, $pagespec, location => $registering_page)) { + my $template=$pagestate{$registering_page}{edittemplate}{$pagespec}; + $form->field(name => "editcontent", + value => filltemplate($template, $page)); + my $type=pagetype($pagesources{$template}) + if $pagesources{$template}; + $form->field(name => "type", + value => $type) + if defined $type; + return; + } + } + } + } + } +} + +sub filltemplate ($$) { + my $template_page=shift; + my $page=shift; + + my $template; + eval { + # force page name absolute so it doesn't look in templates/ + $template=template("/".$template_page); + }; + if ($@) { + # Indicate that the earlier preprocessor directive set + # up a template that doesn't work. + return "[[!edittemplate ".gettext("failed to process template:")." $@]]"; + } + + $template->param(name => $page); + + return $template->output; +} + +1 diff --git a/IkiWiki/Plugin/embed.pm b/IkiWiki/Plugin/embed.pm new file mode 100644 index 000000000..a7d38358f --- /dev/null +++ b/IkiWiki/Plugin/embed.pm @@ -0,0 +1,77 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::embed; + +use warnings; +use strict; +use IkiWiki 3.00; + +my $attribr=qr/[^<>"]+/; + +# regexp matching known-safe html +my $safehtml=qr{( + # google maps + <\s*iframe\s+width="\d+"\s+height="\d+"\s+frameborder="$attribr"\s+ + scrolling="$attribr"\s+marginheight="\d+"\s+marginwidth="\d+"\s+ + src="http://maps.google.com/\?$attribr"\s*>\s* + + | + + # youtube + <\s*object\s+width="\d+"\s+height="\d+"\s*>\s* + <\s*param\s+name="movie"\s+value="http://www.youtube.com/v/$attribr"\s*>\s* + \s* + <\s*param\s+name="wmode"\s+value="transparent"\s*>\s*\s* + \s*\s* + + | + + # google video + <\s*embed\s+style="\s*width:\d+px;\s+height:\d+px;\s*"\s+id="$attribr"\s+ + type="application/x-shockwave-flash"\s+ + src="http://video.google.com/googleplayer.swf\?$attribr"\s+ + flashvars=""\s*>\s* + + | + + # google calendar + <\s*iframe\s+src="http://www.google.com/calendar/embed\?src=$attribr"\s+ + style="\s*border-width:\d+\s*"\s+width="\d+"\s+frameborder="\d+"\s* + height="\d+"\s*>\s* +)}sx; + +my @embedded; + +sub import { + hook(type => "getsetup", id => "embed", call => \&getsetup); + hook(type => "filter", id => "embed", call => \&filter); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +sub embed ($) { + hook(type => "format", id => "embed", call => \&format) unless @embedded; + push @embedded, shift; + return "
"; +} + +sub filter (@) { + my %params=@_; + $params{content} =~ s/$safehtml/embed($1)/eg; + return $params{content}; +} + +sub format (@) { + my %params=@_; + $params{content} =~ s/
<\/div>/$embedded[$1]/eg; + return $params{content}; +} + +1 diff --git a/IkiWiki/Plugin/external.pm b/IkiWiki/Plugin/external.pm new file mode 100644 index 000000000..a4cc1dd3c --- /dev/null +++ b/IkiWiki/Plugin/external.pm @@ -0,0 +1,255 @@ +#!/usr/bin/perl +# Support for external plugins written in other languages. +# Communication via XML RPC to a pipe. +# See externaldemo for an example of a plugin that uses this. +package IkiWiki::Plugin::external; + +use warnings; +use strict; +use IkiWiki 3.00; +use RPC::XML; +use IPC::Open2; +use IO::Handle; + +my %plugins; + +sub import { + my $self=shift; + my $plugin=shift; + return unless defined $plugin; + + my ($plugin_read, $plugin_write); + my $pid = open2($plugin_read, $plugin_write, + IkiWiki::possibly_foolish_untaint($plugin)); + + # open2 doesn't respect "use open ':utf8'" + binmode($plugin_read, ':utf8'); + binmode($plugin_write, ':utf8'); + + $plugins{$plugin}={in => $plugin_read, out => $plugin_write, pid => $pid, + accum => ""}; + + $RPC::XML::ENCODING="utf-8"; + $RPC::XML::FORCE_STRING_ENCODING="true"; + + rpc_call($plugins{$plugin}, "import"); +} + +sub rpc_write ($$) { + my $fh=shift; + my $string=shift; + + $fh->print($string."\n"); + $fh->flush; +} + +sub rpc_call ($$;@) { + my $plugin=shift; + my $command=shift; + + # send the command + my $req=RPC::XML::request->new($command, @_); + rpc_write($plugin->{out}, $req->as_string); + + # process incoming rpc until a result is available + while ($_ = $plugin->{in}->getline) { + $plugin->{accum}.=$_; + while ($plugin->{accum} =~ /^\s*(<\?xml\s.*?<\/(?:methodCall|methodResponse)>)\n(.*)/s) { + $plugin->{accum}=$2; + my $parser; + eval q{ + use RPC::XML::ParserFactory; + $parser = RPC::XML::ParserFactory->new; + }; + if ($@) { + # old interface + eval q{ + use RPC::XML::Parser; + $parser = RPC::XML::Parser->new; + }; + } + my $r=$parser->parse($1); + error("XML RPC parser failure: $r") unless ref $r; + if ($r->isa('RPC::XML::response')) { + my $value=$r->value; + if ($r->is_fault($value)) { + # throw the error as best we can + print STDERR $value->string."\n"; + return ""; + } + elsif ($value->isa('RPC::XML::array')) { + return @{$value->value}; + } + elsif ($value->isa('RPC::XML::struct')) { + my %hash=%{$value->value}; + + # XML-RPC v1 does not allow for + # nil/null/None/undef values to be + # transmitted. The extension + # is the right fix, but for + # back-compat, let external plugins send + # a hash with one key "null" pointing + # to an empty string. + if (exists $hash{null} && + $hash{null} eq "" && + int(keys(%hash)) == 1) { + return undef; + } + + return %hash; + } + else { + return $value->value; + } + } + + my $name=$r->name; + my @args=map { $_->value } @{$r->args}; + + # When dispatching a function, first look in + # IkiWiki::RPC::XML. This allows overriding + # IkiWiki functions with RPC friendly versions. + my $ret; + if (exists $IkiWiki::RPC::XML::{$name}) { + $ret=$IkiWiki::RPC::XML::{$name}($plugin, @args); + } + elsif (exists $IkiWiki::{$name}) { + $ret=$IkiWiki::{$name}(@args); + } + else { + error("XML RPC call error, unknown function: $name"); + } + + # XML-RPC v1 does not allow for nil/null/None/undef + # values to be transmitted, so until XML::RPC::Parser + # honours v2 (), send a hash with one key "null" + # pointing to an empty string. + if (! defined $ret) { + $ret={"null" => ""}; + } + + my $string=eval { RPC::XML::response->new($ret)->as_string }; + if ($@ && ref $ret) { + # One common reason for serialisation to + # fail is a complex return type that cannot + # be represented as an XML RPC response. + # Handle this case by just returning 1. + $string=eval { RPC::XML::response->new(1)->as_string }; + } + if ($@) { + error("XML response serialisation failed: $@"); + } + rpc_write($plugin->{out}, $string); + } + } + + return undef; +} + +package IkiWiki::RPC::XML; +use Memoize; + +sub getvar ($$$) { + my $plugin=shift; + my $varname="IkiWiki::".shift; + my $key=shift; + + no strict 'refs'; + my $ret=$varname->{$key}; + use strict 'refs'; + return $ret; +} + +sub setvar ($$$;@) { + my $plugin=shift; + my $varname="IkiWiki::".shift; + my $key=shift; + my $value=shift; + + no strict 'refs'; + my $ret=$varname->{$key}=$value; + use strict 'refs'; + return $ret; +} + +sub getstate ($$$$) { + my $plugin=shift; + my $page=shift; + my $id=shift; + my $key=shift; + + return $IkiWiki::pagestate{$page}{$id}{$key}; +} + +sub setstate ($$$$;@) { + my $plugin=shift; + my $page=shift; + my $id=shift; + my $key=shift; + my $value=shift; + + return $IkiWiki::pagestate{$page}{$id}{$key}=$value; +} + +sub getargv ($) { + my $plugin=shift; + + return \@ARGV; +} + +sub setargv ($@) { + my $plugin=shift; + my $array=shift; + + @ARGV=@$array; +} + +sub inject ($@) { + # Bind a given perl function name to a particular RPC request. + my $plugin=shift; + my %params=@_; + + if (! exists $params{name} || ! exists $params{call}) { + die "inject needs name and call parameters"; + } + my $sub = sub { + IkiWiki::Plugin::external::rpc_call($plugin, $params{call}, @_) + }; + $sub=memoize($sub) if $params{memoize}; + + # This will add it to the symbol table even if not present. + no warnings; + eval qq{*$params{name}=\$sub}; + use warnings; + + # This will ensure that everywhere it was exported to sees + # the injected version. + IkiWiki::inject(name => $params{name}, call => $sub); + return 1; +} + +sub hook ($@) { + # the call parameter is a function name to call, since XML RPC + # cannot pass a function reference + my $plugin=shift; + my %params=@_; + + my $callback=$params{call}; + delete $params{call}; + + IkiWiki::hook(%params, call => sub { + IkiWiki::Plugin::external::rpc_call($plugin, $callback, @_); + }); +} + +sub pagespec_match ($@) { + # convert return object into a XML RPC boolean + my $plugin=shift; + my $page=shift; + my $spec=shift; + + return RPC::XML::boolean->new(0 + IkiWiki::pagespec_match( + $page, $spec, @_)); +} + +1 diff --git a/IkiWiki/Plugin/favicon.pm b/IkiWiki/Plugin/favicon.pm new file mode 100644 index 000000000..6060914c5 --- /dev/null +++ b/IkiWiki/Plugin/favicon.pm @@ -0,0 +1,33 @@ +#!/usr/bin/perl +# favicon plugin. + +package IkiWiki::Plugin::favicon; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "favicon", call => \&getsetup); + hook(type => "pagetemplate", id => "favicon", call => \&pagetemplate); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + }, +} + +sub pagetemplate (@) { + my %params=@_; + + my $template=$params{template}; + + if ($template->query(name => "favicon")) { + $template->param(favicon => "favicon.ico"); + } +} + +1 diff --git a/IkiWiki/Plugin/filecheck.pm b/IkiWiki/Plugin/filecheck.pm new file mode 100644 index 000000000..cdea5c706 --- /dev/null +++ b/IkiWiki/Plugin/filecheck.pm @@ -0,0 +1,222 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::filecheck; + +use warnings; +use strict; +use IkiWiki 3.00; + +my %units=( # size in bytes + B => 1, + byte => 1, + KB => 2 ** 10, + kilobyte => 2 ** 10, + K => 2 ** 10, + KB => 2 ** 10, + kilobyte => 2 ** 10, + M => 2 ** 20, + MB => 2 ** 20, + megabyte => 2 ** 20, + G => 2 ** 30, + GB => 2 ** 30, + gigabyte => 2 ** 30, + T => 2 ** 40, + TB => 2 ** 40, + terabyte => 2 ** 40, + P => 2 ** 50, + PB => 2 ** 50, + petabyte => 2 ** 50, + E => 2 ** 60, + EB => 2 ** 60, + exabyte => 2 ** 60, + Z => 2 ** 70, + ZB => 2 ** 70, + zettabyte => 2 ** 70, + Y => 2 ** 80, + YB => 2 ** 80, + yottabyte => 2 ** 80, + # ikiwiki, if you find you need larger data quantities, either modify + # yourself to add them, or travel back in time to 2008 and kill me. + # -- Joey +); + +sub import { + hook(type => "getsetup", id => "filecheck", call => \&getsetup); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +sub parsesize ($) { + my $size=shift; + + no warnings; + my $base=$size+0; # force to number + use warnings; + foreach my $unit (sort keys %units) { + if ($size=~/[0-9\s]\Q$unit\E$/i) { + return $base * $units{$unit}; + } + } + return $base; +} + +# This is provided for other plugins that want to convert back the other way. +sub humansize ($) { + my $size=shift; + + foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) { + if ($size / $units{$unit} > 0.25) { + return (int($size / $units{$unit} * 10)/10).$unit; + } + } + return $size; # near zero, or negative +} + +package IkiWiki::PageSpec; + +sub match_maxsize ($$;@) { + my $page=shift; + my $maxsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)}; + if ($@) { + return IkiWiki::ErrorReason->new("unable to parse maxsize (or number too large)"); + } + + my %params=@_; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); + if (! defined $file) { + return IkiWiki::ErrorReason->new("file does not exist"); + } + + if (-s $file > $maxsize) { + return IkiWiki::FailReason->new("file too large (".(-s $file)." > $maxsize)"); + } + else { + return IkiWiki::SuccessReason->new("file not too large"); + } +} + +sub match_minsize ($$;@) { + my $page=shift; + my $minsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)}; + if ($@) { + return IkiWiki::ErrorReason->new("unable to parse minsize (or number too large)"); + } + + my %params=@_; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); + if (! defined $file) { + return IkiWiki::ErrorReason->new("file does not exist"); + } + + if (-s $file < $minsize) { + return IkiWiki::FailReason->new("file too small"); + } + else { + return IkiWiki::SuccessReason->new("file not too small"); + } +} + +sub match_mimetype ($$;@) { + my $page=shift; + my $wanted=shift; + + my %params=@_; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); + if (! defined $file) { + return IkiWiki::ErrorReason->new("file does not exist"); + } + + # Get the mime type. + # + # First, try File::Mimeinfo. This is fast, but doesn't recognise + # all files. + eval q{use File::MimeInfo::Magic}; + my $mimeinfo_ok=! $@; + my $mimetype; + if ($mimeinfo_ok) { + $mimetype=File::MimeInfo::Magic::magic($file); + } + + # Fall back to using file, which has a more complete + # magic database. + if (! defined $mimetype) { + open(my $file_h, "-|", "file", "-bi", $file); + $mimetype=<$file_h>; + chomp $mimetype; + close $file_h; + } + if (! defined $mimetype || $mimetype !~s /;.*//) { + # Fall back to default value. + $mimetype=File::MimeInfo::Magic::default($file) + if $mimeinfo_ok; + if (! defined $mimetype) { + $mimetype="unknown"; + } + } + + my $regexp=IkiWiki::glob2re($wanted); + if ($mimetype!~$regexp) { + return IkiWiki::FailReason->new("file MIME type is $mimetype, not $wanted"); + } + else { + return IkiWiki::SuccessReason->new("file MIME type is $mimetype"); + } +} + +sub match_virusfree ($$;@) { + my $page=shift; + my $wanted=shift; + + my %params=@_; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); + if (! defined $file) { + return IkiWiki::ErrorReason->new("file does not exist"); + } + + if (! exists $IkiWiki::config{virus_checker} || + ! length $IkiWiki::config{virus_checker}) { + return IkiWiki::ErrorReason->new("no virus_checker configured"); + } + + # The file needs to be fed into the virus checker on stdin, + # because the file is not world-readable, and if clamdscan is + # used, clamd would fail to read it. + eval q{use IPC::Open2}; + error($@) if $@; + open (IN, "<", $file) || return IkiWiki::ErrorReason->new("failed to read file"); + binmode(IN); + my $sigpipe=0; + $SIG{PIPE} = sub { $sigpipe=1 }; + my $pid=open2(\*CHECKER_OUT, "<&IN", $IkiWiki::config{virus_checker}); + my $reason=; + chomp $reason; + 1 while (); + close(CHECKER_OUT); + waitpid $pid, 0; + $SIG{PIPE}="DEFAULT"; + if ($sigpipe || $?) { + if (! length $reason) { + $reason="virus checker $IkiWiki::config{virus_checker}; failed with no output"; + } + return IkiWiki::FailReason->new("file seems to contain a virus ($reason)"); + } + else { + return IkiWiki::SuccessReason->new("file seems virusfree ($reason)"); + } +} + +sub match_ispage ($$;@) { + my $filename=shift; + + if (defined IkiWiki::pagetype($filename)) { + return IkiWiki::SuccessReason->new("file is a wiki page"); + } + else { + return IkiWiki::FailReason->new("file is not a wiki page"); + } +} diff --git a/IkiWiki/Plugin/flattr.pm b/IkiWiki/Plugin/flattr.pm new file mode 100644 index 000000000..3aee1eb93 --- /dev/null +++ b/IkiWiki/Plugin/flattr.pm @@ -0,0 +1,97 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::flattr; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "flattr", call => \&getsetup); + hook(type => "preprocess", id => "flattr", call => \&preprocess); + hook(type => "format", id => "flattr", call => \&format); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, + flattr_userid => { + type => "string", + example => 'joeyh', + description => "userid or user name to use by default for Flattr buttons", + advanced => 0, + safe => 1, + rebuild => undef, + }, +} + +my %flattr_pages; + +sub preprocess (@) { + my %params=@_; + + $flattr_pages{$params{destpage}}=1; + + my $url=$params{url}; + if (! defined $url) { + $url=urlto($params{page}, "", 1); + } + + my @fields; + foreach my $field (qw{language uid button hidden category tags}) { + if (exists $params{$field}) { + push @fields, "$field:$params{$field}"; + } + } + + return ''. + (exists $params{description} ? $params{description} : ''). + ''; +} + +sub format (@) { + my %params=@_; + + # Add flattr's javascript to pages with flattr buttons. + if ($flattr_pages{$params{page}}) { + if (! ($params{content}=~s!^(]*>)!$1.flattrjs()!em)) { + # no tag, probably in preview mode + $params{content}=flattrjs().$params{content}; + } + } + return $params{content}; +} + +my $js_cached; +sub flattrjs { + return $js_cached if defined $js_cached; + + my $js_url='https://api.flattr.com/js/0.5.0/load.js?mode=auto'; + if (defined $config{flattr_userid}) { + my $userid=$config{flattr_userid}; + $userid=~s/[^-A-Za-z0-9_]//g; # sanitize for inclusion in javascript + $js_url.="&uid=$userid"; + } + + # This is Flattr's standard javascript snippet to include their + # external javascript file, asynchronously. + return $js_cached=<<"EOF"; + +EOF +} + +1 diff --git a/IkiWiki/Plugin/format.pm b/IkiWiki/Plugin/format.pm new file mode 100644 index 000000000..b596bc0a1 --- /dev/null +++ b/IkiWiki/Plugin/format.pm @@ -0,0 +1,54 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::format; + +use warnings; +use strict; +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 (@) { + my %params=@_; + my $format=shift; + shift; + my $text=IkiWiki::preprocess($params{page}, $params{destpage}, shift); + shift; + + if (! defined $format || ! defined $text) { + error(gettext("must specify format and text")); + } + + # Other plugins can register htmlizeformat hooks to add support + # for page types not suitable for htmlize, or that need special + # processing when included via format. Try them until one succeeds. + my $ret; + IkiWiki::run_hooks(htmlizeformat => sub { + $ret=shift->($format, $text) + unless defined $ret; + }); + + if (defined $ret) { + return $ret; + } + elsif (exists $IkiWiki::hooks{htmlize}{$format}) { + return IkiWiki::htmlize($params{page}, $params{destpage}, + $format, $text); + } + else { + error(sprintf(gettext("unsupported page format %s"), $format)); + } +} + +1 diff --git a/IkiWiki/Plugin/fortune.pm b/IkiWiki/Plugin/fortune.pm new file mode 100644 index 000000000..f481c7eac --- /dev/null +++ b/IkiWiki/Plugin/fortune.pm @@ -0,0 +1,35 @@ +#!/usr/bin/perl +# Include a fortune in a page +package IkiWiki::Plugin::fortune; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "fortune", call => \&getsetup); + hook(type => "preprocess", id => "fortune", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess (@) { + $ENV{PATH}="$ENV{PATH}:/usr/games:/usr/local/games"; + my $f = `fortune 2>/dev/null`; + + if ($?) { + error gettext("fortune failed"); + } + else { + return "
$f
\n"; + } +} + +1 diff --git a/IkiWiki/Plugin/getsource.pm b/IkiWiki/Plugin/getsource.pm new file mode 100644 index 000000000..0a21413bd --- /dev/null +++ b/IkiWiki/Plugin/getsource.pm @@ -0,0 +1,94 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::getsource; + +use warnings; +use strict; +use IkiWiki; +use open qw{:utf8 :std}; + +sub import { + hook(type => "getsetup", id => "getsource", call => \&getsetup); + hook(type => "pagetemplate", id => "getsource", call => \&pagetemplate); + hook(type => "cgi", id => "getsource", call => \&cgi_getsource); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + section => "web", + }, + getsource_mimetype => { + type => "string", + example => "text/plain; charset=utf-8", + description => "Mime type for returned source.", + safe => 1, + rebuild => 0, + }, +} + +sub pagetemplate (@) { + my %params=@_; + + my $page=$params{page}; + my $template=$params{template}; + + if (length $config{cgiurl}) { + $template->param(getsourceurl => IkiWiki::cgiurl(do => "getsource", page => $page)); + $template->param(have_actions => 1); + } +} + +sub cgi_getsource ($) { + my $cgi=shift; + + return unless defined $cgi->param('do') && + $cgi->param("do") eq "getsource"; + + IkiWiki::decode_cgi_utf8($cgi); + + my $page=$cgi->param('page'); + + if (! defined $page || $page !~ /$config{wiki_file_regexp}/) { + error("invalid page parameter"); + } + + # For %pagesources. + IkiWiki::loadindex(); + + if (! exists $pagesources{$page}) { + IkiWiki::cgi_custom_failure( + $cgi, + "404 Not Found", + IkiWiki::cgitemplate($cgi, gettext("missing page"), + "

". + sprintf(gettext("The page %s does not exist."), + htmllink("", "", $page)). + "

")); + exit; + } + + if (! defined pagetype($pagesources{$page})) { + IkiWiki::cgi_custom_failure( + $cgi->header(-status => "403 Forbidden"), + IkiWiki::cgitemplate($cgi, gettext("not a page"), + "

". + sprintf(gettext("%s is an attachment, not a page."), + htmllink("", "", $page)). + "

")); + exit; + } + + if (! $config{getsource_mimetype}) { + $config{getsource_mimetype} = "text/plain; charset=utf-8"; + } + + print "Content-Type: $config{getsource_mimetype}\r\n"; + print ("\r\n"); + print readfile(srcfile($pagesources{$page})); + + exit 0; +} + +1 diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm new file mode 100644 index 000000000..bbdcbdba8 --- /dev/null +++ b/IkiWiki/Plugin/git.pm @@ -0,0 +1,906 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::git; + +use warnings; +use strict; +use IkiWiki; +use Encode; +use URI::Escape q{uri_escape_utf8}; +use open qw{:utf8 :std}; + +my $sha1_pattern = qr/[0-9a-fA-F]{40}/; # pattern to validate Git sha1sums +my $dummy_commit_msg = 'dummy commit'; # message to skip in recent changes + +sub import { + hook(type => "checkconfig", id => "git", call => \&checkconfig); + hook(type => "getsetup", id => "git", call => \&getsetup); + hook(type => "genwrapper", id => "git", call => \&genwrapper); + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); + hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); + hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); + hook(type => "rcs", id => "rcs_commit_staged", call => \&rcs_commit_staged); + hook(type => "rcs", id => "rcs_add", call => \&rcs_add); + hook(type => "rcs", id => "rcs_remove", call => \&rcs_remove); + hook(type => "rcs", id => "rcs_rename", call => \&rcs_rename); + 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); + hook(type => "rcs", id => "rcs_preprevert", call => \&rcs_preprevert); + hook(type => "rcs", id => "rcs_revert", call => \&rcs_revert); +} + +sub checkconfig () { + if (! defined $config{gitorigin_branch}) { + $config{gitorigin_branch}="origin"; + } + if (! defined $config{gitmaster_branch}) { + $config{gitmaster_branch}="master"; + } + if (defined $config{git_wrapper} && + length $config{git_wrapper}) { + push @{$config{wrappers}}, { + wrapper => $config{git_wrapper}, + wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"), + wrapper_background_command => $config{git_wrapper_background_command}, + }; + } + + if (defined $config{git_test_receive_wrapper} && + length $config{git_test_receive_wrapper} && + defined $config{untrusted_committers} && + @{$config{untrusted_committers}}) { + push @{$config{wrappers}}, { + test_receive => 1, + wrapper => $config{git_test_receive_wrapper}, + wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"), + }; + } + + # Avoid notes, parser does not handle and they only slow things down. + $ENV{GIT_NOTES_REF}=""; + + # Run receive test only if being called by the wrapper, and not + # when generating same. + if ($config{test_receive} && ! exists $config{wrapper}) { + require IkiWiki::Receive; + IkiWiki::Receive::test(); + } +} + +sub getsetup () { + return + plugin => { + safe => 0, # rcs plugin + rebuild => undef, + section => "rcs", + }, + git_wrapper => { + type => "string", + example => "/git/wiki.git/hooks/post-update", + description => "git hook to generate", + safe => 0, # file + rebuild => 0, + }, + git_wrapper_background_command => { + type => "string", + example => "git push github", + description => "shell command for git_wrapper to run, in the background", + safe => 0, # command + rebuild => 0, + }, + git_wrappermode => { + type => "string", + example => '06755', + description => "mode for git_wrapper (can safely be made suid)", + safe => 0, + rebuild => 0, + }, + git_test_receive_wrapper => { + type => "string", + example => "/git/wiki.git/hooks/pre-receive", + description => "git pre-receive hook to generate", + safe => 0, # file + rebuild => 0, + }, + untrusted_committers => { + type => "string", + example => [], + description => "unix users whose commits should be checked by the pre-receive hook", + safe => 0, + rebuild => 0, + }, + historyurl => { + type => "string", + example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]];hb=HEAD", + description => "gitweb url to show file history ([[file]] substituted)", + safe => 1, + rebuild => 1, + }, + diffurl => { + type => "string", + example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=blobdiff;f=[[file]];h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_commit]];hpb=[[sha1_parent]]", + description => "gitweb url to show a diff ([[file]], [[sha1_to]], [[sha1_from]], [[sha1_commit]], and [[sha1_parent]] substituted)", + safe => 1, + rebuild => 1, + }, + gitorigin_branch => { + type => "string", + example => "origin", + description => "where to pull and push changes (set to empty string to disable)", + safe => 0, # paranoia + rebuild => 0, + }, + gitmaster_branch => { + type => "string", + example => "master", + description => "branch that the wiki is stored in", + safe => 0, # paranoia + rebuild => 0, + }, +} + +sub genwrapper { + if ($config{test_receive}) { + require IkiWiki::Receive; + return IkiWiki::Receive::genwrapper(); + } + else { + return ""; + } +} + +my $git_dir=undef; +my $prefix=undef; + +sub in_git_dir ($$) { + $git_dir=shift; + my @ret=shift->(); + $git_dir=undef; + $prefix=undef; + return @ret; +} + +sub safe_git (&@) { + # Start a child process safely without resorting to /bin/sh. + # Returns command output (in list content) or success state + # (in scalar context), or runs the specified data handler. + + my ($error_handler, $data_handler, @cmdline) = @_; + + my $pid = open my $OUT, "-|"; + + error("Cannot fork: $!") if !defined $pid; + + if (!$pid) { + # In child. + # Git commands want to be in wc. + if (! defined $git_dir) { + chdir $config{srcdir} + or error("cannot chdir to $config{srcdir}: $!"); + } + else { + chdir $git_dir + or error("cannot chdir to $git_dir: $!"); + } + exec @cmdline or error("Cannot exec '@cmdline': $!"); + } + # In parent. + + # git output is probably utf-8 encoded, but may contain + # other encodings or invalidly encoded stuff. So do not rely + # on the normal utf-8 IO layer, decode it by hand. + binmode($OUT); + + my @lines; + while (<$OUT>) { + $_=decode_utf8($_, 0); + + chomp; + + if (! defined $data_handler) { + push @lines, $_; + } + else { + last unless $data_handler->($_); + } + } + + close $OUT; + + $error_handler->("'@cmdline' failed: $!") if $? && $error_handler; + + return wantarray ? @lines : ($? == 0); +} +# Convenient wrappers. +sub run_or_die ($@) { safe_git(\&error, undef, @_) } +sub run_or_cry ($@) { safe_git(sub { warn @_ }, undef, @_) } +sub run_or_non ($@) { safe_git(undef, undef, @_) } + + +sub merge_past ($$$) { + # Unlike with Subversion, Git cannot make a 'svn merge -rN:M file'. + # Git merge commands work with the committed changes, except in the + # implicit case of '-m' of git checkout(1). So we should invent a + # kludge here. In principle, we need to create a throw-away branch + # in preparing for the merge itself. Since branches are cheap (and + # branching is fast), this shouldn't cost high. + # + # The main problem is the presence of _uncommitted_ local changes. One + # possible approach to get rid of this situation could be that we first + # make a temporary commit in the master branch and later restore the + # initial state (this is possible since Git has the ability to undo a + # commit, i.e. 'git reset --soft HEAD^'). The method can be summarized + # as follows: + # + # - create a diff of HEAD:current-sha1 + # - dummy commit + # - create a dummy branch and switch to it + # - rewind to past (reset --hard to the current-sha1) + # - apply the diff and commit + # - switch to master and do the merge with the dummy branch + # - make a soft reset (undo the last commit of master) + # + # The above method has some drawbacks: (1) it needs a redundant commit + # just to get rid of local changes, (2) somewhat slow because of the + # required system forks. Until someone points a more straight method + # (which I would be grateful) I have implemented an alternative method. + # In this approach, we hide all the modified files from Git by renaming + # them (using the 'rename' builtin) and later restore those files in + # the throw-away branch (that is, we put the files themselves instead + # of applying a patch). + + my ($sha1, $file, $message) = @_; + + my @undo; # undo stack for cleanup in case of an error + my $conflict; # file content with conflict markers + + eval { + # Hide local changes from Git by renaming the modified file. + # Relative paths must be converted to absolute for renaming. + my ($target, $hidden) = ( + "$config{srcdir}/${file}", "$config{srcdir}/${file}.${sha1}" + ); + rename($target, $hidden) + or error("rename '$target' to '$hidden' failed: $!"); + # Ensure to restore the renamed file on error. + push @undo, sub { + return if ! -e "$hidden"; # already renamed + rename($hidden, $target) + or warn "rename '$hidden' to '$target' failed: $!"; + }; + + my $branch = "throw_away_${sha1}"; # supposed to be unique + + # Create a throw-away branch and rewind backward. + push @undo, sub { run_or_cry('git', 'branch', '-D', $branch) }; + run_or_die('git', 'branch', $branch, $sha1); + + # Switch to throw-away branch for the merge operation. + push @undo, sub { + if (!run_or_cry('git', 'checkout', $config{gitmaster_branch})) { + run_or_cry('git', 'checkout','-f',$config{gitmaster_branch}); + } + }; + run_or_die('git', 'checkout', $branch); + + # Put the modified file in _this_ branch. + rename($hidden, $target) + or error("rename '$hidden' to '$target' failed: $!"); + + # _Silently_ commit all modifications in the current branch. + run_or_non('git', 'commit', '-m', $message, '-a'); + # ... and re-switch to master. + run_or_die('git', 'checkout', $config{gitmaster_branch}); + + # Attempt to merge without complaining. + if (!run_or_non('git', 'pull', '--no-commit', '.', $branch)) { + $conflict = readfile($target); + run_or_die('git', 'reset', '--hard'); + } + }; + my $failure = $@; + + # Process undo stack (in reverse order). By policy cleanup + # actions should normally print a warning on failure. + while (my $handle = pop @undo) { + $handle->(); + } + + error("Git merge failed!\n$failure\n") if $failure; + + return $conflict; +} + +sub decode_git_file ($) { + my $file=shift; + + # git does not output utf-8 filenames, but instead + # double-quotes them with the utf-8 characters + # escaped as \nnn\nnn. + if ($file =~ m/^"(.*)"$/) { + ($file=$1) =~ s/\\([0-7]{1,3})/chr(oct($1))/eg; + } + + # strip prefix if in a subdir + if (! defined $prefix) { + ($prefix) = run_or_die('git', 'rev-parse', '--show-prefix'); + if (! defined $prefix) { + $prefix=""; + } + } + $file =~ s/^\Q$prefix\E//; + + return decode("utf8", $file); +} + +sub parse_diff_tree ($) { + # Parse the raw diff tree chunk and return the info hash. + # See git-diff-tree(1) for the syntax. + my $dt_ref = shift; + + # End of stream? + return if ! @{ $dt_ref } || + !defined $dt_ref->[0] || !length $dt_ref->[0]; + + my %ci; + # Header line. + while (my $line = shift @{ $dt_ref }) { + return if $line !~ m/^(.+) ($sha1_pattern)/; + + my $sha1 = $2; + $ci{'sha1'} = $sha1; + last; + } + + # Identification lines for the commit. + while (my $line = shift @{ $dt_ref }) { + # Regexps are semi-stolen from gitweb.cgi. + if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) { + $ci{'tree'} = $1; + } + elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) { + # XXX: collecting in reverse order + push @{ $ci{'parents'} }, $1; + } + elsif ($line =~ m/^(author|committer) (.*) ([0-9]+) (.*)$/) { + my ($who, $name, $epoch, $tz) = + ($1, $2, $3, $4 ); + + $ci{ $who } = $name; + $ci{ "${who}_epoch" } = $epoch; + $ci{ "${who}_tz" } = $tz; + + if ($name =~ m/^([^<]+)\s+<([^@>]+)/) { + $ci{"${who}_name"} = $1; + $ci{"${who}_username"} = $2; + } + elsif ($name =~ m/^([^<]+)\s+<>$/) { + $ci{"${who}_username"} = $1; + } + else { + $ci{"${who}_username"} = $name; + } + } + elsif ($line =~ m/^$/) { + # Trailing empty line signals next section. + last; + } + } + + debug("No 'tree' seen in diff-tree output") if !defined $ci{'tree'}; + + if (defined $ci{'parents'}) { + $ci{'parent'} = @{ $ci{'parents'} }[0]; + } + else { + $ci{'parent'} = 0 x 40; + } + + # Commit message (optional). + while ($dt_ref->[0] =~ /^ /) { + my $line = shift @{ $dt_ref }; + $line =~ s/^ //; + push @{ $ci{'comment'} }, $line; + } + shift @{ $dt_ref } if $dt_ref->[0] =~ /^$/; + + # Modified files. + while (my $line = shift @{ $dt_ref }) { + if ($line =~ m{^ + (:+) # number of parents + ([^\t]+)\t # modes, sha1, status + (.*) # file names + $}xo) { + my $num_parents = length $1; + my @tmp = split(" ", $2); + my ($file, $file_to) = split("\t", $3); + my @mode_from = splice(@tmp, 0, $num_parents); + my $mode_to = shift(@tmp); + my @sha1_from = splice(@tmp, 0, $num_parents); + my $sha1_to = shift(@tmp); + my $status = shift(@tmp); + + if (length $file) { + push @{ $ci{'details'} }, { + 'file' => decode_git_file($file), + 'sha1_from' => $sha1_from[0], + 'sha1_to' => $sha1_to, + 'mode_from' => $mode_from[0], + 'mode_to' => $mode_to, + 'status' => $status, + }; + } + next; + }; + last; + } + + return \%ci; +} + +sub git_commit_info ($;$) { + # Return an array of commit info hashes of num commits + # starting from the given sha1sum. + my ($sha1, $num) = @_; + + my @opts; + push @opts, "--max-count=$num" if defined $num; + + my @raw_lines = run_or_die('git', 'log', @opts, + '--pretty=raw', '--raw', '--abbrev=40', '--always', '-c', + '-r', $sha1, '--', '.'); + + my @ci; + while (my $parsed = parse_diff_tree(\@raw_lines)) { + push @ci, $parsed; + } + + warn "Cannot parse commit info for '$sha1' commit" if !@ci; + + return wantarray ? @ci : $ci[0]; +} + +sub git_sha1 (;$) { + # Return head sha1sum (of given file). + my $file = shift || q{--}; + + # Ignore error since a non-existing file might be given. + my ($sha1) = run_or_non('git', 'rev-list', '--max-count=1', 'HEAD', + '--', $file); + if (defined $sha1) { + ($sha1) = $sha1 =~ m/($sha1_pattern)/; # sha1 is untainted now + } + return defined $sha1 ? $sha1 : ''; +} + +sub rcs_update () { + # Update working directory. + + if (length $config{gitorigin_branch}) { + run_or_cry('git', 'pull', '--prune', $config{gitorigin_branch}); + } +} + +sub rcs_prepedit ($) { + # Return the commit sha1sum of the file when editing begins. + # This will be later used in rcs_commit if a merge is required. + my ($file) = @_; + + return git_sha1($file); +} + +sub rcs_commit (@) { + # Try to commit the page; returns undef on _success_ and + # a version of the page with the rcs's conflict markers on + # failure. + my %params=@_; + + # Check to see if the page has been changed by someone else since + # rcs_prepedit was called. + my $cur = git_sha1($params{file}); + my ($prev) = $params{token} =~ /^($sha1_pattern)$/; # untaint + + if (defined $cur && defined $prev && $cur ne $prev) { + my $conflict = merge_past($prev, $params{file}, $dummy_commit_msg); + return $conflict if defined $conflict; + } + + return rcs_commit_helper(@_); +} + +sub rcs_commit_staged (@) { + # Commits all staged changes. Changes can be staged using rcs_add, + # rcs_remove, and rcs_rename. + return rcs_commit_helper(@_); +} + +sub rcs_commit_helper (@) { + my %params=@_; + + my %env=%ENV; + + if (defined $params{session}) { + # Set the commit author and email based on web session info. + my $u; + if (defined $params{session}->param("name")) { + $u=$params{session}->param("name"); + } + elsif (defined $params{session}->remote_addr()) { + $u=$params{session}->remote_addr(); + } + if (defined $u) { + $u=encode_utf8($u); + $ENV{GIT_AUTHOR_NAME}=$u; + } + if (defined $params{session}->param("nickname")) { + $u=encode_utf8($params{session}->param("nickname")); + $u=~s/\s+/_/g; + $u=~s/[^-_0-9[:alnum:]]+//g; + } + if (defined $u) { + $ENV{GIT_AUTHOR_EMAIL}="$u\@web"; + } + } + + $params{message} = IkiWiki::possibly_foolish_untaint($params{message}); + my @opts; + if ($params{message} !~ /\S/) { + # Force git to allow empty commit messages. + # (If this version of git supports it.) + my ($version)=`git --version` =~ /git version (.*)/; + if ($version ge "1.7.8") { + push @opts, "--allow-empty-message", "--no-edit"; + } + if ($version ge "1.7.2") { + push @opts, "--allow-empty-message"; + } + elsif ($version ge "1.5.4") { + push @opts, '--cleanup=verbatim'; + } + else { + $params{message}.="."; + } + } + if (exists $params{file}) { + push @opts, '--', $params{file}; + } + # git commit returns non-zero if nothing really changed. + # So we should ignore its exit status (hence run_or_non). + if (run_or_non('git', 'commit', '-m', $params{message}, '-q', @opts)) { + if (length $config{gitorigin_branch}) { + run_or_cry('git', 'push', $config{gitorigin_branch}); + } + } + + %ENV=%env; + return undef; # success +} + +sub rcs_add ($) { + # Add file to archive. + + my ($file) = @_; + + run_or_cry('git', 'add', $file); +} + +sub rcs_remove ($) { + # Remove file from archive. + + my ($file) = @_; + + run_or_cry('git', 'rm', '-f', $file); +} + +sub rcs_rename ($$) { + my ($src, $dest) = @_; + + run_or_cry('git', 'mv', '-f', $src, $dest); +} + +sub rcs_recentchanges ($) { + # List of recent changes. + + my ($num) = @_; + + eval q{use Date::Parse}; + error($@) if $@; + + my @rets; + foreach my $ci (git_commit_info('HEAD', $num || 1)) { + # Skip redundant commits. + next if ($ci->{'comment'} && @{$ci->{'comment'}}[0] eq $dummy_commit_msg); + + my ($sha1, $when) = ( + $ci->{'sha1'}, + $ci->{'author_epoch'} + ); + + my @pages; + foreach my $detail (@{ $ci->{'details'} }) { + my $file = $detail->{'file'}; + my $efile = uri_escape_utf8($file); + + my $diffurl = defined $config{'diffurl'} ? $config{'diffurl'} : ""; + $diffurl =~ s/\[\[file\]\]/$efile/go; + $diffurl =~ s/\[\[sha1_parent\]\]/$ci->{'parent'}/go; + $diffurl =~ s/\[\[sha1_from\]\]/$detail->{'sha1_from'}/go; + $diffurl =~ s/\[\[sha1_to\]\]/$detail->{'sha1_to'}/go; + $diffurl =~ s/\[\[sha1_commit\]\]/$sha1/go; + + push @pages, { + page => pagename($file), + diffurl => $diffurl, + }; + } + + my @messages; + my $pastblank=0; + foreach my $line (@{$ci->{'comment'}}) { + $pastblank=1 if $line eq ''; + next if $pastblank && $line=~m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i; + push @messages, { line => $line }; + } + + my $user=$ci->{'author_username'}; + my $web_commit = ($ci->{'author'} =~ /\@web>/); + my $nickname; + + # Set nickname only if a non-url author_username is available, + # and author_name is an url. + if ($user !~ /:\/\// && defined $ci->{'author_name'} && + $ci->{'author_name'} =~ /:\/\//) { + $nickname=$user; + $user=$ci->{'author_name'}; + } + + # compatability code for old web commit messages + if (! $web_commit && + defined $messages[0] && + $messages[0]->{line} =~ m/$config{web_commit_regexp}/) { + $user = defined $2 ? "$2" : "$3"; + $messages[0]->{line} = $4; + $web_commit=1; + } + + push @rets, { + rev => $sha1, + user => $user, + nickname => $nickname, + committype => $web_commit ? "web" : "git", + when => $when, + message => [@messages], + pages => [@pages], + } if @pages; + + last if @rets >= $num; + } + + return @rets; +} + +sub rcs_diff ($;$) { + my $rev=shift; + my $maxlines=shift; + my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint + my @lines; + my $addlines=sub { + my $line=shift; + return if defined $maxlines && @lines == $maxlines; + push @lines, $line."\n" + if (@lines || $line=~/^diff --git/); + return 1; + }; + safe_git(undef, $addlines, "git", "show", $sha1); + if (wantarray) { + return @lines; + } + else { + return join("", @lines); + } +} + +{ +my %time_cache; + +sub findtimes ($$) { + my $file=shift; + my $id=shift; # 0 = mtime ; 1 = ctime + + if (! keys %time_cache) { + my $date; + foreach my $line (run_or_die('git', 'log', + '--pretty=format:%at', + '--name-only', '--relative')) { + if (! defined $date && $line =~ /^(\d+)$/) { + $date=$line; + } + elsif (! length $line) { + $date=undef; + } + else { + my $f=decode_git_file($line); + + if (! $time_cache{$f}) { + $time_cache{$f}[0]=$date; # mtime + } + $time_cache{$f}[1]=$date; # 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); +} + +{ +my $ret; +sub git_find_root { + # The wiki may not be the only thing in the git repo. + # Determine if it is in a subdirectory by examining the srcdir, + # and its parents, looking for the .git directory. + + return @$ret if defined $ret; + + my $subdir=""; + my $dir=$config{srcdir}; + while (! -d "$dir/.git") { + $subdir=IkiWiki::basename($dir)."/".$subdir; + $dir=IkiWiki::dirname($dir); + if (! length $dir) { + error("cannot determine root of git repo"); + } + } + + $ret=[$subdir, $dir]; + return @$ret; +} + +} + +sub git_parse_changes { + my $reverted = shift; + my @changes = @_; + + my ($subdir, $rootdir) = git_find_root(); + my @rets; + foreach my $ci (@changes) { + foreach my $detail (@{ $ci->{'details'} }) { + my $file = $detail->{'file'}; + + # check that all changed files are in the subdir + if (length $subdir && + ! ($file =~ s/^\Q$subdir\E//)) { + error sprintf(gettext("you are not allowed to change %s"), $file); + } + + my ($action, $mode, $path); + if ($detail->{'status'} =~ /^[M]+\d*$/) { + $action="change"; + $mode=$detail->{'mode_to'}; + } + elsif ($detail->{'status'} =~ /^[AM]+\d*$/) { + $action= $reverted ? "remove" : "add"; + $mode=$detail->{'mode_to'}; + } + elsif ($detail->{'status'} =~ /^[DAM]+\d*/) { + $action= $reverted ? "add" : "remove"; + $mode=$detail->{'mode_from'}; + } + else { + error "unknown status ".$detail->{'status'}; + } + + # test that the file mode is ok + if ($mode !~ /^100[64][64][64]$/) { + error sprintf(gettext("you cannot act on a file with mode %s"), $mode); + } + if ($action eq "change") { + if ($detail->{'mode_from'} ne $detail->{'mode_to'}) { + error gettext("you are not allowed to change file modes"); + } + } + + # extract attachment to temp file + if (($action eq 'add' || $action eq 'change') && + ! pagetype($file)) { + eval q{use File::Temp}; + die $@ if $@; + my $fh; + ($fh, $path)=File::Temp::tempfile(undef, UNLINK => 1); + my $cmd = "cd $git_dir && ". + "git show $detail->{sha1_to} > '$path'"; + if (system($cmd) != 0) { + error("failed writing temp file '$path'."); + } + } + + push @rets, { + file => $file, + action => $action, + path => $path, + }; + } + } + + return @rets; +} + +sub rcs_receive () { + my @rets; + while (<>) { + chomp; + my ($oldrev, $newrev, $refname) = split(' ', $_, 3); + + # only allow changes to gitmaster_branch + if ($refname !~ /^refs\/heads\/\Q$config{gitmaster_branch}\E$/) { + error sprintf(gettext("you are not allowed to change %s"), $refname); + } + + # Avoid chdir when running git here, because the changes + # are in the master git repo, not the srcdir repo. + # (Also, if a subdir is involved, we don't want to chdir to + # it and only see changes in it.) + # The pre-receive hook already puts us in the right place. + in_git_dir(".", sub { + push @rets, git_parse_changes(0, git_commit_info($oldrev."..".$newrev)); + }); + } + + return reverse @rets; +} + +sub rcs_preprevert ($) { + my $rev=shift; + my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint + + # Examine changes from root of git repo, not from any subdir, + # in order to see all changes. + my ($subdir, $rootdir) = git_find_root(); + in_git_dir($rootdir, sub { + my @commits=git_commit_info($sha1, 1); + + if (! @commits) { + error "unknown commit"; # just in case + } + + # git revert will fail on merge commits. Add a nice message. + if (exists $commits[0]->{parents} && + @{$commits[0]->{parents}} > 1) { + error gettext("you are not allowed to revert a merge"); + } + + git_parse_changes(1, @commits); + }); +} + +sub rcs_revert ($) { + # Try to revert the given rev; returns undef on _success_. + my $rev = shift; + my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint + + if (run_or_non('git', 'revert', '--no-commit', $sha1)) { + return undef; + } + else { + run_or_die('git', 'reset', '--hard'); + return sprintf(gettext("Failed to revert commit %s"), $sha1); + } +} + +1 diff --git a/IkiWiki/Plugin/goodstuff.pm b/IkiWiki/Plugin/goodstuff.pm new file mode 100644 index 000000000..451cd6f84 --- /dev/null +++ b/IkiWiki/Plugin/goodstuff.pm @@ -0,0 +1,43 @@ +#!/usr/bin/perl +# Bundle of good stuff. +package IkiWiki::Plugin::goodstuff; + +use warnings; +use strict; +use IkiWiki 3.00; + +my @bundle=qw{ + brokenlinks + img + map + more + orphans + pagecount + pagestats + progress + shortcut + smiley + tag + table + template + toc + toggle + repolist +}; + +sub import { + hook(type => "getsetup", id => "goodstuff", call => \&getsetup); + foreach my $plugin (@bundle) { + IkiWiki::loadplugin($plugin); + } +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +1 diff --git a/IkiWiki/Plugin/google.pm b/IkiWiki/Plugin/google.pm new file mode 100644 index 000000000..68cde261c --- /dev/null +++ b/IkiWiki/Plugin/google.pm @@ -0,0 +1,53 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::google; + +use warnings; +use strict; +use IkiWiki 3.00; +use URI; + +sub import { + hook(type => "getsetup", id => "google", call => \&getsetup); + hook(type => "checkconfig", id => "google", call => \&checkconfig); + hook(type => "pagetemplate", id => "google", call => \&pagetemplate); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + section => "web", + }, +} + +sub checkconfig () { + if (! length $config{url}) { + error(sprintf(gettext("Must specify %s when using the %s plugin"), "url", 'google')); + } + + # This is a mass dependency, so if the search form template + # changes, every page is rebuilt. + add_depends("", "templates/googleform.tmpl"); +} + +my $form; +sub pagetemplate (@) { + my %params=@_; + my $page=$params{page}; + my $template=$params{template}; + + # Add search box to page header. + if ($template->query(name => "searchform")) { + if (! defined $form) { + my $searchform = template("googleform.tmpl", blind_cache => 1); + $searchform->param(url => $config{url}); + $searchform->param(html5 => $config{html5}); + $form=$searchform->output; + } + + $template->param(searchform => $form); + } +} + +1 diff --git a/IkiWiki/Plugin/goto.pm b/IkiWiki/Plugin/goto.pm new file mode 100644 index 000000000..6b596ac8b --- /dev/null +++ b/IkiWiki/Plugin/goto.pm @@ -0,0 +1,85 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::goto; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "cgi", id => 'goto', call => \&cgi); + hook(type => "getsetup", id => 'goto', call => \&getsetup); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "web", + } +} + +# cgi_goto(CGI, [page]) +# Redirect to a specified page, or display "not found". If not specified, +# the page param from the CGI object is used. +sub cgi_goto ($;$) { + my $q = shift; + my $page = shift; + + if (!defined $page) { + $page = IkiWiki::decode_utf8($q->param("page")); + + if (!defined $page) { + error("missing page parameter"); + } + } + + # It's possible that $page is not a valid page name; + # if so attempt to turn it into one. + if ($page !~ /$config{wiki_file_regexp}/) { + $page=titlepage($page); + } + + IkiWiki::loadindex(); + + my $link; + if (! IkiWiki::isinternal($page)) { + $link = bestlink("", $page); + } + elsif (defined $pagestate{$page}{meta}{permalink}) { + # Can only redirect to an internal page if it has a + # permalink. + IkiWiki::redirect($q, $pagestate{$page}{meta}{permalink}); + } + + if (! defined $link || ! length $link) { + IkiWiki::cgi_custom_failure( + $q, + "404 Not Found", + IkiWiki::cgitemplate($q, gettext("missing page"), + "

". + sprintf(gettext("The page %s does not exist."), + htmllink("", "", $page)). + "

") + ) + } + else { + IkiWiki::redirect($q, urlto($link)); + } + + exit; +} + +sub cgi ($) { + my $cgi=shift; + my $do = $cgi->param('do'); + + if (defined $do && ($do eq 'goto' || $do eq 'commenter' || + $do eq 'recentchanges_link')) { + # goto is the preferred name for this; recentchanges_link and + # commenter are for compatibility with any saved URLs + cgi_goto($cgi); + } +} + +1; diff --git a/IkiWiki/Plugin/graphviz.pm b/IkiWiki/Plugin/graphviz.pm new file mode 100644 index 000000000..d4018edaa --- /dev/null +++ b/IkiWiki/Plugin/graphviz.pm @@ -0,0 +1,149 @@ +#!/usr/bin/perl +# graphviz plugin for ikiwiki: render graphviz source as an image. +# Josh Triplett +package IkiWiki::Plugin::graphviz; + +use warnings; +use strict; +use IkiWiki 3.00; +use IPC::Open2; + +sub import { + hook(type => "getsetup", id => "graphviz", call => \&getsetup); + hook(type => "needsbuild", id => "version", call => \&needsbuild); + hook(type => "preprocess", id => "graph", call => \&graph, scan => 1); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +my %graphviz_programs = ( + "dot" => 1, "neato" => 1, "fdp" => 1, "twopi" => 1, "circo" => 1 +); + +sub needsbuild { + my $needsbuild=shift; + foreach my $page (keys %pagestate) { + if (exists $pagestate{$page}{graph} && + exists $pagesources{$page} && + grep { $_ eq $pagesources{$page} } @$needsbuild) { + # remove state, will be re-added if + # the graph is still there during the rebuild + delete $pagestate{$page}{graph}; + } + } + return $needsbuild; +} + +sub render_graph (\%) { + my %params = %{(shift)}; + + my $src = "charset=\"utf-8\";\n"; + $src .= "ratio=compress;\nsize=\"".($params{width}+0).", ".($params{height}+0)."\";\n" + if defined $params{width} and defined $params{height}; + $src .= $params{src}; + $src .= "}\n"; + + # Use the sha1 of the graphviz code as part of its filename, + # and as a unique identifier for its imagemap. + eval q{use Digest::SHA}; + error($@) if $@; + my $sha=IkiWiki::possibly_foolish_untaint(Digest::SHA::sha1_hex($params{type}.$src)); + $src = "$params{type} graph$sha {\n".$src; + + my $dest=$params{page}."/graph-".$sha.".png"; + will_render($params{page}, $dest); + + my $map=$pagestate{$params{destpage}}{graph}{$sha}; + if (! -e "$config{destdir}/$dest" || ! defined $map) { + # Use ikiwiki's function to create the image file, this makes + # sure needed subdirs are there and does some sanity checking. + writefile($dest, $config{destdir}, ""); + + my $pid; + my $sigpipe=0; + $SIG{PIPE}=sub { $sigpipe=1 }; + $pid=open2(*IN, *OUT, "$params{prog} -Tpng -o '$config{destdir}/$dest' -Tcmapx"); + + # open2 doesn't respect "use open ':utf8'" + binmode (IN, ':utf8'); + binmode (OUT, ':utf8'); + + print OUT $src; + close OUT; + + local $/ = undef; + $map=$pagestate{$params{destpage}}{graph}{$sha}=; + close IN; + + waitpid $pid, 0; + $SIG{PIPE}="DEFAULT"; + error gettext("failed to run graphviz") if ($sigpipe || $?); + } + + return "\n". + $map; +} + +sub graph (@) { + my %params=@_; + + # Support wikilinks in the graph source. + my $src=$params{src}; + $src="" unless defined $src; + $src=IkiWiki::linkify($params{page}, $params{destpage}, $params{src}); + return unless defined wantarray; # scan mode short-circuit + if ($src ne $params{src}) { + # linkify makes html links, but graphviz wants plain + # urls. This is, frankly a hack: Process source as html, + # throw out everything inside tags that is not a href. + my $s; + my $nested=0; + use HTML::Parser; + error $@ if $@; + my $p=HTML::Parser->new(api_version => 3); + $p->handler(start => sub { + my %attrs=%{shift()}; + if (exists $attrs{href}) { + if ($s=~/href\s*=\s*"$/) { + $s.=$attrs{href}; + } + elsif ($s=~/href\s*=\s*$/) { + $s.="\"$attrs{href}\""; + } + else { + $s.="href=\"$attrs{href}\""; + } + } + $nested++; + }, "attr"); + $p->handler(end => sub { + $nested--; + }); + $p->handler(default => sub { + $s.=join("", @_) unless $nested; + }, "text"); + $p->parse($src); + $p->eof; + $s=~s/\[ href= \]//g; # handle self-links + $params{src}=$s; + } + else { + $params{src}=$src; + } + + $params{type} = "digraph" unless defined $params{type}; + $params{prog} = "dot" unless defined $params{prog}; + error gettext("prog not a valid graphviz program") unless $graphviz_programs{$params{prog}}; + + return render_graph(%params); +} + +1 diff --git a/IkiWiki/Plugin/haiku.pm b/IkiWiki/Plugin/haiku.pm new file mode 100644 index 000000000..bf23dce67 --- /dev/null +++ b/IkiWiki/Plugin/haiku.pm @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# haiku generator plugin +package IkiWiki::Plugin::haiku; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "haiku", call => \&getsetup); + hook(type => "preprocess", id => "haiku", call => \&preprocess); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess (@) { + my %params=@_; + + my $haiku; + eval q{use Coy}; + if ($@ || ! Coy->can("Coy::with_haiku")) { + my @canned=( + "The lack of a Coy: + No darting, subtle haiku. + Instead, canned tuna. + ", + "apt-get install Coy + no, wait, that's not quite it + instead: libcoy-perl + ", + "Coyly I'll do it, + no code, count Five-Seven-Five + to make a haiku. + ", + ); + + $haiku=$canned[rand @canned]; + } + else { + $haiku=Coy::with_haiku($params{hint} ? $params{hint} : $params{page}); + + # trim off other text + $haiku=~s/\s+-----\n//s; + $haiku=~s/\s+-----.*//s; + } + + $haiku=~s/^\s+//mg; + $haiku=~s/\n/
\n/mg; + + return "\n\n

$haiku

\n\n"; +} + +1 diff --git a/IkiWiki/Plugin/headinganchors.pm b/IkiWiki/Plugin/headinganchors.pm new file mode 100644 index 000000000..ece6f4eac --- /dev/null +++ b/IkiWiki/Plugin/headinganchors.pm @@ -0,0 +1,43 @@ +#!/usr/bin/perl +# quick HTML heading id adder by Paul Wise +package IkiWiki::Plugin::headinganchors; + +use warnings; +use strict; +use IkiWiki 3.00; +use URI::Escape; + +sub import { + hook(type => "getsetup", id => "headinganchors", call => \&getsetup); + hook(type => "sanitize", id => "headinganchors", call => \&headinganchors); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub text_to_anchor { + my $str = shift; + $str =~ s/^\s+//; + $str =~ s/\s+$//; + $str =~ s/\s/_/g; + $str =~ s/"//g; + $str =~ s/^[^a-zA-Z]/z-/; # must start with an alphabetical character + $str = uri_escape_utf8($str); + $str =~ s/%/./g; + return $str; +} + +sub headinganchors (@) { + my %params=@_; + my $content=$params{content}; + $content=~s{([^>]*)}{''.$2.''}gie; + return $content; +} + +1 diff --git a/IkiWiki/Plugin/highlight.pm b/IkiWiki/Plugin/highlight.pm new file mode 100644 index 000000000..fbe7ddff4 --- /dev/null +++ b/IkiWiki/Plugin/highlight.pm @@ -0,0 +1,207 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::highlight; + +# This has been tested with highlight 2.16 and highlight 3.2+svn19. +# In particular version 3.2 won't work. It detects the different +# versions by the presence of the the highlight::DataDir class. + +use warnings; +use strict; +use IkiWiki 3.00; +use Encode; + +my $data_dir; + +sub import { + hook(type => "getsetup", id => "highlight", call => \&getsetup); + hook(type => "checkconfig", id => "highlight", call => \&checkconfig); + # this hook is used by the format plugin + hook(type => "htmlizeformat", id => "highlight", + call => \&htmlizeformat, last => 1); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, # format plugin + section => "format", + }, + tohighlight => { + type => "string", + example => ".c .h .cpp .pl .py Makefile:make", + description => "types of source files to syntax highlight", + safe => 1, + rebuild => 1, + }, + filetypes_conf => { + type => "string", + example => "/etc/highlight/filetypes.conf", + description => "location of highlight's filetypes.conf", + safe => 0, + rebuild => undef, + }, + langdefdir => { + type => "string", + example => "/usr/share/highlight/langDefs", + description => "location of highlight's langDefs directory", + safe => 0, + rebuild => undef, + }, +} + +sub checkconfig () { + eval q{use highlight}; + if (highlight::DataDir->can('new')) { + $data_dir=new highlight::DataDir(); + $data_dir->searchDataDir(""); + } else { + $data_dir=undef; + } + + if (! exists $config{filetypes_conf}) { + $config{filetypes_conf}= + ($data_dir ? $data_dir->getConfDir() : "/etc/highlight/") + . "filetypes.conf"; + } + if (! exists $config{langdefdir}) { + $config{langdefdir}= + ($data_dir ? $data_dir->getLangPath("") + : "/usr/share/highlight/langDefs"); + + } + if (exists $config{tohighlight} && read_filetypes()) { + foreach my $file (split ' ', $config{tohighlight}) { + my @opts = $file=~s/^\.// ? + (keepextension => 1) : + (noextension => 1); + my $ext = $file=~s/:(.*)// ? $1 : $file; + + my $langfile=ext2langfile($ext); + if (! defined $langfile) { + error(sprintf(gettext( + "tohighlight contains unknown file type '%s'"), + $ext)); + } + + hook( + type => "htmlize", + id => $file, + call => sub { + my %params=@_; + highlight($langfile, $file, $params{content}); + }, + longname => sprintf(gettext("Source code: %s"), $file), + @opts, + ); + } + } +} + +sub htmlizeformat { + my $format=lc shift; + my $langfile=ext2langfile($format); + + if (! defined $langfile) { + return; + } + + return Encode::decode_utf8(highlight($langfile, $format, shift)); +} + +my %ext2lang; +my $filetypes_read=0; +my %highlighters; + +# Parse highlight's config file to get extension => language mappings. +sub read_filetypes () { + my $f; + if (!open($f, $config{filetypes_conf})) { + warn($config{filetypes_conf}.": ".$!); + return 0; + }; + + local $/=undef; + my $config=<$f>; + close $f; + + # highlight >= 3.2 format (bind-style) + while ($config=~m/Lang\s*=\s*\"([^"]+)\"[,\s]+Extensions\s*=\s*{([^}]+)}/sg) { + my $lang=$1; + foreach my $bit (split ',', $2) { + $bit=~s/.*"(.*)".*/$1/s; + $ext2lang{$bit}=$lang; + } + } + + # highlight < 3.2 format + if (! keys %ext2lang) { + foreach (split("\n", $config)) { + if (/^\$ext\((.*)\)=(.*)$/) { + $ext2lang{$_}=$1 foreach $1, split ' ', $2; + } + } + } + + return $filetypes_read=1; +} + + +# Given a filename extension, determines the language definition to +# use to highlight it. +sub ext2langfile ($) { + my $ext=shift; + + my $langfile="$config{langdefdir}/$ext.lang"; + return $langfile if exists $highlighters{$langfile}; + + read_filetypes() unless $filetypes_read; + if (exists $ext2lang{$ext}) { + return "$config{langdefdir}/$ext2lang{$ext}.lang"; + } + # If a language only has one common extension, it will not + # be listed in filetypes, so check the langfile. + elsif (-e $langfile) { + return $langfile; + } + else { + return undef; + } +} + +# Interface to the highlight C library. +sub highlight ($$) { + my $langfile=shift; + my $extorfile=shift; + my $input=shift; + + eval q{use highlight}; + if ($@) { + print STDERR gettext("warning: highlight perl module not available; falling back to pass through"); + return $input; + } + + my $gen; + if (! exists $highlighters{$langfile}) { + $gen = highlight::CodeGenerator::getInstance($highlight::XHTML); + $gen->setFragmentCode(1); # generate html fragment + $gen->setHTMLEnclosePreTag(1); # include stylish
+		if ($data_dir){
+			# new style, requires a real theme, but has no effect
+			$gen->initTheme($data_dir->getThemePath("seashell.theme"));
+		} else {
+			# old style, anything works.
+			$gen->initTheme("/dev/null");
+		}
+		$gen->loadLanguage($langfile); # must come after initTheme
+		$gen->setEncoding("utf-8");
+		$highlighters{$langfile}=$gen;
+	}
+	else {		
+		$gen=$highlighters{$langfile};
+	}
+
+	return "
".$gen->generateString($input)."
"; +} + +1 diff --git a/IkiWiki/Plugin/hnb.pm b/IkiWiki/Plugin/hnb.pm new file mode 100644 index 000000000..5157a6b93 --- /dev/null +++ b/IkiWiki/Plugin/hnb.pm @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# hnb markup +# Licensed under the GPL v2 or greater +# Copyright (C) 2008 by Axel Beckert +# +# TODO: Make a switch to allow both HTML export routines of hnb +# (`export_html` and `export_htmlcss`) to be used. + +package IkiWiki::Plugin::hnb; + +use warnings; +use strict; +use IkiWiki 3.00; +use File::Temp qw(:mktemp); + +sub import { + hook(type => "getsetup", id => "hnb", call => \&getsetup); + hook(type => "htmlize", id => "hnb", call => \&htmlize); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, # format plugin + section => "format", + }, +} + +sub htmlize (@) { + my %params = @_; + + # hnb outputs version number etc. every time to STDOUT, so + # using files makes it easier to seprarate. + + my ($infh, $tmpin) = mkstemp( "/tmp/ikiwiki-hnbin.XXXXXXXXXX" ); + my ($outfh, $tmpout) = mkstemp( "/tmp/ikiwiki-hnbout.XXXXXXXXXX" ); + + open(TMP, '>', $tmpin) or die "Can't write to $tmpin: $!"; + print TMP $params{content}; + close TMP; + + system("hnb '$tmpin' 'go root' 'export_html $tmpout' > /dev/null"); + unlink $tmpin; + + open(TMP, '<', $tmpout) or die "Can't read from $tmpout: $!"; + local $/; + my $ret = ; + close TMP; + unlink $tmpout; + + $ret =~ s/.*//si; + $ret =~ s/.*//si; + + return $ret; +} + +1; diff --git a/IkiWiki/Plugin/html.pm b/IkiWiki/Plugin/html.pm new file mode 100644 index 000000000..4dbae081b --- /dev/null +++ b/IkiWiki/Plugin/html.pm @@ -0,0 +1,33 @@ +#!/usr/bin/perl +# Raw html as a wiki page type. +package IkiWiki::Plugin::html; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "html", call => \&getsetup); + hook(type => "htmlize", id => "html", call => \&htmlize); + hook(type => "htmlize", id => "htm", call => \&htmlize); + + # ikiwiki defaults to skipping .html files as a security measure; + # make it process them so this plugin can take effect + $config{wiki_file_prune_regexps} = [ grep { !m/\\\.x\?html\?\$/ } @{$config{wiki_file_prune_regexps}} ]; +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, # format plugin + section => "format", + }, +} + +sub htmlize (@) { + my %params=@_; + return $params{content}; +} + +1 diff --git a/IkiWiki/Plugin/htmlbalance.pm b/IkiWiki/Plugin/htmlbalance.pm new file mode 100644 index 000000000..da450eea7 --- /dev/null +++ b/IkiWiki/Plugin/htmlbalance.pm @@ -0,0 +1,58 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::htmlbalance; + +# htmlbalance: Parse and re-serialize HTML to ensure balanced tags +# +# Copyright 2008 Simon McVittie +# Licensed under the GNU GPL, version 2, or any later version published by the +# Free Software Foundation + +use warnings; +use strict; +use IkiWiki 3.00; +use HTML::Entities; + +sub import { + hook(type => "getsetup", id => "htmlbalance", call => \&getsetup); + hook(type => "sanitize", id => "htmlbalance", call => \&sanitize); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +sub sanitize (@) { + my %params=@_; + my $ret = ''; + + eval q{use HTML::TreeBuilder}; + error $@ if $@; + my $tree = HTML::TreeBuilder->new(); + $tree->ignore_unknown(0); + $tree->ignore_ignorable_whitespace(0); + $tree->no_space_compacting(1); + $tree->p_strict(1); + $tree->store_comments(0); + $tree->store_declarations(0); + $tree->store_pis(0); + $tree->parse_content($params{content}); + my @nodes = $tree->disembowel(); + foreach my $node (@nodes) { + if (ref $node) { + $ret .= $node->as_HTML(undef, '', {}); + chomp $ret; + $node->delete(); + } + else { + $ret .= encode_entities($node); + } + } + $tree->delete(); + return $ret; +} + +1 diff --git a/IkiWiki/Plugin/htmlscrubber.pm b/IkiWiki/Plugin/htmlscrubber.pm new file mode 100644 index 000000000..36c012c73 --- /dev/null +++ b/IkiWiki/Plugin/htmlscrubber.pm @@ -0,0 +1,127 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::htmlscrubber; + +use warnings; +use strict; +use IkiWiki 3.00; + +# This regexp matches urls that are in a known safe scheme. +# Feel free to use it from other plugins. +our $safe_url_regexp; + +sub import { + hook(type => "getsetup", id => "htmlscrubber", call => \&getsetup); + hook(type => "sanitize", id => "htmlscrubber", call => \&sanitize); + + # Only known uri schemes are allowed to avoid all the ways of + # embedding javascrpt. + # List at http://en.wikipedia.org/wiki/URI_scheme + my $uri_schemes=join("|", map quotemeta, + # IANA registered schemes + "http", "https", "ftp", "mailto", "file", "telnet", "gopher", + "aaa", "aaas", "acap", "cap", "cid", "crid", + "dav", "dict", "dns", "fax", "go", "h323", "im", "imap", + "ldap", "mid", "news", "nfs", "nntp", "pop", "pres", + "sip", "sips", "snmp", "tel", "urn", "wais", "xmpp", + "z39.50r", "z39.50s", + # Selected unofficial schemes + "aim", "callto", "cvs", "ed2k", "feed", "fish", "gg", + "irc", "ircs", "lastfm", "ldaps", "magnet", "mms", + "msnim", "notes", "rsync", "secondlife", "skype", "ssh", + "sftp", "smb", "sms", "snews", "webcal", "ymsgr", + "bitcoin", "git", "svn", "bzr", "darcs", "hg" + ); + # 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 () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "core", + }, + htmlscrubber_skip => { + type => "pagespec", + example => "!*/Discussion", + description => "PageSpec specifying pages not to scrub", + link => "ikiwiki/PageSpec", + safe => 1, + rebuild => undef, + }, +} + +sub sanitize (@) { + my %params=@_; + + if (exists $config{htmlscrubber_skip} && + length $config{htmlscrubber_skip} && + exists $params{page} && + pagespec_match($params{page}, $config{htmlscrubber_skip})) { + return $params{content}; + } + + return scrubber()->scrub($params{content}); +} + +my $_scrubber; +sub scrubber { + return $_scrubber if defined $_scrubber; + + eval q{use HTML::Scrubber}; + error($@) if $@; + # Lists based on http://feedparser.org/docs/html-sanitization.html + # With html5 tags added. + $_scrubber = HTML::Scrubber->new( + allow => [qw{ + a abbr acronym address area b big blockquote br br/ + button caption center cite code col colgroup dd del + dfn dir div dl dt em fieldset font form h1 h2 h3 h4 + h5 h6 hr hr/ i img input ins kbd label legend li map + menu ol optgroup option p p/ pre q s samp select small + span strike strong sub sup table tbody td textarea + tfoot th thead tr tt u ul var + + video audio source section nav article aside hgroup + header footer figure figcaption time mark canvas + datalist progress meter ruby rt rp details summary + }], + default => [undef, { ( + map { $_ => 1 } qw{ + abbr accept accept-charset accesskey + align alt axis border cellpadding cellspacing + char charoff charset checked class + clear cols colspan color compact coords + datetime dir disabled enctype for frame + headers height hreflang hspace id ismap + label lang maxlength media method + multiple name nohref noshade nowrap prompt + readonly rel rev rows rowspan rules scope + selected shape size span start summary + tabindex target title type valign + value vspace width + + autofocus autoplay preload loopstart + loopend end playcount controls pubdate + placeholder min max step low high optimum + form required autocomplete novalidate pattern + list formenctype formmethod formnovalidate + formtarget reversed spellcheck open hidden + } ), + "/" => 1, # emit proper
XHTML + href => $safe_url_regexp, + src => $safe_url_regexp, + action => $safe_url_regexp, + formaction => $safe_url_regexp, + cite => $safe_url_regexp, + longdesc => $safe_url_regexp, + poster => $safe_url_regexp, + usemap => $safe_url_regexp, + }], + ); + return $_scrubber; +} + +1 diff --git a/IkiWiki/Plugin/htmltidy.pm b/IkiWiki/Plugin/htmltidy.pm new file mode 100644 index 000000000..da77e60f1 --- /dev/null +++ b/IkiWiki/Plugin/htmltidy.pm @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# HTML Tidy plugin +# requires 'tidy' binary, found in Debian or http://tidy.sf.net/ +# mostly a proof-of-concept on how to use external filters. +# It is particularly useful when the html plugin is used. +# +# by Faidon Liambotis +package IkiWiki::Plugin::htmltidy; + +use warnings; +use strict; +use IkiWiki 3.00; +use IPC::Open2; + +sub import { + hook(type => "getsetup", id => "tidy", call => \&getsetup); + hook(type => "sanitize", id => "tidy", call => \&sanitize); + hook(type => "checkconfig", id => "tidy", call => \&checkconfig); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + }, + htmltidy => { + type => "string", + description => "tidy command line", + safe => 0, # path + rebuild => undef, + }, +} + +sub checkconfig () { + if (! defined $config{htmltidy}) { + $config{htmltidy}="tidy -quiet -asxhtml -utf8 --show-body-only yes --show-warnings no --tidy-mark no --markup yes"; + } +} + +sub sanitize (@) { + my %params=@_; + + return $params{content} unless defined $config{htmltidy}; + + my $pid; + my $sigpipe=0; + $SIG{PIPE}=sub { $sigpipe=1 }; + $pid=open2(*IN, *OUT, "$config{htmltidy} 2>/dev/null"); + + # open2 doesn't respect "use open ':utf8'" + binmode (IN, ':utf8'); + binmode (OUT, ':utf8'); + + print OUT $params{content}; + close OUT; + + local $/ = undef; + my $ret=; + close IN; + waitpid $pid, 0; + + $SIG{PIPE}="DEFAULT"; + if ($sigpipe || ! defined $ret) { + return gettext("htmltidy failed to parse this html"); + } + + return $ret; +} + +1 diff --git a/IkiWiki/Plugin/httpauth.pm b/IkiWiki/Plugin/httpauth.pm new file mode 100644 index 000000000..76d574b2a --- /dev/null +++ b/IkiWiki/Plugin/httpauth.pm @@ -0,0 +1,117 @@ +#!/usr/bin/perl +# HTTP basic auth plugin. +package IkiWiki::Plugin::httpauth; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "checkconfig", id => "httpauth", call => \&checkconfig); + 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 () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "auth", + }, + cgiauthurl => { + type => "string", + example => "http://example.com/wiki/auth/ikiwiki.cgi", + description => "url to redirect to when authentication is needed", + 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 checkconfig () { + if ($config{cgi} && defined $config{cgiauthurl} && + keys %{$IkiWiki::hooks{auth}} < 2) { + # There are no other auth hooks registered, so avoid + # the normal signin form, and jump right to httpauth. + require IkiWiki::CGI; + inject(name => "IkiWiki::cgi_signin", call => sub ($$) { + my $cgi=shift; + redir_cgiauthurl($cgi, $cgi->query_string()); + }); + } +} + +sub redir_cgiauthurl ($;@) { + my $cgi=shift; + + IkiWiki::redirect($cgi, + @_ > 1 ? IkiWiki::cgiurl(cgiurl => $config{cgiauthurl}, @_) + : $config{cgiauthurl}."?@_" + ); + exit; +} + +sub auth ($$) { + my $cgi=shift; + my $session=shift; + + if (defined $cgi->remote_user()) { + $session->param("name", $cgi->remote_user()); + } +} + +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 canedit ($$$) { + my $page=shift; + my $cgi=shift; + my $session=shift; + + if (! defined $cgi->remote_user() && + (! defined $session->param("name") || + ! IkiWiki::userinfo_get($session->param("name"), "regdate")) && + 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; + } +} + +1 diff --git a/IkiWiki/Plugin/img.pm b/IkiWiki/Plugin/img.pm new file mode 100644 index 000000000..b92e24cc0 --- /dev/null +++ b/IkiWiki/Plugin/img.pm @@ -0,0 +1,214 @@ +#!/usr/bin/perl +# Ikiwiki enhanced image handling plugin +# Christian Mock cm@tahina.priv.at 20061002 +package IkiWiki::Plugin::img; + +use warnings; +use strict; +use IkiWiki 3.00; + +my %imgdefaults; + +sub import { + hook(type => "getsetup", id => "img", call => \&getsetup); + hook(type => "preprocess", id => "img", call => \&preprocess, scan => 1); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess (@) { + my ($image) = $_[0] =~ /$config{wiki_file_regexp}/; # untaint + my %params=@_; + + if (! defined $image) { + error("bad image filename"); + } + + if (exists $imgdefaults{$params{page}}) { + foreach my $key (keys %{$imgdefaults{$params{page}}}) { + if (! exists $params{$key}) { + $params{$key}=$imgdefaults{$params{page}}->{$key}; + } + } + } + + if (! exists $params{size} || ! length $params{size}) { + $params{size}='full'; + } + + if ($image eq 'defaults') { + $imgdefaults{$params{page}} = \%params; + return ''; + } + + add_link($params{page}, $image); + add_depends($params{page}, $image); + + # optimisation: detect scan mode, and avoid generating the image + if (! defined wantarray) { + return; + } + + my $file = bestlink($params{page}, $image); + my $srcfile = srcfile($file, 1); + if (! length $file || ! defined $srcfile) { + return htmllink($params{page}, $params{destpage}, $image); + } + + my $dir = $params{page}; + my $base = IkiWiki::basename($file); + my $issvg = $base=~s/\.svg$/.png/i; + + eval q{use Image::Magick}; + error gettext("Image::Magick is not installed") if $@; + my $im = Image::Magick->new($issvg ? (magick => "png") : ()); + my $imglink; + my $r = $im->Read($srcfile); + error sprintf(gettext("failed to read %s: %s"), $file, $r) if $r; + + my ($dwidth, $dheight); + + if ($params{size} ne 'full') { + my ($w, $h) = ($params{size} =~ /^(\d*)x(\d*)$/); + error sprintf(gettext('wrong size format "%s" (should be WxH)'), $params{size}) + unless (defined $w && defined $h && + (length $w || length $h)); + + if ((length $w && $w > $im->Get("width")) || + (length $h && $h > $im->Get("height"))) { + # resizing larger + $imglink = $file; + + # don't generate larger image, just set display size + if (length $w && length $h) { + ($dwidth, $dheight)=($w, $h); + } + # avoid division by zero on 0x0 image + elsif ($im->Get("width") == 0 || $im->Get("height") == 0) { + ($dwidth, $dheight)=(0, 0); + } + # calculate unspecified size from the other one, preserving + # aspect ratio + elsif (length $w) { + $dwidth=$w; + $dheight=$w / $im->Get("width") * $im->Get("height"); + } + elsif (length $h) { + $dheight=$h; + $dwidth=$h / $im->Get("height") * $im->Get("width"); + } + } + else { + # resizing smaller + my $outfile = "$config{destdir}/$dir/${w}x${h}-$base"; + $imglink = "$dir/${w}x${h}-$base"; + + will_render($params{page}, $imglink); + + if (-e $outfile && (-M $srcfile >= -M $outfile)) { + $im = Image::Magick->new; + $r = $im->Read($outfile); + error sprintf(gettext("failed to read %s: %s"), $outfile, $r) if $r; + } + else { + $r = $im->Resize(geometry => "${w}x${h}"); + error sprintf(gettext("failed to resize: %s"), $r) if $r; + + # don't actually write resized file in preview mode; + # rely on width and height settings + if (! $params{preview}) { + my @blob = $im->ImageToBlob(); + writefile($imglink, $config{destdir}, $blob[0], 1); + } + else { + $imglink = $file; + } + } + + # always get the true size of the resized image + $dwidth = $im->Get("width"); + $dheight = $im->Get("height"); + } + } + else { + $imglink = $file; + $dwidth = $im->Get("width"); + $dheight = $im->Get("height"); + } + + if (! defined($dwidth) || ! defined($dheight)) { + error sprintf(gettext("failed to determine size of image %s"), $file) + } + + my ($fileurl, $imgurl); + if (! $params{preview}) { + $fileurl=urlto($file, $params{destpage}); + $imgurl=urlto($imglink, $params{destpage}); + } + else { + $fileurl=urlto($file); + $imgurl=urlto($imglink); + } + + if (! exists $params{class}) { + $params{class}="img"; + } + + my $attrs=''; + foreach my $attr (qw{alt title class id hspace vspace}) { + if (exists $params{$attr}) { + $attrs.=" $attr=\"$params{$attr}\""; + } + } + + my $imgtag=''; + + my $link; + if (! defined $params{link}) { + $link=$fileurl; + } + elsif ($params{link} =~ /^\w+:\/\//) { + $link=$params{link}; + } + + if (defined $link) { + $imgtag=''.$imgtag.''; + } + else { + my $b = bestlink($params{page}, $params{link}); + + if (length $b) { + add_depends($params{page}, $b, deptype("presence")); + $imgtag=htmllink($params{page}, $params{destpage}, + $params{link}, linktext => $imgtag, + noimageinline => 1, + ); + } + } + + if (exists $params{caption}) { + return ''. + ''. + ''. + '
'.$params{caption}.'
'.$imgtag.'
'; + } + else { + return $imgtag; + } +} + +1 diff --git a/IkiWiki/Plugin/inline.pm b/IkiWiki/Plugin/inline.pm new file mode 100644 index 000000000..8eb033951 --- /dev/null +++ b/IkiWiki/Plugin/inline.pm @@ -0,0 +1,772 @@ +#!/usr/bin/perl +# Page inlining and blogging. +package IkiWiki::Plugin::inline; + +use warnings; +use strict; +use Encode; +use IkiWiki 3.00; +use URI; + +my %knownfeeds; +my %page_numfeeds; +my @inline; +my $nested=0; + +sub import { + hook(type => "getopt", id => "inline", call => \&getopt); + hook(type => "getsetup", id => "inline", call => \&getsetup); + hook(type => "checkconfig", id => "inline", call => \&checkconfig); + hook(type => "sessioncgi", id => "inline", call => \&sessioncgi); + hook(type => "preprocess", id => "inline", + call => \&IkiWiki::preprocess_inline, scan => 1); + hook(type => "pagetemplate", id => "inline", + call => \&IkiWiki::pagetemplate_inline); + hook(type => "format", id => "inline", call => \&format, first => 1); + # Hook to change to do pinging since it's called late. + # This ensures each page only pings once and prevents slow + # pings interrupting page builds. + hook(type => "rendered", id => "inline", call => \&IkiWiki::pingurl); +} + +sub getopt () { + eval q{use Getopt::Long}; + error($@) if $@; + Getopt::Long::Configure('pass_through'); + GetOptions( + "rss!" => \$config{rss}, + "atom!" => \$config{atom}, + "allowrss!" => \$config{allowrss}, + "allowatom!" => \$config{allowatom}, + "pingurl=s" => sub { + push @{$config{pingurl}}, $_[1]; + }, + ); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "core", + }, + rss => { + type => "boolean", + example => 0, + description => "enable rss feeds by default?", + safe => 1, + rebuild => 1, + }, + atom => { + type => "boolean", + example => 0, + description => "enable atom feeds by default?", + safe => 1, + rebuild => 1, + }, + allowrss => { + type => "boolean", + example => 0, + description => "allow rss feeds to be used?", + safe => 1, + rebuild => 1, + }, + allowatom => { + type => "boolean", + example => 0, + description => "allow atom feeds to be used?", + safe => 1, + rebuild => 1, + }, + pingurl => { + type => "string", + example => "http://rpc.technorati.com/rpc/ping", + description => "urls to ping (using XML-RPC) on feed update", + safe => 1, + rebuild => 0, + }, +} + +sub checkconfig () { + if (($config{rss} || $config{atom}) && ! length $config{url}) { + error(gettext("Must specify url to wiki with --url when using --rss or --atom")); + } + if ($config{rss}) { + push @{$config{wiki_file_prune_regexps}}, qr/\.rss$/; + } + if ($config{atom}) { + push @{$config{wiki_file_prune_regexps}}, qr/\.atom$/; + } + if (! exists $config{pingurl}) { + $config{pingurl}=[]; + } +} + +sub format (@) { + my %params=@_; + + # Fill in the inline content generated earlier. This is actually an + # optimisation. + $params{content}=~s{
}{ + delete @inline[$1,] + }eg; + return $params{content}; +} + +sub sessioncgi ($$) { + my $q=shift; + my $session=shift; + + if ($q->param('do') eq 'blog') { + my $page=titlepage(decode_utf8($q->param('title'))); + $page=~s/(\/)/"__".ord($1)."__"/eg; # don't create subdirs + # if the page already exists, munge it to be unique + my $from=$q->param('from'); + my $add=""; + while (exists $IkiWiki::pagecase{lc($from."/".$page.$add)}) { + $add=1 unless length $add; + $add++; + } + $q->param('page', "/$from/$page$add"); + # now go create the page + $q->param('do', 'create'); + # make sure the editpage plugin is loaded + if (IkiWiki->can("cgi_editpage")) { + IkiWiki::cgi_editpage($q, $session); + } + else { + error(gettext("page editing not allowed")); + } + exit; + } +} + +# Back to ikiwiki namespace for the rest, this code is very much +# internal to ikiwiki even though it's separated into a plugin. +package IkiWiki; + +my %toping; +my %feedlinks; + +sub preprocess_inline (@) { + my %params=@_; + + if (! exists $params{pages} && ! exists $params{pagenames}) { + error gettext("missing pages parameter"); + } + + if (! defined wantarray) { + # Running in scan mode: only do the essentials + + if (yesno($params{trail}) && IkiWiki::Plugin::trail->can("preprocess_trailitems")) { + # default to sorting age, the same as inline itself, + # but let the params override that + IkiWiki::Plugin::trail::preprocess_trailitems(sort => 'age', %params); + } + + return; + } + + if (yesno($params{trail}) && IkiWiki::Plugin::trail->can("preprocess_trailitems")) { + scalar IkiWiki::Plugin::trail::preprocess_trailitems(sort => 'age', %params); + } + + my $raw=yesno($params{raw}); + my $archive=yesno($params{archive}); + 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=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) { + $params{show}=10; + } + if (! exists $params{feedshow} && exists $params{show}) { + $params{feedshow}=$params{show}; + } + my $desc; + if (exists $params{description}) { + $desc = $params{description} + } + else { + $desc = $config{wikiname}; + } + my $actions=yesno($params{actions}); + if (exists $params{template}) { + $params{template}=~s/[^-_a-zA-Z0-9]+//g; + } + else { + $params{template} = $archive ? "archivepage" : "inlinepage"; + } + + my @list; + + if (exists $params{pagenames}) { + foreach my $p (qw(sort pages)) { + if (exists $params{$p}) { + error sprintf(gettext("the %s and %s parameters cannot be used together"), + "pagenames", $p); + } + } + + @list = split ' ', $params{pagenames}; + + if (yesno($params{reverse})) { + @list=reverse(@list); + } + + foreach my $p (@list) { + add_depends($params{page}, $p, deptype($quick ? "presence" : "content")); + } + + @list = grep { exists $pagesources{$_} } @list; + } + else { + my $num=0; + if ($params{show}) { + $num=$params{show}; + } + if ($params{feedshow} && $num < $params{feedshow} && $num > 0) { + $num=$params{feedshow}; + } + if ($params{skip} && $num) { + $num+=$params{skip}; + } + + @list = pagespec_match_list($params{page}, $params{pages}, + deptype => deptype($quick ? "presence" : "content"), + filter => sub { $_[0] eq $params{page} }, + sort => exists $params{sort} ? $params{sort} : "age", + reverse => yesno($params{reverse}), + ($num ? (num => $num) : ()), + ); + } + + if (exists $params{skip}) { + @list=@list[$params{skip} .. $#list]; + } + + my @feedlist; + if ($feeds) { + if (exists $params{feedshow} && + $params{feedshow} && @list > $params{feedshow}) { + @feedlist=@list[0..$params{feedshow} - 1]; + } + else { + @feedlist=@list; + } + } + + if ($params{show} && @list > $params{show}) { + @list=@list[0..$params{show} - 1]; + } + + if ($feeds && exists $params{feedpages}) { + @feedlist = pagespec_match_list( + $params{page}, "($params{pages}) and ($params{feedpages})", + deptype => deptype($quick ? "presence" : "content"), + list => \@feedlist, + ); + } + + my ($feedbase, $feednum); + if ($feeds) { + # Ensure that multiple feeds on a page go to unique files. + + # Feedfile can lead to conflicts if usedirs is not enabled, + # so avoid supporting it in that case. + delete $params{feedfile} if ! $config{usedirs}; + # Tight limits on legal feedfiles, to avoid security issues + # and conflicts. + if (defined $params{feedfile}) { + if ($params{feedfile} =~ /\// || + $params{feedfile} !~ /$config{wiki_file_regexp}/) { + error("illegal feedfile"); + } + $params{feedfile}=possibly_foolish_untaint($params{feedfile}); + } + $feedbase=targetpage($params{page}, "", $params{feedfile}); + + my $feedid=join("\0", $feedbase, map { $_."\0".$params{$_} } sort keys %params); + if (exists $knownfeeds{$feedid}) { + $feednum=$knownfeeds{$feedid}; + } + else { + if (exists $page_numfeeds{$params{destpage}}{$feedbase}) { + if ($feeds) { + $feednum=$knownfeeds{$feedid}=++$page_numfeeds{$params{destpage}}{$feedbase}; + } + } + else { + $feednum=$knownfeeds{$feedid}=""; + if ($feeds) { + $page_numfeeds{$params{destpage}}{$feedbase}=1; + } + } + } + } + + my ($rssurl, $atomurl, $rssdesc, $atomdesc); + if ($feeds) { + if ($rss) { + $rssurl=abs2rel($feedbase."rss".$feednum, dirname(htmlpage($params{destpage}))); + $rssdesc = sprintf(gettext("%s (RSS feed)"), $desc); + } + if ($atom) { + $atomurl=abs2rel($feedbase."atom".$feednum, dirname(htmlpage($params{destpage}))); + $atomdesc = sprintf(gettext("%s (Atom feed)"), $desc); + } + } + + my $ret=""; + + if (length $config{cgiurl} && ! $params{preview} && (exists $params{rootpage} || + (exists $params{postform} && yesno($params{postform}))) && + IkiWiki->can("cgi_editpage")) { + # Add a blog post form, with feed buttons. + my $formtemplate=template_depends("blogpost.tmpl", $params{page}, blind_cache => 1); + $formtemplate->param(cgiurl => IkiWiki::cgiurl()); + $formtemplate->param(rootpage => rootpage(%params)); + if ($feeds) { + if ($rss) { + $formtemplate->param(rssurl => $rssurl); + $formtemplate->param(rssdesc => $rssdesc); + } + if ($atom) { + $formtemplate->param(atomurl => $atomurl); + $formtemplate->param(atomdesc => $atomdesc); + } + } + if (exists $params{postformtext}) { + $formtemplate->param(postformtext => + $params{postformtext}); + } + else { + $formtemplate->param(postformtext => + gettext("Add a new post titled:")); + } + if (exists $params{id}) { + $formtemplate->param(postformid => + $params{id}); + } + $ret.=$formtemplate->output; + + # The post form includes the feed buttons, so + # emptyfeeds cannot be hidden. + $emptyfeeds=1; + } + elsif ($feeds && !$params{preview} && ($emptyfeeds || @feedlist)) { + # Add feed buttons. + my $linktemplate=template_depends("feedlink.tmpl", $params{page}, blind_cache => 1); + if ($rss) { + $linktemplate->param(rssurl => $rssurl); + $linktemplate->param(rssdesc => $rssdesc); + } + if ($atom) { + $linktemplate->param(atomurl => $atomurl); + $linktemplate->param(atomdesc => $atomdesc); + } + if (exists $params{id}) { + $linktemplate->param(id => $params{id}); + } + $ret.=$linktemplate->output; + } + + if (! $feedonly) { + my $template; + if (! $raw) { + # cannot use wiki pages as templates; template not sanitized due to + # format hook hack + eval { + $template=template_depends($params{template}.".tmpl", $params{page}, + blind_cache => 1); + }; + if ($@) { + error sprintf(gettext("failed to process template %s"), $params{template}.".tmpl").": $@"; + } + } + my $needcontent=$raw || (!($archive && $quick) && $template->query(name => 'content')); + + foreach my $page (@list) { + my $file = $pagesources{$page}; + my $type = pagetype($file); + if (! $raw) { + if ($needcontent) { + # Get the content before populating the + # template, since getting the content uses + # the same template if inlines are nested. + my $content=get_inline_content($page, $params{destpage}); + $template->param(content => $content); + } + $template->param(pageurl => urlto($page, $params{destpage})); + $template->param(inlinepage => $page); + $template->param(title => pagetitle(basename($page))); + $template->param(ctime => displaytime($pagectime{$page}, $params{timeformat}, 1)); + $template->param(mtime => displaytime($pagemtime{$page}, $params{timeformat})); + $template->param(first => 1) if $page eq $list[0]; + $template->param(last => 1) if $page eq $list[$#list]; + $template->param(html5 => $config{html5}); + + if ($actions) { + my $file = $pagesources{$page}; + my $type = pagetype($file); + if ($config{discussion}) { + if ($page !~ /.*\/\Q$config{discussionpage}\E$/i && + (length $config{cgiurl} || + exists $pagesources{$page."/".lc($config{discussionpage})})) { + $template->param(have_actions => 1); + $template->param(discussionlink => + htmllink($page, + $params{destpage}, + $config{discussionpage}, + noimageinline => 1, + forcesubpage => 1)); + } + } + if (length $config{cgiurl} && + defined $type && + IkiWiki->can("cgi_editpage")) { + $template->param(have_actions => 1); + $template->param(editurl => cgiurl(do => "edit", page => $page)); + + } + } + + run_hooks(pagetemplate => sub { + shift->(page => $page, destpage => $params{destpage}, + template => $template,); + }); + + $ret.=$template->output; + $template->clear_params; + } + else { + if (defined $type) { + $ret.="\n". + linkify($page, $params{destpage}, + preprocess($page, $params{destpage}, + filter($page, $params{destpage}, + readfile(srcfile($file))))); + } + else { + $ret.="\n". + readfile(srcfile($file)); + } + } + } + } + + if ($feeds && ($emptyfeeds || @feedlist)) { + if ($rss) { + my $rssp=$feedbase."rss".$feednum; + will_render($params{destpage}, $rssp); + if (! $params{preview}) { + writefile($rssp, $config{destdir}, + genfeed("rss", + $config{url}."/".$rssp, $desc, $params{guid}, $params{page}, @feedlist)); + $toping{$params{destpage}}=1 unless $config{rebuild}; + $feedlinks{$params{destpage}}.=qq{}; + } + } + if ($atom) { + my $atomp=$feedbase."atom".$feednum; + will_render($params{destpage}, $atomp); + if (! $params{preview}) { + writefile($atomp, $config{destdir}, + genfeed("atom", $config{url}."/".$atomp, $desc, $params{guid}, $params{page}, @feedlist)); + $toping{$params{destpage}}=1 unless $config{rebuild}; + $feedlinks{$params{destpage}}.=qq{}; + } + } + } + + clear_inline_content_cache(); + + return $ret if $raw || $nested; + push @inline, $ret; + return "
\n\n"; +} + +sub pagetemplate_inline (@) { + my %params=@_; + my $page=$params{page}; + my $template=$params{template}; + + $template->param(feedlinks => $feedlinks{$page}) + if exists $feedlinks{$page} && $template->query(name => "feedlinks"); +} + +{ +my %inline_content; +my $cached_destpage=""; + +sub get_inline_content ($$) { + my $page=shift; + my $destpage=shift; + + if (exists $inline_content{$page} && $cached_destpage eq $destpage) { + return $inline_content{$page}; + } + + my $file=$pagesources{$page}; + my $type=pagetype($file); + my $ret=""; + if (defined $type) { + $nested++; + $ret=htmlize($page, $destpage, $type, + linkify($page, $destpage, + preprocess($page, $destpage, + filter($page, $destpage, + readfile(srcfile($file)))))); + $nested--; + if (isinternal($page)) { + # make inlined text of internal pages searchable + run_hooks(indexhtml => sub { + shift->(page => $page, destpage => $destpage, + content => $ret); + }); + } + } + + if ($cached_destpage ne $destpage) { + clear_inline_content_cache(); + $cached_destpage=$destpage; + } + return $inline_content{$page}=$ret; +} + +sub clear_inline_content_cache () { + %inline_content=(); +} + +} + +sub date_822 ($) { + my $time=shift; + + my $lc_time=POSIX::setlocale(&POSIX::LC_TIME); + POSIX::setlocale(&POSIX::LC_TIME, "C"); + my $ret=POSIX::strftime("%a, %d %b %Y %H:%M:%S %z", localtime($time)); + POSIX::setlocale(&POSIX::LC_TIME, $lc_time); + return $ret; +} + +sub absolute_urls ($$) { + # needed because rss sucks + my $html=shift; + my $baseurl=shift; + + my $url=$baseurl; + $url=~s/[^\/]+$//; + my $urltop; # calculated if needed + + my $ret=""; + + eval q{use HTML::Parser; use HTML::Tagset}; + die $@ if $@; + my $p = HTML::Parser->new(api_version => 3); + $p->handler(default => sub { $ret.=join("", @_) }, "text"); + $p->handler(start => sub { + my ($tagname, $pos, $text) = @_; + if (ref $HTML::Tagset::linkElements{$tagname}) { + while (4 <= @$pos) { + # use attribute sets from right to left + # to avoid invalidating the offsets + # when replacing the values + my ($k_offset, $k_len, $v_offset, $v_len) = + splice(@$pos, -4); + my $attrname = lc(substr($text, $k_offset, $k_len)); + next unless grep { $_ eq $attrname } @{$HTML::Tagset::linkElements{$tagname}}; + next unless $v_offset; # 0 v_offset means no value + my $v = substr($text, $v_offset, $v_len); + $v =~ s/^([\'\"])(.*)\1$/$2/; + eval q{use HTML::Entities}; + my $dv = decode_entities($v); + if ($dv=~/^#/) { + $v=$baseurl.$v; # anchor + } + elsif ($dv=~/^(?!\w+:)[^\/]/) { + $v=$url.$v; # relative url + } + elsif ($dv=~/^\//) { + if (! defined $urltop) { + # what is the non path part of the url? + my $top_uri = URI->new($url); + $top_uri->path_query(""); # reset the path + $urltop = $top_uri->as_string; + } + $v=$urltop.$v; # url relative to top of site + } + $v =~ s/\"/"/g; # since we quote with "" + substr($text, $v_offset, $v_len) = qq("$v"); + } + } + $ret.=$text; + }, "tagname, tokenpos, text"); + $p->parse($html); + $p->eof; + + return $ret; +} + +sub genfeed ($$$$$@) { + my $feedtype=shift; + my $feedurl=shift; + my $feeddesc=shift; + my $guid=shift; + my $page=shift; + my @pages=@_; + + my $url=URI->new(encode_utf8(urlto($page,"",1))); + + my $itemtemplate=template_depends($feedtype."item.tmpl", $page, blind_cache => 1); + my $content=""; + my $lasttime = 0; + foreach my $p (@pages) { + my $u=URI->new(encode_utf8(urlto($p, "", 1))); + my $pcontent = absolute_urls(get_inline_content($p, $page), $url); + + $itemtemplate->param( + title => pagetitle(basename($p)), + url => $u, + permalink => $u, + cdate_822 => date_822($pagectime{$p}), + mdate_822 => date_822($pagemtime{$p}), + cdate_3339 => date_3339($pagectime{$p}), + mdate_3339 => date_3339($pagemtime{$p}), + ); + + if (exists $pagestate{$p}) { + if (exists $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}) { + $itemtemplate->param(mdate_822 => date_822($pagestate{$p}{meta}{updated})); + $itemtemplate->param(mdate_3339 => date_3339($pagestate{$p}{meta}{updated})); + } + } + + if ($itemtemplate->query(name => "enclosure")) { + my $file=$pagesources{$p}; + my $type=pagetype($file); + if (defined $type) { + $itemtemplate->param(content => $pcontent); + } + else { + my $size=(srcfile_stat($file))[8]; + my $mime="unknown"; + eval q{use File::MimeInfo}; + if (! $@) { + $mime = mimetype($file); + } + $itemtemplate->param( + enclosure => $u, + type => $mime, + length => $size, + ); + } + } + else { + $itemtemplate->param(content => $pcontent); + } + + run_hooks(pagetemplate => sub { + shift->(page => $p, destpage => $page, + template => $itemtemplate); + }); + + $content.=$itemtemplate->output; + $itemtemplate->clear_params; + + $lasttime = $pagemtime{$p} if $pagemtime{$p} > $lasttime; + } + + my $template=template_depends($feedtype."page.tmpl", $page, blind_cache => 1); + $template->param( + title => $page ne "index" ? pagetitle($page) : $config{wikiname}, + wikiname => $config{wikiname}, + pageurl => $url, + content => $content, + feeddesc => $feeddesc, + guid => $guid, + feeddate => date_3339($lasttime), + feedurl => $feedurl, + ); + run_hooks(pagetemplate => sub { + shift->(page => $page, destpage => $page, + template => $template); + }); + + return $template->output; +} + +sub pingurl (@) { + return unless @{$config{pingurl}} && %toping; + + eval q{require RPC::XML::Client}; + if ($@) { + debug(gettext("RPC::XML::Client not found, not pinging")); + return; + } + + # daemonize here so slow pings don't slow down wiki updates + defined(my $pid = fork) or error("Can't fork: $!"); + return if $pid; + chdir '/'; + POSIX::setsid() or error("Can't start a new session: $!"); + open STDIN, '/dev/null'; + open STDOUT, '>/dev/null'; + open STDERR, '>&STDOUT' or error("Can't dup stdout: $!"); + + # Don't need to keep a lock on the wiki as a daemon. + IkiWiki::unlockwiki(); + + foreach my $page (keys %toping) { + my $title=pagetitle(basename($page), 0); + my $url=urlto($page, "", 1); + foreach my $pingurl (@{$config{pingurl}}) { + debug("Pinging $pingurl for $page"); + eval { + my $client = RPC::XML::Client->new($pingurl); + my $req = RPC::XML::request->new('weblogUpdates.ping', + $title, $url); + my $res = $client->send_request($req); + if (! ref $res) { + error("Did not receive response to ping"); + } + my $r=$res->value; + if (! exists $r->{flerror} || $r->{flerror}) { + error("Ping rejected: ".(exists $r->{message} ? $r->{message} : "[unknown reason]")); + } + }; + if ($@) { + error "Ping failed: $@"; + } + } + } + + exit 0; # daemon done +} + + +sub rootpage (@) { + my %params=@_; + + my $rootpage; + if (exists $params{rootpage}) { + $rootpage=bestlink($params{page}, $params{rootpage}); + if (!length $rootpage) { + $rootpage=$params{rootpage}; + } + } + else { + $rootpage=$params{page}; + } + return $rootpage; +} + +1 diff --git a/IkiWiki/Plugin/link.pm b/IkiWiki/Plugin/link.pm new file mode 100644 index 000000000..1ba28eafd --- /dev/null +++ b/IkiWiki/Plugin/link.pm @@ -0,0 +1,176 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::link; + +use warnings; +use strict; +use IkiWiki 3.00; + +my $link_regexp; + +my $email_regexp = qr/^.+@.+\..+$/; +my $url_regexp = qr/^(?:[^:]+:\/\/|mailto:).*/i; + +sub import { + hook(type => "getsetup", id => "link", call => \&getsetup); + hook(type => "checkconfig", id => "link", call => \&checkconfig); + hook(type => "linkify", id => "link", call => \&linkify); + hook(type => "scan", id => "link", call => \&scan); + hook(type => "renamepage", id => "link", call => \&renamepage); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + section => "core", + }, +} + +sub checkconfig () { + if ($config{prefix_directives}) { + $link_regexp = qr{ + \[\[(?=[^!]) # beginning of link + (?: + ([^\]\|]+) # 1: link text + \| # followed by '|' + )? # optional + + ([^\n\r\]#]+) # 2: page to link to + (?: + \# # '#', beginning of anchor + ([^\s\]]+) # 3: anchor text + )? # optional + + \]\] # end of link + }x; + } + else { + $link_regexp = qr{ + \[\[ # beginning of link + (?: + ([^\]\|\n\s]+) # 1: link text + \| # followed by '|' + )? # optional + + ([^\s\]#]+) # 2: page to link to + (?: + \# # '#', beginning of anchor + ([^\s\]]+) # 3: anchor text + )? # optional + + \]\] # end of link + }x; + } +} + +sub is_externallink ($$;$) { + my $page = shift; + my $url = shift; + my $anchor = shift; + + if (defined $anchor) { + $url.="#".$anchor; + } + + return ($url =~ /$url_regexp|$email_regexp/) +} + +sub externallink ($$;$) { + my $url = shift; + my $anchor = shift; + my $pagetitle = shift; + + if (defined $anchor) { + $url.="#".$anchor; + } + + # build pagetitle + if (! $pagetitle) { + $pagetitle = $url; + # use only the email address as title for mailto: urls + if ($pagetitle =~ /^mailto:.*/) { + $pagetitle =~ s/^mailto:([^?]+).*/$1/; + } + } + + if ($url !~ /$url_regexp/) { + # handle email addresses (without mailto:) + $url = "mailto:" . $url; + } + + return "$pagetitle"; +} + +sub linkify (@) { + my %params=@_; + my $page=$params{page}; + my $destpage=$params{destpage}; + + $params{content} =~ s{(\\?)$link_regexp}{ + defined $2 + ? ( $1 + ? "[[$2|$3".(defined $4 ? "#$4" : "")."]]" + : is_externallink($page, $3, $4) + ? externallink($3, $4, $2) + : htmllink($page, $destpage, linkpage($3), + anchor => $4, linktext => pagetitle($2))) + : ( $1 + ? "[[$3".(defined $4 ? "#$4" : "")."]]" + : is_externallink($page, $3, $4) + ? externallink($3, $4) + : htmllink($page, $destpage, linkpage($3), + anchor => $4)) + }eg; + + return $params{content}; +} + +sub scan (@) { + my %params=@_; + my $page=$params{page}; + my $content=$params{content}; + + while ($content =~ /(? "getsetup", id => "linkmap", call => \&getsetup); + hook(type => "preprocess", id => "linkmap", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +my $mapnum=0; + +sub preprocess (@) { + my %params=@_; + + $params{pages}="*" unless defined $params{pages}; + + $mapnum++; + my $connected=IkiWiki::yesno($params{connected}); + + # Get all the items to map. + my %mapitems = map { $_ => urlto($_, $params{destpage}) } + pagespec_match_list($params{page}, $params{pages}, + # update when a page is added or removed, or its + # links change + deptype => deptype("presence", "links")); + + my $dest=$params{page}."/linkmap.png"; + + # Use ikiwiki's function to create the file, this makes sure needed + # subdirs are there and does some sanity checking. + will_render($params{page}, $dest); + writefile($dest, $config{destdir}, ""); + + # Run dot to create the graphic and get the map data. + my $pid; + my $sigpipe=0; + $SIG{PIPE}=sub { $sigpipe=1 }; + $pid=open2(*IN, *OUT, "dot -Tpng -o '$config{destdir}/$dest' -Tcmapx"); + + # open2 doesn't respect "use open ':utf8'" + binmode (IN, ':utf8'); + binmode (OUT, ':utf8'); + + print OUT "digraph linkmap$mapnum {\n"; + print OUT "concentrate=true;\n"; + print OUT "charset=\"utf-8\";\n"; + print OUT "ratio=compress;\nsize=\"".($params{width}+0).", ".($params{height}+0)."\";\n" + if defined $params{width} and defined $params{height}; + my %shown; + my $show=sub { + my $item=shift; + if (! $shown{$item}) { + print OUT "\"$item\" [shape=box,href=\"$mapitems{$item}\"];\n"; + $shown{$item}=1; + } + }; + foreach my $item (keys %mapitems) { + $show->($item) unless $connected; + foreach my $link (map { bestlink($item, $_) } @{$links{$item}}) { + next unless length $link and $mapitems{$link}; + foreach my $endpoint ($item, $link) { + $show->($endpoint); + } + print OUT "\"$item\" -> \"$link\";\n"; + } + } + print OUT "}\n"; + close OUT || error gettext("failed to run dot"); + + local $/=undef; + my $ret="\"".gettext("linkmap").\n". + ; + close IN || error gettext("failed to run dot"); + + waitpid $pid, 0; + if ($?) { + error gettext("failed to run dot"); + } + $SIG{PIPE}="DEFAULT"; + error gettext("failed to run dot") if $sigpipe; + + return $ret; +} + +1 diff --git a/IkiWiki/Plugin/listdirectives.pm b/IkiWiki/Plugin/listdirectives.pm new file mode 100644 index 000000000..835e25388 --- /dev/null +++ b/IkiWiki/Plugin/listdirectives.pm @@ -0,0 +1,100 @@ +#!/usr/bin/perl +# Ikiwiki listdirectives plugin. +package IkiWiki::Plugin::listdirectives; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + add_underlay("directives"); + hook(type => "getsetup", id => "listdirectives", call => \&getsetup); + hook(type => "checkconfig", id => "listdirectives", call => \&checkconfig); + hook(type => "needsbuild", id => "listdirectives", call => \&needsbuild); + hook(type => "preprocess", id => "listdirectives", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, + directive_description_dir => { + type => "string", + description => "directory in srcdir that contains directive descriptions", + example => "ikiwiki/directive", + safe => 1, + rebuild => 1, + }, +} + +my @fulllist; +my @shortlist; +my $pluginstring; + +sub checkconfig () { + if (! defined $config{directive_description_dir}) { + $config{directive_description_dir} = "ikiwiki/directive"; + } + else { + $config{directive_description_dir} =~ s/\/+$//; + } +} + +sub needsbuild (@) { + my $needsbuild=shift; + + @fulllist = grep { ! /^_/ } sort keys %{$IkiWiki::hooks{preprocess}}; + @shortlist = grep { ! $IkiWiki::hooks{preprocess}{$_}{shortcut} } @fulllist; + $pluginstring = join(' ', @shortlist) . " : " . join(' ', @fulllist); + + foreach my $page (keys %pagestate) { + if (exists $pagestate{$page}{listdirectives}{shown}) { + if ($pagestate{$page}{listdirectives}{shown} ne $pluginstring) { + push @$needsbuild, $pagesources{$page}; + } + if (exists $pagesources{$page} && + grep { $_ eq $pagesources{$page} } @$needsbuild) { + # remove state, will be re-added if + # the [[!listdirectives]] is still there during the + # rebuild + delete $pagestate{$page}{listdirectives}{shown}; + } + } + } + + return $needsbuild; +} + +sub preprocess (@) { + my %params=@_; + + $pagestate{$params{destpage}}{listdirectives}{shown}=$pluginstring; + + my @pluginlist; + + if (defined $params{generated}) { + @pluginlist = @fulllist; + } + else { + @pluginlist = @shortlist; + } + + my $result = '
    '; + + foreach my $plugin (@pluginlist) { + $result .= '
  • '; + my $link=linkpage($config{directive_description_dir}."/".$plugin); + add_depends($params{page}, $link, deptype("presence")); + $result .= htmllink($params{page}, $params{destpage}, $link); + $result .= '
  • '; + } + + $result .= "
"; + + return $result; +} + +1 diff --git a/IkiWiki/Plugin/localstyle.pm b/IkiWiki/Plugin/localstyle.pm new file mode 100644 index 000000000..111f4dc30 --- /dev/null +++ b/IkiWiki/Plugin/localstyle.pm @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +package IkiWiki::Plugin::localstyle; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "localstyle", call => \&getsetup); + hook(type => "pagetemplate", id => "localstyle", call => \&pagetemplate); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, + }, +} + +sub pagetemplate (@) { + my %params=@_; + + my $template=$params{template}; + + if ($template->query(name => "local_css")) { + my $best=bestlink($params{page}, 'local.css'); + if ($best) { + $template->param(local_css => $best); + } + } +} + +1 diff --git a/IkiWiki/Plugin/lockedit.pm b/IkiWiki/Plugin/lockedit.pm new file mode 100644 index 000000000..5b50fd115 --- /dev/null +++ b/IkiWiki/Plugin/lockedit.pm @@ -0,0 +1,58 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::lockedit; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "lockedit", call => \&getsetup); + hook(type => "canedit", id => "lockedit", call => \&canedit); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 0, + section => "auth", + }, + locked_pages => { + type => "pagespec", + example => "!*/Discussion", + description => "PageSpec controlling which pages are locked", + link => "ikiwiki/PageSpec", + safe => 1, + rebuild => 0, + }, +} + +sub canedit ($$) { + my $page=shift; + my $cgi=shift; + my $session=shift; + + my $user=$session->param("name"); + return undef if defined $user && IkiWiki::is_admin($user); + + if (defined $config{locked_pages} && length $config{locked_pages} && + pagespec_match($page, $config{locked_pages}, + user => $session->param("name"), + ip => $session->remote_addr(), + )) { + if ((! defined $user || + ! IkiWiki::userinfo_get($session->param("name"), "regdate")) && + exists $IkiWiki::hooks{auth}) { + return sub { IkiWiki::needsignin($cgi, $session) }; + } + else { + return sprintf(gettext("%s is locked and cannot be edited"), + htmllink("", "", $page, noimageinline => 1)); + + } + } + + return undef; +} + +1 diff --git a/IkiWiki/Plugin/map.pm b/IkiWiki/Plugin/map.pm new file mode 100644 index 000000000..4a9bf58db --- /dev/null +++ b/IkiWiki/Plugin/map.pm @@ -0,0 +1,163 @@ +#!/usr/bin/perl +# +# Produce a hierarchical map of links. +# +# by Alessandro Dotti Contra +# +# Revision: 0.2 +package IkiWiki::Plugin::map; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "map", call => \&getsetup); + hook(type => "preprocess", id => "map", call => \&preprocess); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, +} + +sub preprocess (@) { + my %params=@_; + $params{pages}="*" unless defined $params{pages}; + + # Needs to update whenever a page is added or removed (or in some + # cases, when its content changes, if show= is specified). + my $deptype=deptype(exists $params{show} ? "content" : "presence"); + + my $common_prefix; + + # Get all the items to map. + my %mapitems; + foreach my $page (pagespec_match_list($params{page}, $params{pages}, + deptype => $deptype)) { + if (exists $params{show} && + exists $pagestate{$page} && + exists $pagestate{$page}{meta}{$params{show}}) { + $mapitems{$page}=$pagestate{$page}{meta}{$params{show}}; + } + else { + $mapitems{$page}=''; + } + # Check for a common prefix. + if (! defined $common_prefix) { + $common_prefix=$page; + } + elsif (length $common_prefix && + $page !~ /^\Q$common_prefix\E(\/|$)/) { + my @a=split(/\//, $page); + my @b=split(/\//, $common_prefix); + $common_prefix=""; + while (@a && @b && $a[0] eq $b[0]) { + if (length $common_prefix) { + $common_prefix.="/"; + } + $common_prefix.=shift(@a); + shift @b; + } + } + } + + # Common prefix should not be a page in the map. + while (defined $common_prefix && length $common_prefix && + exists $mapitems{$common_prefix}) { + $common_prefix=IkiWiki::dirname($common_prefix); + } + + # Set this to 1 or more spaces to pretty-print maps for debugging + my $spaces = ""; + + # Create the map. + my $parent=""; + my $indent=0; + my $openli=0; + my $addparent=""; + my $map = "
\n"; + + if (! keys %mapitems) { + # return empty div for empty map + $map .= "
\n"; + return $map; + } + else { + $map .= "
    \n"; + } + + foreach my $item (sort keys %mapitems) { + my @linktext = (length $mapitems{$item} ? (linktext => $mapitems{$item}) : ()); + $item=~s/^\Q$common_prefix\E\/// + if defined $common_prefix && length $common_prefix; + my $depth = ($item =~ tr/\//\//) + 1; + my $baseitem=IkiWiki::dirname($item); + while (length $parent && length $baseitem && $baseitem !~ /^\Q$parent\E(\/|$)/) { + $parent=IkiWiki::dirname($parent); + last if length $addparent && $baseitem =~ /^\Q$addparent\E(\/|$)/; + $addparent=""; + $map .= ($spaces x $indent) . "\n"; + if ($indent > 1) { + $map .= ($spaces x $indent) . "
\n"; + } + $indent--; + } + while ($depth < $indent) { + $map .= ($spaces x $indent) . "\n"; + if ($indent > 1) { + $map .= ($spaces x $indent) . "\n"; + } + $indent--; + } + my @bits=split("/", $item); + my $p=""; + $p.="/".shift(@bits) for 1..$indent; + while ($depth > $indent) { + $indent++; + if ($indent > 1) { + $map .= ($spaces x $indent) . "
    \n"; + } + if ($depth > $indent) { + $p.="/".shift(@bits); + $addparent=$p; + $addparent=~s/^\///; + $map .= ($spaces x $indent) . "
  • \n"; + $map .= ($spaces x $indent) + .htmllink($params{page}, $params{destpage}, + "/".$common_prefix.$p, class => "mapparent", + noimageinline => 1) + ."\n"; + $openli=1; + } + else { + $openli=0; + } + } + $map .= ($spaces x $indent) . "
  • \n" if $openli; + $map .= ($spaces x $indent) . "
  • \n"; + $map .= ($spaces x $indent) + .htmllink($params{page}, $params{destpage}, + "/".$common_prefix."/".$item, + @linktext, + class => "mapitem", noimageinline => 1) + ."\n"; + $openli=1; + $parent=$item; + } + while ($indent > 0) { + $map .= ($spaces x $indent) . "
  • \n"; + $indent--; + $map .= ($spaces x $indent) . "
\n"; + } + $map =~ s{\n *\n *
    \n}{\n}gs; + $map =~ s{}{}g; + $map .= "
\n"; + return $map; +} + +1 diff --git a/IkiWiki/Plugin/mdwn.pm b/IkiWiki/Plugin/mdwn.pm new file mode 100644 index 000000000..430194bff --- /dev/null +++ b/IkiWiki/Plugin/mdwn.pm @@ -0,0 +1,117 @@ +#!/usr/bin/perl +# Markdown markup language +package IkiWiki::Plugin::mdwn; + +use warnings; +use strict; +use IkiWiki 3.00; + +sub import { + hook(type => "getsetup", id => "mdwn", call => \&getsetup); + hook(type => "htmlize", id => "mdwn", call => \&htmlize, longname => "Markdown"); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => 1, # format plugin + section => "format", + }, + multimarkdown => { + type => "boolean", + example => 0, + description => "enable multimarkdown features?", + safe => 1, + rebuild => 1, + }, + nodiscount => { + type => "boolean", + example => 0, + description => "disable use of markdown discount?", + safe => 1, + rebuild => 1, + }, +} + +my $markdown_sub; +sub htmlize (@) { + my %params=@_; + my $content = $params{content}; + + if (! defined $markdown_sub) { + # Markdown is forked and splintered upstream and can be + # available in a variety of forms. Support them all. + no warnings 'once'; + $blosxom::version="is a proper perl module too much to ask?"; + use warnings 'all'; + + if (exists $config{multimarkdown} && $config{multimarkdown}) { + eval q{use Text::MultiMarkdown}; + if ($@) { + debug(gettext("multimarkdown is enabled, but Text::MultiMarkdown is not installed")); + } + else { + $markdown_sub=sub { + Text::MultiMarkdown::markdown(shift, {use_metadata => 0}); + } + } + } + if (! defined $markdown_sub && + (! exists $config{nodiscount} || ! $config{nodiscount})) { + eval q{use Text::Markdown::Discount}; + if (! $@) { + $markdown_sub=sub { + my $t=shift; + # Workaround for discount binding bug + # https://rt.cpan.org/Ticket/Display.html?id=73657 + return "" if $t=~/^\s*$/; + # Workaround for discount's eliding + # of