Revision 3f3dc4b7

b/snf-astakos-app/astakos/im/forms.py
915 915

  
916 916
class AddProjectMembersForm(forms.Form):
917 917
    q = forms.CharField(
918
        max_length=800, widget=forms.Textarea, label=_('Add members'),
919
        help_text=_(astakos_messages.ADD_PROJECT_MEMBERS_Q_HELP), required=True)
918
        max_length=800, 
919
        widget=forms.Textarea(attrs={
920
            'placeholder': astakos_messages.ADD_PROJECT_MEMBERS_Q_PLACEHOLDER}
921
            ), 
922
        label=_('Add members'),
923
        help_text=_(astakos_messages.ADD_PROJECT_MEMBERS_Q_HELP), 
924
        required=True,)
920 925

  
921 926
    def __init__(self, *args, **kwargs):
922 927
        chain_id = kwargs.pop('chain_id', None)
b/snf-astakos-app/astakos/im/messages.py
166 166
INVALID_PROJECT_END_DATE                =   'Project end date should be equal or greater than than the current date.'
167 167
INCONSISTENT_PROJECT_DATES              =   'Project end date should be greater than the project start date.'
168 168
ADD_PROJECT_MEMBERS_Q_HELP              =   'Add a comma separated list of user emails, eg. user1@user.com, user2@user.com'
169
ADD_PROJECT_MEMBERS_Q_PLACEHOLDER       =   'user1@user.com, user2@user.com'
169 170
MISSING_IDENTIFIER                      =   'Missing identifier.'
170 171
UNKNOWN_USER_ID                         =   'There is no user identified by %s.'
171 172
UNKNOWN_PROJECT_APPLICATION_ID          =   'There is no project application identified by %s.'
b/snf-astakos-app/astakos/im/settings.py
219 219
AQUARIUM_URL = getattr(settings, 'ASTAKOS_AQUARIUM_URL', '')
220 220

  
221 221
# Set how many objects should be displayed per page
222
PAGINATE_BY = getattr(settings, 'ASTAKOS_PAGINATE_BY', 8)
222
PAGINATE_BY = getattr(settings, 'ASTAKOS_PAGINATE_BY', 50)
223 223

  
224 224
# Set how many objects should be displayed per page in show all projects page
225
PAGINATE_BY_ALL = getattr(settings, 'ASTAKOS_PAGINATE_BY_ALL', 15)
225
PAGINATE_BY_ALL = getattr(settings, 'ASTAKOS_PAGINATE_BY_ALL', 50)
226 226

  
227 227
# Enforce token renewal on password change/reset
228 228
NEWPASSWD_INVALIDATE_TOKEN = getattr(
b/snf-astakos-app/astakos/im/static/im/css/forms.css
204 204

  
205 205

  
206 206
/* styles for complex profile form */
207
form .form-following p span.extra-img				{ background:url(../images/symbols3.png) 0 -161px; z-index:8; }
208
form .form-following input:hover + span.extra-img	{ background-position:0 0px; }
207
form .form-following p span.extra-img				{ background:url(../images/symbols3.png) 0 -161px; z-index:8; }

208
form .form-following input:hover + span.extra-img	{ background-position:0 0px; }

209 209
form .form-following p:hover span.extra-img:hover	{ background-position:0 -54px; cursor:pointer; }
210 210
form .form-following.open p span.extra-img				{ background-position: -33px -161px; }
211 211
form .form-following.open input:hover + span.extra-img			{ background-position: -33px 0px; }
......
219 219
form .refresh.open p:hover span.extra-img:hover		{ background-position: -33px -54px; cursor:pointer; }
220 220
.hidden-form-rows									{ display:none; }
221 221
.signup .form-row.with-checkbox						{ margin-top:1em; }
222

  
223

  
224
form.withlabels.upperlabels label 					{ text-transform: uppercase;}
b/snf-astakos-app/astakos/im/static/im/css/modules.css
246 246
table.alt-style tr:nth-child(2n) td				{ background:#F2F2F2 } 
247 247
dl.alt-style dt									{ width:30%; float:left; color:#3582AC; font-weight:normal;}
248 248
dl.alt-style dt:nth-child(2n)					{ background:black; }
249
dl.alt-style dd									{ overflow:hidden; }
249
dl.alt-style dd									{ overflow:hidden; position:relative;}
250 250
.projects										{ padding-bottom:30px; position:relative; }
251 251
.projects h2 span								{ color:#3582AC;}
252 252
.projects h2 em									{ float:right; }
253 253
.projects h3									{ font-size:1.154em; }
254
.projects h3 .rt-action 						{ float:right;}
254 255
.projects .submit-rt							{ margin:0; text-align:right; }
255 256
.projects +.buttons-list.fixpos					{ left:0; right:auto; }
256
.project-actions a { font-size: 0.7em }
257
.project-actions a 								{ font-size: 0.7em }
258
.project-actions a.inactive	 					{ color:#ccc; cursor: default}
259
.project-actions a.inactive:hover				{ text-decoration: none;}
260
.projects dl.alt-style .faint					{ color:#ccc; font-style: italic}
261
.projects dl.alt-style .dl-actions 				{ position:absolute; left:300px;}
262
.projects form.members 							{ margin-top:20px;}
263
.projects form.members label 					{ padding-top:0; text-transform: uppercase;}
264

  
257 265

  
258 266
/* new faq-userguide styles */
259 267

  
......
516 524

  
517 525
h2 .msg-wrap { font-size: 0.7em }
518 526
.msg-wrap									{ position:relative; display:inline-block; }
519
.msg-wrap .dialog							{ position:absolute; display: none;}
527
.msg-wrap .dialog							{ position:absolute; display: none;background:#fff; z-index:2;}
520 528
.msg-wrap .dialog-content           		{ border:1px dashed #ccc;  padding:15px; width:200px; bottom:30px; right:0; background:#fff;}
521 529
.msg-wrap .dialog .submit					{ min-width:30px; padding:5px 22px; }
522 530
.msg-wrap .dialog .no.submit				{ float:right; }
......
617 625

  
618 626

  
619 627
i.tiny { font-size: 0.8em; color: #999;}
628
a i.tiny 	{ color:inherit;}
620 629

  
621 630
h2 span.subtitle { font-size: 0.9em; color: #55B577;}
622 631

  
......
641 650
.content a:visited 		{ border:0 none; }
642 651

  
643 652
#hand											{ position:absolute; height:79px; width:61px; background:url(../images/xeraki_hover.png) no-repeat; overflow:hidden; display:none;  top:40px;}
653

  
654
table.cols-3 									{ width:100%; color:#222;}
655
table.cols-3 td 								{ width:30%;}
656
table.cols-3 td p 								{ padding:0 1em;}
657
table.cols-3 tr.images td 						{ text-align: center;}
658
table.cols-3 tr.links td 						{ font-size:1.154em}
b/snf-astakos-app/astakos/im/tables.py
289 289

  
290 290
    def render_members_count(self, record, *args, **kwargs):
291 291
        append = ""
292
	application = record
292
        application = record
293 293
        project = application.get_project()
294 294
        if project is None:
295 295
            append = mark_safe("<i class='tiny'>%s</i>" % (_('pending'),))
296 296

  
297 297
        c = project.count_pending_memberships()
298 298
        if c > 0:
299
            append = mark_safe("<i class='tiny'> - %d %s</i>"
300
                                % (c, _('pending')))
301

  
302
        return mark_safe(str(record.members_count()) + append)
299
            pending_members_url = reverse('project_pending_members', 
300
                kwargs={'chain_id': application.chain})
301
            pending_members = "<i class='tiny'> - %d %s</i>" % (c, _('pending'))
302
            if self.user.owns_application(record) or self.user.is_project_admin():
303
                pending_members = '<a href="%s">%s</a>' % (pending_members_url,
304
                pending_members)
305
            append = mark_safe(pending_members)
306
        members_url = reverse('project_members', 
307
            kwargs={'chain_id': application.chain})
308
        members_count = record.members_count()
309
        if self.user.owns_application(record) or self.user.is_project_admin():
310
            members_count = '<a href="%s">%d</a>' % (members_url,
311
                members_count)
312
        return mark_safe(str(members_count) + append)
303 313
        
304 314
    class Meta:
305 315
        model = ProjectApplication
b/snf-astakos-app/astakos/im/templates/im/projects/__intro.html
1
{% load i18n %}
2
<table class="cols-3">
3
  <tr class="images">
4
    <td>
5
      <p>
6
        &nbsp;
7
      </p>
8
      
9
    </td>
10
    <td>
11
      <p><a href="{% url project_add %}"><img alt="THINK ABOUT IT" src="/static/im/images/create.png"></a></p>
12
       
13
    </td>
14
    <td>
15
      <p><a href="{% url project_search %}"><img alt="THINK ABOUT IT" src="/static/im/images/join.png"></a></p>
16
        
17
    </td>
18
  </tr>
19
  <tr>
20
    <td>
21
      <p>
22
      {% blocktrans %}
23
      ~okeanos gives the opportunity to
24
      Greek Academic or Research Organizations/Institutions/Faculty
25
      to run their own projects remotely on virtual infrastructure.
26
      Simple, fast, and with minimal to no cost at all.
27
      {% endblocktrans %}
28
      </p> 
29
    </td>
30
    <td>
31
      
32
        <p class="txt">
33
        {% blocktrans %}
34
        Create a new Project. Name it, describe its purpose,
35
        choose virtual resources to be granted to members, and submit.
36
        Your application will be reviewed, and if accepted,
37
        you and your colleagues are ready to deploy!<br/><br/>
38
              {% endblocktrans %}
39
        </p> 
40
    </td>
41
    <td>
42
       
43
        <p class="txt">
44
        {% blocktrans %}
45
        Request to be a member of an existing Project
46
        and instantly gain access to the resources it has to offer you.
47
        Search for public Projects, or submit a join request to a private Project,
48
        if you think its administrators will accept you.
49
        In short: try to Join now.
50
              {% endblocktrans %}
51
        </p> 
52
    </td>
53
  </tr>
54
  <tr class="links">
55
    <td>
56
       
57
      <p><a href="{% url how_it_works %}">How it works ></a></p>
58
    </td>
59
    <td> 
60
        <p><a href="{% url project_add %}" style="color:#55B577">Create a project ></a></p>
61
    </td>
62
    <td> 
63
        <p><a href="{% url project_search %}" style="color:#F24E53">Join a project ></a></p>
64
    </td>
65
  </tr>
66

  
67
</table>
68
<div class="full-dotted">&nbsp;</div>
69

  
70
 
b/snf-astakos-app/astakos/im/templates/im/projects/addmembers_form.html
1
<form action="{% url project_detail object.chain %}#members-table"
2
    method="post" class="withlabels upperlabels" id="members-table" >
3
    {% csrf_token %}
4
    {% with addmembers_form as form %}
5
    {% include "im/form_render.html" %}
6
    {% endwith %}
7
    <div class="form-row submit">
8
      <input type="submit" class="submit altcol" value="ADD MEMBERS" />
9
    </div>
10
</form>
b/snf-astakos-app/astakos/im/templates/im/projects/intro.html
2 2
<div class="two-cols clearfix">
3 3
  <div class="rt">
4 4
    &nbsp;
5

  
5 6
  </div>
6 7
  <div class="lt">
7 8
    <p>
......
17 18
</div>
18 19

  
19 20

  
21

  
20 22
<div class="widjets"> 
21 23
  <!--<a href="#" class="widjet-x" title="remove boxes">X</a>-->
22 24
  <ul class="clearfix">	
b/snf-astakos-app/astakos/im/templates/im/projects/project_detail.html
52 52
    {% include "im/projects/_project_detail_actions.html" %}
53 53
    {% endblock %}
54 54
  </h2>
55

  
55
  {% block inner_project %}
56 56
  <div class="full-dotted">
57 57
    <h3>PROJECT DETAILS</h3>
58 58
    <dl class="alt-style">
......
60 60
      <dd>{{ object.name }}&nbsp;</dd>
61 61
      <dt>Homepage url</dt>
62 62
      <dd>
63
        {% if object.homepage%}
63
        {% if object.homepage %}
64 64
        <a href="{{ object.homepage }}">{{ object.homepage }}</a>
65 65
        {% else %}
66 66
        Not set yet
......
95 95
      </dd>
96 96
    </dl>
97 97
  </div>
98

  
99
  <div class="full-dotted">
100
    <h3>MEMBERSHIP OPTIONS</h3>
101
    <dl class="alt-style">
102
      <dt>Max participants</dt>
103
      <dd>
104
        {% if object.limit_on_members_number %}
105
        {{object.limit_on_members_number}}
106
        {% else %}&nbsp;{% endif %}
107
      </dd>
108
      <dt>Member join policy</dt>
109
      <dd>
110
        {{ object.member_join_policy_display|title }}
111
      </dd>
112
      <dt>Member leave policy</dt>
113
      <dd>
114
        {{ object.member_leave_policy_display|title }}
115
      </dd>
116
    </dl>
117
  </div>
98
 
118 99

  
119 100
  <div class="full-dotted">
120 101
    <h3>RESOURCES</h3>
......
132 113
    {% endif %}
133 114
  </div>
134 115

  
135
  {% if owner_mode and project_view %}
136
    {% if object.project.is_alive %}
137
      <div class="full-dotted">
138
        <h3>MEMBERS</h3>
139
        {% if members_table %}
140
        {% render_table members_table %}
141
        {% endif %}
142
      </div>
143

  
144
      {% if not project.is_deactivated %}
145
        <div class="full-dotted">
146
          <form action="{% url project_detail object.chain %}#members-table"
147
                method="post" class="withlabels" >
148
            {% csrf_token %}
149
            <h2>Enroll more members</h2>
150
            {% with addmembers_form as form %}
151
            {% include "im/form_render.html" %}
152
            {% endwith %}
153
            <div class="form-row submit">
154
              <input type="submit" class="submit altcol" value="ADD MEMBERS" />
155
            </div>
156
          </form>
157
        </div>
158
      {% endif %}
159
    {% endif %}
116
 
117
  <div class="full-dotted">
160 118

  
161
    {% comment %}
162
      {% if modifications_table %}
163
      <div class="full-dotted">
164
        <h3>MODIFICATION REQUESTS</h3>
165
        {% render_table modifications_table %}
166
      </div>
119
    <h3>
120
      {% if owner_mode and project_view %}
121
          {% if object.project.is_alive %}
122
           <a href="{% url project_members object.chain %}">MEMBERS </a>
123
          {% else %}
124
          MEMBERS
125
          {% endif %}
126
      {% else %}
127
          MEMBERS
167 128
      {% endif %}
168
    {% endcomment %}
169 129

  
170
  {% endif %}
130
     
131
    </h3>
132
    
133
    <dl class="alt-style">
134
      <dt>Max participants</dt>
135
      <dd>
136
        {% if object.limit_on_members_number %}
137
        {{object.limit_on_members_number}}
138
        {% else %}Not set{% endif %}
139
      </dd>
140
      <dt>Member join policy</dt>
141
      <dd>
142
        {{ object.member_join_policy_display|title }}
143
      </dd>
144
      <dt>Member leave policy</dt>
145
      <dd>
146
        {{ object.member_leave_policy_display|title }}
147
      </dd>
148
       {% if owner_mode and project_view %}
149
          {% if object.project.is_alive %}
150
      <dt><a href="{% url project_approved_members object.chain %}" title="view approved members">Aprroved members</a></dt>
151
      <dd>{{ approved_members_count }}
152
        <span class="faint"> 
153
          {% if object.limit_on_members_number %}
154
          ({% substract object.limit_on_members_number approved_members_count%} memberships remain)
155
          
156
          {% else %}&nbsp;{% endif %} 
157
        </span>
158
      </dd>
159
      <dt><a href="{% url project_pending_members object.chain %}" title="view pending members">Members pending approval</a></dt>
160
      <dd>{{ pending_members_count }}</dd>
161
        {% if not project.is_deactivated %}
162
    </dl>    
163
    {% include 'im/projects/addmembers_form.html' %}
164
           
165
            {% endif %}
166
          {% endif %}
167
       {% endif %}
171 168

  
169
  </div>
170
{% endblock inner_project %}  
172 171
  <div class="full-dotted">
173 172
    <p>
174 173
      <a href="{% url project_list %}">&lt; Back to Projects</a>
175 174
    </p>
176 175
  </div>
177

  
176
</div>
178 177
{% endwith %}
179 178
{% endblock %}
b/snf-astakos-app/astakos/im/templates/im/projects/project_members.html
1
{% extends "im/projects/project_detail.html" %}
2
  
3
 
4
{% load astakos_tags filters django_tables2 %}
5

  
6

  
7
{% block inner_project %} 
8
   
9
  {% if owner_mode and project_view %}
10
    {% if object.project.is_alive %}
11
      
12
      <div class="full-dotted">
13
        <h3> 
14
          <a href="#members-table" class="rt-action">ADD MORE MEMBERS</a>
15
          MEMBERS  
16
 
17
        <div class="project-actions">
18
          <a href="{% url project_members object.chain %}"  {% if members_status_filter == None %}class="inactive"{% endif %}>ALL</a> -
19
          <a href="{% url project_approved_members object.chain %}" {% if members_status_filter == 1 %}class="inactive"{% endif %}>APPROVED</a> -
20
          <a href="{% url project_pending_members object.chain %}" {% if members_status_filter == 0 %}class="inactive"{% endif %}>PENDING</a>
21

  
22
        </div>
23
        </h3>
24
        
25
        {% if members_table %}
26
        {% render_table members_table %}
27
        {% endif %}
28
        
29
      </div>
30

  
31
     
32
      {% if not project.is_deactivated %}
33
        <div class="full-dotted">
34
          
35
    {% include 'im/projects/addmembers_form.html' %}
36
        </div>
37
      {% endif %}
38
    {% endif %}
39

  
40
     
41
     
42
    
43
  {% endif %}
44
{% endblock inner_project %} 
45
   
46
 
b/snf-astakos-app/astakos/im/templatetags/astakos_tags.py
270 270
    content = render_to_string(template, tpl_context)
271 271
    return content
272 272

  
273

  
274
@register.simple_tag   
275
def substract(arg1, arg2):
276
    return arg1-arg2
b/snf-astakos-app/astakos/im/templatetags/filters.py
208 208
        return dict((RESOURCE_SEPARATOR.join([e[1], e[0]]), e[2]) for e in grants)
209 209
    except:
210 210
        return {}
211
        
b/snf-astakos-app/astakos/im/urls.py
66 66
    url(r'^projects/(?P<chain_id>\d+)/join/?$', 'project_join', {}, name='project_join'),
67 67
    url(r'^projects/(?P<chain_id>\d+)/leave/?$', 'project_leave', {}, name='project_leave'),
68 68
    url(r'^projects/(?P<chain_id>\d+)/cancel/?$', 'project_cancel', {}, name='project_cancel'),
69
    url(r'^projects/(?P<chain_id>\d+)/members/?$', 'project_members', {}, name='project_members'),
70
    url(r'^projects/(?P<chain_id>\d+)/members/approved/?$', 'project_members', {'members_status_filter':1}, name='project_approved_members'),
71
    url(r'^projects/(?P<chain_id>\d+)/members/pending/?$', 'project_members', {'members_status_filter':0}, name='project_pending_members'),
69 72
    url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/accept/?$', 'project_accept_member', {}, name='project_accept_member'),
70 73
    url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/reject/?$', 'project_reject_member', {}, name='project_reject_member'),
71 74
    url(r'^projects/(?P<chain_id>\d+)/(?P<user_id>\d+)/remove/?$', 'project_remove_member', {}, name='project_remove_member'),
b/snf-astakos-app/astakos/im/views.py
1236 1236
                ctx.mark_rollback()
1237 1237
            messages.error(request, e)
1238 1238

  
1239
def common_detail(request, chain_or_app_id, project_view=True):
1239
def common_detail(request, chain_or_app_id, project_view=True, 
1240
                  template_name='im/projects/project_detail.html',
1241
                  members_status_filter=None):
1240 1242
    project = None
1241 1243
    if project_view:
1242 1244
        chain_id = chain_or_app_id
......
1250 1252
                addmembers_form = AddProjectMembersForm()  # clear form data
1251 1253
        else:
1252 1254
            addmembers_form = AddProjectMembersForm()  # initialize form
1253

  
1255
        approved_members_count = 0
1256
        pending_members_count = 0
1257
        remaining_memberships_count = 0
1254 1258
        project, application = get_by_chain_or_404(chain_id)
1255 1259
        if project:
1256 1260
            members = project.projectmembership_set.select_related()
1261
            approved_members_count = members.filter(state=1).count()
1262
            pending_members_count = members.filter(state=0).count()
1263
            if members_status_filter in (0,1):
1264
                members = members.filter(state=members_status_filter)
1257 1265
            members_table = tables.ProjectMembersTable(project,
1258 1266
                                                       members,
1259 1267
                                                       user=request.user,
1260 1268
                                                       prefix="members_")
1269

  
1261 1270
            RequestConfig(request, paginate={"per_page": PAGINATE_BY}
1262 1271
                          ).configure(members_table)
1263 1272

  
......
1299 1308
        request,
1300 1309
        queryset=ProjectApplication.objects.select_related(),
1301 1310
        object_id=application.id,
1302
        template_name='im/projects/project_detail.html',
1311
        template_name= template_name,
1303 1312
        extra_context={
1304 1313
            'project_view': project_view,
1305 1314
            'addmembers_form':addmembers_form,
1315
            'approved_members_count':approved_members_count,
1316
            'pending_members_count':pending_members_count,
1306 1317
            'members_table': members_table,
1307 1318
            'owner_mode': is_owner,
1308 1319
            'admin_mode': is_project_admin,
......
1310 1321
            'mem_display': mem_display,
1311 1322
            'can_join_request': can_join_req,
1312 1323
            'can_leave_request': can_leave_req,
1324
            'members_status_filter':members_status_filter,
1313 1325
            })
1314 1326

  
1315 1327
@require_http_methods(["GET", "POST"])
......
1585 1597
    return render_response(
1586 1598
        'im/api_access.html',
1587 1599
        context_instance=get_context(request))
1600

  
1601

  
1602
@require_http_methods(["GET", "POST"])
1603
@valid_astakos_user_required
1604
def project_members(request, chain_id, members_status_filter=None, 
1605
                    template_name='im/projects/project_members.html'):
1606
    return common_detail(request, chain_id, 
1607
        members_status_filter=members_status_filter, 
1608
        template_name=template_name) 

Also available in: Unified diff