Merge commit 'master' into stable

remotes/origin/enhancement/email-actions 1.1.0
Andrey Antukh 2014-10-13 10:31:01 +02:00
commit fb7dfd2bc4
15 changed files with 74 additions and 22 deletions

View File

@ -1,5 +1,13 @@
<a name="1.0.0"></a> # Changelog #
# 1.0.0 taiga-back (2014-10-07)
## 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 ### Misc
- Lots of small and not so small bugfixes - Lots of small and not so small bugfixes

View File

@ -20,9 +20,10 @@ fn==0.2.13
diff-match-patch==20121119 diff-match-patch==20121119
requests==2.4.1 requests==2.4.1
# Comment it if you are using python >= 3.4
enum34==1.0
easy-thumbnails==2.1 easy-thumbnails==2.1
celery==3.1.12 celery==3.1.12
redis==2.10.3 redis==2.10.3
Unidecode==0.04.16 Unidecode==0.04.16
# Comment it if you are using python >= 3.4
enum34==1.0

View File

@ -36,7 +36,7 @@ from taiga.users.serializers import UserSerializer
from taiga.users.services import get_and_validate_user from taiga.users.services import get_and_validate_user
from .backends import get_token_for_user from .backends import get_token_for_user
from .signals import user_registered as user_registered_signal
def send_public_register_email(user) -> bool: 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.") raise exc.WrongArguments("User is already register.")
# send_public_register_email(user) # send_public_register_email(user)
user_registered_signal.send(sender=user.__class__, user=user)
return 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 = get_membership_by_token(token)
membership.user = user membership.user = user
membership.save(update_fields=["user"]) membership.save(update_fields=["user"])
user_registered_signal.send(sender=user.__class__, user=user)
return 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.user = user
membership.save(update_fields=["user"]) membership.save(update_fields=["user"])
if created:
user_registered_signal.send(sender=user.__class__, user=user)
return user return user

View File

@ -14,11 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django_jinja import library import django.dispatch
from django_sites.utils import static
register = library.Library()
@register.global_function user_registered = django.dispatch.Signal(providing_args=["user"])
def full_url_static(text) -> str:
return static(text)

View File

@ -95,7 +95,7 @@
<td> <td>
<a href="{{ home_url }}" <a href="{{ home_url }}"
title="{{ home_url_name }}"> title="{{ home_url_name }}">
<img src="{{ full_url_static("emails/email-logo.png") }}" alt="Taiga" height="32" /> <img src="{{ static("emails/email-logo.png") }}" alt="Taiga" height="32" />
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -1 +0,0 @@

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
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. 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: if frst == scnd:
del diff[key] del diff[key]
# Removed excluded keys
for key in excluded_keys:
if key in diff:
del diff[key]
return diff return diff

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import uuid import uuid
import os.path as path
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -206,6 +207,8 @@ def store_attachment(project, obj, attachment):
if serialized.object.owner is None: if serialized.object.owner is None:
serialized.object.owner = serialized.object.project.owner serialized.object.owner = serialized.object.project.owner
serialized.object._importing = True 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() serialized.save()
return serialized return serialized
add_errors("attachments", serialized.errors) add_errors("attachments", serialized.errors)

View File

