1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-28 03:10:45 +00:00

added func: set group avatar

This commit is contained in:
llj 2012-07-11 15:13:59 +08:00 committed by xiez
parent cbab401d84
commit 75b724a169
14 changed files with 261 additions and 11 deletions

View File

@ -1,8 +1,30 @@
import os
from django import forms
from django.utils.translation import ugettext as _
from django.template.defaultfilters import filesizeformat
from group.settings import ( AVATAR_MAX_SIZE, AVATAR_ALLOWED_FILE_EXTS)
class MessageForm(forms.Form):
message = forms.CharField(max_length=500)
class MessageReplyForm(forms.Form):
message = forms.CharField(max_length=150)
class AvatarForm(forms.Form):
avatar = forms.ImageField()
def clean_avatar(self):
data = self.cleaned_data['avatar']
if AVATAR_ALLOWED_FILE_EXTS:
(root, ext) = os.path.splitext(data.name.lower())
if ext not in AVATAR_ALLOWED_FILE_EXTS:
raise forms.ValidationError(
_(u"%(ext)s is an invalid file extension. Authorized extensions are : %(valid_exts_list)s") %
{ 'ext' : ext, 'valid_exts_list' : ", ".join(AVATAR_ALLOWED_FILE_EXTS) })
if data.size > AVATAR_MAX_SIZE:
raise forms.ValidationError(
_(u"Your file is too big (%(size)s), the maximum allowed size is %(max_valid_size)s") %
{ 'size' : filesizeformat(data.size), 'max_valid_size' : filesizeformat(AVATAR_MAX_SIZE)})

View File

@ -1,5 +1,26 @@
import datetime
import os
from django.core.files.base import ContentFile
from django.utils.translation import ugettext as _
from django.utils.hashcompat import md5_constructor
from django.db import models
from group.settings import (AVATAR_STORAGE_DIR, AVATAR_RESIZE_METHOD,
AVATAR_MAX_AVATARS_PER_USER, AVATAR_THUMB_FORMAT,
AVATAR_HASH_USERDIRNAMES, AVATAR_HASH_FILENAMES,
AVATAR_THUMB_QUALITY, AUTO_GENERATE_AVATAR_SIZES)
try:
from cStringIO import StringIO
dir(StringIO) # Placate PyFlakes
except ImportError:
from StringIO import StringIO
try:
from PIL import Image
dir(Image) # Placate PyFlakes
except ImportError:
import Image
class GroupMessage(models.Model):
group_id = models.IntegerField()
@ -12,3 +33,86 @@ class MessageReply(models.Model):
from_email = models.EmailField()
message = models.CharField(max_length=150)
timestamp = models.DateTimeField(default=datetime.datetime.now)
def avatar_file_path(instance=None, filename=None, size=None, ext=None):
tmppath = [AVATAR_STORAGE_DIR]
if AVATAR_HASH_USERDIRNAMES:
tmp = md5_constructor(instance.user.username).hexdigest()
tmppath.extend([tmp[0], tmp[1], instance.group_id])
else:
tmppath.append(instance.group_id)
if not filename:
# Filename already stored in database
filename = instance.avatar.name
if ext and AVATAR_HASH_FILENAMES:
# An extension was provided, probably because the thumbnail
# is in a different format than the file. Use it. Because it's
# only enabled if AVATAR_HASH_FILENAMES is true, we can trust
# it won't conflict with another filename
(root, oldext) = os.path.splitext(filename)
filename = root + "." + ext
else:
# File doesn't exist yet
if AVATAR_HASH_FILENAMES:
(root, ext) = os.path.splitext(filename)
filename = md5_constructor(smart_str(filename)).hexdigest()
filename = filename + ext
if size:
tmppath.extend(['resized', str(size)])
tmppath.append(os.path.basename(filename))
return os.path.join(*tmppath)
def find_extension(format):
format = format.lower()
if format == 'jpeg':
format = 'jpg'
return format
class Avatar(models.Model):
group_id = models.CharField(max_length=255)
avatar = models.ImageField(max_length=1024, upload_to=avatar_file_path, blank=True)
date_uploaded = models.DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
return _(u'Avatar for %s') % self.group_id
def thumbnail_exists(self, size):
return self.avatar.storage.exists(self.avatar_name(size))
def create_thumbnail(self, size, quality=None):
try:
orig = self.avatar.storage.open(self.avatar.name, 'rb').read()
image = Image.open(StringIO(orig))
except IOError:
return # What should we do here? Render a "sorry, didn't work" img?
quality = quality or AVATAR_THUMB_QUALITY
(w, h) = image.size
if w != size or h != size:
if w > h:
diff = (w - h) / 2
image = image.crop((diff, 0, w - diff, h))
else:
diff = (h - w) / 2
image = image.crop((0, diff, w, h - diff))
if image.mode != "RGB":
image = image.convert("RGB")
image = image.resize((size, size), AVATAR_RESIZE_METHOD)
thumb = StringIO()
image.save(thumb, AVATAR_THUMB_FORMAT, quality=quality)
thumb_file = ContentFile(thumb.getvalue())
else:
thumb_file = ContentFile(orig)
thumb = self.avatar.storage.save(self.avatar_name(size), thumb_file)
def avatar_url(self, size):
return self.avatar.storage.url(self.avatar_name(size))
def avatar_name(self, size):
ext = find_extension(AVATAR_THUMB_FORMAT)
return avatar_file_path(
instance=self,
size=size,
ext=ext
)

23
group/settings.py Normal file
View File

@ -0,0 +1,23 @@
from django.conf import settings
try:
from PIL import Image
dir(Image) # Placate PyFlakes
except ImportError:
import Image
AVATAR_DEFAULT_SIZE = 48
AUTO_GENERATE_AVATAR_SIZES = (80, 48)
AVATAR_RESIZE_METHOD = getattr(settings, 'AVATAR_RESIZE_METHOD', Image.ANTIALIAS)
AVATAR_STORAGE_DIR = 'avatars/groups'
AVATAR_GRAVATAR_BACKUP = getattr(settings, 'AVATAR_GRAVATAR_BACKUP', True)
AVATAR_GRAVATAR_DEFAULT = getattr(settings, 'AVATAR_GRAVATAR_DEFAULT', None)
AVATAR_DEFAULT_URL = 'avatars/groups/default.png'
AVATAR_MAX_AVATARS_PER_USER = getattr(settings, 'AVATAR_MAX_AVATARS_PER_USER', 42)
AVATAR_MAX_SIZE = getattr(settings, 'AVATAR_MAX_SIZE', 1024 * 1024)
AVATAR_THUMB_FORMAT = getattr(settings, 'AVATAR_THUMB_FORMAT', "JPEG")
AVATAR_THUMB_QUALITY = getattr(settings, 'AVATAR_THUMB_QUALITY', 85)
AVATAR_HASH_FILENAMES = getattr(settings, 'AVATAR_HASH_FILENAMES', False)
AVATAR_HASH_USERDIRNAMES = getattr(settings, 'AVATAR_HASH_USERDIRNAMES', False)
AVATAR_ALLOWED_FILE_EXTS = getattr(settings, 'AVATAR_ALLOWED_FILE_EXTS', None)
AVATAR_CACHE_TIMEOUT = getattr(settings, 'AVATAR_CACHE_TIMEOUT', 60*60)

View File

@ -10,8 +10,9 @@
<div class="side fright">
<h3>操作</h3>
<ul class="with-bg">
<li><a href="{{ SITE_ROOT }}group/{{ group.id }}/set_avatar/">设置小组图标</a></li>
<li><a id="group-remove" href="#" data="{{ SITE_ROOT }}group/{{ group.id }}/?op=delete">解散小组</a></li>
<li><a id="group-info" href="{{ SITE_ROOT }}group/{{ group.id }}/">返回小组</a></li>
<li><a href="{{ SITE_ROOT }}group/{{ group.id }}/">返回小组</a></li>
</ul>
</div>

View File

@ -1,5 +1,5 @@
{% extends "myhome_base.html" %}
{% load seahub_tags %}
{% load seahub_tags group_tags %}
{% block nav_group_class %}class="cur"{% endblock %}
{% block left_panel %}
@ -18,7 +18,7 @@
<li class="group fleft">
<div class="pic fleft">
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">
<img src="{{ MEDIA_URL }}img/group.jpg" alt="小组图标" />
<img src="{% grp_avatar_url group.props.id 48 %}" alt="{{ group.props.group_name }}的图标" title="{{ group.props.group_name }}" />
</a>
</div>
<div class="txt fright">
@ -44,7 +44,7 @@
{% block extra_script %}
<script type="text/javascript">
$("#group-add").click(function() {
$("#group-add-form").modal({appendTo: "#main", containerCss:{padding:18}});
$("#group-add-form").modal({appendTo: "#main"});
return false;
});

View File

@ -0,0 +1,21 @@
{% extends "myhome_base.html" %}
{% load group_tags %}
{% block nav_group_class %}class="cur"{% endblock %}
{% block main_panel %}
<div class="avatar-op">
<h2>修改小组 {{ group.group_name }} 的图标</h2>
<div class="avatar-op-con">
<h3>当前图标:</h3>
<img src="{% grp_avatar_url group.id %}" alt="当前图标" class="avatar" />
<h3 class="upload-new-avatar-hd">上传新图标:</h3>
<form enctype="multipart/form-data" method="post" action="">
{{ form.avatar }}<br />
{{ form.avatar.errors }}
<input type="submit" value="提交" class="submit" />
</form>
</div>
</div>
{% endblock %}

View File

View File

