From 64c3e10df0caa9e2ca914a95441286f2cc832e63 Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 20 Jun 2005 22:51:06 +0000 Subject: [PATCH] clearsilver branch merged and removed --- TODO | 3 + ezmlm-web.cgi | 578 +++++++++++++----------------------- lang/en.hdf | 168 +++++++++++ template/config_list.cs | 58 ++++ template/confirm_delete.cs | 17 ++ template/create_list.cs | 43 +++ template/display_list.cs | 100 +++++++ template/display_options.cs | 23 ++ template/edit_text.cs | 32 ++ template/footer.cs | 9 + template/header.cs | 25 ++ template/list_config.cs | 57 ++++ template/list_textfiles.cs | 28 ++ template/macros.cs | 11 + template/modpath_info.cs | 30 ++ template/select_list.cs | 61 ++++ 16 files changed, 879 insertions(+), 364 deletions(-) create mode 100644 TODO create mode 100644 lang/en.hdf create mode 100644 template/config_list.cs create mode 100644 template/confirm_delete.cs create mode 100644 template/create_list.cs create mode 100644 template/display_list.cs create mode 100644 template/display_options.cs create mode 100644 template/edit_text.cs create mode 100644 template/footer.cs create mode 100644 template/header.cs create mode 100644 template/list_config.cs create mode 100644 template/list_textfiles.cs create mode 100644 template/macros.cs create mode 100644 template/modpath_info.cs create mode 100644 template/select_list.cs diff --git a/TODO b/TODO new file mode 100644 index 0000000..3615e6e --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +start.cs -> select_list.cs + +check permission for uploading before doing! diff --git a/ezmlm-web.cgi b/ezmlm-web.cgi index 8ad62cd..d72d9cd 100755 --- a/ezmlm-web.cgi +++ b/ezmlm-web.cgi @@ -40,11 +40,11 @@ # Modules to include use strict; use Getopt::Std; +use ClearSilver; use Mail::Ezmlm; use Mail::Address; use DB_File; use CGI; -use CGI::Carp qw(fatalsToBrowser set_message); # These two are actually included later and are put here so we remember them. #use File::Find if ($UNSAFE_RM == 1); @@ -69,7 +69,14 @@ use vars qw[$DEFAULT_OPTIONS %EZMLM_LABELS $UNSAFE_RM $ALIAS_USER $LIST_DIR]; use vars qw[$QMAIL_BASE $EZMLM_CGI_RC $EZMLM_CGI_URL $HTML_BGCOLOR $PRETTY_NAMES]; use vars qw[%HELPER $HELP_ICON_URL $HTML_HEADER $HTML_FOOTER $HTML_TEXT $HTML_LINK]; use vars qw[%BUTTON %LANGUAGE $HTML_VLINK $HTML_TITLE $FILE_UPLOAD $WEBUSERS_FILE]; -use vars qw[$HTML_CSS_FILE]; +use vars qw[$HTML_CSS_FILE $TEMPLATE_DIR $LANGUAGE_DIR]; + +#TODO: interim +$TEMPLATE_DIR = "/root/clearsilver/template"; + +# pagedata contains the hdf tree for clearsilver +# pagename refers to the template file that should be used +use vars qw[$pagedata $pagename]; # Get user configuration stuff if(defined($opt_C)) { @@ -109,64 +116,61 @@ if(defined($Q::action) && $Q::action eq '[Web Archive]') { exit; } -# Print header on every page ... -print $q->header(-pragma=>'no-cache', '-cache-control'=>'no-cache', -expires=>'-1d', '-Content-Type'=>'text/html; charset=utf-8'); -print $q->start_html(-style=>{'src'=>"$HTML_CSS_FILE"},-title=>$HTML_TITLE, -author=>'guy-ezmlm@rucus.ru.ac.za', -expires=>'-1d'); -print $HTML_HEADER; +my $pagedata = load_hdf(); # check permissions -&check_permission_for_action == 0 || die 'Error: you are not allowed to do this!'; +&check_permission_for_action == 0 || &error_die('Error: you are not allowed to do this!'); # This is where we decide what to do, depending on the form state and the # users chosen course of action ... unless (defined($q->param('state'))) { # Default action. Present a list of available lists to the user ... - &select_list; + &select_list(); } elsif ($Q::state eq 'select') { # User selects an action to perform on a list ... - if ($Q::action eq "[$BUTTON{'create'}]") { # Create a new list ... + if ($Q::action eq $pagedata->getValue("Lang.Buttons.Create","unknown button")) { # Create a new list ... &allow_create_list; } elsif (defined($Q::list)) { - if ($Q::action eq "[$BUTTON{'edit'}]") { # Edit an existing list ... + if ($Q::action eq $pagedata->getValue("Lang.Buttons.Edit","unknown button")) { # Edit an existing list ... &display_list; - } elsif ($Q::action eq "[$BUTTON{'delete'}]") { # Delete a list ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.Delete","unknown button")) { # Delete a list ... &confirm_delete; } } else { - &select_list; # NOP - Blank input ... + &select_list(); # NOP - Blank input ... } } elsif ($Q::state eq 'edit') { # User chooses to edit a list my($list); $list = $LIST_DIR . '/' . $q->param('list'); - if ($Q::action eq "[$BUTTON{'deleteaddress'}]") { # Delete a subscriber ... + if ($Q::action eq $pagedata->getValue("Lang.Buttons.DeleteAddress","unknown button")) { # Delete a subscriber ... &delete_address($list); &display_list; - } elsif ($Q::action eq "[$BUTTON{'addaddress'}]") { # Add a subscriber ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.AddAddress","unknown button")) { # Add a subscriber ... &add_address($list); &display_list; - } elsif ($Q::action eq "[$BUTTON{'moderators'}]") { # Edit the moderators ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.Moderators","unknown button")) { # Edit the moderators ... &part_subscribers('mod'); - } elsif ($Q::action eq "[$BUTTON{'denylist'}]") { # Edit the deny list ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.DenyList","unknown button")) { # Edit the deny list ... &part_subscribers('deny'); - } elsif ($Q::action eq "[$BUTTON{'allowlist'}]") { # edit the allow list ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.AllowList","unknown button")) { # edit the allow list ... &part_subscribers('allow'); - } elsif ($Q::action eq "[$BUTTON{'digestsubscribers'}]") { # Edit the digest subscribers ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.DigestSubscribers","unknown button")) { # Edit the digest subscribers ... &part_subscribers('digest'); - } elsif ($Q::action eq "[$BUTTON{'configuration'}]") { # Edit the config ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.Configuration","unknown button")) { # Edit the config ... &list_config; } else { # Cancel - Return a screen ... - &select_list; + &select_list(); } } elsif ($Q::state eq 'allow' || $Q::state eq 'mod' || $Q::state eq 'deny' || $q->param('state') eq 'digest') { @@ -184,11 +188,11 @@ unless (defined($q->param('state'))) { $part = 'digest'; } - if ($Q::action eq "[$BUTTON{'deleteaddress'}]") { # Delete a subscriber ... + if ($Q::action eq $pagedata->getValue("Lang.Buttons.DeleteAddress","unknown button")) { # Delete a subscriber ... &delete_address("$LIST_DIR/$Q::list", $part); &part_subscribers($part); - } elsif ($Q::action eq "[$BUTTON{'addaddress'}]") { # Add a subscriber ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.AddAddress","unknown button")) { # Add a subscriber ... &add_address("$LIST_DIR/$Q::list", $part); &part_subscribers($part); @@ -199,32 +203,32 @@ unless (defined($q->param('state'))) { } elsif ($Q::state eq 'confirm_delete') { # User wants to delete a list ... - &delete_list if($q->param('confirm') eq "[$BUTTON{'yes'}]"); # Do it ... + &delete_list if($q->param('confirm') eq $pagedata->getValue("Lang.Buttons.Yes","unknown button")); # Do it ... $q->delete_all; - &select_list; + &select_list(); } elsif ($Q::state eq 'create') { # User wants to create a list ... - if ($Q::action eq "[$BUTTON{'createlist'}]") { + if ($Q::action eq $pagedata->getValue("Lang.Buttons.CreateList","unknown button")) { if (&create_list) { # Return if list creation is unsuccessful ... &allow_create_list; } else { - &select_list; # Else choose a list ... + &select_list(); # Else choose a list ... } } else { # Cancel ... - &select_list; + &select_list(); } } elsif ($Q::state eq 'configuration') { # User updates configuration ... - if ($Q::action eq "[$BUTTON{'updateconfiguration'}]") { # Save current settings ... + if ($Q::action eq $pagedata->getValue("Lang.Buttons.UpdateConfiguration","unknown button")) { # Save current settings ... &update_config; &display_list; - } elsif ($Q::action eq "[$BUTTON{'edittexts'}]") { # Edit DIR/text ... + } elsif ($Q::action eq $pagedata->getValue("Lang.Buttons.EditTexts","unknown button")) { # Edit DIR/text ... &list_text; } else { # Cancel - Return to list editing screen ... @@ -234,7 +238,7 @@ unless (defined($q->param('state'))) { } elsif ($Q::state eq 'list_text') { # User wants to edit texts associated with the list ... - if ($Q::action eq "[$BUTTON{'editfile'}]") { + if ($Q::action eq $pagedata->getValue("Lang.Buttons.EditFile","unknown button")) { &edit_text; } else { &list_config; # Cancel ... @@ -243,19 +247,59 @@ unless (defined($q->param('state'))) { } elsif ($Q::state eq 'edit_text') { # User wants to save a new version of something in DIR/text ... - &save_text if ($Q::action eq "[$BUTTON{'savefile'}]"); + &save_text if ($Q::action eq $pagedata->getValue("Lang.Buttons.SaveFile","unknown button")); &list_text; } else { - print "

