From 8b6a10ead45bcd915f5da223de2b3fd3c30fc7b5 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sat, 12 Jun 2010 22:34:13 -0400 Subject: removed some old cruft --- .gitignore | 1 + askbot.iml | 10 - cache/README.TXT | 1 - django_authopenid/README | 5 + dos2unix.sh | 12 - fbconnect/__init__.py | 0 fbconnect/fb.py | 96 - fbconnect/forms.py | 8 - fbconnect/models.py | 6 - fbconnect/pjson.py | 313 ---- fbconnect/tests.py | 23 - fbconnect/urls.py | 21 - fbconnect/views.py | 112 -- forum/badges/__init__.py | 10 - forum/badges/base.py | 11 - forum/bin/dos2unix.sh | 12 + forum/bin/rmpyc | 1 + forum/conf/skin_counter_settings.py | 2 +- forum/const/__init__.py | 4 +- forum/deps/README | 2 + forum/deps/__init__.py | 0 forum/deps/grapefruit.py | 1973 +++++++++++++++++++++ forum/management/__init__.py | 3 - forum/models/__init__.py | 9 +- forum/modules.py | 78 - forum/skins/default/media/js/com.cnprog.i18n.js | 450 ++--- forum/skins/default/media/style/style.css | 18 +- forum/skins/default/templates/question_list.html | 2 +- forum/skins/default/templates/user_responses.html | 4 +- forum/sql_scripts/update_2010_01_23.sql | 9 - forum/templatetags/extra_filters.py | 2 +- forum/templatetags/extra_tags.py | 10 +- forum/tests.py | 2 +- forum/utils/colors.py | 2 +- forum/views/users.py | 4 +- forum_modules/__init__.py | 0 forum_modules/authentication/README | 3 - forum_modules/authentication/auth.py | 144 -- forum_modules/books/__init__.py | 3 - forum_modules/books/models.py | 63 - forum_modules/books/urls.py | 10 - forum_modules/books/views.py | 142 -- forum_modules/grapefruit.py | 1973 --------------------- forum_modules/pgfulltext/DISABLED | 0 forum_modules/pgfulltext/__init__.py | 9 - forum_modules/pgfulltext/handlers.py | 11 - forum_modules/pgfulltext/management.py | 30 - forum_modules/pgfulltext/pg_fts_install.sql | 38 - forum_modules/robotstxt/DISABLED | 0 forum_modules/robotstxt/__init__.py | 0 forum_modules/robotstxt/templates/robots.txt | 2 - forum_modules/robotstxt/urls.py | 6 - forum_modules/sphinxfulltext/DISABLED | 0 forum_modules/sphinxfulltext/__init__.py | 0 forum_modules/sphinxfulltext/dependencies.py | 2 - forum_modules/sphinxfulltext/handlers.py | 4 - forum_modules/sphinxfulltext/models.py | 11 - forum_modules/sphinxfulltext/settings.py | 5 - livesettings/README | 4 + rmpyc | 1 - run | 1 - run-tests | 1 - 62 files changed, 2251 insertions(+), 3428 deletions(-) delete mode 100755 askbot.iml delete mode 100755 cache/README.TXT create mode 100644 django_authopenid/README delete mode 100644 dos2unix.sh delete mode 100644 fbconnect/__init__.py delete mode 100644 fbconnect/fb.py delete mode 100644 fbconnect/forms.py delete mode 100644 fbconnect/models.py delete mode 100644 fbconnect/pjson.py delete mode 100644 fbconnect/tests.py delete mode 100644 fbconnect/urls.py delete mode 100644 fbconnect/views.py delete mode 100644 forum/badges/__init__.py delete mode 100644 forum/badges/base.py create mode 100644 forum/bin/dos2unix.sh create mode 100755 forum/bin/rmpyc create mode 100644 forum/deps/README create mode 100644 forum/deps/__init__.py create mode 100644 forum/deps/grapefruit.py delete mode 100644 forum/modules.py delete mode 100755 forum/sql_scripts/update_2010_01_23.sql delete mode 100644 forum_modules/__init__.py delete mode 100644 forum_modules/authentication/README delete mode 100644 forum_modules/authentication/auth.py delete mode 100644 forum_modules/books/__init__.py delete mode 100644 forum_modules/books/models.py delete mode 100644 forum_modules/books/urls.py delete mode 100644 forum_modules/books/views.py delete mode 100644 forum_modules/grapefruit.py delete mode 100644 forum_modules/pgfulltext/DISABLED delete mode 100644 forum_modules/pgfulltext/__init__.py delete mode 100644 forum_modules/pgfulltext/handlers.py delete mode 100644 forum_modules/pgfulltext/management.py delete mode 100644 forum_modules/pgfulltext/pg_fts_install.sql delete mode 100755 forum_modules/robotstxt/DISABLED delete mode 100644 forum_modules/robotstxt/__init__.py delete mode 100755 forum_modules/robotstxt/templates/robots.txt delete mode 100644 forum_modules/robotstxt/urls.py delete mode 100644 forum_modules/sphinxfulltext/DISABLED delete mode 100644 forum_modules/sphinxfulltext/__init__.py delete mode 100644 forum_modules/sphinxfulltext/dependencies.py delete mode 100644 forum_modules/sphinxfulltext/handlers.py delete mode 100644 forum_modules/sphinxfulltext/models.py delete mode 100644 forum_modules/sphinxfulltext/settings.py create mode 100644 livesettings/README delete mode 100755 rmpyc delete mode 100755 run delete mode 100755 run-tests diff --git a/.gitignore b/.gitignore index 42ce2e42..2f2899a3 100755 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.swp *.log cache/?? +run *.wsgi nbproject settings_local.py diff --git a/askbot.iml b/askbot.iml deleted file mode 100755 index 4e760f0a..00000000 --- a/askbot.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/cache/README.TXT b/cache/README.TXT deleted file mode 100755 index 54247a82..00000000 --- a/cache/README.TXT +++ /dev/null @@ -1 +0,0 @@ -this file is just a placeholder so the empty directory is not ignored by version control \ No newline at end of file diff --git a/django_authopenid/README b/django_authopenid/README new file mode 100644 index 00000000..67c33d60 --- /dev/null +++ b/django_authopenid/README @@ -0,0 +1,5 @@ +this is a forked version of django-authopenid module +specifically for askbot forum project. + +most likely it is not useful for anything else and +in fact will be phased out in askbot as well diff --git a/dos2unix.sh b/dos2unix.sh deleted file mode 100644 index 2864426a..00000000 --- a/dos2unix.sh +++ /dev/null @@ -1,12 +0,0 @@ -#please take care not to dos2unix anything in your .git directory -#because that will probably break your repo -dos2unix `find . -name '*.py'` -dos2unix `find . -name '*.po'` -dos2unix `find . -name '*.js'` -dos2unix `find . -name '*.css'` -dos2unix `find . -name '*.txt'` -dos2unix `find ./sphinx -type f` -dos2unix `find ./cron -type f` -dos2unix settings_local.py.dist -dos2unix README -dos2unix INSTALL diff --git a/fbconnect/__init__.py b/fbconnect/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/fbconnect/fb.py b/fbconnect/fb.py deleted file mode 100644 index 8d41c3a2..00000000 --- a/fbconnect/fb.py +++ /dev/null @@ -1,96 +0,0 @@ -from forum.conf import settings as forum_settings -from time import time -from datetime import datetime -from urllib import urlopen, urlencode - -try: - from json import load as load_json -except: - from pjson import fread as load_json - -from models import FBAssociation -import hashlib -import logging - -REST_SERVER = 'http://api.facebook.com/restserver.php' - -def generate_sig(values): - keys = [] - - for key in sorted(values.keys()): - keys.append(key) - - signature = ''.join(['%s=%s' % (key, values[key]) for key in keys]) + forum_settings.FB_SECRET - return hashlib.md5(signature).hexdigest() - -def check_cookies_signature(cookies): - API_KEY = forum_settings.FB_API_KEY - - values = {} - - for key in cookies.keys(): - if (key.startswith(API_KEY + '_')): - values[key.replace(API_KEY + '_', '')] = cookies[key] - - return generate_sig(values) == cookies[API_KEY] - -def get_user_data(cookies): - request_data = { - 'method': 'Users.getInfo', - 'api_key': forum_settings.FB_API_KEY, - 'call_id': time(), - 'v': '1.0', - 'uids': cookies[forum_settings.FB_API_KEY + '_user'], - 'fields': 'name,first_name,last_name', - 'format': 'json', - } - - request_data['sig'] = generate_sig(request_data) - fb_response = urlopen(REST_SERVER, urlencode(request_data)) - #print(fb_response) - return load_json(fb_response)[0] - - -def delete_cookies(response): - API_KEY = forum_settings.FB_API_KEY - - response.delete_cookie(API_KEY + '_user') - response.delete_cookie(API_KEY + '_session_key') - response.delete_cookie(API_KEY + '_expires') - response.delete_cookie(API_KEY + '_ss') - response.delete_cookie(API_KEY) - response.delete_cookie('fbsetting_' + API_KEY) - -def check_session_expiry(cookies): - return datetime.fromtimestamp(float(cookies[forum_settings.FB_API_KEY+'_expires'])) > datetime.now() - -STATES = { - 'FIRSTTIMER': 1, - 'SESSIONEXPIRED': 2, - 'RETURNINGUSER': 3, - 'INVALIDSTATE': 4, -} - -def get_user_state(request): - API_KEY = forum_settings.FB_API_KEY - logging.debug('') - - if API_KEY in request.COOKIES: - logging.debug('FB API key is in request cookies') - if check_cookies_signature(request.COOKIES): - logging.debug('FB cookie signature is fine') - if check_session_expiry(request.COOKIES): - logging.debug('FB session is not expired') - try: - uassoc = FBAssociation.objects.get(fbuid=request.COOKIES[API_KEY + '_user']) - logging.debug('found existing FB user association') - return (STATES['RETURNINGUSER'], uassoc.user) - except: - logging.debug('dont have FB association for this user') - return (STATES['FIRSTTIMER'], get_user_data(request.COOKIES)) - else: - logging.debug('FB session expired') - return (STATES['SESSIONEXPIRED'], None) - logging.debug('FB state is INVALID') - - return (STATES['INVALIDSTATE'], None) diff --git a/fbconnect/forms.py b/fbconnect/forms.py deleted file mode 100644 index 94f86816..00000000 --- a/fbconnect/forms.py +++ /dev/null @@ -1,8 +0,0 @@ -from django_authopenid.forms import NextUrlField, UserNameField, UserEmailField - -from django import forms - -class FBConnectRegisterForm(forms.Form): - next = NextUrlField() - username = UserNameField() - email = UserEmailField() diff --git a/fbconnect/models.py b/fbconnect/models.py deleted file mode 100644 index 2172217d..00000000 --- a/fbconnect/models.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.db import models -from django.contrib.auth.models import User - -class FBAssociation(models.Model): - user = models.ForeignKey(User) - fbuid = models.CharField(max_length=12, unique=True) diff --git a/fbconnect/pjson.py b/fbconnect/pjson.py deleted file mode 100644 index 273b684e..00000000 --- a/fbconnect/pjson.py +++ /dev/null @@ -1,313 +0,0 @@ -import string -import types - -## json.py implements a JSON (http://json.org) reader and writer. -## Copyright (C) 2005 Patrick D. Logan -## Contact mailto:patrickdlogan@stardecisions.com -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; either -## version 2.1 of the License, or (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -class _StringGenerator(object): - def __init__(self, string): - self.string = string - self.index = -1 - def peek(self): - i = self.index + 1 - if i < len(self.string): - return self.string[i] - else: - return None - def next(self): - self.index += 1 - if self.index < len(self.string): - return self.string[self.index] - else: - raise StopIteration - def all(self): - return self.string - -class WriteException(Exception): - pass - -class ReadException(Exception): - pass - -class JsonReader(object): - hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15} - escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'} - - def read(self, s): - self._generator = _StringGenerator(s) - result = self._read() - return result - - def _read(self): - self._eatWhitespace() - peek = self._peek() - if peek is None: - raise ReadException, "Nothing to read: '%s'" % self._generator.all() - if peek == '{': - return self._readObject() - elif peek == '[': - return self._readArray() - elif peek == '"': - return self._readString() - elif peek == '-' or peek.isdigit(): - return self._readNumber() - elif peek == 't': - return self._readTrue() - elif peek == 'f': - return self._readFalse() - elif peek == 'n': - return self._readNull() - elif peek == '/': - self._readComment() - return self._read() - else: - raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all() - - def _readTrue(self): - self._assertNext('t', "true") - self._assertNext('r', "true") - self._assertNext('u', "true") - self._assertNext('e', "true") - return True - - def _readFalse(self): - self._assertNext('f', "false") - self._assertNext('a', "false") - self._assertNext('l', "false") - self._assertNext('s', "false") - self._assertNext('e', "false") - return False - - def _readNull(self): - self._assertNext('n', "null") - self._assertNext('u', "null") - self._assertNext('l', "null") - self._assertNext('l', "null") - return None - - def _assertNext(self, ch, target): - if self._next() != ch: - raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all()) - - def _readNumber(self): - isfloat = False - result = self._next() - peek = self._peek() - while peek is not None and (peek.isdigit() or peek == "."): - isfloat = isfloat or peek == "." - result = result + self._next() - peek = self._peek() - try: - if isfloat: - return float(result) - else: - return int(result) - except ValueError: - raise ReadException, "Not a valid JSON number: '%s'" % result - - def _readString(self): - result = "" - assert self._next() == '"' - try: - while self._peek() != '"': - ch = self._next() - if ch == "\\": - ch = self._next() - if ch in 'brnft': - ch = self.escapes[ch] - elif ch == "u": - ch4096 = self._next() - ch256 = self._next() - ch16 = self._next() - ch1 = self._next() - n = 4096 * self._hexDigitToInt(ch4096) - n += 256 * self._hexDigitToInt(ch256) - n += 16 * self._hexDigitToInt(ch16) - n += self._hexDigitToInt(ch1) - ch = unichr(n) - elif ch not in '"/\\': - raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all()) - result = result + ch - except StopIteration: - raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all() - assert self._next() == '"' - return result - - def _hexDigitToInt(self, ch): - try: - result = self.hex_digits[ch.upper()] - except KeyError: - try: - result = int(ch) - except ValueError: - raise ReadException, "The character %s is not a hex digit." % ch - return result - - def _readComment(self): - assert self._next() == "/" - second = self._next() - if second == "/": - self._readDoubleSolidusComment() - elif second == '*': - self._readCStyleComment() - else: - raise ReadException, "Not a valid JSON comment: %s" % self._generator.all() - - def _readCStyleComment(self): - try: - done = False - while not done: - ch = self._next() - done = (ch == "*" and self._peek() == "/") - if not done and ch == "/" and self._peek() == "*": - raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all() - self._next() - except StopIteration: - raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all() - - def _readDoubleSolidusComment(self): - try: - ch = self._next() - while ch != "\r" and ch != "\n": - ch = self._next() - except StopIteration: - pass - - def _readArray(self): - result = [] - assert self._next() == '[' - done = self._peek() == ']' - while not done: - item = self._read() - result.append(item) - self._eatWhitespace() - done = self._peek() == ']' - if not done: - ch = self._next() - if ch != ",": - raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch) - assert ']' == self._next() - return result - - def _readObject(self): - result = {} - assert self._next() == '{' - done = self._peek() == '}' - while not done: - key = self._read() - if type(key) is not types.StringType: - raise ReadException, "Not a valid JSON object key (should be a string): %s" % key - self._eatWhitespace() - ch = self._next() - if ch != ":": - raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch) - self._eatWhitespace() - val = self._read() - result[key] = val - self._eatWhitespace() - done = self._peek() == '}' - if not done: - ch = self._next() - if ch != ",": - raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch) - assert self._next() == "}" - return result - - def _eatWhitespace(self): - p = self._peek() - while p is not None and p in string.whitespace or p == '/': - if p == '/': - self._readComment() - else: - self._next() - p = self._peek() - - def _peek(self): - return self._generator.peek() - - def _next(self): - return self._generator.next() - -class JsonWriter(object): - - def _append(self, s): - self._results.append(s) - - def write(self, obj, escaped_forward_slash=False): - self._escaped_forward_slash = escaped_forward_slash - self._results = [] - self._write(obj) - return "".join(self._results) - - def _write(self, obj): - ty = type(obj) - if ty is types.DictType: - n = len(obj) - self._append("{") - for k, v in obj.items(): - self._write(k) - self._append(":") - self._write(v) - n = n - 1 - if n > 0: - self._append(",") - self._append("}") - elif ty is types.ListType or ty is types.TupleType: - n = len(obj) - self._append("[") - for item in obj: - self._write(item) - n = n - 1 - if n > 0: - self._append(",") - self._append("]") - elif ty is types.StringType or ty is types.UnicodeType: - self._append('"') - obj = obj.replace('\\', r'\\') - if self._escaped_forward_slash: - obj = obj.replace('/', r'\/') - obj = obj.replace('"', r'\"') - obj = obj.replace('\b', r'\b') - obj = obj.replace('\f', r'\f') - obj = obj.replace('\n', r'\n') - obj = obj.replace('\r', r'\r') - obj = obj.replace('\t', r'\t') - self._append(obj) - self._append('"') - elif ty is types.IntType or ty is types.LongType: - self._append(str(obj)) - elif ty is types.FloatType: - self._append("%f" % obj) - elif obj is True: - self._append("true") - elif obj is False: - self._append("false") - elif obj is None: - self._append("null") - else: - raise WriteException, "Cannot write in JSON: %s" % repr(obj) - -def write(obj, escaped_forward_slash=False): - return JsonWriter().write(obj, escaped_forward_slash) - -def read(s): - return JsonReader().read(s) - -def fread(f): - return read(f.read()) diff --git a/fbconnect/tests.py b/fbconnect/tests.py deleted file mode 100644 index 2247054b..00000000 --- a/fbconnect/tests.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these with more appropriate tests for your application. -""" - -from django.test import TestCase - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.failUnlessEqual(1 + 1, 2) - -__test__ = {"doctest": """ -Another way to test that 1 + 1 is equal to 2. - ->>> 1 + 1 == 2 -True -"""} - diff --git a/fbconnect/urls.py b/fbconnect/urls.py deleted file mode 100644 index 81b0cb0f..00000000 --- a/fbconnect/urls.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.conf.urls.defaults import * -from django.utils.translation import ugettext as _ -from django.views.generic.simple import direct_to_template -from views import signin, register - -urlpatterns = patterns('', - url( - r'^xd_receiver$', - direct_to_template, - {'template': 'fbconnect/xd_receiver.html',}, - name='xd_receiver' - ), - - url(r'^%s$' % _('signin/'), signin, name="fb_signin"), - url(r'^%s%s$' % (_('signin/'), _('newquestion/')), signin, {'newquestion': True}, name="fb_signin_new_question"), - url(r'^%s%s$' % (_('signin/'), _('newanswer/')), signin, {'newanswer': True}, name="fb_signin_new_answer"), - - url(r'^%s$' % _('register/'), register, name="fb_user_register"), - url(r'^%s%s$' % (_('register/'), _('newquestion/')), register, {'newquestion': True}, name="fb_user_register_new_question"), - url(r'^%s%s$' % (_('register/'), _('newanswer/')), register, {'newanswer': True}, name="fb_user_register_new_answer"), -) diff --git a/fbconnect/views.py b/fbconnect/views.py deleted file mode 100644 index 91ea757a..00000000 --- a/fbconnect/views.py +++ /dev/null @@ -1,112 +0,0 @@ -from django.shortcuts import render_to_response as render -from django.template import RequestContext -from django.http import HttpResponseRedirect -from django.utils.safestring import mark_safe -from django.core.urlresolvers import reverse -from django.contrib.auth.models import User -from django.contrib.auth import login, logout -from models import FBAssociation -from forum.forms import SimpleEmailSubscribeForm -from django.conf import settings - -import fb -import forms - -import logging - -def signin(request, newquestion = False, newanswer = False): - logging.debug('') - state, context = fb.get_user_state(request) - - if state == fb.STATES['FIRSTTIMER']: - logging.debug('FB state = FIRSTTIMER') - if newquestion: - register_url = 'fb_user_register_new_question' - elif newanswer: - register_url = 'fb_user_register_new_answer' - else: - register_url = 'fb_user_register' - return HttpResponseRedirect(reverse(register_url)) - elif state == fb.STATES['RETURNINGUSER']: - logging.debug('FB state = RETURNINGUSER') - return login_and_forward(request, context, newquestion, newanswer) - elif state == fb.STATES['SESSIONEXPIRED']: - logging.debug('FB state = SESSIONEXPIRED') - response = logout(request, next_page=reverse('index')) - fb.delete_cookies(response) - return response - - return HttpResponseRedirect(reverse('index')) - -def register(request, newquestion = False, newanswer = False): - logging.debug('') - state, context = fb.get_user_state(request) - - if state == fb.STATES['FIRSTTIMER']: - logging.debug('FB FIRSTTIMER - try to register locally') - logging.debug('request method is %s' % request.method) - if request.method == 'POST' and 'bnewaccount' in request.POST: - form1 = forms.FBConnectRegisterForm(request.POST) - email_feeds_form = SimpleEmailSubscribeForm(request.POST) - - if (form1.is_valid() and email_feeds_form.is_valid()): - tmp_pwd = User.objects.make_random_password() - user_ = User.objects.create_user(form1.cleaned_data['username'], - form1.cleaned_data['email'], tmp_pwd) - - user_.set_unusable_password() - logging.debug('created new internal user %s' % form1.cleaned_data['username']) - - uassoc = FBAssociation(user=user_, fbuid=context['uid']) - uassoc.save() - logging.debug('created new user association') - - email_feeds_form.save(user_) - - return login_and_forward(request, user_, newquestion, newanswer) - else: - logging.debug('form user input is invalid') - else: - form1 = forms.FBConnectRegisterForm(initial={ - 'next': '/', - 'username': context['name'], - 'email': '', - }) - email_feeds_form = SimpleEmailSubscribeForm() - - return render('authopenid/complete.html', { - 'form1': form1, - 'email_feeds_form': email_feeds_form, - 'provider':mark_safe('facebook'), - 'login_type':'facebook', - 'gravatar_faq_url':reverse('faq') + '#gravatar', - }, context_instance=RequestContext(request)) - else: - logging.debug('not a FIRSTTIMER --> redirect to index view') - return HttpResponseRedirect(reverse('index')) - -def login_and_forward(request, user, newquestion = False, newanswer = False): - old_session = request.session.session_key - user.backend = "django.contrib.auth.backends.ModelBackend" - logging.debug('attached auth.backends.ModelBackend to this FB user') - login(request, user) - logging.debug('user logged in!') - - from forum.models import signals#todo: move to authentication app - signals.user_logged_in.send(user=user,session_key=old_session,sender=None) - logging.debug('user_logged_in signal sent') - - if (newquestion): - from forum.models import Question - question = Question.objects.filter(author=user).order_by('-added_at')[0] - logging.debug('redirecting to newly posted question') - return HttpResponseRedirect(question.get_absolute_url()) - - if (newanswer): - from forum.models import Answer - answer = Answer.objects.filter(author=user).order_by('-added_at')[0] - logging.debug('redirecting to newly posted answer') - return HttpResponseRedirect(answer.get_absolute_url()) - - logging.debug('redirecting to front page') - return HttpResponseRedirect('/') diff --git a/forum/badges/__init__.py b/forum/badges/__init__.py deleted file mode 100644 index 8d7cd097..00000000 --- a/forum/badges/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -import re - -from forum.badges.base import BadgeImplementation -from forum.modules import get_modules_script_classes - -ALL_BADGES = dict([ - (re.sub('BadgeImpl', '', name).lower(), cls) for name, cls - in get_modules_script_classes('badges', BadgeImplementation).items() - if not re.search('AbstractBadgeImpl$', name) - ]) \ No newline at end of file diff --git a/forum/badges/base.py b/forum/badges/base.py deleted file mode 100644 index 03ef3565..00000000 --- a/forum/badges/base.py +++ /dev/null @@ -1,11 +0,0 @@ - - -class BadgeImplementation(object): - name = "" - description = "" - - def install(self): - pass - - def process_job(self): - raise NotImplementedError \ No newline at end of file diff --git a/forum/bin/dos2unix.sh b/forum/bin/dos2unix.sh new file mode 100644 index 00000000..2864426a --- /dev/null +++ b/forum/bin/dos2unix.sh @@ -0,0 +1,12 @@ +#please take care not to dos2unix anything in your .git directory +#because that will probably break your repo +dos2unix `find . -name '*.py'` +dos2unix `find . -name '*.po'` +dos2unix `find . -name '*.js'` +dos2unix `find . -name '*.css'` +dos2unix `find . -name '*.txt'` +dos2unix `find ./sphinx -type f` +dos2unix `find ./cron -type f` +dos2unix settings_local.py.dist +dos2unix README +dos2unix INSTALL diff --git a/forum/bin/rmpyc b/forum/bin/rmpyc new file mode 100755 index 00000000..014575f6 --- /dev/null +++ b/forum/bin/rmpyc @@ -0,0 +1 @@ +rm `find . -name '*.pyc'` diff --git a/forum/conf/skin_counter_settings.py b/forum/conf/skin_counter_settings.py index 51c7e332..5a9d177f 100644 --- a/forum/conf/skin_counter_settings.py +++ b/forum/conf/skin_counter_settings.py @@ -4,7 +4,7 @@ Skin settings to color view, vote and answer counters from forum.conf.settings_wrapper import settings from livesettings import ConfigurationGroup, IntegerValue, StringValue from django.utils.translation import ugettext as _ -from forum_modules.grapefruit import Color +from forum.deps.grapefruit import Color SKIN_COUNTER_SETTINGS = ConfigurationGroup( 'SKIN_COUNTER_SETTINGS', diff --git a/forum/const/__init__.py b/forum/const/__init__.py index b1c9de7a..d77ba6b7 100644 --- a/forum/const/__init__.py +++ b/forum/const/__init__.py @@ -104,8 +104,8 @@ TYPE_ACTIVITY_MENTION = 19 #todo: rename this to TYPE_ACTIVITY_CHOICES TYPE_ACTIVITY = ( - (TYPE_ACTIVITY_ASK_QUESTION, _('question')), - (TYPE_ACTIVITY_ANSWER, _('answer')), + (TYPE_ACTIVITY_ASK_QUESTION, _('asked a question')), + (TYPE_ACTIVITY_ANSWER, _('answered a question')), (TYPE_ACTIVITY_COMMENT_QUESTION, _('commented question')), (TYPE_ACTIVITY_COMMENT_ANSWER, _('commented answer')), (TYPE_ACTIVITY_UPDATE_QUESTION, _('edited question')), diff --git a/forum/deps/README b/forum/deps/README new file mode 100644 index 00000000..2dfee4dd --- /dev/null +++ b/forum/deps/README @@ -0,0 +1,2 @@ +any python modules that are not accessible +through easy_install, but are necessary for the askbot forum diff --git a/forum/deps/__init__.py b/forum/deps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/forum/deps/grapefruit.py b/forum/deps/grapefruit.py new file mode 100644 index 00000000..ca684745 --- /dev/null +++ b/forum/deps/grapefruit.py @@ -0,0 +1,1973 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*-# + +# Copyright (c) 2008, Xavier Basty +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''GrapeFruit - Color manipulation in Python''' + +# $Id: grapefruit.py 31 2008-06-15 10:48:06Z xbasty $ +__author__ = 'Xavier Basty ' +__version__ = '0.1a3' + + +# The default white reference, use 2° Standard Observer, D65 (daylight) +_DEFAULT_WREF = (0.95043, 1.00000, 1.08890) + +_oneThird = 1.0 / 3 +_srgbGammaCorrInv = 0.03928 / 12.92 +_sixteenHundredsixteenth = 16.0 / 116 + +_RybWheel = ( + 0, 26, 52, + 83, 120, 130, + 141, 151, 162, + 177, 190, 204, + 218, 232, 246, + 261, 275, 288, + 303, 317, 330, + 338, 345, 352, + 360) + +_RgbWheel = ( + 0, 8, 17, + 26, 34, 41, + 48, 54, 60, + 81, 103, 123, + 138, 155, 171, + 187, 204, 219, + 234, 251, 267, + 282, 298, 329, + 360) + +class Color: + '''Hold a color value. + + Example usage: + + To create an instance of the grapefruit.Color from RGB values: + + >>> import grapefruit + >>> r, g, b = 1, 0.5, 0 + >>> col = grapefruit.Color.NewFromRgb(r, g, b) + + To get the values of the color in another colorspace: + + >>> h, s, v = col.hsv + >>> l, a, b = col.lab + + To get the complementary of a color: + + >>> compl = col.ComplementaryColor() + >>> print compl.hsl + (210.0, 1.0, 0.5) + + To directly convert RGB values to their HSL equivalent: + + >>> h, s, l = Color.RgbToHsl(r, g, b) + + ''' + + WHITE_REFERENCE = { + 'std_A' : (1.09847, 1.00000, 0.35582), + 'std_B' : (0.99093, 1.00000, 0.85313), + 'std_C' : (0.98071, 1.00000, 1.18225), + 'std_D50' : (0.96421, 1.00000, 0.82519), + 'std_D55' : (0.95680, 1.00000, 0.92148), + 'std_D65' : (0.95043, 1.00000, 1.08890), + 'std_D75' : (0.94972, 1.00000, 1.22639), + 'std_E' : (1.00000, 1.00000, 1.00000), + 'std_F1' : (0.92834, 1.00000, 1.03665), + 'std_F2' : (0.99145, 1.00000, 0.67316), + 'std_F3' : (1.03753, 1.00000, 0.49861), + 'std_F4' : (1.09147, 1.00000, 0.38813), + 'std_F5' : (0.90872, 1.00000, 0.98723), + 'std_F6' : (0.97309, 1.00000, 0.60191), + 'std_F7' : (0.95017, 1.00000, 1.08630), + 'std_F8' : (0.96413, 1.00000, 0.82333), + 'std_F9' : (1.00365, 1.00000, 0.67868), + 'std_F10' : (0.96174, 1.00000, 0.81712), + 'std_F11' : (1.00899, 1.00000, 0.64262), + 'std_F12' : (1.08046, 1.00000, 0.39228), + 'sup_A' : (1.11142, 1.00000, 0.35200), + 'sup_B' : (0.99178, 1.00000, 0.84349), + 'sup_C' : (0.97286, 1.00000, 1.16145), + 'sup_D50' : (0.96721, 1.00000, 0.81428), + 'sup_D55' : (0.95797, 1.00000, 0.90925), + 'sup_D65' : (0.94810, 1.00000, 1.07305), + 'sup_D75' : (0.94417, 1.00000, 1.20643), + 'sup_E' : (1.00000, 1.00000, 1.00000), + 'sup_F1' : (0.94791, 1.00000, 1.03191), + 'sup_F2' : (1.03245, 1.00000, 0.68990), + 'sup_F3' : (1.08968, 1.00000, 0.51965), + 'sup_F4' : (1.14961, 1.00000, 0.40963), + 'sup_F5' : (0.93369, 1.00000, 0.98636), + 'sup_F6' : (1.02148, 1.00000, 0.62074), + 'sup_F7' : (0.95780, 1.00000, 1.07618), + 'sup_F8' : (0.97115, 1.00000, 0.81135), + 'sup_F9' : (1.02116, 1.00000, 0.67826), + 'sup_F10' : (0.99001, 1.00000, 0.83134), + 'sup_F11' : (1.03820, 1.00000, 0.65555), + 'sup_F12' : (1.11428, 1.00000, 0.40353)} + + NAMED_COLOR = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgrey': '#a9a9a9', + 'darkgreen': '#006400', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'grey': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred': '#cd5c5c', + 'indigo': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgreen': '#90ee90', + 'lightgray': '#d3d3d3', + 'lightgrey': '#d3d3d3', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370db', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#db7093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32'} + + def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): + '''Instantiate a new grapefruit.Color object. + + Parameters: + :values: + The values of this color, in the specified representation. + :mode: + The representation mode used for values. + :alpha: + the alpha value (transparency) of this color. + :wref: + The whitepoint reference, default is 2° D65. + + ''' + if not(isinstance(values, tuple)): + raise TypeError, 'values must be a tuple' + + if mode=='rgb': + self.__rgb = values + self.__hsl = Color.RgbToHsl(*values) + elif mode=='hsl': + self.__hsl = values + self.__rgb = Color.HslToRgb(*values) + else: + raise ValueError('Invalid color mode: ' + mode) + + self.__a = alpha + self.__wref = wref + + def __ne__(self, other): + return not self.__eq__(other) + + def __eq__(self, other): + try: + if isinstance(other, Color): + return (self.__rgb==other.__rgb) and (self.__a==other.__a) + + if len(other) != 4: + return False + rgba = self.__rgb + (self.__a,) + return reduce(lambda x, y: x and (y[0]==y[1]), zip(rgba, other), True) + except TypeError: + return False + except AttributeError: + return False + + def __repr__(self): + return str(self.__rgb + (self.__a,)) + + def __str__(self): + '''A string representation of this grapefruit.Color instance. + + Returns: + The RGBA representation of this grapefruit.Color instance. + + ''' + return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) + + def __unicode__(self): + '''A unicode string representation of this grapefruit.Color instance. + + Returns: + The RGBA representation of this grapefruit.Color instance. + + ''' + return u'(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) + + def __iter__(self): + return iter(self.__rgb + (self.__a,)) + + def __len__(self): + return 4 + + @staticmethod + def RgbToHsl(r, g, b): + '''Convert the color from RGB coordinates to HSL. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (h, s, l) tuple in the range: + h[0...360], + s[0...1], + l[0...1] + + >>> Color.RgbToHsl(1, 0.5, 0) + (30.0, 1.0, 0.5) + + ''' + minVal = min(r, g, b) # min RGB value + maxVal = max(r, g, b) # max RGB value + + l = (maxVal + minVal) / 2.0 + if minVal==maxVal: + return (0.0, 0.0, l) # achromatic (gray) + + d = maxVal - minVal # delta RGB value + + if l < 0.5: s = d / (maxVal + minVal) + else: s = d / (2.0 - maxVal - minVal) + + dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] + + if r==maxVal: + h = db - dg + elif g==maxVal: + h = 2.0 + dr - db + else: + h = 4.0 + dg - dr + + h = (h*60.0) % 360.0 + return (h, s, l) + + @staticmethod + def _HueToRgb(n1, n2, h): + h %= 6.0 + if h < 1.0: return n1 + ((n2-n1) * h) + if h < 3.0: return n2 + if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) + return n1 + + @staticmethod + def HslToRgb(h, s, l): + '''Convert the color from HSL coordinates to RGB. + + Parameters: + :h: + The Hue component value [0...1] + :s: + The Saturation component value [0...1] + :l: + The Lightness component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> Color.HslToRgb(30.0, 1.0, 0.5) + (1.0, 0.5, 0.0) + + ''' + if s==0: return (l, l, l) # achromatic (gray) + + if l<0.5: n2 = l * (1.0 + s) + else: n2 = l+s - (l*s) + + n1 = (2.0 * l) - n2 + + h /= 60.0 + hueToRgb = Color._HueToRgb + r = hueToRgb(n1, n2, h + 2) + g = hueToRgb(n1, n2, h) + b = hueToRgb(n1, n2, h - 2) + + return (r, g, b) + + @staticmethod + def RgbToHsv(r, g, b): + '''Convert the color from RGB coordinates to HSV. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (h, s, v) tuple in the range: + h[0...360], + s[0...1], + v[0...1] + + >>> Color.RgbToHsv(1, 0.5, 0) + (30.0, 1, 1) + + ''' + v = max(r, g, b) + d = v - min(r, g, b) + if d==0: return (0.0, 0.0, v) + s = d / v + + dr, dg, db = [(v - val) / d for val in (r, g, b)] + + if r==v: + h = db - dg # between yellow & magenta + elif g==v: + h = 2.0 + dr - db # between cyan & yellow + else: # b==v + h = 4.0 + dg - dr # between magenta & cyan + + h = (h*60.0) % 360.0 + return (h, s, v) + + @staticmethod + def HsvToRgb(h, s, v): + '''Convert the color from RGB coordinates to HSV. + + Parameters: + :h: + The Hus component value [0...1] + :s: + The Saturation component value [0...1] + :v: + The Value component [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> Color.HslToRgb(30.0, 1.0, 0.5) + (1.0, 0.5, 0.0) + + ''' + if s==0: return (v, v, v) # achromatic (gray) + + h /= 60.0 + h = h % 6.0 + + i = int(h) + f = h - i + if not(i&1): f = 1-f # if i is even + + m = v * (1.0 - s) + n = v * (1.0 - (s * f)) + + if i==0: return (v, n, m) + if i==1: return (n, v, m) + if i==2: return (m, v, n) + if i==3: return (m, n, v) + if i==4: return (n, m, v) + return (v, m, n) + + @staticmethod + def RgbToYiq(r, g, b): + '''Convert the color from RGB to YIQ. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (y, i, q) tuple in the range: + y[0...1], + i[0...1], + q[0...1] + + >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) + '(0.592263, 0.458874, -0.0499818)' + + ''' + y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) + i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) + q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) + return (y, i, q) + + @staticmethod + def YiqToRgb(y, i, q): + '''Convert the color from YIQ coordinates to RGB. + + Parameters: + :y: + Tte Y component value [0...1] + :i: + The I component value [0...1] + :q: + The Q component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) + '(1, 0.5, 5.442e-007)' + + ''' + r = y + (i * 0.9562) + (q * 0.6210) + g = y - (i * 0.2717) - (q * 0.6485) + b = y - (i * 1.1053) + (q * 1.7020) + return (r, g, b) + + @staticmethod + def RgbToYuv(r, g, b): + '''Convert the color from RGB coordinates to YUV. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (y, u, v) tuple in the range: + y[0...1], + u[-0.436...0.436], + v[-0.615...0.615] + + >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) + '(0.5925, -0.29156, 0.357505)' + + ''' + y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) + u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) + v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) + return (y, u, v) + + @staticmethod + def YuvToRgb(y, u, v): + '''Convert the color from YUV coordinates to RGB. + + Parameters: + :y: + The Y component value [0...1] + :u: + The U component value [-0.436...0.436] + :v: + The V component value [-0.615...0.615] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) + '(0.999989, 0.500015, -6.3276e-005)' + + ''' + r = y + (v * 1.13983) + g = y - (u * 0.39465) - (v * 0.58060) + b = y + (u * 2.03211) + return (r, g, b) + + @staticmethod + def RgbToXyz(r, g, b): + '''Convert the color from sRGB to CIE XYZ. + + The methods assumes that the RGB coordinates are given in the sRGB + colorspace (D65). + + .. note:: + + Compensation for the sRGB gamma correction is applied before converting. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (x, y, z) tuple in the range: + x[0...1], + y[0...1], + z[0...1] + + >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) + '(0.488941, 0.365682, 0.0448137)' + + ''' + r, g, b = [((v <= 0.03928) and [v / 12.92] or [((v+0.055) / 1.055) **2.4])[0] for v in (r, g, b)] + + x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) + y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) + z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) + return (x, y, z) + + @staticmethod + def XyzToRgb(x, y, z): + '''Convert the color from CIE XYZ coordinates to sRGB. + + .. note:: + + Compensation for sRGB gamma correction is applied before converting. + + Parameters: + :x: + The X component value [0...1] + :y: + The Y component value [0...1] + :z: + The Z component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) + '(1, 0.5, 6.81883e-008)' + + ''' + r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) + g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) + b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) + return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) + + @staticmethod + def XyzToLab(x, y, z, wref=_DEFAULT_WREF): + '''Convert the color from CIE XYZ to CIE L*a*b*. + + Parameters: + :x: + The X component value [0...1] + :y: + The Y component value [0...1] + :z: + The Z component value [0...1] + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + The color as an (L, a, b) tuple in the range: + L[0...100], + a[-1...1], + b[-1...1] + + >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) + '(66.9518, 0.43084, 0.739692)' + + >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) + '(66.9518, 0.411663, 0.67282)' + + ''' + # White point correction + x /= wref[0] + y /= wref[1] + z /= wref[2] + + # Nonlinear distortion and linear transformation + x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] + + # Vector scaling + l = (116 * y) - 16 + a = 5.0 * (x - y) + b = 2.0 * (y - z) + + return (l, a, b) + + @staticmethod + def LabToXyz(l, a, b, wref=_DEFAULT_WREF): + '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. + + Parameters: + :l: + The L component [0...100] + :a: + The a component [-1...1] + :b: + The a component [-1...1] + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + The color as an (x, y, z) tuple in the range: + x[0...q], + y[0...1], + z[0...1] + + >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) + '(0.488941, 0.365682, 0.0448137)' + + >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) + '(0.488941, 0.365682, 0.0448138)' + + ''' + y = (l + 16) / 116 + x = (a / 5.0) + y + z = y - (b / 2.0) + return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) + + @staticmethod + def CmykToCmy(c, m, y, k): + '''Convert the color from CMYK coordinates to CMY. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :k: + The Black component value [0...1] + + Returns: + The color as an (c, m, y) tuple in the range: + c[0...1], + m[0...1], + y[0...1] + + >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) + '(1, 0.66, 0.5)' + + ''' + mk = 1-k + return ((c*mk + k), (m*mk + k), (y*mk + k)) + + @staticmethod + def CmyToCmyk(c, m, y): + '''Convert the color from CMY coordinates to CMYK. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + + Returns: + The color as an (c, m, y, k) tuple in the range: + c[0...1], + m[0...1], + y[0...1], + k[0...1] + + >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) + '(1, 0.32, 0, 0.5)' + + ''' + k = min(c, m, y) + if k==1.0: return (0.0, 0.0, 0.0, 1.0) + mk = 1-k + return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) + + @staticmethod + def RgbToCmy(r, g, b): + '''Convert the color from RGB coordinates to CMY. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (c, m, y) tuple in the range: + c[0...1], + m[0...1], + y[0...1] + + >>> Color.RgbToCmy(1, 0.5, 0) + (0, 0.5, 1) + + ''' + return (1-r, 1-g, 1-b) + + @staticmethod + def CmyToRgb(c, m, y): + '''Convert the color from CMY coordinates to RGB. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + >>> Color.CmyToRgb(0, 0.5, 1) + (1, 0.5, 0) + + ''' + return (1-c, 1-m, 1-y) + + @staticmethod + def RgbToHtml(r, g, b): + '''Convert the color from (r, g, b) to #RRGGBB. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + A CSS string representation of this color (#RRGGBB). + + >>> Color.RgbToHtml(1, 0.5, 0) + '#ff8000' + + ''' + return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) + + @staticmethod + def HtmlToRgb(html): + '''Convert the HTML color to (r, g, b). + + Parameters: + :html: + the HTML definition of the color (#RRGGBB or #RGB or a color name). + + Returns: + The color as an (r, g, b) tuple in the range: + r[0...1], + g[0...1], + b[0...1] + + Throws: + :ValueError: + If html is neither a known color name or a hexadecimal RGB + representation. + + >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') + '(1, 0.501961, 0)' + >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') + '(1, 0.501961, 0)' + >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') + '(1, 0.4, 0)' + >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') + '(1, 0.4, 0)' + >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') + '(1, 0.980392, 0.803922)' + + ''' + html = html.strip().lower() + if html[0]=='#': + html = html[1:] + elif Color.NAMED_COLOR.has_key(html): + html = Color.NAMED_COLOR[html][1:] + + if len(html)==6: + rgb = html[:2], html[2:4], html[4:] + elif len(html)==3: + rgb = ['%c%c' % (v,v) for v in html] + else: + raise ValueError, 'input #%s is not in #RRGGBB format' % html + + return tuple(((int(n, 16) / 255.0) for n in rgb)) + + @staticmethod + def RgbToPil(r, g, b): + '''Convert the color from RGB to a PIL-compatible integer. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + A PIL compatible integer (0xBBGGRR). + + >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) + '0x0080ff' + + ''' + r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] + return (b << 16) + (g << 8) + r + + @staticmethod + def PilToRgb(pil): + '''Convert the color from a PIL-compatible integer to RGB. + + Parameters: + pil: a PIL compatible color representation (0xBBGGRR) + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r: [0...1] + g: [0...1] + b: [0...1] + + >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) + '(1, 0.501961, 0)' + + ''' + r = 0xff & pil + g = 0xff & (pil >> 8) + b = 0xff & (pil >> 16) + return tuple((v / 255.0 for v in (r, g, b))) + + @staticmethod + def _WebSafeComponent(c, alt=False): + '''Convert a color component to its web safe equivalent. + + Parameters: + :c: + The component value [0...1] + :alt: + If True, return the alternative value instead of the nearest one. + + Returns: + The web safe equivalent of the component value. + + ''' + # This sucks, but floating point between 0 and 1 is quite fuzzy... + # So we just change the scale a while to make the equality tests + # work, otherwise it gets wrong at some decimal far to the right. + sc = c * 100.0 + + # If the color is already safe, return it straight away + d = sc % 20 + if d==0: return c + + # Get the lower and upper safe values + l = sc - d + u = l + 20 + + # Return the 'closest' value according to the alt flag + if alt: + if (sc-l) >= (u-sc): return l/100.0 + else: return u/100.0 + else: + if (sc-l) >= (u-sc): return u/100.0 + else: return l/100.0 + + @staticmethod + def RgbToWebSafe(r, g, b, alt=False): + '''Convert the color from RGB to 'web safe' RGB + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + :alt: + If True, use the alternative color instead of the nearest one. + Can be used for dithering. + + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) + '(1, 0.6, 0)' + + ''' + webSafeComponent = Color._WebSafeComponent + return tuple((webSafeComponent(v, alt) for v in (r, g, b))) + + @staticmethod + def RgbToGreyscale(r, g, b): + '''Convert the color from RGB to its greyscale equivalent + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + + Returns: + The color as an (r, g, b) tuple in the range: + the range: + r[0...1], + g[0...1], + b[0...1] + + >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) + '(0.6, 0.6, 0.6)' + + ''' + v = (r + g + b) / 3.0 + return (v, v, v) + + @staticmethod + def RgbToRyb(hue): + '''Maps a hue on the RGB color wheel to Itten's RYB wheel. + + Parameters: + :hue: + The hue on the RGB color wheel [0...360] + + Returns: + An approximation of the corresponding hue on Itten's RYB wheel. + + >>> Color.RgbToRyb(15) + 26 + + ''' + d = hue % 15 + i = int(hue / 15) + x0 = _RybWheel[i] + x1 = _RybWheel[i+1] + return x0 + (x1-x0) * d / 15 + + @staticmethod + def RybToRgb(hue): + '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. + + Parameters: + :hue: + The hue on Itten's RYB color wheel [0...360] + + Returns: + An approximation of the corresponding hue on the standard RGB wheel. + + >>> Color.RybToRgb(15) + 8 + + ''' + d = hue % 15 + i = int(hue / 15) + x0 = _RgbWheel[i] + x1 = _RgbWheel[i+1] + return x0 + (x1-x0) * d / 15 + + @staticmethod + def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed RGB values. + + Parameters: + :r: + The Red component value [0...1] + :g: + The Green component value [0...1] + :b: + The Blue component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromRgb(1.0, 0.5, 0.0) + (1.0, 0.5, 0.0, 1.0) + >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) + (1.0, 0.5, 0.0, 0.5) + + ''' + return Color((r, g, b), 'rgb', alpha, wref) + + @staticmethod + def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed HSL values. + + Parameters: + :h: + The Hue component value [0...1] + :s: + The Saturation component value [0...1] + :l: + The Lightness component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5) + (1.0, 0.5, 0.0, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5, 0.5) + (1.0, 0.5, 0.0, 0.5) + + ''' + return Color((h, s, l), 'hsl', alpha, wref) + + @staticmethod + def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed HSV values. + + Parameters: + :h: + The Hus component value [0...1] + :s: + The Saturation component value [0...1] + :v: + The Value component [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsv(30, 1, 1) + (1.0, 0.5, 0.0, 1.0) + >>> Color.NewFromHsv(30, 1, 1, 0.5) + (1.0, 0.5, 0.0, 0.5) + + ''' + h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) + return Color((h, s, l), 'hsl', alpha, wref) + + @staticmethod + def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed YIQ values. + + Parameters: + :y: + The Y component value [0...1] + :i: + The I component value [0...1] + :q: + The Q component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) + '(0.999902, 0.499955, -6.6905e-005, 1)' + >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) + '(0.999902, 0.499955, -6.6905e-005, 0.5)' + + ''' + return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) + + @staticmethod + def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed YUV values. + + Parameters: + :y: + The Y component value [0...1] + :u: + The U component value [-0.436...0.436] + :v: + The V component value [-0.615...0.615] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) + '(0.999989, 0.500015, -6.3276e-005, 1)' + >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) + '(0.999989, 0.500015, -6.3276e-005, 0.5)' + + ''' + return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) + + @staticmethod + def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed CIE-XYZ values. + + Parameters: + :x: + The Red component value [0...1] + :y: + The Green component value [0...1] + :z: + The Blue component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) + '(1, 0.5, 6.81883e-008, 1)' + >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) + '(1, 0.5, 6.81883e-008, 0.5)' + + ''' + return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) + + @staticmethod + def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed CIE-LAB values. + + Parameters: + :l: + The L component [0...100] + :a: + The a component [-1...1] + :b: + The a component [-1...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) + '(1, 0.5, 1.09491e-008, 1)' + >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) + '(1.01238, 0.492011, -0.14311, 1)' + >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) + '(1, 0.5, 1.09491e-008, 0.5)' + >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) + '(1.01238, 0.492011, -0.14311, 0.5)' + + ''' + return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) + + @staticmethod + def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed CMY values. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromCmy(0, 0.5, 1) + (1, 0.5, 0, 1.0) + >>> Color.NewFromCmy(0, 0.5, 1, 0.5) + (1, 0.5, 0, 0.5) + + ''' + return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) + + @staticmethod + def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed CMYK values. + + Parameters: + :c: + The Cyan component value [0...1] + :m: + The Magenta component value [0...1] + :y: + The Yellow component value [0...1] + :k: + The Black component value [0...1] + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) + '(0, 0.34, 0.5, 1)' + >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) + '(0, 0.34, 0.5, 0.5)' + + ''' + return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) + + @staticmethod + def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed HTML color definition. + + Parameters: + :html: + The HTML definition of the color (#RRGGBB or #RGB or a color name). + :alpha: + The color transparency [0...1], default is opaque. + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromHtml('#ff8000')) + '(1, 0.501961, 0, 1)' + >>> str(Color.NewFromHtml('ff8000')) + '(1, 0.501961, 0, 1)' + >>> str(Color.NewFromHtml('#f60')) + '(1, 0.4, 0, 1)' + >>> str(Color.NewFromHtml('f60')) + '(1, 0.4, 0, 1)' + >>> str(Color.NewFromHtml('lemonchiffon')) + '(1, 0.980392, 0.803922, 1)' + >>> str(Color.NewFromHtml('#ff8000', 0.5)) + '(1, 0.501961, 0, 0.5)' + + ''' + return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) + + @staticmethod + def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): + '''Create a new instance based on the specifed PIL color. + + Parameters: + :pil: + A PIL compatible color representation (0xBBGGRR) + :alpha: + The color transparency [0...1], default is opaque + :wref: + The whitepoint reference, default is 2° D65. + + Returns: + A grapefruit.Color instance. + + >>> str(Color.NewFromPil(0x0080ff)) + '(1, 0.501961, 0, 1)' + >>> str(Color.NewFromPil(0x0080ff, 0.5)) + '(1, 0.501961, 0, 0.5)' + + ''' + return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) + + def __GetAlpha(self): + return self.__a + alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') + + def __GetWRef(self): + return self.__wref + whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') + + def __GetRGB(self): + return self.__rgb + rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') + + def __GetHue(self): + return self.__hsl[0] + hue = property(fget=__GetHue, doc='The hue of this color.') + + def __GetHSL(self): + return self.__hsl + hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') + + def __GetHSV(self): + h, s, v = Color.RgbToHsv(*self.__rgb) + return (self.__hsl[0], s, v) + hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') + + def __GetYIQ(self): + return Color.RgbToYiq(*self.__rgb) + yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') + + def __GetYUV(self): + return Color.RgbToYuv(*self.__rgb) + yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') + + def __GetXYZ(self): + return Color.RgbToXyz(*self.__rgb) + xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') + + def __GetLAB(self): + return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) + lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') + + def __GetCMY(self): + return Color.RgbToCmy(*self.__rgb) + cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') + + def __GetCMYK(self): + return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) + cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') + + def __GetHTML(self): + return Color.RgbToHtml(*self.__rgb) + html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') + + def __GetPIL(self): + return Color.RgbToPil(*self.__rgb) + pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') + + def __GetwebSafe(self): + return Color.RgbToWebSafe(*self.__rgb) + webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') + + def __GetGreyscale(self): + return Color.RgbToGreyscale(*self.rgb) + greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') + + def ColorWithAlpha(self, alpha): + '''Create a new instance based on this one with a new alpha value. + + Parameters: + :alpha: + The transparency of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) + (1.0, 0.5, 0.0, 0.5) + + ''' + return Color(self.__rgb, 'rgb', alpha, self.__wref) + + def ColorWithWhiteRef(self, wref, labAsRef=False): + '''Create a new instance based on this one with a new white reference. + + Parameters: + :wref: + The whitepoint reference. + :labAsRef: + If True, the L*a*b* values of the current instance are used as reference + for the new color; otherwise, the RGB values are used as reference. + + Returns: + A grapefruit.Color instance. + + + >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) + + >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) + >>> c2.rgb + (1.0, 0.5, 0.0) + >>> '(%g, %g, %g)' % c2.whiteRef + '(0.96721, 1, 0.81428)' + + >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) + >>> '(%g, %g, %g)' % c2.rgb + '(1.01463, 0.490339, -0.148131)' + >>> '(%g, %g, %g)' % c2.whiteRef + '(0.96721, 1, 0.81428)' + >>> '(%g, %g, %g)' % c.lab + '(66.9518, 0.43084, 0.739692)' + >>> '(%g, %g, %g)' % c2.lab + '(66.9518, 0.43084, 0.739693)' + + ''' + if labAsRef: + l, a, b = self.__GetLAB() + return Color.NewFromLab(l, a, b, self.__a, wref) + else: + return Color(self.__rgb, 'rgb', self.__a, wref) + + def ColorWithHue(self, hue): + '''Create a new instance based on this one with a new hue. + + Parameters: + :hue: + The hue of the new color [0...360]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60) + (1.0, 1.0, 0.0, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl + (60, 1, 0.5) + + ''' + h, s, l = self.__hsl + return Color((hue, s, l), 'hsl', self.__a, self.__wref) + + def ColorWithSaturation(self, saturation): + '''Create a new instance based on this one with a new saturation value. + + .. note:: + + The saturation is defined for the HSL mode. + + Parameters: + :saturation: + The saturation of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) + (0.75, 0.5, 0.25, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl + (30, 0.5, 0.5) + + ''' + h, s, l = self.__hsl + return Color((h, saturation, l), 'hsl', self.__a, self.__wref) + + def ColorWithLightness(self, lightness): + '''Create a new instance based on this one with a new lightness value. + + Parameters: + :lightness: + The lightness of the new color [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) + (0.5, 0.25, 0.0, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl + (30, 1, 0.25) + + ''' + h, s, l = self.__hsl + return Color((h, s, lightness), 'hsl', self.__a, self.__wref) + + def DarkerColor(self, level): + '''Create a new instance based on this one but darker. + + Parameters: + :level: + The amount by which the color should be darkened to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) + (0.5, 0.25, 0.0, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl + (30, 1, 0.25) + + ''' + h, s, l = self.__hsl + return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) + + def LighterColor(self, level): + '''Create a new instance based on this one but lighter. + + Parameters: + :level: + The amount by which the color should be lightened to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25) + (1.0, 0.75, 0.5, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl + (30, 1, 0.75) + + ''' + h, s, l = self.__hsl + return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) + + def Saturate(self, level): + '''Create a new instance based on this one but more saturated. + + Parameters: + :level: + The amount by which the color should be saturated to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) + (0.875, 0.5, 0.125, 1.0) + >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl + (30, 0.75, 0.5) + + ''' + h, s, l = self.__hsl + return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) + + def Desaturate(self, level): + '''Create a new instance based on this one but less saturated. + + Parameters: + :level: + The amount by which the color should be desaturated to produce + the new one [0...1]. + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) + (0.625, 0.5, 0.375, 1.0) + >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl + (30, 0.25, 0.5) + + ''' + h, s, l = self.__hsl + return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) + + def WebSafeDither(self): + '''Return the two websafe colors nearest to this one. + + Returns: + A tuple of two grapefruit.Color instances which are the two + web safe colors closest this one. + + >>> c = Color.NewFromRgb(1.0, 0.45, 0.0) + >>> c1, c2 = c.WebSafeDither() + >>> str(c1) + '(1, 0.4, 0, 1)' + >>> str(c2) + '(1, 0.6, 0, 1)' + + ''' + return ( + Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), + Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) + + def Gradient(self, target, steps=100): + '''Create a list with the gradient colors between this and the other color. + + Parameters: + :target: + The grapefruit.Color at the other end of the gradient. + :steps: + The number of gradients steps to create. + + + Returns: + A list of grapefruit.Color instances. + + >>> c1 = Color.NewFromRgb(1.0, 0.0, 0.0, alpha=1) + >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) + >>> c1.Gradient(c2, 3) + [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (0.25, 0.75, 0.0, 0.25)] + + ''' + gradient = [] + rgba1 = self.__rgb + (self.__a,) + rgba2 = target.__rgb + (target.__a,) + + steps += 1 + for n in xrange(1, steps): + d = 1.0*n/steps + r = (rgba1[0]*(1-d)) + (rgba2[0]*d) + g = (rgba1[1]*(1-d)) + (rgba2[1]*d) + b = (rgba1[2]*(1-d)) + (rgba2[2]*d) + a = (rgba1[3]*(1-d)) + (rgba2[3]*d) + + gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) + + return gradient + + def ComplementaryColor(self, mode='ryb'): + '''Create a new instance which is the complementary color of this one. + + Parameters: + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + + Returns: + A grapefruit.Color instance. + + >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor() + (0.0, 0.5, 1.0, 1.0) + >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor().hsl + (210, 1, 0.5) + + ''' + h, s, l = self.__hsl + + if mode == 'ryb': h = Color.RgbToRyb(h) + h = (h+180)%360 + if mode == 'ryb': h = Color.RybToRgb(h) + + return Color((h, s, l), 'hsl', self.__a, self.__wref) + + def MonochromeScheme(self): + '''Return 4 colors in the same hue with varying saturation/lightness. + + Returns: + A tuple of 4 grapefruit.Color in the same hue as this one, + with varying saturation/lightness. + + >>> c = Color.NewFromHsl(30, 0.5, 0.5) + >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] + ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] + + ''' + def _wrap(x, min, thres, plus): + if (x-min) < thres: return x + plus + else: return x-min + + h, s, l = self.__hsl + + s1 = _wrap(s, 0.3, 0.1, 0.3) + l1 = _wrap(l, 0.5, 0.2, 0.3) + + s2 = s + l2 = _wrap(l, 0.2, 0.2, 0.6) + + s3 = s1 + l3 = max(0.2, l + (1-l)*0.2) + + s4 = s + l4 = _wrap(l, 0.5, 0.2, 0.3) + + return ( + Color((h, s1, l1), 'hsl', self.__a, self.__wref), + Color((h, s2, l2), 'hsl', self.__a, self.__wref), + Color((h, s3, l3), 'hsl', self.__a, self.__wref), + Color((h, s4, l4), 'hsl', self.__a, self.__wref)) + + def TriadicScheme(self, angle=120, mode='ryb'): + '''Return two colors forming a triad or a split complementary with this one. + + Parameters: + :angle: + The angle between the hues of the created colors. + The default value makes a triad. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of two grapefruit.Color forming a color triad with + this one or a split complementary. + + >>> c1 = Color.NewFromHsl(30, 1, 0.5) + + >>> c2, c3 = c1.TriadicScheme() + >>> c2.hsl + (150.0, 1, 0.5) + >>> c3.hsl + (270.0, 1, 0.5) + + >>> c2, c3 = c1.TriadicScheme(40) + >>> c2.hsl + (190.0, 1, 0.5) + >>> c3.hsl + (230.0, 1, 0.5) + + ''' + h, s, l = self.__hsl + angle = min(angle, 120) / 2.0 + + if mode == 'ryb': h = Color.RgbToRyb(h) + h += 180 + h1 = (h - angle) % 360 + h2 = (h + angle) % 360 + if mode == 'ryb': + h1 = Color.RybToRgb(h1) + h2 = Color.RybToRgb(h2) + + return ( + Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref)) + + def TetradicScheme(self, angle=30, mode='ryb'): + '''Return three colors froming a tetrad with this one. + + Parameters: + :angle: + The angle to substract from the adjacent colors hues [-90...90]. + You can use an angle of zero to generate a square tetrad. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of three grapefruit.Color forming a color tetrad with + this one. + + >>> col = Color.NewFromHsl(30, 1, 0.5) + >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] + [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] + + ''' + h, s, l = self.__hsl + + if mode == 'ryb': h = Color.RgbToRyb(h) + h1 = (h + 90 - angle) % 360 + h2 = (h + 180) % 360 + h3 = (h + 270 - angle) % 360 + if mode == 'ryb': + h1 = Color.RybToRgb(h1) + h2 = Color.RybToRgb(h2) + h3 = Color.RybToRgb(h3) + + return ( + Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref), + Color((h3, s, l), 'hsl', self.__a, self.__wref)) + + def AnalogousScheme(self, angle=30, mode='ryb'): + '''Return two colors analogous to this one. + + Args: + :angle: + The angle between the hues of the created colors and this one. + :mode: + Select which color wheel to use for the generation (ryb/rgb). + + Returns: + A tuple of grapefruit.Colors analogous to this one. + + >>> c1 = Color.NewFromHsl(30, 1, 0.5) + + >>> c2, c3 = c1.AnalogousScheme() + >>> c2.hsl + (330, 1, 0.5) + >>> c3.hsl + (90, 1, 0.5) + + >>> c2, c3 = c1.AnalogousScheme(10) + >>> c2.hsl + (20, 1, 0.5) + >>> c3.hsl + (40, 1, 0.5) + + ''' + h, s, l = self.__hsl + + if mode == 'ryb': h = Color.RgbToRyb(h) + h += 360 + h1 = (h - angle) % 360 + h2 = (h + angle) % 360 + if mode == 'ryb': + h1 = Color.RybToRgb(h1) + h2 = Color.RybToRgb(h2) + + return (Color((h1, s, l), 'hsl', self.__a, self.__wref), + Color((h2, s, l), 'hsl', self.__a, self.__wref)) + + def AlphaBlend(self, other): + '''Alpha-blend this color on the other one. + + Args: + :other: + The grapefruit.Color to alpha-blend with this one. + + Returns: + A grapefruit.Color instance which is the result of alpha-blending + this color on the other one. + + >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) + >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) + >>> c3 = c1.AlphaBlend(c2) + >>> str(c3) + '(1, 0.875, 0.75, 0.84)' + + ''' + # get final alpha channel + fa = self.__a + other.__a - (self.__a * other.__a) + + # get percentage of source alpha compared to final alpha + if fa==0: sa = 0 + else: sa = min(1.0, self.__a/other.__a) + + # destination percentage is just the additive inverse + da = 1.0 - sa + + sr, sg, sb = [v * sa for v in self.__rgb] + dr, dg, db = [v * da for v in other.__rgb] + + return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) + + def Blend(self, other, percent=0.5): + '''Blend this color with the other one. + + Args: + :other: + the grapefruit.Color to blend with this one. + + Returns: + A grapefruit.Color instance which is the result of blending + this color on the other one. + + >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) + >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) + >>> c3 = c1.Blend(c2) + >>> str(c3) + '(1, 0.75, 0.5, 0.4)' + + ''' + dest = 1.0 - percent + rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) + a = (self.__a * percent) + (other.__a * dest) + return Color(rgb, 'rgb', a, self.__wref) + +def _test(): + import doctest + reload(doctest) + doctest.testmod() + +if __name__=='__main__': + _test() diff --git a/forum/management/__init__.py b/forum/management/__init__.py index 60f8f570..e69de29b 100644 --- a/forum/management/__init__.py +++ b/forum/management/__init__.py @@ -1,3 +0,0 @@ -#todo: modules -#from forum.modules import get_modules_script -#get_modules_script('management') diff --git a/forum/models/__init__.py b/forum/models/__init__.py index 5d25da09..1fb18ff9 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -693,11 +693,4 @@ __all__ = [ #'AuthKeyUserAssociation', 'User', - ] - - -#from forum.modules import get_modules_script_classes -#for k, v in get_modules_script_classes('models', models.Model).items(): -# if not k in __all__: -# __all__.append(k) -# exec "%s = v" % k +] diff --git a/forum/modules.py b/forum/modules.py deleted file mode 100644 index 6c9a9dba..00000000 --- a/forum/modules.py +++ /dev/null @@ -1,78 +0,0 @@ -import os -import types -import re - -from django.template import Template, TemplateDoesNotExist - -MODULES_PACKAGE = 'forum_modules' - -MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE) - -MODULE_LIST = [ - __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules']) - for f in os.listdir(MODULES_FOLDER) - if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and - os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and - not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f)) -] - -def get_modules_script(script_name): - all = [] - - for m in MODULE_LIST: - try: - all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__])) - except Exception, e: - #print script_name + ":" + str(e) - pass - - return all - -def get_modules_script_classes(script_name, base_class): - scripts = get_modules_script(script_name) - all_classes = {} - - for script in scripts: - all_classes.update(dict([ - (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)] - if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class) - ])) - - return all_classes - -def get_all_handlers(name): - handler_files = get_modules_script('handlers') - - return [ - h for h in [ - getattr(f, name) for f in handler_files - if hasattr(f, name) - ] - - if callable(h) - ] - -def get_handler(name, default): - all = get_all_handlers(name) - return len(all) and all[0] or default - -module_template_re = re.compile('^modules\/(\w+)\/(.*)$') - -def module_templates_loader(name, dirs=None): - result = module_template_re.search(name) - - if result is not None: - file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2)) - - if os.path.exists(file_name): - try: - f = open(file_name, 'r') - source = f.read() - f.close() - return (source, file_name) - except: - pass - - raise TemplateDoesNotExist, name - -module_templates_loader.is_usable = True diff --git a/forum/skins/default/media/js/com.cnprog.i18n.js b/forum/skins/default/media/js/com.cnprog.i18n.js index 03690dc5..54c3806f 100644 --- a/forum/skins/default/media/js/com.cnprog.i18n.js +++ b/forum/skins/default/media/js/com.cnprog.i18n.js @@ -1,225 +1,225 @@ -//var i18nLang; -var i18nZh = { - 'insufficient privilege':'用户权限不在操作范围', - 'cannot pick own answer as best':'不能设置自己的回答为最佳答案', - 'anonymous users cannot select favorite questions':'匿名用户不能收藏问题,请先', - 'please login':'注册或者登录', - 'anonymous users cannot vote':'匿名用户不能投票', - '>15 points requried to upvote':'需要+15积分才能投支持票。', - '>100 points required to downvote':'需要+100积分才能投反对票。', - 'please see': '查看', - 'cannot vote for own posts':'不能给自己的帖子投票', - 'daily vote cap exhausted':'对不起,您已用完今日所有的投票。', - 'cannot revoke old vote':'这个投票已经过时,不能撤销。', - 'please confirm offensive':"确定要归类该帖为广告、人身攻击、恶意言论吗?", - 'anonymous users cannot flag offensive posts':'匿名用户不能操作,请先', - 'cannot flag message as offensive twice':'不能重复操作。', - 'flag offensive cap exhausted':'对不起,您已用完今日所有的5次‘水帖’操作。', - 'need >15 points to report spam':"需要+15积分才能归类‘垃圾帖’。", - 'confirm delete':"确定要删除/撤销删除该帖吗?", - 'anonymous users cannot delete/undelete':"匿名用户不能删除或撤销删除帖子", - 'post recovered':"操作成功!该帖子已被恢复。", - 'post deleted':"操作成功!该帖子已删除。", - 'add comment':'添加评论', - 'community karma points':'社区积分', - 'to comment, need':'评论需要', - 'delete this comment':'删除此评论', - 'hide comments':"隐藏评论", - 'add a comment':"添加评论", - 'comments':"评论", - 'confirm delete comment':"真要删除此评论吗?", - 'characters':'字符', - 'can write':'还可写', - 'click to close':'点击消息框关闭', - 'loading...':'读取中...', - 'tags cannot be empty':'标签不能为空。', - 'tablimits info':"最多5个标签,每个标签长度小于20个字符。", - 'content cannot be empty':'内容不能为空。', - 'content minchars': '请输入至少 {0} 字符。', - 'please enter title':'请输入标题。', - 'title minchars':"请输入至少 {0} 字符。", - 'delete':'删除', - 'undelete': '取消', - 'bold':'粗体', - 'italic':'斜体', - 'link':'超链接', - 'quote':'引用', - 'preformatted text':'代码', - 'image':'图片', - 'numbered list':'数字编号列表', - 'bulleted list':'项目符号列表', - 'heading':'标题', - 'horizontal bar':'水平线', - 'undo':'撤销', - 'redo':'重做', - 'enter image url':'输入图片地址

