Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2191 edit updates #2198

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions akvo/rsr/models/project_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,6 @@ def img(self, value=''):
return value
img.allow_tags = True

def edit_window_has_expired(self):
"""Determine whether or not update timeout window has expired.
The timeout is controlled by settings.PROJECT_UPDATE_TIMEOUT and
defaults to 30 minutes.
"""
return (datetime.now() - self.created_at) > self.edit_timeout

@property
def expires_at(self):
return to_gmt(self.created_at + self.edit_timeout)

@property
def edit_timeout(self):
timeout_minutes = getattr(settings, 'PROJECT_UPDATE_TIMEOUT', 30)
return timedelta(minutes=timeout_minutes)

@property
def edit_time_remaining(self):
return self.edit_timeout - self.created_at

@property
def time_gmt(self):
return to_gmt(self.created_at)
Expand Down
154 changes: 154 additions & 0 deletions akvo/rsr/static/scripts-src/my-updates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Akvo RSR is covered by the GNU Affero General Public License.
// 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 >.

// DEFAULT VALUES
var defaultValues = JSON.parse(document.getElementById("default-values").innerHTML);

// CSRF TOKEN
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

var csrftoken = getCookie('csrftoken');

// display error message in empty div below title
function setError(message) {
var errorNode = document.getElementById('projectUpdateError');
errorNode.innerHTML = message;
}

// display confirmation message before deleting update
function confirmDeleteUpdate(node) {
return function(e) {
e.preventDefault();

// check if delete button is enabled
if ((' ' + node.className + ' ').indexOf(' disabled ') == -1) {
Copy link
Contributor

@KasperBrandt KasperBrandt May 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change this line to: if (node.className.indexOf('disabled') < 0) {?


var updateId = node.id.split('-')[1];
var confirmId = 'confirm-delete-' + updateId;

var confirmNode = document.getElementById(confirmId);
node.setAttribute('class', 'delete-update disabled');

var sureNode = document.createElement('span');
sureNode.innerHTML = defaultValues.sure_message;

var yesNode = document.createElement('a');
yesNode.setAttribute('style', 'color: green; margin-left: 5px;');
yesNode.onclick = confirmDelete(yesNode, updateId);
yesNode.innerHTML = defaultValues.yes;

var noNode = document.createElement('a');
noNode.setAttribute('style', 'color: red; margin-left: 5px;');
noNode.onclick = dismissConfirmationNo(sureNode, updateId);
noNode.innerHTML = defaultValues.no;

sureNode.appendChild(yesNode);
sureNode.appendChild(noNode);
confirmNode.appendChild(sureNode);

}

};
}

function dismissConfirmationNo(sureNode, updateId) {
return function(e) {
e.preventDefault();

dismissConfirmation(sureNode, updateId);
};
}

function dismissConfirmation(sureNode, updateId) {
var parentNode = sureNode.parentNode;
parentNode.removeChild(sureNode);

var updateNodeId = 'update-' + updateId;
deleteButtonNode = document.getElementById(updateNodeId);
deleteButtonNode.setAttribute('class', 'delete-update');
}

function confirmDelete(yesNode, updateId) {
return function(e) {
e.preventDefault();
var sureNode = yesNode.parentNode;
sureNode.innerHTML = defaultValues.delete_progress;
// var parentNode = sureNode.parentNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines can be removed 😄

// parentNode.removeChild(sureNode);

deleteUpdate(sureNode, updateId);
};
}

// make api call to delete specified update
function deleteUpdate(sureNode, updateId) {

var api_url = '/rest/v1/project_update/' + updateId + '/?format=json',
request = new XMLHttpRequest();

request.open('DELETE', api_url, true);
request.setRequestHeader("X-CSRFToken", csrftoken);
request.setRequestHeader("Content-type", "application/json");

request.onload = function() {
if (request.status >= 204 && request.status < 300) {
// Successfully deleted update, remove container.
removeUpdateContainer(updateId);
} else if (request.status == 404) {
// Update not found, most likely already deleted
dismissConfirmation(sureNode, updateId);
setError(defaultValues.error_delete);
} else {
// We reached our target server, but it returned an error
dismissConfirmation(sureNode, updateId);
setError(request.status + defaultValues.error_misc);
}
};

request.onerror = function() {
// There was a connection error of some sort
setError(defaultValues.error_connection);
return false;
};

request.send();
}

// remove update from screen once it has been delete
function removeUpdateContainer(updateId) {
var nodeId = 'update-' + updateId + '-container';
var removeNode = document.getElementById(nodeId);
var parentNode = removeNode.parentNode;
parentNode.removeChild(removeNode);
}

// add onlick to all delete buttons
function setDeleteUpdateOnClick() {
var deleteUpdateNodes = document.querySelectorAll('.delete-update');

if (deleteUpdateNodes !== null) {
for (var i = 0; i < deleteUpdateNodes.length; i++) {
deleteUpdateNodes[i].onclick = confirmDeleteUpdate(deleteUpdateNodes[i]);
}
}
}

document.addEventListener('DOMContentLoaded', function() {
setDeleteUpdateOnClick();
});
26 changes: 26 additions & 0 deletions akvo/rsr/static/styles-src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,32 @@ nav.navbar-fixed-top {
position: relative;
right: auto; } }