$Q::action

$LANGUAGE{'nop'}


"; + $pagedata->setValue("Data.Action", $q->param('action')); + $pagedata->setValue("Data.Status", "unknown action"); + $pagename = 'select_list'; } -# Print HTML footer and exit :) ... -print $HTML_FOOTER, $q->end_html; +# Print page and exit :) ... +&output_page; exit; + # ========================================================================= +sub load_hdf { + # initialize the data for clearsilver + my $hdf = ClearSilver::HDF->new(); + + # TODO: respect LANGUAGE_DIR and LANGUAGE + $hdf->readFile($LANGUAGE_DIR . "/en.hdf"); + + # TODO: check for existence + $hdf->setValue("TemplateDir", "$TEMPLATE_DIR/"); + $hdf->setValue("LanguageDir", "$LANGUAGE_DIR/"); + $hdf->setValue("ScriptName", $ENV{'SCRIPT_NAME'}); + $hdf->setValue("Stylesheet", "$HTML_CSS_FILE"); + $hdf->setValue("HelpIconURL", "$HELP_ICON_URL"); + + return $hdf; +} + + +sub output_page { + # Print the page + + my $pagefile = $TEMPLATE_DIR . "/" . $pagename . ".cs"; + die "template ($pagefile) not found!" unless (-e "$pagefile"); + + # print http header + print "Content-Type: text/html\n\n"; + + my $cs = ClearSilver::CS->new($pagedata); + $cs->parseFile($TEMPLATE_DIR . '/macros.cs'); + $cs->parseFile($TEMPLATE_DIR . '/header.cs'); + $cs->parseFile($TEMPLATE_DIR . '/' . $pagename . '.cs'); + $cs->parseFile($TEMPLATE_DIR . '/footer.cs'); + + print $cs->render(); +} + + sub select_list { # List all mailing lists (sub directories) in the list directory. # Allow the user to choose a course of action; either editing an existing @@ -263,43 +307,26 @@ sub select_list { my (@lists, @files, $i, $scrollsize); + $pagename = 'select_list'; + # Read the list directory for mailing lists. - opendir DIR, $LIST_DIR || die "Unable to read $LIST_DIR: $!"; + opendir DIR, $LIST_DIR || &error_die("Unable to read $LIST_DIR: $!"); @files = grep !/^\./, readdir DIR; closedir DIR; # Check that they actually are lists ... + my $num = 0; foreach $i (0 .. $#files) { - if (-e "$LIST_DIR/$files[$i]/lock") { - $lists[$#lists + 1] = $files[$i] if (&webauth($files[$i]) == 0); + if ((-e "$LIST_DIR/$files[$i]/lock") && (&webauth($files[$i]) == 0)) { + $num++; + $pagedata->setValue("Data.Lists." . $num, "$files[$i]"); } } + $pagedata->setValue("Data.ListsCount", "$num"); - # Keep selection box a resonable size - suggested by Sebastian Andersson - $scrollsize = 25 if(($scrollsize = $#lists + 1) > 25); - - # Begin of content - print '
'; - - # Print a form - $q->delete_all; - print $q->startform; - print $q->hidden(-name=>'state', -default=>'select'); - - print '
'; - print $q->scrolling_list(-name=>'list', -size=>$scrollsize, -values=>\@lists) if defined(@lists); - print '
'; # end of main_mainlinglists_list + # TODO: ACL an einer Stelle zentral bestimmen lassen + $pagedata->setValue("Data.Permissions.Create", (&webauth_create_allowed == 0)? 1 : 0 ); - print '
', $LANGUAGE{'chooselistinfo'}, '
'; # explanation of options - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'create'}]"), '' if (&webauth_create_allowed == 0); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'edit'}]"), '' if(defined(@lists)); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'delete'}]"), '' if(defined(@lists)); - print '
'; # end of main_buttons - print $q->endform; - - print '
'; # end of content } # ------------------------------------------------------------------------ @@ -307,25 +334,8 @@ sub select_list { sub confirm_delete { # Make sure that the user really does want to delete the list! - # Begin of content - print '
'; - - # Print a form ... - $q->delete('state'); - print $q->startform; - print $q->hidden(-name=>'state', -default=>'confirm_delete'); - print $q->hidden(-name=>'list', -default=>$q->param('list')); - - print '
'; - print '

