From bf55a7fbb1f27ca815ac1e2ee04867686851a134 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 8 Jun 2009 21:40:06 -0400 Subject: meta: Add openid delegate parameter to allow delegating only openid or openid2. --- IkiWiki/Plugin/meta.pm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/meta.pm b/IkiWiki/Plugin/meta.pm index cc5455d64..b2295923e 100644 --- a/IkiWiki/Plugin/meta.pm +++ b/IkiWiki/Plugin/meta.pm @@ -163,17 +163,22 @@ sub preprocess (@) { "\" type=\"text/css\" />"; } elsif ($key eq 'openid') { + my $delegate=0; # both by default + if (exists $params{delegate}) { + $delegate = 1 if lc $params{delegate} eq 'openid'; + $delegate = 2 if lc $params{delegate} eq 'openid2'; + } if (exists $params{server} && safeurl($params{server})) { push @{$metaheaders{$page}}, ''; + '" rel="openid.server" />' if $delegate ne 2; push @{$metaheaders{$page}}, ''; + '" rel="openid2.provider" />' if $delegate ne 1; } if (safeurl($value)) { push @{$metaheaders{$page}}, ''; + '" rel="openid.delegate" />' if $delegate ne 2; push @{$metaheaders{$page}}, ''; + '" rel="openid2.local_id" />' if $delegate ne 1; } if (exists $params{"xrds-location"} && safeurl($params{"xrds-location"})) { push @{$metaheaders{$page}}, ' Date: Tue, 9 Jun 2009 15:39:00 -0400 Subject: Disable the Preferences link if no plugin with an auth hook is enabled. --- IkiWiki/Plugin/passwordauth.pm | 11 +++++++++-- IkiWiki/Render.pm | 3 ++- debian/changelog | 1 + doc/todo/Allow_disabling_edit_and_preferences_links.mdwn | 10 ++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/passwordauth.pm b/IkiWiki/Plugin/passwordauth.pm index 90e2ca564..8cf5af51e 100644 --- a/IkiWiki/Plugin/passwordauth.pm +++ b/IkiWiki/Plugin/passwordauth.pm @@ -8,9 +8,10 @@ use IkiWiki 3.00; sub import { hook(type => "getsetup", id => "passwordauth", "call" => \&getsetup); - hook(type => "formbuilder_setup", id => "passwordauth", call => \&formbuilder_setup); - hook(type => "formbuilder", id => "passwordauth", call => \&formbuilder); + hook(type => "formbuilder_setup", id => "passwordauth", call => \&formbuilder_setup); + hook(type => "formbuilder", id => "passwordauth", call => \&formbuilder); hook(type => "sessioncgi", id => "passwordauth", call => \&sessioncgi); + hook(type => "auth", id => "passwordauth", call => \&auth); } sub getsetup () { @@ -337,4 +338,10 @@ sub sessioncgi ($$) { } } +sub auth ($$) { + # While this hook is not currently used, it needs to exist + # so ikiwiki knows that the wiki supports logins, and will + # enable the Preferences page. +} + 1 diff --git a/IkiWiki/Render.pm b/IkiWiki/Render.pm index f4de19378..2da18738d 100644 --- a/IkiWiki/Render.pm +++ b/IkiWiki/Render.pm @@ -65,7 +65,8 @@ sub genpage ($$) { if (length $config{cgiurl}) { $template->param(editurl => cgiurl(do => "edit", page => $page)) if IkiWiki->can("cgi_editpage"); - $template->param(prefsurl => cgiurl(do => "prefs")); + $template->param(prefsurl => cgiurl(do => "prefs")) + if exists $hooks{auth}; $actions++; } diff --git a/debian/changelog b/debian/changelog index dd24e0bba..06bed479b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -22,6 +22,7 @@ ikiwiki (3.141) UNRELEASED; urgency=low is not available. Closes: #532285 * meta: Add openid delegate parameter to allow delegating only openid or openid2. + * Disable the Preferences link if no plugin with an auth hook is enabled. -- Joey Hess Tue, 02 Jun 2009 17:03:41 -0400 diff --git a/doc/todo/Allow_disabling_edit_and_preferences_links.mdwn b/doc/todo/Allow_disabling_edit_and_preferences_links.mdwn index 5b9cc8742..4277ae899 100644 --- a/doc/todo/Allow_disabling_edit_and_preferences_links.mdwn +++ b/doc/todo/Allow_disabling_edit_and_preferences_links.mdwn @@ -67,3 +67,13 @@ Patch: >>> Adding a new `canlogin` hook looks like overkill to me. [[Joey]], how >>> about making registration of the `auth` hook mandatory for all plugins >>> making sense of the "Preferences" link? --[[Lunar]] + +>>>> Hmm, using the `auth` hook existance does seem like a nice solution. +>>>> While splitting the preferences code out into its own plugin is +>>>> easily enough done, it has the minor problem of being yet another +>>>> file nearly all ikiwikis will have to load, and also, prefs would +>>>> have to be disabled manually. So I like that using the hook would +>>>> cause it to auto-disable if nothing uses it. It's a bit ugly that +>>>> passwordauth doesn't need an auth hook (it could be reorged to +>>>> use it instead of formbuilder, maybe) and would probably just have an +>>>> empty one. Thanks for the idea. --[[Joey]] [[done]] -- cgit v1.2.3 From 90b4d079605b72bb50d1da41402d994960e10937 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 12 Jun 2009 19:24:47 -0400 Subject: aggregate: Fix storing of changed md5. The aggregate state merge code neglected to merge changes to the md5 field of an item. Therefore, if an item's md5 changed after initial aggregation, it would be updated, and rewritten, each time thereafter. This was wasteful and indirectly led to some expire problems. --- IkiWiki/Plugin/aggregate.pm | 6 ++++++ debian/changelog | 1 + 2 files changed, 7 insertions(+) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/aggregate.pm b/IkiWiki/Plugin/aggregate.pm index e1baae666..60c292d52 100644 --- a/IkiWiki/Plugin/aggregate.pm +++ b/IkiWiki/Plugin/aggregate.pm @@ -404,6 +404,7 @@ sub mergestate () { } # 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 @@ -412,6 +413,11 @@ sub mergestate () { if (! exists $guids{$guid}) { $guids{$guid}=$myguids{$guid}; } + else { + foreach my $field (qw{md5}) { + $guids{$guid}->{$field}=$myguids{$guid}->{$field}; + } + } } } diff --git a/debian/changelog b/debian/changelog index dbf8dac88..6444fb8ba 100644 --- a/debian/changelog +++ b/debian/changelog @@ -24,6 +24,7 @@ ikiwiki (3.141) UNRELEASED; urgency=low openid or openid2. * Disable the Preferences link if no plugin with an auth hook is enabled. * Updated French translation. Closes: #532654 + * aggregate: Fix storing of changed md5. -- Joey Hess Tue, 02 Jun 2009 17:03:41 -0400 -- cgit v1.2.3 From 91513466877df4329567f2cc73b6719999394258 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 12 Jun 2009 19:31:18 -0400 Subject: aggregate: Avoid resetting ctime when an item md5 changes. Besides being wrong to do, this could lead to the wrong item being expired, as follows: If B is added and at the same time A is changed, then A's ctime may be set to the current time, while B's is set to its creation time. Thus the new item, A, is incorrectly removed as older. (This interacted especially badly with the bug fixed by 90b4d079605b72bb50d1da41402d994960e10937.) --- IkiWiki/Plugin/aggregate.pm | 6 ++++-- debian/changelog | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/aggregate.pm b/IkiWiki/Plugin/aggregate.pm index 60c292d52..5a9eb433d 100644 --- a/IkiWiki/Plugin/aggregate.pm +++ b/IkiWiki/Plugin/aggregate.pm @@ -650,11 +650,13 @@ sub add_page (@) { # creation time on record for the new page. utime $mtime, $mtime, "$config{srcdir}/".htmlfn($guid->{page}); # Store it in pagectime for expiry code to use also. - $IkiWiki::pagectime{$guid->{page}}=$mtime; + $IkiWiki::pagectime{$guid->{page}}=$mtime + unless exists $IkiWiki::pagectime{$guid->{page}}; } else { # Dummy value for expiry code. - $IkiWiki::pagectime{$guid->{page}}=time; + $IkiWiki::pagectime{$guid->{page}}=time + unless exists $IkiWiki::pagectime{$guid->{page}}; } } diff --git a/debian/changelog b/debian/changelog index 6444fb8ba..9b96eee81 100644 --- a/debian/changelog +++ b/debian/changelog @@ -25,6 +25,7 @@ ikiwiki (3.141) UNRELEASED; urgency=low * Disable the Preferences link if no plugin with an auth hook is enabled. * Updated French translation. Closes: #532654 * aggregate: Fix storing of changed md5. + * aggregate: Avoid resetting ctime when an item md5 changes. -- Joey Hess Tue, 02 Jun 2009 17:03:41 -0400 -- cgit v1.2.3 From a648c439f3467571374daf597e9b3a659ea2008f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 16 Jun 2009 17:15:06 +0100 Subject: img plugin: do not emit a redundant double-quote before alt attribute --- IkiWiki/Plugin/img.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/img.pm b/IkiWiki/Plugin/img.pm index a697fea19..a186abdfc 100644 --- a/IkiWiki/Plugin/img.pm +++ b/IkiWiki/Plugin/img.pm @@ -121,7 +121,7 @@ sub preprocess (@) { my $imgtag=''.$params{alt}.' Date: Wed, 1 Jul 2009 13:45:28 -0400 Subject: typo --- IkiWiki/Plugin/prettydate.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/prettydate.pm b/IkiWiki/Plugin/prettydate.pm index e155dd39b..82d8a3df3 100644 --- a/IkiWiki/Plugin/prettydate.pm +++ b/IkiWiki/Plugin/prettydate.pm @@ -33,7 +33,7 @@ sub default_timetable { gettext("%A evening"), # 6 "", # 7 gettext("late %A evening"), # 8 - "", # 9 # 9 + "", # 9 gettext("%A night"), # 10 "", # 11 ]; -- cgit v1.2.3 From ea4686a565067b8c9a4cd1f1a76c205c7a38bfed Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 21 Jun 2009 12:12:30 +0100 Subject: Update IkiWiki::openiduser to work with Net::OpenID 2.x openiduser previously used a constructor that no longer works in 2.x. However, all we actually want is the (undocumented) DisplayOfURL function that is invoked by the display method, so try to use that. (cherry picked from commit c3dd0ff5c7c10743107f203a5b456fdcd1b171df) --- IkiWiki/Plugin/openid.pm | 14 ++++++++++++-- debian/changelog | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm index 5424c57e2..87569915b 100644 --- a/IkiWiki/Plugin/openid.pm +++ b/IkiWiki/Plugin/openid.pm @@ -189,8 +189,18 @@ sub openiduser ($) { if ($user =~ m!^https?://! && eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) { - my $oid=Net::OpenID::VerifiedIdentity->new(identity => $user); - my $display=$oid->display; + my $display; + + if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) { + # this works in at least 2.x + $display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user); + } + else { + # this only works in 1.x + 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 !~ /\[/) { diff --git a/debian/changelog b/debian/changelog index c119dea8e..bb33f25f3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,8 @@ ikiwiki (3.1415) UNRELEASED; urgency=low * img: Fix extra double quote with alt text. (smcv) * Updated French debconf templates translation. Closes: #535103 + * openid: Support Net::OpenID 2.x when pretty-printing + openids. (smcv) -- Joey Hess Tue, 16 Jun 2009 15:08:31 -0400 -- cgit v1.2.3 From feae031a80805550b38cb4fd9694d01ce7ef0627 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 7 Jul 2009 16:31:18 -0400 Subject: highlight: Fix utf-8 encoding bug. Closes: #535028 --- IkiWiki/Plugin/highlight.pm | 3 ++- debian/changelog | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/highlight.pm b/IkiWiki/Plugin/highlight.pm index 20f79ef57..9bdde85ae 100644 --- a/IkiWiki/Plugin/highlight.pm +++ b/IkiWiki/Plugin/highlight.pm @@ -4,6 +4,7 @@ package IkiWiki::Plugin::highlight; use warnings; use strict; use IkiWiki 3.00; +use Encode; # locations of highlight's files my $filetypes="/etc/highlight/filetypes.conf"; @@ -69,7 +70,7 @@ sub htmlizefallback { return; } - return highlight($langfile, shift); + return Encode::decode_utf8(highlight($langfile, shift)); } my %ext2lang; diff --git a/debian/changelog b/debian/changelog index bb33f25f3..239b22b42 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,11 +1,12 @@ -ikiwiki (3.1415) UNRELEASED; urgency=low +ikiwiki (3.1415) unstable; urgency=low * img: Fix extra double quote with alt text. (smcv) * Updated French debconf templates translation. Closes: #535103 * openid: Support Net::OpenID 2.x when pretty-printing openids. (smcv) + * highlight: Fix utf-8 encoding bug. Closes: #535028 - -- Joey Hess Tue, 16 Jun 2009 15:08:31 -0400 + -- Joey Hess Tue, 07 Jul 2009 16:25:05 -0400 ikiwiki (3.141) unstable; urgency=low -- cgit v1.2.3 From 0c6a47e9e4c6a5508868b730bed0ac2d0c358ae4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 8 Jul 2009 13:13:23 -0400 Subject: svn: Fix rcs_rename to properly scope call to dirname. --- IkiWiki/Plugin/svn.pm | 4 ++-- debian/changelog | 6 ++++++ ...enaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/svn.pm b/IkiWiki/Plugin/svn.pm index fe55e7d08..06b987f51 100644 --- a/IkiWiki/Plugin/svn.pm +++ b/IkiWiki/Plugin/svn.pm @@ -243,10 +243,10 @@ sub rcs_rename ($$) { if (-d "$config{srcdir}/.svn") { # Add parent directory for $dest - my $parent=dirname($dest); + my $parent=IkiWiki::dirname($dest); if (! -d "$config{srcdir}/$parent/.svn") { while (! -d "$config{srcdir}/$parent/.svn") { - $parent=dirname($dest); + $parent=IkiWiki::dirname($dest); } if (system("svn", "add", "--quiet", "$config{srcdir}/$parent") != 0) { warn("svn add $parent failed\n"); diff --git a/debian/changelog b/debian/changelog index 239b22b42..e83e570d3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +ikiwiki (3.1416) UNRELEASED; urgency=low + + * svn: Fix rcs_rename to properly scope call to dirname. + + -- Joey Hess Wed, 08 Jul 2009 13:10:38 -0400 + ikiwiki (3.1415) unstable; urgency=low * img: Fix extra double quote with alt text. (smcv) diff --git a/doc/bugs/Renaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn b/doc/bugs/Renaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn index 4f2257891..1a737df0a 100644 --- a/doc/bugs/Renaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn +++ b/doc/bugs/Renaming_a_file_via_the_web_is_failing_when_using_subversion.mdwn @@ -22,3 +22,7 @@ Applying the following patch fixed it: if (system("svn", "add", "--quiet", "$config{srcdir}/$parent") != 0) { warn("svn add $parent failed\n"); + +> Thank you very much for the patch, which I've applied. I wonder how +> that snuck in (aside from the obvious, that the svn plugin is not often +> used and the code was added w/o being tested..). [[done]] --[[Joey]] -- cgit v1.2.3 From e12b7f5e54730325c751a889ed2e08580b5ef6ba Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 10 Jul 2009 18:41:16 +0100 Subject: Move OpenID pretty-printing from openid plugin to core On various sites I have two IkiWiki instances running from the same repository: one accessible via http and only accepting openid logins, and one accessible via authenticated https and only accepting httpauth. The https version should still pretty-print OpenIDs seen in git history, even though it does not itself accept OpenID logins. --- IkiWiki.pm | 35 +++++++++++++++++++++++++++++++++++ IkiWiki/Plugin/openid.pm | 39 --------------------------------------- 2 files changed, 35 insertions(+), 39 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki.pm b/IkiWiki.pm index a0a61ac64..0cb3bf143 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -1063,6 +1063,41 @@ sub htmllink ($$$;@) { return "$linktext"; } +sub openiduser ($) { + my $user=shift; + + if ($user =~ m!^https?://! && + eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) { + my $display; + + if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) { + # this works in at least 2.x + $display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user); + } + else { + # this only works in 1.x + 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 userlink ($) { my $user=shift; diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm index 87569915b..dc0e0f48e 100644 --- a/IkiWiki/Plugin/openid.pm +++ b/IkiWiki/Plugin/openid.pm @@ -180,43 +180,4 @@ sub getobj ($$) { ); } -package IkiWiki; - -# This is not used by this plugin, but this seems the best place to put it. -# Used elsewhere to pretty-display the name of an openid user. -sub openiduser ($) { - my $user=shift; - - if ($user =~ m!^https?://! && - eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) { - my $display; - - if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) { - # this works in at least 2.x - $display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user); - } - else { - # this only works in 1.x - 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; -} - 1 -- cgit v1.2.3 From b1b7a2100f6b32ef6bf75e9992e10ed7d28f8525 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 11 Jul 2009 00:33:19 -0400 Subject: img: Pass the align parameter through to the generated img tag. --- IkiWiki/Plugin/img.pm | 1 + debian/changelog | 1 + doc/ikiwiki/directive/img.mdwn | 6 +++--- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'IkiWiki/Plugin') diff --git a/IkiWiki/Plugin/img.pm b/IkiWiki/Plugin/img.pm index a186abdfc..78e378917 100644 --- a/IkiWiki/Plugin/img.pm +++ b/IkiWiki/Plugin/img.pm @@ -123,6 +123,7 @@ sub preprocess (@) { '" height="'.$im->Get("height").'"'. (exists $params{alt} ? ' alt="'.$params{alt}.'"' : ''). (exists $params{title} ? ' title="'.$params{title}.'"' : ''). + (exists $params{align} ? ' align="'.$params{align}.'"' : ''). (exists $params{class} ? ' class="'.$params{class}.'"' : ''). (exists $params{id} ? ' id="'.$params{id}.'"' : ''). ' />'; diff --git a/debian/changelog b/debian/changelog index e83e570d3..5001031df 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ ikiwiki (3.1416) UNRELEASED; urgency=low * svn: Fix rcs_rename to properly scope call to dirname. + * img: Pass the align parameter through to the generated img tag. -- Joey Hess Wed, 08 Jul 2009 13:10:38 -0400 diff --git a/doc/ikiwiki/directive/img.mdwn b/doc/ikiwiki/directive/img.mdwn index 1d1f29bea..66efd008e 100644 --- a/doc/ikiwiki/directive/img.mdwn +++ b/doc/ikiwiki/directive/img.mdwn @@ -18,9 +18,9 @@ making the image smaller than the specified size. You can also specify only the width or the height, and the other value will be calculated based on it: "200x", "x200" -You can also pass `alt`, `title`, `class` and `id` parameters. These are -passed through unchanged to the html img tag. If you include a `caption` -parameter, the caption will be displayed centered beneath the image. +You can also pass `alt`, `title`, `class`, `align` and `id` parameters. +These are passed through unchanged to the html img tag. If you include a +`caption` parameter, the caption will be displayed centered beneath the image. The `link` parameter is used to control whether the scaled down image links to the full size version. By default it does; set "link=somepage" to link -- cgit v1.2.3