div.updateListMenu ul {
margin-top: -10px; }
div.updateListMenu ul li {
display: inline-block; }
@media only screen and (max-width: 992px) {
div.updateListMenu ul li {
display: block; } }
div.updateListMenu ul li a:last-child {
margin-right: 0; }
div.updateListMenu ul li a.disabled {
background-color: #e6e6e6;
color: #cccccc;
pointer-events: initial;
cursor: not-allowed;
border: none;
-moz-border-radius: 5px;
-o-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px; }
div.updateListMenu ul li a.disabled:hover {
text-decoration: none; }

div.update-confirm-container {
margin-top: 5px;
font-size: 85%; }

.navbar-form {
padding-left: 0;
margin-bottom: 25px; }
Expand Down
36 changes: 36 additions & 0 deletions akvo/rsr/static/styles-src/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,42 @@ nav.navbar-fixed-top {
}
}

div.updateListMenu {
ul {
margin-top: -10px;
li {
display: inline-block;
@include responsive (mediums-max-screens) {
display: block;
}
@include responsive (small-max-screens) {}
a {
&:last-child {
margin-right: 0;
}
&.disabled {
background-color: lighten($primary4, 30%);
color: lighten($primary4, 20%);
pointer-events: initial;
cursor: not-allowed;
border: none;
@include border-radius(5px);
&:hover {
text-decoration: none;
}
}
}
@include responsive (wide-screens) {}
> a {}
}
}
}

div.update-confirm-container {
margin-top: 5px;
font-size: 85%;
}