', $LANGUAGE{'confirmdelete'}, ' ', $q->param('list'), '

'; - print '
'; # end of delete->title - - print '
'; - print '', $q->submit(-name=>'confirm', -value=>"[$BUTTON{'no'}]"), ''; - print '', $q->submit(-name=>'confirm', -value=>"[$BUTTON{'yes'}]"), ''; - print '
'; # end of delete->question - - print '
'; # enf of delete + $pagedata->setValue("Data.ListName", $q->param('list')); + $pagename = 'confirm_delete'; } # ------------------------------------------------------------------------ @@ -333,63 +343,31 @@ sub confirm_delete { sub display_list { # Show a list of subscribers to the user ... - my ($i, $list, $listaddress, $moderated, @subscribers, $scrollsize); + my ($list); # Work out the address of this list ... $list = new Mail::Ezmlm("$LIST_DIR/$Q::list"); - $listaddress = &this_listaddress; - - # Get a list of subscribers from ezmlm ... - @subscribers = $list->subscribers; - - # Keep selection box a resonable size - suggested by Sebastian Andersson - $scrollsize = 25 if(($scrollsize = $#subscribers + 1) > 25); + $pagename = 'display_list'; - # Print out a form of options ... - $q->delete('state'); + $pagedata->setValue("Data.ListName", $q->param('list')); + $pagedata->setValue("Data.ListAddress", &this_listaddress); - # Begin of content - print '
'; + my $i = 0; + my $one_subs; + # TODO: use "pretty" output style for visible mail address + foreach $one_subs ($list->subscribers) { + $pagedata->setValue("Data.Subscribers." . $i, "$one_subs"); + $i++; + } + $pagedata->setValue("Data.SubscribersCount", "$i"); - print '
'; - print "

$LANGUAGE{'subscribersto'} $Q::list

"; - print "

($listaddress)

"; - print '
'; - print '
'; # end of edit->title - - print $q->start_multipart_form; - print $q->hidden(-name=>'state', -default=>'edit'); - print $q->hidden(-name=>'list', -default=>$Q::list); - - print '
'; - print $q->scrolling_list(-name=>'delsubscriber', -size=>$scrollsize, -values=>\@subscribers, -labels=>&pretty_names, -multiple=>'true') if defined(@subscribers); - print '
'; # end of edit->list - - print '
'; - print '

', ($#subscribers + 1), ' ', $LANGUAGE{'subscribers'}, '

' if defined(@subscribers); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'deleteaddress'}]"), '' if defined(@subscribers); - print '', $q->textfield(-name=>'addsubscriber', -size=>'40'), ' '; - print '', $q->filefield(-name=>'addfile', -size=>20, -maxlength=>100), ' ' if ($FILE_UPLOAD); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'addaddress'}]"), ''; - print '
'; # end of edit->add_remove - - print '
'; - print '

', $LANGUAGE{'additionalparts'}, ':

' if($list->ismodpost || $list->ismodsub || $list->isremote || $list->isdeny || $list->isallow || $list->isdigest); - print '

'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'moderators'}]"), ' ' if ($list->ismodpost || $list->ismodsub || $list->isremote); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'denylist'}]"), ' ' if ($list->isdeny); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'allowlist'}]"), ' ' if ($list->isallow); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'digestsubscribers'}]"), ' ' if ($list->isdigest); - print '

'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'webarchive'}]"), ' ' if(&ezmlmcgirc); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'configuration'}]"), ' '; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'selectlist'}]"), ''; - print '

