packaging/tag_delivery_delete -text
packaging/tag_packaging_create -text
packaging/tag_packaging_delete -text
+plugins/coclico/mailman/README.txt -text
+plugins/coclico/mailman/bin/db-delete.pl -text
+plugins/coclico/mailman/bin/db-upgrade.pl -text
+plugins/coclico/mailman/common/mailman-init.php -text
+plugins/coclico/mailman/cronjobs/manage_mailmanlists.php -text
+plugins/coclico/mailman/db/install.sql -text
+plugins/coclico/mailman/db/mailman-init.sql -text
+plugins/coclico/mailman/db/uninstall.sql -text
+plugins/coclico/mailman/debian/README.Debian -text
+plugins/coclico/mailman/debian/README.source -text
+plugins/coclico/mailman/debian/changelog -text
+plugins/coclico/mailman/debian/compat -text
+plugins/coclico/mailman/debian/control -text
+plugins/coclico/mailman/debian/copyright -text
+plugins/coclico/mailman/debian/docs -text
+plugins/coclico/mailman/debian/po/templates.pot -text
+plugins/coclico/mailman/debian/rules -text
+plugins/coclico/mailman/debian/source/format -text
+plugins/coclico/mailman/etc/plugins/mailman/config.php -text
+plugins/coclico/mailman/include/BackendMailmanList.class.php -text
+plugins/coclico/mailman/include/MailmanList.class.php -text
+plugins/coclico/mailman/include/MailmanListDao.class.php -text
+plugins/coclico/mailman/include/MailmanListFactory.class.php -text
+plugins/coclico/mailman/include/MailmanPluginDescriptor.class.php -text
+plugins/coclico/mailman/include/MailmanPluginInfo.class.php -text
+plugins/coclico/mailman/include/MailsForUser.class.php -text
+plugins/coclico/mailman/include/events/SystemEvent_MAILMAN_LIST_CREATE.class.php -text
+plugins/coclico/mailman/include/events/SystemEvent_MAILMAN_LIST_DELETE.class.php -text
+plugins/coclico/mailman/include/mailman-init.php -text
+plugins/coclico/mailman/include/mailmanPlugin.class.php -text
+plugins/coclico/mailman/packaging/control/000source -text
+plugins/coclico/mailman/packaging/control/222plugin-mailman -text
+plugins/coclico/mailman/packaging/control/222plugin-mailman.shortdesc -text
+plugins/coclico/mailman/packaging/cron.d/plugin-mailman -text
+plugins/coclico/mailman/packaging/dirs/plugin-mailman -text
+plugins/coclico/mailman/packaging/docs/plugin-mailman -text
+plugins/coclico/mailman/packaging/install/plugin-mailman -text
+plugins/coclico/mailman/packaging/links/plugin-mailman -text
+plugins/coclico/mailman/translations/en.po -text
+plugins/coclico/mailman/translations/gforge.pot -text
+plugins/coclico/mailman/usr/lib/mailman/Mailman/ExternalConnector.py -text
+plugins/coclico/mailman/usr/lib/mailman/Mailman/ForgeSecurityManager.py -text
+plugins/coclico/mailman/usr/lib/mailman/Mailman/MySQLConnector.py -text
+plugins/coclico/mailman/usr/lib/mailman/Mailman/PsycopgConnector.py -text
+plugins/coclico/mailman/utils/manage-translations.sh -text
+plugins/coclico/mailman/var/lib/mailman/lists/extend.py -text
+plugins/coclico/mailman/www/admin/deletelist.php -text
+plugins/coclico/mailman/www/admin/index.php -text
+plugins/coclico/mailman/www/index.html -text
+plugins/coclico/mailman/www/index.php -text
+plugins/coclico/mailman/www/mailman_utils.php -text
plugins/templates/createplugin.sh -text
plugins/templates/helloworld/debian/README.Debian -text
plugins/templates/helloworld/debian/README.source -text
--- /dev/null
+This is the readme of the plugin.
+You should put here any useful information about
+the plugin : install, configuration, ...
+
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Debian-specific script to delete plugin-specific tables
+# Roland Mas <lolando@debian.org>
+
+use strict ;
+use diagnostics ;
+
+use DBI ;
+use MIME::Base64 ;
+use HTML::Entities ;
+
+use vars qw/$dbh @reqlist $query/ ;
+use vars qw/$sys_default_domain $sys_cvs_host $sys_download_host
+ $sys_shell_host $sys_users_host $sys_docs_host $sys_lists_host
+ $sys_dns1_host $sys_dns2_host $FTPINCOMING_DIR $FTPFILES_DIR
+ $sys_urlroot $sf_cache_dir $sys_name $sys_themeroot
+ $sys_news_group $sys_dbhost $sys_dbname $sys_dbuser $sys_dbpasswd
+ $sys_ldap_base_dn $sys_ldap_host $admin_login $admin_password
+ $server_admin $domain_name $newsadmin_groupid $statsadmin_groupid
+ $skill_list/ ;
+use vars qw/$pluginname/ ;
+
+sub is_lesser ( $$ ) ;
+sub is_greater ( $$ ) ;
+sub debug ( $ ) ;
+sub parse_sql_file ( $ ) ;
+
+require ("/usr/share/gforge/lib/include.pl") ; # Include a few predefined functions
+require ("/usr/share/gforge/lib/sqlparser.pm") ; # Our magic SQL parser
+
+debug "You'll see some debugging info during this installation." ;
+debug "Do not worry unless told otherwise." ;
+
+&db_connect ;
+
+# debug "Connected to the database OK." ;
+
+$pluginname = "mailman" ;
+
+$dbh->{AutoCommit} = 0;
+$dbh->{RaiseError} = 1;
+eval {
+ my ($sth, @array, $version, $action, $path, $target, $rname) ;
+
+ my $pattern = "plugin_" . $pluginname . '_%' ;
+
+ $query = "SELECT relname FROM pg_class WHERE relname LIKE '$pattern' AND relkind='v'" ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ while (@array = $sth->fetchrow_array ()) {
+ $rname = $array [0] ;
+ &drop_view_if_exists ($rname) ;
+ }
+ $sth->finish () ;
+
+ $query = "SELECT relname FROM pg_class WHERE relname LIKE '$pattern' AND relkind='r'" ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ while (@array = $sth->fetchrow_array ()) {
+ $rname = $array [0] ;
+ &drop_table_if_exists ($rname) ;
+ }
+ $sth->finish () ;
+
+ $query = "SELECT relname FROM pg_class WHERE relname LIKE '$pattern' AND relkind='i'" ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ while (@array = $sth->fetchrow_array ()) {
+ $rname = $array [0] ;
+ &drop_index_if_exists ($rname) ;
+ }
+ $sth->finish () ;
+
+ $query = "SELECT relname FROM pg_class WHERE relname LIKE '$pattern' AND relkind='s'" ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ while (@array = $sth->fetchrow_array ()) {
+ $rname = $array [0] ;
+ &drop_sequence_if_exists ($rname) ;
+ }
+ $sth->finish () ;
+
+ $dbh->commit ();
+
+
+ debug "It seems your database deletion went well and smoothly. That's cool." ;
+ debug "Please enjoy using Debian GForge." ;
+
+ # There should be a commit at the end of every block above.
+ # If there is not, then it might be symptomatic of a problem.
+ # For safety, we roll back.
+ $dbh->rollback ();
+};
+
+if ($@) {
+ warn "Transaction aborted because $@" ;
+ debug "Transaction aborted because $@" ;
+ debug "Last SQL query was:\n$query\n(end of query)" ;
+ $dbh->rollback ;
+ debug "Please report this bug on the Debian bug-tracking system." ;
+ debug "Please include the previous messages as well to help debugging." ;
+ debug "You should not worry too much about this," ;
+ debug "your DB is still in a consistent state and should be usable." ;
+ exit 1 ;
+}
+
+$dbh->rollback ;
+$dbh->disconnect ;
+
+sub debug ( $ ) {
+ my $v = shift ;
+ chomp $v ;
+ print STDERR "$v\n" ;
+}
+
+sub drop_table_if_exists ( $ ) {
+ my $tname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$tname' AND relkind='r'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping table $tname" ;
+ $query = "DROP TABLE $tname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_sequence_if_exists ( $ ) {
+ my $sname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$sname' AND relkind='S'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping sequence $sname" ;
+ $query = "DROP SEQUENCE $sname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_index_if_exists ( $ ) {
+ my $iname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$iname' AND relkind='i'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping index $iname" ;
+ $query = "DROP INDEX $iname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_view_if_exists ( $ ) {
+ my $iname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$iname' AND relkind='v'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping view $iname" ;
+ $query = "DROP VIEW $iname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
--- /dev/null
+#!/usr/bin/perl -w
+#
+# Debian-specific script to upgrade the database between releases
+# Roland Mas <lolando@debian.org>
+
+use strict ;
+use diagnostics ;
+
+use DBI ;
+use MIME::Base64 ;
+use HTML::Entities ;
+
+use vars qw/$dbh @reqlist $query/ ;
+use vars qw/$sys_default_domain $sys_cvs_host $sys_download_host
+ $sys_shell_host $sys_users_host $sys_docs_host $sys_lists_host
+ $sys_dns1_host $sys_dns2_host $FTPINCOMING_DIR $FTPFILES_DIR
+ $sys_urlroot $sf_cache_dir $sys_name $sys_themeroot
+ $sys_news_group $sys_dbhost $sys_dbname $sys_dbuser $sys_dbpasswd
+ $sys_ldap_base_dn $sys_ldap_host $admin_login $admin_password
+ $server_admin $domain_name $newsadmin_groupid $statsadmin_groupid
+ $skill_list/ ;
+use vars qw/$pluginname/ ;
+
+sub is_lesser ( $$ ) ;
+sub is_greater ( $$ ) ;
+sub debug ( $ ) ;
+sub parse_sql_file ( $ ) ;
+
+require ("/usr/share/gforge/lib/include.pl") ; # Include a few predefined functions
+require ("/usr/share/gforge/lib/sqlparser.pm") ; # Our magic SQL parser
+
+debug "You'll see some debugging info during this installation." ;
+debug "Do not worry unless told otherwise." ;
+
+&db_connect ;
+
+# debug "Connected to the database OK." ;
+
+$pluginname = "mailman" ;
+
+$dbh->{AutoCommit} = 0;
+$dbh->{RaiseError} = 1;
+eval {
+ my ($sth, @array, $version, $path, $target) ;
+
+ &create_metadata_table ("0") ;
+
+ $version = &get_db_version ;
+ $target = "0.1" ;
+ if (is_lesser $version, $target) {
+ my @filelist = ( "/usr/share/gforge/plugins/$pluginname/lib/$pluginname-init.sql" ) ;
+
+ foreach my $file (@filelist) {
+ debug "Processing $file" ;
+ @reqlist = @{ &parse_sql_file ($file) } ;
+
+ foreach my $s (@reqlist) {
+ $query = $s ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+ }
+ @reqlist = () ;
+
+ &update_db_version ($target) ;
+ debug "Committing." ;
+ $dbh->commit () ;
+ }
+
+ $version = &get_db_version ;
+ $target = "0.2" ;
+ if (is_lesser $version, $target) {
+ debug "Adding local data." ;
+
+ do "/etc/gforge/local.pl" or die "Cannot read /etc/gforge/local.pl" ;
+
+ my $ip_address = qx/host $domain_name | awk '{print \}'/ ;
+
+ @reqlist = (
+ "INSERT INTO plugin_".$pluginname."_sample_data (domain, ip_address) VALUES ('$domain_name', '$ip_address')",
+ ) ;
+
+ foreach my $s (@reqlist) {
+ $query = $s ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+ @reqlist = () ;
+
+ &update_db_version ($target) ;
+ debug "Committing." ;
+ $dbh->commit () ;
+ }
+
+ debug "It seems your database install/upgrade went well and smoothly. That's cool." ;
+ debug "Please enjoy using Debian GForge." ;
+
+ # There should be a commit at the end of every block above.
+ # If there is not, then it might be symptomatic of a problem.
+ # For safety, we roll back.
+ $dbh->rollback ();
+};
+
+if ($@) {
+ warn "Transaction aborted because $@" ;
+ debug "Transaction aborted because $@" ;
+ debug "Last SQL query was:\n$query\n(end of query)" ;
+ $dbh->rollback ;
+ debug "Please report this bug on the Debian bug-tracking system." ;
+ debug "Please include the previous messages as well to help debugging." ;
+ debug "You should not worry too much about this," ;
+ debug "your DB is still in a consistent state and should be usable." ;
+ exit 1 ;
+}
+
+$dbh->rollback ;
+$dbh->disconnect ;
+
+sub is_lesser ( $$ ) {
+ my $v1 = shift || 0 ;
+ my $v2 = shift || 0 ;
+
+ my $rc = system "dpkg --compare-versions $v1 lt $v2" ;
+
+ return (! $rc) ;
+}
+
+sub is_greater ( $$ ) {
+ my $v1 = shift || 0 ;
+ my $v2 = shift || 0 ;
+
+ my $rc = system "dpkg --compare-versions $v1 gt $v2" ;
+
+ return (! $rc) ;
+}
+
+sub debug ( $ ) {
+ my $v = shift ;
+ chomp $v ;
+ print STDERR "$v\n" ;
+}
+
+sub create_metadata_table ( $ ) {
+ my $v = shift || "0" ;
+ my $tablename = "plugin_" .$pluginname . "_meta_data" ;
+ # Do we have the metadata table?
+
+ $query = "SELECT count(*) FROM pg_class WHERE relname = '$tablename' and relkind = 'r'";
+ # debug $query ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ # Let's create this table if we have it not
+
+ if ($array [0] == 0) {
+ debug "Creating $tablename table." ;
+ $query = "CREATE TABLE $tablename (key varchar primary key, value text not null)" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+
+ $query = "SELECT count(*) FROM $tablename WHERE key = 'db-version'";
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ # Empty table? We'll have to fill it up a bit
+
+ if ($array [0] == 0) {
+ debug "Inserting first data into $tablename table." ;
+ $query = "INSERT INTO $tablename (key, value) VALUES ('db-version', '$v')" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub update_db_version ( $ ) {
+ my $v = shift or die "Not enough arguments" ;
+ my $tablename = "plugin_" .$pluginname . "_meta_data" ;
+
+ debug "Updating $tablename table." ;
+ $query = "UPDATE $tablename SET value = '$v' WHERE key = 'db-version'" ;
+ # debug $query ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+}
+
+sub get_db_version () {
+ my $tablename = "plugin_" .$pluginname . "_meta_data" ;
+
+ $query = "SELECT value FROM $tablename WHERE key = 'db-version'" ;
+ # debug $query ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ my $version = $array [0] ;
+
+ return $version ;
+}
+
+sub drop_table_if_exists ( $ ) {
+ my $tname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$tname' AND relkind='r'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping table $tname" ;
+ $query = "DROP TABLE $tname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_sequence_if_exists ( $ ) {
+ my $sname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$sname' AND relkind='S'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping sequence $sname" ;
+ $query = "DROP SEQUENCE $sname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_index_if_exists ( $ ) {
+ my $iname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$iname' AND relkind='i'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping index $iname" ;
+ $query = "DROP INDEX $iname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub drop_view_if_exists ( $ ) {
+ my $iname = shift or die "Not enough arguments" ;
+ $query = "SELECT count(*) FROM pg_class WHERE relname='$iname' AND relkind='v'" ;
+ my $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ my @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+
+ if ($array [0] != 0) {
+ # debug "Dropping view $iname" ;
+ $query = "DROP VIEW $iname" ;
+ # debug $query ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ $sth->finish () ;
+ }
+}
+
+sub bump_sequence_to ( $$ ) {
+ my ($sth, @array, $seqname, $targetvalue) ;
+
+ $seqname = shift ;
+ $targetvalue = shift ;
+
+ do {
+ $query = "select nextval ('$seqname')" ;
+ $sth = $dbh->prepare ($query) ;
+ $sth->execute () ;
+ @array = $sth->fetchrow_array () ;
+ $sth->finish () ;
+ } until $array[0] >= $targetvalue ;
+}
--- /dev/null
+<?php
+
+/**
+ *
+ * This file is part of GForge.
+ *
+ * GForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+global $gfplugins;
+require_once $gfplugins.'mailman/include/mailmanPlugin.class.php' ;
+
+$mailmanPluginObject = new mailmanPlugin() ;
+
+register_plugin ($mailmanPluginObject) ;
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+#! /usr/bin/php5 -f
+<?php
+/**
+ * FusionForge source control management
+ *
+ * Copyright 2009, Roland Mas
+ *
+ * This file is part of FusionForge.
+ *
+ * FusionForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * FusionForge is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FusionForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+require 'env.inc.php';
+require_once $gfwww.'include/squal_pre.php';
+require_once $gfcommon.'include/cron_utils.php';
+
+// MailingList backend class
+require_once $gfplugins.'mailman/include/BackendMailmanList.class.php' ;
+
+
+$res = db_query_params ('SELECT id,type, parameters FROM system_event WHERE status=$1 ORDER BY id DESC',
+ array ('1'));
+if (!$res) {
+ printf('Unable to get list of events: '.db_error());
+ return false;
+}
+
+while ($data = db_fetch_array ($res)) {
+ if($data['type'] == 'MAILMAN_LIST_CREATE') {
+ BackendMailmanList::instance()->createList($data['parameters']);
+ } elseif ($data['type'] == 'MAILMAN_LIST_DELETE') {
+ BackendMailmanList::instance()->deleteList($data['parameters']);
+ }
+ $events[$data['id']]=$data['parameters'];
+ echo "events[".$data['id']."]=".$data['parameters'];
+}
+if(isset($events)) {
+ foreach($events as $event_id => $list_id) {
+ $sql = "UPDATE system_event SET end_date='".time()."', log='DONE', status='3' WHERE id='".$event_id."';";
+ $result = db_query($sql);
+ if (!$result) {
+ printf('Unable to update the list of events: '.db_error());
+ return false;
+ }
+ $sql = "UPDATE mail_group_list SET status='3' WHERE group_list_id='".$list_id."';";
+ $result = db_query($sql);
+ if (!$result) {
+ printf('Unable to update the list of events: '.db_error());
+ return false;
+ }
+ }
+
+}
+
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+##
+## Sql Install Script
+##
+ CREATE TABLE IF NOT EXISTS `plugin_mailman` (
+listname varchar( 100 ) NOT NULL ,
+address varchar( 255 ) NOT NULL ,
+hide enum( 'Y', 'N' ) NOT NULL default 'N',
+nomail enum( 'Y', 'N' ) NOT NULL default 'N',
+ack enum( 'Y', 'N' ) NOT NULL default 'Y',
+not_metoo enum( 'Y', 'N' ) NOT NULL default 'Y',
+digest enum( 'Y', 'N' ) NOT NULL default 'N',
+plain enum( 'Y', 'N' ) NOT NULL default 'N',
+PASSWORD varchar( 255 ) NOT NULL default '!',
+lang varchar( 255 ) NOT NULL default 'en',
+name varchar( 255 ) default NULL ,
+one_last_digest enum( 'Y', 'N' ) NOT NULL default 'N',
+user_options bigint( 20 ) NOT NULL default 0,
+delivery_status INT( 10 ) NOT NULL default 0,
+topics_userinterest varchar( 255 ) default NULL ,
+delivery_status_timestamp datetime default '0000-00-00 00:00:00',
+bi_cookie varchar( 255 ) default NULL ,
+bi_score double NOT NULL default '0',
+bi_noticesleft double NOT NULL default '0',
+bi_lastnotice date NOT NULL default '0000-00-00',
+bi_date date NOT NULL default '0000-00-00'
+);
+INSERT INTO service(group_id, label, description, short_name, link, is_active, is_used, scope, rank) VALUES ( 100 , 'Mailman' , 'Manage mailing lists' , 'mailman', '/plugins/mailman/index.php?group_id=$group_id', 1 , 0 , 'system', 230 );
+
+
--- /dev/null
+CREATE TABLE plugin_mailman (
+ listname character varying(100) NOT NULL,
+ address character varying(255) NOT NULL,
+ hide character varying(255) NOT NULL default 'N',
+ nomail character varying(255) NOT NULL default 'N',
+ ack character varying(255) NOT NULL default 'Y',
+ not_metoo character varying(255) NOT NULL default 'Y',
+ digest character varying(255) NOT NULL default 'N',
+ plain character varying(255) NOT NULL default 'N',
+ password character varying(255) NOT NULL default '!',
+ lang character varying(255) NOT NULL default 'en',
+ name character varying(255) default NULL,
+ one_last_digest character varying(255) NOT NULL default 'N',
+ user_options bigint NOT NULL default 0,
+ delivery_status integer NOT NULL default 0,
+ topics_userinterest character varying(255) default NULL,
+ delivery_status_timestamp timestamp without time zone default '1901-01-01 01:01:01',
+ bi_cookie character varying(255) default NULL,
+ bi_score double precision NOT NULL default '0',
+ bi_noticesleft double precision NOT NULL default '0',
+ bi_lastnotice date NOT NULL default '1901-01-01',
+ bi_date date NOT NULL default '1901-01-01',
+ PRIMARY KEY (listname, address)
+);
--- /dev/null
+##
+## Sql Uninstall Script
+##
+
+DELETE FROM service WHERE short_name='mailman';
+
--- /dev/null
+fusionforge-plugin-mailman for Debian
+---------------------
+
+See README.Debian in fusionforge-common package
+
+ -- Christian Bayle <bayle@debian.org> Mon, 08 Mar 2010 16:37:51 +0100
--- /dev/null
+mailman for Debian
+---------------------
+
+<this file describes information about the source package, see Debian policy
+manual section 4.14. You WILL either need to modify or delete this file>
+
+
+
+
--- /dev/null
+fusionforge-plugin-mailman (1.0-1) unstable; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Christian Bayle <bayle@debian.org> Mon, 08 Mar 2010 16:37:51 +0100
--- /dev/null
+Source: fusionforge-plugin-mailman
+Section: devel
+Priority: optional
+Maintainer: Christian Bayle <bayle@debian.org>
+Uploaders: Roland Mas <lolando@debian.org>
+Build-Depends-Indep: devscripts
+Build-Depends: debhelper (>= 7), perl, gettext
+Standards-Version: 3.8.4
+Homepage: http://fusionforge.org/
+Vcs-Bzr: http://scm.fusionforge.org/bzr/fusionforge/svn-trunk-ro/
+
+Package: fusionforge-plugin-mailman
+Architecture: all
+Depends: gforge-common, gforge-db-postgresql | gforge-db, gforge-web-apache2 | gforge-web, gforge-shell-postgresql | gforge-shell, php5-cli, ${misc:Depends}
+Description: collaborative development tool - Helloworld plugin
+ .
+ This plugin contains the Helloworld subsystem of FusionForge. It allows each
+ FusionForge project to have its own Helloworld, and gives some
+ control over it to the project's administrator.
+
--- /dev/null
+The "sourceforge" package was first debianised on Wed, 22 Nov 2000
+22:06:35 +0100 by Roland Mas <lolando@debian.org>. Work has been
+constant since then, and the package evolved a great deal. It began
+to work, for a start, and then it evolved into GForge, what with the
+more recent versions of the Sourceforge software having been made
+proprietary. And then in early 2009 it evolved into FusionForge, what
+with the more recent versions of GForge having been rewritten as
+proprietary software without a name change.
+
+FusionForge is Copyright © 2009-2010 by several people, including:
+Alain Peyrat <aljeux@free.fr>
+Alexandre Neymann <alexandre.neymann@dgfip.finances.gouv.fr>
+Christian Bayle <bayle@debian.org>
+Roland Mas <lolando@debian.org>
+Gonéri Le Bouder <goneri.lebouder@atosorigin.com>
+Julien Heyman <julien.heyman@atosorigin.com>
+Olivier Meunier <olivier.meunier@centraliens.net>
+
+GForge itself is Copyright © 2000-2008 by a fair number of people,
+including:
+Tim Perdue <tim@gforge.org>
+Roland Mas <99.roland.mas@aist.enst.fr>
+Christian Bayle <gforge@free.fr>
+Tom Copeland <tom@infoether.com>
+Guillaume Smet <guillaume-gforge@smet.org>
+Francisco Gimeno <kikov@kikov.org>
+Sung Kim <hunkims@gmail.com>
+Alain Peyrat <aljeux@free.fr>
+
+Past Members of the development team include:
+Reinhard Spisser
+Ryan T. Sammartino
+Edward Ritter
+Michael Jennings
+
+Since FusionForge, and GForge before it, was initially a fork of
+Sourceforge, here's the copyright info for the Sourceforge software:
+,----
+| The original sources were downloaded from http://www.sourceforge.net/
+|
+| Authors: The Sourceforge crew at VA Linux. They are many, they
+| change as time goes by, and they are listed on the Sourceforge
+| website. Let them be thanked for their work.
+|
+| Copyright:
+|
+| This software is Copyright © 1999-2000 by VA Linux.
+|
+| You are free to distribute this software under the terms of the GNU
+| General Public License.
+`----
+
+The packaging and installing scripts (in the debian/ and deb-specific/
+directories amongst others) are Copyright © 2000-2010 by Christian
+Bayle <bayle@aist.enst.fr> and Roland Mas <lolando@debian.org>. You
+are free to use and redistribute them under the terms of the GNU
+General Public License, version 2 or (at your option) any later
+version published by the Free Software Foundation.
+
+See Also Copyright file in fusionforge package
+
+On Debian systems, the complete text of the GNU General Public License
+can be found in the /usr/share/common-licenses directory.
--- /dev/null
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+SRCPACKAGE=$(shell head -1 $(CURDIR)/debian/changelog | sed 's/\(.[^ ]*\) .*/\1/')
+MAJOR=$(shell head -1 $(CURDIR)/debian/changelog | sed 's/.*(\(.*\)-[^-]*).*/\1/')
+ORIGFILE=$(SRCPACKAGE)_$(MAJOR).orig.tar.gz
+OLDPACKAGE=gforge
+PACKAGE=fusionforge
+FORGENAME=FusionForge
+
+SED_REPLACE=sed -e 's/@PACKAGE@/$(PACKAGE)/g' -e 's/@SRCPACKAGE@/$(SRCPACKAGE)/g' -e 's/@FORGENAME@/$(FORGENAME)/g' -e 's/@OLDPACKAGE@/$(OLDPACKAGE)/g'
+
+CRONDFILES=$(patsubst packaging/cron.d/%,debian/$(PACKAGE)-%.cron.d,$(wildcard packaging/cron.d/[a-z]*))
+DIRSFILES=$(patsubst packaging/dirs/%,debian/$(PACKAGE)-%.dirs,$(wildcard packaging/dirs/[a-z]*))
+LINKSFILES=$(patsubst packaging/links/%,debian/$(PACKAGE)-%.links,$(wildcard packaging/links/[a-z]*))
+INSTALLFILES=$(patsubst packaging/install/%,debian/$(PACKAGE)-%.install,$(wildcard packaging/install/[a-z]*))
+DOCSFILES=$(patsubst packaging/docs/%,debian/$(PACKAGE)-%.docs,$(wildcard packaging/docs/[a-z]*))
+POFILES=$(patsubst debian/dsf-po/%,debian/po/%,$(wildcard debian/dsf-po/*) debian/po/POTFILES.in)
+
+.PHONY: conffiles
+conffiles: $(CRONDFILES) $(DIRSFILES) $(LINKSFILES) $(INSTALLFILES) $(DOCSFILES) debian/control
+
+debian/$(PACKAGE)-%.cron.d:
+ (cat $(CURDIR)/packaging/cron.d/00phpcron ; sed -e 's/\$$FFUSER/$(PACKAGE)/g' $(CURDIR)/packaging/cron.d/$*) > $@
+
+debian/$(PACKAGE)-%.dirs:
+ cp $(CURDIR)/packaging/dirs/$* $@
+
+debian/$(PACKAGE)-%.links:
+ cp $(CURDIR)/packaging/links/$* $@
+
+debian/$(PACKAGE)-%.install:
+ cp $(CURDIR)/packaging/install/$* $@
+
+debian/$(PACKAGE)-%.docs:
+ cp $(CURDIR)/packaging/docs/$* $@
+
+debian/control: $(wildcard packaging/control/*)
+ ls $(CURDIR)/packaging/control/[0-9][0-9][0-9]* | grep -v shortdesc | grep -v scmcpold | while read file ; do cat $${file}; if [ -f $${file}.shortdesc ] ; then cat $(CURDIR)/packaging/control/AAAdesc; echo ' .'; cat $${file}.shortdesc; fi; echo ''; done | $(SED_REPLACE) > $@
+
+debian/po/templates.pot: $(wildcard debian/dsf-in/*.templates.dsfh-in) $(wildcard debian/dsf-helper/*.templates)
+ @debconf-updatepo --podir=debian/dsf-po
+
+debian/po/%:
+ cat $(patsubst debian/po/%,debian/dsf-po/%,$@) | $(SED_REPLACE) > $@
+
+build: debian/po/templates.pot conffiles
+ dh $@
+ # Build gettext *.mo files
+ utils/manage-translations.sh build
+
+clean:
+ dh $@
+ rm -f $(CURDIR)/debian/*.cron.d
+ rm -f $(CURDIR)/debian/*.dirs
+ rm -f $(CURDIR)/debian/*.links
+ rm -f $(CURDIR)/debian/*.install
+ rm -f $(CURDIR)/debian/*.docs
+ rm -rf locales
+ rm -f $(POFILES)
+
+makeorig:
+ find $(PKGDIR) -type f -or -type l | grep -v '/CVS/' | \
+ grep -v '/.svn/' | grep -v '/.testbox/' | \
+ grep -v rpm-specific | grep -v docs/phpdoc/docs | \
+ grep -v ^./debian/ | grep -v \\.jar$$ | \
+ grep -v \\.pdf$$ | grep -v plugins/fckeditor/www/_samples | \
+ cpio -o -H ustar | gzip > ../$(ORIGFILE)
+
+%:
+ dh $@
--- /dev/null
+<?php
+$GLOBALS['mailman_bin_dir'] = $GLOBALS['sys_path_to_mailman'].'/bin';
+$GLOBALS['mailman_list_dir'] = '/var/lib/mailman/lists';
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+require_once('common/event/Event.class.php');
+require_once 'mailman/include/MailmanListDao.class.php';
+require_once 'common/dao/CodendiDataAccess.class.php';
+require_once 'mailman/include/MailmanList.class.php';
+require_once 'plugins_utils.php';
+
+class BackendMailmanList {
+
+ protected $_mailinglistdao = null;
+
+ /**
+ * Hold an instance of the class
+ */
+ protected static $_instance;
+
+ /**
+ * Backends are singletons
+ */
+ public static function instance() {
+
+ if (!isset(self::$_instance)) {
+ $c = __CLASS__;
+ self::$_instance = new $c;
+ }
+ return self::$_instance;
+ }
+
+ /**
+ * @return MailingListDao
+ */
+ protected function _getMailingListDao() {
+ if (!$this->_mailinglistdao) {
+ $this->_mailinglistdao = new MailmanListDao(CodendiDataAccess::instance());
+ }
+ return $this->_mailinglistdao;
+ }
+
+
+ /**
+ * Update mailman configuration for the given list
+ * Write configuration in temporary file, and load it with mailman config_list tool
+ * @return true on success, false otherwise
+ */
+ protected function updateListConfig($list) {
+ // write configuration in temporary file
+ $config_file=$GLOBALS['tmp_dir']."/mailman_config_".$list->getID().".in";
+
+ if ($fp = fopen($config_file, 'w')) {
+ // Define encoding of this file for Python. See SR #764
+ // Please note that this allows config_list to run with UTF-8 strings, but if the
+ // description contains non-ascii chars, they will be displayed badly in mailman config web page.
+ fwrite($fp, "# coding=UTF-8\n\n");
+ // Deactivate monthly reminders by default
+ fwrite($fp, "send_reminders = 0\n");
+ // Setup the description
+ fwrite($fp, "description = '".addslashes($list->getDescription())."'\n");
+ // Allow up to 200 kB messages
+ fwrite($fp, "max_message_size = 200\n");
+
+ if ($list->isPublic() == 0) { // Private lists
+ // Don't advertise this list when people ask what lists are on this machine
+ fwrite($fp, "advertised = False\n");
+ // Private archives
+ fwrite($fp, "archive_private = 1\n");
+ // Subscribe requires approval
+ fwrite($fp, "subscribe_policy = 2\n");
+ }
+ fclose($fp);
+
+ if (system($GLOBALS['mailman_bin_dir']."/config_list -i $config_file ".$list->getName()) !== false) {
+ if (unlink($config_file)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Create new mailing list with mailman 'newlist' tool
+ * then update the list configuration according to list settings
+ * @return true on success, false otherwise
+ */
+ public function createList($group_list_id) {
+
+ $dar = $this->_getMailingListDao()->searchByGroupListId($group_list_id);
+
+ if ($row = $dar->getRow()) {
+ echo "j'ai une row";
+ $list = new MailmanList($row['group_id'],$row['group_list_id']);
+ $user=UserManager::instance()->getUserByID($list->getListAdminId());
+ $list_admin_email= $user->getEmail();
+ $list_dir = $GLOBALS['mailman_list_dir']."/".$list->getName();
+
+ if ((! is_dir($list_dir))&&($list->isPublic() != 9)) {
+ // Create list
+ echo "execution system";
+ system($GLOBALS['mailman_bin_dir']."/newlist -q ".$list->getName()." ".$list_admin_email." ".$list->getPassword()." >/dev/null");
+ echo "ok pour system";
+ //$list->update($list->getDescription(),$list->isPublic(),'1');
+ echo "update done";
+ // Then update configuraion
+ return $this->updateListConfig($list);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Delete mailing list
+ * - list and archives are deleted
+ * - backup first in temp directory
+ * @return true on success, false otherwise
+ */
+ public function deleteList($group_list_id) {
+ $dar = $this->_getMailingListDao()->searchByGroupListId($group_list_id);
+
+ if ($row = $dar->getRow()) {
+ $list=new MailmanList($row['group_id'],$group_list_id);
+ $list_dir = $GLOBALS['mailman_list_dir']."/".$list->getName();
+ if ((is_dir($list_dir))&&($list->isPublic() == 9)) {
+
+ // Archive first
+ $list_archive_dir = $GLOBALS['mailman_list_dir']."/../archives/private/".$list->getName(); // Does it work? TODO
+ $backupfile=$GLOBALS['tmp_dir']."/".$list->getName()."-mailman.tgz";
+ system("tar cfz $backupfile $list_dir $list_archive_dir");
+ chmod($backupfile,0600);
+
+ // Delete the mailing list if asked to and the mailing exists (archive deleted as well)
+ system($GLOBALS['mailman_bin_dir']. '/rmlist -a '. $list->getName() .' >/dev/null');
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if the list exists on the file system
+ * @return true if list exists, false otherwise
+ */
+ public function listExists($list) {
+ // Is this the best test?
+ $list_dir = $GLOBALS['mailman_list_dir']."/".$list->getName();
+ if (! is_dir($list_dir)) return false;
+ return true;
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * GForge Mailing Lists Facility
+ *
+ * Copyright 2003 Guillaume Smet
+ * http://gforge.org/
+ *
+ * @version $Id$
+ *
+ * This file is part of GForge.
+ *
+ * GForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+
+/*
+
+ This work is based on Tim Perdue's work on the forum stuff
+
+ */
+
+require_once 'MailmanListDao.class.php';
+require_once 'common/dao/CodendiDataAccess.class.php';
+require_once 'common/system_event/SystemEventManager.class.php';
+require_once 'common/system_event/SystemEvent.class.php';
+
+class MailmanList extends Error {
+ /**
+ * DAO
+ *
+ * @var MailingListDao $mailingDAO.
+ */
+ var $_mailingDAO;
+
+ /**
+ * Associative array of data from db.
+ *
+ * @var array $dataArray.
+ */
+ var $dataArray;
+
+ /**
+ * The Group object.
+ *
+ * @var object $Group.
+ */
+ var $Group;
+
+ /**
+ * The mailing list id
+ *
+ * @var int $groupMailmanListId
+ */
+ var $groupMailmanListId;
+
+ /**
+ * Constructor.
+ *
+ * @param object The Group object to which this mailing list is associated.
+ * @param int The group_list_id.
+ * @param array The associative array of data.
+ * @return boolean success.
+ */
+ function MailmanList($group_id, $groupListId = false, $dataArray = false) {
+ $pm = ProjectManager::instance();
+ $Group = $pm->getProject($group_id);
+ $this->_mailingDAO =& new MailmanListDao(CodendiDataAccess::instance());
+ $this->Error();
+ if (!$Group || !is_object($Group)) {
+ $this->setError(sprintf(_('%1$s:: No Valid Group Object'), 'MailmanList'));
+ return false;
+ }
+ if ($Group->isError()) {
+ $this->setError('MailmanList:: '.$Group->getErrorMessage());
+ return false;
+ }
+ $this->Group =& $Group;
+
+ if ($groupListId) {
+ $this->groupMailmanListId = $groupListId;
+ if (!$dataArray || !is_array($dataArray)) {
+ if (!$this->fetchData($groupListId)) {
+ return false;
+ }
+ } else {
+ $this->dataArray =& $dataArray;
+ if ($this->dataArray['group_id'] != $this->Group->getID()) {
+ $this->setError(_('Group_id in db result does not match Group Object'));
+ $this->dataArray = null;
+ return false;
+ }
+ }
+ if (!$this->isPublic()) {
+ if (!isLogged()) {
+ $this->dataArray = null;
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * create - use this function to create a new entry in the database.
+ *
+ * @param string The name of the mailing list
+ * @param string The description of the mailing list
+ * @param int Pass (1) if it should be public (0) for private.
+ *
+ * @return boolean success.
+ **/
+
+ function create($listName, $description, $isPublic = '1',$creator_id=false) {
+ $current_user=UserManager::instance()->getCurrentUser();
+ //
+ // During the group creation, the current user_id will not match the admin's id
+ //
+ if (!$creator_id) {
+ $creator_id=$current_user->getID();
+ if(!$current_user->isMember($this->Group->getID(),'A')) {
+ exit_permission_denied();
+ return false;
+ }
+ }
+
+ if(!$listName || strlen($listName) < 4) {
+ $this->setError(_('Must Provide List Name That Is 4 or More Characters Long'));
+ return false;
+ }
+
+ $realListName = strtolower($this->Group->getUnixName().'-'.$listName);
+
+ if(!validate_email($realListName.'@'.$GLOBALS['sys_lists_host'])) {
+ $this->setError(_('Invalid List Name') . ': ' .
+ $realListName.'@'.$GLOBALS['sys_lists_host']);
+ return false;
+ }
+ $result=&$this->_mailingDAO->searchByName($realListName);
+
+ if ($result->valid()) {
+ $this->setError(_('List Already Exists'));
+ return false;
+ }
+
+
+ $listPassword = substr(md5($GLOBALS['session_hash'] . time() . rand(0,40000)), 0, 16);
+ $result = $this->_mailingDAO->insertNewList($this->Group->getID(), $realListName,$isPublic,$listPassword,$creator_id,'1',$description);
+ if (!$result) {
+ $this->setError(sprintf(_('Error Creating %1$s'), _('Error Creating %1$s')).db_error());
+ return false;
+ }
+
+ $this->groupMailmanListId = $result;
+ // Raise an event
+ require_once('mailman/include/events/SystemEvent_MAILMAN_LIST_CREATE.class.php');
+ $systemevent = SystemEventManager::instance();
+ $systemevent->createEvent('MAILMAN_LIST_CREATE', array('group_list_id' => $this->groupMailmanListId,),SystemEvent::PRIORITY_MEDIUM);
+ $this->fetchData($this->groupMailmanListId);
+ $user=UserManager::instance()->getUserByID($creator_id);
+
+ $userEmail = $user->getEmail();
+ if(empty($userEmail) || !validate_email($userEmail)) {
+ $this->setInvalidEmailError();
+ return false;
+ } else {
+ sendCreationMail($userEmail,$realListName,$listPassword);
+ }
+
+ return true;
+ }
+
+ /**
+ * fetchData - re-fetch the data for this mailing list from the database.
+ *
+ * @param int The list_id.
+ * @return boolean success.
+ */
+ function fetchData($groupListId) {
+ $res =& $this->_mailingDAO->searchListFromGroup($groupListId, $this->Group->getID());
+
+ if (!$res) {
+ $this->setError(sprintf(_('Error Getting %1$s'), _('Error Getting %1$s')));
+ return false;
+ }
+ $this->dataArray =& $res->getRow();
+ return true;
+ }
+
+ /**
+ * update - use this function to update an entry in the database.
+ *
+ * @param string The description of the mailing list
+ * @param int Pass (1) if it should be public (0) for private
+ * @return boolean success.
+ */
+ function update($description, $isPublic ='1',$status='1') {
+ $current_user=UserManager::instance()->getCurrentUser();
+ if(!$current_user->isMember($this->Group->getID(),'A')) {
+ exit_permission_denied();
+ return false;
+ }
+ $res = $this->_mailingDAO->updateList($this->groupMailmanListId, $this->Group->getID(),$description , $isPublic,$status);
+ if (!$res) {
+ $this->setError(_('Error On Update:').db_error());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * getGroup - get the Group object this mailing list is associated with.
+ *
+ * @return object The Group object.
+ */
+ function &getGroup() {
+ return $this->Group;
+ }
+
+ /**
+ * getID - The id of this mailing list
+ *
+ * @return int The group_list_id #.
+ */
+ function getID() {
+ return $this->dataArray['group_list_id'];
+ }
+
+
+ /**
+ * isPublic - Is this mailing list open to the general public.
+ *
+ * @return boolean is_public.
+ */
+ function isPublic() {
+ return $this->dataArray['is_public'];
+ }
+
+ /**
+ * getName - get the name of this mailing list
+ *
+ * @return string The name of this mailing list
+ */
+ function getName() {
+ return $this->dataArray['list_name'];
+ }
+
+
+ /**
+ * getDescription - get the description of this mailing list
+ *
+ * @return string The description.
+ */
+ function getDescription() {
+ return $this->dataArray['description'];
+ }
+
+ /**
+ * getPassword - get the password to administrate the mailing list
+ *
+ * @return string The password
+ */
+ function getPassword() {
+ return $this->dataArray['password'];
+ }
+
+ /**
+ * getListAdminID - get the user id who is the admin of this mailing list
+ *
+ * @return id The admin user
+ */
+ function getListAdminID() {
+ return $this->dataArray['list_admin'];
+ }
+
+ /**
+ * getStatus - get the status of this mailing list
+ *
+ * @return int The status
+ */
+ function getStatus() {
+ return $this->dataArray['status'];
+ }
+
+ /**
+ * getArchivesUrl - get the url to see the archives of the list
+ *
+ * @return string url of the archives
+ */
+ function getArchivesUrl() {
+ if ($this->isPublic()) {
+ $iframe_url = 'http://'.$GLOBALS['sys_lists_host'].'/pipermail/'.$this->getName().'/';
+ } else {
+ $iframe_url = 'http://'.$GLOBALS['sys_lists_host'].'/mailman/private/'.$this->getName().'/';
+ }
+ htmlIframe($iframe_url, array('class' => 'iframe_service'));
+ }
+
+ /**
+ * getExternalInfoUrl - get the url to subscribe/unsubscribe
+ *
+ * @return string url of the info page
+ */
+ function getExternalInfoUrl() {
+ return 'http://'.$GLOBALS['sys_lists_host'].'/mailman/listinfo/'.$this->getName();
+ }
+ /**
+ * getOptionsUrl - get the url to manage options for user
+ *
+ * @return string url of the info page
+ */
+ function getOptionsUrl() {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $user=$current_user->getEmail();
+ $iframe_url = 'http://'.$GLOBALS['sys_lists_host'].'/mailman/options/'.$this->getName().'/'.$user;
+ htmlIframe($iframe_url, array('class' => 'iframe_service'));
+ }
+ /**
+ * subscribeUrl - add the user to the mailinglist
+ *
+ * @return string url of the info page
+ */
+ function subscribe() {
+ if(isLogged() && $this->isPublic() && !$this->isMonitoring())
+ {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $user=$current_user->getEmail();
+ $passwd= $current_user->getUserPw();
+ $name= $current_user->getRealName();
+ $res = $this->_mailingDAO->newSubscriber($user,$name,$passwd,$this->getName());
+ if (!$res) {
+ $this->setError(_('Error On Update:').db_error());
+ return false;
+ }
+ htmlRedirect('index.php?group_id='.$this->Group->getId());
+ }
+
+ }
+ /**
+ * unsubscribeUrl - delete the user from the mailing list
+ *
+ * @return string url of the info page
+ */
+ function unsubscribe() {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $user=$current_user->getEmail();
+ $res = $this->_mailingDAO->deleteSubscriber($user,$this->getName());
+ if (!$res) {
+ $this->setError(_('Error On Update:').db_error());
+ return false;
+ }
+ htmlRedirect('index.php?group_id='.$this->Group->getId());
+ }
+ /**
+ * isMonitoring - See if the current user is in the list of people monitoring the forum.
+ *
+ * @return boolean is_monitoring.
+ */
+ function isMonitoring() {
+ if (!isLogged()) {
+ return false;
+ }
+ $current_user=UserManager::instance()->getCurrentUser();
+ $user=$current_user->getEmail();
+ $res = $this->_mailingDAO->userIsMonitoring($user,$this->getName());
+ if (!$res) {
+ $this->setError(_('Error On Query:').db_error());
+ return false;
+ }
+ $row_count = $res->getRow();
+ return $row_count['count'] > 0;
+ }
+
+
+
+ /**
+ * getExternalAdminUrl - get the url to admin the list with the external tools used
+ *
+ * @return string url of the admin
+ */
+
+ function getExternalAdminUrl() {
+ $iframe_url = 'http://'.$GLOBALS['sys_lists_host'].'/mailman/admin/'.$this->getName();
+ htmlIframe($iframe_url, array('class' => 'iframe_service'));
+ }
+
+ /**
+ * delete - permanently delete this mailing list
+ *
+ * @param boolean I'm Sure.
+ * @param boolean I'm Really Sure.
+ * @return boolean success;
+ */
+ function deleteList($sure,$really_sure) {
+ $current_user=UserManager::instance()->getCurrentUser();
+ if (!$sure || !$really_sure) {
+ $this->setError('Missing params');
+ return false;
+ }
+
+ if (!$current_user->isMember($this->Group->getID(),'A')) {
+ exit_permission_denied();
+ return false;
+ }
+ $res = $this->_mailingDAO->deleteList($this->Group->getID(),$this->getID());
+ if (!$res) {
+ $this->setError('Could Not Delete List: '.db_error());
+ return false;
+ }
+ require_once('mailman/include/events/SystemEvent_MAILMAN_LIST_DELETE.class.php');
+ $systemevent = SystemEventManager::instance();
+ $systemevent->createEvent('MAILMAN_LIST_DELETE', array('group_list_id' => $this->groupMailmanListId,),SystemEvent::PRIORITY_MEDIUM);
+
+
+ return true;
+
+ }
+
+}
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+require_once 'common/dao/include/DataAccessObject.class.php';
+
+/**
+ * Data Access Object for mailing lists
+ */
+class MailmanListDao extends DataAccessObject {
+
+ public function __construct($da) {
+ parent::__construct($da);
+ }
+
+
+ /**
+ * Search active (=not deteted) mailing lists
+ * return all active lists
+ * @return DataAccessResult
+ */
+ function & searchAllActiveML() {
+ $sql = "SELECT *
+ FROM mail_group_list
+ WHERE is_public IN (0,1)";
+ return $this->retrieve($sql,array());
+ }
+
+ /**
+ * Searches by group_list_id
+ * @return DataAccessResult
+ */
+ function & searchByGroupListId($group_list_id) {
+ $group_list_id = $this->da->quoteSmart($group_list_id);
+ $sql = "SELECT * FROM mail_group_list
+ WHERE group_list_id = $1";
+ return $this->retrieve($sql,array($group_list_id));
+ }
+ /**
+ * Searches by list_name
+ * @return DataAccessResult
+ */
+ function & searchByName($realListName) {
+ $realListName = $this->da->quoteSmart($realListName);
+ $sql = 'SELECT 1 FROM mail_group_list WHERE lower(list_name)=$1';
+ return $this->retrieve($sql,array($realListName));
+ }
+ /**
+ * Searches by group_id
+ * @return DataAccessResult
+ */
+ function & searchByGroupId($group_id) {
+ $group_id = $this->da->quoteSmart($group_id);
+ $sql = "SELECT * FROM mail_group_list
+ WHERE group_id = $1";
+ return $this->retrieve($sql,array($group_id));
+ }
+ /**
+ * Searches data with group_list_id and group_id
+ * @return DataAccessResult
+ */
+ function & searchListFromGroup($group_list_id,$group_id) {
+ $group_id = $this->da->quoteSmart($group_id);
+ $group_list_id = $this->da->quoteSmart($group_list_id);
+ $sql = "SELECT * FROM mail_group_list
+ WHERE group_id = $1 AND group_list_id=$2";
+ return $this->retrieve($sql,array($group_id,$group_list_id));
+ }
+
+ function & insertNewList($group_id, $realListName,$isPublic,$listPassword,$creator_id,$requested,$description) {
+ $group_id = $this->da->quoteSmart($group_id);
+ $group_list_id = $this->da->quoteSmart($group_list_id);
+ $realListName = $this->da->quoteSmart($realListName);
+ $isPublic = $this->da->quoteSmart($isPublic);
+ $creator_id = $this->da->quoteSmart($creator_id);
+ $requested = $this->da->quoteSmart($requested);
+ $description = $this->da->quoteSmart($description);
+ $sql = "INSERT INTO mail_group_list (group_id, list_name, is_public, password, list_admin, status, description) VALUES ($1,$2,$3,$4,$5,$6,$7);";
+ return db_insertid($this->update($sql,array($group_id, $realListName,$isPublic,$listPassword,$creator_id,$requested,$description)),'mail_group_list','group_list_id');
+ }
+ function deleteList($group_id, $list_id) {
+ return $this->updateList($list_id,$group_id,"deleted",9,1);
+ }
+
+ function updateList($group_list_id,$group_id, $description, $isPublic,$status) {
+ $group_id = $this->da->quoteSmart($group_id);
+ $group_list_id = $this->da->quoteSmart($group_list_id);
+ $isPublic = $this->da->quoteSmart($isPublic);
+ $status = $this->da->quoteSmart($status);
+ $description = $this->da->quoteSmart($description);
+ $sql = "UPDATE mail_group_list SET is_public=$1, description=$2," .
+ "status=$3 WHERE group_list_id=$4 AND group_id=$5;";
+ return $this->update($sql,array($isPublic,$description,$status,$group_list_id,$group_id));
+
+ }
+
+ function & newSubscriber($usermail, $username, $userpasswd, $listname) {
+ $usermail = $this->da->quoteSmart($usermail);
+ $username = $this->da->quoteSmart($username);
+ $userpasswd = $this->da->quoteSmart($userpasswd);
+ $listname = $this->da->quoteSmart($listname);
+ $sql="INSERT INTO plugin_mailman (address,password,name,listname) VALUES ($1,$2,$3,$4);";
+ return $this->update($sql,array($usermail,$userpasswd,$username,$listname));
+ }
+
+ function & deleteSubscriber($usermail, $listname) {
+ $usermail = $this->da->quoteSmart($usermail);
+ $listname = $this->da->quoteSmart($listname);
+ $sql="DELETE FROM plugin_mailman WHERE listname=$1 AND address=$2;";
+ return $this->update($sql,array($listname,$usermail));
+ }
+ function & userIsMonitoring($usermail,$listname) {
+ $usermail = $this->da->quoteSmart($usermail);
+ $listname = $this->da->quoteSmart($listname);
+ $sql="SELECT count(*) AS count FROM plugin_mailman WHERE address=$1 AND listname=$2;";
+ return $this->retrieve($sql,array($usermail,$listname));
+ }
+
+ function & listsMonitoredByUser($usermail) {
+ $usermail = $this->da->quoteSmart($usermail);
+ $sql="SELECT groups.group_name,groups.group_id,mail_group_list.group_list_id,mail_group_list.list_name ".
+ "FROM groups,mail_group_list,plugin_mailman ".
+ "WHERE groups.group_id=mail_group_list.group_id AND groups.status ='A' ".
+ "AND mail_group_list.list_name=plugin_mailman.listname ".
+ "AND plugin_mailman.address=$1 ORDER BY group_name DESC";
+
+ return $this->retrieve($sql,array($usermail));
+ }
+
+ function & compareInfos($mail) {
+ $mail = $this->da->quoteSmart($mail);
+ $sql="SELECT password, name FROM plugin_mailman WHERE address=$1;";
+ return $this->retrieve($sql,array($mail));
+
+ }
+ function & updateInfos($mail,$passwd,$name) {
+ $mail = $this->da->quoteSmart($mail);
+ $passwd = $this->da->quoteSmart($passwd);
+ $name = $this->da->quoteSmart($name);
+ $sql="UPDATE plugin_mailman SET password=$1, name=$2 WHERE address=$3;";
+ return $this->update($sql,array($passwd,$name,$mail));
+
+ }
+}
+?>
--- /dev/null
+<?php
+/**
+ * GForge Mailing Lists Facility
+ *
+ * Copyright 2003 Guillaume Smet
+ * http://gforge.org/
+ *
+ * @version $Id$
+ *
+ * This file is part of GForge.
+ *
+ * GForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+ /*
+
+ This work is based on Tim Perdue's work on the forum stuff
+
+ */
+
+
+require_once 'MailmanList.class.php';
+require_once 'MailmanListDao.class.php';
+require_once 'common/dao/CodendiDataAccess.class.php';
+
+class MailmanListFactory extends Error {
+
+ /**
+ * The Group object.
+ *
+ * @var object $Group.
+ */
+ var $Group;
+
+ /**
+ * The mailing lists array.
+ *
+ * @var array $mailingLists.
+ */
+ var $mailingLists;
+ /**
+ * DAO
+ *
+ * @var MailingListDao $mailingDAO.
+ */
+ var $_mailingDAO;
+
+
+ /**
+ * Constructor.
+ *
+ * @param object The Group object to which these mailing lists are associated.
+ */
+ function MailmanListFactory(& $Group) {
+ $this->_mailingDAO =& new MailmanListDao(CodendiDataAccess::instance());
+ $this->Error();
+
+ if (!$Group || !is_object($Group)) {
+ $this->setError(sprintf(_('%1$s:: No Valid Group Object'), 'MailmanListFactory'));
+ return false;
+ }
+ if ($Group->isError()) {
+ $this->setError('MailmanListFactory:: '.$Group->getErrorMessage());
+ return false;
+ }
+ $this->Group =& $Group;
+
+ return true;
+ }
+
+ /**
+ * getGroup - get the Group object this MailmanListFactory is associated with.
+ *
+ * @return object The Group object.
+ */
+ function &getGroup() {
+ return $this->Group;
+ }
+
+ /**
+ * getMailmanLists - get an array of MailmanList objects for this Group.
+ *
+ * @param boolean $admin if we are in admin mode (we want to see deleted lists)
+ * @return array The array of MailmanList objects.
+ */
+ function &getMailmanLists() {
+ $current_user=UserManager::instance()->getCurrentUser();
+ if (isset($this->mailingLists) && is_array($this->mailingLists)) {
+ return $this->mailingLists;
+ }
+
+ if (islogged() && $current_user->isMember($this->Group->getID())) {
+ $public_flag='0,1';
+ } else {
+ $public_flag='1';
+ }
+ $result =& $this->_mailingDAO->searchByGroupId($this->Group->getID());
+
+
+ if (!$result) {
+ $this->setError(sprintf(_('Error Getting %1$s'), _('Error Getting %1$s')).db_error());
+ return false;
+ } else {
+ $this->mailingLists = array();
+ while ($arr =& $result->getRow()) {
+ $this->mailingLists[] = new MailmanList($this->Group->getID(), $arr['group_list_id'], $arr);
+ }
+ }
+ return $this->mailingLists;
+ }
+
+ /**
+ * compareInfos - replace mailman user info by forge user info
+ *
+ * @return string url of the info page
+ */
+ function compareInfos()
+ {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $mail=$current_user->getEmail();
+
+ $passwd= $current_user->getUserPw();
+ $name= $current_user->getRealName();
+ $result =& $this->_mailingDAO->compareInfos($mail);
+ if (!$result) {
+ return false;
+ }
+ else
+ {
+ while( $arr =& $result->getRow())
+ {
+ if($arr['password']!=$passwd || $arr['name']!=$name)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+
+ }
+ /**
+ * updateInfos - replace mailman user info by forge user info
+ *
+ * @return string url of the info page
+ */
+ function updateInfos()
+ {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $mail=$current_user->getEmail();
+
+ $passwd= $current_user->getUserPw();
+ $name= $current_user->getRealName();
+
+ $result =& $this->_mailingDAO->updateInfos($mail,$passwd,$name);
+ if (!$result) {
+ return false;
+ }
+ htmlRedirect('index.php?group_id='.$this->Group->getId());
+ return $result;
+ }
+
+
+}
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+require_once('common/plugin/PluginDescriptor.class.php');
+
+
+class MailmanPluginDescriptor extends PluginDescriptor {
+
+ function MailmanPluginDescriptor() {
+ $this->PluginDescriptor(_('Mailman'), 'v1.0', _('Offers a better integration of Mailman in the forge'));
+ }
+}
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once('common/plugin/PluginInfo.class.php');
+require_once('MailmanPluginDescriptor.class.php');
+
+class MailmanPluginInfo extends PluginInfo {
+
+ function MailmanPluginInfo(&$plugin) {
+ $this->PluginInfo($plugin);
+ $this->setPluginDescriptor(new MailmanPluginDescriptor());
+ }
+
+}
+?>
--- /dev/null
+<?php
+/**
+ * GForge Mails Facility
+ *
+ * Copyright 2002 GForge, LLC
+ * http://gforge.org/
+ *
+ * @version $Id$
+ *
+ * This file is part of GForge.
+ *
+ * GForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+require_once 'MailmanList.class.php';
+
+class MailsForUser extends Error {
+
+ /**
+ * The User object.
+ *
+ * @var object $User.
+ */
+ var $User;
+
+ /**
+ * Constructor.
+ *
+ * @param object The Group object to which this list associated.
+ */
+ function MailsForUser(&$user) {
+ $this->User =& $user;
+
+ return true;
+ }
+
+
+ /**
+ * getMonitoredForums
+ *
+ * @return Forum[] The array of Forums
+ *
+ */
+ function getMonitoredMails() {
+ $lists = array();
+ $sql="SELECT groups.group_name,groups.group_id,mail_group_list.group_list_id,mail_group_list.list_name ".
+ "FROM groups,mail_group_list,mailman_sql ".
+ "WHERE groups.group_id=mail_group_list.group_id AND groups.status ='A' ".
+ "AND mail_group_list.list_name=mailman_sql.listname ".
+ "AND mailman_sql.address='".$this->User->getEmail()."' ORDER BY group_name DESC";
+
+ $result=db_query($sql);
+ $rows=db_numrows($result);
+ if ($rows < 1) {
+ return $lists;
+ }
+ $last_group='';
+ for ($i=0; $i<$rows; $i++) {
+ $group_id = db_result($result,$i,'group_id');
+ $list_id = db_result($result,$i,'group_list_id');
+ $group =& group_get_object($group_id);
+ $list =& new MailmanList($group,$list_id);
+ if ($list->isError()) {
+ $this->setError($list->getErrorMessage());
+ } else {
+ $lists[] =& $list;
+ }
+ }
+ return $lists;
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once 'mailman/include/BackendMailmanList.class.php';
+/**
+* System Event classes
+*
+*/
+class SystemEvent_MAILMAN_LIST_CREATE extends SystemEvent {
+
+ /**
+ * Verbalize the parameters so they are readable and much user friendly in
+ * notifications
+ *
+ * @param bool $with_link true if you want links to entities. The returned
+ * string will be html instead of plain/text
+ *
+ * @return string
+ */
+ public function verbalizeParameters($with_link) {
+ $txt = '';
+ $txt .= 'mailing list: #'. $this->getIdFromParam($this->parameters);
+ return $txt;
+ }
+
+ /**
+ * Process stored event
+ */
+ function process() {
+ // Check parameters
+ $group_list_id=$this->getIdFromParam($this->parameters);
+
+ if ($group_list_id == 0) {
+ return $this->setErrorBadParam();
+ }
+
+ if (!BackendMailmanList::instance()->createList($group_list_id)) {
+ $this->error("Could not create mailing list $group_list_id");
+ return false;
+ }
+
+ // Need to add list aliases
+ Backend::instance('Aliases')->setNeedUpdateMailAliases();
+
+ $this->done();
+ return true;
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+/**
+ * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ *
+ * This file is a part of Codendi.
+ *
+ * Codendi is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Codendi is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Codendi. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once 'mailman/include/BackendMailmanList.class.php';
+
+/**
+* System Event classes
+*
+*/
+class SystemEvent_MAILMAN_LIST_DELETE extends SystemEvent {
+
+ /**
+ * Verbalize the parameters so they are readable and much user friendly in
+ * notifications
+ *
+ * @param bool $with_link true if you want links to entities. The returned
+ * string will be html instead of plain/text
+ *
+ * @return string
+ */
+ public function verbalizeParameters($with_link) {
+ $txt = '';
+ $txt .= 'mailing list: #'. $this->getIdFromParam($this->parameters);
+ return $txt;
+ }
+
+ /**
+ * Process stored event
+ */
+ function process() {
+ // Check parameters
+ $group_list_id=$this->getIdFromParam($this->parameters);
+
+ if ($group_list_id == 0) {
+ return $this->setErrorBadParam();
+ }
+
+ if (!BackendMailmanList::instance()->deleteList($group_list_id)) {
+ $this->error("Could not delete mailing list $group_list_id");
+ return false;
+ }
+
+ // Need to add list aliases
+ Backend::instance('Aliases')->setNeedUpdateMailAliases();
+
+ $this->done();
+ return true;
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+
+/**
+ *
+ * This file is part of GForge.
+ *
+ * GForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+global $gfplugins;
+require_once $gfplugins.'mailman/include/mailmanPlugin.class.php' ;
+
+$mailmanPluginObject = new mailmanPlugin ;
+
+register_plugin ($mailmanPluginObject) ;
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+<?php
+
+/**
+ * mailmanPlugin class
+ *
+ * This file is part of FusionForge.
+ *
+ * FusionForge is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FusionForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Portions Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+
+require_once 'plugins_utils.php';
+require_once('common/system_event/SystemEvent.class.php');
+require_once('common/event/Event.class.php');
+require_once 'MailmanList.class.php';
+require_once 'MailmanListFactory.class.php';
+require_once 'MailmanListDao.class.php';
+require_once 'common/dao/CodendiDataAccess.class.php';
+
+
+
+class mailmanPlugin extends Plugin {
+ public function __construct($id=0) {
+ $this->Plugin($id);
+ //$this->setScope(Plugin::SCOPE_PROJECT);
+ $this->name = "mailman" ;
+ $this->text = "Mailman" ; // To show in the tabs, use...
+ $this->_addHook("user_personal_links");//to make a link to the user�s personal part of the plugin
+ $this->_addHook("usermenu") ;
+ $this->_addHook("groupmenu"); // To put into the project tabs
+ $this->_addHook("groupisactivecheckbox") ; // The "use ..." checkbox in editgroupinfo
+ $this->_addHook("groupisactivecheckboxpost") ; //
+ $this->_addHook("userisactivecheckbox") ; // The "use ..." checkbox in user account
+ $this->_addHook("userisactivecheckboxpost") ; //
+ $this->_addHook("project_admin_plugins"); // to show up in the admin page fro group
+ $this->_addHook("monitored_element"); // to show monitored items in my page
+ $this->_addHook("group_delete"); // to delete mailing list associated when deleting a group
+ $this->_addHook("group_approve"); // to create mailing list 'Commit' when creating a group
+ $this->_addHook('site_admin_option_hook');
+ $this->_addHook(Event::GET_SYSTEM_EVENT_CLASS,'getSystemEventClass', false);//to make SystemEvent manager knows about mailman plugin
+
+ }
+
+ function process() {
+ echo '<h1>Mailman</h1>';
+ echo $this->getPluginInfo()->getpropVal('answer');
+ }
+
+ function getPluginInfo() {
+ if (!is_a($this->pluginInfo, 'MailmanPluginInfo')) {
+ require_once('MailmanPluginInfo.class.php');
+ $this->pluginInfo =& new MailmanPluginInfo($this);
+ }
+ return $this->pluginInfo;
+ }
+
+ function CallHook ($hookname, $params) {
+ global $use_mailmanplugin,$G_SESSION,$HTML,$gfcommon,$gfwww,$gfplugins;
+ if ($hookname == "usermenu") {
+ $text = $this->text; // this is what shows in the tab
+ if ($G_SESSION->usesPlugin("mailman")) {
+ $param = '?type=user&id=' . $G_SESSION->getId() . "&pluginname=" . $this->name; // we indicate the part we�re calling is the user one
+ echo ' | ' . $HTML->PrintSubMenu (array ($text),
+ array ('/plugins/mailman/index.php' . $param ));
+ }
+ } elseif ($hookname == "groupmenu") {
+ $group_id=$params['group'];
+ $project = &group_get_object($group_id);
+ if (!$project || !is_object($project)) {
+ return;
+ }
+ if ($project->isError()) {
+ return;
+ }
+ if (!$project->isProject()) {
+ return;
+ }
+ if ( $project->usesPlugin ( $this->name ) ) {
+ $params['TITLES'][]=$this->text;
+ $params['DIRS'][]='/plugins/mailman/index.php?group_id=' . $group_id . "&pluginname=" . $this->name; // we indicate the part we�re calling is the project one
+ }
+ (($params['toptab'] == $this->name) ? $params['selected']=(count($params['TITLES'])-1) : '' );
+ } elseif ($hookname == "groupisactivecheckbox") {
+ //Check if the group is active
+ // this code creates the checkbox in the project edit public info page to activate/deactivate the plugin
+ $group_id=$params['group'];
+ $group = &group_get_object($group_id);
+ echo "<tr>";
+ echo "<td>";
+ echo ' <input type="CHECKBOX" name="use_mailmanplugin" value="1" ';
+ // CHECKED OR UNCHECKED?
+ if ( $group->usesPlugin ( $this->name ) ) {
+ echo "CHECKED";
+ }
+ echo "><br/>";
+ echo "</td>";
+ echo "<td>";
+ echo "<strong>Use ".$this->text." Plugin</strong>";
+ echo "</td>";
+ echo "</tr>";
+
+ } elseif ($hookname == "groupisactivecheckboxpost") {
+ // this code actually activates/deactivates the plugin after the form was submitted in the project edit public info page
+ $group_id=$params['group'];
+ $group = &group_get_object($group_id);
+ $use_mailmanplugin = getStringFromRequest('use_mailmanplugin');
+ if ( $use_mailmanplugin == 1 ) {
+ $group->setPluginUse ( $this->name );
+ } else {
+ $group->setPluginUse ( $this->name, false );
+ }
+ } elseif ($hookname == "userisactivecheckbox") {
+ //check if user is active
+ // this code creates the checkbox in the user account manteinance page to activate/deactivate the plugin
+ $user = $params['user'];
+ echo "<tr>";
+ echo "<td>";
+ echo ' <input type="CHECKBOX" name="use_mailmanplugin" value="1" ';
+ // CHECKED OR UNCHECKED?
+ if ( $user->usesPlugin ( $this->name ) ) {
+ echo "CHECKED";
+ }
+ echo "> Use ".$this->text." Plugin";
+ echo "</td>";
+ echo "</tr>";
+ } elseif ($hookname == "userisactivecheckboxpost") {
+ // this code actually activates/deactivates the plugin after the form was submitted in the user account manteinance page
+ $user = $params['user'];
+ $use_mailmanplugin = getStringFromRequest('use_mailmanplugin');
+ if ( $use_mailmanplugin == 1 ) {
+ $user->setPluginUse ( $this->name );
+ } else {
+ $user->setPluginUse ( $this->name, false );
+ }
+ echo "<tr>";
+ echo "<td>";
+ echo ' <input type="CHECKBOX" name="use_mailmanplugin" value="1" ';
+ // CHECKED OR UNCHECKED?
+ if ( $user->usesPlugin ( $this->name ) ) {
+ echo "CHECKED";
+ }
+ echo "> Use ".$this->text." Plugin";
+ echo "</td>";
+ echo "</tr>";
+ } elseif ($hookname == "user_personal_links") {
+ // this displays the link in the user�s profile page to it�s personal mailman (if you want other sto access it, youll have to change the permissions in the index.php
+ $userid = $params['user_id'];
+ $user = user_get_object($userid);
+ $text = $params['text'];
+ //check if the user has the plugin activated
+ if ($user->usesPlugin($this->name)) {
+ echo ' <p>' ;
+ echo util_make_link ("/plugins/mailman/index.php?id=$userid&type=user&pluginname=".$this->name,
+ _('View Personal mailman')
+ );
+ echo '</p>';
+ }
+ } elseif ($hookname == "project_admin_plugins") {
+ // this displays the link in the project admin options page to it�s mailman administration
+ $group_id = $params['group_id'];
+ $group = &group_get_object($group_id);
+ if ( $group->usesPlugin ( $this->name ) ) {
+ echo util_make_link ("/plugins/projects_hierarchy/index.php?id=".$group->getID().'&type=admin&pluginname='.$this->name,
+ _('View the mailman Administration')) ;
+ echo '</p>';
+ }
+ }
+ elseif ($hookname == "monitored_element") {
+ $current_user=UserManager::instance()->getCurrentUser();
+ $last_group=0;
+ $order_name_arr=array();
+ $order_name_arr[]=_('Remove');
+ $order_name_arr[]=_('Monitored Lists');
+ echo $HTML->listTableTop($order_name_arr);
+ $dao = & new MailmanListDao(CodendiDataAccess::instance());
+ $result = $dao->listsMonitoredByUser($current_user->getEmail());
+ for ($i=0; $i<$result->rowCount(); $i++) {
+ $listResult = $result->getRow();
+ $list =& new MailmanList($listResult['group_id'],$listResult['group_list_id']);
+ if ($list->isError()) {
+ $this->setError($list->getErrorMessage());
+ } else {
+ $lists[] =& $list;
+ }
+ }
+ if (count($lists) < 1) {
+ echo '<tr><td colspan="2"><strong>'._('You are not monitoring any lists.').'</strong></td></tr>';
+ } else {
+ echo '<tr><td colspan="2"><strong>'.util_make_link ('/plugins/mailman',_('My Monitored Lists')).'</strong></td></tr>';
+ foreach ($lists as $l) {
+ $group = $l->getGroup();
+ if ($group->getID() != $last_group) {
+ echo ' <tr '. $HTML->boxGetAltRowStyle(1) .'><td colspan="2">'.util_make_link ('/plugins/mailman/index.php?group_id='.$group->getID(),$group->getPublicName()).'</td></tr>';
+ }
+ echo '
+ <tr '.$HTML->boxGetAltRowStyle(0).'><td class="align-center"><a href="/plugins/mailman/index.php?group_id='.$group->getID().
+ '&action=unsubscribe&id='.$l->getID().'">' .
+ '<img src="'.$HTML->imgroot.'/ic/trash.png" height="16" width="16" '.'border="0" alt="" /></a>' .
+ '</td><td width="99%"><a href="/plugins/mailman/index.php?group_id='.$group->getID().'&action=options&id='.$l->getID().'">'.
+ $l->getName().'"</a></td></tr>';
+ $last_group= $group->getID();
+ }
+ }
+ echo $HTML->listTableBottom();
+
+ }
+ elseif ($hookname == "group_delete") {
+ $group_id = $params['group_id'];
+ $group = &group_get_object($group_id);
+ $mlf = new MailmanListFactory($group);
+ $ml_arr =& $mlf->getMailmanLists();
+ for ($i=0; $i<count($ml_arr); $i++) {
+ if (!is_object($ml_arr[$i])) {
+ printf (_("Not Object: MailmanList: %d"),$i);
+ continue;
+ }
+ if (!$ml_arr[$i]->deleteList(1,1)) {
+ $this->setError(_('Could not properly delete the mailing list'));
+ }
+ //echo 'MailmanListFactory'.db_error();
+ }
+ }
+ elseif ($hookname == "group_approve") {
+ $idadmin_group =$params[0];
+ $group_id=$params[1];
+ $group = &group_get_object($group_id);
+ $mlist = new MailmanList($group);
+ if (!$mlist->create('commits','Commits',1,$idadmin_group)) {
+ $this->setError(sprintf(_('ML: %s'),$mlist->getErrorMessage()));
+ db_rollback();
+ return false;
+ }
+ }
+ elseif ($hookname=='site_admin_option_hook') {
+ echo '<li><a href="'.$this->getPluginPath().'/">Template</a></li>';
+ }
+
+
+
+ }
+ function getSystemEventClass($params) {
+
+ switch($params['type']) {
+ case 'MAILMAN_LIST_CREATE' :
+
+ require_once(dirname(__FILE__).'/events/SystemEvent_MAILMAN_LIST_CREATE.class.php');
+
+ $params['class'] = 'SystemEvent_MAILMAN_LIST_CREATE';
+ break;
+ case 'MAILMAN_LIST_DELETE' :
+ require_once(dirname(__FILE__).'/events/SystemEvent_MAILMAN_LIST_DELETE.class.php');
+ $params['class'] = 'SystemEvent_MAILMAN_LIST_DELETE';
+ break;
+ default:
+ break;
+ }
+
+ }
+
+
+
+
+
+}
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>
--- /dev/null
+Source: @SRCPACKAGE@
+Section: devel
+Priority: optional
+Maintainer: Christian Bayle <bayle@debian.org>
+Uploaders: Roland Mas <lolando@debian.org>
+Build-Depends-Indep: devscripts
+Build-Depends: debhelper (>= 7), perl, gettext
+Standards-Version: 3.8.4
+Homepage: http://fusionforge.org/
+Vcs-Bzr: http://scm.fusionforge.org/bzr/fusionforge/svn-trunk-ro/
--- /dev/null
+Package: @SRCPACKAGE@
+Architecture: all
+Depends: @OLDPACKAGE@-common, @OLDPACKAGE@-db-postgresql | @OLDPACKAGE@-db, @OLDPACKAGE@-web-apache2 | @OLDPACKAGE@-web, @OLDPACKAGE@-shell-postgresql | @OLDPACKAGE@-shell, php5-cli, ${misc:Depends}
+Description: collaborative development tool - Helloworld plugin
--- /dev/null
+ This plugin contains the Helloworld subsystem of FusionForge. It allows each
+ FusionForge project to have its own Helloworld, and gives some
+ control over it to the project's administrator.
--- /dev/null
+FFCRON="/usr/share/gforge/cronjobs"
+FFUSER=gforge
+
+# You may need to change the pathname to php CLI (command line interface)
+PHP="/usr/bin/php -q -d include_path=.:/etc/gforge:/usr/share/gforge:/usr/share/gforge/www/include:/usr/share/gforge/plugins"
+
+# Don't mail command output to anyone.
+MAILTO=""
+
+#
+# Regular cron jobs for the lists-mailman package
+#
+
+# Mailman-list creation
+* * * * * root $PHP $FFCRON/manage_mailmanlists.php
+
--- /dev/null
+etc/gforge/httpd.d
+etc/gforge/plugins/mailman
+usr/share/gforge/plugins/mailman/cgi-bin
+usr/share/gforge/plugins/mailman/common
+usr/share/gforge/plugins/mailman/www
--- /dev/null
+debian/README.Debian
--- /dev/null
+common/* usr/share/gforge/plugins/mailman/common/
+www/* usr/share/gforge/plugins/mailman/www/
+cronjobs/* usr/share/gforge/cronjobs
+etc/plugins/mailman/* etc/gforge/plugins/mailman/
+usr/lib/mailman/Mailman/* usr/lib/mailman/Mailman/
+var/lib/mailman/lists/* var/lib/mailman/lists/
--- /dev/null
+/usr/share/gforge/plugins/mailman/www /usr/share/gforge/www/plugins/mailman
--- /dev/null
+# Copyright (C) 1998-2008 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+
+import os
+import sys
+import re
+import time
+from types import StringType, TupleType
+
+from Errors import *
+from Mailman import Errors
+from Mailman import MemberAdaptor
+from Mailman import mm_cfg
+from Mailman import Utils
+from Mailman.Logging.Syslog import syslog
+from Mailman.Bouncer import _BounceInfo
+try:
+ from Mailman.Utils import md5_new
+except ImportError:
+ import md5
+ md5_new=md5.new
+ISREGULAR = 1
+ISDIGEST = 2
+tm_min = 4
+
+mm_cfg.connection = 0
+mm_cfg.cursor = 0
+
+try:
+ mm_cfg.MYSQL_MEMBER_CREATE_TABLE
+ mm_cfg.MYSQL_MEMBER_DB_VERBOSE
+except AttributeError:
+ mm_cfg.MYSQL_MEMBER_DB_VERBOSE = False # default value
+ mm_cfg.MYSQL_MEMBER_CREATE_TABLE = True
+ pass
+
+class ExternalConnector(MemberAdaptor.MemberAdaptor):
+ def __init__(self,mlist,param):
+ self._param=param
+ self.__mlist=mlist
+ self._members=None
+ self._member_passwd={}
+ self._member_names={}
+ self._updatetime = 0
+ # define the table and standard condition reflecting listname
+ self._table = param['mailman_table']
+ self._where = "listname = '%s'" %(self.__mlist.internal_name())
+
+ #define query for session management
+ self._cookiename=param['cookiename']
+ self._queryCookieMail=param['queryCookieMail']
+ self._queryCookieId=param['queryCookieId']
+ self._queryIsAdmin=param['queryIsAdmin']
+ self._queryIsSiteAdmin=param['queryIsSiteAdmin']
+ self._queryIsMonitoring=param['queryIsMonitoring']
+
+ self.__db_connect__()
+ if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
+ # Message to indicate successful init.
+ message = "DBMemberships " \
+ + "$Revision: 1.69 $ initialized with host: %s (%s)" % (
+ mm_cfg.connection.get_host_info(),
+ mm_cfg.connection.get_server_info() )
+ syslog('error', message)
+ syslog('mysql', message)
+
+ # add a cache memory
+ self._cache = {}
+ self._cachedate = 0
+
+ def __db_connect__(self):
+ return mm_cfg.cursor
+ def __del__(self):
+ # Cleaning up
+ try:
+ mm_cfg.cursor.close()
+ except:
+ pass
+ try:
+ mm_cfg.connection.close()
+ except:
+ pass
+ if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
+ # Message to indicate successful close.
+ syslog("error", "MysqlMemberships $Revision: 1.69 $ unloaded" )
+ syslog("mysql", "MysqlMemberships $Revision: 1.69 $ unloaded" )
+
+
+
+ # empty the cache (when we touch a value)
+ def uncache(self):
+ self._cache = {}
+ self._cachedate = time.localtime()[tm_min]
+
+ # Apply query on list (manages both 'flat' and 'wide' modes)
+ def query(self, query):
+ if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
+ syslog('mysql', query)
+ mm_cfg.cursor.execute(query)
+ mm_cfg.connection.commit()
+ return
+
+ # return all members according to a certain condition
+ def queryall(self, query, cache=False):
+ self.query(query)
+ # get the number of rows in the resultset
+ numrows = int(mm_cfg.cursor.rowcount)
+ # save one at a time
+ results = []
+ for x in range(0,numrows):
+ row = mm_cfg.cursor.fetchone()
+ results.append(row[0])
+ # we don't want to cache the whole list for global requests
+ if cache and numrows < 1000:
+ self._cache[row[1]] = row[2:]
+ return results
+
+ # select *, cache it, then return only the field that's asked for
+ def select(self, what, where=''):
+ query = "SELECT " + what \
+ + ",address,name,user_options,delivery_status,lang,digest " \
+ + "FROM %s WHERE %s" %(self._table, self._where)
+ if where:
+ query += " AND %s" %(where)
+ return self.queryall(query + ' ORDER BY address', True)
+
+ def select_on(self, what, address):
+ if self._cachedate != time.localtime()[tm_min]:
+ self.uncache()
+ try:
+ a = self._cache[address]
+ if what == 'name':
+ num = 0
+ elif what == 'user_options':
+ num = 1
+ elif what == 'delivery_status':
+ num = 2
+ elif what == 'lang':
+ num = 3
+ elif what == 'digest':
+ num = 4
+ a = [ a[num] ]
+ except:
+ a = self.select(what,
+ "address='%s'" %(self.escape(address)))
+ return a
+
+ def update_on(self, what, value, address):
+ if what == 'delivery_status':
+ dst = ", delivery_status_timestamp=NOW() "
+ else:
+ dst = ""
+ self.query("UPDATE %s " %(self._table)
+ + ("SET %s = '%s' " %(what, self.escape(value))
+ + dst
+ + ("WHERE %s " %(self._where))
+ + ("AND address = '%s'" %(self.escape(address)))))
+ # remove the cache
+ self.uncache()
+
+ def escape(self, value):
+ # transforms accents into html entities (é)
+ # TODO: find out which language is current (here: assumes iso-8859-1)
+ value = Utils.uncanonstr(value)
+
+ return value
+ def db_cookie_to_mail(self,c):
+ if cookiname and c.has_key(self._cookiename):
+ hash=c[self._cookiename]
+ query = self._queryCookieMail %hash
+ mm_cfg.cursor.execute(query)
+ attrs = mm_cfg.cursor.fetchone()
+ if attrs!= None :
+ usernames = attrs[0]
+ return usernames
+ return False
+
+ def db_cookie_to_id(self,c):
+ if self._cookiename and c.has_key(self._cookiename):
+ users=[]
+ hash=c[self._cookiename]
+ query=self._queryCookieId % hash
+ mm_cfg.cursor.execute(query)
+ attrs = mm_cfg.cursor.fetchone()
+ if attrs!= None :
+ users = attrs[0]
+ return users
+ return False
+
+ def db_id_to_monitored(self,user_id):
+ query=self._queryIsMonitoring %(str(user_id),self.__mlist.internal_name())
+ mm_cfg.cursor.execute(query)
+ result = mm_cfg.cursor.fetchone()
+ if result[0] == 0:
+ return False
+ if result[0] >= 1:
+ return True
+
+ def db_isAdmin(self,user_id):
+ query = self._queryIsAdmin %(str(user_id),self.__mlist.internal_name())
+ mm_cfg.cursor.execute(query)
+ result = mm_cfg.cursor.fetchone()
+ if result[0] == 0:
+ return False
+ if result[0] >= 1:
+ return True
+ def db_isSiteAdmin(self,user_id):
+ query = self._queryIsSiteAdmin %(str(user_id))
+ mm_cfg.cursor.execute(query)
+ result = mm_cfg.cursor.fetchone()
+ if result[0] == 0:
+ return False
+ if result[0] >= 1:
+ return True
+
+ ############################### Now the active codes #######
+ #
+ # Read interface
+ #
+
+ # All members
+ def getMembers(self):
+ return self.select('address')
+
+ # regular members
+ def getRegularMemberKeys(self):
+ return self.select('address', "digest = 'N'")
+
+ # digest members
+ def getDigestMemberKeys(self):
+ return self.select('address', "digest = 'Y'")
+
+ # status (regular/digest) of a member (returns a key - lowercase)
+ def __get_cp_member(self, member):
+ lcmember = member.lower()
+ digest = self.select_on('digest', lcmember)
+ if len(digest):
+ if digest is 'Y':
+ return lcmember, ISDIGEST
+ else:
+ return lcmember, ISREGULAR
+ return None, None
+
+ # is she a member?
+ def isMember(self, member):
+ member = self.select_on('name', member)
+ if len(member):
+ return 1
+ return 0
+
+ # Covered by SQL __get_cp_member()
+ def getMemberKey(self, member):
+ cpaddr, where = self.__get_cp_member(member)
+ if cpaddr is None:
+ raise Errors.NotAMemberError, member
+ return member.lower()
+
+ # Covered by SQL __get_cp_member()
+ def getMemberCPAddress(self, member):
+ cpaddr, where = self.__get_cp_member(member)
+ if cpaddr is None:
+ raise Errors.NotAMemberError, member
+ return cpaddr
+
+ # Covered by SQL __get_cp_member()
+ def getMemberCPAddresses(self, members):
+ return [self.__get_cp_member(member)[0] for member in members]
+
+ # SELECT password FROM <listname> WHERE address = member.lower()
+ def getMemberPassword(self, member):
+ password = self.select_on('password', member)
+ if len(password):
+ return password[0]
+ else:
+ raise Errors.NotAMemberError, member
+
+ # Covered by SQL getMemberPassword()
+ def authenticateMember(self, member, response):
+ secret = self.getMemberPassword(member)
+ if secret == md5_new(response).hexdigest():
+ return secret
+ return 0
+
+ # Covered by SQL isMember()
+ def __assertIsMember(self, member):
+ if not self.isMember(member):
+ raise Errors.NotAMemberError, member
+
+ def getMemberLanguage(self, member):
+ lang = self.select_on('lang',member)
+ if len(lang) and lang[0] in self.__mlist.GetAvailableLanguages():
+ return lang[0]
+ else:
+ return self.__mlist.preferred_language
+
+ # getOptions: different methods for digest and other (bitfield) options
+ def getMemberOption(self, member, flag):
+ self.__assertIsMember(member)
+ if flag == mm_cfg.Digests:
+ cpaddr, where = self.__get_cp_member(member)
+ return where == ISDIGEST
+ options = self.select_on('user_options', member)
+ if len(options):
+ return not not (options[0] & flag)
+
+
+ # new method to gest faster results when searching a user in the admin Gui
+ def getMembersMatching(self, regexp):
+ return self.select('address',
+ "(address REGEXP '%s' OR name REGEXP '%s')"
+ %( self.escape(regexp), self.escape(regexp) ) )
+
+ # new method to get faster results when querying the number of subscribers
+ def getMembersCount(self, reason=None):
+ if reason:
+ where = " AND digest='%s'" %reason
+ else:
+ where = ""
+ self.query("SELECT COUNT(*) FROM %s WHERE %s%s" %(
+ self._table, self._where, where))
+ count = mm_cfg.cursor.fetchone()
+ return int(count[0])
+
+ # get member's name (slow method if you need many)
+ # due to the way escape() is built, names are stored in html
+ # format in the DB, hence the canonstr() to put them back to
+ # normal (TODO)
+ def getMemberName(self, member):
+ name = self.select_on('name', member)
+ if len(name):
+ try:
+ return Utils.canonstr(name[0])
+ except:
+ return name[0]
+ self.__assertIsMember(member)
+
+ # topics
+ def getMemberTopics(self, member):
+ topics = self.select_on('topics_userinterest',member)
+ if len(topics) and isinstance(topics[0], str):
+ return topics[0].split(',')
+ return []
+ self.__assertIsMember(member)
+
+ # delivery status
+ def getDeliveryStatus(self, member):
+ status = self.select_on('delivery_status',member)
+ if len(status):
+ if status[0] in (MemberAdaptor.ENABLED, MemberAdaptor.UNKNOWN,
+ MemberAdaptor.BYUSER, MemberAdaptor.BYADMIN,
+ MemberAdaptor.BYBOUNCE):
+ return status[0]
+ else:
+ return MemberAdaptor.ENABLED
+ self.__assertIsMember(member)
+
+
+ # delivery status change time
+ def getDeliveryStatusChangeTime(self, member):
+ time = self.select_on('delivery_status_timestamp',member)
+ if len(time):
+ time = time[0]
+ if time is '0':
+ return MemberAdaptor.ENABLED
+ else:
+ return time
+ self.__assertIsMember(member)
+
+ # Covered by SQL getMembers(), and getDeliveryStatus().
+ def getDeliveryStatusMembers(self, status=(MemberAdaptor.UNKNOWN,
+ MemberAdaptor.BYUSER,
+ MemberAdaptor.BYADMIN,
+ MemberAdaptor.BYBOUNCE)):
+ return [member for member in self.getMembers()
+ if self.getDeliveryStatus(member) in status]
+
+ # show bouncing members
+ def getBouncingMembers(self):
+ mm_cfg.cursor = self.__db_connect__()
+ self.query("""SELECT bi_cookie,bi_score,bi_noticesleft,
+ UNIX_TIMESTAMP(bi_lastnotice),UNIX_TIMESTAMP(bi_date),address
+ FROM %s WHERE %s""" %(self._table, self._where))
+ # get the number of rows in the resultset
+ numrows = int(mm_cfg.cursor.rowcount)
+ # save one address at a time
+ bounce_info_list = []
+ for x in range(0,numrows):
+ row = mm_cfg.cursor.fetchone()
+ # We must not return anything if there is
+ # no bounce info for that member to start with.
+ if row[4] > 0:
+ # Append the member name to the bounce info list.
+ bounce_info_list.append(row[5])
+ return [member.lower() for member in bounce_info_list]
+
+ def getBounceInfo(self, member):
+ mm_cfg.cursor = self.__db_connect__()
+ self.query("""SELECT
+ bi_score,
+ bi_noticesleft,
+ EXTRACT(YEAR FROM bi_lastnotice),
+ EXTRACT(MONTH FROM bi_lastnotice),
+ EXTRACT(DAY FROM bi_lastnotice),
+ EXTRACT(YEAR FROM bi_date),
+ EXTRACT(MONTH FROM bi_date),
+ EXTRACT(DAY FROM bi_date),
+ bi_cookie
+ FROM %s WHERE %s AND """ %(self._table, self._where)
+ + ("address = '%s'" %( self.escape(member) ) ))
+ numrows = int(mm_cfg.cursor.rowcount)
+ if numrows is 0:
+ self.__assertIsMember(member)
+ row = mm_cfg.cursor.fetchone()
+ # We must not return a _BounceInfo instance if there is no bounce info
+ # to start with.
+ if row[3] <= 0:
+ return None;
+ # Otherwise, populate a bounce_info structure.
+ bounce_info = _BounceInfo(member, row[0],
+ (row[5],row[6],row[7]), row[1])
+ bounce_info.lastnotice = (row[2],row[3],row[4])
+ bounce_info.cookie = row[8]
+ return bounce_info
+
+
+ #
+ # Write interface
+ #
+ def addNewMember(self, member, **kws):
+ # assert self.__mlist.Locked()
+ # Make sure this address isn't already a member
+ if self.isMember(member):
+ raise Errors.MMAlreadyAMember, member
+ # Parse the keywords
+ digest = 0
+ password = Utils.MakeRandomPassword()
+ language = self.__mlist.preferred_language
+ realname = None
+ if kws.has_key('digest'):
+ digest = kws['digest']
+ del kws['digest']
+ if kws.has_key('password'):
+ password = kws['password']
+ del kws['password']
+ if kws.has_key('language'):
+ language = kws['language']
+ del kws['language']
+ if kws.has_key('realname'):
+ realname = kws['realname']
+ del kws['realname']
+ # Assert that no other keywords are present
+ if kws:
+ raise ValueError, kws.keys()
+ # If the localpart has uppercase letters in it, then the value in the
+ # members (or digest_members) dict is the case preserved address.
+ # Otherwise the value is 0. Note that the case of the domain part is
+ # of course ignored.
+ if Utils.LCDomain(member) == member.lower():
+ value = 0
+ else:
+ value = member
+ member = member.lower()
+ if digest:
+ digest = 'Y'
+ else:
+ digest = 'N'
+ # All we need to do here is add the address.
+ # and Set the member's default set of options
+ if self.__mlist.new_member_options:
+ options = self.__mlist.new_member_options
+ else:
+ options = 0
+ query = "INSERT INTO %s " \
+ + "(address, user_options, password, lang, " \
+ + "digest, delivery_status,listname) values " \
+ + "('%s',%s,'%s','%s','%s','%s','%s')"
+ query = query %( self._table,
+ self.escape(member), options, md5_new(password).hexdigest(),
+ language, digest, MemberAdaptor.ENABLED,self.__mlist.internal_name())
+ if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
+ syslog('mysql',query)
+ mm_cfg.cursor.execute(query)
+ mm_cfg.connection.commit()
+ if realname:
+ self.setMemberName(member, realname)
+
+ def removeMember(self, member):
+ # assert self.__mlist.Locked()
+ self.__assertIsMember(member)
+ self.query("DELETE FROM %s WHERE %s " %(self._table, self._where)
+ + ("AND address = '%s'" %( self.escape(member.lower()) ) ))
+ self.uncache()
+
+ def changeMemberAddress(self, member, newaddress, nodelete=0):
+ # assert self.__mlist.Locked()
+ # Make sure this address isn't already a member
+ if self.isMember(newaddress):
+ raise Errors.MMAlreadyAMember, newaddress
+ self.update_on('address', newaddress, member)
+
+ def setMemberPassword(self, member, password):
+ # assert self.__mlist.Locked()
+ self.update_on('password', md5_new(password).hexdigest(), member)
+
+ def setMemberLanguage(self, member, lang):
+ # assert self.__mlist.Locked()
+ self.update_on('lang', lang, member)
+
+ def setMemberOption(self, member, flag, value):
+ # assert self.__mlist.Locked()
+ if flag == mm_cfg.Digests:
+ if value:
+ # Be sure the list supports digest delivery
+ if not self.__mlist.digestable:
+ raise Errors.CantDigestError
+ # The user is turning on digest mode
+ # If they are already receiving digests, report an error.
+ if self.getMemberOption(member, mm_cfg.Digests) is 'Y':
+ raise Errors.AlreadyReceivingDigests, member
+ # If we've got past all this, actually turn on digest mode.
+ self.update_on('digest', 'Y', member)
+ else:
+ # Be sure the list supports regular delivery
+ if not self.__mlist.nondigestable:
+ raise Errors.MustDigestError
+ # The user is turning off digest mode
+ # If they are already receiving regular, report an error.
+ if self.getMemberOption(member, mm_cfg.Digests) is 'N':
+ raise Errors.AlreadyReceivingRegularDeliveries, member
+ # If we've got past all this, actually turn off digest mode.
+ self.update_on('digest', 'N', member)
+ return
+
+ # Apparently, mysql supports the & and | operators, so this should
+ # work, maybe. Will have to suck it and see for the moment.
+ # If the value is non-zero, set the bitfield indicated by 'flag'.
+ if value:
+ self.query("UPDATE %s " %(self._table)
+ + ("SET user_options = user_options | %s " %(flag))
+ + "WHERE %s " %(self._where)
+ + ("AND address = '%s'" %( self.escape(member) ) ))
+ else:
+ self.query("UPDATE %s " %(self._table)
+ + ("SET user_options = user_options & ~%s " %(flag))
+ + "WHERE %s " %(self._where)
+ + ("AND address = '%s'" %( self.escape(member) ) ))
+ # remove the cache
+ self.uncache()
+
+ def setMemberName(self, member, name):
+ # assert self.__mlist.Locked()
+ self.update_on('name', name, member)
+
+ def setMemberTopics(self, member, topics):
+ # assert self.__mlist.Locked()
+ if isinstance(topics,list):
+ topics=",".join(topics)
+ else:
+ topics=""
+ self.query("UPDATE %s " %(self._table)
+ + ("SET topics_userinterest = '%s' " %(
+ self.escape(topics) ))
+ + "WHERE %s " %(self._where)
+ + ("AND address = '%s'" %( self.escape(member) )))
+
+ def setDeliveryStatus(self, member, status):
+ assert status in (MemberAdaptor.ENABLED, MemberAdaptor.UNKNOWN,
+ MemberAdaptor.BYUSER, MemberAdaptor.BYADMIN,
+ MemberAdaptor.BYBOUNCE)
+ # assert self.__mlist.Locked()
+ member = member.lower()
+ if status == MemberAdaptor.ENABLED:
+ # Enable by resetting their bounce info.
+ self.setBounceInfo(member, None)
+ else:
+ self.query("UPDATE %s " %(self._table)
+ + ("SET delivery_status = '%s', " %(status))
+ + "delivery_status_timestamp=NOW() WHERE %s " %(self._where)
+ + ("AND address = '%s'" %( self.escape(member) )))
+ # remove the cache
+ self.uncache()
+
+ def setBounceInfo(self, member, info):
+ # assert self.__mlist.Locked()
+ member = member.lower()
+ if info is None:
+ self.query("UPDATE %s " %(self._table)
+ + ("SET delivery_status = '%s', " %(MemberAdaptor.ENABLED))
+ + "bi_cookie = NULL, "
+ + "bi_score = 0, "
+ + "bi_noticesleft = 0, "
+ + "bi_lastnotice = '1901-01-01'::date, "
+ + "bi_date = '1901-01-01'::date "
+ + "WHERE %s " %(self._where)
+ + ("AND address = '%s'" %( self.escape(member) )))
+ else:
+ # Hack the dates to work with MySQL.
+ lnsql = time.strftime("%Y-%m-%d", time.strptime('-'.join(map(str,info.lastnotice)),'%Y-%m-%d'))
+ datesql = time.strftime("%Y-%m-%d", time.strptime('-'.join(map(str,info.date)),'%Y-%m-%d'))
+ self.query("UPDATE %s " %(self._table)
+ + (("SET bi_cookie = '%s', "
+ + "bi_score = %s, "
+ + "bi_noticesleft = %s, "
+ + "bi_lastnotice = '%s', "
+ + "bi_date = '%s' ") %(
+ info.cookie, info.score,
+ info.noticesleft, lnsql, datesql
+ ))
+ + ("WHERE %s " %(self._where))
+ + ("AND address = '%s'" %( self.escape(member) )))
+
+
+
+ # this function can be plugged into Mailman.MailList.Save
+ # it saves a copy of a few list's attributes into a database
+ def SaveToDb(self,dict):
+ query = 'REPLACE lists (listname,moderation,advertised,new_member_options,subscribe_policy,host_name,description,info,count) ' \
+ " VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s')" % (\
+ self.internal_name(), \
+ self.default_member_moderation, \
+ self.advertised, \
+ self.new_member_options, \
+ self.subscribe_policy, \
+ self.host_name, \
+ self.escape(self.description), \
+ self.escape(self.info), \
+ self.getMembersCount() \
+ )
+ syslog('mysql', query)
+ try:
+ self.query(query)
+ except Exception,e:
+ syslog('mysql', 'error %s'%e)
+ pass
--- /dev/null
+
+# Copyright (C) 1998-2008 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+
+"""Handle passwords and sanitize approved messages."""
+
+# There are current 5 roles defined in Mailman, as codified in Defaults.py:
+# user, list-creator, list-moderator, list-admin, site-admin.
+#
+# Here's how we do cookie based authentication.
+#
+# Each role (see above) has an associated password, which is currently the
+# only way to authenticate a role (in the future, we'll authenticate a
+# user and assign users to roles).
+#
+# Each cookie has the following ingredients: the authorization context's
+# secret (i.e. the password, and a timestamp. We generate an SHA1 hex
+# digest of these ingredients, which we call the `mac'. We then marshal
+# up a tuple of the timestamp and the mac, hexlify that and return that as
+# a cookie keyed off the authcontext. Note that authenticating the user
+# also requires the user's email address to be included in the cookie.
+#
+# The verification process is done in CheckCookie() below. It extracts
+# the cookie, unhexlifies and unmarshals the tuple, extracting the
+# timestamp. Using this, and the shared secret, the mac is calculated,
+# and it must match the mac passed in the cookie. If so, they're golden,
+# otherwise, access is denied.
+#
+# It is still possible for an adversary to attempt to brute force crack
+# the password if they obtain the cookie, since they can extract the
+# timestamp and create macs based on password guesses. They never get a
+# cleartext version of the password though, so security rests on the
+# difficulty and expense of retrying the cgi dialog for each attempt. It
+# also relies on the security of SHA1.
+
+import os
+import re
+import time
+import sys
+import Cookie
+import marshal
+import binascii
+import urllib
+from types import StringType, TupleType
+from urlparse import urlparse
+
+try:
+ import crypt
+except ImportError:
+ crypt = None
+
+from Mailman import mm_cfg
+from Mailman import Utils
+from Mailman import Errors
+from Mailman.Logging.Syslog import syslog
+try:
+ from Mailman.Utils import md5_new, sha_new
+except ImportError:
+ import md5
+ import sha
+ md5_new=md5.new
+ sha_new=sha.new
+from Mailman import SecurityManager
+
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
+
+\f
+class ForgeSecurityManager(SecurityManager.SecurityManager):
+ def __init__(self,mlist):
+ self.__mlist = mlist
+ # We used to set self.password here, from a crypted_password argument,
+ # but that's been removed when we generalized the mixin architecture.
+ # self.password is really a SecurityManager attribute, but it's set in
+ # MailList.InitVars().
+ self.mod_password = None
+ # Non configurable
+ self.__mlist.passwords = {}
+
+ def AuthContextInfo(self, authcontext, user=None):
+ # authcontext may be one of AuthUser, AuthListModerator,
+ # AuthListAdmin, AuthSiteAdmin. Not supported is the AuthCreator
+ # context.
+ #
+ # user is ignored unless authcontext is AuthUser
+ #
+ # Return the authcontext's secret and cookie key. If the authcontext
+ # doesn't exist, return the tuple (None, None). If authcontext is
+ # AuthUser, but the user isn't a member of this mailing list, a
+ # NotAMemberError will be raised. If the user's secret is None, raise
+ # a MMBadUserError.
+ key = self.__mlist.internal_name() + '+'
+ if authcontext == mm_cfg.AuthUser:
+ if user is None:
+ # A bad system error
+ raise TypeError, 'No user supplied for AuthUser context'
+ secret = self.__mlist.getMemberPassword(user)
+ userdata = urllib.quote(Utils.ObscureEmail(user), safe='')
+ key += 'user+%s' % userdata
+ elif authcontext == mm_cfg.AuthListModerator:
+ secret = self.mod_password
+ key += 'moderator'
+ elif authcontext == mm_cfg.AuthListAdmin:
+ secret = self.__mlist.password
+ key += 'admin'
+ # BAW: AuthCreator
+ elif authcontext == mm_cfg.AuthSiteAdmin:
+ sitepass = Utils.get_global_password()
+ if mm_cfg.ALLOW_SITE_ADMIN_COOKIES and sitepass:
+ secret = sitepass
+ key = 'site'
+ else:
+ # BAW: this should probably hand out a site password based
+ # cookie, but that makes me a bit nervous, so just treat site
+ # admin as a list admin since there is currently no site
+ # admin-only functionality.
+ secret = self.__mlist.password
+ key += 'admin'
+ else:
+ return None, None
+ return key, secret
+
+ def Authenticate(self, authcontexts, response, user=None):
+ # Given a list of authentication contexts, check to see if the
+ # response matches one of the passwords. authcontexts must be a
+ # sequence, and if it contains the context AuthUser, then the user
+ # argument must not be None.
+ #
+ # Return the authcontext from the argument sequence that matches the
+ # response, or UnAuthorized.
+ for ac in authcontexts:
+ if ac == mm_cfg.AuthCreator:
+ ok = Utils.check_global_password(response, siteadmin=0)
+ if ok:
+ return mm_cfg.AuthCreator
+ elif ac == mm_cfg.AuthSiteAdmin:
+ ok = Utils.check_global_password(response)
+ if ok:
+ return mm_cfg.AuthSiteAdmin
+ elif ac == mm_cfg.AuthListAdmin:
+ def cryptmatchp(response, secret):
+ try:
+ salt = secret[:2]
+ if crypt and crypt.crypt(response, salt) == secret:
+ return True
+ return False
+ except TypeError:
+ # BAW: Hard to say why we can get a TypeError here.
+ # SF bug report #585776 says crypt.crypt() can raise
+ # this if salt contains null bytes, although I don't
+ # know how that can happen (perhaps if a MM2.0 list
+ # with USE_CRYPT = 0 has been updated? Doubtful.
+ return False
+ # The password for the list admin and list moderator are not
+ # kept as plain text, but instead as an sha hexdigest. The
+ # response being passed in is plain text, so we need to
+ # digestify it first. Note however, that for backwards
+ # compatibility reasons, we'll also check the admin response
+ # against the crypted and md5'd passwords, and if they match,
+ # we'll auto-migrate the passwords to sha.
+ key, secret = self.AuthContextInfo(ac)
+ if secret is None:
+ continue
+ sharesponse = sha_new(response).hexdigest()
+ upgrade = ok = False
+ if sharesponse == secret:
+ ok = True
+ elif md5_new(response).digest() == secret:
+ ok = upgrade = True
+ elif cryptmatchp(response, secret):
+ ok = upgrade = True
+ if upgrade:
+ save_and_unlock = False
+ if not self.__mlist.Locked():
+ self.__mlist.Lock()
+ save_and_unlock = True
+ try:
+ self.__mlist.password = sharesponse
+ if save_and_unlock:
+ self.__mlist.Save()
+ finally:
+ if save_and_unlock:
+ self.__mlist.Unlock()
+ if ok:
+ return ac
+ elif ac == mm_cfg.AuthListModerator:
+ # The list moderator password must be sha'd
+ key, secret = self.AuthContextInfo(ac)
+ if secret and sha_new(response).hexdigest() == secret:
+ return ac
+ elif ac == mm_cfg.AuthUser:
+ if user is not None:
+ try:
+ if self.__mlist.authenticateMember(user, response):
+ return ac
+ except Errors.NotAMemberError:
+ pass
+ else:
+ # What is this context???
+ syslog('error', 'Bad authcontext: %s', ac)
+ raise ValueError, 'Bad authcontext: %s' % ac
+ return mm_cfg.UnAuthorized
+
+ def WebAuthenticate(self, authcontexts, response, user=None):
+ # Given a list of authentication contexts, check to see if the cookie
+ # contains a matching authorization, falling back to checking whether
+ # the response matches one of the passwords. authcontexts must be a
+ # sequence, and if it contains the context AuthUser, then the user
+ # argument should not be None.
+ #
+ # Returns a flag indicating whether authentication succeeded or not.
+ for ac in authcontexts:
+ ok = self.CheckCookie(ac,user)
+ if ok:
+ return True
+ # Check passwords
+ ac = self.Authenticate(authcontexts, response, user)
+ if ac:
+ print self.MakeCookie(ac, user)
+ return True
+ return False
+
+ def MakeCookie(self, authcontext, user=None):
+ key, secret = self.AuthContextInfo(authcontext, user)
+ if key is None or secret is None or not isinstance(secret, StringType):
+ raise ValueError
+ # Timestamp
+ issued = int(time.time())
+ # Get a digest of the secret, plus other information.
+ mac = sha_new(secret + `issued`).hexdigest()
+ # Create the cookie object.
+ c = Cookie.SimpleCookie()
+ c[key] = binascii.hexlify(marshal.dumps((issued, mac)))
+ # The path to all Mailman stuff, minus the scheme and host,
+ # i.e. usually the string `/mailman'
+ path = urlparse(self.__mlist.web_page_url)[2]
+ c[key]['path'] = path
+ # We use session cookies, so don't set `expires' or `max-age' keys.
+ # Set the RFC 2109 required header.
+ c[key]['version'] = 1
+ return c
+
+ def ZapCookie(self, authcontext, user=None):
+ # We can throw away the secret.
+ key, secret = self.AuthContextInfo(authcontext, user)
+ # Logout of the session by zapping the cookie. For safety both set
+ # max-age=0 (as per RFC2109) and set the cookie data to the empty
+ # string.
+ c = Cookie.SimpleCookie()
+ c[key] = ''
+ # The path to all Mailman stuff, minus the scheme and host,
+ # i.e. usually the string `/mailman'
+ path = urlparse(self.__mlist.web_page_url)[2]
+ c[key]['path'] = path
+ c[key]['max-age'] = 0
+ # Don't set expires=0 here otherwise it'll force a persistent cookie
+ c[key]['version'] = 1
+ return c
+
+ def CheckCookie(self, authcontext, user):
+ cookiedata = os.environ.get('HTTP_COOKIE')
+ if not cookiedata:
+ return False
+ c = parsecookie(cookiedata)
+ if authcontext == mm_cfg.AuthUser:
+ if user:
+ usernames = [user]
+ else:
+ usernames = []
+ usernames = self.__mlist.db_cookie_to_mail(c)
+ if usernames :
+ for user in usernames:
+ ok = self.__checkone(c, authcontext, user)
+ if ok:
+ return True
+ usernames = []
+ prefix = self.__mlist.internal_name() + '+user+'
+ for k in c.keys():
+ if k.startswith(prefix):
+ usernames.append(k[len(prefix):])
+ # If any check out, we're golden. Note: `@'s are no longer legal
+ # values in cookie keys.
+ for user in [Utils.UnobscureEmail(urllib.unquote(u))
+ for u in usernames]:
+ ok = self.__checkone(c, authcontext, user)
+ if ok:
+ return True
+ return False
+ else:
+ return self.__checkone(c,authcontext, user)
+
+ def __checkone(self, c, authcontext, user):
+ try:
+ key, secret = self.AuthContextInfo(authcontext, user)
+ except Errors.NotAMemberError:
+ return False
+ user_id = self.__mlist.db_cookie_to_id(c)
+ if user_id :
+ monitored = self.__mlist.db_id_to_monitored(user_id)
+ isAdmin = self.__mlist.db_isAdmin(user_id)
+ isSiteAdmin = self.__mlist.db_isSiteAdmin(user_id)
+ if authcontext == mm_cfg.AuthUser:
+ if not monitored:
+ return False
+ else :
+ return True
+ elif authcontext == mm_cfg.AuthListAdmin:
+ if not isAdmin:
+ return False
+ else :
+ return True
+ elif authcontext == mm_cfg.AuthListModerator:
+ if not isAdmin :
+ return False
+ else :
+ return True
+ elif authcontext == mm_cfg.AuthSiteAdmin:
+ if not isSiteAdmin :
+ return False
+ else :
+ return True
+ if not c.has_key(key) or not isinstance(secret, StringType):
+ return False
+ # Undo the encoding we performed in MakeCookie() above. BAW: I
+ # believe this is safe from exploit because marshal can't be forced to
+ # load recursive data structures, and it can't be forced to execute
+ # any unexpected code. The worst that can happen is that either the
+ # client will have provided us bogus data, in which case we'll get one
+ # of the caught exceptions, or marshal format will have changed, in
+ # which case, the cookie decoding will fail. In either case, we'll
+ # simply request reauthorization, resulting in a new cookie being
+ # returned to the client.
+ try:
+ data = marshal.loads(binascii.unhexlify(c[key]))
+ issued, received_mac = data
+ except (EOFError, ValueError, TypeError, KeyError):
+ return False
+ # Make sure the issued timestamp makes sense
+ now = time.time()
+ if now < issued:
+ return False
+ # Calculate what the mac ought to be based on the cookie's timestamp
+ # and the shared secret.
+ mac = sha_new(secret + `issued`).hexdigest()
+ if mac <> received_mac:
+ return False
+ # Authenticated!
+ return True
+
+\f
+splitter = re.compile(';\s*')
+
+def parsecookie(s):
+ c = {}
+ for line in s.splitlines():
+ for p in splitter.split(line):
+ try:
+ k, v = p.split('=', 1)
+ except ValueError:
+ pass
+ else:
+ c[k] = v
+ return c
--- /dev/null
+
+# Copyright (C) 1998-2008 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+
+import os
+import sys
+import re
+import time
+from types import StringType, TupleType
+
+
+from Mailman import ExternalConnector
+from Mailman import mm_cfg
+from Mailman import Utils
+from Mailman.Logging.Syslog import syslog
+import MySQLdb
+class MySQLConnector(ExternalConnector.ExternalConnector):
+ def __init__(self,mlist,param):
+ ExternalConnector.ExternalConnector.__init__(self,mlist,param)
+ def __db_connect__(self):
+ if mm_cfg.connection ==0:
+ connection = MySQLdb.connect (host = self._param['dbhost'], user = self._param['dbuser'], passwd = self._param['dbpassword'],db = self._param['database'])
+ mm_cfg.connection = connection
+ mm_cfg.cursor = connection.cursor()
+ connection.commit()
+ return mm_cfg.cursor
+
--- /dev/null
+# Copyright (C) 1998-2008 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+
+import os
+import sys
+import re
+import time
+from types import StringType, TupleType
+
+from Mailman import ExternalConnector
+
+from Mailman import mm_cfg
+from Mailman import Utils
+from Mailman.Logging.Syslog import syslog
+import psycopg2
+
+class PsycopgConnector(ExternalConnector.ExternalConnector):
+ def __init__(self,mlist,param):
+ ExternalConnector.ExternalConnector.__init__(self,mlist,param)
+ def __db_connect__(self):
+ if mm_cfg.connection == 0:
+ if self._param['dbhost']:
+ connection = psycopg2.connect (host = self._param['dbhost'], user = self._param['dbuser'], password = self._param['dbpassword'], database = self._param['database'])
+ else :
+ connection = psycopg2.connect (user = self._param['dbuser'], password = self._param['dbpassword'], database = self._param['database'])
+ mm_cfg.connection = connection
+ mm_cfg.cursor=connection.cursor()
+ connection.commit()
+ return mm_cfg.cursor
+
--- /dev/null
+#! /bin/sh -e
+
+if [ -e gforge/translations/gforge.pot ] ; then # We're in the parent dir
+ cd gforge
+elif [ -e translations/gforge.pot ] ; then # probably in gforge/ (or a renamed gforge/)
+ cd . # do nothing, but shell syntax requires an instruction in a then-block
+elif [ -e ../gforge/translations/gforge.pot ] ; then # in tools/ or tests/ or something
+ cd ../gforge
+elif [ -e ../translations/gforge.pot ] ; then # In a subdir of gforge/
+ cd ..
+else
+ echo "Couldn't find translations directory..."
+ exit 1
+fi
+
+locales=$(ls translations/*.po \
+ | xargs -n1 -iFILE basename FILE .po \
+ | egrep '^[a-z][a-z](_[A-Z][A-Z]$)?' \
+ | sort)
+
+print_stats () {
+ for l in $(echo $locales | xargs -n 1 | sort) ; do
+ printf "* %5s: " $l
+ msgfmt --statistics -o /dev/null translations/$l.po
+ done
+}
+
+case $1 in
+ stats)
+ print_stats
+ ;;
+ refresh)
+ rm translations/gforge.pot
+
+ find -type f -\( -name \*.php -or -name users -or -name projects -\) \
+ | grep -v -e {arch} -e svn-base \
+ | grep -v ^./plugins/wiki \
+ | LANG=C sort \
+ | xargs xgettext -d gforge -o translations/gforge.pot -L PHP --from-code=iso-8859-1
+
+ for l in $locales ; do
+ echo "Processing $l..."
+ msgmerge -U translations/$l.po translations/gforge.pot
+ done
+ ;;
+ build)
+ for l in $locales ; do
+ mkdir -p locales/$l/LC_MESSAGES
+ msgfmt -o locales/$l/LC_MESSAGES/gforge.mo translations/$l.po
+ done
+ ;;
+ *)
+ echo "Unknown operation"
+ exit 1
+ ;;
+esac
--- /dev/null
+from Mailman.PsycopgConnector import PsycopgConnector
+from Mailman.ForgeSecurityManager import ForgeSecurityManager
+import sys
+
+def extendMemberAdaptor(list):
+ sys.path.append('/etc/gforge')
+ import database
+ dbparam={}
+ #Config to connect to database
+ dbparam['dbhost'] = database.sys_dbhost
+ dbparam['dbuser']= database.sys_dbuser
+ dbparam['dbpassword'] = database.sys_dbpasswd
+ dbparam['database'] = database.sys_dbname
+ dbparam['refresh'] = 360
+
+ dbparam['mailman_table']= 'plugin_mailman'#table where mailman stores memeberships info
+
+ ######################
+ # Session Management #
+ ######################
+ #Forge default session
+ dbparam['cookiename']='session_ser'
+ dbparam['queryCookieMail']="SELECT email FROM plugin_defaultsession,users WHERE users.user_id=plugin_defaultsession.user_id AND session_hash = substring('%s','.*-%%2A-(.*)');"
+ dbparam['queryCookieId']="SELECT user_id FROM plugin_defaultsession WHERE session_hash = substring('%s','.*-%%2A-(.*)');"
+
+ dbparam['queryIsAdmin'] = "SELECT COUNT(*) FROM mail_group_list WHERE list_admin=%s AND list_name='%s';"
+ dbparam['queryIsMonitoring'] = "SELECT COUNT(*) FROM "+dbparam['mailman_table']+", users "+" WHERE users.email = "+dbparam['mailman_table']+".address"+" AND users.user_id=%s AND listname='%s';"
+ dbparam['queryIsSiteAdmin'] = "SELECT count(*) AS count FROM user_group WHERE user_id=%s AND group_id=1 AND admin_flags='A';"
+
+ #Forge ZendSession
+ #dbparam['cookiename']='zend_cookie_session'
+ #dbparam['queryCookieMail']="""select substring(session_data,'email";s:[0-9]*?:"(.*)";s') from plugin_zendsession where session_hash='%s';"""
+ #dbparam['queryCookieId']="""SELECT substring(session_data,'user_id";i:([0-9]{1,})') FROM plugin_zendsession WHERE session_hash='%s';"""
+
+ ######################
+ # Type of connection #
+ ######################
+ db = PsycopgConnector(list,dbparam)
+ list._memberadaptor = db
+
+def extendSecurityManager(list):
+ sm = ForgeSecurityManager(list)
+ list._securitymanager = sm
+
+
--- /dev/null
+<?php
+
+/**
+ * GForge Mailing Lists Facility
+ *
+ * Portions Copyright 1999-2001 (c) VA Linux Systems
+ * The rest Copyright 2003-2004 (c) Guillaume Smet - Open Wide
+ *
+ * @version $Id$
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once ('env.inc.php');
+require_once 'pre.php';
+require_once('plugins_utils.php');
+require_once '../mailman_utils.php';
+
+$request =& HTTPRequest::instance();
+$group_id = $request->get('group_id');
+
+$feedback = '';
+
+if (!$group_id) {
+ exit_no_group();
+}
+$pm = ProjectManager::instance();
+$Group = $pm->getProject($group_id);
+if (!$Group || !is_object($Group) || $Group->isError()) {
+ exit_no_group();
+}
+if(isLogged()) {
+ if (!$current_user->isMember($group_id,'A')) {
+ exit_permission_denied();
+ }
+}
+
+
+$ml = new MailmanList($group_id,$request->get('id'));
+
+if ($request->exist('submit')) {
+ $sure = $request->get('sure');
+ $really_sure = $request->get('really_sure');
+ if (!$ml->deleteList($sure,$really_sure)) {
+ exit_error('Error',$ml->getErrorMessage());
+ } else {
+ htmlRedirect('index.php?group_id='.$group_id.'&feedback=DELETED');
+ }
+}
+
+mailman_header(array(
+ 'title' => _('Permanently Delete List')
+));
+
+?>
+<h3><?php echo $ml->getName(); ?></h3>
+<p>
+<form method="post" action="<?php echo $request->get('PHP_SELF'); ?>?group_id=<?php echo $group_id; ?>&id=<?php echo $ml->getID(); ?>">
+<input type="checkbox" name="sure" value="1"><?php echo _('Confirm Delete'); ?><br />
+<input type="checkbox" name="really_sure" value="1"><?php echo _('Confirm Delete'); ?><br />
+<input type="submit" name="submit" value="<?php echo _('Permanently Delete'); ?>">
+</form>
+</p>
+<?php
+
+mail_footer(array());
+
+?>
--- /dev/null
+<?php
+
+/**
+ * GForge Mailing Lists Facility
+ *
+ * Portions Copyright 1999-2001 (c) VA Linux Systems
+ * The rest Copyright 2003-2004 (c) Guillaume Smet - Open Wide
+ *
+ * @version $Id$
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once 'env.inc.php';
+require_once 'pre.php';
+require_once 'plugins_utils.php';
+require_once '../mailman_utils.php';
+
+$request =& HTTPRequest::instance();
+$group_id=$request->get('group_id');
+$group_list_id=$request->get('group_list_id');
+$pm = ProjectManager::instance();
+$Group = $pm->getProject($group_id);
+$post_changes= $request->get('post_changes');
+$add_list=$request->get('add_list');
+$change_status=$request->get('change_status');
+$list_name=$request->get('list_name');
+$is_public=$request->get('is_public');
+$description=$request->get('description');
+$PHP_SELF = $request->get('PHP_SELF');
+$feedback = '';
+
+
+if ($group_id) {
+ if (!$Group || !is_object($Group) || $Group->isError()) {
+
+ exit_no_group();
+ }
+
+ if (!$current_user->isMember($group_id,'A')) {
+ exit_permission_denied();
+ }
+
+ //
+ // Post Changes to database
+ //
+ if ($post_changes == 'y') {
+ //
+ // Add list
+ //
+ if ($add_list == 'y') {
+ $mailingList = new MailmanList($group_id);
+
+ if(!$mailingList || !is_object($mailingList)) {
+ exit_error(_('Error'), _('Error getting the list'));
+ } elseif($mailingList->isError()) {
+ exit_error(_('Error'), $mailingList->getErrorMessage());
+ }
+
+ if(!$mailingList->create(
+ $list_name,
+ $description,
+ $is_public
+ )) {
+ exit_error(_('Error'), $mailingList->getErrorMessage());
+ } else {
+ $feedback .= _('List Added');
+ }
+ //
+ // Change status
+ //
+ } elseif ($change_status == 'y') {
+ $mailingList = new MailmanList($group_id, $group_list_id);
+
+ if(!$mailingList || !is_object($mailingList)) {
+ exit_error(_('Error'), _('Error getting the list'));
+ } elseif($mailingList->isError()) {
+ exit_error(_('Error'), $mailingList->getErrorMessage());
+ }
+
+ if(!$mailingList->update(
+ $description,
+ $is_public
+ )) {
+
+ exit_error(_('Error'), $mailingList->getErrorMessage());
+ } else {
+ $feedback .= _('List updated');
+ }
+ }
+
+ }
+
+ //
+ // Form to add list
+ //
+ if($add_list) {
+ mailman_header(array(
+ 'title' => _('Add a Mailing List'),
+ 'help'=>'CommunicationServices.html#MailingLists',
+ 'admin' => '1'));
+ printf(_('<p>Lists are named in this manner:<br /><strong>projectname-listname@%1$s</strong></p><p>It will take <span class="important">6-24 Hours</span> for your list to be created.</p>'), $GLOBALS['sys_lists_host']);
+
+ $mlFactory = new MailmanListFactory($Group);
+ if (!$mlFactory || !is_object($mlFactory) || $mlFactory->isError()) {
+ exit_error(_('Error'), $mlFactory->getErrorMessage());
+ }
+
+ $mlArray =& $mlFactory->getMailmanLists();
+
+ if ($mlFactory->isError()) {
+ echo '<h1>'._('Error').' '._('Unable to get the lists') .'</h1>';
+ echo $mlFactory->getErrorMessage();
+ mail_footer(array());
+ exit;
+ }
+
+ //
+ // Form to add list
+ //
+ ?>
+ <form method="post" action="<?php echo $PHP_SELF; ?>?group_id=<?php echo $group_id ?>">
+ <input type="hidden" name="post_changes" value="y" />
+ <input type="hidden" name="add_list" value="y" />
+ <p><strong><?php echo _('Mailing List Name:'); ?></strong><br />
+ <strong><?php echo $Group->getUnixName(); ?>-<input type="text" name="list_name" value="" size="10" maxlength="12" />@<?php echo $GLOBALS['sys_lists_host']; ?></strong><br /></p>
+ <p>
+ <strong><?php echo _('Is Public?'); ?></strong><br />
+ <input type="radio" name="is_public" value="1" checked="checked" /> <?php echo _('Yes'); ?><br />
+ <input type="radio" name="is_public" value="0" /> <?php echo _('No'); ?></p><p>
+ <strong><?php echo _('Description:'); ?></strong><br />
+ <input type="text" name="description" value="" size="40" maxlength="80" /><br /></p>
+ <p>
+ <input type="submit" name="submit" value="<?php echo _('Add This List'); ?>" /></p>
+ </form>
+ <?php
+ mail_footer(array());
+
+ //
+ // Form to modify list
+ //
+ } elseif($change_status && $group_list_id) {
+ echo "je suis la group= et group_id=".$group_list_id;
+ $mailingList = new MailmanList($group_id, $group_list_id);
+echo "coucou";
+ if(!$mailingList || !is_object($mailingList)) {
+ echo "mailinglist error";
+ exit_error(_('Error'), _('Error getting the list'));
+ } elseif($mailingList->isError()) {
+ exit_error(_('Error'), $mailingList->getErrorMessage());
+ }
+echo "avant header";
+ mailman_header(array(
+ 'title' => _('Mail admin'),
+ 'help'=>'CommunicationServices.html#MailingLists',
+ 'admin' => 1));
+ echo "PHPSELF=".$PHP_SELF;
+ ?>
+
+ <h3><?php echo $mailingList->getName(); ?></h3>
+ <form method="post" action="<?php echo $PHP_SELF; ?>?group_id=<?php echo $group_id; ?>&group_list_id=<?php echo $mailingList->getID(); ?>">
+ <input type="hidden" name="post_changes" value="y" />
+ <input type="hidden" name="change_status" value="y" />
+ <p>
+ <strong><?php echo _('Is Public?'); ?></strong><br />
+ <input type="radio" name="is_public" value="1"<?php echo ($mailingList->isPublic() == 1 ? ' checked="checked"' : ''); ?> /> <?php echo _('Yes'); ?><br />
+ <input type="radio" name="is_public" value="0"<?php echo ($mailingList->isPublic() == 0 ? ' checked="checked"' : ''); ?> /> <?php echo _('No'); ?><br />
+ </p>
+ <p><strong><?php echo _('Description:'); ?></strong><br />
+ <input type="text" name="description" value="<?php echo $mailingList->getDescription(); ?>" size="40" maxlength="80" /><br /></p>
+ <p>
+ <input type="submit" name="submit" value="<?php echo _('Update'); ?>" /></p>
+ </form>
+ <?php
+ mail_footer(array());
+ } else {
+ //
+ // Show lists
+ //
+ $mlFactory = new MailmanListFactory($Group);
+ if (!$mlFactory || !is_object($mlFactory) || $mlFactory->isError()) {
+ exit_error(_('Error'), $mlFactory->getErrorMessage());
+ }
+
+ mailman_header(array(
+ 'title' => _('Mailing List Administration'),
+ 'help'=>'CommunicationServices.html#MailingLists',
+ 'admin'=>1)
+ );
+
+ $mlArray =& $mlFactory->getMailmanLists();
+
+ if ($mlFactory->isError()) {
+ echo '<p>'._('Error').' '.sprintf(_('Unable to get the list %s'), $Group->getPublicName()) .'</p>';
+ echo $mlFactory->getErrorMessage();
+ mail_footer(array());
+ exit;
+ }
+ echo '<p>'.sprintf(_('You can administrate lists from here. Please note that private lists can still be viewed by members of your project, but are not listed on %1$s.'), $GLOBALS['sys_name']).'</p>';
+ echo '<ul>
+ <li>
+ <a href="'.$PHP_SELF.'?group_id='.$group_id.'&add_list=1">'._('Add Mailing List').'</a>
+ </li>
+ </ul>';
+ $mlCount = count($mlArray);
+
+ if($mlCount > 0) {
+ table_begin();
+ for ($j = 0; $j < $mlCount; $j++) {
+ $currentList =& $mlArray[$j];
+ display_list_admin($currentList);
+ }
+
+ table_end();
+ }
+ mail_footer(array());
+ }
+} else {
+ exit_no_group();
+}
+?>
--- /dev/null
+<html>
+<body>
+<h1>Plugin Template</h1>
+</body>
+</html>
--- /dev/null
+<?php
+
+
+/*
+ * mailman plugin
+ *
+ * Daniel Perez <danielperez.arg@gmail.com>
+ *
+ * This is an example to watch things in action. You can obviously modify things and logic as you see fit
+ *
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ */
+require_once ('env.inc.php');
+require_once 'pre.php';
+require_once 'plugins_utils.php';
+require_once 'mailman_utils.php';
+$request =& HTTPRequest::instance();
+
+$group_id = $request->get('group_id');
+$pm = ProjectManager::instance();
+$Group = $pm->getProject($group_id);
+if (isset ($group_id)) {
+
+ if (!$Group || !is_object($Group)) {
+
+ exit_error(_('Error'), 'Could Not Get Group');
+ }
+ elseif ($Group->isError()) {
+
+ exit_no_group();
+ }
+
+
+ $mlFactory = new MailmanListFactory($Group);
+ if (!$mlFactory || !is_object($mlFactory)) {
+ exit_error(_('Error'), 'Could Not Get MailmanListFactory');
+ }
+ elseif ($mlFactory->isError()) {
+ exit_error(_('Error'), $mlFactory->getErrorMessage());
+ }
+
+ mailman_header(array (
+ 'title' => _('Mailing Lists for') . $Group->getPublicName(),
+ 'help' => 'CommunicationServices.html#MailingLists',
+ 'pv' => isset ($pv) ? $pv : false
+ ));
+
+ $mlArray = & $mlFactory->getMailmanLists();
+
+ if ($mlFactory->isError()) {
+ echo '<h1>' . _('Error') . ' ' . sprintf(_('Unable to get the list %s'), $Group->getPublicName()) . '</h1>';
+ echo $mlFactory->getErrorMessage();
+ mail_footer(array ());
+ exit;
+ }
+
+ $mlCount = count($mlArray);
+ if ($mlCount == 0) {
+ echo '<p>' . sprintf(_('No Lists found for %1$s'), $Group->getPublicName()) . '</p>';
+ echo '<p>' . _('Project administrators use the admin link to request mailing lists.') . '</p>';
+ mail_footer(array ());
+ exit;
+ }
+ personalized_message();
+
+
+ if (isLogged()){
+ if ($mlFactory->compareInfos()) {
+ echo _('<p>You seem to have mailman account with a different name or password. If you want to update mailman information, click on ');
+ echo '<a href="index.php?group_id=' . $group_id . '&action=update">' . _('Update</p>') . '</a>';
+ }
+ }
+
+ echo _('<p>Choose a list to browse, search, and post messages.</p>');
+
+ table_begin();
+ for ($j = 0; $j < $mlCount; $j++) {
+ $currentList = & $mlArray[$j];
+ display_list($currentList);
+ }
+
+ table_end();
+ if ($request->exist('action')) {
+ if ($request->exist('id')) {
+ $list = new MailmanList($group_id, $request->get('id'));
+ switch ($request->get('action')) {
+ case 'options' :
+ $list->getOptionsURL();
+ break;
+ case 'subscribe' :
+ $list->subscribe();
+ break;
+ case 'unsubscribe' :
+ $list->unsubscribe();
+ break;
+ case 'pipermail' :
+ $list->getArchivesUrl();
+ break;
+ case 'admin' :
+ $list->getExternalAdminUrl();
+ break;
+ default :
+ break;
+ }
+ }
+ if ($request->get('action') == 'update') {
+ $mlFactory->updateInfos();
+ }
+
+ }
+ mail_footer(array ());
+
+} else {
+
+ exit_no_group();
+
+}
+?>
--- /dev/null
+<?php
+/**
+ * FusionForge Mailing Lists Facility
+ *
+ * Portions Copyright 1999-2001 (c) VA Linux Systems
+ * The rest Copyright 2003 (c) Guillaume Smet
+ * Portions Copyright 2010 (c) Mélanie Le Bail
+ *
+ * @version $Id$
+ *
+ */
+
+
+require_once 'mailman/include/MailmanList.class.php';
+require_once 'mailman/include/MailmanListFactory.class.php';
+
+$current_user=UserManager::instance()->getCurrentUser();
+
+
+function sendCreationMail($userEmail,$listname,$listpassword) {
+
+ //$message = $Language->getText('mail_admin_index','list_create_explain',array($GLOBALS['sys_name'], $listname.'@' .$GLOBALS['sys_lists_host'],$list_server."/mailman/listinfo/$listname",$list_server."/mailman/admin/$list_name",$listpassword));
+
+ $hdrs = "From: ".$GLOBALS['sys_email_admin'].$GLOBALS['sys_lf'];
+ $hdrs .='Content-type: text/plain; charset=utf-8'.$GLOBALS['sys_lf'];
+
+ //mail ($userEmail,$GLOBALS['sys_name']." ".$Language->getText('mail_admin_index','new_mail_list'),$message,$hdrs);
+
+
+}
+function table_begin()
+{
+
+ echo "<table WIDTH=\"100%\" border=0>\n"."<TR><TD VALIGN=\"TOP\">\n";
+}
+function table_end()
+{
+ echo '</TD></TR></TABLE>';
+
+}
+function personalized_message()
+{
+
+ echo _('<p>Mailing lists provided via a GForge version of <a href="http://www.list.org/">GNU Mailman</a>. Thanks to the Mailman and <a href="http://www.python.org/">Python</a> crews for excellent software.</p>');
+}
+function display_list($currentList)
+{
+ $request =& HTTPRequest::instance();
+ $current_user=UserManager::instance()->getCurrentUser();
+
+ if($currentList->isPublic()!='9'){
+ if ($currentList->isError()) {
+ echo $currentList->getErrorMessage();
+ } else {
+ getIcon();
+ echo ' <b>'.$currentList->getName().'</b> [';
+ if($currentList->getStatus() == '3') {
+ echo _('Not activated yet');
+ } else {
+ echo ' <A HREF="index.php?group_id='.$request->get('group_id').'&action=pipermail&id='.$currentList->getID().'">'._('Archives').'</A>';
+ if(isLogged())
+ {
+ if ($currentList->isMonitoring()) {
+ echo ' | <a href="index.php?group_id='.$request->get('group_id').'&action=unsubscribe&id='.$currentList->getID().'">'._('Unsubscribe').' </a>';
+ echo ' | <a href="index.php?group_id='.$request->get('group_id').'&action=options&id='.$currentList->getID().'">'._('Preferences').'</a>';
+ } else {
+ echo ' | <a href="index.php?group_id='.$request->get('group_id').'&action=subscribe&id='.$currentList->getID().'">'._('Subscribe').'</a>';
+ }
+ if ($currentList->getListAdminID() == $current_user->getID()){
+ echo ' | <A HREF="index.php?group_id='. $request->get('group_id').'&action=admin&id='. $currentList->getID() .'">'._('Administrate').'</A> ';
+ }
+ }
+ }
+ echo ' ] <br> ';
+ echo htmlspecialchars($currentList->getDescription()).'<p>';
+
+ }
+ }
+
+}
+function display_list_admin($currentList)
+{
+ $request =& HTTPRequest::instance();
+ $current_user=UserManager::instance()->getCurrentUser();
+ if($currentList->isPublic()!='9'){
+ if ($currentList->isError() ) {
+ echo $currentList->getErrorMessage();
+ } else
+ {
+ getIcon();
+ echo ' <b>'.$currentList->getName().'</b> [';
+ }
+ if($currentList->getStatus() == '3') {
+ echo _('Not activated yet');
+ } else {
+
+ echo ' <A HREF="index.php?group_id='.$request->get('group_id').'&change_status=1&group_list_id='.$currentList->getID().'">'._('Update').'</A>';
+ echo ' | <a href="deletelist.php?group_id='.$currentList->Group->getID().'&id='.$currentList->getID().'">'. _('Delete').'</a>';
+
+ if ($currentList->getListAdminID() == $current_user->getID()){
+ echo ' | <A HREF="../index.php?group_id='. $request->get('group_id').'&action=admin&id='. $currentList->getID() .'">'._('Administrate from Mailman').'</A> ';
+ }
+ }
+
+ echo ' ] <br> ';
+ echo htmlspecialchars($currentList->getDescription()).'<p>';
+ }
+}
+
+
+function mailman_header($params) {
+ global $group_id;
+ $current_user=UserManager::instance()->getCurrentUser();
+ $request =& HTTPRequest::instance();
+
+ //required for site_project_header
+ $params['group'] = $request->get('group_id');
+ $params['toptab'] = 'mailman';
+
+ site_project_header($params);
+ echo '<P><B>';
+ // admin link is only displayed if the user is a project administrator
+ if ($current_user->isMember($request->get('group_id'),'A')) {
+ if (isset($params['admin'])){
+ echo '<A HREF="index.php?group_id='.$request->get('group_id').'">'._('Administration').'</A>';
+ }
+ else{
+ echo '<A HREF="admin/index.php?group_id='.$request->get('group_id').'">'._('Administration').'</A>';
+ }
+
+ }
+ if ($params['help']) {
+ helpButton($params['help']);
+ }
+
+}
+
+function mail_footer($params) {
+ site_project_footer($params);
+}
+
+
+// Local Variables:
+// mode: php
+// c-file-style: "bsd"
+// End:
+
+?>