commit
fb7dfd2bc4
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,13 @@
|
|||
<a name="1.0.0"></a>
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -14,11 +14,7 @@
|
|||
# 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/>.
|
||||
|
||||
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"])
|
|
@ -95,7 +95,7 @@
|
|||
<td>
|
||||
<a href="{{ home_url }}"
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# 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.
|
||||
"""
|
||||
|
@ -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
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -37,9 +37,10 @@
|
|||
|
||||
{% for att in values['new']%}
|
||||
<dd style="background: #eee; padding: 5px 15px; color: #444">
|
||||
<i>
|
||||
{{ att.filename|linebreaksbr }} {% if att.description %}({{ att.description|linebreaksbr }}){% endif %}
|
||||
</i>
|
||||
<a href="{{ att.url }}" target="_blank" style="font-weight: bold; color: #444">
|
||||
{{ att.filename|linebreaksbr }}
|
||||
</a>
|
||||
{% if att.description %}<i> {{ att.description|linebreaksbr }}</i>{% endif %}
|
||||
</dd>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
@ -51,7 +52,9 @@
|
|||
|
||||
{% for att in values['changed'] %}
|
||||
<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>
|
||||
{% if att.changes.is_deprecated %}
|
||||
{% if att.changes.is_deprecated.1 %}
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -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."))
|
||||
|
|
|
@ -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()))
|
||||
|
|
Loading…
Reference in New Issue