suppress success message if no subscriber was added

gettext support is now optional - otherwise only english is delivered
fixed GPG_SUPPORT: a missing module does not break the code anymore
subscription log added
moved gettext dependency from code to packaging - the perl gettext support in sarge seems to be broken
This commit is contained in:
lars 2007-03-28 19:08:48 +00:00
parent 9581084a29
commit e6438d0777
12 changed files with 403 additions and 164 deletions

4
README
View File

@ -173,8 +173,8 @@ them in any future releases of ezmlm-web.
VI. Encrypted mailing lists
===========================
If you want to manage encrypted mailing lists (see
http://www.synacklabs.net/projects/crypt-ml/) with ezmlm-web, then you should
read README.gnupg and follow the instructions.
https://systemausfall.org/toolforge/gpgpy-ezmlm) with ezmlm-web, then you
should read README.gnupg and follow the instructions.
VII. Bugs && Bug Reports

View File

@ -4,7 +4,7 @@ $Id$
Content:
1) Requirements
2) Installation of gpg-ezmlm
2) Installation of gpgpy-ezmlm
3) Setup of ezmlm-web
4) Notes

20
TODO
View File

@ -1,27 +1,11 @@
urgent:
- add basic/normal/expert to web interface
- export subscribers
- fix display of language name
clearsilver-Infos updaten
die nicht-Abonnenten-Listen (allow/deny/mod) sagen trotzdem: "x AbonnentInnen"
add basic/normal/expert to web interface
multi-domain-Support?
charset support for idx <= 5.0
suppress success message, if no new subscriber addresses were selected for adding
Maximale Anzahl von Mailinglisten definieren?
- wird wohl von qmailadmin in der .qmailadmin-limits-Datei festgelegt
Infos fuer Massen-Hosting:
http://www.fbis.ch/index-de.php?page=14&frameset=4
restore user input after failed list_create (especially options)
support for:
* show subscription log
* 'mailinglist' (maybe)
support for 'mailinglist' (maybe)

View File

@ -7,8 +7,8 @@ Version 3.2 - 04/14/02006
* script for creating binary suid wrappers added
* bug in MySQL support fixed
* treatment of the special character "dot" in listname and list address fixed
* use gettext for translations (Locale::gettext is now required)
* the formerly required module "Encode" is now optional
* support for listing of subscription log
Version 3.1.4 - 03/26/02006
* new setting: DOTQMAIL_DIR (useful for multi domain vpopmail setups)

View File

@ -29,7 +29,7 @@ $WEBUSERS_FILE = "$LIST_DIR/webusers";
$TEMPLATE_DIR = "/usr/local/share/ezmlm-web/template";
# Safe list deletion?
# 0 = move list to $LIST_DIR/_deleted_lists -> recoverable :)
# 0 = move List to $LIST_DIR/_deleted_lists -> recoverable :)
# 1 = allow user to delete list completely. No backup, therefore no recovery.
$UNSAFE_RM = 0;
@ -62,13 +62,10 @@ $HTML_TITLE = "ezmlm-web - a mailinglists' administration interface";
# this is a URL - you have to copy the css file to the right location before
$HTML_CSS_FILE = "/ezmlm-web.css";
# choose a language (en|de)
# the default interface language
# can only be changed via the web interface if gettext support is available
$HTML_LANGUAGE = "en";
# list of available languages - this should include the default language above
# example: LANGUAGE_LIST = ( "en", "de", "si", "jp" );
@LANGUAGE_LIST = ( "en", "de" );
# turn support for encrypted mailing lists on or off - defaults to 0 (off)
# see https://systemausfall.org/toolforge/gpgpy-ezmlm for details
$GPG_SUPPORT = 0;

View File

