Commit b1c0a457 authored by IKEDA Soji's avatar IKEDA Soji
Browse files

Refactoring and changing. Introduced dump_user() and suck_user().

They dump or restore list users (members, owners or editors) in database from/to file.

[change] File for members was changed to `<list dir>/member.dump` which will replace:
  - `subscribers` (used by Sympa prior to 5.4a.1)
  - `subscribers.db.dump` (generated by dump command)
  - `subscribers.closed.dump` (backup for closed list)
[change] Additional files to dump owner or editor: `<list dir>/owner.dump`, `<list dir>/editor.dump`.
They will be used as initial (permanent) owners/editors, or as backup for closed list
[*change] owner and editor parameters in list config file are no longer sync with actual owners/editors.  They are used only at the time of installation of list.
parent f369898f
......@@ -15918,8 +15918,8 @@ sub do_dump {
return undef;
}
 
$list->dump();
$param->{'file'} = $list->{'dir'} . '/subscribers.db.dump';
$list->dump_user('member');
$param->{'file'} = $list->{'dir'} . '/member.dump';
 
if ($in{'format'} eq 'light') {
unless (open(DUMP, $param->{'file'})) {
......@@ -15948,7 +15948,7 @@ sub do_dump {
}
close LIGHTDUMP;
close DUMP;
$param->{'file'} = "$list->{'dir'}/subscribers.db.dump.light";
$param->{'file'} = $list->{'dir'} . '/member.dump.light';
 
} else {
$param->{'file'} = "$list->{'dir'}/select.dump";
......
......@@ -142,6 +142,12 @@ Delete the indicated users from the list.
=item delete_list_admin ( ROLE, ARRAY )
Delete the indicated admin user with the predefined role from the list.
ROLE may be C<'owner'> or C<'editor'>.
=item dump_user ( ROLE )
Dump user information in user store into file C<I<$role>.dump> under
list directory. ROLE may be C<'member'>, C<'owner'> or C<'editor'>.
=item get_cookie ()
......@@ -198,6 +204,11 @@ the list.
OBSOLETED.
Use get_admins().
=item suck_user ( ROLE )
Import user information into user store from file C<I<$role>.dump> under
list directory. ROLE may be C<'member'>, C<'owner'> or C<'editor'>.
=item update_list_member ( $email, key =E<gt> value, ... )
I<Instance method>.
......@@ -695,24 +706,61 @@ sub _cache_put {
# Moved to: Sympa::Spindle::DistributeMessage::_extract_verp_rcpt().
#sub _extract_verp_rcpt;
## Dumps a copy of lists to disk, in text format
sub dump {
# Dumps a copy of list users to disk, in text format.
# Old name: Sympa::List::dump() which dumped only members.
sub dump_user {
$log->syslog('debug2', '(%s, %s)', @_);
my $self = shift;
$log->syslog('debug2', '(%s)', $self->{'name'});
my $role = shift;
unless (defined $self) {
$log->syslog('err', 'Unknown list');
return undef;
}
die 'bug in logic. Ask developer'
unless grep {$role eq $_} qw(member owner editor);
my $user_file_name = "$self->{'dir'}/subscribers.db.dump";
my $file = $self->{'dir'} . '/' . $role . '.dump';
unless ($self->_save_list_members_file($user_file_name)) {
$log->syslog('err', 'Failed to save file %s', $user_file_name);
unlink $file . '.old' if -e $file . '.old';
rename $file, $file . '.old' if -e $file;
my $lock_fh = Sympa::LockedFile->new($file, 5, '>');
unless ($lock_fh) {
$log->syslog('err', 'Failed to save file %s.new: %s', $file,
Sympa::LockedFile->last_error);
return undef;
}
# Note: "subscribers" file was deprecated. No need to load "stats" file.
if ($role eq 'member') {
my $user;
for (
$user = $self->get_first_list_member();
$user;
$user = $self->get_next_list_member()
) {
foreach my $k (
qw(date update_date email gecos
reception visibility)) {
printf $lock_fh "%s %s\n", $k, $user->{$k}
if defined $user->{$k} and length $user->{$k};
}
print $lock_fh "\n";
}
} else {
foreach my $user (@{$self->_get_admins || []}) {
next unless $user->{role} eq $role;
foreach my $k (
qw(date update_date email gecos profile
reception visibility info
subscribed included id)
) {
printf $lock_fh "%s %s\n", $k, $user->{$k}
if defined $user->{$k} and length $user->{$k};
}
print $lock_fh "\n";
}
}
$lock_fh->close;
# FIXME:Are these lines required?
$self->{'_mtime'}{'config'} =
Sympa::Tools::File::get_mtime($self->{'dir'} . '/config');
......@@ -4319,37 +4367,96 @@ sub load_data_sources_list {
# No longer used.
#sub _load_stats_file;
## Loads the list of subscribers.
sub _load_list_members_file {
my $file = shift;
$log->syslog('debug2', '(%s)', $file);
## Loads the list of users.
# Old name:: Sympa::List::_load_list_members_file($file) which loaded members.
sub suck_user {
$log->syslog('debug2', '(%s, %s)', @_);
my $self = shift;
my $role = shift;
## Open the file and switch to paragraph mode.
open(L, $file) || return undef;
die 'bug in logic. Ask developer'
unless grep {$role eq $_} qw(member owner editor);
## Process the lines
local $RS;
my $data = <L>;
my $file = $self->{'dir'} . '/' . $role . '.dump';
my @users;
foreach (split /\n\n/, $data) {
my (%user, $email);
$user{'email'} = $email = $1 if (/^\s*email\s+(.+)\s*$/om);
$user{'gecos'} = $1 if (/^\s*gecos\s+(.+)\s*$/om);
$user{'date'} = $1 if (/^\s*date\s+(\d+)\s*$/om);
$user{'update_date'} = $1 if (/^\s*update_date\s+(\d+)\s*$/om);
$user{'reception'} = $1
if (
/^\s*reception\s+(digest|nomail|summary|notice|txt|html|urlize|not_me)\s*$/om
);
$user{'visibility'} = $1
if (/^\s*visibility\s+(conceal|noconceal)\s*$/om);
# Open the file and switch to paragraph mode.
my $lock_fh = Sympa::LockedFile->new($file, 5, '<') or return;
local $RS = '';
my $user;
while (my $para = <$lock_fh>) {
if ($role eq 'member') {
$user = {
map {
#FIMXE:Define appropriate schema.
if (/^\s*email\s+(.+)\s*$/) {
(email => $1);
} elsif (/^\s*gecos\s+(.+)\s*$/) {
(gecos => $1);
} elsif (/^\s*date\s+(\d+)\s*$/) {
(date => $1);
} elsif (/^\s*update_date\s+(\d+)\s*$/) {
(update_date => $1);
} elsif (
/^\s*reception\s+(digest|nomail|summary|notice|txt|html|urlize|not_me)\s*$/
) {
(reception => $1);
} elsif (/^\s*visibility\s+(conceal|noconceal)\s*$/) {
(visibility => $1);
} else {
();
}
} split /\n/, $para
};
} else {
my $r;
$user = {
map {
#FIMXE:Define appropriate schema.
if (/^\s*role\s+(owner|editor)\s*$/) {
$r = $1;
();
} elsif (/^\s*email\s+(.+)\s*$/) {
(email => $1);
} elsif (/^\s*gecos\s+(.+)\s*$/) {
(gecos => $1);
} elsif (/^\s*profile\s+(normal|privileged)\s*$/) {
(gecos => $1);
} elsif (/^\s*date\s+(\d+)\s*$/) {
(date => $1);
} elsif (/^\s*update_date\s+(\d+)\s*$/) {
(update_date => $1);
} elsif (
/^\s*reception\s+(mail|nomail)\s*$/
) {
(reception => $1);
} elsif (/^\s*visibility\s+(conceal|noconceal)\s*$/) {
(visibility => $1);
} elsif (/^\s*info\s+(.+)\s*$/) {
(info => $1);
} elsif (/^\s*subscribed\s+(\S+)\s*$/) {
(subscribed => !!$1);
} elsif (/^\s*included\s+(\S+)*$/) {
(included => !!$1);
} elsif (/^\s*id\s+(.+)*$/) {
(id => $1);
} else {
();
}
} split /\n/, $para
};
next unless $r and $r eq $role;
}
next unless %$user;
push @users, \%user;
if ($role eq 'member') {
$self->add_list_member($user);
} else {
$self->add_list_admin($role, $user);
}
}
close(L);
$lock_fh->close;
return @users;
}
## include a remote sympa list as subscribers.
......
......@@ -154,9 +154,10 @@ sub _close {
Conf::get_robot_conf($list->{'domain'}, 'alias_manager'));
$aliases->del($list) if $aliases;
# Dump subscribers.
$list->_save_list_members_file(
$list->{'dir'} . '/subscribers.closed.dump');
# Dump users.
$list->dump_user('member');
$list->dump_user('owner');
$list->dump_user('editor');
## Delete users
my @users;
......
......@@ -221,8 +221,9 @@ sub _move {
my $aliases = Sympa::Aliases->new(
Conf::get_robot_conf($current_list->{'domain'}, 'alias_manager'));
$aliases->del($current_list) if $aliases;
$current_list->_save_list_members_file(
$current_list->{'dir'} . '/subscribers.closed.dump');
$current_list->dump_user('member');
$current_list->dump_user('owner');
$current_list->dump_user('editor');
# Set list status to pending if creation list is moderated.
# Save config file for the new() later to reload it.
......
......@@ -25,6 +25,7 @@ package Sympa::Request::Handler::open_list;
use strict;
use warnings;
use English qw(-no_match_vars);
use File::Path qw();
use Sympa;
......@@ -80,16 +81,32 @@ sub _twist {
return undef;
}
unless (-f $list->{'dir'} . '/subscribers.closed.dump') {
$log->syslog('notice', 'No subscribers to restore');
# Dump initial permanent owners/editors in config file.
if ($mode eq 'install') {
my ($fh, $fh_config);
foreach my $role (qw(owner editor)) {
my $file = $list->{'dir'} . '/' . $role . '.dump';
my $config = $list->{'dir'} . '/config';
if ( !-e $file
and open($fh, '>', $file)
and open($fh_config, '<', $config)) {
local $RS = ''; # read paragraph by each
my $admins = join '', grep {/\A\s*$role\b/} <$fh_config>;
print $fh $admins;
close $fh;
close $fh_config;
}
}
}
# Load permanent users.
$list->suck_user('member');
$list->suck_user('owner');
$list->suck_user('editor');
# Load initial transitional owners/editors from external data sources.
if ($mode eq 'install') {
$list->sync_include_admin;
}
my @users = Sympa::List::_load_list_members_file(
$list->{'dir'} . '/subscribers.closed.dump');
# Insert users in database.
$list->add_list_member(@users);
# Restore admins
$list->sync_include_admin;
# Install new aliases.
my $aliases = Sympa::Aliases->new(
......
......@@ -746,30 +746,29 @@ sub upgrade {
$list->{'name'}
);
my @users = Sympa::List::_load_list_members_file(
"$list->{'dir'}/subscribers");
$list->{'admin'}{'user_data_source'} = 'include2';
# Load <list dir>/subscribers to the DB
if (-e $list->{'dir'} . '/subscribers'
and rename $list->{'dir'} . '/subscribers',
$list->{'dir'} . '/member.dump'
) {
$list->suck_user('member');
## Add users to the DB
$list->add_list_member(@users);
my $total = $list->{'add_outcome'}{'added_members'};
if (defined $list->{'add_outcome'}{'errors'}) {
$log->syslog(
'err',
'Failed to add users: %s',
$list->{'add_outcome'}{'errors'}{'error_message'}
);
my $total = $list->{'add_outcome'}{'added_members'};
if (defined $list->{'add_outcome'}{'errors'}) {
$log->syslog('err', 'Failed to add users: %s',
$list->{'add_outcome'}{'errors'}
{'error_message'});
}
$log->syslog('notice',
'%d subscribers have been loaded into the database',
$total);
}
$log->syslog('notice',
'%d subscribers have been loaded into the database',
$total);
$list->{'admin'}{'user_data_source'} = 'include2';
unless ($list->save_config('automatic')) {
$log->syslog('err',
'Failed to save config file for list %s',
$list->{'name'});
'Failed to save config file for list %s', $list);
}
} elsif ($list->{'admin'}{'user_data_source'} eq 'database') {
......@@ -943,7 +942,7 @@ sub upgrade {
'task_manager_pidfile' => 'No more used',
'archived_pidfile' => 'No more used',
'bounced_pidfile' => 'No more used',
'use_fast_cgi' => 'No longer used', # 6.2.25b deprecated
'use_fast_cgi' => 'No longer used', # 6.2.25b deprecated
);
## Set language of new file content
......@@ -1773,6 +1772,38 @@ sub upgrade {
_get_canonical_read_date($sdm, 'update_admin')
)
);
$log->syslog('notice', 'Upgrading user dumps of closed lists.');
# Upgrading user dumps of closed lists.
my $lists =
Sympa::List::get_lists('*',
filter => [status => 'closed|family_closed']);
foreach my $list (@{$lists || []}) {
my $dir = $list->{'dir'};
if (-e $dir . '/subscribers.closed.dump') {
unlink $dir . '/member.dump.old';
rename $dir . '/member.dump', $dir . '/member.dump.old';
rename $dir . '/subscribers.closed.dump',
$dir . '/member.dump';
}
my ($fh, $fh_config);
foreach my $role (qw(owner editor)) {
my $file = $list->{'dir'} . '/' . $role . '.dump';
my $config = $list->{'dir'} . '/config';
if (!-e $file
and open($fh, '>', $file)
and open($fh_config, '<', $config)) {
local $RS = ''; # read paragraph by each
my $admins = join '', grep {/\A\s*$role\b/} <$fh_config>;
print $fh $admins;
close $fh;
close $fh_config;
}
}
}
}
# GH Issue #240: PostgreSQL: Unable to edit owners/subscribers.
......
......@@ -305,7 +305,7 @@ if ($main::options{'dump'}) {
}
foreach my $list (@$all_lists) {
unless ($list->dump()) {
unless ($list->dump_user('member')) {
print STDERR "Could not dump list(s)\n";
}
}
......@@ -1154,7 +1154,10 @@ Create a list with the XML file under robot robot_name.
=item C<--dump=>I<list>@I<dom>|C<ALL>
Dumps subscribers of for `listname' list or all lists. Subscribers are
dumped in subscribers.db.dump.
dumped in F<member.dump>.
Note: On Sympa prior to 6.2.25b.2, subscribers were dumped in
F<subscribers.db.dump>.
=begin comment
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment