diff --git a/CHANGELOG.md b/CHANGELOG.md
index 275e949b..734a282d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
-
-# 1.0.0 taiga-back (2014-10-07)
+# Changelog #
+
+## 1.1.0 (2014-10-13)
+
+### Misc
+- Fix bugs related to unicode chars on attachments.
+- Fix wrong static url resolve usage on emails.
+- Fix some bugs on import/export api related with attachments.
+
+## 1.0.0 (2014-10-07)
### Misc
- Lots of small and not so small bugfixes
diff --git a/requirements.txt b/requirements.txt
index 1fd56ec2..ad35c9ee 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -20,9 +20,10 @@ fn==0.2.13
diff-match-patch==20121119
requests==2.4.1
-# Comment it if you are using python >= 3.4
-enum34==1.0
easy-thumbnails==2.1
celery==3.1.12
redis==2.10.3
Unidecode==0.04.16
+
+# Comment it if you are using python >= 3.4
+enum34==1.0
diff --git a/taiga/auth/services.py b/taiga/auth/services.py
index fe07b1bf..10c4e573 100644
--- a/taiga/auth/services.py
+++ b/taiga/auth/services.py
@@ -36,7 +36,7 @@ from taiga.users.serializers import UserSerializer
from taiga.users.services import get_and_validate_user
from .backends import get_token_for_user
-
+from .signals import user_registered as user_registered_signal
def send_public_register_email(user) -> bool:
"""
@@ -126,6 +126,7 @@ def public_register(username:str, password:str, email:str, full_name:str):
raise exc.WrongArguments("User is already register.")
# send_public_register_email(user)
+ user_registered_signal.send(sender=user.__class__, user=user)
return user
@@ -177,6 +178,7 @@ def private_register_for_new_user(token:str, username:str, email:str,
membership = get_membership_by_token(token)
membership.user = user
membership.save(update_fields=["user"])
+ user_registered_signal.send(sender=user.__class__, user=user)
return user
@@ -202,6 +204,9 @@ def github_register(username:str, email:str, full_name:str, github_id:int, bio:s
membership.user = user
membership.save(update_fields=["user"])
+ if created:
+ user_registered_signal.send(sender=user.__class__, user=user)
+
return user
diff --git a/taiga/base/templatetags/functions.py b/taiga/auth/signals.py
similarity index 81%
rename from taiga/base/templatetags/functions.py
rename to taiga/auth/signals.py
index 678630c0..9c5b9ca0 100644
--- a/taiga/base/templatetags/functions.py
+++ b/taiga/auth/signals.py
@@ -14,11 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from django_jinja import library
-from django_sites.utils import static
+import django.dispatch
-register = library.Library()
-@register.global_function
-def full_url_static(text) -> str:
- return static(text)
+user_registered = django.dispatch.Signal(providing_args=["user"])
diff --git a/taiga/base/templates/emails/base.jinja b/taiga/base/templates/emails/base.jinja
index 014b1aed..0138a1b7 100644
--- a/taiga/base/templates/emails/base.jinja
+++ b/taiga/base/templates/emails/base.jinja
@@ -95,7 +95,7 @@
-
+
|
diff --git a/taiga/base/templatetags/__init__.py b/taiga/base/templatetags/__init__.py
deleted file mode 100644
index 8b137891..00000000
--- a/taiga/base/templatetags/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/taiga/base/utils/diff.py b/taiga/base/utils/diff.py
index 3d4f0aa8..7c8ea034 100644
--- a/taiga/base/utils/diff.py
+++ b/taiga/base/utils/diff.py
@@ -15,7 +15,7 @@
# along with this program. If not, see .
-def make_diff(first:dict, second:dict, not_found_value=None) -> dict:
+def make_diff(first:dict, second:dict, not_found_value=None, excluded_keys:tuple=()) -> dict:
"""
Compute a diff between two dicts.
"""
@@ -39,4 +39,9 @@ def make_diff(first:dict, second:dict, not_found_value=None) -> dict:
if frst == scnd:
del diff[key]
+ # Removed excluded keys
+ for key in excluded_keys:
+ if key in diff:
+ del diff[key]
+
return diff
diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py
index d2ed7dec..24d44aea 100644
--- a/taiga/export_import/service.py
+++ b/taiga/export_import/service.py
@@ -15,6 +15,7 @@
# along with this program. If not, see .
import uuid
+import os.path as path
from django.contrib.contenttypes.models import ContentType
@@ -206,6 +207,8 @@ def store_attachment(project, obj, attachment):
if serialized.object.owner is None:
serialized.object.owner = serialized.object.project.owner
serialized.object._importing = True
+ serialized.object.size = serialized.object.attached_file.size
+ serialized.object.name = path.basename(serialized.object.attached_file.name).lower()
serialized.save()
return serialized
add_errors("attachments", serialized.errors)
diff --git a/taiga/projects/history/freeze_impl.py b/taiga/projects/history/freeze_impl.py
index 2d350340..1af7bcf0 100644
--- a/taiga/projects/history/freeze_impl.py
+++ b/taiga/projects/history/freeze_impl.py
@@ -162,6 +162,7 @@ def extract_attachments(obj) -> list:
for attach in obj.attachments.all():
yield {"id": attach.id,
"filename": os.path.basename(attach.attached_file.name),
+ "url": attach.attached_file.url,
"description": attach.description,
"is_deprecated": attach.is_deprecated,
"description": attach.description,
diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py
index 9fa3deb5..809cae1c 100644
--- a/taiga/projects/history/models.py
+++ b/taiga/projects/history/models.py
@@ -166,10 +166,14 @@ class HistoryEntry(models.Model):
for aid in set(tuple(oldattachs.keys()) + tuple(newattachs.keys())):
if aid in oldattachs and aid in newattachs:
- if oldattachs[aid] != newattachs[aid]:
+ changes = make_diff_from_dicts(oldattachs[aid], newattachs[aid],
+ excluded_keys=("filename", "url"))
+
+ if changes:
change = {
- "filename": oldattachs[aid]["filename"],
- "changes": make_diff_from_dicts(oldattachs[aid], newattachs[aid])
+ "filename": newattachs[aid]["filename"],
+ "url": newattachs[aid]["url"],
+ "changes": changes
}
attachments["changed"].append(change)
elif aid in oldattachs and aid not in newattachs:
diff --git a/taiga/projects/history/templates/emails/includes/fields_diff-html.jinja b/taiga/projects/history/templates/emails/includes/fields_diff-html.jinja
index ebd2cce4..049bf6cc 100644
--- a/taiga/projects/history/templates/emails/includes/fields_diff-html.jinja
+++ b/taiga/projects/history/templates/emails/includes/fields_diff-html.jinja
@@ -37,9 +37,10 @@
{% for att in values['new']%}
-
- {{ att.filename|linebreaksbr }} {% if att.description %}({{ att.description|linebreaksbr }}){% endif %}
-
+
+ {{ att.filename|linebreaksbr }}
+
+ {% if att.description %} {{ att.description|linebreaksbr }}{% endif %}
{% endfor %}
{% endif %}
@@ -51,7 +52,9 @@
{% for att in values['changed'] %}
- {{ att.filename|linebreaksbr }}
+
+ {{ att.filename|linebreaksbr }}
+
{% if att.changes.is_deprecated %}
{% if att.changes.is_deprecated.1 %}
diff --git a/taiga/projects/userstories/migrations/0005_auto_20141009_1656.py b/taiga/projects/userstories/migrations/0005_auto_20141009_1656.py
new file mode 100644
index 00000000..f8a2efda
--- /dev/null
+++ b/taiga/projects/userstories/migrations/0005_auto_20141009_1656.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('userstories', '0004_auto_20141001_1817'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='userstory',
+ name='generated_from_issue',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to='issues.Issue', verbose_name='generated from issue', related_name='generated_user_stories', null=True),
+ ),
+ ]
diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py
index a520b3df..7f824c84 100644
--- a/taiga/projects/userstories/models.py
+++ b/taiga/projects/userstories/models.py
@@ -94,6 +94,7 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod
verbose_name=_("is team requirement"))
attachments = generic.GenericRelation("attachments.Attachment")
generated_from_issue = models.ForeignKey("issues.Issue", null=True, blank=True,
+ on_delete=models.SET_NULL,
related_name="generated_user_stories",
verbose_name=_("generated from issue"))
_importing = None
diff --git a/taiga/users/api.py b/taiga/users/api.py
index 8c602b89..50855b13 100644
--- a/taiga/users/api.py
+++ b/taiga/users/api.py
@@ -50,7 +50,7 @@ class MembersFilterBackend(BaseFilterBackend):
if project_id:
Project = apps.get_model('projects', 'Project')
project = get_object_or_404(Project, pk=project_id)
- if project.memberships.filter(user=request.user).exists() or project.owner == request.user:
+ if request.user.is_authenticated() and (project.memberships.filter(user=request.user).exists() or project.owner == request.user):
return queryset.filter(Q(memberships__project=project) | Q(id=project.owner.id)).distinct()
else:
raise exc.PermissionDenied(_("You don't have permisions to see this project users."))
diff --git a/taiga/users/models.py b/taiga/users/models.py
index 2a3c35eb..3c1698ea 100644
--- a/taiga/users/models.py
+++ b/taiga/users/models.py
@@ -20,6 +20,8 @@ import os.path as path
import random
import re
+from unidecode import unidecode
+
from django.db import models
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
@@ -27,6 +29,7 @@ from django.contrib.auth.models import UserManager, AbstractBaseUser
from django.core import validators
from django.utils import timezone
from django.utils.encoding import force_bytes
+from django.template.defaultfilters import slugify
from djorm_pgarray.fields import TextArrayField
@@ -41,6 +44,9 @@ def generate_random_hex_color():
def get_user_file_path(instance, filename):
basename = path.basename(filename).lower()
+ base, ext = path.splitext(basename)
+ base = slugify(unidecode(base))
+ basename = "".join([base, ext])
hs = hashlib.sha256()
hs.update(force_bytes(timezone.now().isoformat()))