diff --git a/akvo/rest/urls.py b/akvo/rest/urls.py index a59099b8d6..760e480b37 100644 --- a/akvo/rest/urls.py +++ b/akvo/rest/urls.py @@ -138,6 +138,9 @@ url(r'^project/(?P[0-9]+)/step_10/$', views.project_editor_step10, name='project_editor_step10'), + url(r'^organisation/(?P[0-9]+)/add_logo/$', + views.project_editor_organisation_logo, + name='project_editor_add_org_logo'), ) # Typeahead diff --git a/akvo/rest/views/__init__.py b/akvo/rest/views/__init__.py index cf4a86d187..e2061bc010 100644 --- a/akvo/rest/views/__init__.py +++ b/akvo/rest/views/__init__.py @@ -42,7 +42,8 @@ project_editor_step7, project_editor_step8, project_editor_step9, - project_editor_step10) + project_editor_step10, + project_editor_organisation_logo) from .project_comment import ProjectCommentViewSet from .project_document import ProjectDocumentViewSet from .project_condition import ProjectConditionViewSet @@ -125,6 +126,7 @@ 'project_editor_step8', 'project_editor_step9', 'project_editor_step10', + 'project_editor_organisation_logo', 'PublishingStatusViewSet', 'RecipientCountryViewSet', 'RecipientRegionViewSet', diff --git a/akvo/rest/views/project_editor.py b/akvo/rest/views/project_editor.py index 5507c12a7f..837fed2801 100644 --- a/akvo/rest/views/project_editor.py +++ b/akvo/rest/views/project_editor.py @@ -276,6 +276,7 @@ ## Custom fields ## CUSTOM_FIELD = ('value', 'custom-field-', 'text') +ORGANISATION_LOGO_FIELD = ('logo', 'logo', 'none') def add_error(errors, message, field_name): @@ -1612,3 +1613,32 @@ def project_editor_step10(request, pk=None): 'rel_objects': rel_objects, } ) + + +@api_view(['POST']) +@permission_classes((IsAuthenticated, )) +def project_editor_organisation_logo(request, pk=None): + org = Organisation.objects.get(pk=pk) + user = request.user + + if not user.has_perm('rsr.change_organisation', org): + return HttpResponseForbidden() + + files = request.FILES + logo = None + errors = [] + + if 'logo' in files.keys(): + errors, changes = process_field(org, files, ORGANISATION_LOGO_FIELD, errors, []) + + if not errors: + logo = get_thumbnail( + org.logo, '250x250', format="PNG", upscale=True + ).url + + return Response( + { + 'errors': errors, + 'logo': logo, + } + ) diff --git a/akvo/rsr/models/organisation.py b/akvo/rsr/models/organisation.py index a9295b9a89..67867936ba 100644 --- a/akvo/rsr/models/organisation.py +++ b/akvo/rsr/models/organisation.py @@ -4,8 +4,8 @@ # See more details in the license.txt file located at the root folder of the Akvo RSR module. # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >. - from django.conf import settings +from django.core.exceptions import ValidationError from django.db import models from django.db.models import Sum, Q from django.db.models.query import QuerySet as DjangoQuerySet @@ -173,7 +173,40 @@ def org_type_from_iati_type(cls, iati_type): @models.permalink def get_absolute_url(self): - return ('organisation-main', (), {'organisation_id': self.pk}) + return 'organisation-main', (), {'organisation_id': self.pk} + + def clean(self): + """Organisations can only be saved when we're sure that they do not exist already.""" + validation_errors = {} + + name = self.name.strip() + other_names = Organisation.objects.filter(name__iexact=name) + if name: + if other_names.exists(): + validation_errors['name'] = _('Organisation name already exists: %s.' % + other_names[0].name) + else: + validation_errors['name'] = _('Organisation name can not be blank') + + long_name = self.long_name.strip() + other_long_names = Organisation.objects.filter(long_name__iexact=long_name) + if long_name: + if other_long_names.exists(): + validation_errors['long_name'] = _('Organisation long name already exists: %s.' % + other_long_names[0].long_name) + else: + validation_errors['long_name'] = _('Organisation long name can not be blank') + + if self.iati_org_id: + iati_org_id = self.iati_org_id.strip() + other_iati_ids = Organisation.objects.filter(iati_org_id__iexact=iati_org_id) + if iati_org_id and other_iati_ids.exists(): + validation_errors['iati_org_id'] = _('IATI organisation identifier already exists ' + 'for this organisation: %s.' % + other_iati_ids[0].name) + + if validation_errors: + raise ValidationError(validation_errors) class QuerySet(DjangoQuerySet): def has_location(self): diff --git a/akvo/rsr/static/scripts-src/myrsr-admin.js b/akvo/rsr/static/scripts-src/myrsr-admin.js index 9835083194..4ffe92e7a2 100644 --- a/akvo/rsr/static/scripts-src/myrsr-admin.js +++ b/akvo/rsr/static/scripts-src/myrsr-admin.js @@ -7,6 +7,7 @@ // DEFAULT VALUES var defaultValues = JSON.parse(document.getElementById("default-values").innerHTML); +var countryValues = JSON.parse(document.getElementById("country-values").innerHTML); // CSRF TOKEN function getCookie(name) { @@ -865,15 +866,29 @@ function buildReactComponents(typeaheadOptions, typeaheadCallback, displayOption } return short + ' (' + long + ')'; } - inputClass = selector + " form-control " + childClass; selectorClass = document.querySelector('.' + selector); TypeaheadContainer = React.createClass({displayName: 'TypeaheadContainer', + + getInitialState: function() { + return ({focusClass: 'inactive'}); + }, + onKeyUp: function() { + + /* Only activate the "add org" button for typeaheads that i) are for organisations, + ** and ii) are not for reporting organisations. */ + if (inputType === 'org' && selector.indexOf('reportingOrganisation') === -1) { + this.setState({focusClass: 'active'}); + } + }, + onBlur: function() { + this.setState({focusClass: 'inactive'}); + }, render: function() { return ( - React.DOM.div(null, + React.DOM.div( {className:this.state.focusClass}, Typeahead( {placeholder:"", options:typeaheadOptions, @@ -882,6 +897,8 @@ function buildReactComponents(typeaheadOptions, typeaheadCallback, displayOption displayOption:displayOption, filterOption:filterOption, childID:selector, + onKeyUp:this.onKeyUp, + onBlur:this.onBlur, customClasses:{ typeahead: "", input: inputClass, @@ -893,7 +910,8 @@ function buildReactComponents(typeaheadOptions, typeaheadCallback, displayOption inputProps:{ name: selector, id: selector - }} ) + }} ), + React.DOM.div( {className:"addOrg", onMouseDown:addOrgModal}, "+ ", defaultValues.add_new_organisation) ) ); } @@ -2109,23 +2127,6 @@ function setUnsavedChangesMessage() { }; } -/* Set each "add organisation" link to open the "add organisation" -** modal dialog on click */ - -function setModalOnClicks() { - var links = document.querySelectorAll('.add-organisation'); - - for (var i = 0; i < links.length; i++) { - var el = links[i]; - - el.removeEventListener('click'); - el.addEventListener('click', function(e) { - e.preventDefault(); - addOrgModal(); - }); - } -} - /* Show the "add organisation" modal dialog */ function addOrgModal() { @@ -2137,16 +2138,15 @@ function addOrgModal() { /* Submit the new org */ function submitModal() { - if (allInputsFilled()) { - var api_url, request, form, form_data, reporting_org_id; + if (allInputsFilled() && checkLocationFilled()) { + var api_url, request, form, form_data; + // Add organisation to DB form = document.querySelector('#addOrganisation'); - form_data = serialize(form); - form_data = form_data.replace('iati_org_id=&', ''); - reporting_org_id = document.querySelector('#reportingOrganisation').getAttributeNode("value").value; - form_data += '&content_owner=' + reporting_org_id; + // Remove empty IATI organistion id + form_data = form_data.replace('iati_org_id=&', ''); api_url = '/rest/v1/organisation/?format=json'; @@ -2157,6 +2157,35 @@ function addOrgModal() { request.onload = function() { if (request.status === 201) { + var organisation_id; + + // Get organisation ID + response = JSON.parse(request.responseText); + organisation_id = response.id; + + // Add location (fails silently) + if (form.querySelector('#latitude').value !== '') { + var request_loc; + api_url = '/rest/v1/organisation_location/?format=json'; + request_loc = new XMLHttpRequest(); + request_loc.open('POST', api_url, true); + request_loc.setRequestHeader("X-CSRFToken", csrftoken); + request_loc.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + request_loc.send(form_data + '&location_target=' + organisation_id); + } + + // Add logo (fails silently) + var logo_request, logo_data, org_logo_files; + org_logo_files = document.getElementById("org-logo").files; + if (org_logo_files !== undefined) { + api_url = '/rest/v1/organisation/' + organisation_id + '/add_logo/?format=json'; + logo_data = new FormData(); + logo_data.append("logo", org_logo_files[0]); + logo_request = new XMLHttpRequest(); + logo_request.open("POST", api_url); + logo_request.setRequestHeader("X-CSRFToken", csrftoken); + logo_request.send(logo_data); + } // This flag forces the fetching of a fresh API response var forceReloadOrg = true; @@ -2167,6 +2196,8 @@ function addOrgModal() { var response; response = JSON.parse(request.responseText); + document.querySelector('.orgModal').scrollTop = 0; + for (var key in response) { if (response.hasOwnProperty(key)) { var input = form.querySelector('#' + key); @@ -2186,11 +2217,14 @@ function addOrgModal() { request.onerror = function() { // There was a connection error of some sort - elAddClass(form, 'has-error'); + document.querySelector('#addOrgGeneralError').textContent = defaultValues.general_error; + document.querySelector('.orgModal').scrollTop = 0; return false; }; request.send(form_data); + } else { + document.querySelector('.orgModal').scrollTop = 0; } } @@ -2223,13 +2257,68 @@ function addOrgModal() { longNameHelp.textContent = ''; elRemoveClass(longNameHelp, 'help-block-error'); elRemoveClass(longNameContainer, 'has-error'); - } + } return allInputsFilledBoolean; } + function checkLocationFilled() { + var latitudeNode, longitudeNode, countryNode, latitudeHelp, longitudeHelp, countryHelp, result; + + latitudeNode = document.querySelector('#latitude'); + latitudeHelp = document.querySelector('#latitude + label + .help-block'); + longitudeNode = document.querySelector('#longitude'); + longitudeHelp = document.querySelector('#longitude + label + .help-block'); + countryNode = document.querySelector('#country'); + countryHelp = document.querySelector('#country + label + .help-block'); + + result = true; + + if (latitudeNode.value === '' && longitudeNode.value === '' && countryNode.value === '') { + return result; + } else if (latitudeNode.value === '' || longitudeNode.value === '' || countryNode.value === '') { + if (latitudeNode.value === '') { + latitudeHelp.textContent = defaultValues.location_check; + elAddClass(latitudeHelp, 'help-block-error'); + elAddClass(latitudeHelp.parentNode, 'has-error'); + } + if (longitudeNode.value === '') { + longitudeHelp.textContent = defaultValues.location_check; + elAddClass(longitudeHelp, 'help-block-error'); + elAddClass(longitudeHelp.parentNode, 'has-error'); + } + if (countryNode.value === '') { + countryHelp.textContent = defaultValues.location_check; + elAddClass(countryHelp, 'help-block-error'); + elAddClass(countryHelp.parentNode, 'has-error'); + } + result = false; + } else { + // TODO: Add good text for lat long error + if (latitudeNode.value.indexOf(',') > 0) { + latitudeHelp.textContent = defaultValues.comma_value; + elAddClass(latitudeHelp, 'help-block-error'); + elAddClass(latitudeHelp.parentNode, 'has-error'); + result = false; + } + if (longitudeNode.value.indexOf(',') > 0) { + longitudeHelp.textContent = defaultValues.comma_value; + elAddClass(longitudeHelp, 'help-block-error'); + elAddClass(longitudeHelp.parentNode, 'has-error'); + result = false; + } + } + return result; + } + Modal = React.createClass({displayName: 'Modal', render: function() { + var country_option_list = countryValues.map(function(country) { + return ( + React.DOM.option( {value:country.pk}, country.name) + ); + }); + return ( React.DOM.div( {className:"modalParent"}, React.DOM.div( {className:"modalBackground"} @@ -2240,28 +2329,34 @@ function addOrgModal() { React.DOM.h4(null, defaultValues.add_new_organisation), React.DOM.form( {id:"addOrganisation"}, React.DOM.div( {className:"row"}, - React.DOM.div( {id:"addOrgGeneralError", className:"col-md-12"}) + React.DOM.div( {id:"addOrgGeneralError", className:"col-md-12 help-block-error"}) ), React.DOM.div( {className:"row"}, - React.DOM.div( {className:"inputContainer newOrgName col-md-6"}, + React.DOM.div( {className:"inputContainer newOrgName col-md-4"}, React.DOM.input( {name:"name", id:"name", type:"text", className:"form-control", maxLength:"25"}), React.DOM.label( {htmlFor:"newOrgName", className:"control-label"}, defaultValues.name,React.DOM.span( {className:"mandatory"}, "*")), React.DOM.p( {className:"help-block"}, defaultValues.max, " 25 ", defaultValues.characters) ), - React.DOM.div( {className:"inputContainer newOrgLongName col-md-6"}, + React.DOM.div( {className:"inputContainer newOrgLongName col-md-4"}, React.DOM.input( {name:"long_name", id:"long_name", type:"text", className:"form-control", maxLength:"75"}), React.DOM.label( {htmlFor:"newOrgLongName", className:"control-label"}, defaultValues.long_name,React.DOM.span( {className:"mandatory"}, "*")), React.DOM.p( {className:"help-block"}, defaultValues.max, " 75 ", defaultValues.characters) - ) - ), - React.DOM.div( {className:"row"}, - React.DOM.div( {className:"inputContainer newOrgIatiId col-md-6"}, + ), + React.DOM.div( {className:"inputContainer newOrgIatiId col-md-4"}, React.DOM.input( {name:"iati_org_id", id:"iati_org_id", type:"text", className:"form-control", maxLength:"75"}), React.DOM.label( {htmlFor:"newOrgIatiId", className:"control-label"}, defaultValues.iati_org_id), React.DOM.p( {className:"help-block"}, defaultValues.max, " 75 ", defaultValues.characters) - ), + ) + ), + React.DOM.div( {className:"row"}, + React.DOM.div( {className:"inputContainer col-md-12"}, + React.DOM.input( {type:"file", className:"form-control", id:"org-logo", name:"org-logo", accept:"image/*"}), + React.DOM.label( {className:"control-label", for:"org-logo"}, defaultValues.org_logo) + ) + ), + React.DOM.div( {className:"row"}, React.DOM.div( {className:"IATIOrgTypeContainer inputContainer col-md-6"}, - React.DOM.select( {name:"new_organisation_type", id:"newOrgIATIType", className:"form-control", value:"22"}, + React.DOM.select( {name:"new_organisation_type", id:"newOrgIATIType", className:"form-control"}, React.DOM.option( {value:"10"}, "10 - ", defaultValues.government), React.DOM.option( {value:"15"}, "15 - ", defaultValues.other_public_sector), React.DOM.option( {value:"21"}, "21 - ", defaultValues.international_ngo), @@ -2275,23 +2370,61 @@ function addOrgModal() { ), React.DOM.label( {htmlFor:"newOrgIATIType", className:"control-label"}, defaultValues.org_type,React.DOM.span( {className:"mandatory"}, "*")), React.DOM.p( {className:"help-block"}) + ), + React.DOM.div( {className:"inputContainer col-md-6"}, + React.DOM.input( {name:"url", id:"url", type:"text", className:"form-control"}), + React.DOM.label( {htmlFor:"url", className:"control-label"}, defaultValues.website), + React.DOM.p( {className:"help-block"}, defaultValues.start_http) ) ), React.DOM.div( {className:"row"}, - React.DOM.div( {className:"descriptionContainer inputContainer col-md-12"}, - React.DOM.label( {className:"control-label", htmlFor:"description"}, defaultValues.description), + React.DOM.div( {className:"inputContainer col-md-4"}, + React.DOM.input( {name:"latitude", id:"latitude", type:"text", className:"form-control"}), + React.DOM.label( {htmlFor:"latitude", className:"control-label"}, defaultValues.latitude), + React.DOM.p( {className:"help-block"}) + ), + React.DOM.div( {className:"inputContainer col-md-4"}, + React.DOM.input( {name:"longitude", id:"longitude", type:"text", className:"form-control"}), + React.DOM.label( {htmlFor:"longitude", className:"control-label"}, defaultValues.longitude), + React.DOM.p( {className:"help-block"}) + ), + React.DOM.div( {className:"inputContainer col-md-4"}, + React.DOM.select( {name:"country", id:"country", className:"form-control"}, + React.DOM.option( {value:""}, defaultValues.country,":"), + country_option_list + ), + React.DOM.label( {htmlFor:"country", className:"control-label"}, defaultValues.country), + React.DOM.p( {className:"help-block"}) + ) + ), + React.DOM.div( {className:"row"}, + React.DOM.p( {className:"help-block"}, defaultValues.use_link, " ", React.DOM.a( {href:"http://mygeoposition.com/", target:"_blank"}, "http://mygeoposition.com/"), " ", defaultValues.coordinates) + ), + React.DOM.div( {className:"row"}, + React.DOM.div( {className:"inputContainer col-md-6"}, + React.DOM.input( {name:"contact_person", id:"contact_person", type:"text", className:"form-control"}), + React.DOM.label( {htmlFor:"contact_person", className:"control-label"}, defaultValues.contact_person), + React.DOM.p( {className:"help-block"}) + ), + React.DOM.div( {className:"inputContainer col-md-6"}, + React.DOM.input( {name:"contact_email", id:"contact_email", type:"text", className:"form-control"}), + React.DOM.label( {htmlFor:"contact_email", className:"control-label"}, defaultValues.contact_email), + React.DOM.p( {className:"help-block"}) + ) + ), + React.DOM.div( {className:"row"}, + React.DOM.div( {className:"inputContainer col-md-12"}, React.DOM.textarea( {id:"description", className:"form-control", name:"description", rows:"3"}), + React.DOM.label( {className:"control-label", htmlFor:"description"}, defaultValues.description), React.DOM.p( {className:"help-block"}) ) ) ), React.DOM.div( {className:"controls"}, - React.DOM.button( {className:"modal-cancel btn btn-danger", - onClick:cancelModal}, + React.DOM.button( {className:"modal-cancel btn btn-danger", onClick:cancelModal}, React.DOM.span( {className:"glyphicon glyphicon-trash"}), " ", defaultValues.cancel ), - React.DOM.button( {className:"modal-save btn btn-success", - onClick:submitModal}, + React.DOM.button( {className:"modal-save btn btn-success", onClick:submitModal}, React.DOM.span( {className:"glyphicon glyphicon-plus"}), " ", defaultValues.add_new_organisation ) ) @@ -2380,5 +2513,4 @@ document.addEventListener('DOMContentLoaded', function() { setAllSectionsCompletionPercentage(); setAllSectionsChangeListerner(); setPageCompletionPercentage(); - setModalOnClicks(); }); diff --git a/akvo/rsr/static/scripts-src/myrsr-admin.jsx b/akvo/rsr/static/scripts-src/myrsr-admin.jsx index 97c9fb3e68..10594c5622 100644 --- a/akvo/rsr/static/scripts-src/myrsr-admin.jsx +++ b/akvo/rsr/static/scripts-src/myrsr-admin.jsx @@ -7,6 +7,7 @@ // DEFAULT VALUES var defaultValues = JSON.parse(document.getElementById("default-values").innerHTML); +var countryValues = JSON.parse(document.getElementById("country-values").innerHTML); // CSRF TOKEN function getCookie(name) { @@ -865,15 +866,29 @@ function buildReactComponents(typeaheadOptions, typeaheadCallback, displayOption } return short + ' (' + long + ')'; } - inputClass = selector + " form-control " + childClass; selectorClass = document.querySelector('.' + selector); TypeaheadContainer = React.createClass({ + + getInitialState: function() { + return ({focusClass: 'inactive'}); + }, + onKeyUp: function() { + + /* Only activate the "add org" button for typeaheads that i) are for organisations, + ** and ii) are not for reporting organisations. */ + if (inputType === 'org' && selector.indexOf('reportingOrganisation') === -1) { + this.setState({focusClass: 'active'}); + } + }, + onBlur: function() { + this.setState({focusClass: 'inactive'}); + }, render: function() { return ( -
+
+
+ {defaultValues.add_new_organisation}
); } @@ -2109,23 +2127,6 @@ function setUnsavedChangesMessage() { }; } -/* Set each "add organisation" link to open the "add organisation" -** modal dialog on click */ - -function setModalOnClicks() { - var links = document.querySelectorAll('.add-organisation'); - - for (var i = 0; i < links.length; i++) { - var el = links[i]; - - el.removeEventListener('click'); - el.addEventListener('click', function(e) { - e.preventDefault(); - addOrgModal(); - }); - } -} - /* Show the "add organisation" modal dialog */ function addOrgModal() { @@ -2137,16 +2138,15 @@ function addOrgModal() { /* Submit the new org */ function submitModal() { - if (allInputsFilled()) { - var api_url, request, form, form_data, reporting_org_id; + if (allInputsFilled() && checkLocationFilled()) { + var api_url, request, form, form_data; + // Add organisation to DB form = document.querySelector('#addOrganisation'); - form_data = serialize(form); - form_data = form_data.replace('iati_org_id=&', ''); - reporting_org_id = document.querySelector('#reportingOrganisation').getAttributeNode("value").value; - form_data += '&content_owner=' + reporting_org_id; + // Remove empty IATI organistion id + form_data = form_data.replace('iati_org_id=&', ''); api_url = '/rest/v1/organisation/?format=json'; @@ -2157,6 +2157,35 @@ function addOrgModal() { request.onload = function() { if (request.status === 201) { + var organisation_id; + + // Get organisation ID + response = JSON.parse(request.responseText); + organisation_id = response.id; + + // Add location (fails silently) + if (form.querySelector('#latitude').value !== '') { + var request_loc; + api_url = '/rest/v1/organisation_location/?format=json'; + request_loc = new XMLHttpRequest(); + request_loc.open('POST', api_url, true); + request_loc.setRequestHeader("X-CSRFToken", csrftoken); + request_loc.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + request_loc.send(form_data + '&location_target=' + organisation_id); + } + + // Add logo (fails silently) + var logo_request, logo_data, org_logo_files; + org_logo_files = document.getElementById("org-logo").files; + if (org_logo_files !== undefined) { + api_url = '/rest/v1/organisation/' + organisation_id + '/add_logo/?format=json'; + logo_data = new FormData(); + logo_data.append("logo", org_logo_files[0]); + logo_request = new XMLHttpRequest(); + logo_request.open("POST", api_url); + logo_request.setRequestHeader("X-CSRFToken", csrftoken); + logo_request.send(logo_data); + } // This flag forces the fetching of a fresh API response var forceReloadOrg = true; @@ -2167,6 +2196,8 @@ function addOrgModal() { var response; response = JSON.parse(request.responseText); + document.querySelector('.orgModal').scrollTop = 0; + for (var key in response) { if (response.hasOwnProperty(key)) { var input = form.querySelector('#' + key); @@ -2186,11 +2217,14 @@ function addOrgModal() { request.onerror = function() { // There was a connection error of some sort - elAddClass(form, 'has-error'); + document.querySelector('#addOrgGeneralError').textContent = defaultValues.general_error; + document.querySelector('.orgModal').scrollTop = 0; return false; }; request.send(form_data); + } else { + document.querySelector('.orgModal').scrollTop = 0; } } @@ -2223,13 +2257,67 @@ function addOrgModal() { longNameHelp.textContent = ''; elRemoveClass(longNameHelp, 'help-block-error'); elRemoveClass(longNameContainer, 'has-error'); - } + } return allInputsFilledBoolean; } + function checkLocationFilled() { + var latitudeNode, longitudeNode, countryNode, latitudeHelp, longitudeHelp, countryHelp, result; + + latitudeNode = document.querySelector('#latitude'); + latitudeHelp = document.querySelector('#latitude + label + .help-block'); + longitudeNode = document.querySelector('#longitude'); + longitudeHelp = document.querySelector('#longitude + label + .help-block'); + countryNode = document.querySelector('#country'); + countryHelp = document.querySelector('#country + label + .help-block'); + + result = true; + + if (latitudeNode.value === '' && longitudeNode.value === '' && countryNode.value === '') { + return result; + } else if (latitudeNode.value === '' || longitudeNode.value === '' || countryNode.value === '') { + if (latitudeNode.value === '') { + latitudeHelp.textContent = defaultValues.location_check; + elAddClass(latitudeHelp, 'help-block-error'); + elAddClass(latitudeHelp.parentNode, 'has-error'); + } + if (longitudeNode.value === '') { + longitudeHelp.textContent = defaultValues.location_check; + elAddClass(longitudeHelp, 'help-block-error'); + elAddClass(longitudeHelp.parentNode, 'has-error'); + } + if (countryNode.value === '') { + countryHelp.textContent = defaultValues.location_check; + elAddClass(countryHelp, 'help-block-error'); + elAddClass(countryHelp.parentNode, 'has-error'); + } + result = false; + } else { + if (latitudeNode.value.indexOf(',') > 0) { + latitudeHelp.textContent = defaultValues.comma_value; + elAddClass(latitudeHelp, 'help-block-error'); + elAddClass(latitudeHelp.parentNode, 'has-error'); + result = false; + } + if (longitudeNode.value.indexOf(',') > 0) { + longitudeHelp.textContent = defaultValues.comma_value; + elAddClass(longitudeHelp, 'help-block-error'); + elAddClass(longitudeHelp.parentNode, 'has-error'); + result = false; + } + } + return result; + } + Modal = React.createClass({ render: function() { + var country_option_list = countryValues.map(function(country) { + return ( + + ); + }); + return (
@@ -2240,28 +2328,34 @@ function addOrgModal() {

{defaultValues.add_new_organisation}

-
+
-
+

{defaultValues.max} 25 {defaultValues.characters}

-
+

{defaultValues.max} 75 {defaultValues.characters}

-
-
-
+

{defaultValues.max} 75 {defaultValues.characters}

+
+
+
+ + +
+
+
- @@ -2276,22 +2370,60 @@ function addOrgModal() {

+
+ + +

{defaultValues.start_http}

+
-
- +
+ + +

+
+
+ + +

+
+
+ + +

+
+
+
+

{defaultValues.use_link} http://mygeoposition.com/ {defaultValues.coordinates}

+
+
+
+ + +

+
+
+ + +

+
+
+
+
+

- -
@@ -2380,5 +2512,4 @@ document.addEventListener('DOMContentLoaded', function() { setAllSectionsCompletionPercentage(); setAllSectionsChangeListerner(); setPageCompletionPercentage(); - setModalOnClicks(); }); diff --git a/akvo/rsr/static/styles-src/main.css b/akvo/rsr/static/styles-src/main.css index 7d0db02aa7..e94b758da1 100755 --- a/akvo/rsr/static/styles-src/main.css +++ b/akvo/rsr/static/styles-src/main.css @@ -1962,8 +1962,22 @@ body.translationBarActive div.skiptranslate ~ article, body.translationBarActive margin-left: -1.25em; } } } /* Modal dialog stuff for the Project Editor */ -.btn.add-organisation { - margin-top: 10px; } +div.inactive .addOrg { + display: none; } + +div.active .addOrg { + display: block; + padding: 0.5em; + background-color: white; + border: 1px solid #c8c8c8; + border-radius: 0 0 4px 4px; + margin-top: -3px; + cursor: pointer; + color: #5cb85c; } + div.active .addOrg:hover { + font-weight: bold; } + div.active .addOrg:active { + opacity: 0.8; } .modalBackground { position: fixed; @@ -2016,16 +2030,16 @@ body.translationBarActive div.skiptranslate ~ article, body.translationBarActive .modalContainer .orgModal .modalContents .inputContainer { margin-top: 2.75em; margin-bottom: 1em; } - .modalContainer .orgModal .modalContents .inputContainer input + label, .modalContainer .orgModal .modalContents .inputContainer select + label { + .modalContainer .orgModal .modalContents .inputContainer input + label, .modalContainer .orgModal .modalContents .inputContainer select + label, .modalContainer .orgModal .modalContents .inputContainer textarea + label { position: absolute; top: -25px; transition: top 0.7s ease, opacity 0.7s ease; opacity: 0; font-size: 13px; font-weight: 600; } - .modalContainer .orgModal .modalContents .inputContainer input:valid + label, .modalContainer .orgModal .modalContents .inputContainer select:valid + label { + .modalContainer .orgModal .modalContents .inputContainer input:valid + label, .modalContainer .orgModal .modalContents .inputContainer select:valid + label, .modalContainer .orgModal .modalContents .inputContainer textarea:valid + label { opacity: 1; } - .modalContainer .orgModal .modalContents .inputContainer input:focus + label, .modalContainer .orgModal .modalContents .inputContainer select:focus + label { + .modalContainer .orgModal .modalContents .inputContainer input:focus + label, .modalContainer .orgModal .modalContents .inputContainer select:focus + label, .modalContainer .orgModal .modalContents .inputContainer textarea:focus + label { color: #2c2a74; } .modalContainer .orgModal .controls { position: absolute; diff --git a/akvo/rsr/static/styles-src/main.scss b/akvo/rsr/static/styles-src/main.scss index e68de99800..a6a396720d 100755 --- a/akvo/rsr/static/styles-src/main.scss +++ b/akvo/rsr/static/styles-src/main.scss @@ -2534,8 +2534,30 @@ body { /* Modal dialog stuff for the Project Editor */ -.btn.add-organisation { - margin-top: 10px; +div.inactive { + .addOrg { + display: none; + } +} + +div.active { + .addOrg { + display: block; + padding: 0.5em; + background-color: white; + border: 1px solid rgb(200,200,200); + border-radius: 0 0 4px 4px; + margin-top: -3px; + cursor: pointer; + color: rgba($progressSuccess, 1); + + &:hover { + font-weight: bold; + } + &:active { + opacity: 0.8; + } + } } .modalBackground { @@ -2592,7 +2614,7 @@ body { margin-top: 2.75em; margin-bottom: 1em; - input, select { + input, select, textarea { & + label { position: absolute; top: -25px; diff --git a/akvo/templates/myrsr/project_editor.html b/akvo/templates/myrsr/project_editor.html index 4b488c035c..e5a19ea14f 100644 --- a/akvo/templates/myrsr/project_editor.html +++ b/akvo/templates/myrsr/project_editor.html @@ -486,9 +486,6 @@
{% trans 'Reporting organisation' %}
{% trans 'Participating organisations' %}

{% trans 'Select an organisation that is playing a role in the project. If an organisation is not currently featured in RSR, please use the "Add a new organisation" button to add a new organisation to the database.' %}

-
{% for partnership in project.partnerships.all %} {% include "myrsr/project_editor_partials/partner_input.html" with key=partnership.pk %} @@ -1249,10 +1246,27 @@
{% trans 'Keywords' %}
"multilateral": "{% trans 'Multilateral' %}", "foundation": "{% trans 'Foundation' %}", "private_sector": "{% trans 'Private Sector' %}", - "academic_training_research": "{% trans 'Academic, Training and Research' %}" + "academic_training_research": "{% trans 'Academic, Training and Research' %}", + "website": "{% trans 'Website' %}", + "start_http": "{% trans 'The URL of the website must begin with http://' %}", + "contact_person": "{% trans 'Contact name' %}", + "contact_email": "{% trans 'Contact email' %}", + "longitude": "{% trans 'Longitude' %}", + "latitude": "{% trans 'Latitude' %}", + "country": "{% trans 'Country' %}", + "use_link": "{% trans 'Use the link to' %}", + "coordinates": "{% trans 'to obtain the coordinates for your locations to ensure the pins are correctly placed.' %}", + "org_logo": "{% trans 'Organisation logo' %}", + "location_check": "{% trans 'All location fields have to be filled in' %}", + "general_error": "{% trans 'Something went wrong while adding the organisation' %}", + "comma_value": "{% trans 'It is not allowed to use a comma, use a period to denote decimals' %}" } + +