'; - print '
'; # end of edit->options - print $q->endform; - - print '
'; # end of edit + $pagedata->setValue("Data.ConfigAvail.Extras", 1) if($list->ismodpost || $list->ismodsub || $list->isremote || $list->isdeny || $list->isallow || $list->isdigest); + $pagedata->setValue("Data.ConfigAvail.Moderation", 1) if ($list->ismodpost || $list->ismodsub || $list->isremote); + $pagedata->setValue("Data.ConfigAvail.DenyList", 1) if ($list->isdeny); + $pagedata->setValue("Data.ConfigAvail.AllowList", 1) if ($list->isallow); + $pagedata->setValue("Data.ConfigAvail.Digest", 1) if ($list->isdigest); + $pagedata->setValue("Data.ConfigAvail.WebArch", 1) if(&ezmlmcgirc); } @@ -426,7 +404,7 @@ sub delete_list { closedir DIR; foreach (@files) { unless (move($_, "$HOME_DIR/deleted.qmail/")) { - die "Unable to move .qmail files: $!"; + error_die("Unable to move .qmail files: $!"); } } warn "List '$oldfile' moved (deleted)"; @@ -434,13 +412,13 @@ sub delete_list { # This, however, does DELETE the list. I don't like the idea, but I was # asked to include support for it so ... if (!rmtree("$LIST_DIR/$Q::list")) { - die "Unable to delete list: $!"; + error_die("Unable to delete list: $!"); } opendir(DIR, "$HOME_DIR") or die "Unable to get directory listing: $!"; my @files = map { "$HOME_DIR/$1" if m{^(\.qmail.+)$} } grep { /^\.qmail-$listaddress/ } readdir DIR; closedir DIR; if (unlink(@files) <= 0) { - die "Unable to delete .qmail files: $!"; + &error_die("Unable to delete .qmail files: $!"); } warn "List '$list->thislist()' deleted"; } @@ -489,7 +467,6 @@ sub check_permission_for_action { $ret = 0; } return $ret; - } # ------------------------------------------------------------------------ @@ -503,7 +480,7 @@ sub add_address { if (($q->param('addfile')) && ($FILE_UPLOAD)) { # Sanity check - die "File upload must be of type text/*" unless($q->uploadInfo($q->param('addfile'))->{'Content-Type'} =~ m{^text/}); + &error_die("File upload must be of type text/*") unless($q->uploadInfo($q->param('addfile'))->{'Content-Type'} =~ m{^text/}); # Handle file uploads of addresses my($fh) = $q->param('addfile'); @@ -543,7 +520,7 @@ sub add_address { } if ($list->sub($add->address(), $part) != 1) { - die "Unable to subscribe to list: $!"; + &error_die("Unable to subscribe to list: $!"); } $count++; } @@ -563,7 +540,7 @@ sub delete_address { @address = $q->param('delsubscriber'); if ($list->unsub(@address, $part) != 1) { - die "Unable to unsubscribe from list $list: $!"; + &error_die("Unable to unsubscribe from list $list: $!"); } if($PRETTY_NAMES) { @@ -586,12 +563,16 @@ sub part_subscribers { my ($i, $list, $listaddress, @subscribers, $moderated, $scrollsize, $type); + $pagename = "config_list"; + # Work out the address of this list ... $list = new Mail::Ezmlm("$LIST_DIR/$Q::list"); - $listaddress = &this_listaddress; + $listaddress = &this_listaddress(); if($part eq 'mod') { # Lets know what is moderated :) + + $pagedata->setValue("Data.isModerated",1); # do we store things in different directories? my $config = $list->getconfig; @@ -599,65 +580,34 @@ sub part_subscribers { my($subpath) = $config =~ m{8\s*'([^']+)'}; my($remotepath) = $config =~ m{9\s*'([^']+)'}; - my($divclass); + $pagedata->setValue("Data.isPostMod", ($list->ismodpost)? 1 : 0); + $pagedata->setValue("Data.PostModPath", "$postpath"); - $divclass = ($postpath)? 'warning' : 'ok'; - $moderated .= "

[$LANGUAGE{'posting'}]" if ($list->ismodpost); - $moderated .= '' if ($postpath); - $moderated .= '

' if ($list->ismodpost); + $pagedata->setValue("Data.isSubMod", ($list->ismodsub)? 1 : 0); + $pagedata->setValue("Data.SubModPath", "$subpath"); - $divclass = ($subpath)? 'warning' : 'ok'; - $moderated .= "

[$LANGUAGE{'subscription'}]" if($list->ismodsub); - $moderated .= '' if ($subpath); - $moderated .= '

' if ($list->ismodsub); - - $divclass = ($remotepath)? 'warning' : 'ok'; - $moderated .= "

[$LANGUAGE{'remoteadmin'}]" if($list->isremote); - $moderated .= '' if ($remotepath); - $moderated .= '

if ($list->isremote)'; - + $pagedata->setValue("Data.isRemote", ($list->isremote)? 1 : 0); + $pagedata->setValue("Data.RemotePath", "$remotepath"); } # What type of sublist is this? ($type) = $Q::action =~ m/^\[(.+)\]$/; - # Get a list of moderators from ezmlm ... - @subscribers = $list->subscribers($part); + my $i = 0; + my $one_subs; + # TODO: use "pretty" output style for visible mail address + foreach $one_subs ($list->subscribers($part)) { + $pagedata->setValue("Data.List." . $i, "$one_subs"); + $i++; + } + $pagedata->setValue("Data.ListCount", "$i"); - # Keep selection box a resonable size - suggested by Sebastian Andersson - $scrollsize = 25 if(($scrollsize = $#subscribers + 1) > 25); + $pagedata->setValue("Data.ListName", $q->param('list')); + $pagedata->setValue("Data.ListAddress", "$listaddress"); - # Begin of content - print '
'; - - # Print out a form of options ... - $q->delete('state'); - - print '
'; - print "

$type $LANGUAGE{'for'} $listaddress

"; - print '
'; - print '
'; # end of parts_title - - print '
', "$moderated", '
' if(defined($moderated)); - - print $q->start_multipart_form; - print $q->hidden(-name=>'state', -default=>$part); - print $q->hidden(-name=>'list', -default=>$Q::list), "\n"; - - print '
', $q->scrolling_list(-name=>'delsubscriber', -size=>$scrollsize, -values=>\@subscribers, -multiple=>'true', -labels=>&pretty_names), '
' if defined(@subscribers); - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'deleteaddress'}]"), '' if defined(@subscribers); - print '', $q->textfield(-name=>'addsubscriber', -size=>'40'), ' '; - print '', $q->filefield(-name=>'addfile', -size=>20, -maxlength=>100), ' ' if ($FILE_UPLOAD); - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'addaddress'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'subscribers'}]"), ''; - print '
'; # end of parts_subscribers_actions - - print $q->endform; - - print '
'; # end of parts + $pagedata->setValue("Data.Form.State", $q->param('part')); + $pagedata->setValue("Data.FileUploadAllowed", ($FILE_UPLOAD)? 1 : 0); } # ------------------------------------------------------------------------ @@ -666,6 +616,9 @@ sub allow_create_list { # Let the user select options for list creation ... my($username, $hostname, %labels, $j); + # TODO: klaeren wofuer %labels da ist + + $pagename = 'create_list'; # Work out if this user has a virtual host and set input accordingly ... if(-e "$QMAIL_BASE/virtualdomains") { @@ -681,56 +634,22 @@ sub allow_create_list { $hostname = $DEFAULT_HOST; } - print '
'; + $pagedata->setValue("Data.UserName", "$username"); + $pagedata->setValue("Data.HostName", "$hostname"); - # Print a form of options ... - $q->delete_all; - - print '
'; - print '