.navbar-form {
padding-left: 0;
margin-bottom: 25px;
Expand Down
4 changes: 0 additions & 4 deletions akvo/rsr/views/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,6 @@ def set_update(request, project_id, edit_mode=False, form_class=ProjectUpdateFor
request.error_message = u'You can only edit your own updates.'
raise PermissionDenied

if update.edit_window_has_expired():
request.error_message = u'You cannot edit this update anymore, the 30 minutes time limit has passed.'
raise PermissionDenied

if request.method == 'POST':
updateform = form_class(request.POST, request.FILES, instance=update)
if updateform.is_valid():
Expand Down
6 changes: 6 additions & 0 deletions akvo/settings/40-pipeline.conf
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ PIPELINE_JS = {
),
'output_filename': 'scripts/my-results-select.min.js',
},
'my_updates': {
'source_filenames': (
'scripts-src/my-updates.js',
),
'output_filename': 'scripts/my-updates.min.js',
},
'more_partners': {
'source_filenames': (
'scripts-src/more-partners.js',
Expand Down
37 changes: 33 additions & 4 deletions akvo/templates/myrsr/my_updates.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{% extends "myrsr/myrsr_base.html" %}

{% load i18n bootstrap3 rsr_utils %}
{% load i18n bootstrap3 rsr_utils compressed %}

{% block title %}{% trans 'MyRSR - My updates' %}{% endblock %}

{% block myrsr_main %}
<div class="col-sm-6">
<h3>{% trans 'My updates' %}</h3>
<p class="text-danger" id="projectUpdateError"></p>
</div>
<div class="col-sm-6 btnHeader text-right">
<form class="navbar-form" role="search">
Expand All @@ -23,10 +24,10 @@ <h3>{% trans 'My updates' %}</h3>
{% if page %}
<div id="updates" class=" col-sm-12">
<table class="table table-striped table-responsive">
<thead><tr><th>{% trans 'Media' %}</th><th>{% trans 'Title' %}</th><th>{% trans 'Date' %}</th><th>{% trans 'Project' %}</th><th>{% trans 'Actions' %}</th></tr></thead>
<thead><tr><th>{% trans 'Media' %}</th><th>{% trans 'Title' %}</th><th>{% trans 'Date' %}</th><th>{% trans 'Project' %}</th><th class="text-right">{% trans 'Actions' %}</th></tr></thead>
<tbody>
{% for update in page %}
<tr>
<tr id="update-{{ update.id }}-container">
<td>
<a href="{% url 'update-main' update.project.id update.id %}">
{% if update.video %}
Expand All @@ -43,7 +44,15 @@ <h3>{% trans 'My updates' %}</h3>
</td>
<td>{{ update.created_at }}</td>
<td>{{ update.project }}</td>
<td><div class="twoColumns"><a href="{% url 'update-main' update.project.id update.id %}">{% trans 'View' %}</a></div></td>
<td>
<div class="updateListMenu text-right">
<ul class="nav">
<li><a href="{% url 'edit-update' update.project.id update.id %}">Edit</a></li>
<li><a href="" class="delete-update" id="update-{{ update.id }}">Delete</a></li>
</ul>
</div>
<div id="confirm-delete-{{ update.id }}" class="update-confirm-container text-right"></div>
</td>
</tr>
{% endfor %}
</tbody>
Expand All @@ -66,4 +75,24 @@ <h3>{% trans 'My updates' %}</h3>
{% trans "You have not placed any updates yet." %}
</p>
{% endif %}

{% endblock %}

{% block js %}
{{ block.super }}

<script type="application/json" id="default-values">
{
"sure_message": "{% trans 'Are you sure?' %}",
"yes": "{% trans 'Yes' %}",
"no": "{% trans 'No' %}",
"delete_text": "{% trans 'Delete' %}",
"delete_progress": "{% trans 'Deleting update' %} <i class=\"fa fa-spin fa-spinner\"/>",
"error_delete": "{% trans 'Update not found, try reloading the page.' %}",
"error_connection": "{% trans 'Connection error, check your internet connection.' %}",
"error_misc": "{% trans ' error, please contact support using the help button to the right.' %}"
}
</script>

{% compressed_js 'my_updates' %}
{% endblock %}
4 changes: 2 additions & 2 deletions akvo/templates/update_add.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ <h2>{% if edit_mode %}{% trans 'Edit an update' %}{% else %}{% trans 'Add an upd
{% else %}
{% if edit_mode %}
<p class="small">
{% blocktrans with update_time=update.time_gmt|date:"H:i T" expires_time=update.expires_at|date:"H:i T" %}
You posted this update at {{update_time}}. You have until {{expires_time}} to save your edits.
{% blocktrans with update_time=update.time_gmt|date:"H:i T" %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add the date here as well.

Previously you would not be able to see this on a different date (since we only allowed editing within 20 minutes), but now it's a bit weird to see "You posted this update at 14:29 GMT." when that was two weeks ago.

You posted this update at {{update_time}}.
{% endblocktrans %}
</p>
{% endif %}
Expand Down
7 changes: 1 addition & 6 deletions akvo/templates/update_main.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@
<div class="container">
<div class="row">
<div class="col-md-7 col-xs-12">
<h2><i class="fa fa-camera-retro"></i> {{update.title}}
{% if user == update.user and not update.edit_window_has_expired %}
<a href="{% url 'edit-update' project.pk update.pk %}" class="btn btn-primary">
{% bootstrap_icon "pencil" %} Edit</a>
{% endif %}
</h2>
<h2><i class="fa fa-camera-retro"></i> {{update.title}}</h2>
<div class="upDateTime"><i class="fa fa-calendar-o"></i> {{update.created_at|date:"d-M-Y"}}</div>
<div class="post-info media">
<div class="media-body">
Expand Down