Просмотр исходного кода

- Special avatars
- Improvements in profile template

Ralfp 12 лет назад
Родитель
Сommit
febf492d92

+ 21 - 6
misago/context_processors.py

@@ -1,15 +1,30 @@
 from django.conf import settings
 from django.conf import settings
+from django.utils.importlib import import_module
 from misago import get_version
 from misago import get_version
 
 
+# Get formats
+try:
+    locale_formats = import_module('django.conf.locale.%s.formats' % settings.LANGUAGE_CODE)
+    formats = {
+               'DATE_FORMAT': locale_formats.DATE_FORMAT,
+               'TIME_FORMAT': locale_formats.TIME_FORMAT,
+               'DATETIME_FORMAT': locale_formats.DATETIME_FORMAT,
+               'SHORT_DATE_FORMAT': locale_formats.SHORT_DATE_FORMAT,
+               'SHORT_DATETIME_FORMAT': locale_formats.SHORT_DATETIME_FORMAT,
+               }
+except (ImportError, AttributeError):
+    formats = {
+               'DATE_FORMAT': settings.DATE_FORMAT,
+               'TIME_FORMAT': settings.TIME_FORMAT,
+               'DATETIME_FORMAT': settings.DATETIME_FORMAT,
+               'SHORT_DATE_FORMAT': settings.SHORT_DATE_FORMAT,
+               'SHORT_DATETIME_FORMAT': settings.SHORT_DATETIME_FORMAT,
+               }
+
 # Register context processors
 # Register context processors
 def core(request):
 def core(request):
     return {
     return {
         'board_address': settings.BOARD_ADDRESS,
         'board_address': settings.BOARD_ADDRESS,
         'version': get_version(),
         'version': get_version(),
-        'f': {
-                    'DATE_FORMAT': settings.DATE_FORMAT,
-                    'TIME_FORMAT': settings.TIME_FORMAT,
-                    'DATETIME_FORMAT': settings.DATETIME_FORMAT,
-                    'SHORT_DATETIME_FORMAT': settings.SHORT_DATETIME_FORMAT,
-                    }
+        'f': formats
     }
     }

+ 11 - 1
misago/template/templatetags/django2jinja.py

@@ -17,4 +17,14 @@ def query_string(**kwargs):
 @register.filter(name='markdown')
 @register.filter(name='markdown')
 def parse_markdown(value, format="html5"):
 def parse_markdown(value, format="html5"):
     import markdown
     import markdown
-    return markdown.markdown(value, safe_mode='escape', output_format=format)
+    return markdown.markdown(value, safe_mode='escape', output_format=format)
+
+
+@register.filter(name='reldate')
+def reldate(value, format=""):
+    return 'TODO: fancydate'
+
+
+@register.filter(name='timesince')
+def timesince(value, format=""):
+    return 'TODO: timesince'

+ 14 - 0
misago/users/admin/users/forms.py

@@ -1,6 +1,8 @@
+from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 from django import forms
 from django import forms
+from PIL import Image
 from misago.acl.models import Role
 from misago.acl.models import Role
 from misago.users.models import User, Rank
 from misago.users.models import User, Rank
 from misago.users.validators import validate_password, validate_email
 from misago.users.validators import validate_password, validate_email
@@ -14,6 +16,7 @@ class UserForm(Form):
     email = forms.EmailField(max_length=255)
     email = forms.EmailField(max_length=255)
     new_password = forms.CharField(max_length=255,required=False,widget=forms.PasswordInput)
     new_password = forms.CharField(max_length=255,required=False,widget=forms.PasswordInput)
     signature = forms.CharField(widget=forms.Textarea,required=False)
     signature = forms.CharField(widget=forms.Textarea,required=False)
+    avatar_custom = forms.CharField(max_length=255,required=False)
     avatar_ban = forms.BooleanField(widget=YesNoSwitch,required=False) 
     avatar_ban = forms.BooleanField(widget=YesNoSwitch,required=False) 
     avatar_ban_reason_user = forms.CharField(widget=forms.Textarea,required=False)
     avatar_ban_reason_user = forms.CharField(widget=forms.Textarea,required=False)
     avatar_ban_reason_admin = forms.CharField(widget=forms.Textarea,required=False)
     avatar_ban_reason_admin = forms.CharField(widget=forms.Textarea,required=False)