@ -162,6 +162,7 @@ def extract_attachments(obj) -> list:
for attach in obj.attachments.all(): for attach in obj.attachments.all():
yield {"id": attach.id, yield {"id": attach.id,
"filename": os.path.basename(attach.attached_file.name), "filename": os.path.basename(attach.attached_file.name),
"url": attach.attached_file.url,
"description": attach.description, "description": attach.description,
"is_deprecated": attach.is_deprecated, "is_deprecated": attach.is_deprecated,
"description": attach.description, "description": attach.description,

View File

@ -166,10 +166,14 @@ class HistoryEntry(models.Model):
for aid in set(tuple(oldattachs.keys()) + tuple(newattachs.keys())): for aid in set(tuple(oldattachs.keys()) + tuple(newattachs.keys())):
if aid in oldattachs and aid in newattachs: 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 = { change = {
"filename": oldattachs[aid]["filename"], "filename": newattachs[aid]["filename"],
"changes": make_diff_from_dicts(oldattachs[aid], newattachs[aid]) "url": newattachs[aid]["url"],
"changes": changes
} }
attachments["changed"].append(change) attachments["changed"].append(change)
elif aid in oldattachs and aid not in newattachs: elif aid in oldattachs and aid not in newattachs:

View File

@ -37,9 +37,10 @@
{% for att in values['new']%} {% for att in values['new']%}
<dd style="background: #eee; padding: 5px 15px; color: #444"> <dd style="background: #eee; padding: 5px 15px; color: #444">
<i> <a href="{{ att.url }}" target="_blank" style="font-weight: bold; color: #444">
{{ att.filename|linebreaksbr }} {% if att.description %}({{ att.description|linebreaksbr }}){% endif %} {{ att.filename|linebreaksbr }}
</i> </a>
{% if att.description %}<i> {{ att.description|linebreaksbr }}</i>{% endif %}
</dd> </dd>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
@ -51,7 +52,9 @@
{% for att in values['changed'] %} {% for att in values['changed'] %}
<dd style="background: #eee; padding: 5px 15px; color: #444"> <dd style="background: #eee; padding: 5px 15px; color: #444">
<b>{{ att.filename|linebreaksbr }}</b> <a href="{{ att.url }}" target="_blank" style="font-weight: bold; color: #444">
{{ att.filename|linebreaksbr }}
</a>
<ul> <ul>
{% if att.changes.is_deprecated %} {% if att.changes.is_deprecated %}
{% if att.changes.is_deprecated.1 %} {% if att.changes.is_deprecated.1 %}

View File

@ -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),
),
]

View File

@ -94,6 +94,7 @@ class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, mod
verbose_name=_("is team requirement")) verbose_name=_("is team requirement"))
attachments = generic.GenericRelation("attachments.Attachment") attachments = generic.GenericRelation("attachments.Attachment")
generated_from_issue = models.ForeignKey("issues.Issue", null=True, blank=True, generated_from_issue = models.ForeignKey("issues.Issue", null=True, blank=True,
on_delete=models.SET_NULL,
related_name="generated_user_stories", related_name="generated_user_stories",
verbose_name=_("generated from issue")) verbose_name=_("generated from issue"))
_importing = None _importing = None

View File

@ -50,7 +50,7 @@ class MembersFilterBackend(BaseFilterBackend):
if project_id: if project_id:
Project = apps.get_model('projects', 'Project') Project = apps.get_model('projects', 'Project')
project = get_object_or_404(Project, pk=project_id) 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() return queryset.filter(Q(memberships__project=project) | Q(id=project.owner.id)).distinct()
else: else:
raise exc.PermissionDenied(_("You don't have permisions to see this project users.")) raise exc.PermissionDenied(_("You don't have permisions to see this project users."))

View File

@ -20,6 +20,8 @@ import os.path as path
import random import random
import re import re
from unidecode import unidecode
from django.db import models from django.db import models
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _ 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.core import validators
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.template.defaultfilters import slugify
from djorm_pgarray.fields import TextArrayField from djorm_pgarray.fields import TextArrayField
@ -41,6 +44,9 @@ def generate_random_hex_color():
def get_user_file_path(instance, filename): def get_user_file_path(instance, filename):
basename = path.basename(filename).lower() basename = path.basename(filename).lower()
base, ext = path.splitext(basename)
base = slugify(unidecode(base))
basename = "".join([base, ext])
hs = hashlib.sha256() hs = hashlib.sha256()
hs.update(force_bytes(timezone.now().isoformat())) hs.update(force_bytes(timezone.now().isoformat()))