#! /usr/bin/perl

=head1 NAME

project_members - lists of project members

=head1 SYNOPSIS

 https://server/schulkonsole/project_list

=head1 DESCRIPTION

C<project_list> displays lists of all members in a selected project and
allows to perform a query for possible new members.
The HTML template is project_members.tt.

=head2 Template variables

Additionally to the variables of Schulkonsole::Session C<project_members>
provides the following variables:

=over

=cut

use strict;
use utf8;
use lib '/usr/share/schulkonsole';
use Schulkonsole::Session;
use Schulkonsole::Info;
use Schulkonsole::Sophomorix;
use Schulkonsole::Cyrus;
use Schulkonsole::Repair;
use Proc::ProcessTable;

my $this_file = 'project_members';


my $sk_session = new Schulkonsole::Session($this_file);
if (not $sk_session->get_password()) {
	my $q = new CGI;
	my $url = $q->url( -full => 1 );

	# we send cookies over secure connections only
	if ($url =~ s/^http:/https:/g) {
		$sk_session->redirect($url);
	} else {
		$sk_session->exit_with_login_page($this_file);
	}
}

my $q = $sk_session->query();

my $projects = Schulkonsole::Info::groups_projects($sk_session->groups());
my $classs = Schulkonsole::Info::groups_classes($sk_session->groups());

my $project = $q->param('projects');
if (    $project
    and $$projects{$project}) {
	$sk_session->param('project', $project);
}
$project = $sk_session->param('project');

if (   not $project
    or not $$projects{$project}) {
	my $url = $q->url( -absolute => 1 );
	$url =~ s/$this_file$/project/g;
	$sk_session->param('requested_page',$this_file);
	$sk_session->redirect($url);
}



my $id = $sk_session->userdata('id');
my $password = $sk_session->get_password();
my $admins = Schulkonsole::DB::project_admins($$projects{$project}{id});
my $is_admin = $$admins{$sk_session->userdata('uidnumber')};
my $is_joinable = $$projects{$project}{joinable};

my $process_table = new Proc::ProcessTable;
my $app_repair = $Schulkonsole::Config::_cmd_sophomorix_repair;
$app_repair =~ s:.*/::;
my %running;
foreach my $process (@{ $process_table->table }) {
	if ($process->fname =~ /^soph/ and $process->cmndline =~ /$app_repair/) {
			$running{repair} = $process->pid;
	}
}