@ -22,6 +22,14 @@ use CGI;
use IO::File;
use POSIX;
use English;
use Time::localtime;
# gettext support is optional
my $GETTEXT_SUPPORT = 1;
unless (&safely_import_module("Locale::gettext")) {
$GETTEXT_SUPPORT = 0;
warn "Gettext support is not available - the multilingual web interface is not available!";
}
# the Encode module is optional - we do not break if it is absent
my $ENCODE_SUPPORT = 1;
@ -125,10 +133,11 @@ do $config_file;
# do we support encrypted mailing lists?
# see https://systemausfall.org/toolforge/crypto-ezmlm
$GPG_SUPPORT = 0 unless defined($GPG_SUPPORT);
if (defined($GPG_SUPPORT) && ($GPG_SUPPORT)) {
if ($GPG_SUPPORT) {
if (&safely_import_module("Mail::Ezmlm::Gpg")) {
$GPG_SUPPORT = 1;
} else {
$GPG_SUPPORT = 0;
warn "WARNING: Support for encrypted mailinglists is disabled, as the module Mail::Ezmlm::Gpg failed to load!";
}
}
@ -173,15 +182,14 @@ if (defined($MAIL_DOMAIN) && ($MAIL_DOMAIN ne '')) {
my $pagedata = &init_hdf();
my $action = $q->param('action');
# check permissions
unless (&check_permission_for_action) {
$pagename = 'list_select';
$error = 'Forbidden';
}
# This is where we decide what to do, depending on the form state and the
# users chosen course of action ...
# TODO: unify all these "is list param set?" checks ...
elsif ($action eq '' || $action eq 'list_select') {
# check permissions
unless (&check_permission_for_action()) {
$pagename = 'list_select';
$error = 'Forbidden';
} elsif ($action eq '' || $action eq 'list_select') {
# Default action. Present a list of available lists to the user ...
$pagename = 'list_select';
} elsif ($action eq 'show_page') {
@ -217,6 +225,24 @@ elsif ($action eq '' || $action eq 'list_select') {
$error = 'ParameterMissing';
$pagename = 'list_select';
}
} elsif ($action eq 'download_subscribers') {
# requesting a text file of all subscribers
if (defined($q->param('list'))) {
&download_subscribers();
# just in case we return (something bad happened)
$pagename = 'subscribers';
} else {
$pagename = 'list_select';
$error = 'ParameterMissing';
}
} elsif ($action eq 'subscribe_log') {
if (defined($q->param('list'))) {
&set_pagedata_subscription_log($q->param('list'));
$pagename = 'show_subscription_log';
} else {
$pagename = 'list_select';
$error = 'ParameterMissing';
}
} elsif ($action eq 'list_delete_ask') {
# Confirm list removal
if (defined($q->param('list'))) {
@ -332,7 +358,7 @@ elsif ($action eq '' || $action eq 'list_select') {
exit 0;
} else {
$warning = 'GnupgExportKey';
# TODO: pagename is quite random here ...
# pagename is quite random here ...
$pagename = 'gnupg_public';
}
} else {
@ -419,7 +445,9 @@ elsif ($action eq '' || $action eq 'list_select') {
# set default action, if there is no list available and the user is
# allowed to create a new one
if (($action eq '') && (&webauth_create_allowed()) && ($pagedata->getValue('Data.Lists.0','') eq '')) {
if (((!defined($action)) || ($action eq ''))
&& (&webauth_create_allowed())
&& ($pagedata->getValue('Data.Lists.0','') eq '')) {
$pagename = 'list_create';
}
@ -432,6 +460,7 @@ exit;
sub init_hdf {
# initialize the data for clearsilver
my $hdf = ClearSilver::HDF->new();
&fatal_error("Template dir ($TEMPLATE_DIR) not found!") unless (-e $TEMPLATE_DIR);
@ -451,7 +480,7 @@ sub init_hdf {
$hdf = &load_interface_language($hdf);
$hdf->setValue("ScriptName", $ENV{'SCRIPT_NAME'});
$hdf->setValue("ScriptName", $ENV{SCRIPT_NAME}) if (defined($ENV{SCRIPT_NAME}));
$hdf->setValue("Stylesheet", "$HTML_CSS_FILE");
$hdf->setValue("Config.PageTitle", "$HTML_TITLE");
@ -543,29 +572,35 @@ sub translate_language_data {
my ($hdf, $language) = @_;
my $langdata;
my %translation;
my %language_strings;
my $key;
# create gettext object
# TODO: getttext support seems to be broken???
# TODO: provide an alternative, if no gettext is available
#&setlocale(POSIX::LC_MESSAGES, $language);
&textdomain("ezmlm-web");
warn "failed to set locale: $@" unless (&setlocale(LC_ALL, ''));
# "setlocale" seems to need "de_DE" instead of just "de" - so we will
# use the environment setting instead
# see http://lists.debian.org/debian-perl/2000/01/msg00016.html
# beware that no other programs are called afterwards: their output may suffer :)
$ENV{LC_ALL} = "$language";
# read language template
$langdata = ClearSilver::HDF->new();
$langdata->readFile("$TEMPLATE_DIR/language.hdf");
# translate all strings
# parse all strings
my $subtree = $langdata->getObj("Lang");
%translation = &recurse_hdf($subtree, "Lang");
foreach $key (keys %translation) {
$hdf->setValue($key, gettext($translation{$key}))
%language_strings = &recurse_hdf($subtree, "Lang");
if ($GETTEXT_SUPPORT) {
# create gettext object
&textdomain("ezmlm-web");
warn "failed to set locale: $@" unless (&setlocale(LC_MESSAGES, ''));
# "setlocale" seems to need "de_DE" instead of just "de" - so we will
# use the environment setting instead
# see http://lists.debian.org/debian-perl/2000/01/msg00016.html
# avoid calling other programs later: their output may suffer :)
$ENV{LC_ALL} = "$language";
# translate every string
foreach $key (keys %language_strings) {
$hdf->setValue($key, &gettext($language_strings{$key}))
}
} else {
# just copy all strings
foreach $key (keys %language_strings) {
$hdf->setValue($key, $language_strings{$key})
}
}
}
@ -578,7 +613,6 @@ sub recurse_hdf {
$value = $node->objValue();
if ($value) {
#print "Prefix: " . $prefix . " / " . $value . "\n";
#TODO: check if this works on the same single object - no tests up to now
$result{$prefix} = $value;
}
$next = $node->objChild();
@ -594,20 +628,19 @@ sub recurse_hdf {
# ---------------------------------------------------------------------------
# look for preferred browser language setting
# this code was adapted from Per Cederberg
# http://www.percederberg.net/home/perl/select.perl
# it returns an empty string, if no supported language was found
sub get_browser_language {
# look for preferred browser language setting
# this code was adapted from Per Cederberg
# http://www.percederberg.net/home/perl/select.perl
# it returns an empty string, if no supported language was found
my ($str, @langs, @res);
# Use language preference settings
if ($ENV{'HTTP_ACCEPT_LANGUAGE'} ne '')
{
@langs = split(/,/, $ENV{'HTTP_ACCEPT_LANGUAGE'});
foreach (@langs)
{
if (defined($ENV{HTTP_ACCEPT_LANGUAGE})
&& ($ENV{HTTP_ACCEPT_LANGUAGE} ne '')) {
@langs = split(/,/, $ENV{HTTP_ACCEPT_LANGUAGE});
foreach (@langs) {
# get the first part of the language setting
($str) = ($_ =~ m/([a-z]+)/);
# check, if it is available
@ -698,7 +731,7 @@ sub set_pagedata {
$pagedata->setValue("Data.WebUser.UserName", $ENV{'REMOTE_USER'}||'ALL');
# list specific configuration - use defaults if no list is selected
if ($q->param('list') ne '' ) {
if (defined($q->param('list')) && ($q->param('list') ne '')) {
&set_pagedata4list(&get_list_part());
} else {
&set_pagedata4options($DEFAULT_OPTIONS);
@ -744,8 +777,9 @@ sub set_pagedata4list {
# ---------------------------------------------------------------------------
# extract hdf-data for encrypted lists
sub set_pagedata_crypto {
# extract hdf-data for encrypted lists
my ($listname) = @_;
my ($gpg_list, %config, $item, @gpg_keys, $gpg_key);
@ -829,8 +863,8 @@ sub set_pagedata_subscribers {
# ---------------------------------------------------------------------------
# set the names of the textfiles of this list
sub set_pagedata_textfiles {
# set the names of the textfiles of this list
my $list = shift;
my ($i, @files, $item);
@ -925,9 +959,6 @@ sub set_pagedata4options {
my($options) = shift;
my($i, $list, $key, $state, $value, $dir_of_list);
$dir_of_list = $LIST_DIR . '/' . $q->param('list');
$list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list'));
$i = 0;
$key = lc(substr($options,$i,1));
# parse the first part of the options string
@ -939,6 +970,43 @@ sub set_pagedata4options {
$key = lc(substr($options,$i,1));
}
# scan the config for settings
for ($i=0; $i<=9; $i++) {
unless (($i eq 1) || ($i eq 2)) {
$state = ($options =~ /\s-$i (?:'(.+?)')/);
unless ($state) {
# set default values
if ($i eq 0) {
$value = 'mainlist@' . $DEFAULT_HOST;
} elsif ($i eq 3) {
$value = 'from_address@domain.org';
} elsif ($i eq 4) {
$value = '-t24 -m30 -k64';
} elsif ($i eq 5) {
$value = 'owner_address@domain.org';
} elsif ($i eq 6) {
$value = 'host:port:user:password:database:table';
} elsif (($i >= 7) && ($i <= 9)) {
if (defined($q->param('list'))) {
$value = $LIST_DIR . '/' . $q->param('list') . "/mod";
} else {
$value = "mod";
}
}
} else {
# use the configured value (extracted by the pattern matching for 'state')
$value = $1;
}
$pagedata->setValue("Data.List.Settings." . $i . ".value", $value);
$pagedata->setValue("Data.List.Settings." . $i . ".state", $state ? 1 : 0);
}
}
# the list dependent stuff follows - we can stop if no list is selected
return unless (defined($q->param('list')));
$dir_of_list = $LIST_DIR . '/' . $q->param('list');
$list = new Mail::Ezmlm($dir_of_list);
# the options "tpxmsr" are used to create a default value
# if they are unset, the next ezmlm-make will remove the appropriate files
# but: these files are used, if they exist - regardless of the flag
@ -959,48 +1027,72 @@ sub set_pagedata4options {
if (-e "$dir_of_list/modsub");
$pagedata->setValue("Data.List.Options.r" , 1)
if (-e "$dir_of_list/remote");
for ($i=0; $i<=9; $i++) {
unless (($i eq 1) || ($i eq 2)) {
$state = ($options =~ /\s-$i (?:'(.+?)')/);
unless ($state) {
# set default values
if ($i eq 0) {
$value = 'mainlist@' . $DEFAULT_HOST;
} elsif ($i eq 3) {
$value = 'from_address@domain.org';
} elsif ($i eq 4) {
$value = '-t24 -m30 -k64';
} elsif ($i eq 5) {
$value = 'owner_address@domain.org';
} elsif ($i eq 6) {
$value = 'host:port:user:password:database:table';
} elsif (($i >= 7) && ($i <= 9)) {
$value = "$dir_of_list/mod";
}
} else {
# use the configured value (extracted by the pattern matching for 'state')
$value = $1;
}
$pagedata->setValue("Data.List.Settings." . $i . ".value", $value);
$pagedata->setValue("Data.List.Settings." . $i . ".state", $state ? 1 : 0);
}
}
}
# ---------------------------------------------------------------------------
sub check_filename()
{
sub download_subscribers {
# return a list of subscribers of a list for download
my ($list, $listname, $filename, $part_type);
my (%pretty, $address, $address_name, @subscribers);
$listname = $q->param('list');
$list = new Mail::Ezmlm("$LIST_DIR/$listname");
if (defined($q->param('part'))) {
$part_type = $q->param('part');
$filename = "mailinglist-$listname-$part_type.txt";
} else {
$filename = "mailinglist-$listname-subscribers.txt";
}
tie %pretty, "DB_File", "$LIST_DIR/$listname/webnames" if ($PRETTY_NAMES);
foreach $address (sort $list->subscribers($part_type)) {
if ($address ne '') {
if ($PRETTY_NAMES) {
$address_name = $pretty{$address};
if ($address_name eq '') {
push @subscribers, $address;
} else {
push @subscribers, "$address_name <$address>";
}
} else {
push @subscribers, $address;
}
}
}
untie %pretty if ($PRETTY_NAMES);
if ($#subscribers lt 0) {
$warning = 'EmptyList';
return (1==0);
}
print "Content-Type: text/plain\n";
# suggest a download filename
# (taken from http://www.bewley.net/perl/download.pl)
print "Content-Disposition: attachment; filename=$filename\n";
print "Content-Description: exported subscribers list of $listname\n\n";
foreach $address (@subscribers) {
print "$address\r\n";
}
exit;
}
# ---------------------------------------------------------------------------
sub check_filename {
my $filename = shift;
return ($filename =~ m/[^\w-]/) ? (1==0) : (0==0);
}
# ---------------------------------------------------------------------------
sub get_list_part
# return the name of the part list (deny, allow, mod, digest or '')
{
sub get_list_part {
# return the name of the part list (deny, allow, mod, digest or '')
$q->param('part') =~ m/^(allow|deny|digest|mod)$/;
return $1;
}
@ -1008,6 +1100,7 @@ sub get_list_part
# ---------------------------------------------------------------------------
sub is_list_encrypted {
my ($listname) = @_;
return (1==0) unless ($GPG_SUPPORT);
@ -1018,6 +1111,7 @@ sub is_list_encrypted {
# ---------------------------------------------------------------------------
sub get_dotqmail_files {
my ($list, @files, $qmail_prefix);
$list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list'));
@ -1062,6 +1156,44 @@ sub get_dotqmail_files {
# ---------------------------------------------------------------------------
sub set_pagedata_subscription_log {
my ($listname) = @_;
my ($log_file, @event, $i, $epoch_seconds, $note, $address);
$log_file = "$LIST_DIR/" . $q->param('list') . "/Log";
# break if there is no log_file
return unless (-e "$log_file");
unless (open LOG_FILE, "<$log_file") {
warn "Failed to open log file: $log_file";
$warning = 'LogFile';
return (1==0);
}
$i = 0;
while (<LOG_FILE>) {
chomp;
split;
@event = @_;
if ($#event eq 2) {
$epoch_seconds = $event[0];
my $datetext = ctime($epoch_seconds);
$note = $event[1];
$address = $event[2];
$pagedata->setValue("Data.List.SubscribeLog.$i.date", $datetext);
$pagedata->setValue("Data.List.SubscribeLog.$i.text", $note);
$pagedata->setValue("Data.List.SubscribeLog.$i.address", $address);
$i++;
}
}
close LOG_FILE;
}
# ---------------------------------------------------------------------------
sub delete_list {
# Delete a list ...
@ -1133,13 +1265,13 @@ sub delete_list {
}
# ------------------------------------------------------------------------
sub untaint {
$DEFAULT_HOST = $1 if $DEFAULT_HOST =~ /^([\w\d\.-]+)$/;
sub untaint {
# Go through all the CGI input and make sure it is not tainted. Log any
# tainted data that we come accross ... See the perlsec(1) man page ...
$DEFAULT_HOST = $1 if $DEFAULT_HOST =~ /^([\w\d\.-]+)$/;
my (@params, $i, $param);
@params = $q->param;
@ -1162,7 +1294,9 @@ sub untaint {
# special stuff
# check the list name
if (($q->param('list') =~ /[^\w\.-]/) && ($q->param('action') !~ /^list_create_(do|ask)$/)) {
if (defined($q->param('list')) &&
($q->param('list') =~ /[^\w\.-]/) &&
($q->param('action') !~ /^list_create_(do|ask)$/)) {
$warning = 'InvalidListName' if ($warning eq '');
$q->param(-name=>'list', -values=>'');
}
@ -1172,19 +1306,20 @@ sub untaint {
# ------------------------------------------------------------------------
sub check_permission_for_action {
# test if the user is allowed to modify the choosen list or to create an new one
# the user would still be allowed to fill out the create-form (however he got there),
# but the final creation is omitted
# test if the user is allowed to modify the choosen list or to create a
# new one the user would still be allowed to fill out the create-form
# (however he got there), but the final creation is omitted
my $ret;
if ($action eq 'list_create_ask' || $action eq 'list_create_do') {
$ret = &webauth_create_allowed();
} elsif (defined($q->param('list'))) {
$ret = &webauth($q->param('list'));
} else {
$ret = (0==0);
}
return $ret;
my $ret;
if (defined($action) &&
(($action eq 'list_create_ask' || $action eq 'list_create_do'))) {
$ret = &webauth_create_allowed();
} elsif (defined($q->param('list'))) {
$ret = &webauth($q->param('list'));
} else {
$ret = (0==0);
}
return $ret;
}
# ------------------------------------------------------------------------
@ -1192,11 +1327,12 @@ sub check_permission_for_action {
sub add_address {
# Add an address to a list ..
my ($address, $list, $part, @addresses, $fail_count);
my ($address, $list, $part, @addresses, $fail_count, $success_count);
$list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list'));
$part = &get_list_part();
$fail_count = 0;
$success_count = 0;
if (($q->param('mailaddressfile')) && ($FILE_UPLOAD)) {
# Sanity check
@ -1252,6 +1388,7 @@ sub add_address {
if (defined($add->name()) && $PRETTY_NAMES) {
$pretty{$add->address()} = $add->name();
}
$success_count++;
} else {
$fail_count++;
}
@ -1260,7 +1397,11 @@ sub add_address {
if ($fail_count gt 0) {
$warning = 'AddAddress';
return (1==0);
} elsif ($success_count eq 0) {
# no subscribers - we report an error without issuing a warning
return (1==0);
} else {
# no failures and at least one subscriber -> success
return (0==0);
}
}
@ -1296,9 +1437,9 @@ sub delete_address {
# ------------------------------------------------------------------------
sub set_pagedata4part_list {
my($part) = @_;
# Deal with list parts ....
my($part) = @_;
my ($i, $list, $listaddress,);
# Work out the address of this list ...
@ -1405,9 +1546,9 @@ sub create_list {
# ------------------------------------------------------------------------
sub extract_options_from_params()
{
sub extract_options_from_params {
# Work out the command line options ...
my ($options, $settings, $i);
my ($listname, $old_options, $state, $old_key);
@ -1473,9 +1614,9 @@ sub extract_options_from_params()
# ------------------------------------------------------------------------
sub manage_gnupg_keys()
# manage gnupg keys
{
sub manage_gnupg_keys {
# manage gnupg keys
my ($list, $listname, $upload_file);
$listname = $q->param('list');
@ -1506,8 +1647,8 @@ sub manage_gnupg_keys()
# ------------------------------------------------------------------------
sub gnupg_export_key()
{
sub gnupg_export_key {
my ($listname, $keyid) = @_;
my $list = new Mail::Ezmlm::Gpg("$LIST_DIR/$listname");
@ -1530,7 +1671,7 @@ sub gnupg_export_key()
# suggest a download filename
# (taken from http://www.bewley.net/perl/download.pl)
print "Content-Disposition: attachment; filename=$name\n";
print "Content-Description: exported key";
print "Content-Description: exported key\n\n";
print $key_armor;
return (0==0);
} else {
@ -1540,8 +1681,8 @@ sub gnupg_export_key()
# ------------------------------------------------------------------------
sub gnupg_import_key()
{
sub gnupg_import_key {
my ($list, $upload_file) = @_;
if ($upload_file) {
@ -1556,7 +1697,6 @@ sub gnupg_import_key()
# Handle key upload
my @ascii_key = <$upload_file>;
# TODO: filter content?
if ($list->import_key(join ('',@ascii_key))) {
$success = 'GnupgKeyImport';
return (0==0);
@ -1572,7 +1712,7 @@ sub gnupg_import_key()
# ------------------------------------------------------------------------
sub gnupg_generate_key() {
sub gnupg_generate_key {
my ($list, $listname) = @_;
my ($key_name, $key_comment, $key_size, $key_expires);
@ -1628,7 +1768,8 @@ sub gnupg_generate_key() {
# ------------------------------------------------------------------------
sub gnupg_remove_key() {
sub gnupg_remove_key {
my ($list) = @_;
my $removed = 0;
@ -1902,7 +2043,7 @@ sub webauth {
return (0==0) if (! -e "$WEBUSERS_FILE");
# if there was no user authentication, then everything is allowed
return (0==0) if ($ENV{'REMOTE_USER'} eq '');
return (0==0) if (!defined($ENV{REMOTE_USER}) or ($ENV{REMOTE_USER} eq ''));
# Read authentication level from webusers file. Format of this file is
# somewhat similar to the unix groups file
@ -1919,7 +2060,7 @@ sub webauth {
while(<USERS>) {
if (/^($listname|ALL):/im) {
# the following line should be synchronized with the webauth_create_allowed sub
if (/^[^:]*:(|.*[\s,])($ENV{'REMOTE_USER'}|ALL)(,|\s|$)/m) {
if (/^[^:]*:(|.*[\s,])($ENV{REMOTE_USER}|ALL)(,|\s|$)/m) {
close USERS;
return (0==0);
}
@ -1937,7 +2078,7 @@ sub webauth_create_allowed {
return (0==0) if (defined($opt_c));
# if there was no user authentication, then everything is allowed
return (0==0) if ($ENV{'REMOTE_USER'} eq '');
return (0==0) if (!defined($ENV{REMOTE_USER}) || ($ENV{REMOTE_USER} eq ''));
# Check if webusers file exists - if not, then access is granted
return (0==0) if (! -e "$WEBUSERS_FILE");
@ -1980,6 +2121,7 @@ sub get_available_interface_languages {
# ---------------------------------------------------------------------------
sub check_interface_language {
my ($language) = @_;
my %languages = &get_available_interface_languages();
return defined($languages{$language});
@ -1988,6 +2130,7 @@ sub check_interface_language {
# ---------------------------------------------------------------------------
sub check_list_language {
my ($list, $lang) = @_;
my $found = 0;
my $item;
@ -2016,6 +2159,7 @@ sub safely_import_module {
# ---------------------------------------------------------------------------
sub fatal_error() {
my $text = shift;
print "Content-Type: text/html; charset=utf-8\n\n";

View File

@ -55,18 +55,21 @@ except ImportError, errMsg:
sys.exit(1)
LANGUAGE_FILE = 'language.hdf'
HDF_DIR = 'lang'
## name of the main domain and prefix for all plugin domains
GETTEXT_DOMAIN = 'ezmlm-web'
## set the msgstrs for this language to the value of the respective msgids
DEFAULT_LANG = 'en'
LANG_DIR = 'intl'
PO_DIR = 'intl'
## mail adress for translation bugs
MAIL_ADDRESS = 'devel@sumpfralle.de'
## the complete list of languages wastes a lot of space - for now we use only a few
#ALL_LANGUAGES = "af aka am ar bn ca cs da de el en es et eu fa fi fr fur gl he hi hr hu hy is it ja ka kg ko ku lt lv mr ms mt nb ne nl nn ns pa pl pt ru sl sr st sv tr uk ve vi xh".split(" ")
ALL_LANGUAGES = "cs da de en es fi fr hu it ja nl pl pt ru sl sv".split(" ")
## use subversion for reverting?
USE_SVN = False
# --------------=-=-=- functions -=-=-=--------------------
def revert_if_unchanged(po_file):
@ -99,9 +102,9 @@ def revert_if_unchanged(po_file):
proc.wait()
def process_language_file(hdf_file, po_dir, textDomain):
def generate_po_files(hdf_file, po_dir, textDomain):
## prepare hdf
if not os.path.isfile(hdf_file) or not os.access(hdf_file, os.R_OK):
if ((not os.path.isfile(hdf_file)) or (not os.access(hdf_file, os.R_OK))):
sys.stderr.write("Unable to read the hdf file: %s\n" % hdf_file)
return
if not os.path.isdir(po_dir):
@ -157,7 +160,9 @@ def process_language_file(hdf_file, po_dir, textDomain):
for ld in ALL_LANGUAGES:
if not os.path.isdir(os.path.join(po_dir,ld)):
os.mkdir(os.path.join(po_dir, ld))
po_file = os.path.join(po_dir, ld, "%s.po" % textDomain)
if not os.path.isdir(os.path.join(po_dir,ld, 'LC_MESSAGES')):
os.mkdir(os.path.join(po_dir, ld, 'LC_MESSAGES'))
po_file = os.path.join(po_dir, ld, 'LC_MESSAGES', "%s.po" % textDomain)
if not os.path.isfile(po_file):
translate.convert.pot2po.convertpot(file(pot_file), file(po_file,'w'), None)
else:
@ -177,7 +182,8 @@ def process_language_file(hdf_file, po_dir, textDomain):
po_content.removeduplicates()
po_content.removeblanks()
po_content.savefile(po_file)
revert_if_unchanged(po_file)
if USE_SVN:
revert_if_unchanged(po_file)
## make it writeable for pootle
os.chmod(po_file, 0666)
## compile po file
@ -185,6 +191,67 @@ def process_language_file(hdf_file, po_dir, textDomain):
translate.tools.pocompile.convertmo(file(po_file), file(mo_file,'w'), file(pot_file))
def generate_translated_hdf_files(orig_hdf_file, po_dir, hdf_dir, textdomain):
for lang in ALL_LANGUAGES:
if lang != DEFAULT_LANG:
generate_translated_hdf_file(orig_hdf_file, po_dir, hdf_dir, textdomain, lang)
def generate_translated_hdf_file(orig_hdf_file, po_dir, hdf_dir, textdomain, language):
import gettext
## prepare original hdf
if ((not os.path.isfile(orig_hdf_file)) or (not os.access(orig_hdf_file, os.R_OK))):
sys.stderr.write("Unable to read the hdf file: %s\n" % orig_hdf_file)
return
hdf = neo_util.HDF()
hdf.readFile(orig_hdf_file)
## name of new hdf file
new_hdf_file = os.path.join(hdf_dir, language + '.hdf')
## create translation object
translator = gettext.translation(
textdomain,
localedir=po_dir,
languages=[language])
## translate entries
## count the number of translated items - so we can decide later, if we
## want to create the language file
translate_count = 0
def walk_hdf(prefix, node):
def addHdfItem(hdf_node):
## ignore hdf values with a "LINK" attribute
for (key,value) in hdf_node.attrs():
if key == "LINK":
return
if not hdf_node.value():
return
translated = translator.gettext(hdf_node.value())
if translated:
translate_count += 1
hdf.setValue("%s%s" % (prefix, hdf_node.name()), translated)
else:
hdf.setValue("%s%s" % (prefix, hdf_node.name()), hdf_node.value())
while node:
if node.name():
new_prefix = prefix + node.name() + '.'
else:
new_prefix = prefix
## as the attribute feature of clearsilver does not work yet, we
## have to rely on magic names to prevent the translation of links
if not (new_prefix.endswith(".Link.Rel.") \
or new_prefix.endswith(".Link.Prot.") \
or new_prefix.endswith(".Link.Abs.") \
or new_prefix.endswith(".Link.Attr1.name.") \
or new_prefix.endswith(".Link.Attr1.value.") \
or new_prefix.endswith(".Link.Attr2.name.") \
or new_prefix.endswith(".Link.Attr2.value.")):
addHdfItem(node)
walk_hdf(new_prefix, node.child())
node = node.next()
walk_hdf("", hdf)
## if there was at least one valid translation, then we should write
## the language file
if translate_count > 0:
hdf.writeFile(new_hdf_file)
# ----------------=-=-=- main -=-=-=-----------------------
@ -195,8 +262,14 @@ if __name__ == "__main__":
## the project directory is the parent of the directory of this script
PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.path.pardir))
process_language_file(
os.path.join(PROJECT_DIR, 'templates', LANGUAGE_FILE),
os.path.join(PROJECT_DIR, LANG_DIR),
generate_po_files(
os.path.join(PROJECT_DIR, HDF_DIR, DEFAULT_LANG + '.hdf'),
os.path.join(PROJECT_DIR, PO_DIR),
GETTEXT_DOMAIN)
generate_translated_hdf_files(
os.path.join(PROJECT_DIR, HDF_DIR, DEFAULT_LANG + '.hdf'),
os.path.join(PROJECT_DIR, PO_DIR),
os.path.join(PROJECT_DIR, HDF_DIR),
GETTEXT_DOMAIN)

View File

@ -26,6 +26,7 @@ Lang {
Properties = Properties of
Language = Language
Help = Help (external)
SubscribeLog = Subscriber's log
}
@ -47,6 +48,7 @@ Lang {
ListDelete = Delete list
FileSelect = Choose a file for editing
FileEdit = Editing file
SubscribeLog = Subscription events
GnupgConvert = Encryption
GnupgPublic = Public keys
GnupgSecret = Secret keys
@ -59,6 +61,7 @@ Lang {
Create = Create list
ConfirmDeletion = Delete the list
DeleteAddress = Delete address(es)
DownloadSubscribersList = Download subscribers
AddAddress = Add address(es)
UpdateConfiguration = Update configuration
UpdateGnupg = Update keyring
@ -112,10 +115,12 @@ Lang {
InvalidListName = The name of the list contains invalid characters
ReservedListName = This listname may not be used as it is reserved for internal purposes
EmptyListName = The name of the list may not be empty
EmptyList = This list has no subscribers.
InvalidLocalPart = The local part of the list address is not valid
RequiresIDX5 = This action requires ezmlm-idx v5.0 or higher.
ResetFileIsDefault = There is no customized text file, that can be removed.
ResetFile = Removal of custimized text file failed.
LogFile = Reading of log file failed.
GnupgNoKeyFile = There was no key file selected for upload!
GnupgDelKey = Removal of (at least) one key failed!
GnupgNoKeySelected = There was no key selected to be removed!
@ -281,6 +286,7 @@ Lang {
TextFileReset = Discard customized text
TextFileInfo = Useful placeholders
AvailableLists = Available lists
SubscribeLog = Events
GnupgConvert = Encryption support
GnupgPublicKeys = Public keys of this list
GnupgSecretKeys = Secret keys of this list

View File

@ -17,7 +17,7 @@
<?cs if:((subcount(Data.Lists) > 0) && (UI.Navigation.ListSelect == 1))
|| (Data.Permissions.Create && (UI.Navigation.ListCreate == 1)) ?>
<hr/>
<li><hr/></li>
<?cs /if ?>
@ -164,6 +164,11 @@
title="<?cs var:html_escape(Lang.Menue.GnupgConvert) ?>"><?cs
var:html_escape(Lang.Menue.GnupgConvert) ?></a></li><?cs /if ?>
<?cs if:UI.Navigation.SubscribeLog == 1
?><li><a <?cs if:(Data.Action == "subscribe_log") ?> class="nav_active"<?cs /if ?>
href="<?cs call:link("list",Data.List.Name,"action","subscribe_log","","") ?>"
title="<?cs var:html_escape(Lang.Menue.SubscribeLog) ?>"><?cs
var:html_escape(Lang.Menue.SubscribeLog) ?></a></li><?cs /if ?>
<?cs if:UI.Navigation.ListDelete == 1
?><li><a <?cs if:(Data.Action == "list_delete") ?> class="nav_active"<?cs /if ?>
href="<?cs call:link("list",Data.List.Name,"action","list_delete_ask","","") ?>"
@ -172,13 +177,13 @@
</ul></li>
<hr/>
<li><hr/></li>
<?cs /if ?>
<?cs if:UI.Navigation.Language
?><li><?cs include:TemplateDir + '/language_select.cs' ?></li>
<hr/>
<li><hr/></li>
<?cs /if ?>

View File

@ -0,0 +1,18 @@
<div class="title">
<h1><?cs var:html_escape(Lang.Title.SubscribeLog) ?></h1>
</div>
<fieldset class="form">
<legend><?cs var:html_escape(Lang.Legend.SubscribeLog) ?> </legend>
<table>
<?cs each:x = Data.List.SubscribeLog ?>
<tr>
<td><?cs var:html_escape(x.address) ?></td>
<td><?cs var:html_escape(x.text) ?></td>
<td><?cs var:html_escape(x.date) ?></td>
</tr>
<?cs /each ?>
</table>
</fieldset>

View File

@ -88,21 +88,32 @@
<ul>
<!-- scrollbox for list's subscribers -->
<!-- Keep selection box a reasonable size - suggested by Sebastian Andersson -->
<?cs if:subcount(Data.List.Subscribers) > 15 ?>
<?cs set:Data.ScrollSize = 15 ?>
<?cs else ?>
<?cs set:Data.ScrollSize = subcount(Data.List.Subscribers) ?>
<?cs /if ?>
<li><select name="mailaddress_del"
size="<?cs var:Data.ScrollSize ?>" multiple="multiple">
<?cs each:item = Data.List.Subscribers ?>
<option value="<?cs var:item.address ?>"><?cs var:item.address ?><?cs if:item.name ?> (<?cs var:item.name ?>)<?cs /if ?></option>
<?cs /each ?>
</select></li>
<li><?cs var:subcount(Data.List.Subscribers) ?> <?cs var:html_escape(Lang.Misc.Subscribers) ?></li>
<li><input type="hidden" name="action" value="address_del" />
<button type="submit" name="send" value="do"><?cs var:html_escape(Lang.Buttons.DeleteAddress) ?></button></li>
</ul></form></td>
<?cs if:subcount(Data.List.Subscribers) > 15 ?>
<?cs set:Data.ScrollSize = 15 ?>
<?cs else ?>
<?cs set:Data.ScrollSize = subcount(Data.List.Subscribers) ?>
<?cs /if ?>
<li><select name="mailaddress_del"
size="<?cs var:Data.ScrollSize ?>" multiple="multiple">
<?cs each:item = Data.List.Subscribers ?>
<option value="<?cs var:item.address ?>"><?cs var:item.address ?><?cs if:item.name ?> (<?cs var:item.name ?>)<?cs /if ?></option>
<?cs /each ?>
</select></li>
<li><?cs var:subcount(Data.List.Subscribers) ?> <?cs var:html_escape(Lang.Misc.Subscribers) ?></li>
<li><input type="hidden" name="action" value="address_del" />
<button type="submit" name="send" value="do"><?cs var:html_escape(Lang.Buttons.DeleteAddress) ?></button></form></li>
<li><form method="post" action="<?cs call:link('','','','','','')
?>" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="list" value="<?cs
var:Data.List.Name ?>" />
<input type="hidden" name="action" value="download_subscribers" />
<?cs if:Data.List.PartType ?>
<input type="hidden" name="part" value="<?cs
var:Data.List.PartType ?>" /><?cs /if ?>
<button type="submit" name="send" value="do"><?cs
var:html_escape(Lang.Buttons.DownloadSubscribersList)
?></button></form></li>
</ul></td>
<?cs /if ?>
<td><form method="post" action="<?cs call:link("","","","","","") ?>" enctype="multipart/form-data">

View File

@ -30,6 +30,7 @@ UI {
TextEdit = 1
ListDelete = 1
SubscribeLog = 1
GnupgConvert = 1
Language = 1
Interface = 1