', $LANGUAGE{'createnew'}, '

'; - print '
'; - print '
'; # end of create->title - - print $q->startform; - print $q->hidden(-name=>'state', -value=>'create'); - - print '
'; - print '', $LANGUAGE{'listname'}, ': ', $q->textfield(-name=>'list', -size=>'20'), ' '; - print '', $LANGUAGE{'listaddress'}, ': ', $q->textfield(-name=>'inlocal', -default=>$username, -size=>'10'); - print ' @ ', $q->textfield(-name=>'inhost', -default=>$hostname, -size=>'30'), ' '; - - print '', $LANGUAGE{'listoptions'}, ':'; + # TODO: migrate to cs &display_options($DEFAULT_OPTIONS); - # Allow creation of mysql table if the module allows it - if($Mail::Ezmlm::MYSQL_BASE) { - print '', $q->checkbox(-name=>'sql', -label=>$LANGUAGE{'mysqlcreate'}, -on=>1); - print ' '; - } - - if(-e "$WEBUSERS_FILE") { - print '', $LANGUAGE{'allowedtoedit'}, ': '; - print $q->textfield(-name=>'webusers', -value=>$ENV{'REMOTE_USER'}||'ALL', -size=>'30'); - print ' '; - print '', $HELPER{'allowedit'}, ''; - } - print '
'; # end of create->input - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'createlist'}]"), ''; - print '', $q->reset(-value=>"[$BUTTON{'resetform'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'cancel'}]"), ''; - print '
'; # end of create->question - print $q->endform; - - print '
'; # end of create + $pagedata->setValue("Data.mysqlModule", ($Mail::Ezmlm::MYSQL_BASE)? 1 : 0); + $pagedata->setValue("Data.WebUser.show", (-e "$WEBUSERS_FILE")? 1 : 0); + $pagedata->setValue("Data.WebUser.UserName", $ENV{'REMOTE_USER'}||'ALL'); } # ------------------------------------------------------------------------ sub create_list { - # Create a list acording to user selections ... + # Create a list according to user selections ... # Check the list directory exists and create if necessary ... if(!-e $LIST_DIR) { @@ -746,7 +665,8 @@ sub create_list { # Sanity Checks ... return 1 if ($listname eq '' || $qmail eq ''); if(-e ("$LIST_DIR/$listname/lock") || -e ("$HOME_DIR/.qmail-$qmail")) { - print '

', "List '$listname' already exists :(

"; + &error_die("Can't create list '$listname', as it already exists"); + # TODO: create a language string for this message return 1; } @@ -774,13 +694,13 @@ sub create_list { -switches=>$options, -user=>$USER) ) { - die 'List creation failed', $list->errmsg(); + &error_die('List creation failed' , $list->errmsg()); } # handle MySQL stuff if($q->param('sql') && $options =~ m/-6\s+/) { unless($list->createsql()) { - die 'SQL table creation failed: ', $list->errmsg(); + error_die('SQL table creation failed: ' , $list->errmsg()); } } @@ -794,51 +714,32 @@ sub create_list { sub list_config { # Allow user to alter the list configuration ... - my ($list, $listaddress, $listname, $options); - my ($headeradd, $headerremove, $mimeremove, $prefix, $j); + my ($list, $listname); + + $pagename = "list_config"; # Store some variables before we delete them ... $list = new Mail::Ezmlm("$LIST_DIR/$Q::list"); $listname = $q->param('list'); - $listaddress = &this_listaddress; - # Begin of content - print '
'; - - # Print a form of options ... - $q->delete_all; - - print '
'; - print '

', $LANGUAGE{'editconfiguration'}, '

'; - print '
'; - print '
'; # end of config->title - - print $q->startform; - print $q->hidden(-name=>'state', -value=>'configuration'); - print $q->hidden(-name=>'list', -value=>$listname); - - print '
'; - print '

', $LANGUAGE{'listname'}, ": $listname

"; - print '

', "$LANGUAGE{'listaddress'}: $listaddress

"; - print '
'; # end of config->info - - print '
'; - print '

', $LANGUAGE{'listoptions'}, ':

'; + $pagedata->setValue("Data.ListName", "$listname"); + $pagedata->setValue("Data.ListAddress", &this_listaddress); + # TODO: migrate # Print a list of options, selecting the ones that apply to this list ... &display_options($list->getconfig); # Get the contents of the headeradd, headerremove, mimeremove and prefix files - $headeradd = $list->getpart('headeradd'); - $headerremove = $list->getpart('headerremove'); - $mimeremove = $list->getpart('mimeremove'); - $prefix = $list->getpart('prefix'); + $pagedata->setValue("Data.List.Prefix", $list->getpart('prefix')); + # TODO: die folgenden Zeilen enden in einem Hash anstelle des Inhalts + my $temp = $list->getpart('headeradd'); + $pagedata->setValue("Data.List.HeaderAdd", "$temp"); + $temp = $list->getpart('headerremove'); + $pagedata->setValue("Data.List.HeaderRemove", "$temp"); + $temp = $list->getpart('mimeremove'); + $pagedata->setValue("Data.List.MimeRemove", "$temp"); - print '', $LANGUAGE{'prefix'}, ': ', $q->textfield(-name=>'prefix', -default=>$prefix, -size=>12), ' ' if defined($prefix); - print '

', $LANGUAGE{'headerremove'}, ':
', $q->textarea(-name=>'headerremove', -default=>$headerremove, -rows=>5, -columns=>70), ''; - print '', $LANGUAGE{'headeradd'}, ':
', $q->textarea(-name=>'headeradd', -default=>$headeradd, -rows=>5, -columns=>70), '