eval {
COMMANDS: {

$q->param('repair_projecthomes;'.$project) and do {
	if (not $is_admin) {
		$sk_session->set_status($sk_session->d()-get(
				'Sie sind kein Administrator dieses Projekts'), 1);
		last COMMANDS;
	}
	Schulkonsole::Repair::repair_projecthomes($id, $password, $project);
	$sk_session->set_status($sk_session->d()->get(
				'Reparatur gestartet...'), 0);
	$running{repair} = 1;
	last COMMANDS;
};

$q->param('projectisopenaccept') and do {
	if (not $is_admin) {
		$sk_session->set_status($sk_session->d()->get(
				'Sie sind kein Administrator dieser Klasse'),
			1);
		last COMMANDS;
	}

	if ($q->param('projectisopen')) {
		if (not $is_joinable) {
			Schulkonsole::Sophomorix::set_project_joinable(
				$sk_session->userdata('id'),
				$sk_session->get_password(),
				$project,
				1);
			$sk_session->set_status($sk_session->d()->get(
					'Das Projekt wurde geöffnet'),
				0);
			$is_joinable = 1;
		} else {
			$sk_session->set_status($sk_session->d()->get(
					'Das Projekt ist bereits geöffnet'),
				1);
		}
	} else {
		if ($is_joinable) {
			Schulkonsole::Sophomorix::set_project_joinable(
				$sk_session->userdata('id'),
				$sk_session->get_password(),
				$project,
				0);
			$is_joinable = 0;
			$sk_session->set_status($sk_session->d()->get(
					'Das Projekt wurde geschlossen'),
				0);
		} else {
			$sk_session->set_status($sk_session->d()->get(
					'Das Projekt ist bereits geschlossen'),
				1);
		}
	}


	last COMMANDS;
};

foreach my $param ($q->param) {
	if (my ($member, $action, $scope, @rest) = $param
	    	=~ /^(.+)_(goto|remove)(class|project|user)$/) {
		if ($action =~ /^g/) {	# goto
			if ($scope =~ /^c/) {	# class
				if ($$classs{$member}) {
					$sk_session->param('class', $member);

					my $url = $q->url( -absolute => 1 );
					$url =~ s/$this_file$/class_list/g;
					$sk_session->redirect($url);
				} else {
					$sk_session->set_status($sk_session->d()->get(
							'Sie sind kein Mitglied dieser Klasse'),
						1);
				}
			} elsif ($scope =~ /^p/) {	# project
				if ($$projects{$member}) {
					$sk_session->param('project', $member);
					$project = $member;
				} else {
					$sk_session->set_status($sk_session->d()->get(
							'Sie sind kein Mitglied dieses Projekts'),
						1);
				}
			}
		} else {	# remove
			my $id = $sk_session->userdata('id');
			my $username = $sk_session->userdata('uid');
			my $password = $sk_session->get_password();

			if ($is_admin or $username eq $member) {
				SCOPE: {
				$scope =~ /^u/ and do {	# user
					my $member_userdata
						= Schulkonsole::DB::get_userdata($member);
					if (not $member_userdata) {
						$sk_session->set_status(
							sprintf($sk_session->d()->get(
								'Benutzer %s gibt es nicht.'), $member));
						last SCOPE;
					}

					my $is_member_admin = Schulkonsole::DB::is_project_admin(
						$$projects{$project}{id},
						$$member_userdata{uidnumber});

					if ($is_member_admin) {
						if ($is_member_admin == -1) {
							$sk_session->set_status(
								sprintf($sk_session->d()->get(
									'Sie sind der letzte Projektleiter von %s'),
									$$projects{$project}{displayname}),
								1);

							last COMMANDS;
						}

						Schulkonsole::Sophomorix::remove_admin_from_project(
							$id, $password,
							$project,
							$member);
					}

					Schulkonsole::Sophomorix::remove_from_project(
						$id, $password,
						$project,
						$member);

					if ($member eq $sk_session->userdata('uid')) {
						$sk_session->set_status_redirect(
							sprintf($sk_session->d()->get(
								'%s aus Projekt %s ausgetragen'),
								$member, $$projects{$project}{displayname}),
							0);

						my $url = $q->url( -absolute => 1 );
						$url =~ s/$this_file$/project/g;
						$sk_session->redirect($url);
					}

					$sk_session->set_status(
						sprintf($sk_session->d()->get(
							'%s aus Projekt %s ausgetragen'),
							$member, $$projects{$project}{displayname}),
						0);


					last SCOPE;
				};
				$scope =~ /^c/ and do {	# class
					Schulkonsole::Sophomorix::remove_class_from_project(
						$id, $password,
						$project,
						$member);

					$sk_session->set_status(
						sprintf($sk_session->d()->get(
							'%s aus Projekt %s ausgetragen'),
							$member,
							$$projects{$project}{displayname}),
						0);

					last SCOPE;
				};
				$scope =~ /^p/ and do {	# project
					Schulkonsole::Sophomorix::remove_project_from_project(
						$id, $password,
						$project,
						$member);

					$sk_session->set_status(
						sprintf($sk_session->d()->get(
							'%s aus Projekt %s ausgetragen'),
							$member,
							$$projects{$project}{displayname}),
						0);

					last SCOPE;
				};
				}
			} else {
				$sk_session->set_status(
					sprintf($sk_session->d()->get(
						'Sie sind kein Projektleiter von %s'),
						$$projects{$project}{displayname}),
					1);

				last COMMANDS;
			}
		}

		last COMMANDS;
	}
}

} # end COMMANDS
};
if ($@) {
	$sk_session->standard_error_handling($this_file, $@);
}





my $member_userdatas
	= Schulkonsole::DB::project_user_members($$projects{$project}{gidnumber});