@@ -41,6 +44,7 @@ class UserForm(Form):
               [
               [
                _("User Avatar"),
                _("User Avatar"),
                [
                [
+                ('avatar_custom', {'label': _("Set Non-Standard Avatar"), 'help_text': _("You can make this member use special avatar by entering name of image file located in avatars directory here.")}),
                 ('avatar_ban', {'label': _("Lock Member's Avatar"), 'help_text': _("If you set this field to yes, this member's avatar will be deleted and replaced with random one selected from _removed gallery and member will not be able to change his avatar.")}),
                 ('avatar_ban', {'label': _("Lock Member's Avatar"), 'help_text': _("If you set this field to yes, this member's avatar will be deleted and replaced with random one selected from _removed gallery and member will not be able to change his avatar.")}),
                 ('avatar_ban_reason_user', {'label': _("User-visible reason for lock"), 'help_text': _("You can leave message to member explaining why he or she is unable to change his avatar anymore. This message will be displayed to member in his control panel.")}),
                 ('avatar_ban_reason_user', {'label': _("User-visible reason for lock"), 'help_text': _("You can leave message to member explaining why he or she is unable to change his avatar anymore. This message will be displayed to member in his control panel.")}),
                 ('avatar_ban_reason_admin', {'label': _("Forum Team-visible reason for lock"), 'help_text': _("You can leave message to other forum team members exmplaining why this member's avatar has been locked.")}),
                 ('avatar_ban_reason_admin', {'label': _("Forum Team-visible reason for lock"), 'help_text': _("You can leave message to other forum team members exmplaining why this member's avatar has been locked.")}),
@@ -102,6 +106,16 @@ class UserForm(Form):
             return self.cleaned_data['new_password']
             return self.cleaned_data['new_password']
         return ''
         return ''
 
 
+    def clean_avatar_custom(self):
+        if self.cleaned_data['avatar_custom']:
+            try:
+                avatar_image = Image.open('%s/avatars/%s' % (settings.STATICFILES_DIRS[0], self.cleaned_data['avatar_custom']))
+            except IOError:
+                raise ValidationError(_("Avatar does not exist or is not image file."))
+            return self.cleaned_data['avatar_custom']            
+        return ''
+
+
 class SearchUsersForm(Form):
 class SearchUsersForm(Form):
     username = forms.CharField(max_length=255, required=False)
     username = forms.CharField(max_length=255, required=False)
     email = forms.CharField(max_length=255, required=False)
     email = forms.CharField(max_length=255, required=False)

+ 6 - 0
misago/users/admin/users/views.py

@@ -232,6 +232,12 @@ class Edit(FormWidget):
                 target.default_avatar(request.settings)
                 target.default_avatar(request.settings)
         target.avatar_ban = form.cleaned_data['avatar_ban']
         target.avatar_ban = form.cleaned_data['avatar_ban']
                
                
+        # Set custom avatar
+        if form.cleaned_data['avatar_custom']:
+            target.delete_avatar()
+            target.avatar_image = form.cleaned_data['avatar_custom']
+            target.avatar_type = 'gallery'
+        
         # Update user roles
         # Update user roles
         if request.user.is_god():
         if request.user.is_god():
             target.roles.clear()
             target.roles.clear()

+ 1 - 1
misago/users/models.py

@@ -1,6 +1,7 @@
 import hashlib
 import hashlib
 import math
 import math
 from random import choice
 from random import choice
+from path import path
 from django.conf import settings
 from django.conf import settings
 from django.contrib.auth.hashers import (
 from django.contrib.auth.hashers import (
     check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
     check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
@@ -16,7 +17,6 @@ from misago.security import get_random_string
 from misago.settings.settings import Settings as DBSettings
 from misago.settings.settings import Settings as DBSettings
 from misago.users.validators import validate_username, validate_password, validate_email
 from misago.users.validators import validate_username, validate_password, validate_email
 from misago.utils import slugify
 from misago.utils import slugify
-from path import path
 
 
 class UserManager(models.Manager):
 class UserManager(models.Manager):
     """
     """

+ 6 - 2
static/sora/css/sora.css

@@ -885,12 +885,12 @@ th.table-sort.sort-desc a:hover{border-bottom:3px solid #eca09a;padding-bottom:5
 .alert{padding:10px 11px;}.alert p{margin-left:28px;}
 .alert{padding:10px 11px;}.alert p{margin-left:28px;}
 .alert p:last-child{margin-bottom:0px;}
 .alert p:last-child{margin-bottom:0px;}
 .alert p.protip{font-size:100%;}
 .alert p.protip{font-size:100%;}
-.avatar-big{border-radius:3px;width:180px;height:180px;}
+.avatar-big{border-radius:3px;width:125px;height:125px;}
 .avatar-normal{border-radius:3px;width:64px;height:64px;}
 .avatar-normal{border-radius:3px;width:64px;height:64px;}
 .avatar-small{border-radius:3px;width:28px;height:28px;}
 .avatar-small{border-radius:3px;width:28px;height:28px;}
 .avatar-tiny{border-radius:3px;width:16px;height:16px;}
 .avatar-tiny{border-radius:3px;width:16px;height:16px;}
 .navbar .avatar-small{margin:-10px 0px;position:relative;bottom:2px;}
 .navbar .avatar-small{margin:-10px 0px;position:relative;bottom:2px;}
-.page-header .avatar{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
+.page-header .avatar,.page-header .avatar-big{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
 .page-header h1 .avatar{width:46px;height:46px;}
 .page-header h1 .avatar{width:46px;height:46px;}
 .page-header h2 .avatar{width:40px;height:40px;}
 .page-header h2 .avatar{width:40px;height:40px;}
 .page-header h3 .avatar{width:28px;height:28px;}
 .page-header h3 .avatar{width:28px;height:28px;}
@@ -929,3 +929,7 @@ th.table-sort.sort-desc a:hover{border-bottom:3px solid #eca09a;padding-bottom:5
 .nav-tabs .tab-search.tab-search-no-tabs{position:relative;bottom:12px;}
 .nav-tabs .tab-search.tab-search-no-tabs{position:relative;bottom:12px;}
 .nav-tabs button{padding-left:7px;padding-right:3px;}
 .nav-tabs button{padding-left:7px;padding-right:3px;}
 .clickable{cursor:pointer;}
 .clickable{cursor:pointer;}
+.profile-header{overflow:auto;border-bottom:none;margin-bottom:30px;}.profile-header .avatar-height{overflow:auto;}.profile-header .avatar-height .avatar{margin-right:24px;width:125px;height:125px;}
+.profile-header .avatar-height h1{font-size:300%;}
+.profile-header .avatar-height .lead{color:#7b7b7b;}
+.profile-header .nav-tabs{margin-top:-22px;margin-bottom:0px;padding-left:142px;}

+ 4 - 4
static/sora/css/sora/avatars.less

@@ -2,8 +2,8 @@
 // -------------------------
 // -------------------------
 .avatar-big {
 .avatar-big {
   border-radius: 3px;
   border-radius: 3px;
-  width: 180px;
-  height: 180px;
+  width: 125px;
+  height: 125px;
 }
 }
 
 
 .avatar-normal {
 .avatar-normal {
@@ -35,7 +35,7 @@
 }
 }
 
 
 .page-header {
 .page-header {
-  .avatar {
+  .avatar, .avatar-big {
     .border-radius(3px);
     .border-radius(3px);
   }
   }
   
   
@@ -43,7 +43,7 @@
     width: 46px;
     width: 46px;
     height: 46px;
     height: 46px;
   }
   }
-  
+    
   h2 .avatar {
   h2 .avatar {
     width: 40px;
     width: 40px;
     height: 40px;
     height: 40px;

+ 31 - 1
static/sora/css/sora/utilities.less

@@ -2,4 +2,34 @@
 // --------------------------------------------------
 // --------------------------------------------------
 .clickable {
 .clickable {
   cursor: pointer;
   cursor: pointer;
-}
+}
+
+.profile-header {
+  overflow: auto;
+  border-bottom: none;
+  margin-bottom: @baseLineHeight * 1.5;
+  
+  .avatar-height {
+    overflow: auto;
+  
+    .avatar {
+      margin-right: 24px;
+      width: 125px;
+      height: 125px;
+    }
+    
+    h1 {
+      font-size: 300%;
+    }
+    
+    .lead {
+      color: lighten(@gray, 15%);
+    }
+  }
+  
+  .nav-tabs {
+    margin-top: -22px;
+    margin-bottom: 0px;
+    padding-left: 142px;
+  }
+}

+ 19 - 3
templates/sora/users/profile.html

@@ -1,13 +1,29 @@
 {% extends "sora/layout.html" %}
 {% extends "sora/layout.html" %}
 {% load i18n %}
 {% load i18n %}
+{% load humanize %}
 {% load url from future %}
 {% load url from future %}
 {% import "_forms.html" as form_theme with context %}
 {% import "_forms.html" as form_theme with context %}
 {% import "sora/macros.html" as macros with context %}
 {% import "sora/macros.html" as macros with context %}
 
 
-{% block title %}{{ macros.page_title(title=username, parent=_('Users')) }}{% endblock %}
+{% block title %}{{ macros.page_title(profile.username) }}{% endblock %}
 
 
 {% block content %}
 {% block content %}
-<div class="page-header">
-  <h1><img src="{{ profile.get_avatar() }}" class="avatar" alt="{% trans %}Member Avatar{% endtrans %}" title="{% trans %}Member Avatar{% endtrans %}"> {{ profile.username }} <small>{% trans %}Member Profile{% endtrans %}</small></h1>
+<div class="page-header profile-header header-tabbed">
+  <div class="avatar-height">
+    <img src="{{ profile.get_avatar() }}" class="avatar pull-left" alt="{% trans %}Member Avatar{% endtrans %}" title="{% trans %}Member Avatar{% endtrans %}">
+    <div class="pull-left">
+      <h1>{{ profile.username }} <small>{% if profile.title %}{{ _(profile.title) }}{% elif profile.rank.title %}{{ _(profile.rank.title) }}{% endif %}</small></h1>
+      <p class="lead">{% trans joined=profile.join_date|date(f.DATE_FORMAT), member_since=profile.join_date|naturaltime %}Joined on {{ joined }}, {{ member_since }}.{% endtrans %} {% trans last_visit=profile.last_date|naturaltime %}Last seen {{ last_visit }}.{% endtrans %}</p>
+    </div>
+  </div>	
+  <ul class="nav nav-tabs">
+    <li><a href="#">Last Posts</a></li>
+    <li class="active"><a href="#">Following</a></li>
+    <li><a href="#">Followers</a></li>
+    <li><a href="#">Profile Summary</a></li>
+  </ul>
 </div>
 </div>
+
+<p class="lead">WORK IN PROGRESS</p>
+
 {% endblock %}
 {% endblock %}