From 2ada6b09311374080ee1fc0d4c5025a8add978e6 Mon Sep 17 00:00:00 2001
From: Kenichi Ishigaki Dear visitor,
},
+$mess,
+
+HTML
+ $res->finalize;
+ };
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/App/Index.pm b/lib/pause_2025/PAUSE/Web/App/Index.pm
new file mode 100644
index 000000000..b56c65318
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/App/Index.pm
@@ -0,0 +1,61 @@
+package PAUSE::Web::App::Index;
+
+use Mojo::Base -base;
+use Plack::Request;
+use Plack::Response;
+use HTTP::Status qw/:constants/;
+
+sub to_app {
+ my $self = shift;
+
+ return sub {
+ my $req = Plack::Request->new(shift);
+ my $res = $self->dispatch($req);
+ return $res if ref $res;
+ [$res =~ /^\d+$/ ? $res : 500, [], [$res]];
+ };
+}
+
+
+sub dispatch {
+ my ($self, $req) = @_;
+
+ my $method = $req->method;
+ my $redir_to = $req->base;
+ my $is_ssl = $req->headers->header("X-pause-is-SSL") || 1;
+ if ($is_ssl) {
+ $redir_to->scheme("https");
+ }
+ if ($method eq "GET" && $redir_to->path eq "/" && $req->env->{QUERY_STRING}) {
+ my $args = $req->env->{QUERY_STRING};
+ # warn "Returning SERVER_ERROR: the_request[$the_request]uri[$uri]args[$args]";
+ # return SERVER_ERROR;
+ $redir_to->path("/pause/query");
+ $args =~ s|/$||;
+ $args =~ s|\s.*||;
+ $redir_to->query($args) if $args;
+ # warn "Statistics: Redirecting the_request[$the_request]redir_to[$redir_to]";
+ my $res = $req->new_response(HTTP_MOVED_PERMANENTLY);
+ $res->headers->header("Location", $redir_to);
+ return $res->finalize;
+ }
+
+ my $uri = $req->path;
+ #my $host = $r->server->server_hostname;
+ #my $args = $r->args;
+ #warn "index-uri[$uri]host[$host]args[$args]";
+ return HTTP_NOT_FOUND unless $uri eq "/" || $uri eq "/index.html";
+
+ #my(%redir) = (
+ # "/" => "query",
+ # "/index.html" => "query?ACTION=pause_05news",
+ # );
+ # $r->internal_redirect_handler("/query");
+ $redir_to->path("/pause/query");
+ $redir_to->query("ACTION=pause_05news") if $uri eq "/index.html";
+ my $res = $req->new_response(HTTP_MOVED_PERMANENTLY);
+ $res->headers->header("Location", $redir_to);
+ return $res->finalize;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Config.pm b/lib/pause_2025/PAUSE/Web/Config.pm
new file mode 100644
index 000000000..04d4e1230
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Config.pm
@@ -0,0 +1,656 @@
+package PAUSE::Web::Config;
+
+use Mojo::Base -base;
+use PAUSE;
+
+our %Actions = (
+ # PUBLIC
+ request_id => {
+ x_mojo_to => "public-request_id#request",
+ verb => "Request PAUSE account",
+ priv => "public",
+ cat => "00reg/01",
+ desc => "Apply for a PAUSE account.",
+ method => 'POST',
+ x_form => {
+ pause99_request_id_fullname => {form_type => "text_field"},
+ pause99_request_id_email => {form_type => "text_field"},
+ pause99_request_id_homepage => {form_type => "text_field"},
+ pause99_request_id_userid => {form_type => "text_field"},
+ pause99_request_id_rationale => {form_type => "text_area"},
+ SUBMIT_pause99_request_id_sub => {form_type => "submit_button"},
+ url => {form_type => "text_field"}, # bot-trap
+ },
+ },
+ mailpw => {
+ x_mojo_to => "public#mailpw",
+ verb => "Forgot Password?",
+ priv => "public",
+ cat => "00urg/01",
+ desc => "A passwordmailer that sends you a password that enables you to set a new password.",
+ method => 'POST',
+ x_csrf_protection => 1,
+ x_form => {
+ ABRA => {form_type => "hidden_field"},
+ pause99_mailpw_1 => {form_type => "text_field"},
+ pause99_mailpw_sub => {form_type => "submit_button"},
+ },
+ },
+ pause_04about => {
+ x_mojo_to => "public#about",
+ verb => "About PAUSE",
+ priv => "public",
+ cat => "01self/04a",
+ desc => "Same as modules/04pause.html on any CPAN server",
+ },
+ pause_04imprint => {
+ x_mojo_to => "public#imprint",
+ verb => "Imprint/Impressum",
+ priv => "public",
+ cat => "01self/06b",
+ },
+ pause_05news => {
+ x_mojo_to => "public#news",
+ verb => "PAUSE News",
+ priv => "public",
+ cat => "01self/05",
+ desc => "What's going on on PAUSE",
+ },
+ pause_06history => {
+ x_mojo_to => "public#history",
+ verb => "PAUSE History",
+ priv => "public",
+ cat => "01self/06",
+ desc => "Old News",
+ },
+ pause_namingmodules => {
+ x_mojo_to => "public#naming",
+ verb => "On The Naming of Modules",
+ priv => "public",
+ cat => "01self/04c",
+ desc => "A couple of suggestions that hopefully get you on track",
+ },
+ pause_operating_model => {
+ x_mojo_to => "public#operating_model",
+ verb => "PAUSE Operating Model",
+ priv => "public",
+ cat => "01self/04b",
+ desc => "How the PAUSE admins run PAUSE",
+ has_title => 1,
+ },
+ pause_privacy_policy => {
+ x_mojo_to => "public#privacy_policy",
+ verb => "PAUSE Privacy Policy",
+ priv => "public",
+ cat => "01self/04c",
+ desc => "Your rights as a user of PAUSE",
+ has_title => 1,
+ },
+ who_pumpkin => {
+ x_mojo_to => "public#pumpkin",
+ verb => "List of pumpkins",
+ priv => "public",
+ cat => "02serv/05",
+ desc => "A list, also available as YAML",
+ },
+ who_admin => {
+ x_mojo_to => "public#admin",
+ verb => "List of admins",
+ priv => "public",
+ cat => "02serv/06",
+ desc => "A list, also available as YAML",
+ },
+
+ # USER
+ # USER/FILES
+
+ add_uri => {
+ x_mojo_to => "user-uri#add",
+ verb => "Upload a file to CPAN",
+ priv => "user",
+ cat => "User/01Files/01up",
+ desc => "This is the heart of the Upload Server, the page most heavily used on PAUSE.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ CAN_MULTIPART => {form_type => "hidden_field"},
+ pause99_add_uri_subdirscrl => {form_type => "select_field"},
+ pause99_add_uri_subdirtext => {form_type => "text_field"},
+ pause99_add_uri_httpupload => {form_type => "file_field"},
+ SUBMIT_pause99_add_uri_httpupload => {form_type => "submit_button"},
+ pause99_add_uri_uri => {form_type => "text_field"},
+ SUBMIT_pause99_add_uri_uri => {form_type => "submit_button"},
+ },
+ },
+ show_files => {
+ x_mojo_to => "user-files#show",
+ verb => "Show my files",
+ priv => "user",
+ cat => "User/01Files/02show",
+ desc => "find . -ls resemblance",
+ },
+ edit_uris => {
+ x_mojo_to => "user#edit_uris",
+ verb => "Repair a Pending Upload",
+ priv => "user",
+ cat => "User/01Files/03rep",
+ desc => "When an upload you requested hangs for some reason, you can go here and edit the file to be uploaded.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ pause99_edit_uris_3 => {form_type => "select_field"}, # distributions
+ pause99_edit_uris_2 => {form_type => "submit_button"}, # select target
+ pause99_edit_uris_uri => {form_type => "text_field"}, # file to upload
+ pause99_edit_uris_4 => {form_type => "submit_button"}, # upload
+ },
+ },
+ delete_files => {
+ x_mojo_to => "user-files#delete",
+ verb => "Delete Files",
+ priv => "user",
+ cat => "User/01Files/04del",
+ desc => "Schedule files for deletion. There is a delay until the deletion really happens. Until then you can also undelete files here.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ SUBMIT_pause99_delete_files_delete => {form_type => "submit_button"},
+ SUBMIT_pause99_delete_files_undelete => {form_type => "submit_button"},
+ pause99_delete_files_FILE => {form_type => "check_box"},
+ }
+ },
+
+ # User/Permissions
+
+ peek_perms => {
+ x_mojo_to => "user-perms#peek",
+ verb => "View Permissions per module",
+ priv => "user",
+ cat => "User/04Permissions/11",
+ desc => "Whose uploads of what are being indexed on PAUSE",
+ x_form => {
+ pause99_peek_perms_by => {form_type => "select_field"},
+ pause99_peek_perms_query => {form_type => "text_field"},
+ pause99_peek_perms_sub => {form_type => "submit_button"},
+ },
+ display => 0,
+ },
+ share_perms => {
+ x_mojo_to => "user-perms#share",
+ verb => "Change Permissions per module",
+ priv => "user",
+ cat => "User/04Permissions/12",
+ desc => "Enable other users to upload a module for any of your namespaces, manage your own permissions.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ # pause99_edit_mod_3 => {form_type => "select_field"},
+ pause99_share_perms_pr_m => {form_type => "select_field"},
+ weaksubmit_pause99_share_perms_movepr => {form_type => "submit_button"},
+ weaksubmit_pause99_share_perms_remopr => {form_type => "submit_button"},
+ pause99_share_perms_makeco_m => {form_type => "select_field"},
+ weaksubmit_pause99_share_perms_makeco => {form_type => "submit_button"},
+ weaksubmit_pause99_share_perms_remocos => {form_type => "submit_button"},
+ pause99_share_perms_remome_m => {form_type => "select_field"},
+ weaksubmit_pause99_share_perms_remome => {form_type => "submit_button"},
+ },
+ x_form_movepr => {
+ pause99_share_perms_pr_m => {form_type => "select_field"},
+ pause99_share_perms_movepr_a => {form_type => "text_field"},
+ SUBMIT_pause99_share_perms_movepr => {form_type => "submit_button"},
+ },
+ x_form_remopr => {
+ pause99_share_perms_pr_m => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remopr => {form_type => "select_field"},
+ },
+ x_form_makeco => {
+ pause99_share_perms_makeco_m => {form_type => "select_field"},
+ pause99_share_perms_makeco_a => {form_type => "text_field"},
+ SUBMIT_pause99_share_perms_makeco => {form_type => "submit_button"},
+ },
+ x_form_remocos => {
+ pause99_share_perms_remocos_tuples => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remocos => {form_type => "submit_button"},
+ },
+ x_form_remome => {
+ pause99_share_perms_remome_m => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remome => {form_type => "submit_button"},
+ },
+ },
+ move_primary => {
+ x_mojo_to => "user-perms#move_primary",
+ verb => "Transfer Primary Permissions per module",
+ priv => "user",
+ cat => "User/04Permissions/13",
+ desc => "Transfer primary maintainership status to somebody else (you become co-maintainer).",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_share_perms_pr_m => {form_type => "select_field"},
+ pause99_share_perms_movepr_a => {form_type => "text_field"},
+ SUBMIT_pause99_share_perms_movepr => {form_type => "submit_button"},
+ },
+ display => 0,
+ },
+ remove_primary => {
+ x_mojo_to => "user-perms#remove_primary",
+ verb => "Put Up My Module(s) For Adoption per module",
+ priv => "user",
+ cat => "User/04Permissions/14",
+ desc => "Give up primary maintainership status (abandoning it without transfering it to someone else).",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_share_perms_pr_m => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remopr => {form_type => "select_field"},
+ },
+ display => 0,
+ },
+ make_comaint => {
+ x_mojo_to => "user-perms#make_comaint",
+ verb => "Add Comaintainers per module",
+ priv => "user",
+ cat => "User/04Permissions/15",
+ desc => "Make somebody else co-maintainer.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_share_perms_makeco_m => {form_type => "select_field"},
+ pause99_share_perms_makeco_a => {form_type => "text_field"},
+ SUBMIT_pause99_share_perms_makeco => {form_type => "submit_button"},
+ },
+ display => 0,
+ },
+ remove_comaint => {
+ x_mojo_to => "user-perms#remove_comaint",
+ verb => "Remove Comaintainers per module",
+ priv => "user",
+ cat => "User/04Permissions/16",
+ desc => "Remove a co-maintainer.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_share_perms_remocos_tuples => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remocos => {form_type => "submit_button"},
+ },
+ display => 0,
+ },
+ giveup_comaint => {
+ x_mojo_to => "user-perms#giveup_comaint",
+ verb => "Give up (Module's) co-maintainership status",
+ priv => "user",
+ cat => "User/04Permissions/17",
+ desc => "Give up co-maintainership status.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_share_perms_remome_m => {form_type => "select_field"},
+ SUBMIT_pause99_share_perms_remome => {form_type => "submit_button"},
+ },
+ display => 0,
+ },
+ peek_dist_perms => {
+ x_mojo_to => "user-distperms#peek",
+ verb => "View Permissions",
+ priv => "user",
+ cat => "User/04Permissions/01",
+ desc => "Whose uploads of what are being indexed on PAUSE",
+ x_form => {
+ pause99_peek_dist_perms_by => {form_type => "select_field"},
+ pause99_peek_dist_perms_query => {form_type => "text_field"},
+ pause99_peek_dist_perms_sub => {form_type => "submit_button"},
+ },
+ },
+ move_dist_primary => {
+ x_mojo_to => "user-distperms#move_dist_primary",
+ verb => "Transfer Primary Permissions",
+ priv => "user",
+ cat => "User/04Permissions/02",
+ desc => "Transfer distribution's primary maintainership status to somebody else (you become co-maintainer).",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_make_dist_primary_d => {form_type => "select_field"},
+ pause99_make_dist_primary_a => {form_type => "text_field"},
+ SUBMIT_pause99_make_dist_primary => {form_type => "submit_button"},
+ },
+ },
+ remove_dist_primary => {
+ x_mojo_to => "user-distperms#remove_dist_primary",
+ verb => "Put Up My Distribution(s) For Adoption",
+ priv => "user",
+ cat => "User/04Permissions/03",
+ desc => "Give up distribution's primary maintainership status (abandoning it without transfering it to someone else).",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_remove_dist_primary_d => {form_type => "select_field"},
+ SUBMIT_pause99_remove_dist_primary => {form_type => "select_field"},
+ },
+ },
+ make_dist_comaint => {
+ x_mojo_to => "user-distperms#make_dist_comaint",
+ verb => "Add Comaintainers",
+ priv => "user",
+ cat => "User/04Permissions/04",
+ desc => "Make somebody else co-maintainer of a distribution.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_make_dist_comaint_d => {form_type => "select_field"},
+ pause99_make_dist_comaint_a => {form_type => "text_field"},
+ SUBMIT_pause99_make_dist_comaint => {form_type => "submit_button"},
+ },
+ },
+ remove_dist_comaint => {
+ x_mojo_to => "user-distperms#remove_dist_comaint",
+ verb => "Remove Comaintainers",
+ priv => "user",
+ cat => "User/04Permissions/05",
+ desc => "Remove a distribution's co-maintainer.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_remove_dist_comaint_tuples => {form_type => "select_field"},
+ SUBMIT_pause99_remove_dist_comaint => {form_type => "submit_button"},
+ },
+ },
+ giveup_dist_comaint => {
+ x_mojo_to => "user-distperms#giveup_dist_comaint",
+ verb => "Give up co-maintainership status",
+ priv => "user",
+ cat => "User/04Permissions/06",
+ desc => "Give up distribution's co-maintainership status.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ lsw => {form_type => "hidden_field"},
+ pause99_giveup_dist_comaint_d => {form_type => "select_field"},
+ SUBMIT_pause99_giveup_dist_comaint => {form_type => "submit_button"},
+ },
+ },
+
+ # User/Util
+
+ tail_logfile => {
+ x_mojo_to => "user#tail_logfile",
+ verb => "Tail Daemon Logfile",
+ priv => "user",
+ cat => "User/05Utils/06",
+ x_form => {
+ pause99_tail_logfile_1 => {form_type => "select_field"}, # how many lines to tail
+ pause99_tail_logfile_sub => {form_type => "submit_button"},
+ }
+ },
+ reindex => {
+ x_mojo_to => "user#reindex",
+ verb => "Force Reindexing",
+ priv => "user",
+ cat => "User/05Utils/02",
+ desc => "Tell the indexer to index a file again (e.g. after a change in the perms table)",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ SUBMIT_pause99_reindex_delete => {form_type => "submit_button"},
+ pause99_reindex_FILE => {form_type => "check_box"},
+ },
+ },
+ reset_version => {
+ x_mojo_to => "user#reset_version",
+ verb => "Reset Version",
+ priv => "user",
+ cat => "User/05Utils/02",
+ desc => "Overrule the record of the current version number of a module that the indexer uses and set it to 'undef'",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ SUBMIT_pause99_reset_version_forget => {form_type => "submit_button"},
+ pause99_reset_version_PKG => {form_type => "check_box"},
+ },
+ },
+
+ # User/Account
+
+ change_passwd => {
+ x_mojo_to => "user#change_passwd",
+ verb => "Change Password",
+ priv => "user",
+ cat => "User/06Account/02",
+ desc => "Change your password any time you want.",
+ method => 'POST',
+ x_csrf_protection => 1,
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ ABRA => {form_type => "hidden_field"},
+ pause99_change_passwd_pw1 => {form_type => "password_field"},
+ pause99_change_passwd_pw2 => {form_type => "password_field"},
+ pause99_change_passwd_sub => {form_type => "submit_button"},
+ },
+ },
+ edit_cred => {
+ x_mojo_to => "user-cred#edit",
+ verb => "Edit Account Info",
+ priv => "user",
+ cat => "User/06Account/01",
+ desc => "Edit your user name, your email addresses (both public and secret one), change the URL of your homepage.",
+ method => 'POST',
+ x_csrf_protection => 1,
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ pause99_edit_cred_fullname => {form_type => "text_field"},
+ pause99_edit_cred_asciiname => {form_type => "text_field"},
+ pause99_edit_cred_email => {form_type => "text_field"},
+ pause99_edit_cred_secretemail => {form_type => "text_field"},
+ pause99_edit_cred_homepage => {form_type => "text_field"},
+ pause99_edit_cred_cpan_mail_alias => {form_type => "radio_button"},
+ pause99_edit_cred_ustatus => {form_type => "check_box"}, # to delete
+ pause99_edit_cred_sub => {form_type => "submit_button"},
+ },
+ },
+ pause_logout => {
+ x_mojo_to => "user#pause_logout",
+ verb => "About Logging Out",
+ priv => "user",
+ cat => "User/06Account/04",
+ },
+
+ # ADMIN+mlrep+modlistmaint
+
+ select_ml_action => {
+ x_mojo_to => "mlrepr#select_ml_action",
+ verb => "Select Mailinglist/Action",
+ priv => "mlrepr",
+ cat => "09root/02",
+ desc => "Representatives of mailing lists have their special menu here.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "select_field"},
+ ACTIONREQ => {form_type => "select_field"},
+ pause99_select_ml_action_sub => {form_type => "submit_button"},
+ },
+ },
+ show_ml_repr => {
+ x_mojo_to => "mlrepr#show_ml_repr",
+ verb => "Show Mailinglist Reps",
+ priv => "mlrepr",
+ cat => "09root/04",
+ desc => "Admins and the representatives themselves can lookup who is elected to be representative of a mailing list.",
+ },
+
+ add_user => {
+ x_mojo_to => "admin-user#add",
+ verb => "Add a User or Mailinglist",
+ priv => "admin",
+ cat => "01usr/01add",
+ desc => "Admins can add users or mailinglists.",
+ method => 'POST',
+ x_form => {
+ SUBMIT_pause99_add_user_Soundex => {form_type => "submit_button"},
+ SUBMIT_pause99_add_user_Metaphone => {form_type => "submit_button"},
+ SUBMIT_pause99_add_user_Definitely => {form_type => "submit_button"},
+ pause99_add_user_userid => {form_type => "text_field"},
+ pause99_add_user_fullname => {form_type => "text_field"},
+ pause99_add_user_email => {form_type => "text_field"},
+ pause99_add_user_homepage => {form_type => "text_field"},
+ pause99_add_user_subscribe => {form_type => "text_field"},
+ pause99_add_user_memo => {form_type => "text_area"},
+ },
+ },
+ manage_id_requests => {
+ x_mojo_to => "admin-manage_id#manage",
+ verb => "Manage a registration request (alpha)",
+ priv => "admin",
+ cat => "01usr/01rej",
+ desc => "show/reject open registration requests",
+ method => 'POST',
+ },
+ edit_ml => {
+ x_mojo_to => "admin#edit_ml",
+ verb => "Edit a Mailinglist",
+ priv => "admin",
+ cat => "01usr/02",
+ desc => "Admins and mailing list representatives can change the name, address and description of a mailing list.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "hidden_field"},
+ pause99_edit_ml_3 => {form_type => "select_field"}, # mailing lists
+ pause99_edit_ml_2 => {form_type => "submit_button"}, # select ml
+ pause99_edit_ml_maillistname => {form_type => "text_field"},
+ pause99_edit_ml_address => {form_type => "text_field"},
+ pause99_edit_ml_subscribe => {form_type => "text_area"},
+ pause99_edit_ml_4 => {form_type => "submit_button"}, # update
+ },
+ },
+ email_for_admin => {
+ x_mojo_to => "admin#email_for_admin",
+ verb => "Look up the forward email address",
+ priv => "admin",
+ cat => "01usr/01look",
+ desc => "Admins can look where email should go",
+ },
+ select_user => {
+ x_mojo_to => "admin#select_user",
+ verb => "Select User/Action",
+ priv => "admin",
+ cat => "01usr/03",
+ desc => "Admins can access PAUSE as-if they were somebody else. Here they select a user/action pair.",
+ method => 'POST',
+ x_form => {
+ HIDDENNAME => {form_type => "select_field"},
+ ACTIONREQ => {form_type => "select_field"},
+ pause99_select_user_sub => {form_type => "submit_button"},
+ },
+ },
+);
+
+our @AllowAdminTakeover = qw(
+ add_uri
+ change_passwd
+ delete_files
+ edit_cred
+ edit_ml
+ edit_uris
+ reindex
+ reset_version
+ share_perms
+ move_primary
+ remove_primary
+ make_comaint
+ remove_comaint
+ giveup_comaint
+ move_dist_primary
+ remove_dist_primary
+ make_dist_comaint
+ remove_dist_comaint
+ giveup_dist_comaint
+);
+
+our @AllowMlreprTakeover = qw(
+ edit_ml
+ reset_version
+ share_perms
+ move_primary
+ remove_primary
+ make_comaint
+ remove_comaint
+ giveup_comaint
+ move_dist_primary
+ remove_dist_primary
+ make_dist_comaint
+ remove_dist_comaint
+ giveup_dist_comaint
+);
+
+sub allow_admin_takeover { @AllowAdminTakeover }
+sub allow_mlrepr_takeover { @AllowMlreprTakeover }
+
+sub action_names_for {
+ my ($self, $priv) = @_;
+ grep {$Actions{$_}{priv} eq $priv} keys %Actions;
+}
+
+sub action {
+ my ($self, $name) = @_;
+ $name && exists $Actions{$name} ? $Actions{$name} : {};
+}
+
+sub has_action {
+ my ($self, $name) = @_;
+ exists $Actions{$name} ? 1 : 0;
+}
+
+sub action_map_to_verb {
+ my ($self, @actions) = @_;
+ my %action_map;
+ for my $action (@actions) {
+ next unless exists $Actions{$action};
+ my $verb = $Actions{$action}{verb} or next;
+ $action_map{$action} = $verb;
+ }
+ \%action_map;
+}
+
+sub sort_allowed_group_actions {
+ my ($self, $group, $names) = @_;
+ map {$Actions{$_}{name} = $_; $Actions{$_}}
+ sort {$Actions{$a}{cat} cmp $Actions{$b}{cat}}
+ grep {$Actions{$_}{priv} eq $group}
+ @{$names || []};
+}
+
+our %GroupLabel = (
+ public => "Public",
+ user => "User",
+ mlrepr => "Mailinglists",
+ admin => "Admin",
+);
+
+our @PublicGroups = qw/public/;
+our @AllGroups = qw/public user mlrepr admin/;
+our @ExtraGroups = qw/mlrepr admin/;
+
+sub public_groups { @PublicGroups }
+sub extra_groups { @ExtraGroups }
+sub all_groups { @AllGroups }
+
+sub group_label {
+ my ($self, $group) = @_;
+ exists $GroupLabel{$group} ? $GroupLabel{$group} : Carp::confess "no label for $group";
+}
+
+our $Valid_Userid = qr/^[A-Z]{3,9}$/;
+
+sub valid_userid { $Valid_Userid }
+
+sub mailto_admins { join(",", @{$PAUSE::Config->{ADMINS}}) }
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Context.pm b/lib/pause_2025/PAUSE/Web/Context.pm
new file mode 100644
index 000000000..c642afe69
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Context.pm
@@ -0,0 +1,213 @@
+package PAUSE::Web::Context;
+
+use Mojo::Base -base;
+use Mojo::ByteStream;
+use Log::Dispatch::Config;
+use Encode;
+use Sys::Hostname ();
+use Email::Sender::Simple;
+use Email::MIME;
+use Data::Dumper;
+use PAUSE::Web::Config;
+use PAUSE::Web::Exception;
+
+our $VERSION = "1072";
+
+has root => sub { Carp::confess "requires root" };
+has config => sub { PAUSE::Web::Config->new };
+has logger => sub { Log::Dispatch::Config->instance };
+has mailer => sub { Email::Sender::Simple->new };
+
+sub init {
+ my $self = shift;
+
+ my $root = $self->root;
+ Log::Dispatch::Config->configure("$root/etc/plack_log.conf.".($ENV{PLACK_ENV} // "development"));
+}
+
+
+sub version {
+ my $self = shift;
+ return $self->{VERSION} if defined $self->{VERSION};
+ my $version = $VERSION;
+ for my $m (grep {! m!/Test/!} grep /pause_2017/, keys %INC) {
+ $m =~ s|/|::|g;
+ $m =~ s|\.pm$||;
+ my $v = $m->VERSION || 0;
+ warn "Warning: Strange versioning style in m[$m]v[$v]" if $v < 10;
+ $version = $v if $v > $version;
+ }
+ $version;
+}
+
+sub hostname {
+ my $self = shift;
+ $PAUSE::Config->{SERVER_NAME} || Sys::Hostname::hostname();
+}
+
+sub log {
+ my ($self, $arg) = @_;
+ $self->logger->log(%$arg)
+}
+
+### Database
+
+sub connect {
+ my $self = shift;
+ eval {$self->{DbHandle} ||= DBI->connect(
+ $PAUSE::Config->{MOD_DATA_SOURCE_NAME},
+ $PAUSE::Config->{MOD_DATA_SOURCE_USER},
+ $PAUSE::Config->{MOD_DATA_SOURCE_PW},
+ { RaiseError => 1,
+ mysql_auto_reconnect => 1,
+ # mysql_enable_utf8 => 1,
+ }
+ )};
+ return $self->{DbHandle} if $self->{DbHandle};
+ $self->database_alert;
+}
+
+sub authen_connect {
+ my $self = shift;
+ # local($SIG{PIPE}) = 'IGNORE';
+ eval {$self->{DbHandle4Authen} ||= DBI->connect(
+ $PAUSE::Config->{AUTHEN_DATA_SOURCE_NAME},
+ $PAUSE::Config->{AUTHEN_DATA_SOURCE_USER},
+ $PAUSE::Config->{AUTHEN_DATA_SOURCE_PW},
+ { RaiseError => 1,
+ mysql_auto_reconnect => 1,
+ # mysql_enable_utf8 => 1,
+ }
+ )};
+ return $self->{DbHandle4Authen} if $self->{DbHandle4Authen};
+ $self->database_alert;
+}
+
+sub database_alert {
+ my $self = shift;
+ my $mess = Carp::longmess($@);
+ my $tsf = "$PAUSE::Config->{RUNDATA}/alert.db.not.available.ts";
+ if (! -f $tsf or (time - (stat _)[9]) > 6*60*60) {
+ my $server = $self->hostname;
+ my $header = {
+ From => "database_alert",
+ To => $PAUSE::Config->{ADMIN},
+ Subject => "PAUSE Database Alert $server",
+ };
+ $self->send_mail($header, $mess);
+ open my $fh, ">", $tsf or warn "Could not open $tsf: $!";
+ }
+ die PAUSE::Web::Exception->new(ERROR => <<"ERROR_END");
+Sorry, the PAUSE Database currently seems unavailable.
+Administration has been notified.
+Please try again later.
+ERROR_END
+}
+
+# A wrapper function for fetchrow_array and fetchrow_hashref
+# XXX: Should mysql_enable_utf8 suffice?
+sub fetchrow {
+ my ($self, $sth, $what) = @_;
+
+ if (wantarray) {
+ my @arr = $sth->$what;
+ for (@arr) {
+ defined && /[^\000-\177]/ && Encode::_utf8_on($_);
+ }
+ return @arr;
+ } else {
+ my $ret = $sth->$what;
+ if (ref $ret) {
+ for my $k (keys %$ret) {
+ defined && /[^\000-\177]/ && Encode::_utf8_on($_) for $ret->{$k};
+ }
+ return $ret;
+ } else {
+ defined && /[^\000-\177]/ && Encode::_utf8_on($_) for $ret;
+ return $ret;
+ }
+ }
+}
+
+### Mailer
+
+sub prepare_sendto {
+ my ($self, $active_user, $pause_user, @admin) = @_;
+
+ my %umailset;
+ my $name = $active_user->{asciiname} || $active_user->{fullname} || "";
+ my $Uname = $pause_user->{asciiname} || $pause_user->{fullname} || "";
+ if ($active_user->{secretemail}) {
+ $umailset{qq{"$name" <$active_user->{secretemail}>}} = 1;
+ } elsif ($active_user->{email}) {
+ $umailset{qq{"$name" <$active_user->{email}>}} = 1;
+ }
+ if ($active_user->{userid} ne $pause_user->{userid}) {
+ if ($pause_user->{secretemail}) {
+ $umailset{qq{"$Uname" <$pause_user->{secretemail}>}} = 1;
+ }elsif ($pause_user->{email}) {
+ $umailset{qq{"$Uname" <$pause_user->{email}>}} = 1;
+ }
+ }
+ my @to = keys %umailset;
+ push @to, @admin if @admin;
+ @to;
+}
+
+sub send_mail_multi {
+ my ($self, $to, $header, $mailblurb) = @_;
+ warn "sending to[@$to]";
+ warn "mailblurb[$mailblurb]";
+ for my $to2 (@$to) {
+ $header->{To} = $to2;
+ $self->send_mail($header, "$mailblurb");
+ }
+}
+
+sub send_mail {
+ my ($self, $header, $blurb) = @_;
+
+ my @hdebug = %$header; $self->log({level => "info", message => sprintf("hdebug[%s]", join "|", @hdebug) });
+ $header->{From} ||= qq{"Perl Authors Upload Server" <$PAUSE::Config->{UPLOAD}>};
+ $header->{"Reply-To"} ||= join ", ", @{$PAUSE::Config->{ADMINS}};
+
+ my $email = Email::MIME->create(
+ header_str => [%$header],
+ attributes => {
+ charset => 'utf-8',
+ content_type => 'text/plain',
+ encoding => 'quoted-printable',
+ },
+ body_str => $blurb,
+ );
+
+ if ($PAUSE::Config->{TESTHOST}){
+ warn "TESTHOST is NOT sending mail";
+ warn "Line " . __LINE__ . ", File: " . __FILE__ . "\n" .
+ Data::Dumper->new([$header,$blurb],[qw(header blurb)])
+ ->Indent(1)->Useqq(1)->Dump;
+ }
+ eval {
+ $self->mailer->send($email);
+ };
+ if (my $error = $@) {
+ if ($error->isa('Email::Sender::Failure')) {
+ warn "Sendmail error: $error";
+ die PAUSE::Web::Exception->new(ERROR => Mojo::ByteStream->new(<<"ERROR_END"));
+Sorry, the PAUSE failed to send an email.
+Administration has been notified.
+ERROR_END
+ } else {
+ die $error;
+ }
+ }
+ 1;
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->{DbHandle4Authen}->disconnect if ref $self->{DbHandle4Authen};
+ $self->{DbHandle}->disconnect if ref $self->{DbHandle};
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Admin.pm b/lib/pause_2025/PAUSE/Web/Controller/Admin.pm
new file mode 100644
index 000000000..ed601df7b
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Admin.pm
@@ -0,0 +1,202 @@
+package PAUSE::Web::Controller::Admin;
+
+use Mojo::Base "Mojolicious::Controller";
+
+sub email_for_admin {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my %ALL;
+ {
+ my $dba = $mgr->authen_connect;
+ my $dbm = $mgr->connect;
+ my $sth1 = $dbm->prepare(qq{SELECT userid, email
+ FROM users
+ WHERE isa_list = ''
+ AND (
+ cpan_mail_alias='publ'
+ OR
+ cpan_mail_alias='secr'
+ )});
+ $sth1->execute;
+ while (my($id,$mail) = $sth1->fetchrow_array) {
+ $ALL{$id} = $mail; # we store public email even for those who want
+ # secret, because we never know if we will find a
+ # secret one
+ }
+ $sth1->finish;
+ my $sth2 = $dbm->prepare(qq{SELECT userid
+ FROM users
+ WHERE cpan_mail_alias='secr'
+ AND isa_list = ''});
+ $sth2->execute;
+ my $sth3 = $dba->prepare(qq{SELECT secretemail
+ FROM usertable
+ WHERE user=?});
+ while (my($id) = $sth2->fetchrow_array) {
+ $sth3->execute($id);
+ next unless $sth3->rows;
+ my($mail) = $sth3->fetchrow_array or next;
+ $ALL{$id} = $mail;
+ }
+ $sth2->finish;
+ $sth3->finish;
+ };
+ my $output_format = $req->param("OF");
+ if ($output_format){
+ if ($output_format eq "YAML") {
+ return $c->render_yaml(\%ALL);
+ } else {
+ die "not supported OF=$output_format"
+ }
+ } else {
+ my @list;
+ for my $id (sort keys %ALL) {
+ push @list, {id => $id, mail => $ALL{$id}};
+ }
+ $pause->{list} = \@list;
+ }
+}
+
+sub edit_ml {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $selectedid = "";
+ my $selectedrec = {};
+
+ my $param;
+ if ($param = $req->param("pause99_edit_ml_3")) { # upper selectbox
+ $selectedid = $param;
+ } elsif ($param = $req->param("HIDDENNAME")) {
+ $selectedid = $param;
+ $req->param("pause99_edit_ml_3" => $param);
+ }
+
+ warn sprintf(
+ "selectedid[%s]IsMR[%s]",
+ $selectedid,
+ join(":",
+ keys(%{$pause->{IsMailinglistRepresentative}})
+ )
+ );
+
+ my($sql,@bind);
+ if (exists $pause->{IsMailinglistRepresentative}{$selectedid}) {
+ $sql = qq{SELECT users.userid
+ FROM users JOIN list2user
+ ON users.userid = list2user.maillistid
+ WHERE users.isa_list > ''
+ AND list2user.userid = ?
+ ORDER BY users.userid
+};
+ @bind = $pause->{User}{userid};
+ } else {
+ $sql = qq{SELECT userid FROM users WHERE isa_list > '' ORDER BY userid};
+ @bind = ();
+ }
+
+ my $dbh = $mgr->connect;
+ my $sth = $dbh->prepare($sql);
+ $sth->execute(@bind);
+ my @all_mls;
+ my %mls_lab;
+ if ($sth->rows) {
+ my $sth2 = $dbh->prepare(qq{SELECT * FROM maillists WHERE maillistid=?});
+ while (my($id) = $mgr->fetchrow($sth, "fetchrow_array")) {
+ # register this mailinglist for the selectbox
+ push @all_mls, $id;
+ # query for more info about it
+ $sth2->execute($id);
+ my($rec) = $mgr->fetchrow($sth2, "fetchrow_hashref");
+ # we will display the name along the ID
+ $mls_lab{$id} = "$id ($rec->{maillistname})";
+ if ($id eq $selectedid) {
+ # if this is the selected one, we just store it immediately
+ $selectedrec = $rec;
+ }
+ }
+ }
+ $pause->{mls} = [map {[$mls_lab{$_} => $_]} @all_mls];
+
+ if ($selectedid) {
+ $pause->{selected} = $selectedrec;
+ my $force_sel = $req->param('pause99_edit_ml_2');
+ my $update_sel = $req->param('pause99_edit_ml_4');
+
+ $pause->{updated_sel} = $update_sel;
+
+ my $saw_a_change;
+ my $now = time;
+
+ for my $field (qw(maillistname address subscribe)) {
+ my $fieldname = "pause99_edit_ml_$field";
+ if ($force_sel){
+ $req->param($fieldname => $selectedrec->{$field}||"");
+ } elsif ($update_sel) {
+ my $param = $req->param($fieldname);
+ if ($param ne $selectedrec->{$field}) {
+ my $sql = qq{UPDATE maillists
+ SET $field=?,
+ changed=?,
+ changedby=?
+ WHERE maillistid=?};
+ my $usth = $dbh->prepare($sql);
+ my $ret = $usth->execute($param, $now, $u->{userid}, $selectedrec->{maillistid});
+ $saw_a_change = 1 if $ret > 0;
+ $usth->finish;
+ }
+ }
+ }
+ if ($saw_a_change) {
+ $pause->{changed} = 1;
+ my $mailblurb = $c->render_to_string("email/admin/edit_ml", format => "email");
+ my @to = ($u->{secretemail}||$u->{email}, $mgr->config->mailto_admins);
+ warn "sending to[@to]";
+ warn "mailblurb[$mailblurb]";
+ my $header = {
+ Subject => "Mailinglist update for $selectedrec->{maillistid}"
+ };
+ $mgr->send_mail_multi(\@to, $header, $mailblurb);
+ }
+ }
+}
+
+sub select_user {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ if (my $action = $req->param("ACTIONREQ")) {
+ if (
+ $mgr->config->has_action($action)
+ ) {
+ $req->param("ACTION" => $action);
+ $pause->{Action} = $action;
+ return $c->delegate($action);
+ } else {
+ die "cannot action[$action]";
+ }
+ }
+
+ my %user_meta = $c->user_meta;
+ my $labels = $user_meta{userid}{args}{labels};
+ $pause->{hidden_name_list} = [map {[
+ $labels->{$_} => $_,
+ ($_ eq $pause->{User}{userid} ? (selected => "selected") : ()),
+ ]} sort keys %$labels];
+
+ my $action_map = $mgr->config->action_map_to_verb($mgr->config->allow_admin_takeover);
+ $pause->{action_req_list} = [map {[
+ $action_map->{$_} => $_,
+ ($_ eq 'edit_cred' ? (selected => "selected") : ()),
+ ]} sort keys %$action_map];
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Admin/ManageId.pm b/lib/pause_2025/PAUSE/Web/Controller/Admin/ManageId.pm
new file mode 100644
index 000000000..6fd2f120d
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Admin/ManageId.pm
@@ -0,0 +1,66 @@
+package PAUSE::Web::Controller::Admin::ManageId;
+
+use Mojo::Base "Mojolicious::Controller";
+use Storable;
+use File::Find;
+use JSON::XS; # used in the template
+
+sub manage {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ return unless exists $pause->{UserGroups}{admin};
+
+ return unless -d $c->session_data_dir;
+
+ my %ALL;
+ my $delete;
+ if ($req->param("subaction") && $req->param("subaction") eq "delete") {
+ $delete = $req->param("USERID");
+ }
+ my $dbh = $mgr->connect;
+ my $sthu = $dbh->prepare("SELECT userid from users where userid=?");
+
+ find
+ (
+ {wanted => sub {
+ my $path = $_;
+ my @stat = stat $path or die "Could not stat '$path': $!";
+ return unless -f _;
+ my $mtime = $stat[9];
+ open my $fh, "<", $path or die "Couldn't open '$path': $!";
+ local $/;
+ my $content = <$fh>;
+ my $session = Storable::thaw $content;
+ # warn "DEBUG: mtime[$mtime]stat[@stat]session[$session]";
+ my $userid = $session->{APPLY}{userid} or return;
+ if ($delete && $session->{_session_id} eq $delete) {
+ unlink $path or die "Could not unlink '$path': $!";
+ return;
+ }
+ my $type;
+ if (exists $session->{APPLY}{fullname}) {
+ $sthu->execute($userid);
+ return if $sthu->rows > 0;
+ $type = "user";
+ }
+ if ($session->{APPLY}{rationale} =~ /\b(?:BLONDE\s+NAKED|NAKED\s+SEXY|FREE\s+CUMSHOT|CUMSHOT\s+VIDEOS|FREE\s+SEX|FREE\s+TUBE|GROUP\s+SEX|FREE\s+PORN|SEX\s+VIDEO|SEX\s+MOVIES?|SEX\s+TUBE|SEX\s+MATURE|STREET\s+BLOWJOBS|SEX\s+PUBLIC|TUBE\s+PORN|PORN\s+TUBE|TUBE\s+VIDEOS|VIDEO\s+TUBE|XNXX\s+VIDEOS|XXX\s+FREE|ANIMAL\s+SEX|GIRLS\s+SEX|PORN\s+VIDEOS?|PORN\s+MOVIES|TITS\s+PORN|RAW\s+SEX|DEEPTHROAT\s+TUBE|celeb\s+porn|PREGNANT\s+TUBE|picture\s+sex|NAKED\s+WOMEN|WOMEN\s+MOVIES|MATURE\s+NAKED|SEX\s+ANIME|hot\s+nude|nude\s+celebs|ANIME\s+TUBES|SEX\s+DOG|MATURE\s+SEX|MATURE\s+PUSSY|Rape\s+Porn|brutal\s+fuck|rape\s+video|ANIMAL\s+TUBE|SHEMALE\s+CUMSHOT|ANIMAL\s+PORN|ANIMAP\s+CLIP|CLIP\s+SEX|PUBLIC\s+BLOWJOB|free\s+lesbian|lesbian\s+sex|SEX\s+ZOO|tv-adult|numismata.org|www.soulcommune.com|www.petsusa.org|www.csucssa.org|www.thisis50.com|www.comunidad-latina.net|www.singlefathernetwork.com|www.freetoadvertise.biz|gayforum.dk|www.purevolume.com|playgroup.themouthpiece.com|www.bananacorp.cl|party.thebamboozle.com|blog.tellurideskiresort.com|www.pethealthforums.com|www.burropride.com|lpokemon.19.forumer.com|Zootube365|Eskimotube|xtube-1|phentermine without a prescription)\b/i) {
+ unlink $path or die "Could not unlink '$path': $!";
+ return;
+ }
+ $ALL{$path} = {
+ session => $session,
+ mtime => $mtime,
+ type => $type,
+ };
+ },
+ no_chdir => 1,
+ },
+ $c->session_data_dir,
+ );
+ $pause->{all} = \%ALL;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Admin/User.pm b/lib/pause_2025/PAUSE/Web/Controller/Admin/User.pm
new file mode 100644
index 000000000..8f542932a
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Admin/User.pm
@@ -0,0 +1,307 @@
+package PAUSE::Web::Controller::Admin::User;
+
+use Mojo::Base "Mojolicious::Controller";
+use PAUSE::Web::Util::Encode;
+use Text::Soundex;
+use Text::Metaphone;
+use Text::Format;
+
+sub add {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+
+ if ($req->param("USERID")) {
+ my $session = $c->new_session_counted;
+ my $s = $session->{APPLY};
+ for my $a (keys %$s) {
+ $req->param("pause99_add_user_$a" => $s->{$a});
+ warn "retrieving from session a[$a]s(a)[$s->{$a}]";
+ }
+ }
+
+ my $userid;
+ if ( $userid = $req->param("pause99_add_user_userid") ) {
+
+ $userid = uc($userid);
+ $userid ||= "";
+ $pause->{userid} = $userid;
+
+ my @error;
+ if ( $userid !~ $mgr->config->valid_userid ) {
+ push @error, {invalid => 1};
+ }
+
+ $req->param("pause99_add_user_userid" => $userid) if $userid;
+
+ my $doit = 0;
+ my $fullname_raw = $req->param('pause99_add_user_fullname') // '';
+ my($fullname);
+ $fullname = PAUSE::Web::Util::Encode::any2utf8($fullname_raw);
+ warn "fullname[$fullname]fullname_raw[$fullname_raw]";
+ if ($fullname ne $fullname_raw) {
+ $req->param("pause99_add_user_fullname" => $fullname);
+ my $debug = $req->param("pause99_add_user_fullname");
+ warn "debug[$debug]fullname[$fullname]";
+ }
+ unless ($fullname) {
+ warn "no fullname";
+ push @error, {no_fullname => 1};
+ }
+ $pause->{fullname} = $fullname;
+
+ unless (@error) {
+ if ($req->param('SUBMIT_pause99_add_user_Definitely')) {
+ $doit = 1;
+ } elsif (
+ $req->param('SUBMIT_pause99_add_user_Soundex')
+ ||
+ $req->param('SUBMIT_pause99_add_user_Metaphone')
+ ) {
+
+ # START OF SOUNDEX/METAPHONE check
+
+ my ($surname);
+ my($s_package) = $req->param('SUBMIT_pause99_add_user_Soundex') ?
+ 'Text::Soundex' : 'Text::Metaphone';
+
+ ($surname = $fullname) =~ s/.*\s//;
+ my $query = qq{SELECT userid, fullname, email, homepage,
+ introduced, changedby, changed
+ FROM users
+ WHERE isa_list=''
+ };
+ my $sth = $dbh->prepare($query);
+ $sth->execute;
+ my $s_func;
+ if ($s_package eq "Text::Soundex") {
+ $s_func = \&Text::Soundex::soundex;
+ } elsif ($s_package eq "Text::Metaphone") {
+ $s_func = \&Text::Metaphone::Metaphone;
+ }
+ my $s_code = $s_func->($surname);
+ $pause->{s_package} = $s_package;
+ $pause->{s_code} = $s_code;
+
+ warn "s_code[$s_code]";
+ my $requserid = $req->param("pause99_add_user_userid")||"";
+ my $reqfullname = $req->param("pause99_add_user_fullname")||"";
+ my $reqemail = $req->param("pause99_add_user_email")||"";
+ my $reqhomepage = $req->param("pause99_add_user_homepage")||"";
+ my($suserid,$sfullname, $spublic_email, $shomepage,
+ $sintroduced, $schangedby, $schanged);
+ # if a user has a preference to display secret emails in a
+ # certain color, they can enter it here:
+ my %se_color_map = (
+ jv => "black",
+ andk => "#f33",
+ );
+ my $se_color = $se_color_map{lc $pause->{User}{userid}} || "red";
+ $pause->{se_color} = $se_color;
+
+ my @urows;
+ while (($suserid, $sfullname, $spublic_email, $shomepage,
+ $sintroduced, $schangedby, $schanged) =
+ $mgr->fetchrow($sth, "fetchrow_array")) {
+ (my $dbsurname = $sfullname) =~ s/.*\s//;
+ next unless $s_func->($dbsurname) eq $s_code;
+ my %urow;
+ my $score = 0;
+ my $ssecretemail = $c->get_secretemail($suserid);
+
+ if (defined($suserid)&&length($suserid)) {
+ if ($requserid eq $suserid) {
+ $urow{same_userid} = 1;
+ $score++;
+ }
+ $urow{userid} = $suserid;
+ }
+ {
+ if ($sfullname eq $reqfullname) {
+ $urow{same_fullname} = 1;
+ $score++;
+ } elsif ($sfullname =~ /\Q$surname\E/) {
+ $urow{surname} = $surname;
+ my ($before, $after) = split /\Q$surname\E/, $sfullname, 2;
+ $urow{before_surname} = $before // "";
+ $urow{after_surname} = $after // "";
+ $score++;
+ }
+ if (defined($sfullname)&&length($sfullname)) {
+ $urow{fullname} = $sfullname;
+ }
+ }
+ my @email_parts = split '@', $spublic_email;
+ {
+ if ($spublic_email eq $reqemail) {
+ $urow{same_email} = 1;
+ $score++;
+ }
+ $urow{email_parts} = \@email_parts;
+ }
+ if ($ssecretemail) {
+ $urow{secretemail} = $ssecretemail;
+
+ if ($ssecretemail eq $reqemail) {
+ $urow{same_secretemail} = 1;
+ $score++;
+ }
+ }
+ if ($shomepage) {
+ if ($shomepage eq $reqhomepage) {
+ $urow{same_homepage} = 1;
+ $score++;
+ }
+ $urow{homepage} = $shomepage;
+ }
+ if ($sintroduced) {
+ $urow{introduced} = scalar(gmtime($sintroduced));
+ }
+ if ($schanged) {
+ $urow{changed} = scalar(gmtime($schanged));
+ }
+ $urow{changedby} = $schangedby;
+ push @urows, {
+ line => \%urow,
+ score => $score,
+ };
+ }
+ if (@urows) {
+ $doit = 0;
+ $pause->{urows} = \@urows;
+ } else {
+ $doit = 1;
+ }
+
+ # END OF SOUNDEX/METAPHONE check
+ }
+ }
+ $pause->{doit} = $doit;
+
+ if ($doit) {
+ $c->add_user_doit($userid,$fullname);
+ } elsif (@error) {
+ $pause->{error} = \@error;
+ } else {
+ my $T = time;
+ warn "T[$T]doit[$doit]userid[$userid]";
+ }
+ } else {
+ warn "No userid, nothing done";
+ }
+}
+
+sub get_secretemail {
+ my ($c, $userid) = @_;
+ my $mgr = $c->app->pause;
+ my $dbh2 = $mgr->authen_connect;
+ my $sth2 = $dbh2->prepare("SELECT secretemail
+ FROM $PAUSE::Config->{AUTHEN_USER_TABLE}
+ WHERE $PAUSE::Config->{AUTHEN_USER_FLD}=?");
+ $sth2->execute($userid);
+ my($h2) = $mgr->fetchrow($sth2, "fetchrow_array");
+ $sth2->finish;
+ $h2;
+}
+
+sub add_user_doit {
+ my($c, $userid, $fullname) = @_;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $T = time;
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+
+ my($query,$sth,@qbind);
+ my($email) = $req->param('pause99_add_user_email');
+ my($homepage) = $req->param('pause99_add_user_homepage');
+ my $subscribe = $req->param('pause99_add_user_subscribe') // '';
+ my $entered_by = $pause->{User}{fullname} || $pause->{User}{userid};
+ my $is_mailing_list = $subscribe gt '';
+ if ( $is_mailing_list ) {
+ $query = qq{INSERT INTO users (
+ userid, isa_list, introduced,
+ changed, changedby)
+ VALUES (
+ ?, ?, ?,
+ ?, ?)};
+ @qbind = ($userid,1,$T,$T,$pause->{User}{userid});
+ } else {
+ $query = qq{INSERT INTO users (
+ userid, email, homepage, fullname,
+ isa_list, introduced, changed, changedby)
+ VALUES (
+ ?, ?, ?, ?,
+ ?, ?, ?, ?)};
+ @qbind = ($userid,"CENSORED",$homepage,$fullname,"",$T,$T,$pause->{User}{userid});
+ }
+
+ # We have a query for INSERT INTO users
+
+ if ($dbh->do($query,undef,@qbind)) {
+ $pause->{succeeded} = 1;
+
+ if ( $is_mailing_list ) {
+ # Add a mailinglist: INSERT INTO maillists
+
+ my($maillistid) = $userid;
+ my($maillistname) = $fullname;
+ my($changed) = $T;
+ $pause->{maillistname} = $maillistname;
+ $pause->{subscribe} = $subscribe;
+
+ $query = qq{INSERT INTO maillists (
+ maillistid, maillistname,
+ subscribe, changed, changedby, address)
+ VALUES (
+ ?, ?,
+ ?, ?, ?, ?)};
+ my @qbind2 = ($maillistid, $maillistname,
+ $subscribe, $changed, $pause->{User}{userid}, $email);
+ unless ($dbh->do($query,undef,@qbind2)) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => [qq{Query[$query]with qbind2[@qbind2] failed. Reason:}, $DBI::errstr]);
+ }
+
+ } else {
+ # Not a mailinglist: set and send one time password
+ my $onetime = $c->set_onetime_password($userid, $email);
+ $c->send_otp_email($userid, $email, $onetime);
+ # send emails to user and modules@perl.org; latter must censor the
+ # user's email address
+ my ($subject, $blurb) = $c->send_welcome_email( [$email], $userid, $email, $fullname, $homepage, $entered_by );
+ $c->send_welcome_email( $PAUSE::Config->{ADMINS}, $userid, "CENSORED", $fullname, $homepage, $entered_by );
+
+ $pause->{subject} = $subject;
+ $pause->{blurb} = $blurb;
+ $pause->{send_to} = join(" AND ", @{$PAUSE::Config->{ADMINS}}, $email);
+ }
+
+ warn "Info: clearing all fields";
+ for my $field (qw(userid fullname email homepage subscribe)) {
+ my $param = "pause99_add_user_$field";
+ $req->param($param => "");
+ }
+
+ } else {
+ $pause->{query} = $query;
+ $pause->{query_error} = $dbh->errstr;
+ }
+
+ # usertable {
+ {
+ my $sql = "SELECT * FROM users WHERE userid=?";
+ my $sth = $dbh->prepare($sql);
+ $sth->execute($userid);
+ return unless $sth->rows == 1;
+ my $rec = $mgr->fetchrow($sth, "fetchrow_hashref");
+
+ $pause->{usertable} = $rec;
+ }
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Mlrepr.pm b/lib/pause_2025/PAUSE/Web/Controller/Mlrepr.pm
new file mode 100644
index 000000000..3d71bb403
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Mlrepr.pm
@@ -0,0 +1,78 @@
+package PAUSE::Web::Controller::Mlrepr;
+
+use Mojo::Base "Mojolicious::Controller";
+
+sub select_ml_action {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $dbh = $mgr->connect;
+ if (my $action = $req->param("ACTIONREQ")) {
+ if (
+ $mgr->config->has_action($action)
+ &&
+ grep { $_ eq $action } $mgr->config->allow_mlrepr_takeover
+ ) {
+ $req->param(ACTION => $action);
+ $pause->{Action} = $action;
+ return $c->delegate($action);
+ } else {
+ die "cannot or want not action[$action]";
+ }
+ }
+
+ my ($sql, @bind);
+ if (exists $pause->{UserGroups}{admin}) {
+ $sql = qq{SELECT users.userid
+ FROM users, list2user
+ WHERE isa_list > ''
+ AND users.userid = list2user.maillistid
+ ORDER BY users.userid
+ };
+ } else {
+ $sql = qq{SELECT users.userid
+ FROM users, list2user
+ WHERE isa_list > ''
+ AND users.userid = list2user.maillistid
+ AND list2user.userid = ?
+ ORDER BY users.userid
+ };
+ @bind = $pause->{User}{userid};
+ }
+
+ my $sth = $dbh->prepare($sql);
+ $sth->execute(@bind);
+ my %u;
+ while (my @row = $mgr->fetchrow($sth, "fetchrow_array")) {
+ $u{$row[0]} = $row[0];
+ }
+
+ my $action_map = $mgr->config->action_map_to_verb($mgr->config->allow_mlrepr_takeover);
+ my @action_reqs = map {[$action_map->{$_} => $_]} sort keys %$action_map;
+ $pause->{users} = [sort {lc($u{$a}) cmp lc($u{$b})} keys %u];
+ $pause->{action_reqs} = \@action_reqs;
+}
+
+sub show_ml_repr {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+
+ my $dbh = $mgr->connect;
+ my $sth = $dbh->prepare("SELECT maillistid, userid
+ FROM list2user
+ ORDER BY maillistid, userid");
+ $sth->execute;
+
+ my @lists;
+ while (my $rec = $mgr->fetchrow($sth, "fetchrow_hashref")) {
+ push @lists, $rec;
+ }
+ $sth->finish;
+
+ $pause->{lists} = \@lists;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Public.pm b/lib/pause_2025/PAUSE/Web/Controller/Public.pm
new file mode 100644
index 000000000..46ae90bb2
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Public.pm
@@ -0,0 +1,223 @@
+package PAUSE::Web::Controller::Public;
+
+use Mojo::Base "Mojolicious::Controller";
+use Time::Duration;
+
+sub mailpw {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my ($param, $email);
+
+ # TUT: We reach this point in the code only if the Querystring
+ # specified ACTION=mailpw or something equivalent. The parameter ABRA
+ # is used to denote the token that we might have sent them.
+ my $abra = $req->param("ABRA") || "";
+
+ # TUT: The parameter pause99_mailpw_1 denotes the userid of the user
+ # for whom a password change was requested. Note that anybody has
+ # access to that parameter, we do not authentify its origin. Of
+ # course not, because that guy says he has lost the password:-) If
+ # this parameter is there, we are asked to send a token. Otherwise
+ # they only want to see the password-requesting form.
+ $param = $req->param("pause99_mailpw_1");
+ if ( uc $req->method eq 'POST' and $param ) {
+ $param = uc($param);
+ unless ($param =~ /^[A-Z\-]+$/) {
+ if ($param =~ /@/) {
+ die PAUSE::Web::Exception->new(ERROR =>
+ qq{Please supply a userid, not an email address.});
+ }
+ die PAUSE::Web::Exception->new(ERROR =>
+ qq{A userid of $param is not allowed, please retry with a valid userid. Nothing done.}); # FIXME
+ }
+ $pause->{mailpw_userid} = $param;
+
+ # TUT: The object $mgr is our knows/is/can-everything object. Here
+ # it connects us to the authenticating database
+ my $authen_dbh = $mgr->authen_connect;
+ my $sql = qq{SELECT *
+ FROM usertable
+ WHERE user = ? };
+ my $sth = $authen_dbh->prepare($sql);
+ $sth->execute($param);
+ my $rec = {};
+ if ($sth->rows == 1) {
+ $rec = $mgr->fetchrow($sth, "fetchrow_hashref");
+ } else {
+ my $u;
+ eval {
+ $u = $c->active_user_record($param);
+ };
+ if ($@) {
+ # FIXME
+ die PAUSE::Web::Exception->new(ERROR =>
+ qq{Cannot find a userid
+ of $param, please
+ retry with a valid
+ userid.});
+ } elsif ($u->{userid} && $u->{email}) {
+ # this is one of the 94 users (counted on 2005-01-05) that has
+ # a users record but no usertable record
+ $sql = qq{INSERT INTO usertable (user,secretemail,forcechange,changed)
+ VALUES (?, ?, 1, ?)};
+
+ $authen_dbh->do($sql,{},$u->{userid},$u->{email},time)
+ or die PAUSE::Web::Exception->new(ERROR =>
+ qq{The userid of $param
+ is too old for this interface. Please get in touch with administration.}); # FIXME
+
+ $rec->{secretemail} = $u->{email};
+ } else {
+ die PAUSE::Web::Exception->new(ERROR =>
+ qq{A userid of $param
+ is not known, please retry with a valid userid.}); # FIXME
+ }
+ }
+
+ # TUT: all users may have a secret and a public email. We pick what
+ # we have.
+ unless ($email = $rec->{secretemail}) {
+ my $u = $c->active_user_record($param,{hidden_user_ok => 1});
+ require YAML::Syck;
+ my $message = "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . YAML::Syck::Dump({u=>$u});
+ $mgr->log({level => 'debug', message => $message});
+ $email = $u->{email};
+ }
+ if ($email) {
+ $pause->{valid_email} = 1;
+
+ # TUT: Before we insert a record from that table, we remove old
+ # entries so the primary key of an old record doesn't block us now.
+ $sql = sprintf qq{DELETE FROM abrakadabra
+ WHERE NOW() > expires};
+ $authen_dbh->do($sql);
+
+ my $passwd = sprintf "%08x" x 4, rand(0xffffffff), rand(0xffffffff),
+ rand(0xffffffff), rand(0xffffffff);
+ # warn "pw[$passwd]";
+ $pause->{passwd} = $passwd;
+
+ my $then = time + $PAUSE::Config->{ABRA_EXPIRATION};
+ $sql = qq{INSERT INTO abrakadabra
+ ( user, chpasswd, expires )
+ VALUES
+ ( ?, ?, from_unixtime(?) ) };
+ local($authen_dbh->{RaiseError}) = 0;
+ if ( $authen_dbh->do($sql,undef,$param,$passwd,$then) ) {
+ } elsif ($authen_dbh->errstr =~ /Duplicate entry/) {
+ my $duration;
+ $duration = Time::Duration::duration($PAUSE::Config->{ABRA_EXPIRATION});
+ die PAUSE::Web::Exception->new
+ (
+ ERROR => qq{A token for $param that allows
+ changing of the password has been requested recently
+ (less than $duration ago) and is still valid. Nothing
+ done.}
+ );
+ } else {
+ die PAUSE::Web::Exception->new(ERROR => $authen_dbh->errstr);
+ }
+
+ # between Apache::URI and URI::URL
+ my $me = $c->my_full_url; # FIXME
+ $me =~ s/^http:/https:/; # do not blindly inherit the schema
+
+ my $mailblurb = $c->render_to_string("email/public/mailpw", format => "email");
+
+ my $header = { Subject => "Your visit at $me" };
+ warn "mailto[$email]mailblurb[$mailblurb]";
+ $mgr->send_mail_multi([$email], $header, "$mailblurb");
+ }
+ }
+}
+
+sub about {
+ my $c = shift;
+ $c->serve_pause_doc("04pause.html", "needs_rewrite")
+}
+
+sub naming {
+ my $c = shift;
+ $c->serve_pause_doc("namingmodules.html")
+}
+
+sub news {
+ my $c = shift;
+ $c->serve_pause_doc("index.html")
+}
+
+sub history {
+ my $c = shift;
+ $c->serve_pause_doc("history.html")
+}
+
+sub imprint {
+ my $c = shift;
+ $c->serve_pause_doc("imprint.html")
+}
+
+sub operating_model {
+ my $c = shift;
+ $c->serve_pause_doc("doc/operating-model.md")
+}
+
+sub privacy_policy {
+ my $c = shift;
+ $c->serve_pause_doc("doc/privacy-policy.md")
+}
+
+sub pumpkin {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+
+ my @hres;
+ {
+ my $dbh = $mgr->authen_connect;
+ my $sth = $dbh->prepare("SELECT user FROM grouptable WHERE ugroup='pumpking' order by user");
+ $sth->execute;
+ while (my @row = $sth->fetchrow_array) {
+ push @hres, $row[0];
+ }
+ $sth->finish;
+ };
+
+ if (my $output_format = $c->req->param("OF")) {
+ if ($output_format eq "YAML") {
+ return $c->render_yaml(\@hres);
+ } else {
+ die "not supported OF=$output_format"
+ }
+ }
+ $pause->{pumpkins} = \@hres;
+}
+
+sub admin {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+
+ my @hres;
+ {
+ my $dbh = $mgr->authen_connect;
+ my $sth = $dbh->prepare("SELECT user FROM grouptable WHERE ugroup='admin' order by user");
+ $sth->execute;
+ while (my @row = $sth->fetchrow_array) {
+ push @hres, $row[0];
+ }
+ $sth->finish;
+ };
+ my $output_format = $c->req->param("OF");
+ if ($output_format){
+ if ($output_format eq "YAML") {
+ return $c->render_yaml(\@hres);
+ } else {
+ die "not supported OF=$output_format"
+ }
+ }
+ $pause->{admins} = \@hres;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Public/RequestId.pm b/lib/pause_2025/PAUSE/Web/Controller/Public/RequestId.pm
new file mode 100644
index 000000000..5ea014ceb
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Public/RequestId.pm
@@ -0,0 +1,269 @@
+package PAUSE::Web::Controller::Public::RequestId;
+
+use Mojo::Base "Mojolicious::Controller";
+use PAUSE::Web::Util::Encode;
+use Email::Address;
+
+sub request {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $valid_userid = $mgr->config->valid_userid;
+
+ # first time: form
+ # second time with error: error message + form
+ # second time without error: OK message
+ # bot debunked? => "Thank you!"
+
+ my $showform = 0;
+ my $regOK = 0;
+
+ if ($req->param('url')) { # debunked
+ $c->stash(format => 'text');
+ $c->render(text => "Thank you!");
+ return;
+ }
+
+ my $fullname = $req->param('pause99_request_id_fullname') || "";
+ my $ufullname = PAUSE::Web::Util::Encode::any2utf8($fullname);
+ if ($ufullname ne $fullname) {
+ $req->param("pause99_request_id_fullname" => $ufullname);
+ $fullname = $ufullname;
+ }
+ my $email = $req->param('pause99_request_id_email') || "";
+ my $homepage = $req->param('pause99_request_id_homepage') || "";
+ my $userid = $req->param('pause99_request_id_userid') || "";
+ my $rationale = $req->param("pause99_request_id_rationale") || "";
+ my $token = $req->param("g-recaptcha-response") || "";
+ my $urat = PAUSE::Web::Util::Encode::any2utf8($rationale);
+ if ($urat ne $rationale) {
+ $req->param("pause99_request_id_rationale" => $urat);
+ $rationale = $urat;
+ }
+ warn sprintf(
+ "userid[%s]Valid_Userid[%s]args[%s]",
+ $userid,
+ $valid_userid,
+ scalar($req->url->query)||"",
+ );
+
+ if ( $req->param("SUBMIT_pause99_request_id_sub") ) {
+ # check for errors
+
+ my @errors = ();
+ if ( $fullname ) {
+ unless ($fullname =~ /[ ]/) {
+ push @errors, "Name does not look like a full civil name. Please accept our apologies if you believe we're wrong. In this case please write to @{$PAUSE::Config->{ADMINS}}.";
+ }
+ } else {
+ push @errors, "You must supply a name\n";
+ }
+ if( $email ) {
+ my $addr_spec = $Email::Address::addr_spec;
+ push @errors, "Your email address doesn't look like valid email address.\n" unless $email =~ /\A$addr_spec\z/;
+ } else {
+ push @errors, "You must supply an email address\n";
+ }
+ if ( $rationale ) {
+
+ $rationale =~ s/^\s+//;
+ $rationale =~ s/\s+$//;
+ $rationale =~ s/\s+/ /;
+ push @errors, "Thank you for giving us a short description of
+ what you're planning to contribute, but frankly, this looks a
+ bit too short" if length($rationale)<10;
+ push @errors, "Please do not use HTML links in your description of
+ what you're planning to contribute" if $rationale =~ /<\s*a\s+href\s*=/ims;
+
+ my $url_count =()= $rationale =~ m{https?://}gi;
+ push @errors, "Please do not include more than one URL in your description of
+ what you're planning to contribute" if $url_count > 1;
+
+ } else {
+
+ push @errors, "You must supply a short description of what
+ you're planning to contribute\n";
+
+ }
+ if ( $userid ) {
+ $userid = uc $userid;
+ $req->param('pause99_request_id_userid' => $userid);
+ my $db = $mgr->connect;
+ my $sth = $db->prepare("SELECT userid FROM users WHERE userid=?");
+ $sth->execute($userid);
+ warn sprintf("userid[%s]Valid_Userid[%s]matches[%s]",
+ $userid,
+ $valid_userid,
+ $userid =~ $valid_userid || "",
+ );
+ if ($sth->rows > 0) {
+ push @errors, "The userid $userid is already taken.";
+ } elsif ($userid !~ $valid_userid) {
+ push @errors, "The userid $userid does not match $valid_userid.";
+ }
+ $sth->finish;
+ } else {
+ push @errors, "You must supply a desired user-ID\n";
+ }
+ if ( $PAUSE::Config->{RECAPTCHA_ENABLED} && ! $token ) {
+ push @errors, "You must complete the recaptcha to proceed\n";
+ }
+ if( @errors ) {
+ $pause->{errors} = \@errors;
+ $showform = 1;
+ } else {
+ $regOK = 1;
+ }
+ } else {
+ $showform = 1;
+ }
+ $pause->{showform} = $showform;
+ $pause->{reg_ok} = $regOK;
+
+ if ($regOK) {
+ if ( $PAUSE::Config->{RECAPTCHA_ENABLED} ) {
+ if ( $c->auto_registration_rate_limit_ok ) {
+ $pause->{recaptcha_enabled} = 1;
+ my ($valid, $err) = $c->verify_recaptcha($token);
+ if ( $valid ) {
+ # If recaptcha is valid, we shortcut and add the user directly,
+ # returning HTML for them to see.
+ return $c->_directly_add_user($userid, $fullname);
+ }
+ elsif ( defined $valid && ! $valid ) {
+ die PAUSE::Web::Exception->new(ERROR => "recaptcha failed validation: $err\n");
+ }
+ # else recapture couldn't complete so continue with normal
+ # ID request moderation
+ } else {
+ warn "reCAPTCHA rate limit is exceeded";
+ }
+ }
+
+ my @to = $mgr->config->mailto_admins;
+ push @to, $email;
+ $pause->{send_to} = "@to";
+ my $time = time;
+ if ($rationale) {
+ # wrap it
+ $rationale =~ s/\r\n/\n/g;
+ $rationale =~ s/\r/\n/g;
+ my @rat = split /\n\n/, $rationale;
+ my $tf = Text::Format->new( bodyIndent => 4, firstIndent => 5);
+ $rationale = $tf->paragraphs(@rat);
+ $rationale =~ s/^\s{5}/\n /gm;
+ }
+
+ my $session = $c->new_session_counted;
+ $session->{APPLY} = {
+ fullname => $fullname,
+ email => $email,
+ homepage => $homepage,
+ userid => $userid,
+ rationale => $rationale,
+ };
+ require Data::Dumper;
+ my $message = "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->new([$session->{APPLY}],[qw(APPLY)])->Indent(1)->Useqq(1)->Dump;
+ $c->app->pause->log({level => 'debug', message => $message });
+ if (lc($fullname) eq lc($userid)) {
+ die PAUSE::Web::Exception->new(ERROR => "fullname looks like spam");
+ }
+ if (my @x = $rationale =~ /(\.info)/g) {
+ die PAUSE::Web::Exception->new(ERROR => "rationale looks like spam") if @x >= 5;
+ }
+ if (my @x = $rationale =~ m|(http://)|g) {
+ die PAUSE::Web::Exception->new(ERROR => "rationale looks like spam") if @x >= 5;
+ }
+ if ($rationale =~ /interesting/i && $homepage =~ m|http://[^/]+\.cn/.+\.htm$|) {
+ die PAUSE::Web::Exception->new(ERROR => "rationale looks like spam");
+ }
+
+ $pause->{fullname} = $fullname;
+ $pause->{userid} = $userid;
+ $pause->{homepage} = $homepage;
+ $pause->{rationale} = $rationale;
+
+ $pause->{session_id} = $c->session_counted_userid;
+ my $subject = "PAUSE ID request ($userid; $fullname)";
+ my $header = {
+ To => $email,
+ Subject => $subject,
+ };
+ my $blurb = $c->render_to_string("email/public/request_id", format => "email");
+
+ require HTML::Entities;
+ my($blurbcopy) = HTML::Entities::encode($blurb,qq{<>&"});
+ $blurbcopy =~ s{(
+ https?://
+ [^"'<>\s]+ # arbitrary exclusions, we had \S there,
+ # but it broke too often
+ )
+ }{$1}xg;
+ $blurbcopy =~ s|(>http.*?)U|$1\n U|gs; # break the long URL
+
+ $pause->{subject} = $subject;
+ $pause->{blurbcopy} = $blurbcopy;
+
+ $header = {
+ Subject => $subject
+ };
+ warn "To[@to]Subject[$header->{Subject}]";
+ $mgr->send_mail_multi(\@to,$header,$blurb);
+ }
+}
+
+sub _directly_add_user {
+ my ($c, $userid, $fullname) = @_;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $T = time;
+ my $dbh = $mgr->connect;
+ local ( $dbh->{RaiseError} ) = 0;
+
+ my ( $query, $sth, @qbind );
+ my ($email) = $req->param('pause99_request_id_email');
+ my ($homepage) = $req->param('pause99_request_id_homepage');
+ $query = qq{INSERT INTO users (
+ userid, email, homepage, fullname,
+ isa_list, introduced, changed, changedby)
+ VALUES (
+ ?, ?, ?, ?,
+ ?, ?, ?, ?)};
+ @qbind =
+ ( $userid, "CENSORED", $homepage, $fullname, "", $T, $T, 'RECAPTCHA' );
+
+ # We have a query for INSERT INTO users
+
+ if ( $dbh->do( $query, undef, @qbind ) ) {
+ $pause->{added_user} = 1;
+ # Not a mailinglist: set and send one time password
+ my $onetime = $c->set_onetime_password( $userid, $email );
+ $c->send_otp_email( $userid, $email, $onetime );
+
+ # send emails to user and modules@perl.org; latter must censor the
+ # user's email address
+ my ( $subject, $blurb ) =
+ $c->send_welcome_email( [$email], $userid, $email, $fullname, $homepage,
+ $fullname );
+ $c->send_welcome_email( $PAUSE::Config->{ADMINS},
+ $userid, "CENSORED", $fullname, $homepage, $fullname );
+
+ $pause->{subject_for_user_addition} = $subject;
+ $pause->{blurb_for_user_addition} = $blurb;
+
+ warn "Info: clearing all fields";
+ for my $field (qw(userid fullname email homepage subscribe)) {
+ my $param = "pause99_request_id_$field";
+ $req->param( $param, "" );
+ }
+ }
+ else {
+ warn qq{New user creation failed: [$query] failed. Reason: } . $dbh->errstr;
+ # TODO should notify administrators if this occurs
+ }
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/Root.pm b/lib/pause_2025/PAUSE/Web/Controller/Root.pm
new file mode 100644
index 000000000..c2e2397fc
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/Root.pm
@@ -0,0 +1,50 @@
+package PAUSE::Web::Controller::Root;
+
+use Mojo::Base "Mojolicious::Controller";
+
+sub check {
+ my $c = shift;
+
+ if ($c->pause_is_closed) {
+ my $user = $c->req->env->{REMOTE_USER};
+ if ($user and $user eq "ANDK") {
+ } else {
+ $c->render("closed");
+ return;
+ }
+ }
+
+ return 1;
+}
+
+sub index {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ return unless exists $pause->{User};
+ my $u = $c->active_user_record;
+
+ # Special case for cpan-uploaders that post to the /pause/authenquery without any ACTION
+ return unless $u->{userid};
+ return unless uc $req->method eq 'POST';
+ return unless $req->param('SUBMIT_pause99_add_uri_HTTPUPLOAD') || $req->param('SUBMIT_pause99_add_uri_httpupload');
+
+ my $action = 'add_uri';
+ $req->param('ACTION' => $action);
+ $pause->{Action} = $action;
+
+ # kind of delegate but don't add action to stack
+ my $routes = $c->app->routes;
+ my $route = $routes->lookup($action) or die "no route for $action";
+ my $to = $route->to;
+ $routes->_controller($c, $to);
+}
+
+sub auth {
+ my $c = shift;
+ return 1;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User.pm b/lib/pause_2025/PAUSE/Web/Controller/User.pm
new file mode 100644
index 000000000..d15ffede7
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User.pm
@@ -0,0 +1,420 @@
+package PAUSE::Web::Controller::User;
+
+use Mojo::Base "Mojolicious::Controller";
+use File::pushd;
+use PAUSE ();
+use Set::Crontab;
+
+sub edit_uris {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $selectedid = "";
+ my $selectedrec = {};
+ if (my $param = $req->param("pause99_edit_uris_3")) { # upper selectbox
+ $selectedid = $param;
+ }
+ my $u = $c->active_user_record;
+
+ my $dbh = $mgr->connect;
+ my $sql = qq{SELECT uriid
+ FROM uris
+ WHERE dgot=''
+ AND userid=?
+ ORDER BY uriid};
+ my $sth = $dbh->prepare($sql);
+ $sth->execute($u->{userid});
+
+ my @all_recs;
+ my %labels;
+ if (my $rows = $sth->rows) {
+ my $sth2 = $dbh->prepare(qq{SELECT *
+ FROM uris
+ WHERE dgot=''
+ AND dverified=''
+ AND uriid=?
+ AND userid=?});
+ while (my($id) = $mgr->fetchrow($sth, "fetchrow_array")) {
+ # register this mailinglist for the selectbox
+ push @all_recs, $id;
+ # query for more info about it
+ $sth2->execute($id,$u->{userid}); # really needed only for the
+ # record we want to edit, but
+ # maybe also needed for a
+ # label in the selectbox
+ my($rec) = $mgr->fetchrow($sth2, "fetchrow_hashref");
+ # we will display the name along the ID
+ # $labels{$id} = "$id ($rec->{userid})";
+ $labels{$id} = $id; # redundant, but flexible
+ if ($rows == 1 || $id eq $selectedid) {
+ # if this is the selected one, we just store it immediately
+ $selectedid = $id;
+ $selectedrec = $rec;
+ }
+ }
+ } else {
+ $pause->{no_pending_uploads} = 1;
+ return;
+ }
+
+ $pause->{all_recs} = [map {[$labels{$_} => $_]} @all_recs];
+ $pause->{selected} = $selectedrec;
+
+ if ($selectedid) {
+ my @m_rec;
+ my $force_sel = $req->param('pause99_edit_uris_2');
+ my $update_sel = $req->param('pause99_edit_uris_4');
+ $pause->{update_sel} = $update_sel;
+
+ my $saw_a_change;
+ my $now = time;
+
+ for my $field (qw(
+ uri
+ nosuccesstime
+ nosuccesscount
+ changed
+ changedby
+ )) {
+ my $fieldname = "pause99_edit_uris_$field";
+ if ($force_sel) {
+ $req->param($fieldname, $selectedrec->{$field}||"");
+ } elsif ($update_sel && $field eq "uri") {
+ my $param = $req->param($fieldname);
+ if ($param ne $selectedrec->{$field}) {
+ # no, we do not double check for user here. What if they
+ # change the owner? And we do not prepare outside the loop
+ # because the is a $fields in there
+ my $sql = qq{UPDATE uris
+ SET $field=?,
+ changed=?,
+ changedby=?
+ WHERE uriid=?};
+
+ my $usth = $dbh->prepare($sql);
+ my $ret = $usth->execute($param,
+ $now,
+ $u->{userid},
+ $selectedrec->{uriid});
+
+ $saw_a_change = 1 if $ret > 0;
+ $usth->finish;
+ }
+ }
+ }
+
+ if ($saw_a_change) {
+ $pause->{changed} = 1;
+
+ my $mailbody = $c->render_to_string("email/user/edit_uris", format => "email");
+ my @to = $mgr->prepare_sendto($u, $pause->{User}, $mgr->config->mailto_admins);
+ my $header = {
+ Subject => "Uri update for $selectedrec->{uriid}"
+ };
+ $mgr->send_mail_multi(\@to,$header,$mailbody);
+ }
+ }
+}
+
+sub reindex {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $u = $c->active_user_record;
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+ my $userhome = PAUSE::user2dir($u->{userid});
+ $pause->{userhome} = $userhome;
+
+ my $pushd = eval { pushd("$PAUSE::Config->{MLROOT}/$userhome") } or return;
+
+ my $blurb = "";
+ my $server = $PAUSE::Config->{SERVER_NAME} || $req->url->to_abs->host;
+ if ($req->param('SUBMIT_pause99_reindex_delete')) {
+
+ my $sql = "DELETE FROM distmtimes
+ WHERE dist = ?";
+ my $sth = $dbh->prepare($sql);
+ foreach my $f (@{$req->every_param('pause99_reindex_FILE')}) {
+ if ($f =~ m,^/, || $f =~ m,/\.\./,) {
+ $blurb .= "WARNING: illegal filename: $userhome/$f\n";
+ next;
+ }
+ unless (-f $f){
+ $blurb .= "WARNING: file not found: $userhome/$f\n";
+ next;
+ }
+ if ($f =~ m{ (^|/) CHECKSUMS }x) {
+ $blurb .= "WARNING: indexing CHECKSUMS considered unnecessary: $userhome/$f\n";
+ next;
+ }
+ # delete from distmtimes where distmtimes.dist like '%SREZIC%Tk-DateE%';
+ my $ret = $sth->execute("$userhome/$f");
+ if ($ret > 0) {
+ $blurb .= "\$CPAN/authors/id/$userhome/$f\n";
+ } else {
+ $blurb .= "WARNING: $userhome/$f has never been indexed.\n"
+ . "(Maybe it's not a stable release and will not get (re)indexed.)\n";
+ next;
+ }
+ }
+ }
+ if ($blurb) {
+ my $eta;
+ {
+ my $ctf = "$PAUSE::Config->{CRONPATH}/CRONTAB.ROOT"; # crontabfile
+ unless (-f $ctf) {
+ $ctf = "/tmp/crontab.root";
+ }
+ if (-f $ctf) {
+ open my $fh, "<", $ctf or die "XXX";
+ local $/ = "\n";
+ my $minute;
+ while (<$fh>) {
+ s/\#.*//;
+ next unless /mldistwatch/;
+ ($minute) = split " ", $_, 2;
+ last;
+ }
+ my $sc;
+ eval { $sc = Set::Crontab->new($minute,[0..59]); };
+ if ($@) {
+ warn "Could not create a Crontab object: $@ (minute[$minute])";
+ $eta = "N/A";
+ } else {
+ my $now = time;
+ $now -= $now%60;
+ for (my $i = 1; $i<=60; $i++) {
+ my $fut = $now + $i * 60;
+ my $fum = int $fut % 3600 / 60;
+ next unless $sc->contains($fum);
+ $eta = gmtime( $fut + $PAUSE::Config->{RUNTIME_MLDISTWATCH} ) . " UTC";
+ last;
+ }
+ }
+ } else {
+ warn "Not found: $ctf";
+ $eta = "N/A";
+ }
+ }
+ $pause->{blurb} = $blurb;
+ $pause->{eta} = $eta;
+
+ my @to = $mgr->prepare_sendto($u, $pause->{User}, $PAUSE::Config->{ADMIN});
+ my $mailbody = $c->render_to_string("email/user/reindex", format => "email");
+ my $header = {
+ Subject => "Scheduled for reindexing $u->{userid}"
+ };
+ $mgr->send_mail_multi(\@to, $header, $mailbody);
+
+ $pause->{mailbody} = $mailbody;
+ }
+
+ my %files = $c->manifind;
+
+ foreach my $f (keys %files) {
+ if (
+ $f =~ /readme$/ ||
+ $f eq "CHECKSUMS"
+ ) {
+ delete $files{$f};
+ next;
+ }
+ }
+ $pause->{files} = \%files;
+}
+
+sub reset_version {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+
+ my $blurb = "";
+ my($usersubstr) = sprintf("%s/%s/%s/",
+ substr($u->{userid},0,1),
+ substr($u->{userid},0,2),
+ $u->{userid},
+ );
+ my($usersubstrlen) = length $usersubstr;
+
+ my $sqls = "SELECT package, version, dist FROM packages
+ WHERE substring(dist,1,$usersubstrlen) = ?";
+ my $sths = $dbh->prepare($sqls);
+ if ($req->param('SUBMIT_pause99_reset_version_forget')) {
+ my $sqls2 = "SELECT version FROM packages
+ WHERE package = ? AND substring(dist,1,$usersubstrlen) = ?";
+ my $sths2 = $dbh->prepare($sqls2);
+ my $sqlu = "UPDATE packages
+ SET version='undef'
+ WHERE package = ? AND substring(dist,1,$usersubstrlen) = ?";
+ my $sthu = $dbh->prepare($sqlu);
+ PKG: foreach my $f (@{$req->every_param('pause99_reset_version_PKG')}) {
+ $sths2->execute($f,$usersubstr);
+ my($version) = $sths2->fetchrow_array;
+ next PKG if $version eq 'undef';
+ my $ret = $sthu->execute($f,$usersubstr);
+ $blurb .= sprintf(
+ "%s: %s '%s' => 'undef'\n",
+ $ret==0 ? "Not reset" : "Reset",
+ $f,
+ $version,
+ );
+ }
+ }
+
+ if ($blurb) {
+ $pause->{blurb} = $blurb;
+
+ my @to = $mgr->prepare_sendto($u, $pause->{User}, $PAUSE::Config->{ADMIN});
+ my $mailbody = $c->render_to_string("email/user/reset_version", format => "email");
+ my $header = {
+ Subject => "Version reset for $u->{userid}"
+ };
+ $mgr->send_mail_multi(\@to, $header, $mailbody);
+
+ $pause->{mailbody} = $mailbody;
+ }
+ $sths->execute($usersubstr);
+ if ($sths->rows == 0) {
+ return;
+ }
+
+ my %packages;
+ while (my($package, $version, $dist) = $sths->fetchrow_array) {
+ $packages{$package} = {version => $version, dist => $dist};
+ }
+ $pause->{packages} = \%packages;
+}
+
+sub tail_logfile {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $req = $c->req;
+
+ my $tail = $req->param("pause99_tail_logfile_1") || 5000;
+ my $file = $PAUSE::Config->{PAUSE_LOG};
+ if ($PAUSE::Config->{TESTHOST}) {
+ $file = "/usr/local/apache/logs/error_log"; # for testing
+ }
+ open my $fh, "<", $file or die "Could not open $file: $!";
+ seek $fh, -$tail, 2;
+ local($/);
+ $/ = "\n";
+ <$fh>;
+ $/ = undef;
+
+ $pause->{tail} = <$fh>;
+}
+
+sub change_passwd {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $u = eval { $c->active_user_record };
+ die PAUSE::Web::Exception->new(ERROR => "User not found", HTTP_STATUS => 401) if $@;
+
+ if (uc $req->method eq 'POST' and $req->param("pause99_change_passwd_sub")) {
+ if (my $pw1 = $req->param("pause99_change_passwd_pw1")) {
+ if (my $pw2 = $req->param("pause99_change_passwd_pw2")) {
+ if ($pw1 eq $pw2) {
+ # create a new crypted password, store it, report
+ my $pwenc = PAUSE::Crypt::hash_password($pw1);
+ my $dbh = $mgr->authen_connect;
+ my $sql = qq{UPDATE $PAUSE::Config->{AUTHEN_USER_TABLE}
+ SET $PAUSE::Config->{AUTHEN_PASSWORD_FLD} = ?,
+ forcechange = ?,
+ changed = ?,
+ changedby = ?
+ WHERE $PAUSE::Config->{AUTHEN_USER_FLD} = ?};
+ # warn "sql[$sql]";
+ my $rc = $dbh->do($sql,undef,
+ $pwenc,0,time,$pause->{User}{userid},$u->{userid});
+ warn "rc[$rc]";
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ sprintf qq[Could not set password: '%s'], $dbh->errstr
+ ) unless $rc;
+ if ($rc == 0) {
+ $sql = qq{INSERT INTO $PAUSE::Config->{AUTHEN_USER_TABLE}
+ ($PAUSE::Config->{AUTHEN_USER_FLD},
+ $PAUSE::Config->{AUTHEN_PASSWORD_FLD},
+ forcechange,
+ changed,
+ changedby ) VALUES
+ (?, ?, ?, ?, ?)
+ };
+ $rc = $dbh->do($sql,undef,
+ $u->{userid},
+ $pwenc,
+ 0,
+ time,
+ $pause->{User}{userid},
+ $u->{userid}
+ );
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ sprintf qq[Could not insert user record: '%s'], $dbh->errstr
+ ) unless $rc;
+ }
+ for my $anon ($pause->{User}, $u) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "Panic: unknown user") unless $anon->{userid};
+ next if $anon->{fullname};
+ $mgr->log({level => 'error', message => "Unknown fullname for $anon->{userid}!" });
+ }
+ $pause->{password_stored} = 1;
+
+ my @to = $mgr->prepare_sendto($u, $pause->{User});
+ my $header = {Subject => "Password Update"};
+ my $mailbody = $c->render_to_string("email/user/change_passwd", format => "email");
+ $mgr->send_mail_multi(\@to, $header, $mailbody);
+
+ # Remove used token
+ $sql = qq{DELETE FROM abrakadabra WHERE user = ?};
+ $rc = $dbh->do($sql, undef, $u->{userid});
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ sprintf qq[Could not delete token: '%s'], $dbh->errstr
+ ) unless $rc;
+ $mgr->log({level => 'info', message => "Removed used token for $u->{userid}" });
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "The two passwords didn't match.");
+ }
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You need to fill in the same password in both fields.");
+ }
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "Please fill in the form with passwords.");
+ }
+ }
+}
+
+sub pause_logout {
+ my $c = shift;
+ $c->serve_pause_doc("logout.html", \&_fix_logout);
+}
+
+sub _fix_logout {
+ my $html = shift;
+ my $rand = rand 1;
+ # the redirect solutions fail miserably the second time when tried
+ # with the exact same querystring again.
+ $html =~ s/__RANDOMSTRING__/$rand/g;
+ $html;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User/Cred.pm b/lib/pause_2025/PAUSE/Web/Controller/User/Cred.pm
new file mode 100644
index 000000000..680ed8c4c
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User/Cred.pm
@@ -0,0 +1,221 @@
+package PAUSE::Web::Controller::User::Cred;
+
+use Mojo::Base "Mojolicious::Controller";
+use Email::Address;
+use PAUSE::Web::Util::Encode;
+use Text::Unidecode;
+
+sub edit {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my ($u, $nu); # user, newuser
+ $u = $c->active_user_record;
+
+ # @allmeta *must* be the union of meta and secmeta
+ my @meta = qw( fullname asciiname email homepage cpan_mail_alias ustatus);
+ my @secmeta = qw(secretemail);
+ my @allmeta = qw( fullname asciiname email secretemail homepage cpan_mail_alias ustatus);
+
+ my $cpan_alias = lc($u->{userid}) . '@cpan.org';
+
+ my %meta = map {$_ => 1} @allmeta;
+
+ my $consistentsubmit = 0;
+ if (uc $req->method eq 'POST' and $req->param("pause99_edit_cred_sub")) {
+ my $wantemail = $req->param("pause99_edit_cred_email");
+ my $wantsecretemail = $req->param("pause99_edit_cred_secretemail");
+ my $wantalias = $req->param("pause99_edit_cred_cpan_mail_alias");
+ my $addr_spec = $Email::Address::addr_spec;
+ if ($wantemail=~/^\s*$/ && $wantsecretemail=~/^\s*$/) {
+ $pause->{error}{no_email} = 1;
+ } elsif ($wantalias eq "publ" && $wantemail=~/^\s*$/) {
+ $pause->{error}{no_public_email} = 1;
+ } elsif ($wantalias eq "publ" && $wantemail=~/\Q$cpan_alias\E/i) {
+ $pause->{error}{public_is_cpan_alias} = 1;
+ } elsif ($wantalias eq "secr" && $wantsecretemail=~/^\s*$/) {
+ $pause->{error}{no_secret_email} = 1;
+ } elsif ($wantalias eq "secr" && $wantsecretemail=~/\Q$cpan_alias\E/i) {
+ $pause->{error}{secret_is_cpan_alias} = 1;
+ } elsif (defined $wantsecretemail && $wantsecretemail!~/^\s*$/ && $wantsecretemail!~/^\s*$addr_spec\s*$/) {
+ $pause->{error}{invalid_secret} = 1;
+ } elsif (defined $wantemail && $wantemail!~/^\s*$/ && $wantemail!~/^\s*$addr_spec\s*$/ && $wantemail ne 'CENSORED') {
+ $pause->{error}{invalid_public} = 1;
+ } else {
+ $consistentsubmit = 1;
+ }
+
+ if ($consistentsubmit) {
+ # more testing: make sure that we have in asciiname only ascii
+ if (my $wantasciiname = $req->param("pause99_edit_cred_asciiname")) {
+ if ($wantasciiname =~ /[^\040-\177]/) {
+ $pause->{error}{not_ascii} = 1;
+ $consistentsubmit = 0;
+ } else {
+ # set asciiname to empty if it equals fullname
+ my $wantfullname = $req->param("pause99_edit_cred_fullname");
+ if ($wantfullname eq $wantasciiname) {
+ $req->param("pause99_edit_cred_asciiname", "");
+ }
+ }
+ } else {
+ # set asciiname on our own if they don't supply it
+ my $wantfullname = $req->param("pause99_edit_cred_fullname");
+ if ($wantfullname =~ /[^\040-\177]/) {
+ $wantfullname = PAUSE::Web::Util::Encode::any2utf8($wantfullname);
+ $wantasciiname = Text::Unidecode::unidecode($wantfullname);
+ $req->param("pause99_edit_cred_asciiname", $wantasciiname);
+ }
+ }
+ }
+ } else {
+ for my $field (@allmeta) {
+ unless ($meta{$field}){
+ warn "Someone tried strange field[$field], ignored";
+ next;
+ }
+ if ( $field eq "ustatus" ) {
+ if ( $u->{"ustatus"} eq "active" ) {
+ next;
+ }
+ }
+ $req->param("pause99_edit_cred_$field" => $u->{$field});
+ }
+ }
+
+ if ($consistentsubmit) {
+ $pause->{consistentsubmit} = 1;
+ my $saw_a_change;
+ my $now = time;
+
+ # We once duplicated nearly exactly the same code of 100 lines.
+ # Once for secretemail, once for the other attributes. Lines
+ # marked with four hashmarks are the ones that differ. Why not
+ # make it a function? Well, that function would have to take at
+ # least 5 arguments and we want some variables in the lexical
+ # scope. So I made it a loop for two complicated arrays.
+ for my $quid (
+ [
+ "connect",
+ \@meta,
+ "users",
+ "userid",
+ 1
+ ],
+ ["authen_connect",
+ \@secmeta,
+ $PAUSE::Config->{AUTHEN_USER_TABLE},
+ $PAUSE::Config->{AUTHEN_USER_FLD},
+ 0
+ ]
+ ) {
+ my($connect,$atmeta,$table,$column,$mailto_admins) = @$quid;
+ my(@set,@mailblurb);
+ my $dbh = $mgr->$connect(); #### the () for older perls
+ for my $field (@$atmeta) { ####
+ # warn "field[$field]";
+ # Ignore fields we do not intend to change
+ unless ($meta{$field}){
+ warn "Someone tried strange field[$field], ignored";
+ next;
+ }
+ # find out the form field name
+ my $form_field = "pause99_edit_cred_$field";
+ if ( $field eq "ustatus" ) {
+ if ( $u->{"ustatus"} eq "active" ) {
+ next;
+ } elsif (!$req->param($form_field)) {
+ $req->param($form_field,"unused");
+ }
+ }
+ # $s is the value they entered
+ my $s_raw = $req->param($form_field) || "";
+ # we're in edit_cred
+ my $s;
+ $s = PAUSE::Web::Util::Encode::any2utf8($s_raw);
+ $s =~ s/^\s+//;
+ $s =~ s/\s+\z//;
+ if ($s ne $s_raw) {
+ $req->param($form_field,$s);
+ }
+ $nu->{$field} = $s;
+ $u->{$field} = "" unless defined $u->{$field};
+ my $mb; # mailblurb
+ if ($u->{$field} ne $s) {
+ $saw_a_change = 1;
+ # No UTF8 running before we have the system walking
+ # my $utf = $mgr->formfield_as_utf8($s);
+ # unless ( $s eq $utf ) {
+ # $req->param($form_field, $utf);
+ # $s = $utf;
+ # }
+ # next if $pause->{User}{$field} eq $s;
+
+ # not ?-ising this as rely on quote() method
+ push @set, "$field = " . $dbh->quote($s);
+ $mb = {field => $field, value => $s, was => $u->{$field}};
+ if ($field eq "ustatus") {
+ push @set, "ustatus_ch = NOW()";
+ }
+ $u->{$field} = $s;
+ } else {
+ $mb = {field => $field, value => $s};
+ }
+ if ($field eq "secretemail") {
+ $mb = {field => $field, value => "CENSORED"};
+ }
+ push @mailblurb, $mb;
+ }
+
+ if (@set) {
+ my @query_params = ($now, $pause->{User}{userid}, $u->{userid});
+ my $sql = "UPDATE $table SET " . ####
+ join(", ", @set, "changed = ?, changedby=?") .
+ " WHERE $column = ?"; ####
+ $pause->{mailblurb} = \@mailblurb;
+ my $mailblurb = $c->render_to_string("email/user/cred/edit", format => "email");
+ # warn "sql[$sql]mailblurb[$mailblurb]";
+ # die;
+ if ($dbh->do($sql, undef, @query_params)) {
+ $pause->{registered}{$table} = 1;
+ $nu = $c->active_user_record($u->{userid});
+ if ($nu->{userid} && $nu->{userid} eq $pause->{User}{userid}) {
+ $pause->{User} = $nu;
+ }
+ # Send separate emails to user and public places because
+ # CC leaks secretemail to others
+ my @to;
+ my %umailset;
+ for my $lu ($u, $nu) {
+ for my $att (qw(secretemail email)) {
+ if ($lu->{$att}){
+ $umailset{qq{<$lu->{$att}>}} = 1;
+ last;
+ }
+ }
+ }
+ push @to, join ", ", keys %umailset;
+ push @to, $mgr->config->mailto_admins if $mailto_admins;
+ my $header = {Subject => "User update for $u->{userid}"};
+ $mgr->send_mail_multi(\@to,$header, $mailblurb);
+ } else {
+# FIXME
+ push @{$pause->{ERROR}}, sprintf(qq{Could not enter the data
+ into the database: %s.},$dbh->errstr);
+ }
+ }
+ } # end of quid loop
+
+ if ($saw_a_change) {
+ $pause->{saw_a_change} = 1;
+ # expire temporary token to free mailpw for immediate use
+ my $sql = qq{DELETE FROM abrakadabra
+ WHERE user = ?};
+ my $dbh = $mgr->authen_connect();
+ $dbh->do($sql,undef,$u->{userid});
+ }
+ }
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User/Distperms.pm b/lib/pause_2025/PAUSE/Web/Controller/User/Distperms.pm
new file mode 100644
index 000000000..fcc96716c
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User/Distperms.pm
@@ -0,0 +1,554 @@
+package PAUSE::Web::Controller::User::Distperms;
+
+use Mojo::Base "Mojolicious::Controller";
+
+sub peek {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ unless ($req->param("pause99_peek_dist_perms_query")) {
+ $req->param("pause99_peek_dist_perms_query" => $pause->{User}{userid});
+ }
+ unless ($req->param("pause99_peek_dist_perms_by")) {
+ $req->param("pause99_peek_dist_perms_by" => "a");
+ }
+
+ if (my $qterm = $req->param("pause99_peek_dist_perms_query")) {
+ my $by = $req->param("pause99_peek_dist_perms_by");
+ my $query = qq{SELECT packages.distname,
+ GROUP_CONCAT(DISTINCT primeur.userid ORDER BY primeur.userid),
+ GROUP_CONCAT(DISTINCT perms.userid ORDER BY perms.userid)
+ FROM packages LEFT JOIN primeur ON primeur.package=packages.package
+ LEFT JOIN perms ON perms.package=packages.package AND primeur.userid <> perms.userid
+ };
+
+ my $db = $mgr->connect;
+ my @res;
+ my %seen;
+ my $where;
+ my @bind;
+ if ($by =~ /^d/) {
+ @bind = ($qterm);
+ if ($by eq "de") {
+ $where = qq{WHERE packages.distname=? GROUP BY packages.distname};
+ } else {
+ $where = qq{WHERE packages.distname LIKE ? GROUP BY packages.distname LIMIT 1000};
+ # I saw 5.7.3 die with Out Of Memory on the query "%" when no
+ # Limit was applied
+ }
+ } elsif ($by eq "a") {
+ @bind = ($qterm, $qterm);
+ $where = qq{WHERE primeur.userid=? OR perms.userid=? GROUP BY packages.distname};
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "Illegal parameter for pause99_peek_dist_perms_by");
+ }
+ $query .= $where;
+ my $sth = $db->prepare($query);
+ $sth->execute(@bind);
+ if ($sth->rows > 0) {
+ # warn sprintf "query[%s]qterm[%s]rows[%d]", $query, $qterm, $sth->rows;
+ while (my @row = $mgr->fetchrow($sth, "fetchrow_array")) {
+ if ($seen{$row[0]}++){
+ # warn "Ignoring row[$row[0]][$row[1]]";
+ next;
+ }
+ push @res, \@row;
+ }
+ }
+ $sth->finish;
+ if (@res) {
+ my $dbh = $mgr->connect;
+ my @column_names = qw(dist owner comaint);
+ my $output_format = $req->param("OF");
+ if ($output_format){
+ my @hres;
+ for my $row (@res) {
+ push @hres, { map {$column_names[$_] => $row->[$_] } 0..$#$row };
+ }
+ if ($output_format eq "YAML") {
+ return $c->render_yaml(\@hres);
+ } else {
+ die "not supported OF=$output_format"
+ }
+ }
+ $pause->{column_names} = \@column_names;
+
+ @res = sort {
+ $a->[0] cmp $b->[0]
+ ||
+ $a->[1] cmp $b->[1]
+ ||
+ $a->[2] cmp $b->[2]
+ } @res;
+
+ $pause->{rows} = \@res;
+ }
+ }
+}
+
+sub move_dist_primary {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_dists = $c->all_pdists($u);
+
+ if (
+ $req->param("SUBMIT_pause99_move_dist_primary")
+ ) {
+ eval {
+ my(@seldists, $other_user);
+ if (@seldists = @{$req->every_param("pause99_move_dist_primary_d")}
+ and
+ $other_user = $req->param("pause99_move_dist_primary_a")
+ ) {
+ $other_user = uc $other_user;
+ my $sth1 = $db->prepare("SELECT userid
+ FROM users
+ WHERE userid=?");
+ $sth1->execute($other_user);
+ die PAUSE::Web::Exception
+ ->new(ERROR => "$other_user is not a valid userid.")
+ unless $sth1->rows;
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("UPDATE primeur SET userid=? WHERE package=?");
+ my @results;
+ for my $seldist (@seldists) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $seldist")
+ unless exists $all_dists->{$seldist};
+ my $mods = $db->selectcol_arrayref(
+ q{SELECT primeur.package
+ FROM primeur JOIN packages ON primeur.package = packages.package
+ WHERE packages.distname=? AND primeur.userid=?},
+ undef, $seldist, $u->{userid});
+ for my $selmod (@$mods) {
+ my $ret = $sth->execute($other_user,$selmod);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: seldist[$seldist]selmod[$selmod]other_user[$other_user]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ dist => $seldist,
+ };
+ } else {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ dist => $seldist,
+ error => $err,
+ };
+ }
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_dists = $c->all_pdists($u); # again
+ my @all_dists = map {[$_, $all_dists->{$_}]} sort keys %$all_dists;
+ $pause->{dists} = \@all_dists;
+
+ if (@all_dists == 1) {
+ $req->param("pause99_move_dist_primary_d" => $all_dists[0][0]);
+ }
+}
+
+sub remove_dist_primary {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_dists = $c->all_pdists($u);
+
+ if (0) {
+ # here I discovered that Apache::Request has case-insensitive keys
+ my %p = map { $_, [ $req->every_param($_)] } @{$req->param->names};
+ require Data::Dumper;
+ my $message = "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->new([\%p],[qw()])->Indent(1)->Useqq(1)->Dump;
+ $c->app->pause->log({level => 'debug', message => $message});
+ }
+
+ if (
+ $req->param("SUBMIT_pause99_remove_dist_primary")
+ ) {
+ eval {
+ my(@seldists);
+ if (@seldists = @{$req->every_param("pause99_remove_dist_primary_d")}
+ ) {
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("UPDATE primeur SET userid=? WHERE userid=? AND package=?");
+
+ my @results;
+ for my $seldist (@seldists) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $seldist")
+ unless exists $all_dists->{$seldist};
+ my $mods = $db->selectcol_arrayref(
+ q{SELECT primeur.package
+ FROM primeur JOIN packages ON primeur.package = packages.package
+ WHERE packages.distname=? AND primeur.userid=?},
+ undef, $seldist, $u->{userid});
+ for my $selmod (@$mods) {
+ my $ret = $sth->execute('ADOPTME',$u->{userid},$selmod);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: seldist[$seldist]selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ dist => $seldist,
+ };
+ } else {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ dist => $seldist,
+ error => $err,
+ };
+ }
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ }
+
+ $all_dists = $c->all_pdists($u); # again
+ my @all_dists = map {[$_, $all_dists->{$_}]} sort keys %$all_dists;
+ $pause->{dists} = \@all_dists;
+
+ if (@all_dists == 1) {
+ $req->param("pause99_remove_dist_primary_d" => $all_dists[0][0]);
+ }
+}
+
+sub make_dist_comaint {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+ # warn "u->userid[%s]", $u->{userid};
+
+ my $db = $mgr->connect;
+
+ my $all_dists = $c->all_pdists($u);
+ # warn sprintf "all_pdists[%s]", join("|", keys %$all_pdists);
+
+ if (
+ $req->param("SUBMIT_pause99_make_dist_comaint")
+ ) {
+ eval {
+ my(@seldists,$other_user);
+ if (@seldists = @{$req->every_param("pause99_make_dist_comaint_d")}
+ and
+ $other_user = $req->param("pause99_make_dist_comaint_a")
+ ) {
+ $other_user = uc $other_user;
+ my $sth1 = $db->prepare("SELECT userid
+ FROM users
+ WHERE userid=?");
+ $sth1->execute($other_user);
+ die PAUSE::Web::Exception
+ ->new(ERROR => sprintf(
+ "$other_user is not a valid userid.",
+ )
+ )
+ unless $sth1->rows;
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("INSERT INTO perms (package,lc_package,userid)
+ VALUES (?,?,?)");
+ my @results;
+ for my $seldist (@seldists) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $seldist")
+ unless exists $all_dists->{$seldist};
+ my $mods = $db->selectcol_arrayref(
+ q{SELECT primeur.package
+ FROM primeur JOIN packages ON primeur.package = packages.package
+ WHERE packages.distname=? AND primeur.userid=?},
+ undef, $seldist, $u->{userid});
+ for my $selmod (@$mods) {
+ my $ret = $sth->execute($selmod,lc $selmod,$other_user);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: seldist[$seldist]selmod[$selmod]other_user[$other_user]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ dist => $seldist,
+ };
+ } elsif ($err =~ /Duplicate entry/) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ dist => $seldist,
+ duplicated => 1,
+ };
+ } else {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ dist => $seldist,
+ error => $err,
+ };
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_dists = $c->all_pdists($u); # again
+ my @all_dists = map {[$_, $all_dists->{$_}]} sort keys %$all_dists;
+ $pause->{dists} = \@all_dists;
+
+ if (@all_dists == 1) {
+ $req->param("pause99_make_dist_comaint_d" => $all_dists[0][0]);
+ }
+}
+
+sub remove_dist_comaint {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_dists = $c->all_pdists($u);
+ my $all_comaints = $c->all_comaints($all_dists,$u);
+
+ if (
+ $req->param("SUBMIT_pause99_remove_dist_comaint")
+ ) {
+ eval {
+ my @sel = @{$req->every_param("pause99_remove_dist_comaint_tuples")};
+ my $sth1 = $db->prepare("DELETE FROM perms WHERE package=? AND userid=?");
+ if (@sel) {
+ my @results;
+ for my $sel (@sel) {
+ my($seldist,$otheruser) = $sel =~ /^(\S+)\s--\s(\S+)$/;
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be owner of $seldist.")
+ unless exists $all_dists->{$seldist};
+ unless (exists $all_comaints->{$sel}) {
+ push @results, {
+ sel => $sel,
+ not_exists => 1,
+ };
+ next;
+ }
+ my $mods = $db->selectcol_arrayref(
+ q{SELECT primeur.package
+ FROM primeur JOIN packages ON primeur.package = packages.package
+ WHERE packages.distname=? AND primeur.userid=?},
+ undef, $seldist, $u->{userid});
+ for my $selmod (@$mods) {
+ my $ret = $sth1->execute($selmod,$otheruser);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: seldist[$seldist]selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $otheruser,
+ mod => $selmod,
+ dist => $seldist,
+ };
+ } else {
+ push @results, {
+ user => $otheruser,
+ mod => $selmod,
+ dist => $seldist,
+ error => $err,
+ };
+ }
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_comaints = $c->all_comaints($all_dists,$u); # again
+ my @all = sort keys %$all_comaints;
+ $pause->{dists} = \@all;
+}
+
+sub giveup_dist_comaint {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+ my $db = $mgr->connect;
+
+ my $all_dists = $c->all_only_cdists($u);
+
+ if (
+ $req->param("SUBMIT_pause99_giveup_dist_comaint")
+ ) {
+ eval {
+ my(@seldists);
+ if (@seldists = @{$req->every_param("pause99_giveup_dist_comaint_d")}
+ ) {
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("DELETE FROM perms WHERE package=? AND userid=?");
+
+ my @results;
+ for my $seldist (@seldists) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be co-maintainer of $seldist")
+ unless exists $all_dists->{$seldist};
+ my $mods = $db->selectcol_arrayref(
+ q{SELECT perms.package
+ FROM perms JOIN packages ON perms.package = packages.package
+ WHERE packages.distname=? AND perms.userid=?},
+ undef, $seldist, $u->{userid});
+ for my $selmod (@$mods) {
+ my $ret = $sth->execute($selmod,$u->{userid});
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: seldist[$seldist]selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ dist => $seldist,
+ };
+ delete $all_dists->{$seldist};
+ } else {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ dist => $seldist,
+ error => $err,
+ };
+ }
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_dists = $c->all_only_cdists($u); # again
+ my @all_dists = map {[$_, $all_dists->{$_}]} sort keys %$all_dists;
+ $pause->{dists} = \@all_dists;
+
+ if (@all_dists == 1) {
+ $req->param("pause99_giveup_dist_comaint_d" => $all_dists[0][0]);
+ }
+}
+
+sub all_pdists {
+ my ($c, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ my(%all_dists);
+# XXX: This query was too slow under mysql 5.1...
+# qq{SELECT packages.distname, GROUP_CONCAT(DISTINCT p3.userid ORDER BY p3.userid)
+# FROM packages JOIN primeur ON primeur.userid = ? AND primeur.package=packages.package
+# LEFT JOIN packages AS p2 ON packages.distname = p2.distname
+# LEFT JOIN primeur AS p3 ON p2.package = p3.package GROUP BY packages.distname});
+ my $sth2 = $db->prepare(
+ qq{SELECT packages.distname
+ FROM packages JOIN primeur ON primeur.userid = ? AND primeur.package=packages.package});
+ $sth2->execute($u->{userid});
+ while (my($distname) = $mgr->fetchrow($sth2, "fetchrow_array")) {
+ next if $distname eq '';
+ my $owners = $db->selectcol_arrayref(
+ qq{SELECT DISTINCT(userid) FROM primeur JOIN packages ON packages.distname = ? AND primeur.package = packages.package},
+ undef, $distname);
+ $all_dists{$distname} = join ',', @$owners;
+ }
+ $sth2->finish;
+ \%all_dists;
+}
+
+sub all_cdists {
+ my ($c, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ my(%all_dists);
+ my $sth2 = $db->prepare(qq{SELECT packages.distname, GROUP_CONCAT(DISTINCT primeur.userid ORDER BY primeur.userid)
+ FROM packages
+ JOIN perms ON perms.userid = ? AND perms.package = packages.package
+ LEFT JOIN primeur ON packages.package = primeur.package
+ GROUP BY packages.distname});
+ $sth2->execute($u->{userid});
+ while (my($id, $owner) = $mgr->fetchrow($sth2, "fetchrow_array")) {
+ $all_dists{$id} = $owner;
+ }
+ $sth2->finish;
+ \%all_dists;
+}
+
+sub all_only_cdists {
+ my($c,$u) = @_;
+ my $all_pdists = $c->all_pdists($u);
+ my $all_dists = $c->all_cdists($u);
+
+ for my $k (keys %$all_pdists) {
+ delete $all_dists->{$k};
+ }
+ $all_dists;
+}
+
+sub all_comaints {
+ my ($c, $all_dists, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $result = {};
+ return $result unless %$all_dists;
+ my $db = $mgr->connect;
+ my $or = join " OR\n", map { "packages.distname='$_'" } keys %$all_dists;
+ my $sth2 = $db->prepare(qq{SELECT packages.distname, userid, perms.package
+ FROM perms LEFT JOIN packages ON perms.package = packages.package
+ WHERE userid <> '$u->{userid}' AND ( $or )
+ });
+ $sth2->execute;
+ while (my($d,$i,$p) = $mgr->fetchrow($sth2,"fetchrow_array")) {
+ $result->{"$d -- $i"}{$p} = undef;
+ warn "d[$d]p[$p]i[$i]";
+ }
+ return $result;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User/Files.pm b/lib/pause_2025/PAUSE/Web/Controller/User/Files.pm
new file mode 100644
index 000000000..822fc3790
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User/Files.pm
@@ -0,0 +1,218 @@
+package PAUSE::Web::Controller::User::Files;
+
+use Mojo::Base "Mojolicious::Controller";
+use HTTP::Date ();
+use File::pushd;
+use PAUSE ();
+
+sub show {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $u = $c->active_user_record;
+
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+ my $userhome = PAUSE::user2dir($u->{userid});
+ $pause->{userhome} = $userhome;
+
+ my $pushd = eval { pushd("$PAUSE::Config->{MLROOT}/$userhome") } or return;
+ warn "DEBUG: MLROOT[$PAUSE::Config->{MLROOT}] userhome[$userhome] E:M:V[$ExtUtils::Manifest::VERSION]";
+
+ my $time = time;
+ my %files = $c->manifind;
+ my (%deletes, %whendele, $sth);
+ if (
+ $sth = $dbh->prepare(qq{SELECT deleteid, changed
+ FROM deletes
+ WHERE deleteid
+ LIKE ?})
+ and
+ $sth->execute("$userhome/%")
+ and
+ $sth->rows
+ ) {
+ my $dhash;
+ while ($dhash = $mgr->fetchrow($sth, "fetchrow_hashref")) {
+ $dhash->{deleteid} =~ s/\Q$userhome\E\///;
+ $deletes{$dhash->{deleteid}}++;
+ $whendele{$dhash->{deleteid}} = $dhash->{changed};
+ }
+ }
+ $sth->finish if ref $sth;
+
+ my $indexed = $c->indexed($dbh, $u->{userid});
+
+ foreach my $f (keys %files) {
+ unless (stat $f) {
+ warn "ALERT: Could not stat f[$f]: $!";
+ next;
+ }
+ my $blurb = $deletes{$f} ?
+ $c->scheduled($whendele{$f}) :
+ HTTP::Date::time2str((stat _)[9]);
+ $files{$f} = {stat => -s _, blurb => $blurb, indexed => $indexed->{$f} };
+ }
+ $pause->{files} = \%files;
+}
+
+sub delete {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $dbh = $mgr->connect;
+ local($dbh->{RaiseError}) = 0;
+ my $userhome = PAUSE::user2dir($u->{userid});
+ $pause->{userhome} = $userhome;
+
+ my $pushd = eval { pushd("$PAUSE::Config->{MLROOT}/$userhome") } or return;
+ warn "DEBUG: MLROOT[$PAUSE::Config->{MLROOT}] userhome[$userhome] ExtUtils:Manifest:VERSION[$ExtUtils::Manifest::VERSION]";
+
+ my $time = time;
+ my $blurb = "";
+ if ($req->param('SUBMIT_pause99_delete_files_delete')) {
+
+ foreach my $f (@{$req->every_param('pause99_delete_files_FILE')}) {
+ if ($f =~ m,^/, || $f =~ m,/\.\./,) {
+ $blurb .= "WARNING: illegal filename: $userhome/$f\n";
+ next;
+ }
+ unless (-f $f){
+ $blurb .= "WARNING: file not found: $userhome/$f\n";
+ next;
+ }
+ if ($f =~ m{ (^|/) CHECKSUMS }x) {
+ $blurb .= "WARNING: CHECKSUMS not erasable: $userhome/$f\n";
+ next;
+ }
+ $dbh->do(
+ "INSERT INTO deletes VALUES (?, ?, ?)", undef,
+ "$userhome/$f", $time, "$pause->{User}{userid}"
+ ) or next;
+
+ $blurb .= "\$CPAN/authors/id/$userhome/$f\n";
+
+ # README
+ next if $f =~ /\.readme$/;
+ my $readme = $f;
+ $readme =~ s/(\.tar.gz|\.zip)$/.readme/;
+ if ($readme ne $f && -f $readme) {
+ $dbh->do(
+ q{INSERT INTO deletes VALUES (?,?,?)}, undef,
+ "$userhome/$readme", $time, $pause->{User}{userid},
+ ) or next;
+ $blurb .= "\$CPAN/authors/id/$userhome/$readme\n";
+ }
+ }
+ } elsif ($req->param('SUBMIT_pause99_delete_files_undelete')) {
+ foreach my $f (@{$req->every_param('pause99_delete_files_FILE')}) {
+ my $sql = "DELETE FROM deletes WHERE deleteid = ?";
+ $dbh->do(
+ $sql, undef,
+ "$userhome/$f"
+ ) or warn sprintf "FAILED Query: %s/: %s", $sql, "$userhome/$f", $DBI::errstr;
+ }
+ }
+
+ if ($blurb) {
+ $pause->{blurb} = $blurb;
+ $blurb = $c->render_to_string("email/user/delete_files", format => "email");
+
+ my %umailset;
+ my $name = $u->{asciiname} || $u->{fullname} || "";
+ my $Uname = $pause->{User}{asciiname} || $pause->{User}{fullname} || "";
+ if ($u->{secretemail}) {
+ $umailset{qq{"$name" <$u->{secretemail}>}} = 1;
+ } elsif ($u->{email}) {
+ $umailset{qq{"$name" <$u->{email}>}} = 1;
+ }
+ if ($u->{userid} ne $pause->{User}{userid}) {
+ if ($pause->{User}{secretemail}) {
+ $umailset{qq{"$Uname" <$pause->{User}{secretemail}>}} = 1;
+ }elsif ($pause->{User}{email}) {
+ $umailset{qq{"$Uname" <$pause->{User}{email}>}} = 1;
+ }
+ }
+ $umailset{$PAUSE::Config->{ADMIN}} = 1;
+ my @to = keys %umailset;
+ my $header = {
+ Subject => "Files of $u->{userid} scheduled for deletion"
+ };
+ $mgr->send_mail_multi(\@to, $header, $blurb);
+ }
+
+ my %files = $c->manifind;
+ my (%deletes, %whendele, $sth);
+ if (
+ $sth = $dbh->prepare(qq{SELECT deleteid, changed
+ FROM deletes
+ WHERE deleteid
+ LIKE ?}) #}
+ and
+ $sth->execute("$userhome/%")
+ and
+ $sth->rows
+ ) {
+ my $dhash;
+ while ($dhash = $mgr->fetchrow($sth, "fetchrow_hashref")) {
+ $dhash->{deleteid} =~ s/\Q$userhome\E\///;
+ $deletes{$dhash->{deleteid}}++;
+ $whendele{$dhash->{deleteid}} = $dhash->{changed};
+ }
+ }
+ $sth->finish if ref $sth;
+
+ my $indexed = $c->indexed($dbh, $u->{userid});
+
+ foreach my $f (keys %files) {
+ unless (stat $f) {
+ warn "ALERT: Could not stat f[$f]: $!";
+ next;
+ }
+ my $blurb = $deletes{$f} ?
+ $c->scheduled($whendele{$f}) :
+ HTTP::Date::time2str((stat _)[9]);
+ $files{$f} = {stat => -s _, blurb => $blurb, indexed => $indexed->{$f} };
+ $pause->{deleting_indexed_files} = 1 if $deletes{$f} && $indexed->{$f};
+ }
+ $pause->{files} = \%files;
+}
+
+sub scheduled {
+ my ($c, $when) = @_;
+ my $time = time;
+ my $expires = $when + ($PAUSE::Config->{DELETES_EXPIRE}
+ || 60*60*24*2);
+ my $return = "Scheduled for deletion \(";
+ $return .= $time < $expires ? "due at " : "already expired at ";
+ $return .= HTTP::Date::time2str($expires);
+ $return .= "\)";
+ $return;
+}
+
+sub indexed {
+ my ($c, $dbh, $userid) = @_;
+
+ my %indexed;
+ my $sth;
+ if ($sth = $dbh->prepare(qq{SELECT distinct(packages.dist) AS dist FROM packages JOIN uris ON packages.dist = uris.uriid WHERE packages.status = ? AND uris.userid = ?})
+ and
+ $sth->execute('index', $userid)
+ and
+ $sth->rows
+ ) {
+ require CPAN::DistnameInfo;
+ my $dist;
+ while(($dist) = $sth->fetchrow_array) {
+ my $file = CPAN::DistnameInfo->new($dist)->filename or next;
+ $indexed{$file} = 1;
+ }
+ }
+ $sth->finish if ref $sth;
+ return \%indexed;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User/Perms.pm b/lib/pause_2025/PAUSE/Web/Controller/User/Perms.pm
new file mode 100644
index 000000000..43972fa6d
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User/Perms.pm
@@ -0,0 +1,659 @@
+package PAUSE::Web::Controller::User::Perms;
+
+use Mojo::Base "Mojolicious::Controller";
+
+sub peek {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ unless ($req->param("pause99_peek_perms_query")) {
+ $req->param("pause99_peek_perms_query" => $pause->{User}{userid});
+ }
+ unless ($req->param("pause99_peek_perms_by")) {
+ $req->param("pause99_peek_perms_by" => "a");
+ }
+
+ if (my $qterm = $req->param("pause99_peek_perms_query")) {
+ my $by = $req->param("pause99_peek_perms_by");
+ my @query = (
+ qq{SELECT primeur.package,
+ primeur.userid,
+ "first-come",
+ primeur.userid
+ FROM primeur LEFT JOIN users ON primeur.userid=users.userid
+ },
+ qq{SELECT perms.package,
+ perms.userid,
+ "co-maint",
+ primeur.userid
+ FROM perms LEFT JOIN users ON perms.userid=users.userid
+ LEFT JOIN primeur ON perms.package=primeur.package
+ },
+ );
+
+ my $db = $mgr->connect;
+ my @res;
+ my %seen;
+ for my $query (@query) {
+ my %fields = (
+ "first-come" => {
+ package => "primeur.package",
+ userid => "primeur.userid",
+ },
+ "co-maint" => {
+ package => "perms.package",
+ userid => "perms.userid",
+ }
+ );
+ my($qtype) = $query =~ /\"(.+)\"/;
+ my($fmap) = $fields{$qtype};
+ my $where;
+ if ($by =~ /^m/) {
+ if ($by eq "me") {
+ $where = qq{WHERE $fmap->{package}=?};
+ } else {
+ $where = qq{WHERE $fmap->{package} LIKE ? LIMIT 1000};
+ # I saw 5.7.3 die with Out Of Memory on the query "%" when no
+ # Limit was applied
+ }
+ } elsif ($by eq "a") {
+ $where = qq{WHERE $fmap->{userid}=?};
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "Illegal parameter for pause99_peek_perms_by");
+ }
+ $query .= $where;
+ my $sth = $db->prepare($query);
+ $sth->execute($qterm);
+ if ($sth->rows > 0) {
+ # warn sprintf "query[%s]qterm[%s]rows[%d]", $query, $qterm, $sth->rows;
+ while (my @row = $mgr->fetchrow($sth, "fetchrow_array")) {
+ if ($seen{join "|", @row[0,1]}++){
+ # warn "Ignoring row[$row[0]][$row[1]]";
+ next;
+ }
+ push @res, \@row;
+ }
+ }
+ $sth->finish;
+ }
+ if (@res) {
+ my $dbh = $mgr->connect;
+ for my $row (@res) {
+ # add the owner on column 3
+ # will already be set except for co-maint modules where the
+ # owner is in the modlist but not first-come
+ $row->[3] ||= PAUSE::owner_of_module($row->[0], $dbh);
+ }
+ my @column_names = qw(module userid type owner);
+ my $output_format = $req->param("OF");
+ if ($output_format){
+ my @hres;
+ for my $row (@res) {
+ push @hres, { map {$column_names[$_] => $row->[$_] } 0..$#$row };
+ }
+ if ($output_format eq "YAML") {
+ return $c->render_yaml(\@hres);
+ } else {
+ die "not supported OF=$output_format"
+ }
+ }
+ $pause->{column_names} = \@column_names;
+
+ @res = sort {
+ $a->[0] cmp $b->[0]
+ ||
+ $a->[1] cmp $b->[1]
+ ||
+ $a->[2] cmp $b->[2]
+ ||
+ $a->[3] cmp $b->[3]
+ } @res;
+
+ $pause->{rows} = \@res;
+ }
+ }
+}
+
+sub share {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $subaction = $req->param("SUBACTION");
+ unless ($subaction) {
+ ####################### 2.1 2.2 3.1 3.2 4.1
+ SUBACTION: for my $sa (qw(movepr remopr makeco remocos remome)) {
+ if ($req->param("pause99_share_perms_$sa")
+ or
+ $req->param("SUBMIT_pause99_share_perms_$sa")
+ or
+ $req->param("weaksubmit_pause99_share_perms_$sa")
+ ) {
+ $subaction = $sa;
+ last SUBACTION;
+ }
+ }
+ }
+ $pause->{subaction} = $subaction;
+ my $u = $c->active_user_record;
+
+ # warn sprintf "subaction[%s] u->userid[%s]", $subaction||"", $u->{userid}||"";
+
+ unless ($subaction) {
+ # NOTE: the 6 submit buttons below are "weak" submit buttons. I
+ # want that people first reach the next page with more text and
+ # more options.
+
+ my $dbh = $mgr->connect;
+
+ {
+ my $all_mods = $c->all_pmods_not_mmods($u);
+ my @all_mods = sort keys %$all_mods;
+ $pause->{remove_primary} = \@all_mods;
+ }
+
+ {
+ # it should be sufficiently helpful to prepare only makeco_m on
+ # these two submit buttons. For 3.2 people may be a little confused
+ # but it is so rarely needed that we do not worry.
+ my $all_mods = $c->all_pmods($u);
+ my @all_mods = sort keys %$all_mods;
+ $pause->{make_comaintainer} = \@all_mods;
+ }
+
+ {
+ my $all_mods = $c->all_only_cmods($u);
+ my @all_mods = sort keys %$all_mods;
+ my %labels;
+ my @all_mods_with_label;
+ for my $m (@all_mods) {
+ # get the owner for modlist modules that don't have first-come
+ my $owner = $all_mods->{$m} || PAUSE::owner_of_module($m, $dbh) || '?';
+ push @all_mods_with_label, ["$m => $owner", $m];
+ }
+
+ $pause->{remove_comaintainer} = \@all_mods_with_label;
+ }
+
+ return;
+ }
+
+ my $method = "_share_$subaction";
+ $c->$method;
+}
+
+sub move_primary {
+ my $c = shift;
+ $c->_share_movepr(@_);
+ $c->_prepare_dist_package_mapping;
+}
+
+sub _share_movepr {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_mods = $c->all_pmods_not_mmods($u);
+
+ if (
+ $req->param("SUBMIT_pause99_share_perms_movepr")
+ ) {
+ eval {
+ my(@selmods, $other_user);
+ if (@selmods = @{$req->every_param("pause99_share_perms_pr_m")}
+ and
+ $other_user = $req->param("pause99_share_perms_movepr_a")
+ ) {
+ $other_user = uc $other_user;
+ my $sth1 = $db->prepare("SELECT userid
+ FROM users
+ WHERE userid=?");
+ $sth1->execute($other_user);
+ die PAUSE::Web::Exception
+ ->new(ERROR => "$other_user is not a valid userid.")
+ unless $sth1->rows;
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("UPDATE primeur SET userid=? WHERE package=?");
+ my @results;
+ for my $selmod (@selmods) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $selmod")
+ unless exists $all_mods->{$selmod};
+ my $ret = $sth->execute($other_user,$selmod);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: selmod[$selmod]other_user[$other_user]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ };
+ } else {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ error => $err,
+ };
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_mods = $c->all_pmods_not_mmods($u); # again
+ my @all_mods = sort keys %$all_mods;
+ $pause->{mods} = \@all_mods;
+
+ if (@all_mods == 1) {
+ $req->param("pause99_share_perms_pr_m" => $all_mods[0]);
+ }
+}
+
+sub remove_primary {
+ my $c = shift;
+ $c->_share_remopr(@_);
+ $c->_prepare_dist_package_mapping;
+}
+
+sub _share_remopr {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_mods = $c->all_pmods_not_mmods($u);
+
+ if (0) {
+ # here I discovered that Apache::Request has case-insensitive keys
+ my %p = map { $_, [ $req->every_param($_)] } @{$req->param->names};
+ require Data::Dumper;
+ my $message = "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->new([\%p],[qw()])->Indent(1)->Useqq(1)->Dump;
+ $c->app->pause->log({level => 'debug', message => $message});
+ }
+
+ if (
+ $req->param("SUBMIT_pause99_share_perms_remopr")
+ ) {
+ eval {
+ my(@selmods);
+ if (@selmods = @{$req->every_param("pause99_share_perms_pr_m")}
+ ) {
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("UPDATE primeur SET userid = ? WHERE userid=? AND package=?");
+
+ my @results;
+ for my $selmod (@selmods) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $selmod")
+ unless exists $all_mods->{$selmod};
+ my $ret = $sth->execute('ADOPTME',$u->{userid},$selmod);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ };
+ } else {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ error => $err,
+ };
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ }
+
+ $all_mods = $c->all_pmods_not_mmods($u); # again
+ my @all_mods = sort keys %$all_mods;
+ $pause->{mods} = \@all_mods;
+
+ if (@all_mods == 1) {
+ $req->param("pause99_share_perms_pr_m" => $all_mods[0]);
+ }
+}
+
+sub make_comaint {
+ my $c = shift;
+ $c->_share_makeco(@_);
+ $c->_prepare_dist_package_mapping;
+}
+
+sub _share_makeco {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+ # warn "u->userid[%s]", $u->{userid};
+
+ my $db = $mgr->connect;
+
+ my $all_pmods = $c->all_pmods($u);
+ # warn sprintf "all_pmods[%s]", join("|", keys %$all_pmods);
+ my $all_mods = {%$all_pmods};
+
+ if (
+ $req->param("SUBMIT_pause99_share_perms_makeco")
+ ) {
+ eval {
+ my(@selmods,$other_user);
+ if (@selmods = @{$req->every_param("pause99_share_perms_makeco_m")}
+ and
+ $other_user = $req->param("pause99_share_perms_makeco_a")
+ ) {
+ $other_user = uc $other_user;
+ my $sth1 = $db->prepare("SELECT userid
+ FROM users
+ WHERE userid=?");
+ $sth1->execute($other_user);
+ die PAUSE::Web::Exception
+ ->new(ERROR => sprintf(
+ "$other_user is not a valid userid.",
+ )
+ )
+ unless $sth1->rows;
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("INSERT INTO perms (package,lc_package,userid)
+ VALUES (?,?,?)");
+
+ my @results;
+ for my $selmod (@selmods) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be maintainer of $selmod")
+ unless exists $all_mods->{$selmod};
+ my $ret = $sth->execute($selmod,lc $selmod,$other_user);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: selmod[$selmod]other_user[$other_user]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ };
+ } elsif ($err =~ /Duplicate entry/) {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ duplicated => 1,
+ };
+ } else {
+ push @results, {
+ user => $other_user,
+ mod => $selmod,
+ error => $err,
+ };
+ }
+ $pause->{results} = \@results;
+ }
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ my @all_mods = sort keys %$all_mods;
+ $pause->{mods} = \@all_mods;
+
+ if (@all_mods == 1) {
+ $req->param("pause99_share_perms_makeco_m" => $all_mods[0]);
+ }
+}
+
+sub remove_comaint {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ $c->_share_remocos(@_);
+ $c->_prepare_dist_package_mapping([map {/^(\S+)/; $1} @{$pause->{mods}}]);
+}
+
+sub _share_remocos {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+
+ my $db = $mgr->connect;
+
+ my $all_mods = $c->all_pmods($u);
+ my $all_comaints = $c->all_comaints($all_mods,$u);
+
+ if (
+ $req->param("SUBMIT_pause99_share_perms_remocos")
+ ) {
+ eval {
+ my @sel = @{$req->every_param("pause99_share_perms_remocos_tuples")};
+ my $sth1 = $db->prepare("DELETE FROM perms WHERE package=? AND userid=?");
+ if (@sel) {
+ my @results;
+ for my $sel (@sel) {
+ my($selmod,$otheruser) = $sel =~ /^(\S+)\s--\s(\S+)$/;
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be owner of $selmod.")
+ unless exists $all_mods->{$selmod};
+ unless (exists $all_comaints->{$sel}) {
+ push @results, {
+ mod => $sel,
+ not_exists => 1,
+ };
+ next;
+ }
+ my $ret = $sth1->execute($selmod,$otheruser);
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $otheruser,
+ mod => $selmod,
+ };
+ } else {
+ push @results, {
+ user => $otheruser,
+ mod => $selmod,
+ error => $err,
+ };
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ $all_comaints = $c->all_comaints($all_mods,$u); # again
+ my @all = sort keys %$all_comaints;
+ $pause->{mods} = \@all;
+}
+
+sub giveup_comaint {
+ my $c = shift;
+ $c->_share_remome(@_);
+ $c->_prepare_dist_package_mapping;
+}
+
+sub _share_remome {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+ my $db = $mgr->connect;
+
+ my $all_mods = $c->all_only_cmods($u);
+
+ if (
+ $req->param("SUBMIT_pause99_share_perms_remome")
+ ) {
+ eval {
+ my(@selmods);
+ if (@selmods = @{$req->every_param("pause99_share_perms_remome_m")}
+ ) {
+ local($db->{RaiseError}) = 0;
+ my $sth = $db->prepare("DELETE FROM perms WHERE package=? AND userid=?");
+
+ my @results;
+ for my $selmod (@selmods) {
+ die PAUSE::Web::Exception
+ ->new(ERROR => "You do not seem to be co-maintainer of $selmod")
+ unless exists $all_mods->{$selmod};
+ my $ret = $sth->execute($selmod,$u->{userid});
+ my $err = "";
+ $err = $db->errstr unless defined $ret;
+ $ret ||= "";
+ warn "DEBUG: selmod[$selmod]ret[$ret]err[$err]";
+ if ($ret) {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ };
+ delete $all_mods->{$selmod};
+ } else {
+ push @results, {
+ user => $u->{userid},
+ mod => $selmod,
+ error => $err,
+ };
+ }
+ }
+ $pause->{results} = \@results;
+ }
+ };
+ if ($@) {
+ $pause->{error} = $@->{ERROR};
+ }
+ }
+
+ my @all_mods = sort keys %$all_mods;
+ $pause->{mods} = \@all_mods;
+
+ if (@all_mods == 1) {
+ $req->param("pause99_share_perms_remome_m" => $all_mods[0]);
+ }
+}
+
+sub all_pmods_not_mmods {
+ my ($c, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ my(%all_mods);
+ my $sth2 = $db->prepare(qq{SELECT package
+ FROM primeur
+ WHERE userid=?});
+ $sth2->execute($u->{userid});
+ while (my($id) = $mgr->fetchrow($sth2, "fetchrow_array")) {
+ $all_mods{$id} = undef;
+ }
+ $sth2->finish;
+ \%all_mods;
+}
+
+sub all_cmods {
+ my ($c, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ my(%all_mods);
+ my $sth2 = $db->prepare(qq{SELECT perms.package, primeur.userid
+ FROM perms LEFT JOIN primeur
+ ON perms.package = primeur.package
+ WHERE perms.userid=?});
+ $sth2->execute($u->{userid});
+ while (my($id, $owner) = $mgr->fetchrow($sth2, "fetchrow_array")) {
+ $all_mods{$id} = $owner;
+ }
+ $sth2->finish;
+ \%all_mods;
+}
+
+sub all_pmods {
+ my ($c, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ my(%all_mods);
+ my $sth2 = $db->prepare(qq{SELECT package
+ FROM primeur
+ WHERE userid=?});
+ $sth2->execute($u->{userid});
+ while (my($id) = $mgr->fetchrow($sth2, "fetchrow_array")) {
+ $all_mods{$id} = undef;
+ }
+ $sth2->finish;
+ \%all_mods;
+}
+
+sub all_only_cmods {
+ my($c,$u) = @_;
+ my $all_pmods = $c->all_pmods($u);
+ my $all_mods = $c->all_cmods($u);
+
+ for my $k (keys %$all_pmods) {
+ delete $all_mods->{$k};
+ }
+ $all_mods;
+}
+
+sub all_comaints {
+ my ($c, $all_mods, $u) = @_;
+ my $mgr = $c->app->pause;
+ my $result = {};
+ my $db = $mgr->connect;
+ my $or = join " OR\n", map { "package='$_'" } keys %$all_mods;
+ my $sth2 = $db->prepare(qq{SELECT package, userid
+ FROM perms
+ WHERE userid <> '$u->{userid}' AND ( $or )});
+ $sth2->execute;
+ while (my($p,$i) = $mgr->fetchrow($sth2,"fetchrow_array")) {
+ $result->{"$p -- $i"} = undef;
+ warn "p[$p]i[$i]";
+ }
+ return $result;
+}
+
+sub _prepare_dist_package_mapping {
+ my ($c, $packages) = @_;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $db = $mgr->connect;
+ $packages //= [@{$pause->{mods} // []}];
+
+ my %map;
+ while(my @part = splice @$packages, 0, 500) {
+ my $placeholders = substr "?," x @part, 0, -1;
+ my $sth = $db->prepare("SELECT dist, package FROM packages WHERE package IN ($placeholders)");
+ $sth->execute(@part);
+ while(my ($dist, $package) = $sth->fetchrow_array) {
+ $map{$package} = $dist;
+ }
+ }
+ $pause->{dist_for_package} = \%map;
+}
+
+1;
diff --git a/lib/pause_2025/PAUSE/Web/Controller/User/Uri.pm b/lib/pause_2025/PAUSE/Web/Controller/User/Uri.pm
new file mode 100644
index 000000000..7f5169e29
--- /dev/null
+++ b/lib/pause_2025/PAUSE/Web/Controller/User/Uri.pm
@@ -0,0 +1,280 @@
+package PAUSE::Web::Controller::User::Uri;
+
+use Mojo::Base "Mojolicious::Controller";
+use Mojo::ByteStream;
+use Mojo::URL;
+use File::pushd;
+
+sub add {
+ my $c = shift;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+
+ $PAUSE::Config->{INCOMING_LOC} =~ s|/$||;
+
+ my $u = $c->active_user_record;
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ "Unidentified error happened, please write to the PAUSE admins
+ at $PAUSE::Config->{ADMIN} and help them identifying what's going on. Thanks!")
+ unless $u->{userid};
+
+ my($tryupload) = 1; # everyone supports multipart now
+ my($uri);
+ my $userhome = PAUSE::user2dir($u->{userid});
+
+ if ($req->param("SUBMIT_pause99_add_uri_HTTPUPLOAD")
+ || $req->param("SUBMIT_pause99_add_uri_httpupload")) {
+ my $upl = $req->upload('pause99_add_uri_httpupload');
+ unless ($upl->size) {
+ warn "Warning: maybe they hit RETURN, no upload size, not doing HTTPUPLOAD";
+ $req->param("SUBMIT_pause99_add_uri_HTTPUPLOAD","");
+ $req->param("SUBMIT_pause99_add_uri_httpupload","");
+ }
+ }
+ if (! $req->param("SUBMIT_pause99_add_uri_HTTPUPLOAD")
+ &&! $req->param("SUBMIT_pause99_add_uri_httpupload")
+ &&! $req->param("SUBMIT_pause99_add_uri_uri")
+ &&! $req->param("SUBMIT_pause99_add_uri_upload")
+ ) {
+ # no submit button
+ if ($req->param("pause99_add_uri_uri")) {
+ $req->param("SUBMIT_pause99_add_uri_uri", "2ndguess");
+ } elsif ($req->param("pause99_add_uri_upload")) {
+ $req->param("SUBMIT_pause99_add_uri_upload", "2ndguess");
+ }
+ }
+
+ my $didit = 0;
+ my $now = time;
+ if (
+ $req->param("SUBMIT_pause99_add_uri_httpupload") || # from 990806
+ $req->param("SUBMIT_pause99_add_uri_HTTPUPLOAD")
+ ) {
+ { # $pause->{UseModuleSet} eq "ApReq"
+ my $upl;
+ if (
+ $upl = $req->upload("pause99_add_uri_httpupload") or # from 990806
+ $upl = $req->upload("HTTPUPLOAD")
+ ) {
+ if ($upl->size) {
+ my $filename = $upl->filename;
+ $filename =~ s(.*/)()gs; # no slash
+ $filename =~ s(.*\\)()gs; # no backslash
+ $filename =~ s(.*:)()gs; # no colon
+ $filename =~ s/[^A-Za-z0-9_\-\.\@\+]//g; # only ASCII-\w and - . @ + allowed
+ my $to = "$PAUSE::Config->{INCOMING_LOC}/$filename";
+ # my $fhi = $upl->fh;
+ if (-f $to && -s _ == 0) { # zero sized files are a common problem
+ unlink $to;
+ }
+ if ($upl->move_to($to)){
+ $uri = $filename;
+ # Got an empty $to in the HTML page, so for debugging..
+ $pause->{successfully_copied_to} = $to;
+ warn "h1[File successfully copied to '$to']filename[$filename]";
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ "Couldn't copy file '$filename' to '$to': $!");
+ }
+ unless ($upl->filename eq $filename) {
+
+ require Dumpvalue;
+ my $dv = Dumpvalue->new;
+ $req->param("pause99_add_uri_httpupload",$filename);
+ $pause->{upload_is_renamed} = {
+ from => $dv->stringify($upl->filename),
+ to => $dv->stringify($filename),
+ };
+ }
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ "uploaded file was zero sized");
+ }
+ } else {
+ die PAUSE::Web::Exception
+ ->new(ERROR =>
+ "Could not create an upload object. DEBUG: upl[$upl]");
+ }
+ }
+ } elsif ( $req->param("SUBMIT_pause99_add_uri_uri") ) {
+ $uri = $req->param("pause99_add_uri_uri");
+ $req->param("pause99_add_uri_httpupload",""); # I saw spurious
+ # nonsense in the
+ # field that broke
+ # XHTML
+ } elsif ( $req->param("SUBMIT_pause99_add_uri_upload") ) {
+ $uri = $req->param("pause99_add_uri_upload");
+ $req->param("pause99_add_uri_httpupload",""); # I saw spurious
+ # nonsense in the
+ # field that broke
+ # XHTML
+ }
+ my $server = $PAUSE::Config->{SERVER_NAME} || $req->url->to_abs->host;
+ my $dbh = $mgr->connect;
+
+ $pause->{uploaded_uri} = $uri;
+ if ($uri) {
+ $c->add_uri_continue_with_uri($uri,\$didit);
+ }
+
+ if ($tryupload) {
+ $pause->{tryupload} = $tryupload;
+ my $subdirs = $c->_find_subdirs($u);
+ $pause->{subdirs} = $subdirs if $subdirs;
+ }
+
+ # HTTP UPLOAD
+
+ if ($tryupload) {
+ $c->need_form_data(1);
+ $c->res->headers->accept("*");
+ }
+
+ # via FTP GET
+
+ warn "DEBUG: UPLOAD[$PAUSE::Config->{UPLOAD}]";
+
+ # END OF UPLOAD OPTIONS
+}
+
+sub _find_subdirs {
+ my ($c, $u) = @_;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+
+ my $userhome = PAUSE::user2dir($u->{userid});
+ $pause->{userhome} = $userhome;
+
+ my $pushd = eval { pushd("$PAUSE::Config->{MLROOT}/$userhome") } or return;
+ warn "DEBUG: MLROOT[$PAUSE::Config->{MLROOT}] userhome[$userhome] E:M:V[$ExtUtils::Manifest::VERSION]";
+
+ my %files = $c->manifind;
+ my %seen;
+ my @dirs = sort grep !$seen{$_}++, grep s|(.+)/[^/]+|$1|, keys %files;
+ return unless @dirs;
+ unshift @dirs, ".";
+ return \@dirs;
+}
+
+sub add_uri_continue_with_uri {
+ my ($c, $uri, $didit) = @_;
+ my $pause = $c->stash(".pause");
+ my $mgr = $c->app->pause;
+ my $req = $c->req;
+ my $u = $c->active_user_record;
+ my $userhome = PAUSE::user2dir($u->{userid});
+ my $dbh = $mgr->connect;
+ my $now = time;
+ my $server = $PAUSE::Config->{SERVER_NAME} || $req->url->to_abs->host;
+
+ eval { Mojo::URL->new("$PAUSE::Config->{INCOMING}/$uri") };
+ if ($@) {
+ $pause->{invalid_uri} = 1;
+ # FIXME
+ die PAUSE::Web::Exception
+ ->new(ERROR => [Mojo::ByteStream->new(qq{
+Sorry, $uri could not be recognized as an uri (}),
+ $@,
+ Mojo::ByteStream->new(qq{\)Please
+try again or report errors to the administrator
PAUSE is closed for maintainance for about <%= $closed->{will_last} %>. Estimated time of opening is in <%= $closed->{delta} %>.
Sorry for the inconvenience and Thanks for your patience.
diff --git a/lib/pause_2025/templates/_debug.html.ep b/lib/pause_2025/templates/_debug.html.ep new file mode 100644 index 000000000..6d665966c --- /dev/null +++ b/lib/pause_2025/templates/_debug.html.ep @@ -0,0 +1,285 @@ +%# stolen from Mojolicious' default debug template +% unless ($ENV{TEST_PAUSE_WEB}) { + + +<%= $value %>
<%= $value %>
+ <%= $exception->message %>+
+ <%= $frame->[1] . ':' . $frame->[2] %>+ |
+
<%= ' ' x $depth %><%= $pattern %>+
<%= uc(join ',', @{$route->via || []}) || '*' %>
+ <%= $route->has_custom_name ? qq{"$name"} : $name %>
+ | Pattern | +Methods | +Name | +
|---|
+ <%= app->log->format->(@$msg) %>+ |
+
+ + From: andreas.koenig@anima.de (Andreas J. Koenig) + To: kstar@chapin.edu + Subject: Re: [elagache@ipn.caida.org: No email found for CAIDA? (Re: Missing CAIDA password?)] + Date: 02 Nov 2000 17:59:28 +0100 + + A mailing list occupies the same namespace as users because we do + not want that users and mailing lists get confused. But a mailing + list does not have a password and does not have a directory of its + own. Only people can upload and occupy a directory and have a + password. (It's clear that the user namespace is not related to the + modules namespace, right?) + + The Module List may list a mailinglist as "the contact", so the + field userid in the table mods identifies either a mailing list or + a user. This has been useful in the past when several clueful + people represent several related modules and use a common mailing + list as the contact. + + The table list2user maps mailing lists to their owners so that the + owners can edit the data associated with the mailing list like + address and comment. The table list2user does not have a web + interface because we are not really established as the primary + source for mailing list information and so it has not been used + much. But I'm open to offer one if you believe it's useful. + [...] ++ + + +<%= select_field "pause99_edit_ml_3" => $pause->{mls}, + size => (@{$pause->{mls}} > 18 ? 15 : scalar @{$pause->{mls}}), +=%> +
The name of the mailing list
+The name appears in the CPAN authors list, so it is good if the name contains the term mailing list or something equivalent
+<%= text_field pause99_edit_ml_maillistname => $pause->{selected}{maillistname}, + size => 50, + maxsize => 64, +=%> +The address of the mailing list
+This is the address where people post to (where all members of the group can be contacted)
+<%= text_field pause99_edit_ml_address => $pause->{selected}{address}, + size => 50, +=%> +How to subscribe
+This is a text that describes how to join the mailing list. E.g. the mailing list subscribe address or a URL with more details.
+<%= text_area pause99_edit_ml_subscribe => $pause->{selected}{subscribe}, + rows => 5, + cols => 60, +=%> +The record has been updated in the database
+% } elsif ($pause->{updated_sel}) { +It seems to me the record was NOT updated. Maybe +nothing changed? Please take a closer look and inform an admin if +things didn't proceed as expected.
+% } +% } diff --git a/lib/pause_2025/templates/admin/email_for_admin.html.ep b/lib/pause_2025/templates/admin/email_for_admin.html.ep new file mode 100644 index 000000000..d1dad4034 --- /dev/null +++ b/lib/pause_2025/templates/admin/email_for_admin.html.ep @@ -0,0 +1,35 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + +Query a combination of usertable table and user for public or private emails according to the preferences
| id | +id@cpan.org gets forwarded to | +
|---|---|
| <%= $_->{id} %> | +<%= $_->{mail} %> | +
View all pending applications for new user IDs and for modules registrations
+ +| Type | +Userid | +Time | +Raw Session | +Actions | +
|---|---|---|---|---|
| <%= $ALL{$k}{type} %> | +<%= $ALL{$k}{session}{APPLY}{userid} %> | +<%= POSIX::strftime("%FT%TZ", gmtime $ALL{$k}{mtime}) %> | +<%= do {
+ my $json = $json_encoder->encode($ALL{$k}{session});
+ $json =~ s/\\n/\n/g;
+ $json;
+ } %> |
+
+ $ALL{$k}{session}{_session_id},
+ (exists $ALL{$k}{session}{APPLY}{fullname} ? "SUBMIT_pause99_add_user_sub" : "SUBMIT_pause99_add_mod_preview") => 1
+ ) %>">Go To Registration
+ + "delete", + USERID => $ALL{$k}{session}{_session_id}, + ) %>">Delete Registration + |
+
<%= $pause->{s_package} %> converted the fullname [<%= $pause->{fullname} %>] to [<%= $pause->{s_code} %>]
+| userid | +fullname | +(public) email | +other | +||||||
| <%= $row->{userid} %> | +% } elsif ($row->{userid}) { +<%= $row->{userid} %> | +% } else { ++% } + +% if ($row->{same_fullname}) { + | <%= $row->{fullname} %> | +% } elsif ($row->{surname}) { +<%= $row->{before_surname} %><%= $row->{surname} %><%= $row->{after_surname} %> | +% } elsif ($row->{fullname}) { +<%= $row->{fullname} %> | +% } else { ++% } + +% if ($row->{same_email}) { + | <%= $row->{email_parts}[0] %> @<%= $row->{email_parts}[1] %> |
+% } else {
+<%= $row->{email_parts}[0] %> @<%= $row->{email_parts}[1] %> |
+% }
+
+
+% if ($row->{same_secretemail}) {
+secret email: <%= $row->{secretemail} %> +% } elsif ($row->{secretemail}) { +secret email: <%= $row->{secret_email} %> +% } + +% if ($row->{same_homepage}) { +homepage: <%= $row->{homepage} %> +% } elsif ($row->{homepage}) { +homepage: <%= $row->{homepage} %> +% } + +% if ($row->{introduced}) { +% my $time = $row->{introduced}; +% $time =~ s/\s/\ /g; +introduced on: <%== $time %> +% } + +% if ($row->{changed}) { +% my $time = $row->{changed}; +% $time =~ s/\s/\ /g; +changed on: <%== $time %> by <%= $row->{changedby} %> +% } else { +changed by <%= $row->{changedby} %> +% } + |
+
New user creation succeeded.
+ +% if ($pause->{subscribe}) { # mailing list +Mailing list entered by <%= $pause->{User}{fullname} %>:
+Userid: <%= $pause->{userid} %>
+Name: <%= $pause->{maillistname} %>
+Description: <%= $pause->{subscribe} %>
+% } else { # new user + +Sending separate mails to: <%= $pause->{send_to} %> +
+From: <%= $PAUSE::Config->{UPLOAD} %>
+Subject: <%= $pause->{subject} %>
+
+<%= $pause->{blurb} %>
+
+% }
+% } else {
+Query [<%= $pause->{query} %>] failed. Reason:
<%= $pause->{query_error} %>
+ +% } + +Content of user record in table users:| <%= $_ %> | <%= $pause->{usertable}{$_} || b(" ") %> |
Please retry.
+% } +% } + +If this is a bad request: "delete", + USERID => $pause->{userid}, +) %>">Delete the ID request
diff --git a/lib/pause_2025/templates/closed.html.ep b/lib/pause_2025/templates/closed.html.ep new file mode 100644 index 000000000..599a23d80 --- /dev/null +++ b/lib/pause_2025/templates/closed.html.ep @@ -0,0 +1,7 @@ +Andreas Koenig
+ + diff --git a/lib/pause_2025/templates/disabled.html.ep b/lib/pause_2025/templates/disabled.html.ep new file mode 100644 index 000000000..51465ae01 --- /dev/null +++ b/lib/pause_2025/templates/disabled.html.ep @@ -0,0 +1,5 @@ +% layouts 'layout', title => "Closed for Maintenance"; +% my $pause = stash(".pause") || {}; + +
+ Scheduled downtime
+ On <%= $downtime->{http_time} %> (that is in <%= $downtime->{delta} %>) PAUSE will be closed for maintainance work. The estimated downtime is <%= $downtime->{will_last} %>.
+
Hi <%= $user %>, you see the site now but it is closed for maintainance. +Please be careful not to disturb the database operation. Expect failures everywhere. Do not edit anything, it may get lost. Other users get the following text:
+%= include "_closed"; +Please try again, probably by using the Back button of your browser and repeating the last action you took.
++Mailinglist support is intended to be available on a +delegates/representatives basis, that means, one or more users +are "elected" (no formal election though) to be allowed to act +on behalf of a mailing list. There is no password for a mailing +list, there are no user credentials for a mailing list. There +are no uploads for mailing lists, thus no deletes or repairs of +uploads. +
++There are only the infos about the mailing list +editable via the method edit_ml and ther are a number of +modules associated with a mailing list and these are accessible +in the edit_mod method. +
++The menu item Select +Mailinglist/Action lets you access the available methods and +the mailing lists you are associated with. Only people elected +as a representative of a mailing list should be able to ever see +the menu entry.
This feature is available since Oct 25th, +1999 and hardly tested, so please take care and let us know how +it goes. +
+ +These are the contents of the table list2user. +There's currently no way to edit the table except +direct SQL. The table says who is representative of a +mailing list.
+ +| Mailing list | +User-ID | +
|---|---|
| <%= $rec->{maillistid} %> | +<%= $rec->{userid} %> | +
Query the grouptable table for who is an admin bit holder
Registered admins: <%= join ", ", @{$pause->{admins} || []} %>
+"YAML") %>" style="text-decoration: none;"> + +
diff --git a/lib/pause_2025/templates/public/mailpw.html.ep b/lib/pause_2025/templates/public/mailpw.html.ep new file mode 100644 index 000000000..89576372e --- /dev/null +++ b/lib/pause_2025/templates/public/mailpw.html.ep @@ -0,0 +1,21 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; +%= csrf_field + +"> + +% if ($pause->{mailpw_userid}) { +% if ($pause->{valid_email}) { +A token to change the password for <%= $pause->{mailpw_userid} %> is on its way to its owner. Should the mail not arrive, please tell us.
+% } else { +We have not found the email of <%= $pause->{mailpw_user_id} %>. Please try with a different name or mail to the administrator directly.
+% } +% } + +This form lets you request a token that enables you to set a new +password. It only operates correctly if the database knows you and +your email adress. Please fill in your userid on the CPAN. The token +will be mailed to that userid.
+ +<%= text_field "pause99_mailpw_1", size => 32 %> + diff --git a/lib/pause_2025/templates/public/pumpkin.html.ep b/lib/pause_2025/templates/public/pumpkin.html.ep new file mode 100644 index 000000000..bdb2bb324 --- /dev/null +++ b/lib/pause_2025/templates/public/pumpkin.html.ep @@ -0,0 +1,8 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + +Query the grouptable table for who is a pumpkin bit holder
Registered pumpkins: <%= join ", ", @{$pause->{pumpkins} || []} %>
+"YAML") %>" style="text-decoration: none;"> + +
diff --git a/lib/pause_2025/templates/public/request_id/_form.html.ep b/lib/pause_2025/templates/public/request_id/_form.html.ep new file mode 100644 index 000000000..b090853b1 --- /dev/null +++ b/lib/pause_2025/templates/public/request_id/_form.html.ep @@ -0,0 +1,63 @@ +% my $pause = stash(".pause") || {}; +% my $alt = 0; + +A PAUSE account is only required to distribute and manage Perl module +distributions on CPAN. You do not need a PAUSE account to submit +bug reports to RT or participate +in many Perl community sites.
+ +Your full name (civil name)
+Unicode Characters OK.
++<%= text_field "pause99_request_id_fullname", size => 32 %> +
+required, otherwise we cannot send you the password
++<%= text_field "pause99_request_id_email", size => 32 %> +
+Web site
+optional
++<%= text_field "pause99_request_id_homepage", size => 32 %> +
+Desired ID
+3-9 characters matching [A-Z], please
+<%= text_field "pause99_request_id_userid", size => 32 %> +A short description of why you would like a +PAUSE ID:
required; include what you are planning to contribute; do not use HTML
+ +<%= text_area "pause99_request_id_rationale", rows=>8, cols=>60 =%> + +
Please retry.
+% } + +% if ($pause->{showform}) { + +%= include "public/request_id/_form"; + +% } +% if ($pause->{reg_ok}) { +% if ($pause->{recaptcha_enabled}) { +% if ($pause->{added_user}) { +New user creation succeeded.
+ +LOOK FOR AN EMAIL WITH YOUR TEMPORARY PASSWORD.
+ +You'll also receive a welcome email like the one below.
+ +
+From: <%= $PAUSE::Config->{UPLOAD} %>
+Subject: <%= $pause->{subject_for_user_addition} %>
+
+<%== $pause->{blurb_for_user_addition} %>
+
+New user creation failed.
+% } +% } +% elsif ($pause->{blurbcopy}) { +Sending mail to: <%= $pause->{send_to} %> +
+From: <%= $PAUSE::Config->{UPLOAD} %>
+Subject: <%= $pause->{subject} %>
+
+<%== $pause->{blurbcopy} %>
+
+The usermenu to the left shows all menus available to +you, the table below shows descriptions for all menues available +to anybody on PAUSE.
+ +% my $alter = 1; +
+
|
New password stored and enabled. Be prepared that +you will be asked for a new authentication on the next request. If +this doesn't work out, it may be that you have to restart the +browser.
+% } else { +% if ( $pause->{UserSecrets}{forcechange} ) { + +Your password in the database is tainted which +means you have to renew it. If you believe this is wrong, please +complain, it's always possible that you are seeing a bug.
+ +% } + +Please fill in your new password in both textboxes. +Only if both fields contain the same password, we will be able to +proceed.
+ +<%= password_field "pause99_change_passwd_pw1", + maxlength => 72, + size => 16, +=%> + +<%= password_field "pause99_change_passwd_pw2", + maxlength => 72, + size => 16, +=%> + +%= csrf_field + + +% } + diff --git a/lib/pause_2025/templates/user/cred/edit.html.ep b/lib/pause_2025/templates/user/cred/edit.html.ep new file mode 100644 index 000000000..6d020e443 --- /dev/null +++ b/lib/pause_2025/templates/user/cred/edit.html.ep @@ -0,0 +1,151 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; +% my $cpan_alias = lc($pause->{HiddenUser}{userid}) . '@cpan.org'; + + + +Full Name++PAUSE supports names containing UTF-8 characters. +See also the field ASCII transliteration below. + +<%= text_field "pause99_edit_cred_fullname", + size => 50, + maxlength => 127, # caution! +%> + |
ASCII transliteration of Full Name++If your Full Name contains +characters above 0x7f, please supply an +ASCII transliteration that can be used in +mail written in ASCII. Leave empty if you +trust the Text::Unidecode module. + +<%= text_field "pause99_edit_cred_asciiname", + size => 50, + maxlength => 255, +%> + |
Publicly visible email address (published in many listings)+<%= text_field "pause99_edit_cred_email", + size => 50, + maxlength => 255, +%> + |
Secret email address only used by the PAUSE, never published.++If you leave this field empty, +PAUSE will use the public email address +for communicating with you. + +<%= text_field "pause99_edit_cred_secretemail", + size => 50, + maxlength => 255, +%> + |
Homepage or any contact URL except mailto:+<%= text_field "pause99_edit_cred_homepage", + size => 50, + maxlength => 255, +%> + |
The email address <%= $cpan_alias %> should be configured to forward mail to ...+
+cpan.org has a mail
+address for you and it's your choice if you want it to point to your
+public email address or to your secret one. Please allow a few hours
+for any change you make to this setting for propagation. BTW, let us
+reassure you that cpan.org gets the data through a secure
+channel. +<%= radio_button "pause99_edit_cred_cpan_mail_alias" => "secr" %> +my secret email address +<%= radio_button "pause99_edit_cred_cpan_mail_alias" => "none", + checked => "checked", +%> neither nor + |
Remove account?++You have not yet uploaded any files +to the CPAN, so your account can still be +cancelled. If you want to retire your +account, please click here. If you do +this, your account will not be removed +immediately but instead be removed +manually by the database maintainer at a +later date. + +<%= check_box pause99_edit_cred_ustatus => "delete" %> +Account can be removed + |
<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more distributions. Nothing done.
+% } +Please select one or +more distributions for which you want to be removed from +the co-maintainer table and press Give Up
+ +If you need finer control (eg. to give up comaintainership +for a removed module), visit + +Give up Co-maintainership status per module page.
+ +Select one or more distributions:
+ +| + | Distribution | +Owners | +
|---|---|---|
| <%= check_box "pause99_giveup_dist_comaint_d" => $_->[0] %> | +<%= $_->[0] %><% if ($_->[1] =~ /,/) { %> (incomplete ownership)<% } %> | +<%= $_->[1] %> | +
+
+Sorry, <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %> does not seem to be co-maintainer of any distribution.
+% } diff --git a/lib/pause_2025/templates/user/distperms/make_dist_comaint.html.ep b/lib/pause_2025/templates/user/distperms/make_dist_comaint.html.ep new file mode 100644 index 000000000..ae228259a --- /dev/null +++ b/lib/pause_2025/templates/user/distperms/make_dist_comaint.html.ep @@ -0,0 +1,92 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_make_dist_comaint")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more distributions and enter a userid. + Nothing done.
+% } +Please select one or +more distributions for which you want to select a +co-maintainer, enter the CPAN userid of the co-maintainer +into the text field and press Make Co-Maintainer
+ +If you are open to someone else asking for your first-come +permissions, but you wish to decide on any such request, you +can give a co-maint to a special user called +HANDOFF.
+ +You can also grant co-maint to +NEEDHELP +if you would like additional volunteers to help you work on a particular module.
+ +If you need finer control (eg. to add comaintainers for only +a small part of a distribution just to allow them to handle RT tickets +while prohibiting them to upload the distribution), visit + +Add Comaintainers per module page.
+ +Select one or more distributions:
+| + | Distribution | +Owners | +
|---|---|---|
| <%= check_box "pause99_make_dist_comaint_d" => $_->[0] %> | +<%= $_->[0] %><% if (($_->[1] // '') =~ /,/) { %> (incomplete ownership)<% } %> | +<%= $_->[1] %> | +
Select a userid:
+<%= text_field "pause99_make_dist_comaint_a", size => 15, maxlength => 9 %>
+
+
+
+Sorry, there are no distributions registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } diff --git a/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep b/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep new file mode 100644 index 000000000..36c1441a2 --- /dev/null +++ b/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep @@ -0,0 +1,87 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_move_dist_primary")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more distributions and enter a userid. +Nothing done.
+% } +Please select one +or more distributions for which you want to pass primary +maintainership status, enter the CPAN userid of the new +maintainer into the text field and press Pass Maintainership +Status. Note: you keep co-maintainer status after this move. +If you want to get rid of that too, please visit query(ACTION => 'remove_dist_primary') %>">Give up +co-maintainership status next.
+ +You can only transfer what you actually own. +If multiple owners are listed, you (or the person you are going to +transfer the distribution) need to ask those owners to transfer +their permissions as well.
+ +If you need finer control (eg. to transfer only a small part of +a distribution you and other people own, for clarity's sake), visit + +Transfer Primary Permissions per module page.
+ +Select one or more distributions:
+
| + | Distribution | +Owners | +
|---|---|---|
| <%= check_box "pause99_move_dist_primary_d" => $_->[0] %> | +<%= $_->[0] %><% if ($_->[1] =~ /,/) { %> (incomplete ownership)<% } %> | +<%= $_->[1] %> | +
Select a userid:
+<%= text_field "pause99_move_dist_primary_a", size => 15, maxlength => 9 =%>
+
+
+ +Sorry, there are no distributions registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/distperms/peek.html.ep b/lib/pause_2025/templates/user/distperms/peek.html.ep new file mode 100644 index 000000000..132e6c29e --- /dev/null +++ b/lib/pause_2025/templates/user/distperms/peek.html.ep @@ -0,0 +1,113 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + +Query the perms table by author or by
+distribution. Select the option and fill in a distribution name or
+user ID as appropriate. The answer is all distributions that an
+user ID is registered for or all user IDs registered for a
+distribution, as appropriate.
Registration comes in one of two types: type +first-come is the automatic registration on a +first-come-first-serve basis that happens on the initial +upload. And type co-maint is the registration as +co-maintainer which means that the primary maintainer of +the namespace has granted permission to upload this module +to other userid(s). Per namespace there can only be one +primary maintainer (userid in the +first-come category) and any number of userids in +the co-maint category. Being registered in any of +the categories means that a user is able not only to +upload a module in that namespace but also be accepted by +the indexer. In other words, the indexer will not ignore +uploads for that namespace by that person.
+ +Permission is granted per namespace, +not per distribution. So you might not have enough +permission to upload a distribution or grant permissions to +other people if you are listed here (when you have permissions for +only a part of the distribution). If you want more detailed +information, visit +View permission per module page.
+ +The +contents of the tables presented on this page are mostly +generated automatically, so please report any errors you +observe to <%= "@{$PAUSE::Config->{ADMINS}}" %> so that the tables +can be corrected.--Thank you!
+ +<%= select_field 'pause99_peek_dist_perms_by' => [ + ["for a distribution--exact match" => "de"], + [qq{for a distribution--SQL "LIKE" match} => "dl"], + ["of an author" => "a"], +], size => 1 =%> + +<%= text_field 'pause99_peek_dist_perms_query', size => 44, maxlength => 112, =%> + + + +
+% if (@{$pause->{rows} || []}) { +| <%= $_ %> | +% } +||
|---|---|---|
| $row->[0], + pause99_peek_dist_perms_sub => 1, + ]) %>"><%= $row->[0] %> | ++% my @owners = split /,/, $row->[1] // ''; +% while(my $owner = shift @owners) { + $owner, + pause99_peek_dist_perms_sub => 1, + ]) %>"><%= $owner %><% if (@owners) { %>,<% } %> +% } + | ++% my @comaints = split /,/, $row->[2] // ''; +% while(my $comaint = shift @comaints) { + $comaint, + pause99_peek_dist_perms_sub => 1, + ]) %>"><%= $comaint %><% if (@comaints) { %>,<% } %> +% } + | +
<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more distributions. Nothing done.
+% } +The scrolling +list shows you, which distributions are associated with other +maintainers besides yourself. Every line denotes a tuple +of a distribution and a userid. Select those that you want to +remove and press Remove
+ +If you need finer control (eg. to remove comaintainers +only for a small part of a distribution, or remove comaintainers +for a removed module), visit + +Remove Comaintainers per module page.
+ +| + | Distribution | +UserID | +
|---|---|---|
| <%= check_box "pause99_remove_dist_comaint_tuples" => $_ %> | +<%= $dist %> | +<%= $userid %> | +
+
+There are no co-maintainers registered to any of <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>'s distributions.
+% } diff --git a/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep b/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep new file mode 100644 index 000000000..670c18667 --- /dev/null +++ b/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep @@ -0,0 +1,96 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_remove_dist_primary")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more distributions. Nothing done.
+% } +Please select one or more distributions for which you +want to give up primary maintainership status and press +Give Up Maintainership Status. Note: you keep co-maintainer +status after this move. If you want to get rid of that too, +please visit ">Give up +co-maintainership status next.
+ +Giving up primary permissions now means that the permissions are +transferred to a special user called +ADOPTME. +
+ +You can only give up what you actually own. +If multiple owners are listed, those owners keep their primary +maintainership for their part of the distribution. In this case, +you are strongly advised to +">transfer your primary permissions +to one of the other owners.
+ ++If you have are unsure about what to do, or have any questions, +please email the PAUSE admins at modules@cpan.org. +
+ +If you need finer control (eg. to give up only a small part of +a distribution for whatever reasons), visit + +<%= $c->app->pause->config->action('remove_primary')->{verb} %> page.
+ +Select one or more distributions:
+| + | Distribution | +Owners | +
|---|---|---|
| <%= check_box "pause99_remove_dist_primary_d" => $_->[0] %> | +<%= $_->[0] %><% if ($_->[1] =~ /,/) { %> (incomplete ownership)<% } %> | +<%= $_->[1] %> | +
Sorry, there are no distributions registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/edit_uris.html.ep b/lib/pause_2025/templates/user/edit_uris.html.ep new file mode 100644 index 000000000..baf4724bb --- /dev/null +++ b/lib/pause_2025/templates/user/edit_uris.html.ep @@ -0,0 +1,83 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + +No pending uploads for <%= $pause->{HiddenUser}{userid} %> found
+% } else { + +<%= select_field "pause99_edit_uris_3" => $pause->{all_recs}, + size => 1, +%> + +The record has been updated in the database
+% } elsif ($pause->{update_sel}) { +It seems to me the record was NOT updated. Maybe nothing has changed? +Please take a closer look and inform an admin if things didn't proceed as expected.URI to download
+ +If you change this field to a different URI, +PAUSE will try to fetch this URI instead. Note that the +filename on PAUSE will remain unaltered. So you can fix a +typo, but you cannot alter the name of the uploaded file, it +will be the original filename. So this is only an opportunity +to fix broken uploads that cannot be completed, not an +opportunity to turn the time back. + +To re-iterate: If you change the content of this field to +http://www.slashdot.org/, PAUSE will fetch the current +Slashdot page and will put it into +<%= $pause->{selected}{uriid} %>. If you change it to +FooBar-3.14.tar.gz, PAUSE will try to get +<%= $PAUSE::Config->{INCOMING} %>/FooBar-3.14.tar.gz and if it +finds it, it puts it into <%= $pause->{selected}{uriid} %>.
+ +An example: if you made a typo and requested to upload +http://badsite.org/foo instead of +http://goodsite.org/foo, just correct the thing in the +textfield below.
+ +Another example: If your upload was unsuccessful and you now have +a bad file in the incoming directory, then you have the +problem that PAUSE tries to fetch your file (say foo) +but doesn't succeed and then it retries and retries. Your +solution: transfer the file into the incoming directory with +a different name (say bar) using ftp. Fill in +the different name below. PAUSE will fetch bar and +upload it as foo. So you're done.
+UNIX time of last unsuccessful attempt to retrieve this item
+<%= $pause->{selected}{nosuccesstime} || 0 %>Number of unsuccessful attempts so far
+<%= $pause->{selected}{nosuccesscount} || 0 %>Record was last changed on
+<%= $pause->{selected}{changed} || 0 %>Record was last changed by
+<%= $pause->{selected}{changedby} || 0 %>| + | File | +Size | +Modified | +|
|---|---|---|---|---|
| <%= check_box "pause99_delete_files_FILE" => $file %> | +% if ($files->{$file}{indexed}) { +<%= $file %> [indexed] | +% } else { +<%= $file %> | +% } +<%= $files->{$file}{stat} %> | +<%= $files->{$file}{blurb} %> | +
| File | +Size | +Modified | +|
|---|---|---|---|
| <%= $file %> [indexed] | +% } else { +<%= $file %> | +% } +<%= $files->{$file}{stat} %> | +<%= $files->{$file}{blurb} %> | +
<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +% for (@{$pause->{results}}) { +% if ($_->{error}) { +Error trying to add <%= $_->{user} %> to co-maintainers of <%= $_->{mod} %>: <%= $_->{error} %>
+% } elsif ($_->{duplicated}) { +<%= $_->{user} %> was already a co-maintainer of <%= $_->{mod} %>: skipping
+ +% } else { +Added <%= $_->{user} %> to co-maintainers of <%= $_->{mod} %>.
+% } +% } +% } else { +You need to select one or more packages and enter a userid. + Nothing done.
+% } +Please select one or +more namespaces for which you want to select a +co-maintainer, enter the CPAN userid of the co-maintainer +into the text field and press Make Co-Maintainer
+ +If you are open to someone else asking for your first-come +permissions, but you wish to decide on any such request, you +can give a co-maint to a special user called +HANDOFF.
+ +You can also grant co-maint to +NEEDHELP +if you would like additional volunteers to help you work on a particular module.
+ +If you want to add comaintainers for all the modules in a +distribution, visit +Add Comaintainers per distribution page.
+ +Select one or more namespaces:
+ +<%= select_field "pause99_share_perms_makeco_m" => $pause->{mods}, + multiple => "multiple", + size => (@{$pause->{mods}} > 18 ? 15 : scalar @{$pause->{mods}}), +=%> + +
+Select a userid:
+<%= text_field "pause99_share_perms_makeco_a", size => 15, maxlength => 9 %>
+
+
+
+ +% } else { +Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } diff --git a/lib/pause_2025/templates/user/perms/_share_movepr.html.ep b/lib/pause_2025/templates/user/perms/_share_movepr.html.ep new file mode 100644 index 000000000..862cbfd62 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/_share_movepr.html.ep @@ -0,0 +1,52 @@ +% my $pause = stash(".pause") || {}; + +% if (param("SUBMIT_pause99_share_perms_movepr")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +% for my $res (@{$pause->{results}}) { +% if ($res->{error}) { +Error trying to make <%= $res->{user} %> primary maintainer of <%= $res->{mod} %>: <%= $res->{error} %>
\n"; +% } else { +Made <%= $res->{user} %> primary maintainer of <%= $res->{mod} %>.
+% } +% } +% } else { +You need to select one or more packages and enter a userid. +Nothing done.
+% } +Please select one +or more namespaces for which you want to pass primary +maintainership status, enter the CPAN userid of the new +maintainer into the text field and press Pass Maintainership +Status. Note: you keep co-maintainer status after this move. +If you want to get rid of that too, please visit query([pause99_share_perms_remome => 1]) %>">Give up +co-maintainership status next.
+ +If you want to transfer all the modules in a distribution, visit + +Transfer Primary Permissions per distribution page.
+ +Select one or more namespaces:
+<%= select_field "pause99_share_perms_pr_m" => $pause->{mods}, + multiple => "multiple", + size => (@{$pause->{mods}} > 18 ? 15 : scalar @{$pause->{mods}}), +=%> + +
+Select a userid:
+<%= text_field "pause99_share_perms_movepr_a", size => 15, maxlength => 9 =%>
+
+
+ +% } else { +Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/perms/_share_remocos.html.ep b/lib/pause_2025/templates/user/perms/_share_remocos.html.ep new file mode 100644 index 000000000..694d58ddf --- /dev/null +++ b/lib/pause_2025/templates/user/perms/_share_remocos.html.ep @@ -0,0 +1,48 @@ +% my $pause = stash(".pause") || {}; + +% if (param("SUBMIT_pause99_share_perms_remocos")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +% for (@{$pause->{results}}) { +% if ($_->{not_exists}) { +Cannot handle tuple <%= $_->{mod} %>. If you believe, this is a bug, please complain.
+% } elsif ($_->{error}) { +Error trying to remove <%= $_->{user} %> from co-maintainers of <%= $_->{mod} %>: <%= $_->{error} %>
+% } else { +Removed <%= $_->{user} %> from co-maintainers of <%= $_->{mod} %>.
+% } +% } +% } else { +You need to select one or more packages. Nothing done.
+% } +The scrolling +list shows you, which packages are associated with other +maintainers besides yourself. Every line denotes a tuple +of a namespace and a userid. Select those that you want to +remove and press Remove
+ +
If you want to remove comaintainers from all the modules +in a distribution, visit + +Remove Comaintainers per distribution page.
+ +<%= select_field "pause99_share_perms_remocos_tuples" => $pause->{mods}, + multiple => "multiple", + size => (@{$pause->{mods}} > 18 ? 15 : scalar (@{$pause->{mods}})), +%> + ++
+ +% } else { +There are no co-maintainers registered to any of <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>'s modules.
+% } diff --git a/lib/pause_2025/templates/user/perms/_share_remome.html.ep b/lib/pause_2025/templates/user/perms/_share_remome.html.ep new file mode 100644 index 000000000..39b8dee96 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/_share_remome.html.ep @@ -0,0 +1,47 @@ +% my $pause = stash(".pause") || {}; + +% if (param("SUBMIT_pause99_share_perms_remome")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +% for (@{$pause->{results}}) { +% if ($_->{error}) { +Error trying to remove <%= $_->{user} %> from co-maintainers of <%= $_->{mod} %>: <%= $_->{error} %>
+% } else { +Removed <%= $_->{user} %> from co-maintainers of <%= $_->{mod} %>.
+% } +% } +% } else { +You need to select one or more packages. Nothing done.
+% } +Please select one or +more namespaces for which you want to be removed from +the co-maintainer table and press Give Up
+ +If you want to give up comaintainership for all the modules +in a distribution, visit + +Give up Co-maintainership status per distribution page.
+ +Select one or more namespaces:
+ +<%= select_field "pause99_share_perms_remome_m" => $pause->{mods}, + multiple => "multiple", + size => (@{$pause->{mods}} > 18 ? 15 : scalar @{$pause->{mods}}), +=%> + +
++
+ +% } else { +Sorry, <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %> does not seem to be co-maintainer of any module.
+% } diff --git a/lib/pause_2025/templates/user/perms/_share_remopr.html.ep b/lib/pause_2025/templates/user/perms/_share_remopr.html.ep new file mode 100644 index 000000000..3dbee1681 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/_share_remopr.html.ep @@ -0,0 +1,50 @@ +% my $pause = stash(".pause") || {}; + +% if (param("SUBMIT_pause99_share_perms_remopr")) { +<%= $pause->{error} %>
+% } elsif (!@{$pause->{results} || []}) { +You need to select one or more packages. Nothing done.
+% } else { +% for (@{$pause->{results}}) { +% if ($_->{error}) { +Error trying to remove primary maintainership of <%= $_->{user} %> from <%= $_->{mod} %>: <%= $_->{error} %>
+% } else { +Removed primary maintainership of <%= $_->{user} %> from <%= $_->{mod} %>.
+% } +% } +% } +Please select one or more namespaces for which you +want to give up primary maintainership status and press +Give Up Maintainership Status. Note: you keep co-maintainer +status after this move. If you want to get rid of that too, +please visit Give up +co-maintainership status next.
+ +If you want to give up comaintainership for all the modules +in a distribution, visit + +Give up Co-maintainership status per distribution page.
+ +Select one or more namespaces:
+<%= select_field "pause99_share_perms_pr_m" => $pause->{mods}, + multilple => "multiple", + size => (@{$pause->{mods}} > 18 ? 15 : scalar @{$pause->{mods}}), +=%> + +
+
+ +% } else { +Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep new file mode 100644 index 000000000..a1d10c908 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep @@ -0,0 +1,75 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_share_perms_remome")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more packages. Nothing done.
+% } +Please select one or +more namespaces for which you want to be removed from +the co-maintainer table and press Give Up
+ +If you want to give up comaintainership for all the modules +in a distribution, visit + +Give up Co-maintainership status per distribution page.
+ +Select one or more namespaces:
+ +| + | Package | +Indexed Distribution | +
|---|---|---|
| <%= check_box "pause99_share_perms_remome_m" => $_ %> | +<%= $_ %> | +<%= $pause->{dist_for_package}{$_} // '' %> | +
+
+Sorry, <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %> does not seem to be co-maintainer of any module.
+% } diff --git a/lib/pause_2025/templates/user/perms/make_comaint.html.ep b/lib/pause_2025/templates/user/perms/make_comaint.html.ep new file mode 100644 index 000000000..33cd2092b --- /dev/null +++ b/lib/pause_2025/templates/user/perms/make_comaint.html.ep @@ -0,0 +1,90 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_share_perms_makeco")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more packages and enter a userid. + Nothing done.
+% } +Please select one or +more namespaces for which you want to select a +co-maintainer, enter the CPAN userid of the co-maintainer +into the text field and press Make Co-Maintainer
+ +If you are open to someone else asking for your first-come +permissions, but you wish to decide on any such request, you +can give a co-maint to a special user called +HANDOFF.
+ +You can also grant co-maint to +NEEDHELP +if you would like additional volunteers to help you work on a particular module.
+ +If you want to add comaintainers for all the modules in a +distribution, visit +Add Comaintainers per distribution page.
+ +Select one or more namespaces:
+| + | Package | +Indexed Distribution | +
|---|---|---|
| <%= check_box "pause99_share_perms_makeco_m" => $_ %> | +<%= $_ %> | +<%= $pause->{dist_for_package}{$_} %> | +
Select a userid:
+<%= text_field "pause99_share_perms_makeco_a", size => 15, maxlength => 9 %>
+
+
+
+Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } diff --git a/lib/pause_2025/templates/user/perms/move_primary.html.ep b/lib/pause_2025/templates/user/perms/move_primary.html.ep new file mode 100644 index 000000000..faa112646 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/move_primary.html.ep @@ -0,0 +1,81 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_share_perms_movepr")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more packages and enter a userid. +Nothing done.
+% } +Please select one +or more namespaces for which you want to pass primary +maintainership status, enter the CPAN userid of the new +maintainer into the text field and press Pass Maintainership +Status. Note: you keep co-maintainer status after this move. +If you want to get rid of that too, please visit query(ACTION => 'remove_primary') %>">Give up +co-maintainership status next.
+ +If you want to transfer all the modules in a distribution, visit + +Transfer Primary Permissions per distribution page.
+ +Select one or more namespaces:
+
| + | Package | +Indexed Distribution | +
|---|---|---|
| <%= check_box "pause99_share_perms_pr_m" => $_ %> | +<%= $_ %> | +<%= $pause->{dist_for_package}{$_} // '' %> | +
Select a userid:
+<%= text_field "pause99_share_perms_movepr_a", size => 15, maxlength => 9 =%>
+
+
+ +Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/perms/peek.html.ep b/lib/pause_2025/templates/user/perms/peek.html.ep new file mode 100644 index 000000000..d0d0d5ea8 --- /dev/null +++ b/lib/pause_2025/templates/user/perms/peek.html.ep @@ -0,0 +1,95 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + +Query the perms table by author or by
+module. Select the option and fill in a module name or
+user ID as appropriate. The answer is all modules that an
+user ID is registered for or all user IDs registered for a
+module, as appropriate.
Registration comes in one of two types: type +first-come is the automatic registration on a +first-come-first-serve basis that happens on the initial +upload. And type co-maint is the registration as +co-maintainer which means that the primary maintainer of +the namespace has granted permission to upload this module +to other userid(s). Per namespace there can only be one +primary maintainer (userid in the +first-come category) and any number of userids in +the co-maint category. Being registered in any of +the categories means that a user is able not only to +upload a module in that namespace but also be accepted by +the indexer. In other words, the indexer will not ignore +uploads for that namespace by that person.
+ +If the list is too long, visit +View permission per distribution page.
+ +The +contents of the tables presented on this page are mostly +generated automatically, so please report any errors you +observe to <%= "@{$PAUSE::Config->{ADMINS}}" %> so that the tables +can be corrected.--Thank you!
+ +<%= select_field 'pause99_peek_perms_by' => [ + ["for a module--exact match" => "me"], + [qq{for a module--SQL "LIKE" match} => "ml"], + ["of an author" => "a"], +], size => 1 =%> + +<%= text_field 'pause99_peek_perms_query', size => 44, maxlength => 112, =%> + + + +
+% if (@{$pause->{rows} || []}) { +| <%= $_ %> | +% } +|||
|---|---|---|---|
| $row->[0], + pause99_peek_perms_sub => 1, + ]) %>"><%= $row->[0] %> | +$row->[1], + pause99_peek_perms_sub => 1, + ]) %>"><%= $row->[1] %> | +<%= $row->[2] %> | +<%= $row->[3] %> | +
<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more packages. Nothing done.
+% } +The scrolling +list shows you, which packages are associated with other +maintainers besides yourself. Every line denotes a tuple +of a namespace and a userid. Select those that you want to +remove and press Remove
+ +If you want to remove comaintainers from all the modules +in a distribution, visit + +Remove Comaintainers per distribution page.
+ +| + | Package | +Indexed Distribution | +UserID | +
|---|---|---|---|
| <%= check_box "pause99_share_perms_remocos_tuples" => $_ %> | +<%= $package %> | +<%= $pause->{dist_for_package}{$package} // '' %> | +<%= $userid %> | +
+
+There are no co-maintainers registered to any of <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>'s modules.
+% } diff --git a/lib/pause_2025/templates/user/perms/remove_primary.html.ep b/lib/pause_2025/templates/user/perms/remove_primary.html.ep new file mode 100644 index 000000000..778a7cd0a --- /dev/null +++ b/lib/pause_2025/templates/user/perms/remove_primary.html.ep @@ -0,0 +1,92 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; + + + +% if (param("SUBMIT_pause99_share_perms_remopr")) { +<%= $pause->{error} %>
+% } elsif (@{$pause->{results} || []}) { +You need to select one or more packages. Nothing done.
+% } +Please select one or more namespaces for which you +want to give up primary maintainership status and press +Give Up Maintainership Status. Note: you keep co-maintainer +status after this move. If you want to get rid of that too, +please visit ">Give up +co-maintainership status next.
+ +Giving up primary permissions now means that the permissions are +transferred to a special user called +ADOPTME.
+ +If multiple owners are listed, those owners keep their primary +maintainership for those modules. In this case, you are strongly advised to +">transfer your primary permissions +to one of the other owners.
+ ++If you have are unsure about what to do, or have any questions, +please email the PAUSE admins at modules@cpan.org. +
+ +If you want to give up all the modules in a distribution, visit + +<%= $c->app->pause->config->action('remove_dist_primary')->{verb} %> page.
+ +Select one or more namespaces:
+| + | Package | +Indexed Distribution | +
|---|---|---|
| <%= check_box "pause99_share_perms_pr_m" => $_ %> | +<%= $_ %> | +<%= $pause->{dist_for_package}{$_} // '' %> | +
Sorry, there are no modules registered belonging to <%= $pause->{HiddenUser}{userid} || $pause->{User}{userid} %>.
+% } + diff --git a/lib/pause_2025/templates/user/perms/share.html.ep b/lib/pause_2025/templates/user/perms/share.html.ep new file mode 100644 index 000000000..061ed849c --- /dev/null +++ b/lib/pause_2025/templates/user/perms/share.html.ep @@ -0,0 +1,159 @@ +% layout 'layout'; +% my $pause = stash(".pause") || {}; +% my $subaction = $pause->{subaction} || ""; + + + + +% unless ($subaction) { +Permissions on PAUSE come in three flavors:
+You can view your current set of permissions on the View Permissions page. To + change permissions, select one of the following submit + buttons, each of which leads you to a different page:
+ +| + 1. You are registered in modulelist + | +||
| +% if (my @mods = @{$pause->{mods} || []}) { +<%= select_field "pause99_edit_mod_3" => \@mods, size => (@mods > 18 ? 15 : scalar @mods) %> +% } else { +--NONE-- +% } + | ++ | ++ Module Metadata has been removed from PAUSE and + is no longer editable. Please contact a PAUSE administrator to + choose a new owner. + | +
| 2. You are primary maintainer: | ||
| +% if (my @mods = @{$pause->{remove_primary} || []}) { +<%= select_field "pause99_share_perms_pr_m" => \@mods, size => (@mods > 18 ? 15 : scalar @mods), multiple => "multiple" %> +% } else { +--NONE-- +% } + | ++ + | ++ 2.1 Transfer primary maintainership status to somebody else + (you become co-maintainer) + | +
| + + | ++ 2.2 Give up primary maintainership status (abandoning it without + transfering it to someone else) + | +|
| + 3. Making and unmaking co-maintainers (for both modulelist + owners and primary maintainers): + | +||
| +% if (my @mods = @{$pause->{make_comaintainer} || []}) { +<%= select_field "pause99_share_perms_makeco_m" => \@mods, size => (@mods > 18 ? 15 : scalar @mods), multiple => "multiple" %> +% } else { +--NONE-- +% } + | ++ + | ++ 3.1 Make somebody else co-maintainer + | +
| + + | +3.2 Remove a co-maintainer | +|
| 4. You are co-maintainer | ||
| +% if (my @mods = @{$pause->{remove_comaintainer} || []}) { +<%= select_field "pause99_share_perms_remome_m" => \@mods, size => (@mods > 18 ? 15 : scalar @mods), multiple => "multiple" %> +% } else { +--NONE-- +% } + | ++ | ++ 4.1 Give up co-maintainership status + | +
Indexing normally happens only once, shortly after the upload takes place. Sometimes it is necessary to reindex a file. The reason is typically one of the following:
+perms table got altered, now a file should be visited again to overrule the previous indexing decision.With this form you can tell the indexer to index selected files again. As it is done by a cron job, it may take up to an hour until the indexer actually executes the command. If this doesn't repair the index, please email me.
+ +% if (%$files) { +% if ($pause->{mailbody}) { +<%= $pause->{mailbody} %>| + | File | +
|---|---|
| <%= check_box "pause99_reindex_FILE" => $file %> | +<%= $file %> | +
Note: resetting versions is a major inconvenience for +module users. This page will probably be withdrawn from PAUSE if +the perl community does not want to allow decreasing version numbers +on the CPAN. For now: use with care. Thanks.
+ +Below you see the packages and version numbers that +the indexer considers the current and highest version number that +it has seen so far. By selecting an item in the list and clicking +Forget, this value is set to undef. This opens the +way for a Force Reindexing run in which the version of the +package in the reindexed distribution can become the current.
+ +Did I say, this operation should not be done lightly? Because +users of the module out there may still have that higher version +installed and so will not notice the newer but lower-numbered +release. Let me repeat: please make responsible use of this +page.
+ +Q: So why is this page up at all?
+ +A: Combine a multi-module-distro with a small mistake in an +older release or a bug in the PAUSE indexer. In such a case you +will be happy to use this page and nobody else will ever notice +there was a problem.
+% if ($pause->{mailbody}) { +<%= $pause->{mailbody} %>| + | Package | +Version | +Dist | +
|---|---|---|---|
| <%= check_box pause99_reset_version_PKG => $package %> | +<%= $package %> | +<%= $pause->{packages}{$package}{version} %> | +<%= $pause->{packages}{$package}{dist} %> | +
| <%= $_ %> |
+ + +% if ($pause->{invalid_uri}) { +% } else { + ++ +Submitting query
+ +% if ($pause->{query_succeeded}) { + +Query succeeded. Thank you for your contribution
+ +As it is done by a separate process, it may take a few minutes to +complete the upload. The processing of your file is going on while you +read this. There's no need for you to retry. The form below is only +here in case you want to upload further files.
+ +Please tidy up your homedir: CPAN is getting larger every day which +is nice but usually there is no need to keep old an outdated version +of a module on several hundred mirrors. Please consider query(ACTION => "delete_files") %>">removing old versions of +your module from PAUSE and CPAN. If you are worried that someone might +need an old version, it can always be found on the {userhome} %>/">backpan +
+ +Debugging: your submission should show up soon at {usrdir} %>"><%= $pause->{usrdir} %>. If something's wrong, please +check the logfile of the daemon: see the tail of it with {tailurl} %>"><%= $pause->{tailurl} %>. If you already know what's going wrong, you +may wish to visit the ">repair +tool for pending uploads.
+ +% } else { + +Could not enter the URL into the database. +Reason:
<%= $pause->{errmsg} %>
+ +% if ($pause->{duplicate}) { +This indicates that you probably tried to upload a file that is +already in the database. You will most probably have to rename your +file and try again, because PAUSE doesn't let you upload a file +twice.
+ +This seems to be the record causing the conflict:
++% if (my $rec = $pause->{rec}) { +% for my $k (sort keys %$rec) { +
+ + +% } +% } +% } + + ++% } +% } + <%= $k %> <%= $rec->{$k} || b(" ") %>
Your filename has been altered as it contained characters besides +the class [A-Za-z0-9_\\-\\.\\@\\+]. DEBUG: your filename[<%= $renamed->{from} %>] corrected +filename[<%= $renamed->{to} %>].
+ +% } + +% if (! $pause->{uploaded_uri} ) { + +% } else { + +%= include "user/uri/_continued"; + +% } + + + +This form enables you to enter one file at a time +into CPAN in one of these ways:
+ +% if ($pause->{tryupload}) { + +HTTP Upload: As an
+HTTP upload: enter the filename in the lower text field.
+Hint: If you encounter problems processing this form,
+it may be due to the fact that your browser can't handle
+multipart/form-data forms that support file
+upload. In such a case, please retry to access this query(ACTION => "add_uri", CAN_MULTIPART => 0) %>">file-upload-disabled form.
HTTP Upload: As +you do not seem to want HTTP upload enabled, we do +not offer it. If this is not what you want, try to + 1) %>">explicitly enable HTTP upload.
+ +% } + +GET URL: PAUSE fetches any http or ftp +URL that can be handled by LWP (Note: https is currently not +supported): use the text field (please specify the complete +URL).
+ +Please, make sure your filename +contains a version number. For security reasons you will never +be able to upload a file with the same name again (not even +after deleting it). Thank you.+ +
There is no need to upload README files separately. The +upload server will unwrap your files (.tar.gz or .zip files +only) within a few hours after uploading and will put the +topmost README file as, say, Foo-Bar-3.14.readme into your +directory. Hint: if you're looking for an even more +convenient way to upload files than this form, you can try the +cpan-upload script. +
+ +If you want to load the +file into a directory below your CPAN directory, +please specify the directory name here. Any number of +subdirectory levels is allowed, they all will be +created on the fly if they don't exist yet. Only sane +directory names are allowed and the number of +characters for the whole path is limited.
+NOTE: To upload a Perl6 distribution a target +directory whose top level subdirectory is "Perl6" must +be specified. In addition, a Perl6 distribution must +contain a META6.json. Pause will only consider it a +Perl6 dist if these two conditions are satisfied. +
+ +
+<%= text_field "pause99_add_uri_subdirtext",
+ size => 32,
+ maxlength => 128,
+%>
+
+% if (@{$pause->{subdirs} || []}) {
+
+<%= select_field "pause99_add_uri_subdirscrl" => $pause->{subdirs},
+ size => (@{$pause->{subdirs}} > 18 ? 15 : scalar @{$pause->{subdirs}}),
+%>
+% }
+
If your browser can handle
+file upload, enter the filename here and I'll transfer it
+to your homedirectory:
+
+
<%= file_field "pause99_add_uri_httpupload",
+ size => 50,
+%>
+
If you want me to fetch a file from an URL, enter the full URL here.
+
+
<%= text_field "pause99_add_uri_uri",
+ size => 64,
+ maxlength => 128,
+%>
+
If this is a bad request: "manage_id_requests",
subaction => "delete",
USERID => $pause->{userid},
diff --git a/lib/pause_2025/templates/email/admin/user/onetime_password.email.ep b/lib/pause_2025/templates/email/admin/user/onetime_password.email.ep
index 5b7aee6e6..c9708c173 100644
--- a/lib/pause_2025/templates/email/admin/user/onetime_password.email.ep
+++ b/lib/pause_2025/templates/email/admin/user/onetime_password.email.ep
@@ -12,7 +12,7 @@ assigned to you a change-password-only-password that enables
you to pick your own password. This password is "<%== $pause->{onetime} %>"
(without the enclosing quotes). Please visit
- <%== my_full_url->path("/pause/authenquery")->query(ACTION => "change_passwd")->scheme("https") %>
+ <%== my_full_url(ACTION => "change_passwd")->scheme("https") %>
and use this password to initialize your account in the authentication
database. Once you have entered your password there, your one-time
diff --git a/lib/pause_2025/templates/email/admin/user/welcome_user.email.ep b/lib/pause_2025/templates/email/admin/user/welcome_user.email.ep
index 0640e15c2..293aba8c7 100644
--- a/lib/pause_2025/templates/email/admin/user/welcome_user.email.ep
+++ b/lib/pause_2025/templates/email/admin/user/welcome_user.email.ep
@@ -23,9 +23,9 @@ Please note that your email address is exposed in various listings and
database dumps. You can register with both a public and a secret email
if you want to protect yourself from SPAM. If you want to do this,
please visit
- <%== my_full_url->path("/pause/authenquery")->query(ACTION => "edit_cred" )->scheme("https") %>
+ <%== my_full_url(ACTION => "edit_cred" )->scheme("https") %>
or
- <%== my_full_url->path("/pause/authenquery")->query(ACTION => "edit_cred" )->scheme("http") %>
+ <%== my_full_url(ACTION => "edit_cred" )->scheme("http") %>
If you need any further information, please visit
$CPAN/modules/04pause.html.
diff --git a/lib/pause_2025/templates/email/public/mailpw.email.ep b/lib/pause_2025/templates/email/public/mailpw.email.ep
index 761df49a8..8e3d9b36e 100644
--- a/lib/pause_2025/templates/email/public/mailpw.email.ep
+++ b/lib/pause_2025/templates/email/public/mailpw.email.ep
@@ -9,14 +9,14 @@ take more precautions to prevent abuse.)
Somebody, probably you, has visited the URL
- <%== my_full_url->query(ACTION => "mailpw") %>
+ <%== my_full_url(ACTION => "mailpw") %>
and asked that you, "<%= $pause->{mailpw_userid} %>", should get a token that enables the
setting of a new password. Here it is (please watch out for line
wrapping errors of your mail reader and other cut and paste errors,
this URL must not contain any spaces):
- <%== my_full_url->path("/pause/query")->query(ACTION => "change_passwd", ABRA => "$pause->{mailpw_userid}.$pause->{passwd}")->scheme("https") %>
+ <%== my_full_url(ACTION => "change_passwd", ABRA => "$pause->{mailpw_userid}.$pause->{passwd}")->scheme("https") %>
Please visit this URL, it should open you the door to a password
changer that lets you set a new password for yourself. This token
diff --git a/lib/pause_2025/templates/email/public/request_id.email.ep b/lib/pause_2025/templates/email/public/request_id.email.ep
index 965f616e0..0da2a780a 100644
--- a/lib/pause_2025/templates/email/public/request_id.email.ep
+++ b/lib/pause_2025/templates/email/public/request_id.email.ep
@@ -14,12 +14,12 @@ homepage: <%== $pause->{homepage} %>
The following links are only valid for PAUSE maintainers:
Registration form with editing capabilities:
- <%== my_full_url->path("/pause/authenquery")->scheme('https')->query(
+ <%== my_full_url(
ACTION => "add_user",
USERID => $pause->{session_id},
- SUBMIT_pause99_add_user_sub => 1) %>
+ SUBMIT_pause99_add_user_sub => 1)->scheme('https') %>
Immediate (one click) registration:
- <%== my_full_url->path("/pause/authenquery")->scheme('https')->query(
+ <%== my_full_url(
ACTION => "add_user",
USERID => $pause->{session_id},
- SUBMIT_pause99_add_user_Definitely => 1 ) %>
+ SUBMIT_pause99_add_user_Definitely => 1 )->scheme('https') %>
diff --git a/lib/pause_2025/templates/email/user/delete_files.email.ep b/lib/pause_2025/templates/email/user/delete_files.email.ep
index ca74814e9..5fada0747 100644
--- a/lib/pause_2025/templates/email/user/delete_files.email.ep
+++ b/lib/pause_2025/templates/email/user/delete_files.email.ep
@@ -7,8 +7,8 @@ According to a request entered by <%== $pause->{User}{fullname} %> the
following files and the symlinks pointing to them have been scheduled
for deletion. They will expire after 72 hours and then be deleted by a
cronjob. Until then you can undelete them via
-<%== my_full_url->path("/pause/authenquery")->query(ACTION => "delete_files")->scheme("https") %> or
-<%== my_full_url->path("/pause/authenquery")->query(ACTION => "delete_files")->scheme("http") %>
+<%== my_full_url(ACTION => "delete_files")->scheme("https") %> or
+<%== my_full_url(ACTION => "delete_files")->scheme("http") %>
% end
<%== $pause->{blurb} %>
diff --git a/lib/pause_2025/templates/public/admin.html.ep b/lib/pause_2025/templates/public/admin.html.ep
index 4b2a11ad7..461079328 100644
--- a/lib/pause_2025/templates/public/admin.html.ep
+++ b/lib/pause_2025/templates/public/admin.html.ep
@@ -3,6 +3,6 @@
Query the Registered admins: <%= join ", ", @{$pause->{admins} || []} %> "YAML") %>" style="text-decoration: none;">
+ "YAML") %>" style="text-decoration: none;">
Query the Registered pumpkins: <%= join ", ", @{$pause->{pumpkins} || []} %> If you need finer control (eg. to give up comaintainership
for a removed module), visit
-
+
Give up Co-maintainership status per module page. Select one or more distributions: If you are open to someone else asking for your first-come
permissions, but you wish to decide on any such request, you
can give a co-maint to a special user called
-HANDOFF.grouptable table for who is an admin bit holdergrouptable table for who is a pumpkin bit holder
You can also grant co-maint to -NEEDHELP +NEEDHELP if you would like additional volunteers to help you work on a particular module.
If you need finer control (eg. to add comaintainers for only a small part of a distribution just to allow them to handle RT tickets while prohibiting them to upload the distribution), visit - + Add Comaintainers per module page.
Select one or more distributions:
diff --git a/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep b/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep index 36c1441a2..b950ba585 100644 --- a/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep +++ b/lib/pause_2025/templates/user/distperms/move_dist_primary.html.ep @@ -32,7 +32,7 @@ maintainership status, enter the CPAN userid of the new maintainer into the text field and press Pass Maintainership Status. Note: you keep co-maintainer status after this move. If you want to get rid of that too, please visit query(ACTION => 'remove_dist_primary') %>">Give up +href="<%= my_url(ACTION => 'remove_dist_primary') %>">Give up co-maintainership status next.You can only transfer what you actually own. @@ -42,7 +42,7 @@ their permissions as well.
If you need finer control (eg. to transfer only a small part of a distribution you and other people own, for clarity's sake), visit - + Transfer Primary Permissions per module page.
Select one or more distributions:
diff --git a/lib/pause_2025/templates/user/distperms/peek.html.ep b/lib/pause_2025/templates/user/distperms/peek.html.ep index 132e6c29e..d40b579b4 100644 --- a/lib/pause_2025/templates/user/distperms/peek.html.ep +++ b/lib/pause_2025/templates/user/distperms/peek.html.ep @@ -27,7 +27,7 @@ not per distribution. So you might not have enough permission to upload a distribution or grant permissions to other people if you are listed here (when you have permissions for only a part of the distribution). If you want more detailed -information, visit +information, visit View permission per module page.
The @@ -61,7 +61,7 @@ can be corrected.--Thank you!
% }
If you need finer control (eg. to remove comaintainers only for a small part of a distribution, or remove comaintainers for a removed module), visit - + Remove Comaintainers per module page.
Giving up primary permissions now means that the permissions are transferred to a special user called -ADOPTME. +ADOPTME.
You can only give up what you actually own. If multiple owners are listed, those owners keep their primary maintainership for their part of the distribution. In this case, you are strongly advised to -">transfer your primary permissions +">transfer your primary permissions to one of the other owners.
@@ -53,7 +53,7 @@ please email the PAUSE admins at modules@cpan.
If you need finer control (eg. to give up only a small part of
a distribution for whatever reasons), visit
-
+
<%= $c->app->pause->config->action('remove_primary')->{verb} %> page. Select one or more distributions:
If you are open to someone else asking for your first-come permissions, but you wish to decide on any such request, you can give a co-maint to a special user called -HANDOFF.
+HANDOFF.You can also grant co-maint to -NEEDHELP +NEEDHELP if you would like additional volunteers to help you work on a particular module.
If you want to add comaintainers for all the modules in a -distribution, visit +distribution, visit Add Comaintainers per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/_share_movepr.html.ep b/lib/pause_2025/templates/user/perms/_share_movepr.html.ep index 862cbfd62..d7239e0cc 100644 --- a/lib/pause_2025/templates/user/perms/_share_movepr.html.ep +++ b/lib/pause_2025/templates/user/perms/_share_movepr.html.ep @@ -27,11 +27,11 @@ maintainership status, enter the CPAN userid of the new maintainer into the text field and press Pass Maintainership Status. Note: you keep co-maintainer status after this move. If you want to get rid of that too, please visit query([pause99_share_perms_remome => 1]) %>">Give up +href="<%= my_url([pause99_share_perms_remome => 1]) %>">Give up co-maintainership status next.
If you want to transfer all the modules in a distribution, visit - + Transfer Primary Permissions per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/_share_remocos.html.ep b/lib/pause_2025/templates/user/perms/_share_remocos.html.ep index 694d58ddf..498c83c0b 100644 --- a/lib/pause_2025/templates/user/perms/_share_remocos.html.ep +++ b/lib/pause_2025/templates/user/perms/_share_remocos.html.ep @@ -31,7 +31,7 @@ remove and press Remove
If you want to remove comaintainers from all the modules in a distribution, visit - + Remove Comaintainers per distribution page.
<%= select_field "pause99_share_perms_remocos_tuples" => $pause->{mods}, diff --git a/lib/pause_2025/templates/user/perms/_share_remome.html.ep b/lib/pause_2025/templates/user/perms/_share_remome.html.ep index 39b8dee96..b09b655df 100644 --- a/lib/pause_2025/templates/user/perms/_share_remome.html.ep +++ b/lib/pause_2025/templates/user/perms/_share_remome.html.ep @@ -27,7 +27,7 @@ the co-maintainer table and press Give UpIf you want to give up comaintainership for all the modules in a distribution, visit - + Give up Co-maintainership status per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/_share_remopr.html.ep b/lib/pause_2025/templates/user/perms/_share_remopr.html.ep index 3dbee1681..d5d3c4526 100644 --- a/lib/pause_2025/templates/user/perms/_share_remopr.html.ep +++ b/lib/pause_2025/templates/user/perms/_share_remopr.html.ep @@ -26,12 +26,12 @@ want to give up primary maintainership status and press Give Up Maintainership Status. Note: you keep co-maintainer status after this move. If you want to get rid of that too, -please visit Give up +please visit Give up co-maintainership status next.
If you want to give up comaintainership for all the modules in a distribution, visit - + Give up Co-maintainership status per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep index a1d10c908..88cd9549b 100644 --- a/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep +++ b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep @@ -32,7 +32,7 @@ the co-maintainer table and press Give Up
If you want to give up comaintainership for all the modules in a distribution, visit - + Give up Co-maintainership status per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/make_comaint.html.ep b/lib/pause_2025/templates/user/perms/make_comaint.html.ep index 33cd2092b..a05bf3fee 100644 --- a/lib/pause_2025/templates/user/perms/make_comaint.html.ep +++ b/lib/pause_2025/templates/user/perms/make_comaint.html.ep @@ -38,14 +38,14 @@ into the text field and press Make Co-MaintainerIf you are open to someone else asking for your first-come permissions, but you wish to decide on any such request, you can give a co-maint to a special user called -HANDOFF.
+HANDOFF.You can also grant co-maint to -NEEDHELP +NEEDHELP if you would like additional volunteers to help you work on a particular module.
If you want to add comaintainers for all the modules in a -distribution, visit +distribution, visit Add Comaintainers per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/move_primary.html.ep b/lib/pause_2025/templates/user/perms/move_primary.html.ep index faa112646..94505b229 100644 --- a/lib/pause_2025/templates/user/perms/move_primary.html.ep +++ b/lib/pause_2025/templates/user/perms/move_primary.html.ep @@ -32,11 +32,11 @@ maintainership status, enter the CPAN userid of the new maintainer into the text field and press Pass Maintainership Status. Note: you keep co-maintainer status after this move. If you want to get rid of that too, please visit query(ACTION => 'remove_primary') %>">Give up +href="<%= my_url(ACTION => 'remove_primary') %>">Give up co-maintainership status next.If you want to transfer all the modules in a distribution, visit - + Transfer Primary Permissions per distribution page.
Select one or more namespaces:
diff --git a/lib/pause_2025/templates/user/perms/peek.html.ep b/lib/pause_2025/templates/user/perms/peek.html.ep index d0d0d5ea8..06907ad64 100644 --- a/lib/pause_2025/templates/user/perms/peek.html.ep +++ b/lib/pause_2025/templates/user/perms/peek.html.ep @@ -22,7 +22,7 @@ upload a module in that namespace but also be accepted by the indexer. In other words, the indexer will not ignore uploads for that namespace by that person.
-If the list is too long, visit
+ If the list is too long, visit
View permission per distribution page. The
@@ -56,12 +56,12 @@ can be corrected.--Thank you!
% }
If you want to remove comaintainers from all the modules
in a distribution, visit
-
+
Remove Comaintainers per distribution page. Giving up primary permissions now means that the permissions are
transferred to a special user called
-ADOPTME. If multiple owners are listed, those owners keep their primary
maintainership for those modules. In this case, you are strongly advised to
-">transfer your primary permissions
+">transfer your primary permissions
to one of the other owners.
@@ -49,7 +49,7 @@ please email the PAUSE admins at modules@cpan.
If you want to give up all the modules in a distribution, visit
-
+
<%= $c->app->pause->config->action('remove_dist_primary')->{verb} %> page. Select one or more namespaces: Please tidy up your homedir: CPAN is getting larger every day which
is nice but usually there is no need to keep old an outdated version
of a module on several hundred mirrors. Please consider query(ACTION => "delete_files") %>">removing old versions of
+href="<%= my_url(ACTION => "delete_files") %>">removing old versions of
your module from PAUSE and CPAN. If you are worried that someone might
need an old version, it can always be found on the backpan
@@ -30,7 +30,7 @@ href="http://backpan.cpan.org/authors/id/<%= $pause->{userhome} %>/">backpan
href="<%= $pause->{usrdir} %>"><%= $pause->{usrdir} %>. If something's wrong, please
check the logfile of the daemon: see the tail of it with <%= $pause->{tailurl} %>. If you already know what's going wrong, you
-may wish to visit the ">repair
+may wish to visit the ">repair
tool for pending uploads. HTTP Upload: As
you do not seem to want HTTP upload enabled, we do
not offer it. If this is not what you want, try to
- 1) %>">explicitly enable HTTP upload.
-
-
$row->[0],
pause99_peek_perms_sub => 1,
]) %>"><%= $row->[0] %>
- $row->[1],
pause99_peek_perms_sub => 1,
@@ -72,7 +72,7 @@ can be corrected.--Thank you! multipart/form-data forms that support file
upload. In such a case, please retry to access this query(ACTION => "add_uri", CAN_MULTIPART => 0) %>">file-upload-disabled form.
+href="<%= my_url(ACTION => "add_uri", CAN_MULTIPART => 0) %>">file-upload-disabled form.
% } else {
% content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('perms', { valueNames: ['dist', 'owner', 'comaint'] diff --git a/lib/pause_2025/templates/user/distperms/remove_dist_comaint.html.ep b/lib/pause_2025/templates/user/distperms/remove_dist_comaint.html.ep index eb406ff69..355f92c08 100644 --- a/lib/pause_2025/templates/user/distperms/remove_dist_comaint.html.ep +++ b/lib/pause_2025/templates/user/distperms/remove_dist_comaint.html.ep @@ -66,7 +66,7 @@ Remove Comaintainers per module page. value="Remove"> % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('dists', { valueNames: ['dist', 'userid'] diff --git a/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep b/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep index 582b04862..67254d0f7 100644 --- a/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep +++ b/lib/pause_2025/templates/user/distperms/remove_dist_primary.html.ep @@ -82,7 +82,7 @@ a distribution for whatever reasons), visit value="Give Up Maintainership Status"> % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('dists', { valueNames: ['dist', 'owners'] diff --git a/lib/pause_2025/templates/user/files/delete.html.ep b/lib/pause_2025/templates/user/files/delete.html.ep index 6bd26d07d..d53a8354c 100644 --- a/lib/pause_2025/templates/user/files/delete.html.ep +++ b/lib/pause_2025/templates/user/files/delete.html.ep @@ -45,7 +45,7 @@ % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('files', { valueNames: ['file', 'size', 'modified'] diff --git a/lib/pause_2025/templates/user/files/show.html.ep b/lib/pause_2025/templates/user/files/show.html.ep index 9bab21d54..de31614d7 100644 --- a/lib/pause_2025/templates/user/files/show.html.ep +++ b/lib/pause_2025/templates/user/files/show.html.ep @@ -30,7 +30,7 @@ % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('files', { valueNames: ['file', 'size', 'modified'] diff --git a/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep index 88cd9549b..b82d0590e 100644 --- a/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep +++ b/lib/pause_2025/templates/user/perms/giveup_comaint.html.ep @@ -62,7 +62,7 @@ Give up Co-maintainership status per distribution page. value="Give Up">
% content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('perms', { valueNames: ['module', 'userid', 'type', 'owner'] diff --git a/lib/pause_2025/templates/user/perms/remove_comaint.html.ep b/lib/pause_2025/templates/user/perms/remove_comaint.html.ep index df8cbb91b..19a3c4f16 100644 --- a/lib/pause_2025/templates/user/perms/remove_comaint.html.ep +++ b/lib/pause_2025/templates/user/perms/remove_comaint.html.ep @@ -67,7 +67,7 @@ Remove Comaintainers per distribution page. value="Remove"> % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('packages', { valueNames: ['pacakge', 'dist', 'userid'] diff --git a/lib/pause_2025/templates/user/perms/remove_primary.html.ep b/lib/pause_2025/templates/user/perms/remove_primary.html.ep index 04ded54b8..e36a79fcb 100644 --- a/lib/pause_2025/templates/user/perms/remove_primary.html.ep +++ b/lib/pause_2025/templates/user/perms/remove_primary.html.ep @@ -78,7 +78,7 @@ please email the PAUSE admins at modules@cpan. value="Give Up Maintainership Status"> % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('packages', { valueNames: ['package', 'dist'] diff --git a/lib/pause_2025/templates/user/reindex.html.ep b/lib/pause_2025/templates/user/reindex.html.ep index be2863966..31befcf4f 100644 --- a/lib/pause_2025/templates/user/reindex.html.ep +++ b/lib/pause_2025/templates/user/reindex.html.ep @@ -45,7 +45,7 @@ % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('files', { valueNames: ['file'] diff --git a/lib/pause_2025/templates/user/reset_version.html.ep b/lib/pause_2025/templates/user/reset_version.html.ep index 0c649fe5c..32faca254 100644 --- a/lib/pause_2025/templates/user/reset_version.html.ep +++ b/lib/pause_2025/templates/user/reset_version.html.ep @@ -58,7 +58,7 @@ there was a problem. % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var List = new List('packages', { valueNames: ['package', 'version', 'dist'] diff --git a/lib/pause_2025/templates/user/tail_logfile.html.ep b/lib/pause_2025/templates/user/tail_logfile.html.ep index b7a469320..84bdab3bd 100644 --- a/lib/pause_2025/templates/user/tail_logfile.html.ep +++ b/lib/pause_2025/templates/user/tail_logfile.html.ep @@ -14,7 +14,7 @@ % content_for javascript => begin -%= javascript "/list.min.js" +%= javascript "/pause/list.min.js" %= javascript begin var logList = new List('logs', {valueNames: ['log']}); % end From 73fce8e609e88f04d54e89e3e451a6a691ca4417 Mon Sep 17 00:00:00 2001 From: Kenichi Ishigaki
Hi <%= $user %>, you see the site now but it is closed for maintainance.
From 15d0876da465df3b9ca3aa2e6ecdc3f1960464ae Mon Sep 17 00:00:00 2001
From: Kenichi Ishigaki Scan the QR code and submit 6-digit code to enable Multifactor Authentication. If you really need to disable multifactor authentication, please look at your authenticator and submit 6-digit code shown there (or one of the recovery codes you have never used before). CODE: <%= text_field "pause99_mfa_code" => '',
+ size => 10,
+ maxlength => 10,
+ autocomplete => 'off',
+%>
+ Query the Registered admins: <%= join ", ", @{$pause->{admins} || []} %> Query the Select the option and fill in a module name or
user ID as appropriate. The answer is all modules that an
user ID is registered for or all user IDs registered for a
module, as appropriate.You have already enabled multifactor authentication.
+% } else {
+Enable Multifactor Authentication for <%= $pause->{HiddenUser}{userid} %>
+% if (exists $pause->{UserGroups}{admin}) {
+ (lastvisit <%= $pause->{HiddenUser}{lastvisit} || "before 2005-12-02" %>)
+% }
+
+% }
+
+% if (my $error = $pause->{error}) {
+
+
+% }
+% if (!$pause->{HiddenUser}{mfa}) {
+
+
grouptable table for who is an admin bit holder<%= $file %>
% }
<%= $files->{$file}{stat} %>
- <%= $files->{$file}{blurb} %>
+ <%= $files->{$file}{blurb} %>
% }
@@ -33,7 +33,7 @@
%= javascript "/pause/list.min.js"
%= javascript begin
var List = new List('files', {
- valueNames: ['file', 'size', 'modified']
+ valueNames: ['file', 'size', { name: 'modified', attr: 'data-modified' }]
});
% end
% end
diff --git a/lib/pause_2025/templates/user/perms/peek.html.ep b/lib/pause_2025/templates/user/perms/peek.html.ep
index e72d0c28d..6e169b00e 100644
--- a/lib/pause_2025/templates/user/perms/peek.html.ep
+++ b/lib/pause_2025/templates/user/perms/peek.html.ep
@@ -1,8 +1,7 @@
% layout 'layout';
% my $pause = stash(".pause") || {};
-perms table by author or by
-module. Select the option and fill in a module name or
+
If you have are unsure about what to do, or have any questions, -please email the PAUSE admins at modules@cpan.org. +please email the PAUSE admins at modules@perl.org.
If you want to give up all the modules in a distribution, visit diff --git a/lib/pause_2025/templates/user/uri/add.html.ep b/lib/pause_2025/templates/user/uri/add.html.ep index 1e484ad13..d2d59be85 100644 --- a/lib/pause_2025/templates/user/uri/add.html.ep +++ b/lib/pause_2025/templates/user/uri/add.html.ep @@ -56,8 +56,7 @@ you do not seem to want HTTP upload enabled, we do % }
GET URL: PAUSE fetches any http or ftp -URL that can be handled by LWP (Note: https is currently not -supported): use the text field (please specify the complete +URL that can be handled by LWP: use the text field (please specify the complete URL).
Please, make sure your filename @@ -127,7 +126,7 @@ to your homedirectory:
<%= text_field "pause99_add_uri_uri", size => 64, - maxlength => 128, + maxlength => 255, %>