Commit a3ac522a authored by Sofia Papagiannaki's avatar Sofia Papagiannaki

Progress VI

parent 612c385e
[
{
"model": "im.memberrejectpolicy",
"model": "im.memberleavepolicy",
"pk": 1,
"fields": {
"policy": "auto_accept",
......@@ -8,7 +8,7 @@
}
},
{
"model": "im.memberrejectpolicy",
"model": "im.memberleavepolicy",
"pk": 2,
"fields": {
"policy": "owner_accepts",
......@@ -16,7 +16,7 @@
}
},
{
"model": "im.memberrejectpolicy",
"model": "im.memberleavepolicy",
"pk": 3,
"fields": {
"policy": "closed",
......
[
{
"model": "im.memberacceptpolicy",
"model": "im.memberjoinpolicy",
"pk": 1,
"fields": {
"policy": "auto_accept",
......@@ -8,7 +8,7 @@
}
},
{
"model": "im.memberacceptpolicy",
"model": "im.memberjoinpolicy",
"pk": 2,
"fields": {
"policy": "owner_accepts",
......@@ -16,7 +16,7 @@
}
},
{
"model": "im.memberacceptpolicy",
"model": "im.memberjoinpolicy",
"pk": 3,
"fields": {
"policy": "closed",
......
......@@ -922,8 +922,15 @@ class ProjectApplicationForm(forms.ModelForm):
class Meta:
model = ProjectDefinition
exclude = ('resource_grants', 'serial')
exclude = ('resource_grants', 'id')
def __init__(self, *args, **kwargs):
super(ProjectApplicationForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance')
if instance:
self.initial['comments'] = instance.projectapplication.comments
def clean(self):
userid = self.data.get('user', None)[0]
self.user = None
......@@ -960,8 +967,7 @@ class ProjectApplicationForm(forms.ModelForm):
return policies
def save(self, commit=True):
definition = super(ProjectApplicationForm, self).save(commit=commit)
definition.resource_policies=self.resource_policies
definition = super(ProjectApplicationForm, self).save(commit=False)
applicant = self.user
comments = self.cleaned_data.pop('comments', None)
try:
......@@ -970,6 +976,7 @@ class ProjectApplicationForm(forms.ModelForm):
precursor_application = None
return ProjectApplication.submit(
definition,
self.resource_policies,
applicant,
comments,
precursor_application,
......@@ -984,8 +991,32 @@ class ProjectSortForm(forms.Form):
('definition__start_date', 'Sort by Start Date'),
('definition__end_date', 'Sort by End Date'),
# ('approved_members_num', 'Sort by Participants'),
('definition__member_accept_policy', 'Sort by Member Accept Policy'),
('definition__member_reject_policy', 'Sort by Member Reject Policy')
('definition__member_join_policy__description', 'Sort by Member Join Policy'),
('definition__member_leave_policy__description', 'Sort by Member Leave Policy')
),
required=True
)
\ No newline at end of file
)
class AddProjectMembersForm(forms.Form):
q = forms.CharField(
max_length=800, widget=forms.Textarea, label=_('Add members'),
help_text=_(astakos_messages.ADD_PROJECT_MEMBERS_Q_HELP),
required=True)
def clean(self):
q = self.cleaned_data.get('q') or ''
users = q.split(',')
users = list(u.strip() for u in users if u)
db_entries = AstakosUser.objects.filter(email__in=users)
unknown = list(set(users) - set(u.email for u in db_entries))
if unknown:
raise forms.ValidationError(_(astakos_messages.UNKNOWN_USERS) % ','.join(unknown))
self.valid_users = db_entries
return self.cleaned_data
def get_valid_users(self):
"""Should be called after form cleaning"""
try:
return self.valid_users
except:
return ()
\ No newline at end of file
......@@ -347,7 +347,7 @@ class SendFeedbackError(SendMailError):
class ChangeEmailError(SendMailError):
def __init__(self):
self.message = self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
self.message = _(astakos_messages.CHANGE_EMAIL_SEND_ERR)
super(ChangeEmailError, self).__init__()
......
......@@ -33,26 +33,44 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from astakos.im.models import approve_application, ProjectApplication
class Command(BaseCommand):
args = "<project application serial>"
help = "Update project state"
def handle(self, *args, **options):
if len(args) < 1:
raise CommandError("Please provide a group identifier")
serial = args[0]
try:
approve_application(serial)
except ProjectApplication.DoesNotExist, e:
raise CommandError("Invalid serial")
except Exception, e:
import traceback
traceback.print_exc()
raise CommandError("Project application approval failed with: %s" % e)
else:
self.stdout.write("Project application has been successfully approved")
\ No newline at end of file
from django.core.management.base import NoArgsCommand
from astakos.im.models import ProjectApplication
class Command(NoArgsCommand):
help = "List resources"
option_list = NoArgsCommand.option_list + (
make_option('-c',
action='store_true',
dest='csv',
default=False,
help="Use pipes to separate values"),
)
def handle_noargs(self, **options):
apps = ProjectApplication.objects.select_related().all()
labels = ('id', 'name', 'status')
columns = (3, 40, 10)
if not options['csv']:
line = ' '.join(l.rjust(w) for l, w in zip(labels, columns))
self.stdout.write(line + '\n')
sep = '-' * len(line)
self.stdout.write(sep + '\n')
for app in apps:
fields = (
str(app.id),
app.definition.name,
app.status
)
if options['csv']:
line = '|'.join(fields)
else:
line = ' '.join(f.rjust(w) for f, w in zip(fields, columns))
self.stdout.write(line.encode('utf8') + '\n')
......@@ -34,74 +34,67 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from astakos.im.models import approve_application,
from astakos.im.models import _lookup_object, ProjectApplication, Project
@transaction.commit_on_success
class Command(BaseCommand):
args = "<project application serial>"
args = "<project application id>"
help = "Update project state"
option_list = BaseCommand.option_list + (
make_option('--approve',
action='store_true',
dest='approve',
default=False,
help="Approve group"),
make_option('--terminate',
action='store_true',
dest='terminate',
default=False,
help="Enable group"),
help="Terminate group"),
make_option('--suspend',
action='store_true',
dest='suspend',
default=False,
help="Disable group"),
make_option('--activate',
action='store_true',
dest='activate',
default=False,
help="Disable group"),
help="Suspend group")
)
def handle(self, *args, **options):
if len(args) < 1:
raise CommandError("Please provide a group identifier")
group = None
try:
if args[0].isdigit():
group = AstakosGroup.objects.get(id=args[0])
else:
group = AstakosGroup.objects.get(name=args[0])
except AstakosGroup.DoesNotExist, e:
raise CommandError("Invalid group")
app = None
p = None
try:
pname = options.get('add-permission')
if pname:
r, created = add_group_permission(group, pname)
if created:
self.stdout.write(
'Permission: %s created successfully\n' % pname)
if r == 0:
self.stdout.write(
'Group has already permission: %s\n' % pname)
else:
self.stdout.write(
'Permission: %s added successfully\n' % pname)
pname = options.get('delete-permission')
if pname:
r = remove_group_permission(group, pname)
if r < 0:
self.stdout.write(
'Invalid permission codename: %s\n' % pname)
elif r == 0:
self.stdout.write('Group has not permission: %s\n' % pname)
elif r > 0:
self.stdout.write(
'Permission: %s removed successfully\n' % pname)
id = int(args[0])
except ValueError:
raise CommandError('Invalid id')
else:
try:
# Is it a project application id?
app = _lookup_object(ProjectApplication, id=id)
except ProjectApplication.DoesNotExist:
try:
# Is it a project id?
p = _lookup_object(Project, id=id)
except Project.DoesNotExist:
raise CommandError('Invalid id')
try:
if options['approve']:
if not app:
raise CommandError('Project application id is required.')
app.approve()
if options.get('enable'):
group.enable()
elif options.get('disable'):
group.disable()
if app and app.status != 'Pending':
p = app.project
except Exception, e:
raise CommandError(e)
if options['terminate']:
p.terminate()
if options['suspend']:
p.suspend()
except BaseException, e:
import traceback
traceback.print_exc()
raise CommandError(e)
......@@ -129,9 +129,12 @@ INVALID_KEY_PARAMETER = 'Invalid key.'
DOMAIN_VALUE_ERR = 'Enter a valid domain.'
QH_SYNC_ERROR = 'Failed to get synchronized with quotaholder.'
UNIQUE_PROJECT_NAME_CONSTRAIN_ERR = 'The project name (as specified in its application\'s definition) must be unique among all active projects.'
INVALID_PROJECT = 'Project %(serial)s is invalid.'
NOT_ALIVE_PROJECT = 'Project %(serial)s is not alive.'
INVALID_PROJECT = 'Project %(id)s is invalid.'
NOT_ALIVE_PROJECT = 'Project %(id)s is not alive.'
NOT_ALLOWED = 'You do not have the permissions to perform this action.'
MEMBER_NUMBER_LIMIT_REACHED = 'Maximum participant number has been reached.'
MEMBER_ACCEPT_POLICY_CLOSED = 'The project member accept policy is cloesd.'
NO_APPLICANT = 'Project application requires an applicant. None found.'
\ No newline at end of file
MEMBER_JOIN_POLICY_CLOSED = 'The project member join policy is cloesd.'
MEMBER_LEAVE_POLICY_CLOSED = 'The project member leave policy is cloesd.'
NOT_MEMBERSHIP_REQUEST = 'There is no such a membership request.'
NO_APPLICANT = 'Project application requires an applicant. None found.'
ADD_PROJECT_MEMBERS_Q_HELP = 'Add comma separated user emails, eg. user1@user.com, user2@user.com'
\ No newline at end of file
This diff is collapsed.
......@@ -34,6 +34,8 @@
import logging
import socket
from smtplib import SMTPException
from django.conf import settings
from django.core.mail import send_mail
from django.utils.translation import ugettext as _
......@@ -64,14 +66,11 @@ class EmailNotification(Notification):
self.sender,
self.recipients
)
except BaseException, e:
except (SMTPException, socket.error), e:
logger.exception(e)
raise SendNotificationError()
class SendMailError(Exception):
pass
raise NotificationError()
class SendNotificationError(SendMailError):
class NotificationError(Exception):
def __init__(self):
self.message = _(astakos_messages.NOTIFICATION_SEND_ERR)
super(SendNotificationError, self).__init__()
super(NotificationError, self).__init__()
\ No newline at end of file
......@@ -42,7 +42,7 @@
</dl>
</div>
<div class="editable" style="display:none;">
<form action="{% url project_detail object.serial %}" method="post"
<form action="{% url project_detail object.id %}" method="post"
class="withlabels">{% csrf_token %}
{% with update_form as form %}
{% include "im/form_render.html" %}
......@@ -69,9 +69,13 @@
Not set yet
{% endif %}
</dd>
<dt>Member accept policy</dt>
<dt>Member join policy</dt>
<dd>
{{ object.definition.member_accept_policy }}
{{ object.definition.member_join_policy }}
</dd>
<dt>Member leave policy</dt>
<dd>
{{ object.definition.member_leave_policy }}
</dd>
<dt>Issue date:</dt>
<dd>{{object.issue_date|date:"d/m/Y"}}&nbsp;</dd>
......@@ -79,8 +83,8 @@
<dd>{{object.definition.start_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>End Date</dt>
<dd>{{object.definition.end_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>Activated</dt>
<dd>{% if object.is_active %}Yes{% else %}No{% endif %}</dd>
<dt>Status</dt>
<dd>{{ object.status }}</dd>
<dt>Owner</dt>
<dd>{% if user == object.owner %}
Me
......@@ -91,6 +95,20 @@
</dd>
<dt>Max participants</dt>
<dd>{% if object.definition.limit_on_members_number%}{{object.definition.limit_on_members_number}}{% else %}&nbsp;{% endif %}</dd>
<dt>Precursor Application</dt>
<dd>
{% if object.precursor_application %}
<a href="{% url project_detail object.precursor_application.id %}">{{object.precursor_application.id}}</a>
{% endif %}
&nbsp;
</dd>
<dt>Follower Application</dt>
<dd>
{% if object.follower %}
<a href="{% url project_detail object.follower.id %}">{{object.follower.id}}</a>
{% endif %}
&nbsp;
</dd>
</dl>
</div>
<div class="full-dotted">
......@@ -130,7 +148,7 @@
<p>No resources</p>
{% endif %}
</div>
{% if object.is_alive %}
{% if object.project.is_alive %}
<div class="full-dotted">
{% with page|concat:sorting as args %}
{% with object.project.projectmembership_set.select_related.all|paginate:args as membership %}
......@@ -148,7 +166,7 @@
<caption>MEMBERS:</caption>
<thead>
<tr>
<th>User Email</th>
{%if user.is_superuser or user == object.owner %}<th>User Email</th>{% endif %}
<th>Name</th>
<th>Status</th>
</tr>
......@@ -156,22 +174,22 @@
<tbody>
{% for m in membership.object_list %}
<tr>
<td>{%if user.is_superuser or user == o.owner %}{{m.person.email}}{% endif %}</td>
{%if user.is_superuser or user == object.owner %}<td>{{m.person.email}}</td>{% endif %}
<td>{{m.person.realname}}</td>
{% if m.person == o.owner %}
{% if m.person == object.owner %}
<td>Owner</td>
{% else %}
{% if m.is_accepted %}
{% if m.acceptance_date %}
<td>Approved
{% if user == object.owner %}
<a href="{% url project_disapprove_member object.serial m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
{% if user == object.owner and user != m.person %}
<a href="{% url project_remove_member object.id m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
{% endif %}
</td>
{% else %}
<td>Pending
{% if user == object.owner %}
<a href="{% url project_approve_member object.serial m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Accept</a>
<a href="{% url project_disapprove_member object.serial m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
<a href="{% url project_approve_member object.id m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Accept</a>
<a href="{% url project_remove_member object.id m.person.id %}?{% if page %}page={{ page }}{% endif %}{% if sorting %}&sorting={{sorting}}{% endif %}">Remove</a>
{% endif %}
</td>
{% endif %}
......@@ -205,7 +223,7 @@
<div class="full-dotted">
<form action="{% url project_detail object.serial %}#members-table" method="post" class="withlabels" >{% csrf_token %}
<form action="{% url project_detail object.id %}#members-table" method="post" class="withlabels" >{% csrf_token %}
<h2>Enroll more members</h2>
{% with addmembers_form as form %}
{% include "im/form_render.html" %}
......
......@@ -67,8 +67,8 @@
<option value="definition__end_date" {% if sorting == 'definition__end_date' %}selected{% endif %}>Sort by End Date</option>
<!-- <option value="approved_members_num" {% if sorting == 'approved_members_num' %}selected{% endif %}>Sort by Participants</option> -->
<!-- <option value="status" {% if sorting == '' %}selected{% endif %}>Sort by Member Accept Policy</option> -->
<option value="definition__member_accept_policy" {% if sorting == 'definition__member_accept_policy' %}selected{% endif %}>Sort by Member Accept Policy</option>
<option value="definition__member_reject_policy" {% if sorting == 'definition__member_reject_policy' %}selected{% endif %}>Sort by Member Reject Policy</option>
<option value="definition__member_join_policy__description" {% if sorting == 'definition__member_join_policy__description' %}selected{% endif %}>Sort by Member Join Policy</option>
<option value="definition__member_leave_policy__description" {% if sorting == 'definition__member_leave_policy__description' %}selected{% endif %}>Sort by Member Leave Policy</option>
</select>
<input type="hidden" name="q" value="{{q}}"/>
</div>
......@@ -96,17 +96,17 @@
</thead>
<tbody>
{% for o in object_list %}
{% with o.project.members as members %}
{% with o.project.members.all as members %}
{% with o.project.approved_members as approved_members%}
<tr class="{% cycle 'tr1' 'tr2' %}">
<td style="width:22%"><a href="{% url project_detail o.serial %}" title="visit group page">{{o.definition.name|truncatename}}</a></td>
<td style="width:22%"><a href="{% url project_detail o.id %}" title="visit group page">{{o.definition.name|truncatename}}</a></td>
<!--td>{{o.kindname|capfirst}}</td-->
<td style="width:13%">{{o.issue_date|date:"d/m/Y"}}</td>
<td style="width:13%">{{o.definition.start_date|date:"d/m/Y"}}</td>
<td style="width:13%">{{o.definition.end_date|date:"d/m/Y"}}</td>
<td style="width:11%">{{approved_members|length}}</td>
<td style="width:11%">
{{o.status}} <a href="{% url project_update o.serial %}">Update</a>
{{o.status}} <a href="{% url project_update o.id %}">Update</a>
</td>
<td style="width:17%">
<div class="msg-wrap">
......@@ -131,12 +131,12 @@
{% if user in members %}
{% if user in approved_members %}
{% if not user == o.owner %}
<form action="{% url project_leave o.serial %}" method="post" class="link-like">{% csrf_token %}
<form action="{% url project_leave o.id %}" method="post" class="link-like">{% csrf_token %}
<input type="submit" value="x leave group" class="leave"/>
</form>
<div class="dialog">
Are you sure you what to leave this group?<br>
Name: <a href="{% url project_detail o.serial %}" title="visit group page">{{o.groupname}}</a><br>
Name: <a href="{% url project_detail o.id %}" title="visit group page">{{o.groupname}}</a><br>
{% if o.definition.description %}Description:{{o.definition.description|truncatewords:30}}{% endif %}<br><br>
<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
</div>
......@@ -148,12 +148,12 @@
{% endif %}
{% else %}
{% if o.project.is_alive %}
<form action="{% url project_join o.serial %}" method="post" class="link-like">{% csrf_token %}
<form action="{% url project_join o.id %}" method="post" class="link-like">{% csrf_token %}
<input type="submit" value="+ join group" class="join_group join" />
</form>
<div class="dialog">
Are you sure you what to join this group?<br>
Name: <a href="{% url project_detail o.serial %}" title="visit group page">{{o.groupname}}</a><br>
Name: <a href="{% url project_detail o.id %}" title="visit group page">{{o.groupname}}</a><br>
{% if o.definition.description %}Description:{{o.definition.description|truncatewords:30}}{% endif %}<br><br>
<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
......@@ -162,8 +162,8 @@
{% endif %}
</div>
</td>
<td class="centered" style="width:9%">{{o.definition.member_accept_policy}}</td>
<td class="centered" style="width:9%">{{o.definition.member_reject_policy}}</td>
<td class="centered" style="width:9%">{{o.definition.member_join_policy}}</td>
<td class="centered" style="width:9%">{{o.definition.member_leave_policy}}</td>
<!--td><a href="#" class="more-info" title="more info">+ more info</a></td-->
</tr>
<tr class="{% cycle 'tmore1' 'tmore2' %}" style="display:none">
......@@ -244,7 +244,7 @@
<tbody>
{% for o in page_obj.object_list %}
<tr class="{% cycle 'tr1' 'tr2' %}">
<td style="width:22%"><a href="{% url project_detail o.serial %}" title="visit group page">{{o.definition.name|truncatename }}</a></td>
<td style="width:22%"><a href="{% url project_detail o.id %}" title="visit group page">{{o.definition.name|truncatename }}</a></td>
<!--td>{{o.kindname|capfirst}}</td-->
<td style="width:13%">{{o.issue_date|date:"d/m/Y"}}</td>
<td style="width:13%">{{o.definition.start_date|date:"d/m/Y"}}</td>
......@@ -285,12 +285,12 @@
{% if o.is_active %}
{% if user in approved_members %}
<form action="{% url project_leave o.serial %}" method="post" class="link-like">{% csrf_token %}
<form action="{% url project_leave o.id %}" method="post" class="link-like">{% csrf_token %}
<input type="submit" value="x leave" class="leave" />
</form>
<div class="dialog">
Are you sure you want to leave this group?<br>
Name: <a href="{% url project_detail o.serial %}" title="visit group page">{{o.groupname}}</a><br>
Name: <a href="{% url project_detail o.id %}" title="visit group page">{{o.groupname}}</a><br>
{% if o.definition.description %}Description:{{o.definition.description|truncatewords:30}}{% endif %}<br><br>
<a href="#" class="yes submit">Yes</a>&nbsp;&nbsp;&nbsp;<a href="#" class="no submit">No</a>
......
{% extends "im/account_base.html" %}
{% load filters %}
{% block page.body %}
<div class="projects">
<h2>
<span>{{ object.definition.name|upper }}</span>
</h2>
<div class="details">
<h3>
GENERAL INFO
{% if object.owner == user %}
<a href="#" class="edit">EDIT</a>
{% endif %}
</h3>
<div class="data">
<p class="restricted">{{ object.definition.description|safe }}</p>
<dl class="alt-style">
<dt>Homepage url</dt>
<dd>
{% if object.definition.homepage%}
<a href="{{ object.homepage }}">{{ object.definition.homepage }}</a>
{% else %}
Not set yet
{% endif %}
</dd>
<dt>Moderation</dt>
<dd>
{{ object.definition.member_accept_policy.description }}
</dd>
</dl>
</div>
<div class="editable" style="display:none;">
<form action="{% url astakos.im.views.project_application_detail object.serial %}" method="post"
class="withlabels">{% csrf_token %}
{% with update_form as form %}
{% include "im/form_render.html" %}
<div class="form-row submit">
<input type="submit" class="submit altcol" value="SAVE" />
</div>
{% endwith %}
</form>
</div>
</div>
<div class="full-dotted">
<h3>DETAILS</h3>
<dl class="alt-style">
<dt>Name</dt>
<dd>{{ object.definition.name }}&nbsp;</dd>
<dt>Issue date:</dt>
<dd>{{object.issue_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>Start Date</dt>
<dd>{{object.definition.start_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>End Date</dt>
<dd>{{object.definition.end_date|date:"d/m/Y"}}&nbsp;</dd>
<dt>Valid</dt>
<dd>{% if object.is_valid %}Yes{% else %}No{% endif %}</dd>
<dt>Activated</dt>
<dd>{% if object.is_active %}Yes{% else %}No{% endif %}</dd>
<dt>Terminated</dt>
<dd>{% if object.is_terminated %}Yes{% else %}No{% endif %}</dd>
<dt>Suspended</dt>
<dd>{% if object.is_suspended %}Yes{% else %}No{% endif %}</dd>
<dt>Alive</dt>
<dd>{% if object.is_alive %}Yes{% else %}No{% endif %}</dd>
<dt>Inconsistent</dt>
<dd>{% if object.is_inconsistent %}Yes{% else %}No{% endif %}</dd>
<dt>Owner</dt>
<dd>{% if object.object == user %}
Me
{% else%}