my $member_classdatas
	= Schulkonsole::DB::project_class_members($$projects{$project}{id});
my $member_projectdatas
	= Schulkonsole::DB::project_project_members($$projects{$project}{id});


my $member_quotaroots = Schulkonsole::Cyrus::quota($sk_session->userdata('id'), 
							$sk_session->get_password(), 
							keys %$member_userdatas);



if ($$admins{$sk_session->userdata('uidnumber')}) {

=item C<isadmin>

Indicates that the current user is an administrator of the selected project

=cut

	$sk_session->set_var('isadmin', 1);

=item C<joinable>

Indicates that the selected project is open
(and adding new members is possible)

=cut

	$sk_session->set_var('isopen', $is_joinable);
}


my @student_members;
my @teacher_members;
foreach my $user (sort {
	$$member_userdatas{$a}{surname} cmp $$member_userdatas{$b}{surname} }
	keys %$member_userdatas) {

	my $groups = Schulkonsole::DB::user_groups(
		$$member_userdatas{$user}{uidnumber},
		$$member_userdatas{$user}{gidnumber},
		$$member_userdatas{$user}{gid});

	if ($$groups{teachers}) {
		my $member = {
			login => $user,
			firstname => $$member_userdatas{$user}{firstname},
			surname => $$member_userdatas{$user}{surname},
			isadmin => $$admins{$$member_userdatas{$user}{uidnumber}},
		};

		push @teacher_members, $member;
	} else {
		my $quotaemail_usage;
		my $quotaemail_limit;
		my $quotaemail_percent;
		if ($$member_quotaroots{$user}) {
			$quotaemail_usage
				= $$member_quotaroots{$user}{"user.$user"}{quota}{STORAGE}{usage};
			$quotaemail_limit
				= $$member_quotaroots{$user}{"user.$user"}{quota}{STORAGE}{limit};
			$quotaemail_percent = sprintf("%.1f", 100 *
			                              $quotaemail_usage / $quotaemail_limit);
		} else {
			$quotaemail_percent = '0.0';
		}
	
		my $disk_quotas =
			Schulkonsole::Info::disk_quotas($$member_userdatas{$user}{uidnumber});
	
		my @array_diskquotas;
		foreach my $disk_quota (@$disk_quotas) {
		    my ($filesystem, $path,
			    $blocks_usage, $blocks_soft, $blocks_hard,
			    $blocks_grace,
			    $files_usage, $files_soft, $files_hard,
			    $files_grace) = @$disk_quota;
			next unless (defined $files_hard and ($blocks_hard or $files_hard));
	
			my $blocks_percent = $blocks_soft ?
				  sprintf('%.1f', 100 * $blocks_usage / $blocks_soft)
				: 0;
			my $files_percent = $files_soft ?
				  sprintf('%.1f', 100 * $files_usage / $files_soft)
				: 0;
	
			my $blocks_grace_str;
			if ($blocks_grace) {
				$blocks_grace -= $^T;
				if ($blocks_grace > 86400) {
					$blocks_grace_str = sprintf($sk_session->d()->get('%d Tage'),
						$blocks_grace / 86400 + 0.5);
				} elsif ($blocks_grace > 7200) {
					$blocks_grace_str = sprintf($sk_session->d()->get('%d Stunden'),
						$blocks_grace / 3600 + 0.5);
				} elsif ($blocks_grace > 0) {
					$blocks_grace_str = sprintf($sk_session->d()->get('%d Minuten'),
						$blocks_grace / 60 + 0.5);
				} else {
					$blocks_grace_str = $sk_session->d()->get('abgelaufen');
				}
			} else {
				$blocks_grace_str = '-';
			}
		
			my $files_grace_str;
			if ($files_grace) {
				$files_grace -= $^T;
				if ($files_grace > 86400) {
					$files_grace_str = sprintf($sk_session->d()->get('%d Tage'),
						$files_grace / 86400 + 0.5);
				} elsif ($files_grace > 7200) {
					$files_grace_str = sprintf($sk_session->d()->get('%d Stunden'),
						$files_grace / 3600 + 0.5);
				} elsif ($files_grace > 0) {
					$files_grace_str = sprintf($sk_session->d()->get('%d Minuten'),
						$files_grace / 60 + 0.5);
				} else {
					$files_grace_str = $sk_session->d()->get('abgelaufen');
				}
			} else {
				$files_grace_str = '-';
			}
	
			my $diskquota = {
				'warn' => $blocks_grace || $files_grace,
				path => $path,
				blocks_usage => $blocks_usage,
				blocks_soft => $blocks_soft,
				blocks_hard => $blocks_hard,
				blocks_percent => $blocks_percent,
				blocks_percent_rounded => ($blocks_percent < 100.0 ?
				                             int($blocks_percent + 0.5)
				                           : 100),
				blocks_grace => $blocks_grace_str,
				files_usage => $files_usage,
				files_soft => $files_soft,
				files_hard => $files_hard,
				files_percent => $files_percent,
				files_percent_rounded => ($files_percent < 100.0 ?
				                            int($files_percent + 0.5)
				                          : 100),
				files_grace => $files_grace_str,
			};
	
			push @array_diskquotas, $diskquota;
		}
	
		my $mailquotas =  { usage => $quotaemail_usage,
		                    limit => $quotaemail_limit,
		                    percent => $quotaemail_percent,
							percent_rounded => ($quotaemail_percent ?
							                      int($quotaemail_percent + 0.5)
							                    : 100),
		                    'warn' => ($quotaemail_usage > $quotaemail_limit) };

		my @classs;
		my $classs = Schulkonsole::Info::groups_classes($groups);
		foreach my $class (values %$classs) {
			push @classs, $$class{displayname};
		}
		my $var_classes_str = join ', ', @classs;

		my $member = {
			login => $user,
			firstname => $$member_userdatas{$user}{firstname},
			surname => $$member_userdatas{$user}{surname},
			diskquotas => \@array_diskquotas,
			mailquotas => $mailquotas,
			class => $var_classes_str,
		};

		push @student_members, $member;
	}


}