'; - print '', $LANGUAGE{'mimeremove'}, ':
', $q->textarea(-name=>'mimeremove', -default=>$mimeremove, -rows=>5, -columns=>70), '
' if defined($mimeremove); - + # TODO: this is definitely ugly - create a new sub! if(open(WEBUSER, "<$WEBUSERS_FILE")) { my($webusers); while() { @@ -847,24 +748,8 @@ sub list_config { close WEBUSER; $webusers ||= $ENV{'REMOTE_USER'} || 'ALL'; - print '', $LANGUAGE{'allowedtoedit'}, ': '; - print $q->textfield(-name=>'webusers', -value=>$webusers, -size=>'30'); - print ' ', - print '', $HELPER{'allowedit'}, ''; - + $pagedata->setValue("Data.List.WebUsers", "$webusers"); } - print '
'; # end of config->input - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'updateconfiguration'}]"), ''; - print '', $q->reset(-value=>"[$BUTTON{'resetform'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'cancel'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'edittexts'}]"), ''; - print '
'; # end of config->question - - print $q->endform; - - print ''; # end of config } # ------------------------------------------------------------------------ @@ -945,41 +830,27 @@ sub this_listaddress { sub list_text { # Show a listing of what is in DIR/text ... + + $pagename = 'list_textfiles'; my(@files, $list); $list = $LIST_DIR . '/' . $q->param('list'); # Read the list directory for text ... - opendir DIR, "$list/text" || die "Unable to read DIR/text: $!"; + opendir DIR, "$list/text" || &error_die("Unable to read DIR/text: $!"); @files = grep !/^\./, readdir DIR; closedir DIR; - # Begin of content - print '
'; + $pagedata->setValue("Data.ListName", $q->param('list')); - # Print a form ... - $q->delete('state'); - print $q->startform; - print $q->hidden(-name=>'state', -default=>'list_text'); - print $q->hidden(-name=>'list', -default=>$q->param('list')); - - print '
'; - print $q->scrolling_list(-name=>'file', -values=>\@files); - print '
'; # end of textfiles->list - - print '
'; - print $LANGUAGE{'edittextinfo'}; - print '
'; # end of textfiles->info - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'editfile'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'cancel'}]"), ''; - print '
'; # end of textfiles->question - - print $q->endform; - - print '
'; - + # TODO: find a better way to set a list ... + my $i = 0; + my $one_file; + foreach $one_file (@files) { + $pagedata->setValue("Data.Files." . $i, "$one_file"); + $i++; + } + $pagedata->setValue("Data.FilesCount", "$i"); } # ------------------------------------------------------------------------ @@ -987,42 +858,16 @@ sub list_text { sub edit_text { # Allow user to edit the contents of DIR/text ... + $pagename = 'edit_text'; + my ($content); my($list) = new Mail::Ezmlm("$LIST_DIR/$Q::list"); $content = $list->getpart("text/$Q::file"); - # Begin of content - print '
'; - - # Print a form ... - $q->delete('state'); - - print '
'; - print '

', $LANGUAGE{'editingfile'}, ': ', $Q::file, '

'; - print '
'; # end of edittext->title - - print $q->startform; - print $q->hidden(-name=>'state', -default=>'edit_text'); - print $q->hidden(-name=>'list', -default=>$q->param('list')); - print $q->hidden(-name=>'file', -default=>$q->param('file')); - - print '
'; - print '', $q->textarea(-name=>'content', -default=>$content, -rows=>'25', -columns=>'72'), ''; - print '
'; # end of edittext->input - - print ''; - print $LANGUAGE{'editfileinfo'}; - print '
'; # end of edittext->info - - print '
'; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'savefile'}]"), ''; - print '', $q->reset(-value=>"[$BUTTON{'resetform'}]"), ''; - print '', $q->submit(-name=>'action', -value=>"[$BUTTON{'cancel'}]"), ''; - print '
'; # end of edittext->question - - print $q->endform; - - print ''; # end of edittext + $pagedata->setValue("Data.ListName", $q->param('list')); + # TODO: file wird nurals hash interpretiert + $pagedata->setValue("Data.File.Name", $q->param('file')); + $pagedata->setValue("Data.File.Content", "$content"); } # ------------------------------------------------------------------------ @@ -1087,33 +932,29 @@ sub display_options { my($opts) = shift; my($i, $j); - print ""; - print '

'; + # TODO: remove when migration to cs is done + $j = 0; + # convert EZMLM_LABELS to hdf-language values foreach $i (grep {/\D/} keys %EZMLM_LABELS) { - if ($opts =~ /^\w*$i\w*\s*/) { - print '', $q->checkbox(-name=>$i, -value=>$i, -label=>$EZMLM_LABELS{$i}[0], -on=>'1'); - } else { - print '', $q->checkbox(-name=>$i, -value=>$i, -label=>$EZMLM_LABELS{$i}[0]); - } - print ''; - print ''; $j++; - if ($j >= 3) { - $j = 0; print '

'; - } + $pagedata->setValue("Data.ListOptions." . $i . ".name", "$i"); + $pagedata->setValue("Data.ListOptions." . $i . ".short", "$EZMLM_LABELS{$i}[0]"); + $pagedata->setValue("Data.ListOptions." . $i . ".long", "$EZMLM_LABELS{$i}[1]"); + $pagedata->setValue("Data.ListOptions." . $i . ".state", ($opts =~ /^\w*$i\w*\s*/)? 1 : 0); + $j++; } - print '

'; + $pagedata->setValue("Data.ListOptionsCount", "$j"); + $j = 0; + # convert EZMLM_LABELS to hdf-language values foreach $i (grep {/\d/} keys %EZMLM_LABELS) { - print '

'; - if ($opts =~ /$i (?:'(.+?)')/) { - print '', $q->checkbox(-name=>$i, -value=>$i, -label=>$EZMLM_LABELS{$i}[0], -on=>'1'); - } else { - print '', $q->checkbox(-name=>$i, -value=>$i, -label=>$EZMLM_LABELS{$i}[0]); - } - print ''; - print '', $q->textfield(-name=>"$i-value", -value=>$1||$EZMLM_LABELS{$i}[2], -size=>30), ''; - print '

'; + $pagedata->setValue("Data.ListSettings." . $i . ".name", "$i"); + $pagedata->setValue("Data.ListSettings." . $i . ".short", "$EZMLM_LABELS{$i}[0]"); + $pagedata->setValue("Data.ListSettings." . $i . ".long", "$EZMLM_LABELS{$i}[1]"); + $pagedata->setValue("Data.ListSettings." . $i . ".state", ($opts =~ /$i (?:'(.+?)')/)? 1 : 0); + $pagedata->setValue("Data.ListSettings." . $i . ".value", $1||$EZMLM_LABELS{$i}[2]); + $j++; } + $pagedata->setValue("Data.ListSettingsCount", "$j"); } @@ -1179,9 +1020,17 @@ BEGIN { quoting the error message above.