示例:
http://www.example.com/image.jpg \"我的截图\"', - 'enter url':'输入Web地址

示例:
http://www.cnprog.com/ \"我的网站\"

"', - 'upload image':'或者上传本地图片:' -}; - -var i18nEn = { - 'need >15 points to report spam':'need >15 points to report spam ', - '>15 points requried to upvote':'>15 points required to upvote ', - 'tags cannot be empty':'please enter at least one tag', - 'anonymous users cannot vote':'sorry, anonymous users cannot vote ', - 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ', - 'to comment, need': '(to comment other people\'s posts, karma ', - 'please see':'please see ', - 'community karma points':' or more is necessary) - ', - 'upload image':'Upload image:', - 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"', - 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"', - 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap', - 'cannot pick own answer as best':'sorry, you cannot accept your own answer', - 'cannot revoke old vote':'sorry, older votes cannot be revoked', - 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?', - 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ', - 'confirm delete':'are you sure you want to delete this?', - 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts', - 'post recovered':'your post is now restored!', - 'post deleted':'your post has been deleted', - 'confirm delete comment':'do you really want to delete this comment?', - 'can write':'have ', - 'tablimits info':'up to 5 tags, no more than 20 characters each', - 'content minchars': 'please enter more than {0} characters', - 'title minchars':"please enter at least {0} characters", - 'characters':'characters left', - 'cannot vote for own posts':'sorry, you cannot vote for your own posts', - 'cannot flag message as offensive twice':'cannot flag message as offensive twice ', - '>100 points required to downvote':'>100 points required to downvote ' -}; - -var i18nTr = { - 'insufficient privilege':'buna yetkiniz yoktur', - 'cannot pick own answer as best':'en cevap olarak kendi cevabınızı seçemezsiniz', - 'anonymous users cannot select favorite questions':'üye girişi yapmadan favori seçemezsiniz', - 'please login':'lütfen üye girişi yapınız', - 'anonymous users cannot vote':'üye girişi yapmadan oy kullanamazsınız', - '>15 points requried to upvote': 'beğeninizi göstermek için en az 15 puan toplamalısınız', - '>100 points required to downvote':'beğenmediğinizi göstermek için en az 100 puan toplamalısınız', - 'please see': 'lütfen bakın', - 'cannot vote for own posts':'kendi yazılarınıza oy veremezsiniz', - 'daily vote cap exhausted':'bugünlük oy verme kotanız doldu', - 'cannot revoke old vote':'verilen bir oyu iptal edemezsiniz', - 'please confirm offensive':"şikayetinizi onaylayın", - 'anonymous users cannot flag offensive posts':'üye girişi yapmadan şikayet gönderemezsiniz', - 'cannot flag message as offensive twice':'şikayet mesajı olarak iki kez işaretlemelisiniz', - 'flag offensive cap exhausted':'şikayet kotası aşıldı', - 'need >15 points to report spam':"spam olarak bildirmek için an az 15 puanınız olmalı", - 'confirm delete':"Bunu silmek istediğinizden emin misiniz?", - 'anonymous users cannot delete/undelete':"üye girişi yapmadan yazı silemez yada geri alamazsınız", - 'post recovered':"yazı geri alındı", - 'post deleted':"yazı silindi", - 'add comment':'yorum ekle', - 'community karma points':'site itibar puanları', - 'to comment, need':'Yorum için itibar puanınız olmalı', - 'delete this comment':'bu yorumu sil', - 'hide comments':"yorumları gizle", - 'add a comment':"yorum ekle", - 'comments':"yorumlar", - 'confirm delete comment':"yorumu silmek istediğinizden emin misiniz?", - 'characters':'karakter eksik', - 'can write':'yazılabilir ', - 'click to close':'kapatmak için tıklayın', - 'loading...':'yükleniyor...', - 'tags cannot be empty':'etiketler boş olamaz', - 'tablimits info':"En fazla 5 etiket ve her biri en fazla 20 karakter", - 'content cannot be empty':'içerik boş olamaz', - 'content minchars': 'Lütfen en az (0) karakter girin', - 'please enter title':'lütfen bir başlık yazın', - 'title minchars':"Lütfen en az (0) karakter girin", - 'delete':'sil', - 'undelete': 'geri al', - 'bold': 'kalın', - 'italic':'italik', - 'link':'link', - 'quote':'alıntı', - 'preformatted text':'hazır metin', - 'image':'resimler', - 'numbered list':'numaralı liste', - 'bulleted list':'işaretli liste', - 'heading':'Başlık', - 'horizontal bar':'yatay bar', - 'undo':'geri', - 'redo':'yeniden', - 'enter image url':'örnek resmin URLsini girin:
http://www.example.com/image.jpg \"resim başlığı\"', - 'enter url':'web adresini girin:
http://www.cnprog.com/ \"başlık bağlantısı\"

"', - 'upload image':'resim yükle:', - 'questions/' : 'sorular/', - 'answers/' : 'cevaplar/', - 'comments/' : 'yorumlar/', - 'vote/' : 'oy/', - 'delete/' : 'sil/' -}; - -var i18nEs = { - 'insufficient privilege':'privilegio insuficiente', - 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor', - 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar', - 'please login':'por favor inicie sesión', - 'anonymous users cannot vote':'usuarios anónimos no pueden votar', - '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente', - '>100 points required to downvote':'>100 puntos requeridos para votar negativamente', - 'please see': 'por favor vea', - 'cannot vote for own posts':'no se puede votar por sus propias publicaciones', - 'daily vote cap exhausted':'cuota de votos diarios excedida', - 'cannot revoke old vote':'no puede revocar un voto viejo', - 'please confirm offensive':"por favor confirme ofensiva", - 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas', - 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces', - 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida', - 'need >15 points to report spam':"necesita >15 puntos para reportar spam", - 'confirm delete':"¿Está seguro que desea borrar esto?", - 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones", - 'post recovered':"publicación recuperada", - 'post deleted':"publicación borrada。", - 'add comment':'agregar comentario', - 'community karma points':'reputación comunitaria', - 'to comment, need':'para comentar, necesita reputación', - 'delete this comment':'borrar este comentario', - 'hide comments':"ocultar comentarios", - 'add a comment':"agregar comentarios", - 'comments':"comentarios", - 'confirm delete comment':"¿Realmente desea borrar este comentario?", - 'characters':'caracteres faltantes', - 'can write':'tiene ', - 'click to close':'haga click para cerrar', - 'loading...':'cargando...', - 'tags cannot be empty':'las etiquetas no pueden estar vacías', - 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una", - 'content cannot be empty':'el contenido no puede estar vacío', - 'content minchars': 'por favor introduzca mas de {0} caracteres', - 'please enter title':'por favor ingrese un título', - 'title minchars':"por favor introduzca al menos {0} caracteres", - 'delete':'borrar', - 'undelete': 'recuperar', - 'bold': 'negrita', - 'italic':'cursiva', - 'link':'enlace', - 'quote':'citar', - 'preformatted text':'texto preformateado', - 'image':'imagen', - 'numbered list':'lista numerada', - 'bulleted list':'lista no numerada', - 'heading':'标题', - 'horizontal bar':'barra horizontal', - 'undo':'deshacer', - 'redo':'rehacer', - 'enter image url':'introduzca la URL de la imagen, por ejemplo:
http://www.example.com/image.jpg \"titulo de imagen\"', - 'enter url':'introduzca direcciones web, ejemplo:
http://www.cnprog.com/ \"titulo del enlace\"

"', - 'upload image':'cargar imagen:', - 'questions/' : 'preguntas/', - 'answers/' : 'respuestas/', - 'comments/' : 'comentarios/', - 'vote/' : 'votar/', - 'delete/' : 'eliminar/' -}; - -var i18n = { - 'en':i18nEn, - 'zh-cn':i18nZh, - 'es':i18nEs, - 'tr':i18nTr -}; - -var i18n_dict = i18n[i18nLang]; +//var i18nLang; +var i18nZh = { + 'insufficient privilege':'用户权限不在操作范围', + 'cannot pick own answer as best':'不能设置自己的回答为最佳答案', + 'anonymous users cannot select favorite questions':'匿名用户不能收藏问题,请先', + 'please login':'注册或者登录', + 'anonymous users cannot vote':'匿名用户不能投票', + '>15 points requried to upvote':'需要+15积分才能投支持票。', + '>100 points required to downvote':'需要+100积分才能投反对票。', + 'please see': '查看', + 'cannot vote for own posts':'不能给自己的帖子投票', + 'daily vote cap exhausted':'对不起,您已用完今日所有的投票。', + 'cannot revoke old vote':'这个投票已经过时,不能撤销。', + 'please confirm offensive':"确定要归类该帖为广告、人身攻击、恶意言论吗?", + 'anonymous users cannot flag offensive posts':'匿名用户不能操作,请先', + 'cannot flag message as offensive twice':'不能重复操作。', + 'flag offensive cap exhausted':'对不起,您已用完今日所有的5次‘水帖’操作。', + 'need >15 points to report spam':"需要+15积分才能归类‘垃圾帖’。", + 'confirm delete':"确定要删除/撤销删除该帖吗?", + 'anonymous users cannot delete/undelete':"匿名用户不能删除或撤销删除帖子", + 'post recovered':"操作成功!该帖子已被恢复。", + 'post deleted':"操作成功!该帖子已删除。", + 'add comment':'添加评论', + 'community karma points':'社区积分', + 'to comment, need':'评论需要', + 'delete this comment':'删除此评论', + 'hide comments':"隐藏评论", + 'add a comment':"添加评论", + 'comments':"评论", + 'confirm delete comment':"真要删除此评论吗?", + 'characters':'字符', + 'can write':'还可写', + 'click to close':'点击消息框关闭', + 'loading...':'读取中...', + 'tags cannot be empty':'标签不能为空。', + 'tablimits info':"最多5个标签,每个标签长度小于20个字符。", + 'content cannot be empty':'内容不能为空。', + 'content minchars': '请输入至少 {0} 字符。', + 'please enter title':'请输入标题。', + 'title minchars':"请输入至少 {0} 字符。", + 'delete':'删除', + 'undelete': '取消', + 'bold':'粗体', + 'italic':'斜体', + 'link':'超链接', + 'quote':'引用', + 'preformatted text':'代码', + 'image':'图片', + 'numbered list':'数字编号列表', + 'bulleted list':'项目符号列表', + 'heading':'标题', + 'horizontal bar':'水平线', + 'undo':'撤销', + 'redo':'重做', + 'enter image url':'输入图片地址

示例:
http://www.example.com/image.jpg \"我的截图\"', + 'enter url':'输入Web地址

示例:
http://www.cnprog.com/ \"我的网站\"

"', + 'upload image':'或者上传本地图片:' +}; + +var i18nEn = { + 'need >15 points to report spam':'need >15 points to report spam ', + '>15 points requried to upvote':'>15 points required to upvote ', + 'tags cannot be empty':'please enter at least one tag', + 'anonymous users cannot vote':'sorry, anonymous users cannot vote ', + 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ', + 'to comment, need': '(to comment other people\'s posts, karma ', + 'please see':'please see ', + 'community karma points':' or more is necessary) - ', + 'upload image':'Upload image:', + 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"', + 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"', + 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap', + 'cannot pick own answer as best':'sorry, you cannot accept your own answer', + 'cannot revoke old vote':'sorry, older votes cannot be revoked', + 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?', + 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ', + 'confirm delete':'are you sure you want to delete this?', + 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts', + 'post recovered':'your post is now restored!', + 'post deleted':'your post has been deleted', + 'confirm delete comment':'do you really want to delete this comment?', + 'can write':'have ', + 'tablimits info':'up to 5 tags, no more than 20 characters each', + 'content minchars': 'please enter more than {0} characters', + 'title minchars':"please enter at least {0} characters", + 'characters':'characters left', + 'cannot vote for own posts':'sorry, you cannot vote for your own posts', + 'cannot flag message as offensive twice':'cannot flag message as offensive twice ', + '>100 points required to downvote':'>100 points required to downvote ' +}; + +var i18nTr = { + 'insufficient privilege':'buna yetkiniz yoktur', + 'cannot pick own answer as best':'en cevap olarak kendi cevabınızı seçemezsiniz', + 'anonymous users cannot select favorite questions':'üye girişi yapmadan favori seçemezsiniz', + 'please login':'lütfen üye girişi yapınız', + 'anonymous users cannot vote':'üye girişi yapmadan oy kullanamazsınız', + '>15 points requried to upvote': 'beğeninizi göstermek için en az 15 puan toplamalısınız', + '>100 points required to downvote':'beğenmediğinizi göstermek için en az 100 puan toplamalısınız', + 'please see': 'lütfen bakın', + 'cannot vote for own posts':'kendi yazılarınıza oy veremezsiniz', + 'daily vote cap exhausted':'bugünlük oy verme kotanız doldu', + 'cannot revoke old vote':'verilen bir oyu iptal edemezsiniz', + 'please confirm offensive':"şikayetinizi onaylayın", + 'anonymous users cannot flag offensive posts':'üye girişi yapmadan şikayet gönderemezsiniz', + 'cannot flag message as offensive twice':'şikayet mesajı olarak iki kez işaretlemelisiniz', + 'flag offensive cap exhausted':'şikayet kotası aşıldı', + 'need >15 points to report spam':"spam olarak bildirmek için an az 15 puanınız olmalı", + 'confirm delete':"Bunu silmek istediğinizden emin misiniz?", + 'anonymous users cannot delete/undelete':"üye girişi yapmadan yazı silemez yada geri alamazsınız", + 'post recovered':"yazı geri alındı", + 'post deleted':"yazı silindi", + 'add comment':'yorum ekle', + 'community karma points':'site itibar puanları', + 'to comment, need':'Yorum için itibar puanınız olmalı', + 'delete this comment':'bu yorumu sil', + 'hide comments':"yorumları gizle", + 'add a comment':"yorum ekle", + 'comments':"yorumlar", + 'confirm delete comment':"yorumu silmek istediğinizden emin misiniz?", + 'characters':'karakter eksik', + 'can write':'yazılabilir ', + 'click to close':'kapatmak için tıklayın', + 'loading...':'yükleniyor...', + 'tags cannot be empty':'etiketler boş olamaz', + 'tablimits info':"En fazla 5 etiket ve her biri en fazla 20 karakter", + 'content cannot be empty':'içerik boş olamaz', + 'content minchars': 'Lütfen en az (0) karakter girin', + 'please enter title':'lütfen bir başlık yazın', + 'title minchars':"Lütfen en az (0) karakter girin", + 'delete':'sil', + 'undelete': 'geri al', + 'bold': 'kalın', + 'italic':'italik', + 'link':'link', + 'quote':'alıntı', + 'preformatted text':'hazır metin', + 'image':'resimler', + 'numbered list':'numaralı liste', + 'bulleted list':'işaretli liste', + 'heading':'Başlık', + 'horizontal bar':'yatay bar', + 'undo':'geri', + 'redo':'yeniden', + 'enter image url':'örnek resmin URLsini girin:
http://www.example.com/image.jpg \"resim başlığı\"', + 'enter url':'web adresini girin:
http://www.cnprog.com/ \"başlık bağlantısı\"

"', + 'upload image':'resim yükle:', + 'questions/' : 'sorular/', + 'answers/' : 'cevaplar/', + 'comments/' : 'yorumlar/', + 'vote/' : 'oy/', + 'delete/' : 'sil/' +}; + +var i18nEs = { + 'insufficient privilege':'privilegio insuficiente', + 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor', + 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar', + 'please login':'por favor inicie sesión', + 'anonymous users cannot vote':'usuarios anónimos no pueden votar', + '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente', + '>100 points required to downvote':'>100 puntos requeridos para votar negativamente', + 'please see': 'por favor vea', + 'cannot vote for own posts':'no se puede votar por sus propias publicaciones', + 'daily vote cap exhausted':'cuota de votos diarios excedida', + 'cannot revoke old vote':'no puede revocar un voto viejo', + 'please confirm offensive':"por favor confirme ofensiva", + 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas', + 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces', + 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida', + 'need >15 points to report spam':"necesita >15 puntos para reportar spam", + 'confirm delete':"¿Está seguro que desea borrar esto?", + 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones", + 'post recovered':"publicación recuperada", + 'post deleted':"publicación borrada。", + 'add comment':'agregar comentario', + 'community karma points':'reputación comunitaria', + 'to comment, need':'para comentar, necesita reputación', + 'delete this comment':'borrar este comentario', + 'hide comments':"ocultar comentarios", + 'add a comment':"agregar comentarios", + 'comments':"comentarios", + 'confirm delete comment':"¿Realmente desea borrar este comentario?", + 'characters':'caracteres faltantes', + 'can write':'tiene ', + 'click to close':'haga click para cerrar', + 'loading...':'cargando...', + 'tags cannot be empty':'las etiquetas no pueden estar vacías', + 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una", + 'content cannot be empty':'el contenido no puede estar vacío', + 'content minchars': 'por favor introduzca mas de {0} caracteres', + 'please enter title':'por favor ingrese un título', + 'title minchars':"por favor introduzca al menos {0} caracteres", + 'delete':'borrar', + 'undelete': 'recuperar', + 'bold': 'negrita', + 'italic':'cursiva', + 'link':'enlace', + 'quote':'citar', + 'preformatted text':'texto preformateado', + 'image':'imagen', + 'numbered list':'lista numerada', + 'bulleted list':'lista no numerada', + 'heading':'标题', + 'horizontal bar':'barra horizontal', + 'undo':'deshacer', + 'redo':'rehacer', + 'enter image url':'introduzca la URL de la imagen, por ejemplo:
http://www.example.com/image.jpg \"titulo de imagen\"', + 'enter url':'introduzca direcciones web, ejemplo:
http://www.cnprog.com/ \"titulo del enlace\"

"', + 'upload image':'cargar imagen:', + 'questions/' : 'preguntas/', + 'answers/' : 'respuestas/', + 'comments/' : 'comentarios/', + 'vote/' : 'votar/', + 'delete/' : 'eliminar/' +}; + +var i18n = { + 'en':i18nEn, + 'zh-cn':i18nZh, + 'es':i18nEs, + 'tr':i18nTr +}; + +var i18n_dict = i18n[i18nLang]; diff --git a/forum/skins/default/media/style/style.css b/forum/skins/default/media/style/style.css index 1ae117c4..9d34a26e 100755 --- a/forum/skins/default/media/style/style.css +++ b/forum/skins/default/media/style/style.css @@ -229,7 +229,7 @@ blockquote { } #CARight { - width: 240px; + width: 235px; float: right; } @@ -272,7 +272,7 @@ blockquote { } #top a.ab-responses-envelope { - margin-left: 0; + margin-left: 3px; } #top a img { vertical-align:middle; @@ -288,7 +288,7 @@ blockquote { #logoContainer { } #navTabContainer { - width: 600px; + width: 610px; padding-left: 10px; text-align: left; } @@ -371,7 +371,7 @@ blockquote { line-height: 24px; height: 36px; width: 605px; - margin: 0px; + margin: 0px 3px 0px 0px; padding: 5px 0 0 5px; } @@ -381,7 +381,7 @@ blockquote { height: 36px; width: 561px; padding: 5px 0 0 5px; - margin: 0px; + margin: 0px 3px 0px 0px; } #searchBar .searchBtn { @@ -392,8 +392,8 @@ blockquote { width: 80px; width: 80px; line-height: 22px; + margin: 0px; text-align: center; - margin-top:1px; padding-bottom: 4px; } @@ -403,8 +403,8 @@ blockquote { height: 40px; width: 40px; line-height: 22px; + margin: 0px 3px 0px 0px; padding-bottom: 4px; - margin-top:1px; text-align: center; } @@ -694,7 +694,6 @@ blockquote { background: white /*#cacdc6; /*f9f7ed;*/ padding: 10px; margin-bottom: 8px; - margin-left: 10px; /* border-top: 1px solid #eeeeec; border-left: 1px solid #eeeeec; @@ -833,7 +832,7 @@ conflicts with WMD! .tags a { white-space: nowrap; - font-size: 13px; + font-size: 10px; font-weight: normal; color: #333; text-decoration: none; @@ -843,6 +842,7 @@ conflicts with WMD! border-bottom: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 8px 1px 8px; + margin-right:3px; } .tags a:hover { diff --git a/forum/skins/default/templates/question_list.html b/forum/skins/default/templates/question_list.html index 38ac254a..c9fc1f96 100644 --- a/forum/skins/default/templates/question_list.html +++ b/forum/skins/default/templates/question_list.html @@ -11,7 +11,7 @@
{% diff_date question.last_activity_at %} {% if question.last_activity_by %} - {{ question.last_activity_by }} {% get_score_badge question.last_activity_by %} +  {{ question.last_activity_by }} {% get_score_badge question.last_activity_by %} {% endif %}
diff --git a/forum/skins/default/templates/user_responses.html b/forum/skins/default/templates/user_responses.html index 21f08046..2d96112b 100644 --- a/forum/skins/default/templates/user_responses.html +++ b/forum/skins/default/templates/user_responses.html @@ -28,8 +28,8 @@ response_snippet - abbreviated content of the response
{{ response.user.username }} - {{ response.response_type }}, - {% diff_date response.timestamp 3 %}:
+ {{ response.response_type }} + ({% diff_date response.timestamp 3 "True" %}):
"{{ response.response_title }}"  {{ response.response_snippet|safe }}
diff --git a/forum/sql_scripts/update_2010_01_23.sql b/forum/sql_scripts/update_2010_01_23.sql deleted file mode 100755 index 621207be..00000000 --- a/forum/sql_scripts/update_2010_01_23.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE `fbconnect_fbassociation` ( - `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, - `user_id` integer NOT NULL, - `fbuid` varchar(12) NOT NULL UNIQUE -) -; -ALTER TABLE `fbconnect_fbassociation` ADD CONSTRAINT `user_id_refs_id_3534873d` -FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`); -CREATE INDEX `fbconnect_fbassociation_user_id` ON `fbconnect_fbassociation` (`user_id`); diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py index 2da313db..8c023810 100644 --- a/forum/templatetags/extra_filters.py +++ b/forum/templatetags/extra_filters.py @@ -1,6 +1,6 @@ from django import template from forum import auth -from forum_modules.grapefruit import Color +from forum.deps.grapefruit import Color from django.utils.translation import ugettext as _ import logging diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py index 15f97ac8..960870a0 100644 --- a/forum/templatetags/extra_tags.py +++ b/forum/templatetags/extra_tags.py @@ -301,7 +301,7 @@ def convert2tagname_list(question): return '' @register.simple_tag -def diff_date(date, limen=2): +def diff_date(date, limen=2, use_on_prefix = False): now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#??? diff = now - date days = diff.days @@ -310,9 +310,13 @@ def diff_date(date, limen=2): if days > 2: if date.year == now.year: - return date.strftime("%b %d")# at %H:%M") + date_token = date.strftime("%b %d") else: - return date.strftime("%b %d '%y")# at %H:%M") + date_token = date.strftime("%b %d '%y") + if use_on_prefix: + return _('on %(date)s') % { 'date': date_token } + else: + return date_token elif days == 2: return _('2 days ago') elif days == 1: diff --git a/forum/tests.py b/forum/tests.py index 28544b73..4f0482f5 100644 --- a/forum/tests.py +++ b/forum/tests.py @@ -614,7 +614,7 @@ class UpdateNotificationTests(TestCase): class AnonymousVisitorTests(TestCase): - fixtures = ['forum/fixtures/full_dump.json', ] + fixtures = ['tmp/fixture1.json', ] def test_index(self): #todo: merge this with all reader url tests diff --git a/forum/utils/colors.py b/forum/utils/colors.py index 694cc3b0..f7bee01e 100644 --- a/forum/utils/colors.py +++ b/forum/utils/colors.py @@ -1,4 +1,4 @@ -from forum_modules.grapefruit import Color +from forum.deps.grapefruit import Color import math def get_counter_colors(count, counter_max=10, empty_bg='white', empty_fg='black', diff --git a/forum/views/users.py b/forum/views/users.py index 42d406c2..0995812e 100644 --- a/forum/views/users.py +++ b/forum/views/users.py @@ -637,8 +637,8 @@ def user_responses(request, user_id, user_view): page """ user = get_object_or_404(models.User, id=user_id) - #if request.user != user: - # raise Http404 + if request.user != user: + raise Http404 user = get_object_or_404(models.User, id=user_id) response_list = [] diff --git a/forum_modules/__init__.py b/forum_modules/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/forum_modules/authentication/README b/forum_modules/authentication/README deleted file mode 100644 index a602dc2c..00000000 --- a/forum_modules/authentication/README +++ /dev/null @@ -1,3 +0,0 @@ -THIS DIRECTORY IS NOT USED - -authentication module will be redone as separate application diff --git a/forum_modules/authentication/auth.py b/forum_modules/authentication/auth.py deleted file mode 100644 index b46e3df3..00000000 --- a/forum_modules/authentication/auth.py +++ /dev/null @@ -1,144 +0,0 @@ -from django.shortcuts import render_to_response, get_object_or_404 -from django.template import RequestContext -from django.core.urlresolvers import reverse -from django.contrib.auth.models import User -from django.http import HttpResponseRedirect, Http404 -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext as _ -from django.utils.http import urlquote_plus -from django.contrib.auth.decorators import login_required -from django.contrib.auth import login, logout -from django.http import get_host -import types -import datetime - -from forum.models import AuthKeyUserAssociation, ValidationHash -from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \ - TemporaryLoginRequestForm, ChangePasswordForm, SetPasswordForm -from forum.utils.email import send_email - -from forum.authentication.base import InvalidAuthentication -from forum.authentication import AUTH_PROVIDERS - -from forum.models import Question, Answer - -def send_validation_email(user): - hash = ValidationHash.objects.create_new(user, 'email', [user.email]) - send_email(_("Email Validation"), [user.email], "auth/email_validation.html", { - 'validation_code': hash, - 'user': user - }) - -def validate_email(request, user, code): - user = get_object_or_404(User, id=user) - - if (ValidationHash.objects.validate(code, user, 'email', [user.email])): - user.email_isvalid = True - user.save() - return login_and_forward(request, user, None, _("Thank you, your email is now validated.")) - else: - raise Http404() - -@login_required -def auth_settings(request): - """ - change password view. - - url : /changepw/ - template: authopenid/changepw.html - """ - user_ = request.user - auth_keys = user_.auth_keys.all() - - if user_.has_usable_password(): - FormClass = ChangePasswordForm - else: - FormClass = SetPasswordForm - - if request.POST: - form = FormClass(request.POST, user=user_) - if form.is_valid(): - if user_.has_usable_password(): - request.user.message_set.create(message=_("Your password was changed")) - else: - request.user.message_set.create(message=_("New password set")) - FormClass = ChangePasswordForm - - user_.set_password(form.cleaned_data['password1']) - user_.save() - return HttpResponseRedirect(reverse('user_authsettings')) - - form = FormClass(user=user_) - - auth_keys_list = [] - - for k in auth_keys: - provider = AUTH_PROVIDERS.get(k.provider, None) - - if provider is not None: - name = "%s: %s" % (provider.context.human_name, provider.context.readable_key(k)) - else: - from forum.authentication.base import ConsumerTemplateContext - "unknown: %s" % ConsumerTemplateContext.readable_key(k) - - auth_keys_list.append({ - 'name': name, - 'id': k.id - }) - - return render_to_response('auth/auth_settings.html', { - 'form': form, - 'has_password': user_.has_usable_password(), - 'auth_keys': auth_keys_list, - }, context_instance=RequestContext(request)) - -def newquestion_signin_action(user): - question = Question.objects.filter(author=user).order_by('-added_at')[0] - return question.get_absolute_url() - -def newanswer_signin_action(user): - answer = Answer.objects.filter(author=user).order_by('-added_at')[0] - return answer.get_absolute_url() - -POST_SIGNIN_ACTIONS = { - 'newquestion': newquestion_signin_action, - 'newanswer': newanswer_signin_action, -} - -def login_and_forward(request, user, forward=None, message=None): - old_session = request.session.session_key - user.backend = "django.contrib.auth.backends.ModelBackend" - login(request, user) - - from forum.models import signals#todo: move to auth app - signals.user_logged_in.send(user=user,session_key=old_session,sender=None) - - if not forward: - signin_action = request.session.get('on_signin_action', None) - if not signin_action: - forward = request.session.get('on_signin_url', None) - - if not forward: - forward = reverse('index') - else: - try: - forward = POST_SIGNIN_ACTIONS[signin_action](user) - except: - forward = reverse('index') - - if message is None: - message = _("Welcome back %s, you are now logged in") % user.username - - request.user.message_set.create(message=message) - return HttpResponseRedirect(forward) - -@login_required -def signout(request): - """ - signout from the website. Remove openid from session and kill it. - - url : /signout/" - """ - - logout(request) - return HttpResponseRedirect(reverse('index')) diff --git a/forum_modules/books/__init__.py b/forum_modules/books/__init__.py deleted file mode 100644 index c51a2bfb..00000000 --- a/forum_modules/books/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -NAME = 'Books' -DESCRIPTION = "Allows discussion around books." -CAN_ENABLE = True diff --git a/forum_modules/books/models.py b/forum_modules/books/models.py deleted file mode 100644 index a78c0e76..00000000 --- a/forum_modules/books/models.py +++ /dev/null @@ -1,63 +0,0 @@ -from django.db import models -from django.contrib.auth.models import User -from forum.models import Question -from django.core.urlresolvers import reverse -from django.utils.http import urlquote as django_urlquote -from django.template.defaultfilters import slugify - -class Book(models.Model): - """ - Model for book info - """ - user = models.ForeignKey(User) - title = models.CharField(max_length=255) - short_name = models.CharField(max_length=255) - author = models.CharField(max_length=255) - price = models.DecimalField(max_digits=6, decimal_places=2) - pages = models.SmallIntegerField() - published_at = models.DateTimeField() - publication = models.CharField(max_length=255) - cover_img = models.CharField(max_length=255) - tagnames = models.CharField(max_length=125) - added_at = models.DateTimeField() - last_edited_at = models.DateTimeField() - questions = models.ManyToManyField(Question, related_name='book', db_table='book_question') - - def get_absolute_url(self): - return reverse('book', args=[django_urlquote(slugify(self.short_name))]) - - def __unicode__(self): - return self.title - - class Meta: - app_label = 'forum' - db_table = u'book' - -class BookAuthorInfo(models.Model): - """ - Model for book author info - """ - user = models.ForeignKey(User) - book = models.ForeignKey(Book) - blog_url = models.CharField(max_length=255) - added_at = models.DateTimeField() - last_edited_at = models.DateTimeField() - - class Meta: - app_label = 'forum' - db_table = u'book_author_info' - -class BookAuthorRss(models.Model): - """ - Model for book author blog rss - """ - user = models.ForeignKey(User) - book = models.ForeignKey(Book) - title = models.CharField(max_length=255) - url = models.CharField(max_length=255) - rss_created_at = models.DateTimeField() - added_at = models.DateTimeField() - - class Meta: - app_label = 'forum' - db_table = u'book_author_rss' \ No newline at end of file diff --git a/forum_modules/books/urls.py b/forum_modules/books/urls.py deleted file mode 100644 index bc0811e7..00000000 --- a/forum_modules/books/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls.defaults import * -from django.utils.translation import ugettext as _ - -import views as app - -urlpatterns = patterns('', - url(r'^%s$' % _('books/'), app.books, name='books'), - url(r'^%s%s(?P[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'), - url(r'^%s(?P[^/]+)/$' % _('books/'), app.book, name='book'), -) \ No newline at end of file diff --git a/forum_modules/books/views.py b/forum_modules/books/views.py deleted file mode 100644 index d4907e5f..00000000 --- a/forum_modules/books/views.py +++ /dev/null @@ -1,142 +0,0 @@ -from django.shortcuts import render_to_response, get_object_or_404 -from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404 -from django.template import RequestContext -from django.contrib.auth.decorators import login_required -from django.core.urlresolvers import reverse -from django.utils.html import * - -from models import * - -from forum.forms import AskForm -from forum.views.readers import _get_tags_cache_json -from forum.models import * -from forum.utils.html import sanitize_html - -def books(request): - return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang') - -def book(request, short_name, unanswered=False): - """ - 1. questions list - 2. book info - 3. author info and blog rss items - """ - """ - List of Questions, Tagged questions, and Unanswered questions. - """ - books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) - match_count = len(books) - if match_count == 0: - raise Http404 - else: - # the book info - book = books[0] - # get author info - author_info = BookAuthorInfo.objects.get(book=book) - # get author rss info - author_rss = BookAuthorRss.objects.filter(book=book) - - # get pagesize from session, if failed then get default value - user_page_size = request.session.get("page_size", QUESTIONS_PAGE_SIZE) - # set pagesize equal to logon user specified value in database - if request.user.is_authenticated() and request.user.questions_per_page > 0: - user_page_size = request.user.questions_per_page - - try: - page = int(request.GET.get('page', '1')) - except ValueError: - page = 1 - - view_id = request.GET.get('sort', None) - view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } - try: - orderby = view_dic[view_id] - except KeyError: - view_id = "latest" - orderby = "-added_at" - - # check if request is from tagged questions - if unanswered: - # check if request is from unanswered questions - # Article.objects.filter(publications__id__exact=1) - objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby) - else: - objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby) - - # RISK - inner join queries - objects = objects.select_related(); - objects_list = Paginator(objects, user_page_size) - questions = objects_list.page(page) - - return render_to_response('book.html', { - "book" : book, - "author_info" : author_info, - "author_rss" : author_rss, - "questions" : questions, - "context" : { - 'is_paginated' : True, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': questions.has_previous(), - 'has_next': questions.has_next(), - 'previous': questions.previous_page_number(), - 'next': questions.next_page_number(), - 'base_url' : request.path + '?sort=%s&' % view_id, - 'page_size' : user_page_size - } - }, context_instance=RequestContext(request)) - -@login_required -def ask_book(request, short_name): - if request.method == "POST": - form = AskForm(request.POST) - if form.is_valid(): - added_at = datetime.datetime.now() - html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) - question = Question( - title = strip_tags(form.cleaned_data['title']), - author = request.user, - added_at = added_at, - last_activity_at = added_at, - last_activity_by = request.user, - wiki = form.cleaned_data['wiki'], - tagnames = form.cleaned_data['tags'].strip(), - html = html, - summary = strip_tags(html)[:120] - ) - if question.wiki: - question.last_edited_by = question.author - question.last_edited_at = added_at - question.wikified_at = added_at - - question.save() - - # create the first revision - QuestionRevision.objects.create( - question = question, - revision = 1, - title = question.title, - author = request.user, - revised_at = added_at, - tagnames = question.tagnames, - summary = CONST['default_version'], - text = form.cleaned_data['text'] - ) - - books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) - match_count = len(books) - if match_count == 1: - # the book info - book = books[0] - book.questions.add(question) - - return HttpResponseRedirect(question.get_absolute_url()) - else: - form = AskForm() - - tags = _get_tags_cache_json() - return render_to_response('ask.html', { - 'form' : form, - 'tags' : tags, - 'email_validation_faq_url': reverse('faq') + '#validate', - }, context_instance=RequestContext(request)) diff --git a/forum_modules/grapefruit.py b/forum_modules/grapefruit.py deleted file mode 100644 index ca684745..00000000 --- a/forum_modules/grapefruit.py +++ /dev/null @@ -1,1973 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*-# - -# Copyright (c) 2008, Xavier Basty -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -'''GrapeFruit - Color manipulation in Python''' - -# $Id: grapefruit.py 31 2008-06-15 10:48:06Z xbasty $ -__author__ = 'Xavier Basty ' -__version__ = '0.1a3' - - -# The default white reference, use 2° Standard Observer, D65 (daylight) -_DEFAULT_WREF = (0.95043, 1.00000, 1.08890) - -_oneThird = 1.0 / 3 -_srgbGammaCorrInv = 0.03928 / 12.92 -_sixteenHundredsixteenth = 16.0 / 116 - -_RybWheel = ( - 0, 26, 52, - 83, 120, 130, - 141, 151, 162, - 177, 190, 204, - 218, 232, 246, - 261, 275, 288, - 303, 317, 330, - 338, 345, 352, - 360) - -_RgbWheel = ( - 0, 8, 17, - 26, 34, 41, - 48, 54, 60, - 81, 103, 123, - 138, 155, 171, - 187, 204, 219, - 234, 251, 267, - 282, 298, 329, - 360) - -class Color: - '''Hold a color value. - - Example usage: - - To create an instance of the grapefruit.Color from RGB values: - - >>> import grapefruit - >>> r, g, b = 1, 0.5, 0 - >>> col = grapefruit.Color.NewFromRgb(r, g, b) - - To get the values of the color in another colorspace: - - >>> h, s, v = col.hsv - >>> l, a, b = col.lab - - To get the complementary of a color: - - >>> compl = col.ComplementaryColor() - >>> print compl.hsl - (210.0, 1.0, 0.5) - - To directly convert RGB values to their HSL equivalent: - - >>> h, s, l = Color.RgbToHsl(r, g, b) - - ''' - - WHITE_REFERENCE = { - 'std_A' : (1.09847, 1.00000, 0.35582), - 'std_B' : (0.99093, 1.00000, 0.85313), - 'std_C' : (0.98071, 1.00000, 1.18225), - 'std_D50' : (0.96421, 1.00000, 0.82519), - 'std_D55' : (0.95680, 1.00000, 0.92148), - 'std_D65' : (0.95043, 1.00000, 1.08890), - 'std_D75' : (0.94972, 1.00000, 1.22639), - 'std_E' : (1.00000, 1.00000, 1.00000), - 'std_F1' : (0.92834, 1.00000, 1.03665), - 'std_F2' : (0.99145, 1.00000, 0.67316), - 'std_F3' : (1.03753, 1.00000, 0.49861), - 'std_F4' : (1.09147, 1.00000, 0.38813), - 'std_F5' : (0.90872, 1.00000, 0.98723), - 'std_F6' : (0.97309, 1.00000, 0.60191), - 'std_F7' : (0.95017, 1.00000, 1.08630), - 'std_F8' : (0.96413, 1.00000, 0.82333), - 'std_F9' : (1.00365, 1.00000, 0.67868), - 'std_F10' : (0.96174, 1.00000, 0.81712), - 'std_F11' : (1.00899, 1.00000, 0.64262), - 'std_F12' : (1.08046, 1.00000, 0.39228), - 'sup_A' : (1.11142, 1.00000, 0.35200), - 'sup_B' : (0.99178, 1.00000, 0.84349), - 'sup_C' : (0.97286, 1.00000, 1.16145), - 'sup_D50' : (0.96721, 1.00000, 0.81428), - 'sup_D55' : (0.95797, 1.00000, 0.90925), - 'sup_D65' : (0.94810, 1.00000, 1.07305), - 'sup_D75' : (0.94417, 1.00000, 1.20643), - 'sup_E' : (1.00000, 1.00000, 1.00000), - 'sup_F1' : (0.94791, 1.00000, 1.03191), - 'sup_F2' : (1.03245, 1.00000, 0.68990), - 'sup_F3' : (1.08968, 1.00000, 0.51965), - 'sup_F4' : (1.14961, 1.00000, 0.40963), - 'sup_F5' : (0.93369, 1.00000, 0.98636), - 'sup_F6' : (1.02148, 1.00000, 0.62074), - 'sup_F7' : (0.95780, 1.00000, 1.07618), - 'sup_F8' : (0.97115, 1.00000, 0.81135), - 'sup_F9' : (1.02116, 1.00000, 0.67826), - 'sup_F10' : (0.99001, 1.00000, 0.83134), - 'sup_F11' : (1.03820, 1.00000, 0.65555), - 'sup_F12' : (1.11428, 1.00000, 0.40353)} - - NAMED_COLOR = { - 'aliceblue': '#f0f8ff', - 'antiquewhite': '#faebd7', - 'aqua': '#00ffff', - 'aquamarine': '#7fffd4', - 'azure': '#f0ffff', - 'beige': '#f5f5dc', - 'bisque': '#ffe4c4', - 'black': '#000000', - 'blanchedalmond': '#ffebcd', - 'blue': '#0000ff', - 'blueviolet': '#8a2be2', - 'brown': '#a52a2a', - 'burlywood': '#deb887', - 'cadetblue': '#5f9ea0', - 'chartreuse': '#7fff00', - 'chocolate': '#d2691e', - 'coral': '#ff7f50', - 'cornflowerblue': '#6495ed', - 'cornsilk': '#fff8dc', - 'crimson': '#dc143c', - 'cyan': '#00ffff', - 'darkblue': '#00008b', - 'darkcyan': '#008b8b', - 'darkgoldenrod': '#b8860b', - 'darkgray': '#a9a9a9', - 'darkgrey': '#a9a9a9', - 'darkgreen': '#006400', - 'darkkhaki': '#bdb76b', - 'darkmagenta': '#8b008b', - 'darkolivegreen': '#556b2f', - 'darkorange': '#ff8c00', - 'darkorchid': '#9932cc', - 'darkred': '#8b0000', - 'darksalmon': '#e9967a', - 'darkseagreen': '#8fbc8f', - 'darkslateblue': '#483d8b', - 'darkslategray': '#2f4f4f', - 'darkslategrey': '#2f4f4f', - 'darkturquoise': '#00ced1', - 'darkviolet': '#9400d3', - 'deeppink': '#ff1493', - 'deepskyblue': '#00bfff', - 'dimgray': '#696969', - 'dimgrey': '#696969', - 'dodgerblue': '#1e90ff', - 'firebrick': '#b22222', - 'floralwhite': '#fffaf0', - 'forestgreen': '#228b22', - 'fuchsia': '#ff00ff', - 'gainsboro': '#dcdcdc', - 'ghostwhite': '#f8f8ff', - 'gold': '#ffd700', - 'goldenrod': '#daa520', - 'gray': '#808080', - 'grey': '#808080', - 'green': '#008000', - 'greenyellow': '#adff2f', - 'honeydew': '#f0fff0', - 'hotpink': '#ff69b4', - 'indianred': '#cd5c5c', - 'indigo': '#4b0082', - 'ivory': '#fffff0', - 'khaki': '#f0e68c', - 'lavender': '#e6e6fa', - 'lavenderblush': '#fff0f5', - 'lawngreen': '#7cfc00', - 'lemonchiffon': '#fffacd', - 'lightblue': '#add8e6', - 'lightcoral': '#f08080', - 'lightcyan': '#e0ffff', - 'lightgoldenrodyellow': '#fafad2', - 'lightgreen': '#90ee90', - 'lightgray': '#d3d3d3', - 'lightgrey': '#d3d3d3', - 'lightpink': '#ffb6c1', - 'lightsalmon': '#ffa07a', - 'lightseagreen': '#20b2aa', - 'lightskyblue': '#87cefa', - 'lightslategray': '#778899', - 'lightslategrey': '#778899', - 'lightsteelblue': '#b0c4de', - 'lightyellow': '#ffffe0', - 'lime': '#00ff00', - 'limegreen': '#32cd32', - 'linen': '#faf0e6', - 'magenta': '#ff00ff', - 'maroon': '#800000', - 'mediumaquamarine': '#66cdaa', - 'mediumblue': '#0000cd', - 'mediumorchid': '#ba55d3', - 'mediumpurple': '#9370db', - 'mediumseagreen': '#3cb371', - 'mediumslateblue': '#7b68ee', - 'mediumspringgreen': '#00fa9a', - 'mediumturquoise': '#48d1cc', - 'mediumvioletred': '#c71585', - 'midnightblue': '#191970', - 'mintcream': '#f5fffa', - 'mistyrose': '#ffe4e1', - 'moccasin': '#ffe4b5', - 'navajowhite': '#ffdead', - 'navy': '#000080', - 'oldlace': '#fdf5e6', - 'olive': '#808000', - 'olivedrab': '#6b8e23', - 'orange': '#ffa500', - 'orangered': '#ff4500', - 'orchid': '#da70d6', - 'palegoldenrod': '#eee8aa', - 'palegreen': '#98fb98', - 'paleturquoise': '#afeeee', - 'palevioletred': '#db7093', - 'papayawhip': '#ffefd5', - 'peachpuff': '#ffdab9', - 'peru': '#cd853f', - 'pink': '#ffc0cb', - 'plum': '#dda0dd', - 'powderblue': '#b0e0e6', - 'purple': '#800080', - 'red': '#ff0000', - 'rosybrown': '#bc8f8f', - 'royalblue': '#4169e1', - 'saddlebrown': '#8b4513', - 'salmon': '#fa8072', - 'sandybrown': '#f4a460', - 'seagreen': '#2e8b57', - 'seashell': '#fff5ee', - 'sienna': '#a0522d', - 'silver': '#c0c0c0', - 'skyblue': '#87ceeb', - 'slateblue': '#6a5acd', - 'slategray': '#708090', - 'slategrey': '#708090', - 'snow': '#fffafa', - 'springgreen': '#00ff7f', - 'steelblue': '#4682b4', - 'tan': '#d2b48c', - 'teal': '#008080', - 'thistle': '#d8bfd8', - 'tomato': '#ff6347', - 'turquoise': '#40e0d0', - 'violet': '#ee82ee', - 'wheat': '#f5deb3', - 'white': '#ffffff', - 'whitesmoke': '#f5f5f5', - 'yellow': '#ffff00', - 'yellowgreen': '#9acd32'} - - def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): - '''Instantiate a new grapefruit.Color object. - - Parameters: - :values: - The values of this color, in the specified representation. - :mode: - The representation mode used for values. - :alpha: - the alpha value (transparency) of this color. - :wref: - The whitepoint reference, default is 2° D65. - - ''' - if not(isinstance(values, tuple)): - raise TypeError, 'values must be a tuple' - - if mode=='rgb': - self.__rgb = values - self.__hsl = Color.RgbToHsl(*values) - elif mode=='hsl': - self.__hsl = values - self.__rgb = Color.HslToRgb(*values) - else: - raise ValueError('Invalid color mode: ' + mode) - - self.__a = alpha - self.__wref = wref - - def __ne__(self, other): - return not self.__eq__(other) - - def __eq__(self, other): - try: - if isinstance(other, Color): - return (self.__rgb==other.__rgb) and (self.__a==other.__a) - - if len(other) != 4: - return False - rgba = self.__rgb + (self.__a,) - return reduce(lambda x, y: x and (y[0]==y[1]), zip(rgba, other), True) - except TypeError: - return False - except AttributeError: - return False - - def __repr__(self): - return str(self.__rgb + (self.__a,)) - - def __str__(self): - '''A string representation of this grapefruit.Color instance. - - Returns: - The RGBA representation of this grapefruit.Color instance. - - ''' - return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) - - def __unicode__(self): - '''A unicode string representation of this grapefruit.Color instance. - - Returns: - The RGBA representation of this grapefruit.Color instance. - - ''' - return u'(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) - - def __iter__(self): - return iter(self.__rgb + (self.__a,)) - - def __len__(self): - return 4 - - @staticmethod - def RgbToHsl(r, g, b): - '''Convert the color from RGB coordinates to HSL. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (h, s, l) tuple in the range: - h[0...360], - s[0...1], - l[0...1] - - >>> Color.RgbToHsl(1, 0.5, 0) - (30.0, 1.0, 0.5) - - ''' - minVal = min(r, g, b) # min RGB value - maxVal = max(r, g, b) # max RGB value - - l = (maxVal + minVal) / 2.0 - if minVal==maxVal: - return (0.0, 0.0, l) # achromatic (gray) - - d = maxVal - minVal # delta RGB value - - if l < 0.5: s = d / (maxVal + minVal) - else: s = d / (2.0 - maxVal - minVal) - - dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] - - if r==maxVal: - h = db - dg - elif g==maxVal: - h = 2.0 + dr - db - else: - h = 4.0 + dg - dr - - h = (h*60.0) % 360.0 - return (h, s, l) - - @staticmethod - def _HueToRgb(n1, n2, h): - h %= 6.0 - if h < 1.0: return n1 + ((n2-n1) * h) - if h < 3.0: return n2 - if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) - return n1 - - @staticmethod - def HslToRgb(h, s, l): - '''Convert the color from HSL coordinates to RGB. - - Parameters: - :h: - The Hue component value [0...1] - :s: - The Saturation component value [0...1] - :l: - The Lightness component value [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> Color.HslToRgb(30.0, 1.0, 0.5) - (1.0, 0.5, 0.0) - - ''' - if s==0: return (l, l, l) # achromatic (gray) - - if l<0.5: n2 = l * (1.0 + s) - else: n2 = l+s - (l*s) - - n1 = (2.0 * l) - n2 - - h /= 60.0 - hueToRgb = Color._HueToRgb - r = hueToRgb(n1, n2, h + 2) - g = hueToRgb(n1, n2, h) - b = hueToRgb(n1, n2, h - 2) - - return (r, g, b) - - @staticmethod - def RgbToHsv(r, g, b): - '''Convert the color from RGB coordinates to HSV. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (h, s, v) tuple in the range: - h[0...360], - s[0...1], - v[0...1] - - >>> Color.RgbToHsv(1, 0.5, 0) - (30.0, 1, 1) - - ''' - v = max(r, g, b) - d = v - min(r, g, b) - if d==0: return (0.0, 0.0, v) - s = d / v - - dr, dg, db = [(v - val) / d for val in (r, g, b)] - - if r==v: - h = db - dg # between yellow & magenta - elif g==v: - h = 2.0 + dr - db # between cyan & yellow - else: # b==v - h = 4.0 + dg - dr # between magenta & cyan - - h = (h*60.0) % 360.0 - return (h, s, v) - - @staticmethod - def HsvToRgb(h, s, v): - '''Convert the color from RGB coordinates to HSV. - - Parameters: - :h: - The Hus component value [0...1] - :s: - The Saturation component value [0...1] - :v: - The Value component [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> Color.HslToRgb(30.0, 1.0, 0.5) - (1.0, 0.5, 0.0) - - ''' - if s==0: return (v, v, v) # achromatic (gray) - - h /= 60.0 - h = h % 6.0 - - i = int(h) - f = h - i - if not(i&1): f = 1-f # if i is even - - m = v * (1.0 - s) - n = v * (1.0 - (s * f)) - - if i==0: return (v, n, m) - if i==1: return (n, v, m) - if i==2: return (m, v, n) - if i==3: return (m, n, v) - if i==4: return (n, m, v) - return (v, m, n) - - @staticmethod - def RgbToYiq(r, g, b): - '''Convert the color from RGB to YIQ. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (y, i, q) tuple in the range: - y[0...1], - i[0...1], - q[0...1] - - >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) - '(0.592263, 0.458874, -0.0499818)' - - ''' - y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) - i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) - q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) - return (y, i, q) - - @staticmethod - def YiqToRgb(y, i, q): - '''Convert the color from YIQ coordinates to RGB. - - Parameters: - :y: - Tte Y component value [0...1] - :i: - The I component value [0...1] - :q: - The Q component value [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) - '(1, 0.5, 5.442e-007)' - - ''' - r = y + (i * 0.9562) + (q * 0.6210) - g = y - (i * 0.2717) - (q * 0.6485) - b = y - (i * 1.1053) + (q * 1.7020) - return (r, g, b) - - @staticmethod - def RgbToYuv(r, g, b): - '''Convert the color from RGB coordinates to YUV. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (y, u, v) tuple in the range: - y[0...1], - u[-0.436...0.436], - v[-0.615...0.615] - - >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) - '(0.5925, -0.29156, 0.357505)' - - ''' - y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) - u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) - v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) - return (y, u, v) - - @staticmethod - def YuvToRgb(y, u, v): - '''Convert the color from YUV coordinates to RGB. - - Parameters: - :y: - The Y component value [0...1] - :u: - The U component value [-0.436...0.436] - :v: - The V component value [-0.615...0.615] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) - '(0.999989, 0.500015, -6.3276e-005)' - - ''' - r = y + (v * 1.13983) - g = y - (u * 0.39465) - (v * 0.58060) - b = y + (u * 2.03211) - return (r, g, b) - - @staticmethod - def RgbToXyz(r, g, b): - '''Convert the color from sRGB to CIE XYZ. - - The methods assumes that the RGB coordinates are given in the sRGB - colorspace (D65). - - .. note:: - - Compensation for the sRGB gamma correction is applied before converting. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (x, y, z) tuple in the range: - x[0...1], - y[0...1], - z[0...1] - - >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) - '(0.488941, 0.365682, 0.0448137)' - - ''' - r, g, b = [((v <= 0.03928) and [v / 12.92] or [((v+0.055) / 1.055) **2.4])[0] for v in (r, g, b)] - - x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) - y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) - z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) - return (x, y, z) - - @staticmethod - def XyzToRgb(x, y, z): - '''Convert the color from CIE XYZ coordinates to sRGB. - - .. note:: - - Compensation for sRGB gamma correction is applied before converting. - - Parameters: - :x: - The X component value [0...1] - :y: - The Y component value [0...1] - :z: - The Z component value [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) - '(1, 0.5, 6.81883e-008)' - - ''' - r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) - g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) - b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) - return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) - - @staticmethod - def XyzToLab(x, y, z, wref=_DEFAULT_WREF): - '''Convert the color from CIE XYZ to CIE L*a*b*. - - Parameters: - :x: - The X component value [0...1] - :y: - The Y component value [0...1] - :z: - The Z component value [0...1] - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - The color as an (L, a, b) tuple in the range: - L[0...100], - a[-1...1], - b[-1...1] - - >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) - '(66.9518, 0.43084, 0.739692)' - - >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) - '(66.9518, 0.411663, 0.67282)' - - ''' - # White point correction - x /= wref[0] - y /= wref[1] - z /= wref[2] - - # Nonlinear distortion and linear transformation - x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] - - # Vector scaling - l = (116 * y) - 16 - a = 5.0 * (x - y) - b = 2.0 * (y - z) - - return (l, a, b) - - @staticmethod - def LabToXyz(l, a, b, wref=_DEFAULT_WREF): - '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. - - Parameters: - :l: - The L component [0...100] - :a: - The a component [-1...1] - :b: - The a component [-1...1] - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - The color as an (x, y, z) tuple in the range: - x[0...q], - y[0...1], - z[0...1] - - >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) - '(0.488941, 0.365682, 0.0448137)' - - >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) - '(0.488941, 0.365682, 0.0448138)' - - ''' - y = (l + 16) / 116 - x = (a / 5.0) + y - z = y - (b / 2.0) - return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) - - @staticmethod - def CmykToCmy(c, m, y, k): - '''Convert the color from CMYK coordinates to CMY. - - Parameters: - :c: - The Cyan component value [0...1] - :m: - The Magenta component value [0...1] - :y: - The Yellow component value [0...1] - :k: - The Black component value [0...1] - - Returns: - The color as an (c, m, y) tuple in the range: - c[0...1], - m[0...1], - y[0...1] - - >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) - '(1, 0.66, 0.5)' - - ''' - mk = 1-k - return ((c*mk + k), (m*mk + k), (y*mk + k)) - - @staticmethod - def CmyToCmyk(c, m, y): - '''Convert the color from CMY coordinates to CMYK. - - Parameters: - :c: - The Cyan component value [0...1] - :m: - The Magenta component value [0...1] - :y: - The Yellow component value [0...1] - - Returns: - The color as an (c, m, y, k) tuple in the range: - c[0...1], - m[0...1], - y[0...1], - k[0...1] - - >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) - '(1, 0.32, 0, 0.5)' - - ''' - k = min(c, m, y) - if k==1.0: return (0.0, 0.0, 0.0, 1.0) - mk = 1-k - return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) - - @staticmethod - def RgbToCmy(r, g, b): - '''Convert the color from RGB coordinates to CMY. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (c, m, y) tuple in the range: - c[0...1], - m[0...1], - y[0...1] - - >>> Color.RgbToCmy(1, 0.5, 0) - (0, 0.5, 1) - - ''' - return (1-r, 1-g, 1-b) - - @staticmethod - def CmyToRgb(c, m, y): - '''Convert the color from CMY coordinates to RGB. - - Parameters: - :c: - The Cyan component value [0...1] - :m: - The Magenta component value [0...1] - :y: - The Yellow component value [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - >>> Color.CmyToRgb(0, 0.5, 1) - (1, 0.5, 0) - - ''' - return (1-c, 1-m, 1-y) - - @staticmethod - def RgbToHtml(r, g, b): - '''Convert the color from (r, g, b) to #RRGGBB. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - A CSS string representation of this color (#RRGGBB). - - >>> Color.RgbToHtml(1, 0.5, 0) - '#ff8000' - - ''' - return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) - - @staticmethod - def HtmlToRgb(html): - '''Convert the HTML color to (r, g, b). - - Parameters: - :html: - the HTML definition of the color (#RRGGBB or #RGB or a color name). - - Returns: - The color as an (r, g, b) tuple in the range: - r[0...1], - g[0...1], - b[0...1] - - Throws: - :ValueError: - If html is neither a known color name or a hexadecimal RGB - representation. - - >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') - '(1, 0.501961, 0)' - >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') - '(1, 0.501961, 0)' - >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') - '(1, 0.4, 0)' - >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') - '(1, 0.4, 0)' - >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') - '(1, 0.980392, 0.803922)' - - ''' - html = html.strip().lower() - if html[0]=='#': - html = html[1:] - elif Color.NAMED_COLOR.has_key(html): - html = Color.NAMED_COLOR[html][1:] - - if len(html)==6: - rgb = html[:2], html[2:4], html[4:] - elif len(html)==3: - rgb = ['%c%c' % (v,v) for v in html] - else: - raise ValueError, 'input #%s is not in #RRGGBB format' % html - - return tuple(((int(n, 16) / 255.0) for n in rgb)) - - @staticmethod - def RgbToPil(r, g, b): - '''Convert the color from RGB to a PIL-compatible integer. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - A PIL compatible integer (0xBBGGRR). - - >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) - '0x0080ff' - - ''' - r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] - return (b << 16) + (g << 8) + r - - @staticmethod - def PilToRgb(pil): - '''Convert the color from a PIL-compatible integer to RGB. - - Parameters: - pil: a PIL compatible color representation (0xBBGGRR) - Returns: - The color as an (r, g, b) tuple in the range: - the range: - r: [0...1] - g: [0...1] - b: [0...1] - - >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) - '(1, 0.501961, 0)' - - ''' - r = 0xff & pil - g = 0xff & (pil >> 8) - b = 0xff & (pil >> 16) - return tuple((v / 255.0 for v in (r, g, b))) - - @staticmethod - def _WebSafeComponent(c, alt=False): - '''Convert a color component to its web safe equivalent. - - Parameters: - :c: - The component value [0...1] - :alt: - If True, return the alternative value instead of the nearest one. - - Returns: - The web safe equivalent of the component value. - - ''' - # This sucks, but floating point between 0 and 1 is quite fuzzy... - # So we just change the scale a while to make the equality tests - # work, otherwise it gets wrong at some decimal far to the right. - sc = c * 100.0 - - # If the color is already safe, return it straight away - d = sc % 20 - if d==0: return c - - # Get the lower and upper safe values - l = sc - d - u = l + 20 - - # Return the 'closest' value according to the alt flag - if alt: - if (sc-l) >= (u-sc): return l/100.0 - else: return u/100.0 - else: - if (sc-l) >= (u-sc): return u/100.0 - else: return l/100.0 - - @staticmethod - def RgbToWebSafe(r, g, b, alt=False): - '''Convert the color from RGB to 'web safe' RGB - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - :alt: - If True, use the alternative color instead of the nearest one. - Can be used for dithering. - - Returns: - The color as an (r, g, b) tuple in the range: - the range: - r[0...1], - g[0...1], - b[0...1] - - >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) - '(1, 0.6, 0)' - - ''' - webSafeComponent = Color._WebSafeComponent - return tuple((webSafeComponent(v, alt) for v in (r, g, b))) - - @staticmethod - def RgbToGreyscale(r, g, b): - '''Convert the color from RGB to its greyscale equivalent - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - - Returns: - The color as an (r, g, b) tuple in the range: - the range: - r[0...1], - g[0...1], - b[0...1] - - >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) - '(0.6, 0.6, 0.6)' - - ''' - v = (r + g + b) / 3.0 - return (v, v, v) - - @staticmethod - def RgbToRyb(hue): - '''Maps a hue on the RGB color wheel to Itten's RYB wheel. - - Parameters: - :hue: - The hue on the RGB color wheel [0...360] - - Returns: - An approximation of the corresponding hue on Itten's RYB wheel. - - >>> Color.RgbToRyb(15) - 26 - - ''' - d = hue % 15 - i = int(hue / 15) - x0 = _RybWheel[i] - x1 = _RybWheel[i+1] - return x0 + (x1-x0) * d / 15 - - @staticmethod - def RybToRgb(hue): - '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. - - Parameters: - :hue: - The hue on Itten's RYB color wheel [0...360] - - Returns: - An approximation of the corresponding hue on the standard RGB wheel. - - >>> Color.RybToRgb(15) - 8 - - ''' - d = hue % 15 - i = int(hue / 15) - x0 = _RgbWheel[i] - x1 = _RgbWheel[i+1] - return x0 + (x1-x0) * d / 15 - - @staticmethod - def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed RGB values. - - Parameters: - :r: - The Red component value [0...1] - :g: - The Green component value [0...1] - :b: - The Blue component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromRgb(1.0, 0.5, 0.0) - (1.0, 0.5, 0.0, 1.0) - >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) - (1.0, 0.5, 0.0, 0.5) - - ''' - return Color((r, g, b), 'rgb', alpha, wref) - - @staticmethod - def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed HSL values. - - Parameters: - :h: - The Hue component value [0...1] - :s: - The Saturation component value [0...1] - :l: - The Lightness component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5) - (1.0, 0.5, 0.0, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5, 0.5) - (1.0, 0.5, 0.0, 0.5) - - ''' - return Color((h, s, l), 'hsl', alpha, wref) - - @staticmethod - def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed HSV values. - - Parameters: - :h: - The Hus component value [0...1] - :s: - The Saturation component value [0...1] - :v: - The Value component [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsv(30, 1, 1) - (1.0, 0.5, 0.0, 1.0) - >>> Color.NewFromHsv(30, 1, 1, 0.5) - (1.0, 0.5, 0.0, 0.5) - - ''' - h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) - return Color((h, s, l), 'hsl', alpha, wref) - - @staticmethod - def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed YIQ values. - - Parameters: - :y: - The Y component value [0...1] - :i: - The I component value [0...1] - :q: - The Q component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) - '(0.999902, 0.499955, -6.6905e-005, 1)' - >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) - '(0.999902, 0.499955, -6.6905e-005, 0.5)' - - ''' - return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) - - @staticmethod - def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed YUV values. - - Parameters: - :y: - The Y component value [0...1] - :u: - The U component value [-0.436...0.436] - :v: - The V component value [-0.615...0.615] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) - '(0.999989, 0.500015, -6.3276e-005, 1)' - >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) - '(0.999989, 0.500015, -6.3276e-005, 0.5)' - - ''' - return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) - - @staticmethod - def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed CIE-XYZ values. - - Parameters: - :x: - The Red component value [0...1] - :y: - The Green component value [0...1] - :z: - The Blue component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) - '(1, 0.5, 6.81883e-008, 1)' - >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) - '(1, 0.5, 6.81883e-008, 0.5)' - - ''' - return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) - - @staticmethod - def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed CIE-LAB values. - - Parameters: - :l: - The L component [0...100] - :a: - The a component [-1...1] - :b: - The a component [-1...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) - '(1, 0.5, 1.09491e-008, 1)' - >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) - '(1.01238, 0.492011, -0.14311, 1)' - >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) - '(1, 0.5, 1.09491e-008, 0.5)' - >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) - '(1.01238, 0.492011, -0.14311, 0.5)' - - ''' - return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) - - @staticmethod - def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed CMY values. - - Parameters: - :c: - The Cyan component value [0...1] - :m: - The Magenta component value [0...1] - :y: - The Yellow component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromCmy(0, 0.5, 1) - (1, 0.5, 0, 1.0) - >>> Color.NewFromCmy(0, 0.5, 1, 0.5) - (1, 0.5, 0, 0.5) - - ''' - return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) - - @staticmethod - def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed CMYK values. - - Parameters: - :c: - The Cyan component value [0...1] - :m: - The Magenta component value [0...1] - :y: - The Yellow component value [0...1] - :k: - The Black component value [0...1] - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) - '(0, 0.34, 0.5, 1)' - >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) - '(0, 0.34, 0.5, 0.5)' - - ''' - return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) - - @staticmethod - def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed HTML color definition. - - Parameters: - :html: - The HTML definition of the color (#RRGGBB or #RGB or a color name). - :alpha: - The color transparency [0...1], default is opaque. - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromHtml('#ff8000')) - '(1, 0.501961, 0, 1)' - >>> str(Color.NewFromHtml('ff8000')) - '(1, 0.501961, 0, 1)' - >>> str(Color.NewFromHtml('#f60')) - '(1, 0.4, 0, 1)' - >>> str(Color.NewFromHtml('f60')) - '(1, 0.4, 0, 1)' - >>> str(Color.NewFromHtml('lemonchiffon')) - '(1, 0.980392, 0.803922, 1)' - >>> str(Color.NewFromHtml('#ff8000', 0.5)) - '(1, 0.501961, 0, 0.5)' - - ''' - return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) - - @staticmethod - def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): - '''Create a new instance based on the specifed PIL color. - - Parameters: - :pil: - A PIL compatible color representation (0xBBGGRR) - :alpha: - The color transparency [0...1], default is opaque - :wref: - The whitepoint reference, default is 2° D65. - - Returns: - A grapefruit.Color instance. - - >>> str(Color.NewFromPil(0x0080ff)) - '(1, 0.501961, 0, 1)' - >>> str(Color.NewFromPil(0x0080ff, 0.5)) - '(1, 0.501961, 0, 0.5)' - - ''' - return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) - - def __GetAlpha(self): - return self.__a - alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') - - def __GetWRef(self): - return self.__wref - whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') - - def __GetRGB(self): - return self.__rgb - rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') - - def __GetHue(self): - return self.__hsl[0] - hue = property(fget=__GetHue, doc='The hue of this color.') - - def __GetHSL(self): - return self.__hsl - hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') - - def __GetHSV(self): - h, s, v = Color.RgbToHsv(*self.__rgb) - return (self.__hsl[0], s, v) - hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') - - def __GetYIQ(self): - return Color.RgbToYiq(*self.__rgb) - yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') - - def __GetYUV(self): - return Color.RgbToYuv(*self.__rgb) - yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') - - def __GetXYZ(self): - return Color.RgbToXyz(*self.__rgb) - xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') - - def __GetLAB(self): - return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) - lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') - - def __GetCMY(self): - return Color.RgbToCmy(*self.__rgb) - cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') - - def __GetCMYK(self): - return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) - cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') - - def __GetHTML(self): - return Color.RgbToHtml(*self.__rgb) - html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') - - def __GetPIL(self): - return Color.RgbToPil(*self.__rgb) - pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') - - def __GetwebSafe(self): - return Color.RgbToWebSafe(*self.__rgb) - webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') - - def __GetGreyscale(self): - return Color.RgbToGreyscale(*self.rgb) - greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') - - def ColorWithAlpha(self, alpha): - '''Create a new instance based on this one with a new alpha value. - - Parameters: - :alpha: - The transparency of the new color [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) - (1.0, 0.5, 0.0, 0.5) - - ''' - return Color(self.__rgb, 'rgb', alpha, self.__wref) - - def ColorWithWhiteRef(self, wref, labAsRef=False): - '''Create a new instance based on this one with a new white reference. - - Parameters: - :wref: - The whitepoint reference. - :labAsRef: - If True, the L*a*b* values of the current instance are used as reference - for the new color; otherwise, the RGB values are used as reference. - - Returns: - A grapefruit.Color instance. - - - >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) - - >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) - >>> c2.rgb - (1.0, 0.5, 0.0) - >>> '(%g, %g, %g)' % c2.whiteRef - '(0.96721, 1, 0.81428)' - - >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) - >>> '(%g, %g, %g)' % c2.rgb - '(1.01463, 0.490339, -0.148131)' - >>> '(%g, %g, %g)' % c2.whiteRef - '(0.96721, 1, 0.81428)' - >>> '(%g, %g, %g)' % c.lab - '(66.9518, 0.43084, 0.739692)' - >>> '(%g, %g, %g)' % c2.lab - '(66.9518, 0.43084, 0.739693)' - - ''' - if labAsRef: - l, a, b = self.__GetLAB() - return Color.NewFromLab(l, a, b, self.__a, wref) - else: - return Color(self.__rgb, 'rgb', self.__a, wref) - - def ColorWithHue(self, hue): - '''Create a new instance based on this one with a new hue. - - Parameters: - :hue: - The hue of the new color [0...360]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60) - (1.0, 1.0, 0.0, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl - (60, 1, 0.5) - - ''' - h, s, l = self.__hsl - return Color((hue, s, l), 'hsl', self.__a, self.__wref) - - def ColorWithSaturation(self, saturation): - '''Create a new instance based on this one with a new saturation value. - - .. note:: - - The saturation is defined for the HSL mode. - - Parameters: - :saturation: - The saturation of the new color [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) - (0.75, 0.5, 0.25, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl - (30, 0.5, 0.5) - - ''' - h, s, l = self.__hsl - return Color((h, saturation, l), 'hsl', self.__a, self.__wref) - - def ColorWithLightness(self, lightness): - '''Create a new instance based on this one with a new lightness value. - - Parameters: - :lightness: - The lightness of the new color [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) - (0.5, 0.25, 0.0, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl - (30, 1, 0.25) - - ''' - h, s, l = self.__hsl - return Color((h, s, lightness), 'hsl', self.__a, self.__wref) - - def DarkerColor(self, level): - '''Create a new instance based on this one but darker. - - Parameters: - :level: - The amount by which the color should be darkened to produce - the new one [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) - (0.5, 0.25, 0.0, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl - (30, 1, 0.25) - - ''' - h, s, l = self.__hsl - return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) - - def LighterColor(self, level): - '''Create a new instance based on this one but lighter. - - Parameters: - :level: - The amount by which the color should be lightened to produce - the new one [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25) - (1.0, 0.75, 0.5, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl - (30, 1, 0.75) - - ''' - h, s, l = self.__hsl - return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) - - def Saturate(self, level): - '''Create a new instance based on this one but more saturated. - - Parameters: - :level: - The amount by which the color should be saturated to produce - the new one [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) - (0.875, 0.5, 0.125, 1.0) - >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl - (30, 0.75, 0.5) - - ''' - h, s, l = self.__hsl - return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) - - def Desaturate(self, level): - '''Create a new instance based on this one but less saturated. - - Parameters: - :level: - The amount by which the color should be desaturated to produce - the new one [0...1]. - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) - (0.625, 0.5, 0.375, 1.0) - >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl - (30, 0.25, 0.5) - - ''' - h, s, l = self.__hsl - return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) - - def WebSafeDither(self): - '''Return the two websafe colors nearest to this one. - - Returns: - A tuple of two grapefruit.Color instances which are the two - web safe colors closest this one. - - >>> c = Color.NewFromRgb(1.0, 0.45, 0.0) - >>> c1, c2 = c.WebSafeDither() - >>> str(c1) - '(1, 0.4, 0, 1)' - >>> str(c2) - '(1, 0.6, 0, 1)' - - ''' - return ( - Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), - Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) - - def Gradient(self, target, steps=100): - '''Create a list with the gradient colors between this and the other color. - - Parameters: - :target: - The grapefruit.Color at the other end of the gradient. - :steps: - The number of gradients steps to create. - - - Returns: - A list of grapefruit.Color instances. - - >>> c1 = Color.NewFromRgb(1.0, 0.0, 0.0, alpha=1) - >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) - >>> c1.Gradient(c2, 3) - [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (0.25, 0.75, 0.0, 0.25)] - - ''' - gradient = [] - rgba1 = self.__rgb + (self.__a,) - rgba2 = target.__rgb + (target.__a,) - - steps += 1 - for n in xrange(1, steps): - d = 1.0*n/steps - r = (rgba1[0]*(1-d)) + (rgba2[0]*d) - g = (rgba1[1]*(1-d)) + (rgba2[1]*d) - b = (rgba1[2]*(1-d)) + (rgba2[2]*d) - a = (rgba1[3]*(1-d)) + (rgba2[3]*d) - - gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) - - return gradient - - def ComplementaryColor(self, mode='ryb'): - '''Create a new instance which is the complementary color of this one. - - Parameters: - :mode: - Select which color wheel to use for the generation (ryb/rgb). - - - Returns: - A grapefruit.Color instance. - - >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor() - (0.0, 0.5, 1.0, 1.0) - >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor().hsl - (210, 1, 0.5) - - ''' - h, s, l = self.__hsl - - if mode == 'ryb': h = Color.RgbToRyb(h) - h = (h+180)%360 - if mode == 'ryb': h = Color.RybToRgb(h) - - return Color((h, s, l), 'hsl', self.__a, self.__wref) - - def MonochromeScheme(self): - '''Return 4 colors in the same hue with varying saturation/lightness. - - Returns: - A tuple of 4 grapefruit.Color in the same hue as this one, - with varying saturation/lightness. - - >>> c = Color.NewFromHsl(30, 0.5, 0.5) - >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] - ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] - - ''' - def _wrap(x, min, thres, plus): - if (x-min) < thres: return x + plus - else: return x-min - - h, s, l = self.__hsl - - s1 = _wrap(s, 0.3, 0.1, 0.3) - l1 = _wrap(l, 0.5, 0.2, 0.3) - - s2 = s - l2 = _wrap(l, 0.2, 0.2, 0.6) - - s3 = s1 - l3 = max(0.2, l + (1-l)*0.2) - - s4 = s - l4 = _wrap(l, 0.5, 0.2, 0.3) - - return ( - Color((h, s1, l1), 'hsl', self.__a, self.__wref), - Color((h, s2, l2), 'hsl', self.__a, self.__wref), - Color((h, s3, l3), 'hsl', self.__a, self.__wref), - Color((h, s4, l4), 'hsl', self.__a, self.__wref)) - - def TriadicScheme(self, angle=120, mode='ryb'): - '''Return two colors forming a triad or a split complementary with this one. - - Parameters: - :angle: - The angle between the hues of the created colors. - The default value makes a triad. - :mode: - Select which color wheel to use for the generation (ryb/rgb). - - Returns: - A tuple of two grapefruit.Color forming a color triad with - this one or a split complementary. - - >>> c1 = Color.NewFromHsl(30, 1, 0.5) - - >>> c2, c3 = c1.TriadicScheme() - >>> c2.hsl - (150.0, 1, 0.5) - >>> c3.hsl - (270.0, 1, 0.5) - - >>> c2, c3 = c1.TriadicScheme(40) - >>> c2.hsl - (190.0, 1, 0.5) - >>> c3.hsl - (230.0, 1, 0.5) - - ''' - h, s, l = self.__hsl - angle = min(angle, 120) / 2.0 - - if mode == 'ryb': h = Color.RgbToRyb(h) - h += 180 - h1 = (h - angle) % 360 - h2 = (h + angle) % 360 - if mode == 'ryb': - h1 = Color.RybToRgb(h1) - h2 = Color.RybToRgb(h2) - - return ( - Color((h1, s, l), 'hsl', self.__a, self.__wref), - Color((h2, s, l), 'hsl', self.__a, self.__wref)) - - def TetradicScheme(self, angle=30, mode='ryb'): - '''Return three colors froming a tetrad with this one. - - Parameters: - :angle: - The angle to substract from the adjacent colors hues [-90...90]. - You can use an angle of zero to generate a square tetrad. - :mode: - Select which color wheel to use for the generation (ryb/rgb). - - Returns: - A tuple of three grapefruit.Color forming a color tetrad with - this one. - - >>> col = Color.NewFromHsl(30, 1, 0.5) - >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] - [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] - - ''' - h, s, l = self.__hsl - - if mode == 'ryb': h = Color.RgbToRyb(h) - h1 = (h + 90 - angle) % 360 - h2 = (h + 180) % 360 - h3 = (h + 270 - angle) % 360 - if mode == 'ryb': - h1 = Color.RybToRgb(h1) - h2 = Color.RybToRgb(h2) - h3 = Color.RybToRgb(h3) - - return ( - Color((h1, s, l), 'hsl', self.__a, self.__wref), - Color((h2, s, l), 'hsl', self.__a, self.__wref), - Color((h3, s, l), 'hsl', self.__a, self.__wref)) - - def AnalogousScheme(self, angle=30, mode='ryb'): - '''Return two colors analogous to this one. - - Args: - :angle: - The angle between the hues of the created colors and this one. - :mode: - Select which color wheel to use for the generation (ryb/rgb). - - Returns: - A tuple of grapefruit.Colors analogous to this one. - - >>> c1 = Color.NewFromHsl(30, 1, 0.5) - - >>> c2, c3 = c1.AnalogousScheme() - >>> c2.hsl - (330, 1, 0.5) - >>> c3.hsl - (90, 1, 0.5) - - >>> c2, c3 = c1.AnalogousScheme(10) - >>> c2.hsl - (20, 1, 0.5) - >>> c3.hsl - (40, 1, 0.5) - - ''' - h, s, l = self.__hsl - - if mode == 'ryb': h = Color.RgbToRyb(h) - h += 360 - h1 = (h - angle) % 360 - h2 = (h + angle) % 360 - if mode == 'ryb': - h1 = Color.RybToRgb(h1) - h2 = Color.RybToRgb(h2) - - return (Color((h1, s, l), 'hsl', self.__a, self.__wref), - Color((h2, s, l), 'hsl', self.__a, self.__wref)) - - def AlphaBlend(self, other): - '''Alpha-blend this color on the other one. - - Args: - :other: - The grapefruit.Color to alpha-blend with this one. - - Returns: - A grapefruit.Color instance which is the result of alpha-blending - this color on the other one. - - >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) - >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) - >>> c3 = c1.AlphaBlend(c2) - >>> str(c3) - '(1, 0.875, 0.75, 0.84)' - - ''' - # get final alpha channel - fa = self.__a + other.__a - (self.__a * other.__a) - - # get percentage of source alpha compared to final alpha - if fa==0: sa = 0 - else: sa = min(1.0, self.__a/other.__a) - - # destination percentage is just the additive inverse - da = 1.0 - sa - - sr, sg, sb = [v * sa for v in self.__rgb] - dr, dg, db = [v * da for v in other.__rgb] - - return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) - - def Blend(self, other, percent=0.5): - '''Blend this color with the other one. - - Args: - :other: - the grapefruit.Color to blend with this one. - - Returns: - A grapefruit.Color instance which is the result of blending - this color on the other one. - - >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) - >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) - >>> c3 = c1.Blend(c2) - >>> str(c3) - '(1, 0.75, 0.5, 0.4)' - - ''' - dest = 1.0 - percent - rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) - a = (self.__a * percent) + (other.__a * dest) - return Color(rgb, 'rgb', a, self.__wref) - -def _test(): - import doctest - reload(doctest) - doctest.testmod() - -if __name__=='__main__': - _test() diff --git a/forum_modules/pgfulltext/DISABLED b/forum_modules/pgfulltext/DISABLED deleted file mode 100644 index e69de29b..00000000 diff --git a/forum_modules/pgfulltext/__init__.py b/forum_modules/pgfulltext/__init__.py deleted file mode 100644 index 8215e1a9..00000000 --- a/forum_modules/pgfulltext/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -NAME = 'Postgresql Full Text Search' -DESCRIPTION = "Enables PostgreSql full text search functionality." - -try: - import psycopg2 - CAN_ENABLE = True -except: - CAN_ENABLE = False - \ No newline at end of file diff --git a/forum_modules/pgfulltext/handlers.py b/forum_modules/pgfulltext/handlers.py deleted file mode 100644 index f4a7a3b2..00000000 --- a/forum_modules/pgfulltext/handlers.py +++ /dev/null @@ -1,11 +0,0 @@ -from forum.models import Question - -def question_search(keywords, orderby): - return Question.objects.filter(deleted=False).extra( - select={ - 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)", - }, - where=["tsv @@ plainto_tsquery(%s)"], - params=[keywords], - select_params=[keywords] - ).order_by(orderby, '-ranking') \ No newline at end of file diff --git a/forum_modules/pgfulltext/management.py b/forum_modules/pgfulltext/management.py deleted file mode 100644 index 15ba3bd7..00000000 --- a/forum_modules/pgfulltext/management.py +++ /dev/null @@ -1,30 +0,0 @@ -import os - -from django.db import connection, transaction -from django.conf import settings - -import forum.models - -if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ): - from django.db.models.signals import post_syncdb - - def setup_pgfulltext(sender, **kwargs): - if sender == forum.models: - install_pg_fts() - - post_syncdb.connect(setup_pgfulltext) - -def install_pg_fts(): - f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r') - - try: - try: - cursor = connection.cursor() - cursor.execute(f.read()) - transaction.commit_unless_managed() - except: - pass - finally: - cursor.close() - - f.close() diff --git a/forum_modules/pgfulltext/pg_fts_install.sql b/forum_modules/pgfulltext/pg_fts_install.sql deleted file mode 100644 index 72eca516..00000000 --- a/forum_modules/pgfulltext/pg_fts_install.sql +++ /dev/null @@ -1,38 +0,0 @@ -ALTER TABLE question ADD COLUMN tsv tsvector; - -CREATE OR REPLACE FUNCTION public.create_plpgsql_language () - RETURNS TEXT - AS $$ - CREATE LANGUAGE plpgsql; - SELECT 'language plpgsql created'::TEXT; - $$ -LANGUAGE 'sql'; - -SELECT CASE WHEN - (SELECT true::BOOLEAN - FROM pg_language - WHERE lanname='plpgsql') - THEN - (SELECT 'language already installed'::TEXT) - ELSE - (SELECT public.create_plpgsql_language()) - END; - -DROP FUNCTION public.create_plpgsql_language (); - -CREATE OR REPLACE FUNCTION set_question_tsv() RETURNS TRIGGER AS $$ -begin - new.tsv := - setweight(to_tsvector('english', coalesce(new.tagnames,'')), 'A') || - setweight(to_tsvector('english', coalesce(new.title,'')), 'B') || - setweight(to_tsvector('english', coalesce(new.summary,'')), 'C'); - RETURN new; -end -$$ LANGUAGE plpgsql; - -CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE -ON question FOR EACH ROW EXECUTE PROCEDURE set_question_tsv(); - - CREATE INDEX question_tsv ON question USING gin(tsv); - -UPDATE question SET title = title; diff --git a/forum_modules/robotstxt/DISABLED b/forum_modules/robotstxt/DISABLED deleted file mode 100755 index e69de29b..00000000 diff --git a/forum_modules/robotstxt/__init__.py b/forum_modules/robotstxt/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/forum_modules/robotstxt/templates/robots.txt b/forum_modules/robotstxt/templates/robots.txt deleted file mode 100755 index 574fc315..00000000 --- a/forum_modules/robotstxt/templates/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / \ No newline at end of file diff --git a/forum_modules/robotstxt/urls.py b/forum_modules/robotstxt/urls.py deleted file mode 100644 index 79a6d84c..00000000 --- a/forum_modules/robotstxt/urls.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template - -urlpatterns = patterns('', - (r'^robots.txt$', direct_to_template, {'template': 'modules/robotsdennyall/robots.txt'}), -) diff --git a/forum_modules/sphinxfulltext/DISABLED b/forum_modules/sphinxfulltext/DISABLED deleted file mode 100644 index e69de29b..00000000 diff --git a/forum_modules/sphinxfulltext/__init__.py b/forum_modules/sphinxfulltext/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/forum_modules/sphinxfulltext/dependencies.py b/forum_modules/sphinxfulltext/dependencies.py deleted file mode 100644 index 046ebfc5..00000000 --- a/forum_modules/sphinxfulltext/dependencies.py +++ /dev/null @@ -1,2 +0,0 @@ -DJANGO_APPS = ('djangosphinx', ) - diff --git a/forum_modules/sphinxfulltext/handlers.py b/forum_modules/sphinxfulltext/handlers.py deleted file mode 100644 index 226acf72..00000000 --- a/forum_modules/sphinxfulltext/handlers.py +++ /dev/null @@ -1,4 +0,0 @@ -from forum.models import Question - -def question_search(keywords, orderby): - return Question.search.query(keywords) \ No newline at end of file diff --git a/forum_modules/sphinxfulltext/models.py b/forum_modules/sphinxfulltext/models.py deleted file mode 100644 index a188728d..00000000 --- a/forum_modules/sphinxfulltext/models.py +++ /dev/null @@ -1,11 +0,0 @@ -from forum.models import Question -from django.conf import settings -from djangosphinx.manager import SphinxSearch - -from djangosphinx.models import SphinxSearch - -Question.add_to_class('search', SphinxSearch( - index=' '.join(settings.SPHINX_SEARCH_INDICES), - mode='SPH_MATCH_ALL', - ) - ) diff --git a/forum_modules/sphinxfulltext/settings.py b/forum_modules/sphinxfulltext/settings.py deleted file mode 100644 index 564404c6..00000000 --- a/forum_modules/sphinxfulltext/settings.py +++ /dev/null @@ -1,5 +0,0 @@ -SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation -SPHINX_SEARCH_INDICES=('askbot',) #a tuple of index names remember about a comma after the -#last item, especially if you have just one :) -SPHINX_SERVER='localhost' -SPHINX_PORT=3312 diff --git a/livesettings/README b/livesettings/README new file mode 100644 index 00000000..6fe70cc5 --- /dev/null +++ b/livesettings/README @@ -0,0 +1,4 @@ +this is very slightly forked version of django-livesettings +for use in the askbot forum project + +will attempt to re-merge into the original django-livesettings diff --git a/rmpyc b/rmpyc deleted file mode 100755 index 014575f6..00000000 --- a/rmpyc +++ /dev/null @@ -1 +0,0 @@ -rm `find . -name '*.pyc'` diff --git a/run b/run deleted file mode 100755 index 06279161..00000000 --- a/run +++ /dev/null @@ -1 +0,0 @@ -python manage.py runserver `hostname -i`:8000 diff --git a/run-tests b/run-tests deleted file mode 100755 index 642b8a0a..00000000 --- a/run-tests +++ /dev/null @@ -1 +0,0 @@ -python manage.py test forum -- cgit v1.2.3-1-g7c22