=item C<membersstudents>

The student members of the selected project as an array of hashes with
the keys:

=over

=item C<login>

The login of a user

=item C<firstname>

The user's first name

=item C<surname>

The user's surname

=item C<diskquotas>

The user's disk quotas in an array of hashes with the keys

=over

=item C<path>

A file path

=item C<blocks_usage>

The user's blocks usage in this path

=item C<blocks_soft>

The user's soft blocks limit for this path

=item C<blocks_hard>

The user's hard blocks limit for this path

=item C<blocks_percent>

The user's blocks usage in percent in this path

=item C<blocks_percent_rounded>

The user's blocks usage in percent in this path rounded to an integer

=item C<blocks_grace>

Time for this user to get below soft blocks limit

=item C<files_usage>

The user's file usage in this path

=item C<files_soft>

The user's soft files limit for this path

=item C<files_hard>

The user's hard files limit for this path

=item C<files_percent>

The user's files usage in percent in this path

=item C<files_percent_rounded>

The user's files usage in percent in this path rounded to an integer

=item C<files_grace>

Time for this user to get below soft files limit

=item C<warn>

True if over blocks or files quota

=back

=item C<mailquotas>

The user's INBOX mail quota in a hash with the keys

=over

=item C<usage>

The user's INBOX usage

=item C<limit>

The user's INBOX limit

=item C<percent>

The user's INBOX usage in percent

=item C<percent_rounded>

The user's INBOX usage in percent rounded to an integer

=item C<warn>

True if user is over quota

=back

=item C<class>

The user's class

=back

=cut

$sk_session->set_var('membersstudents', \@student_members);

=item C<membersteachers>

The teacher members of the selected project as an array of hashes with
the keys:

=over

=item C<login>

The login of a user

=item C<firstname>

The user's first name

=item C<surname>

The user's surname

=item C<isadmin>

True if the teacher is an administrator of the selected project

=back

=cut

$sk_session->set_var('membersteachers', \@teacher_members);