EOM - } - set_message(\&handle_errors); + + sub error_die { + my $msg = @_; + $pagedata->setValue("Data.ErrorMessage", "$msg"); + # TODO: besser waere eine Warnung in header.cs + $pagename = 'error'; + &output_page; + die; + } + } # ------------------------------------------------------------------------ @@ -1235,6 +1084,7 @@ F<./ezmlmwebrc> =head1 AUTHOR Guy Antony Halse + Lars Kruse =head1 BUGS diff --git a/lang/en.hdf b/lang/en.hdf new file mode 100644 index 0000000..f722215 --- /dev/null +++ b/lang/en.hdf @@ -0,0 +1,168 @@ +Language = en + +PageTitle = ezmlm-web - a mailinglists' administration interface + + +Lang { + Buttons { + Create = [Create List] + Edit = [Edit] + Delete = [Delete] + Yes = [Yes] + No = [No] + Cancel = [Cancel] + DeleteAddress = [Delete Address] + AddAddress = [Add Address] + Moderators = [Moderators] + DenyList = [Deny List] + AllowList = [Allow List] + DigestSubscribers = [Digest Subscribers] + Configuration = [Configuration] + UpdateConfiguration = [Update Configuration] + EditTexts = [Edit Texts] + EditFile = [Edit File] + SaveFile = [Save File] + WebArchive = [Web Archive] + SelectList = [Select List] + Subscribers = [Subscribers] + ResetForm = [Reset Form] + } + + + Options { + a.short = Archived + a.long = Ezmlm will archive new messages + b.short = Block archive + b.long = Only moderators are allowed to access the archive +# c => config. This is implicity called, so is not defined here + d.short = Digest + d.long = Set up a digest list to disseminate digest of the list messages +# e => edit. Also implicity called, so not defined here + f.short = Prefix + f.long = Outgoing subject will be prefixed with the list name + g.short = Guard Archive + g.long = Archive access requests from unrecognized SENDERs will be rejected + h.short = Help subscription + h.long = Subscriptions do not require confirmation + i.short = Indexed + i.long = Indexed for WWW archive access + j.short = Jump off + j.long = Unsubscribe does not require confirmation + k.short = Kill + k.long = Posts from addresses in dir/deny/ are rejected + l.short = Subscriber List + l.long = Remote administrators can request a subscriber list + m.short = Message Moderation + m.long = All incoming messages are moderated + n.short = Text Editing + n.long = Allow remote administrators to edit files in dir/text/ + o.short = Others rejected + o.long = Posts from addresses other than moderators are rejected + p.short = Public + p.long = List will respond to administrative requests and archive retrieval + q.short = Service Request Address + q.long = Process commands sent in the subject to local-request@host + r.short = Remote Admin + r.long = Enable remote adminstration of the list + s.short = Subscription Moderation + s.long = Subscriptions to the main list and digest will be moderated + t.short = Trailer + t.long = Add a trailer to outgoing messages + u.short = User Posts Only + u.long = Posts from unrecognized SENDER addresses will be rejected +# v => version. I doubt you will really need this ;-) + w.short = Remove Warn + w.long = Remove the ezmlm-warn(1) invocations from the list setup. It is assumed that ezmlm-warn(1) is run by other means + x.short = Extra + x.long = Strip certain mimetypes, etc +# y => not used +# z => not used + } + + Settings { + 0.short = Sublist + 0.long = Make the list a sublist of list mainlist@host + 0.default = mainlist@host +# 1 => not used +# 2 => not used + 3.short = From Address + 3.long = Replace the "From:" header of the message with "From: fromarg" + 3.default = fromarg + 4.short = Digest Options + 4.long = Switches for ezmlm-tstdig(1) + 4.default = -t24 -m30 -k64 + 5.short = List Owner + 5.default = The email address of the list owner + 6.short = SQL Database + 6.long = SQL database connect information. Requires SQL support + 6.default = host:port:user:password:datab:table + 7.short = Message Moderation Path + 7.long = Make /path the path to the database for message moderators, if the list is set up for message moderation + 7.default = /some/full/path + 8.short = Subscription Moderation Path + 8.long = Make /path the path to the database for message moderators, if the list is set up for message moderation + 8.default = /some/full/path + 9.short = Remote Admin Path + 9.long = Make /path the path to the database for message moderators, if the list is set up for message moderation + 9.default = /some/full/path + } + + + Helper { + AddAddress = You may enter any RFC822 compliant email address here, including the comment part. For example; 'J Random User ' + AddAddressFile = or you may enter the filename of a plain text file containing multiple RFC822 email addresses, one per line + Moderator = Moderators: people who control who may subscribe or post to a list + Deny = Deny: A list of addresses that are _never_ allowed to mail the list + Allow = Allow: A list of address that are allowed to mail the list even if the configuration otherwise restricts it + Digest = Digest: People who will recieve a digest of all messages on the list + WebArch = View the web based archive of this list + Config = This lets you alter the way the list is set up + ListName = This is the name of the list as displayed on the Select Lists screen. It is also the name of the subdirectory that contains the list + ListAdd = This is the email address of the list. Note that the defaults come from your qmail config. You should just update the local part (before the @) + WebUsers = NB! At this stage, any users specified here must exist. User creation may be added in future versions + Prefix = Text to add to the subject line of all outgoing messages + HeaderRemove = This is a list of headers to remove from all outgoing mail + HeaderAdd = This is a list of headers to add to all outging mail + MimeRemove = All messages whose Content-Type matches these mime types will be bounced back to sender + AllowEdit = Comma separated list of usernames, or 'ALL' (all valid users) + MysqlCreate = This will create the necessary MySQL tables if the list configuration above requires it + SelectList = Go back to list selection + } + + + Misc { + ListSelectDescription { + 1 = choose a mailing list from the selection box or click on [Create List] + 2 = click on the [Edit] button if you want to edit the selected list + 3 = click on the [Delete] button if you want to delete the selected list + } + UnknownAction = Action not yet implemented + ConfirmDelete = Confirm deletion of + SubscribersTo = Subscribers to + Subscribers = subscribers + AdditionalParts = Additional list parts + Posting = Posting + Subscription = Subscription + RemoteAdmin = Remote Admin + For = for + # as in; "moderators for ..." + CreateNew = Create a New List + ListName = List Name + ListAddress = List Address + ListOptions = List Options + AllowedToEdit = Users allowed to edit this list + EditConfiguration = Edit the List Configuration + Prefix = Subject prefex for outgoing messages + HeaderRemove = Headers to strip from all outgoing mail + HeaderAdd = Headers to add to all outgoing mail + MimeRemove = Mime types to strip from all outgoing mail + EditTextInfo = The box on the left contains a list of files available in the DIR/text directory. These files are sent out in response to specfic user request, or as part of all outgoing messages
To edit a file, select its name from the box. Then click on the [Edit File] button. Press [cancel] when you have finished editing. + EditingFile = Editing File + EditFileInfo = ezmlm-manage
<#l#> The list name
<#A#>The subscription address
<#R#> The address a subscriber must reply to

ezmlm-store
<#l#> The list name
<#A#>The acceptance address
<#R#> The rejection address + mysqlCreate = Create the MySQL database tables if necessary + SuggestEdit = You will have to edit them manually. + PostModPathWarn = Posting Moderators are stored in a non-standard location + SubModPathWarn = Subscriber Moderators are stored in a non-standard location + RemotePathWarn = Remote Administrators are stored in a non-standard location + } +} diff --git a/template/config_list.cs b/template/config_list.cs new file mode 100644 index 0000000..eae79df --- /dev/null +++ b/template/config_list.cs @@ -0,0 +1,58 @@ +

+
+ +

+

+
+
+ + + + + + + +
+ + +
+ + + # TODO: the same as of "display_list.cs" + # list of moderators/administrators + 0 ?> + + 25) ?> + + + + + + + + +
+ + 0) ?> + + + + + + + + + + + + +
+ + +
diff --git a/template/confirm_delete.cs b/template/confirm_delete.cs new file mode 100644 index 0000000..f062fa2 --- /dev/null +++ b/template/confirm_delete.cs @@ -0,0 +1,17 @@ +
+ +
+ + +
+