@ -0,0 +1,45 @@
import urllib
from django.conf import settings
from django import template
from django.utils.hashcompat import md5_constructor
from django.core.urlresolvers import reverse
from group.settings import (AVATAR_DEFAULT_SIZE, AVATAR_DEFAULT_URL)
from group.models import Avatar
register = template.Library()
def get_default_avatar_url():
base_url = getattr(settings, 'STATIC_URL', None)
if not base_url:
base_url = getattr(settings, 'MEDIA_URL', '')
# Don't use base_url if the default avatar url starts with http:// of https://
if AVATAR_DEFAULT_URL.startswith('http://') or AVATAR_DEFAULT_URL.startswith('https://'):
return AVATAR_DEFAULT_URL
# We'll be nice and make sure there are no duplicated forward slashes
ends = base_url.endswith('/')
begins = AVATAR_DEFAULT_URL.startswith('/')
if ends and begins:
base_url = base_url[:-1]
elif not ends and not begins:
return '%s/%s' % (base_url, AVATAR_DEFAULT_URL)
return '%s%s' % (base_url, AVATAR_DEFAULT_URL)
@register.simple_tag
def grp_avatar_url(group_id, size=AVATAR_DEFAULT_SIZE):
grp_avatars = Avatar.objects.filter(group_id=group_id)
if grp_avatars:
avatar = grp_avatars.order_by('-date_uploaded')[0]
else:
avatar = None
if avatar:
if not avatar.thumbnail_exists(size):
avatar.create_thumbnail(size)
avatar_src = avatar.avatar_url(size)
else:
avatar_src = get_default_avatar_url()
return avatar_src

View File

@ -1,12 +1,13 @@
from django.conf.urls.defaults import *
from views import group_list, group_info, group_member_operations, \
group_members, msg_reply, msg_reply_new
group_members, msg_reply, msg_reply_new, set_avatar
urlpatterns = patterns('',
url(r'^(?P<group_id>[\d]+)/$', group_info, name='group_info'),
url(r'^reply/(?P<msg_id>[\d]+)/$', msg_reply, name='msg_reply'),
url(r'^reply/new/$', msg_reply_new, name='msg_reply_new'),
url(r'^(?P<group_id>[\d]+)/set_avatar/$', set_avatar, name='set_avatar'),
url(r'^(?P<group_id>[\d]+)/members/$', group_members, name='group_members'),
(r'^(?P<group_id>[\d]+)/member/(?P<user_name>[^/]+)/$', group_member_operations),
)

View File

@ -10,8 +10,8 @@ from seaserv import ccnet_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, get_re
get_group_repoids, check_group_staff, get_commits
from pysearpc import SearpcError
from models import GroupMessage, MessageReply
from forms import MessageForm, MessageReplyForm
from models import GroupMessage, MessageReply, Avatar
from forms import MessageForm, MessageReplyForm, AvatarForm
from signals import grpmsg_added, grpmsg_reply_added
from seahub.contacts.models import Contact
from seahub.notifications.models import UserNotification
@ -457,3 +457,32 @@ def group_unshare_repo(request, repo_id, group_id, from_email):
if seafserv_threaded_rpc.group_unshare_repo(repo_id, group_id, from_email) != 0:
return go_error(request, u'共享失败:内部错误')
@login_required
def set_avatar(request, group_id):
try:
group_id_int = int(group_id)
except ValueError:
return go_error(request, u'group id 不是有效参数')
if not check_group_staff(group_id_int, request.user):
return go_permission_error(request, u'只有小组管理员有权设置小组图标')
group = ccnet_threaded_rpc.get_group(group_id_int)
if not group:
return HttpResponseRedirect(reverse('group_list', args=[]))
form = AvatarForm(request.POST or None, request.FILES or None)
if request.method == 'POST' and 'avatar' in request.FILES:
if form.is_valid():
image_file = request.FILES['avatar']
avatar = Avatar()
avatar.avatar.save(image_file.name, image_file)
avatar.group_id = group_id
avatar.save()
return render_to_response('group/set_avatar.html', {
'group' : group,
'form' : form,
}, context_instance=RequestContext(request))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,5 +1,5 @@
{% extends "myhome_base.html" %}
{% load seahub_tags avatar_tags %}
{% load seahub_tags avatar_tags group_tags %}
{% block nav_myhome_class %}class="cur"{% endblock %}
{% block left_panel %}
@ -31,7 +31,9 @@
<ul>
{% for group in groups_manage %}
<li class="mygroup">
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/"><img src="{{ MEDIA_URL }}img/group.jpg" alt="" /></a><br />
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">
<img src="{% grp_avatar_url group.props.id 48 %}" alt="{{ group.props.group_name }}的图标" title="{{ group.props.group_name }}" />
</a><br />
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">{{ group.props.group_name }}</a>
{% if group.new_msg %}
<span class="tips">(新留言)</span>
@ -48,7 +50,9 @@
<ul>
{% for group in groups_join %}
<li class="mygroup">
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/"><img src="{{ MEDIA_URL }}img/group.jpg" alt="" /></a><br />
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">
<img src="{% grp_avatar_url group.props.id 48 %}" alt="{{ group.props.group_name }}的图标" title="{{ group.props.group_name }}" />
</a><br />
<a href="{{ SITE_ROOT }}group/{{ group.props.id }}/">{{ group.props.group_name }}</a>
{% if group.new_msg %}
<span class="tips">(新留言)</span>