my @class_members;
foreach my $class (sort {
	    $$member_classdatas{$a}{displayname}
	cmp $$member_classdatas{$b}{displayname} }
	keys %$member_classdatas) {
	my $member = {
		gid => $$member_classdatas{$class}{gid},
		name => $$member_classdatas{$class}{displayname},
		ismember => defined $$classs{$class},
	};
	push @class_members, $member;
}

=item C<membersclasses>

The class members of the selected project as an array of hashes with
the keys:

=over

=item C<gid>

GID of a class

=item C<name>

The class' name

=item C<ismember>

True if the current user is a member of the class

=back

=cut

$sk_session->set_var('membersclasses', \@class_members);



my @project_members;
foreach my $project (sort {
	    $$member_projectdatas{$a}{displayname}
	cmp $$member_projectdatas{$b}{displayname} }
	keys %$member_projectdatas) {
	my $member = {
		gid => $$member_projectdatas{$project}{gid},
		name => $$member_projectdatas{$project}{displayname},
		ismember => defined $$projects{$project},
		isopen => $$member_projectdatas{$project}{joinable},
	};
	push @project_members, $member;
}

=item C<membersprojects>

The project members of the selected project as an array of hashes with
the keys:

=over

=item C<gid>

GID of a project

=item C<name>

The project's name

=item C<ismember>

True if the current user is a member of the project

=back

=cut

$sk_session->set_var('membersprojects', \@project_members);







my @projects;
foreach my $project (sort {
	$$projects{$a}{displayname} cmp $$projects{$b}{displayname} }
	                 keys %$projects) {
	push @projects, { gid => $project,
	                  name => $$projects{$project}{displayname},
	                };
}

=item C<projects>

Projects of the current user as an array of hashes with the keys

=over

=item C<gid>

GID of the project

=item C<name>

Name of the project

=item C<addquota>

Additional quota for members of this project

=item C<isadmin>

True if the current user is an administrator of the project

=item C<islastadmin>

True if there are no other administrators of the project but the current
user

=back

=cut

$sk_session->set_var('projects', \@projects);


if ($project) {

=item C<project_gid>

GID of the selected project

=cut

	$sk_session->set_var('project_gid', $project);

=item C<project>

Name of the selected project

=cut

	$sk_session->set_var('project', $$projects{$project}{displayname});

}

if($q->param('logrepair') or $running{repair}) {
	eval {
		my $logrepair =
			Schulkonsole::Repair::read_repair_log_file($id, $password);

=item C<logrepair>

Output of C<sophomorix-repair ...>

=cut

		splice @$logrepair, 0, -30;
		$sk_session->set_var('logrepair', join("", @$logrepair));
	};
	if ($@) {
		$sk_session->set_status(
			$sk_session->d()->get('Kann Logdatei nicht öffnen'), 1);
	}
	
}

=item C<isbusy>

True if C<sophomorix-repair> is running

=cut

$sk_session->set_var('isbusy', ($running{repair}? 1 : 0));

$sk_session->print_page("$this_file.tt", $this_file);


=back

=head2 Form fields

=over

=item C<projects>

Select the project with this GID.
Possible values read in loop over template variable C<projects>.

=item C<query>

Query to search for possible new members

=item C<scope>

Scope of query. Possible values are C<student>, C<teacher>, C<class>,
and C<project>

=item C<${membersclasses{gid}}_gotoclass>

Select class with GID C<$membersclasses{gid}> and show class page.
Usually created in loop over template variable C<membersclasses>

=item C<${membersclasses{gid}}_removeclass>

Remove class with GID C<$membersclasses{gid}> from project.
Created in loop over template variable C<membersclasses>

=item C<${membersprojects{gid}}_gotoproject>

Select project with GID C<$membersprojects{gid}> and show project page.
Usually created in loop over template variable C<membersprojects>

=item C<${membersprojects{gid}}_removeproject>

Remove project with GID C<$membersprojects{gid}> from project.
Created in loop over template variable C<membersprojects>

=item C<${membersteachers{login}}_removeuser>/C<${membersstudents{login}}_removeuser>

Remove user from project.
Created in loops over template variables C<membersteachers> and
C<membersstudents>

=back