+
+ + +
+
+
+ +
diff --git a/template/create_list.cs b/template/create_list.cs new file mode 100644 index 0000000..9819c35 --- /dev/null +++ b/template/create_list.cs @@ -0,0 +1,43 @@ +
+ +
+

+
+
+ + +
+ +
+ : + : + @ + : + + + + + + + + + + : + + # TODO: the following span is quite unusual + + +
+ +
+ + + +
+
+ +
diff --git a/template/display_list.cs b/template/display_list.cs new file mode 100644 index 0000000..4e44ac9 --- /dev/null +++ b/template/display_list.cs @@ -0,0 +1,100 @@ +
+ +
+ +

+

+
+
+ +
+
+ + + + + + 25) ?> + + + + + + +
+ 0) ?> +

+ + + + + + +
+
+ + +

:

+ +

+ + + + + + + + + + + + + + + + + + + + + + + +

+ +

+ + + + + + + + + + + + +

+ +
+
+ +
+ +
diff --git a/template/display_options.cs b/template/display_options.cs new file mode 100644 index 0000000..dc3a936 --- /dev/null +++ b/template/display_options.cs @@ -0,0 +1,23 @@ + +

+ + +

checked="checked"> +
+ +

+ +

+ +

checked="checked"> + + +
+ + +

diff --git a/template/edit_text.cs b/template/edit_text.cs new file mode 100644 index 0000000..531aa7e --- /dev/null +++ b/template/edit_text.cs @@ -0,0 +1,32 @@ +
+ +
+

+
+ +
+ + + + +
+ +
+ +
+ +
+ +
+ + + +
+ +
+ +
diff --git a/template/footer.cs b/template/footer.cs new file mode 100644 index 0000000..81bff46 --- /dev/null +++ b/template/footer.cs @@ -0,0 +1,9 @@ + + + +
+ezmlm-web (v2.3) A web interface to ezmlm +
+ + + diff --git a/template/header.cs b/template/header.cs new file mode 100644 index 0000000..204f502 --- /dev/null +++ b/template/header.cs @@ -0,0 +1,25 @@ + + + + + + <?cs var:PageTitle ?> + + + + + + + + + + + +

+

E Z Mailing List Manager
+

+ +
+ diff --git a/template/list_config.cs b/template/list_config.cs new file mode 100644 index 0000000..a8a3c26 --- /dev/null +++ b/template/list_config.cs @@ -0,0 +1,57 @@ +
+ +
+

+
+
+ +
+ + +
+

:

+

:

+
+ +
+

:

+ + + + +
:
+ +
: +
+
: +
+ +
: +
+ + + +
+ : + + +
+ +
+ +
+ + + + +
+ +
+ +
diff --git a/template/list_textfiles.cs b/template/list_textfiles.cs new file mode 100644 index 0000000..99298ae --- /dev/null +++ b/template/list_textfiles.cs @@ -0,0 +1,28 @@ +
+ +
+ + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
diff --git a/template/macros.cs b/template/macros.cs new file mode 100644 index 0000000..09b9b2d --- /dev/null +++ b/template/macros.cs @@ -0,0 +1,11 @@ + + + +   + + + + + +   + diff --git a/template/modpath_info.cs b/template/modpath_info.cs new file mode 100644 index 0000000..218d76a --- /dev/null +++ b/template/modpath_info.cs @@ -0,0 +1,30 @@ +
+ + + +

+ + (). + +

+ + + + +

+ + (). + +

+ + + + +

+ + (). + +

+ + +
diff --git a/template/select_list.cs b/template/select_list.cs new file mode 100644 index 0000000..037e92e --- /dev/null +++ b/template/select_list.cs @@ -0,0 +1,61 @@ + + + +
+

+

+
+ + + + +
+ +
+ + + 0) ?> + +
+ + 25) ?> + + + + + +
+ + + +
+
    + +
  • + +
+
+ + +
+ + + + + 0) ?> + + + + +
+
+
+ +