diff options
27 files changed, 265 insertions, 630 deletions
@@ -13,6 +13,5 @@ pip-log.txt *.zip tmp/* load -run forum/skins/default/media/js/flot forum/skins/common/media/js/closure/google-closure @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> - <name>osqa</name> + <name>askbot</name> <comment></comment> <projects> </projects> diff --git a/.pydevproject b/.pydevproject index f7f3fd1a..2ba60488 100755 --- a/.pydevproject +++ b/.pydevproject @@ -5,6 +5,6 @@ <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> -<path>/osqa</path> +<path>/askbot</path> </pydev_pathproperty> </pydev_project> diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py deleted file mode 100755 index eee0c870..00000000 --- a/forum/authentication/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -import re -from forum.modules import get_modules_script_classes -from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext - -class ConsumerAndContext: - def __init__(self, id, consumer, context): - self.id = id - self._consumer = consumer - - context.id = id - self.context = context - - @property - def consumer(self): - return self._consumer() - -consumers = dict([ - (re.sub('AuthConsumer$', '', name).lower(), cls) for name, cls - in get_modules_script_classes('authentication', AuthenticationConsumer).items() - if not re.search('AbstractAuthConsumer$', name) - ]) - -contexts = dict([ - (re.sub('AuthContext$', '', name).lower(), cls) for name, cls - in get_modules_script_classes('authentication', ConsumerTemplateContext).items() - ]) - -AUTH_PROVIDERS = dict([ - (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys() - if name in contexts - ]) diff --git a/forum/authentication/base.py b/forum/authentication/base.py deleted file mode 100755 index 99005866..00000000 --- a/forum/authentication/base.py +++ /dev/null @@ -1,44 +0,0 @@ - -class AuthenticationConsumer(object): - - def prepare_authentication_request(self, request, redirect_to): - raise NotImplementedError() - - def process_authentication_request(self, response): - raise NotImplementedError() - - def get_user_data(self, key): - raise NotImplementedError() - - -class ConsumerTemplateContext(object): - """ - Class that provides information about a certain authentication provider context in the signin page. - - class attributes: - - mode - one of BIGICON, SMALLICON, FORM - - human_name - the human readable name of the provider - - extra_js - some providers require us to load extra javascript on the signin page for them to work, - this is the place to add those files in the form of a list - - extra_css - same as extra_js but for css files - """ - mode = '' - weight = 500 - human_name = '' - extra_js = [] - extra_css = [] - show_to_logged_in_user = True - - @classmethod - def readable_key(cls, key): - return key.key - -class InvalidAuthentication(Exception): - def __init__(self, message): - self.message = message - -
\ No newline at end of file diff --git a/forum/authentication/forms.py b/forum/authentication/forms.py deleted file mode 100755 index 7fa06b01..00000000 --- a/forum/authentication/forms.py +++ /dev/null @@ -1,73 +0,0 @@ -from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm -from forum.models import EmailFeedSetting, Question, User -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext as _ -from django.utils.safestring import mark_safe -from django import forms -from forum.forms import EditUserEmailFeedsForm -import logging - -class SimpleRegistrationForm(forms.Form): - next = NextUrlField() - username = UserNameField() - email = UserEmailField() - -class TemporaryLoginRequestForm(forms.Form): - def __init__(self, data=None): - super(TemporaryLoginRequestForm, self).__init__(data) - self.user_cache = None - - email = forms.EmailField( - required=True, - label=_("Your account email"), - error_messages={ - 'required': _("You cannot leave this field blank"), - 'invalid': _('please enter a valid email address'), - } - ) - - def clean_email(self): - try: - user = User.objects.get(email=self.cleaned_data['email']) - except: - raise forms.ValidationError(_("Sorry, but this email is not on our database.")) - - self.user_cache = user - return self.cleaned_data['email'] - - -class SimpleEmailSubscribeForm(forms.Form): - SIMPLE_SUBSCRIBE_CHOICES = ( - ('y',_('okay, let\'s try!')), - ('n',_('no OSQA community email please, thanks')) - ) - subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \ - error_messages={'required':_('please choose one of the options above')}, - choices=SIMPLE_SUBSCRIBE_CHOICES) - - def save(self,user=None): - EFF = EditUserEmailFeedsForm - if self.cleaned_data['subscribe'] == 'y': - email_settings_form = EFF() - logging.debug('%s wants to subscribe' % user.username) - else: - email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL) - email_settings_form.save(user,save_unbound=True) - -class ChangePasswordForm(SetPasswordForm): - """ change password form """ - oldpw = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}), - label=mark_safe(_('Current password'))) - - def __init__(self, data=None, user=None, *args, **kwargs): - if user is None: - raise TypeError("Keyword argument 'user' must be supplied") - super(ChangePasswordForm, self).__init__(data, *args, **kwargs) - self.user = user - - def clean_oldpw(self): - """ test old password """ - if not self.user.check_password(self.cleaned_data['oldpw']): - raise forms.ValidationError(_("Old password is incorrect. \ - Please enter the correct password.")) - return self.cleaned_data['oldpw'] diff --git a/forum/documentation/HOW_TO_DEBUG b/forum/documentation/HOW_TO_DEBUG index ba36198a..fbbdb1f7 100644 --- a/forum/documentation/HOW_TO_DEBUG +++ b/forum/documentation/HOW_TO_DEBUG @@ -18,17 +18,17 @@ logging.debug('') #this will add time, line number, function and file record #etc - take a look at http://docs.python.org/library/logging.html ------------------------------- -in OSQA logging is currently set up in settings_local.py.dist +in Askbot logging is currently set up in settings_local.py.dist please update it if you need - in older revs logging strings have less info messages of interest can be grepped out of the log file by module/file/function name e.g. to take out all django_authopenid logs run: ->grep 'osqa\/django_authopenid' log/django.osqa.log | sed 's/^.*MSG: //' +>grep 'askbot\/django_authopenid' log/django.askbot.log | sed 's/^.*MSG: //' in the example above 'sed' call truncates out a long prefix and makes output look more meaningful 2) DJANGO DEBUG TOOLBAR -osqa works with django debug toolbar +askbot works with django debug toolbar if debugging under apache server, check that debug toolbar media is loaded correctly if toolbar is enabled but you do not see it, possibly some Alias statement diff --git a/forum/documentation/INSTALL b/forum/documentation/INSTALL index 6ac9ea68..73714566 100644 --- a/forum/documentation/INSTALL +++ b/forum/documentation/INSTALL @@ -118,6 +118,7 @@ python manage.py syncdb #create tables for anything not under control of migrati python manage.py migrate forum #run migration command - will apply all migrations in sequence Now you are ready to test your installation: + python manage.py runserver `hostname -i`:8000 (choose another port number if you wish) @@ -142,7 +143,6 @@ And then run : python manage.py syncdb python manage.py migrate - 3. Running Askbot on the development server Run "python manage.py runserver" to startup django diff --git a/forum/documentation/INSTALL.pip b/forum/documentation/INSTALL.pip index 92b1c7fa..2f817ff8 100644 --- a/forum/documentation/INSTALL.pip +++ b/forum/documentation/INSTALL.pip @@ -12,7 +12,7 @@ git://github.com/johnl/deb-sphinx-search.git * Install a virtual environment OUTSIDE of this directory: - pip install -E ~/env -r osqa-requirements.txt + pip install -E ~/env -r askbot-requirements.txt [there is discussion on the pinax forums about what it should be outside the source directory] diff --git a/forum/documentation/INSTALL.webfaction b/forum/documentation/INSTALL.webfaction index 401971a0..a449ffe6 100644 --- a/forum/documentation/INSTALL.webfaction +++ b/forum/documentation/INSTALL.webfaction @@ -1,28 +1,30 @@ -Detailed instructions for installing OSQA on WebFaction +NOTE: this file may be a bit out of date + +Detailed instructions for installing Askbot on WebFaction Adapted from http://code.pinaxproject.com/wiki/DetailedPinaxWebfaction/ Please email turian at gmail with any updates or corrections. -Installing OSQA on Webfaction +Installing Askbot on Webfaction ------------------------------------ -Details the steps for setting up OSQA on a Webfaction shared-hosting +Details the steps for setting up Askbot on a Webfaction shared-hosting account, including email setup, using Apache2, mod_wsgi, psycopg2. If you want to search-and-replace through this file, you will need to replace: - osqa_server [name of Webfaction application, which will be in ~/webapps/] - osqa_static [name of Webfaction application for static media serving] - DOMAIN.com [domain name for OSQA site] + askbot_server [name of Webfaction application, which will be in ~/webapps/] + askbot_static [name of Webfaction application for static media serving] + DOMAIN.com [domain name for Askbot site] PORT [port number assigned by WebFaction to your mod_wsgi application] - SITENAME [name you give the OSQA site, which will contain the apache logs] - MYOSQA [name of the OSQA project] + SITENAME [name you give the Askbot site, which will contain the apache logs] + myaskbot [name of the Askbot project] MAILBOX_USERNAME [username you give the email address] MAILBOX_PASSWORD [password that webfaction gives to this email username] - OSQADATABASE_NAME [username you give the database] - OSQADATABASE_PASSWORD [password that webfaction gives to this database] - ~/envs/osqa [directory for the OSQA python environment, grep for 'env'] + AskbotDATABASE_NAME [username you give the database] + AskbotDATABASE_PASSWORD [password that webfaction gives to this database] + ~/envs/askbot [directory for the Askbot python environment, grep for 'env'] USERNAME [your WebFaction username] Some things I'm not sure about: @@ -30,7 +32,7 @@ Some things I'm not sure about: Here's what I don't know how to do: * Set up a nginx server for static media. * Configure sphinx search - * Use PostgreSQL, not MySQL: http://osqa.net/question/13/can-i-use-osqa-with-postgresql + * Use PostgreSQL, not MySQL: http://askbot.net/question/13/can-i-use-askbot-with-postgresql Webfaction Control Panel @@ -46,7 +48,7 @@ for you, website/subdomain and associate the two for you.) Let's call the domain DOMAIN.com. Create a new Webfaction application with a "Type:" of "mod_wsgi - 2.5/Python2.5", naming it "osqa_server". (These instructions + 2.5/Python2.5", naming it "askbot_server". (These instructions might also work with mod_wsgi 2.0, if you like.) https://panel.webfaction.com/app_/list @@ -55,8 +57,8 @@ for you, website/subdomain and associate the two for you.) it PORT. Create a new Webfaction website which will associate the subdomain - with the new osqa_server application. Give it name SITENAME, at least one - domain, and set it to use the osqa_server application for the site's + with the new askbot_server application. Give it name SITENAME, at least one + domain, and set it to use the askbot_server application for the site's root location, "/". https://panel.webfaction.com/site/list @@ -64,20 +66,20 @@ for you, website/subdomain and associate the two for you.) You will need to create a database, typically one for each project you create. Change the type to PostgreSql and modify the name (it defaults to your webfaction account name) by adding an underscore - and a project-specific identifier such as "_osqa". Before + and a project-specific identifier such as "_askbot". Before leaving this section of the control panel, you may wish to change the password. https://panel.webfaction.com/database/create - Call these OSQADATABASE_NAME and OSQADATABASE_PASSWORD. + Call these AskbotDATABASE_NAME and AskbotDATABASE_PASSWORD. Save the database password for later. [The following I haven't figured out yet] You will probably want to add a static media server. This is a Webfaction application. I created one of type "Static only (no - .htaccess)" and with the name of "osqa_static". + .htaccess)" and with the name of "askbot_static". https://panel.webfaction.com/app_/create @@ -96,15 +98,15 @@ for you, website/subdomain and associate the two for you.) https://panel.webfaction.com/email/list -OSQA Software +Askbot Software -------------- Log onto webfaction and get the code. I use my fork because I have a simple pip installation: - git://github.com/turian/osqa.git + git://github.com/turian/askbot.git In my situation, I keep source code in ~/utils/src, create - virtual environments in ~/envs/osqa, and create Pinax projects in - ~/webapps/osqa_server/projects. + virtual environments in ~/envs/askbot, and create Pinax projects in + ~/webapps/askbot_server/projects. You will need pip + virtualenv installed: @@ -112,15 +114,15 @@ OSQA Software easy_install --prefix=~/utils/ virtualenv cd ~/utils/src/ - git clone git://github.com/turian/osqa.git - cd osqa + git clone git://github.com/turian/askbot.git + cd askbot # We need python2.5 to be compatible with WSGI - python2.5 ~/utils/bin/pip install -E ~/envs/osqa -r osqa-requirements.txt - source ~/envs/osqa/bin/activate + python2.5 ~/utils/bin/pip install -E ~/envs/askbot -r askbot-requirements.txt + source ~/envs/askbot/bin/activate # [Optional] If you want a MySQL database - easy_install-2.5 --prefix ~/envs/osqa/ mysql-python + easy_install-2.5 --prefix ~/envs/askbot/ mysql-python Additional Software ------------------- @@ -147,39 +149,39 @@ Additional Software Create a Project ---------------- - In Pinax, you clone a project from OSQA. - However, OSQA we just copy it. + In Pinax, you clone a project from Askbot. + However, Askbot we just copy it. - cd ~/webapps/osqa_server + cd ~/webapps/askbot_server mkdir projects cd projects - cp -R ~/utils/src/osqa MYOSQA - cd MYOSQA - export OSQAPROJECT=`pwd` + cp -R ~/utils/src/askbot myaskbot + cd myaskbot + export AskbotPROJECT=`pwd` - Make some directories, as described in the OSQA INSTALL file: + Make some directories, as described in the Askbot INSTALL file: [okay I haven't actually done this yet] -# mkdir -p $OSQASITE/upfiles/ -# mkdir -p $OSQALOG -# sudo chown -R `whoami`:www-data $OSQASITE -# sudo chown -R `whoami`:www-data $OSQALOG -# chmod -R g+w $OSQASITE/upfiles -# chmod -R g+w $OSQALOG +# mkdir -p $AskbotSITE/upfiles/ +# mkdir -p $AskbotLOG +# sudo chown -R `whoami`:www-data $AskbotSITE +# sudo chown -R `whoami`:www-data $AskbotLOG +# chmod -R g+w $AskbotSITE/upfiles +# chmod -R g+w $AskbotLOG Edit the settings files: - cd $OSQAPROJECT + cd $AskbotPROJECT cp settings_local.py.dist settings_local.py vi settings_local.py settings.py Pay attention to the following settings: DATABASE_ENGINE = 'mysql' - DATABASE_NAME = 'OSQADATABASE_NAME' - DATABASE_USER = 'OSQADATABASE_NAME' - DATABASE_PASSWORD = 'OSQADATABASE_PASSWORD' + DATABASE_NAME = 'AskbotDATABASE_NAME' + DATABASE_USER = 'AskbotDATABASE_NAME' + DATABASE_PASSWORD = 'AskbotDATABASE_PASSWORD' EMAIL_HOST='smtp.webfaction.com' EMAIL_HOST_USER='MAILBOX_USERNAME' @@ -197,7 +199,7 @@ Create a Project Create a directory for logs: - cd $OSQAPROJECT + cd $AskbotPROJECT mkdir log Modify mail cron scripts "cron/send_email_alerts" as follows: @@ -206,8 +208,8 @@ Create a Project #!/bin/sh - WORKON_HOME=~/envs/osqa - PROJECT_ROOT=~/webapps/osqa_server/projects/MYOSQA/ + WORKON_HOME=~/envs/askbot + PROJECT_ROOT=~/webapps/askbot_server/projects/myaskbot/ # activate virtual environment . $WORKON_HOME/bin/activate @@ -217,7 +219,7 @@ Create a Project Use command "crontab -e" to add this script to your cron file, to run twice a day:: - 1 0,12 * * * ~/webapps/osqa_server/projects/MYOSQA/cron/send_email_alerts + 1 0,12 * * * ~/webapps/askbot_server/projects/myaskbot/cron/send_email_alerts [Configure sphinx] @@ -229,8 +231,8 @@ Create a Project Build media directory links within the project and create symbolic links on the static media server. python manage.py build_media -all - mkdir ~/webapps/OSQA_STATIC/MYOSQA - ln -sd ~/webapps/osqa_server/projects/MYOSQA/site_media ~/webapps/OSQA_STATIC/MYOSQA/site_media + mkdir ~/webapps/Askbot_STATIC/myaskbot + ln -sd ~/webapps/askbot_server/projects/myaskbot/site_media ~/webapps/Askbot_STATIC/myaskbot/site_media Set up the badges: @@ -249,7 +251,7 @@ Create a Project 4. Use `crontab -e` to call `cron/multi_award_badges` maybe four times an hour. - 4,19,34,49 * * * * ~/webapps/osqa_server/projects/MYOSQA/cron/multi_award_badges + 4,19,34,49 * * * * ~/webapps/askbot_server/projects/myaskbot/cron/multi_award_badges 5. Repeat steps 1-4 for `cron/once_award_badges`. @@ -257,10 +259,10 @@ Create a Project Configure Apache2 ---------------- - Edit ~/webapps/osqa_server/apache2/conf/httpd.conf as follows:: + Edit ~/webapps/askbot_server/apache2/conf/httpd.conf as follows:: ServerAdmin "MAILBOX_USERNAME@DOMAIN.com" - ServerRoot "/home/USERNAME/webapps/osqa_server/apache2" + ServerRoot "/home/USERNAME/webapps/askbot_server/apache2" ServerName DOMAIN.com LoadModule dir_module modules/mod_dir.so @@ -274,42 +276,39 @@ Configure Apache2 KeepAlive Off Listen PORT LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined - CustomLog /home/USERNAME/logs/user/access_osqa_server_log combined - ErrorLog /home/USERNAME/logs/user/error_osqa_server_log + CustomLog /home/USERNAME/logs/user/access_askbot_server_log combined + ErrorLog /home/USERNAME/logs/user/error_askbot_server_log ServerLimit 2 #SetEnvIf X-Forwarded-SSL on HTTPS=1 - WSGIPythonPath /home/USERNAME/envs/osqa/lib/python2.5/site-packages/ - WSGIScriptAlias / /home/USERNAME/webapps/osqa_server/projects/MYOSQA/osqa.wsgi + WSGIPythonPath /home/USERNAME/envs/askbot/lib/python2.5/site-packages/ + WSGIScriptAlias / /home/USERNAME/webapps/askbot_server/projects/myaskbot/django.wsgi LoadModule alias_module modules/mod_alias.so - WSGIDaemonProcess osqaWSGI user=USERNAME group=USERNAME threads=25 python-path=/home/USERNAME/envs/osqa/lib/python2.5/site-packages - WSGIProcessGroup osqaWSGI + WSGIDaemonProcess askbotWSGI user=USERNAME group=USERNAME threads=25 python-path=/home/USERNAME/envs/askbot/lib/python2.5/site-packages + WSGIProcessGroup askbotWSGI NameVirtualHost 127.0.0.1:PORT - #ErrorLog "logs/MYOSQA_2009_05_06.log" + #ErrorLog "logs/myaskbot_2009_05_06.log" SetHandler none - #Alias /site_media /home/USERNAME/webapps/static/MYOSQA/site_media + #Alias /site_media /home/USERNAME/webapps/static/myaskbot/site_media #force all content to be served as static files #otherwise django will be crunching images through itself wasting time - Alias /content/ /home/USERNAME/webapps/osqa_server/projects/MYOSQA/templates/content/ - Alias /forum/admin/media/ /home/turian/envs/osqa/lib/python2.5/site-packages/django/contrib/admin/media/ - #AliasMatch /([^/]*\.css) /home/USERNAME/webapps/osqa_server/projects/MYOSQA/templates/content/style/$1 - <Directory "/home/USERNAME/webapps/osqa_server/projects/MYOSQA/templates/content"> + Alias /content/ /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/ + Alias /forum/admin/media/ /home/turian/envs/askbot/lib/python2.5/site-packages/django/contrib/admin/media/ + #AliasMatch /([^/]*\.css) /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/style/$1 + <Directory "/home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content"> # Order deny,allow # Allow from all </Directory> If you want virtual hosts of the admin interface under HTTPS, please - look at OSQA's install file. - - Create osqa.wsgi and edit it: - cp osqa.wsgi.dist osqa.wsgi + look at Askbot's install file. - Edit ~/webapps/osqa_server/projects/MYOSQA/deploy/osqa.wsgi as follows:: + Edit ~/webapps/askbot_server/projects/myaskbot/deploy/django.wsgi as follows:: import os import sys @@ -323,13 +322,13 @@ Configure Apache2 # add the virtual environment site-packages to the path from site import addsitedir - addsitedir('/home/USERNAME/envs/osqa/lib/python2.5/site-packages') + addsitedir('/home/USERNAME/envs/askbot/lib/python2.5/site-packages') sys.path.insert(0, abspath(join(dirname(__file__), "../"))) sys.path.append(abspath(dirname(__file__))) from django.conf import settings - os.environ["DJANGO_SETTINGS_MODULE"] = "MYOSQA.settings" + os.environ["DJANGO_SETTINGS_MODULE"] = "myaskbot.settings" #print sys.path @@ -338,8 +337,8 @@ Configure Apache2 And then you're up and running with: - ~/webapps/osqa_server/apache2/bin/stop - ~/webapps/osqa_server/apache2/bin/start + ~/webapps/askbot_server/apache2/bin/stop + ~/webapps/askbot_server/apache2/bin/start You should log in to the admin interface (http://DOMAIN.com/admin/), and go to "Sites > Sites", and change the domain name that is used in diff --git a/forum/documentation/WISH_LIST b/forum/documentation/WISH_LIST index 3e383d3e..2b53662c 100644 --- a/forum/documentation/WISH_LIST +++ b/forum/documentation/WISH_LIST @@ -1,15 +1,15 @@ * smarter debug mode -* The wonder bar (integrated the search / ask functionality)
-* The authentication system ???
+* The wonder bar (integrated the search / ask functionality) +* The authentication system ??? * allow multiple logins to the same account -* allow multiple logins to the same account
-* more advanced templating/skinning system
-* per-tag email subscriptions
-* view for personalized news on the site
-* a little flag popping when there are news
-* drill-down mode for navigation by tags
-* improved admin console
-* sort out mess with profile - currently we patch django User
+* allow multiple logins to the same account +* more advanced templating/skinning system +* per-tag email subscriptions +* view for personalized news on the site +* a little flag popping when there are news +* drill-down mode for navigation by tags +* improved admin console +* sort out mess with profile - currently we patch django User * Some functionality should be moved out of the forums app, in the case that the forum app is restricted only to authenticated users: @@ -34,9 +34,9 @@ Just a joke - pick yourself a task and work on it. * validate HTML * set up loading of default settings from inside the /forum dir * automatic dependency checking for modules -* propose how to rename directory forum --> osqa +* propose how to rename directory forum --> askbot without breaking things and keeping name of the project root - named the same way - osqa + named the same way - askbot ==New features== Whoever wants - pick a feature from the WISH_LIST diff --git a/forum/importers/stackexchange/README b/forum/importers/stackexchange/README index 64d8f5fb..598a8555 100644 --- a/forum/importers/stackexchange/README +++ b/forum/importers/stackexchange/README @@ -2,10 +2,10 @@ this app's function will be to: * install it's own tables (#todo: not yet automated) * read SE xml dump into DjangoDB (automated) -* populate osqa database (automated) +* populate askbot database (automated) * remove SE tables (#todo: not done yet) -Current process to load SE data into OSQA is: +Current process to load SE data into Askbot is: ============================================== 1) backup database @@ -26,7 +26,7 @@ Current process to load SE data into OSQA is: 5) Install stackexchange models (as well as any other missing models) python manage.py syncdb -6) make sure that osqa badges are installed +6) make sure that badges are installed if not, run (example for mysql): mysql -u user -p dbname < sql_scripts/badges.sql @@ -47,7 +47,7 @@ it assumes that SE dump has been unzipped inside the tmp directory #!/bin/sh$ python manage.py flush #delete all data - mysql -u osqa -p osqa < sql_scripts/badges.sql + mysql -u askbot -p aksbot < sql_scripts/badges.sql python manage.py load_stackexchange tmp Untested parts are tagged with comments starting with diff --git a/forum/skins/README b/forum/skins/README index 5565fa83..01ef0a9d 100755 --- a/forum/skins/README +++ b/forum/skins/README @@ -10,11 +10,11 @@ default/templates - templates must be named the same way NO NEED TO CREATE ALL TEMPLATES/MEDIA FILES AT ONCE templates are resolved in the following way: -* check in skin named as in settings.OSQA_DEFAULT_SKIN +* check in skin named as in settings.ASKBOT_DEFAULT_SKIN * then skin named 'default' media is resolved with one extra option -* settings.OSQA_DEFAULT_SKIN +* settings.ASKBOT_DEFAULT_SKIN * 'default' * 'common' diff --git a/forum/skins/default/media/images/favicon.ico b/forum/skins/default/media/images/favicon.ico Binary files differdeleted file mode 100644 index 35c9e149..00000000 --- a/forum/skins/default/media/images/favicon.ico +++ /dev/null diff --git a/forum/skins/default/media/js/com.cnprog.i18n.js b/forum/skins/default/media/js/com.cnprog.i18n.js index 56ea1062..23164aab 100755 --- a/forum/skins/default/media/js/com.cnprog.i18n.js +++ b/forum/skins/default/media/js/com.cnprog.i18n.js @@ -149,7 +149,8 @@ var i18nEs = { 'questions/' : 'preguntas/', 'answers/' : 'respuestas/', 'comments/' : 'comentarios/', - 'vote/' : 'votar/' + 'vote/' : 'votar/', + 'delete/' : 'eliminar/' }; var i18n = { diff --git a/forum/skins/default/media/js/com.cnprog.post.js b/forum/skins/default/media/js/com.cnprog.post.js index 38ff8df1..7c064acf 100755 --- a/forum/skins/default/media/js/com.cnprog.post.js +++ b/forum/skins/default/media/js/com.cnprog.post.js @@ -609,7 +609,7 @@ function createComments(type) { $(this).children().each( function(i){ var comment_id = $(this).attr('id').replace('comment-',''); - var delete_url = scriptUrl + objectType + 's/' + post_id + '/' + + var delete_url = scriptUrl + $.i18n._(objectType + 's/') + post_id + '/' + $.i18n._('comments/') + comment_id + '/' + $.i18n._('delete/'); var html = $(this).html(); var CommentsClass; diff --git a/forum/skins/default/templates/base_content.html b/forum/skins/default/templates/base_content.html index fff7838d..284007d8 100644 --- a/forum/skins/default/templates/base_content.html +++ b/forum/skins/default/templates/base_content.html @@ -9,7 +9,7 @@ {% if settings.GOOGLE_SITEMAP_CODE %} <meta name="google-site-verification" content="{{ settings.GOOGLE_SITEMAP_CODE }}" /> {% endif %} - <link rel="shortcut icon" href="{% media "/media/images/favicon.ico" %}" /> + <link rel="shortcut icon" href="{% media "/media/images/favicon.gif" %}" /> <link href="{% media "/media/style/style.css" %}" rel="stylesheet" type="text/css" /> {% spaceless %} {% block forestyle %}{% endblock %} diff --git a/forum/views/__init__.py b/forum/views/__init__.py index a5f6f99d..291fee2a 100755 --- a/forum/views/__init__.py +++ b/forum/views/__init__.py @@ -3,4 +3,3 @@ import writers import commands import users import meta -import auth diff --git a/forum/views/auth.py b/forum/views/auth.py deleted file mode 100755 index a25bd18e..00000000 --- a/forum/views/auth.py +++ /dev/null @@ -1,364 +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 signin_page(request, action=None): - if action is None: - request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/') - else: - request.session['on_signin_action'] = action - request.session['on_signin_url'] = reverse('auth_action_signin', kwargs={'action': action}) - - all_providers = [provider.context for provider in AUTH_PROVIDERS.values()] - - sort = lambda c1, c2: c1.weight - c2.weight - can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user - - bigicon_providers = sorted([ - context for context in all_providers if context.mode == 'BIGICON' and can_show(context) - ], sort) - - smallicon_providers = sorted([ - context for context in all_providers if context.mode == 'SMALLICON' and can_show(context) - ], sort) - - top_stackitem_providers = sorted([ - context for context in all_providers if context.mode == 'TOP_STACK_ITEM' and can_show(context) - ], sort) - - stackitem_providers = sorted([ - context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context) - ], sort) - - try: - msg = request.session['auth_error'] - del request.session['auth_error'] - except: - msg = None - - return render_to_response( - 'auth/signin.html', - { - 'msg': msg, - 'all_providers': all_providers, - 'bigicon_providers': bigicon_providers, - 'top_stackitem_providers': top_stackitem_providers, - 'stackitem_providers': stackitem_providers, - 'smallicon_providers': smallicon_providers, - }, - RequestContext(request)) - -def prepare_provider_signin(request, provider): - force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes' - request.session['force_email_request'] = force_email_request - - if provider in AUTH_PROVIDERS: - provider_class = AUTH_PROVIDERS[provider].consumer - - try: - request_url = provider_class.prepare_authentication_request(request, - reverse('auth_provider_done', kwargs={'provider': provider})) - - return HttpResponseRedirect(request_url) - except NotImplementedError, e: - return process_provider_signin(request, provider) - except InvalidAuthentication, e: - request.session['auth_error'] = e.message - - return HttpResponseRedirect(reverse('auth_signin')) - - -def process_provider_signin(request, provider): - if provider in AUTH_PROVIDERS: - provider_class = AUTH_PROVIDERS[provider].consumer - - try: - assoc_key = provider_class.process_authentication_request(request) - except InvalidAuthentication, e: - request.session['auth_error'] = e.message - return HttpResponseRedirect(reverse('auth_signin')) - - if request.user.is_authenticated(): - if isinstance(assoc_key, (type, User)): - if request.user != assoc_key: - request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.") - else: - request.session['auth_error'] = _("You are already logged in with that user.") - else: - try: - assoc = AuthKeyUserAssociation.objects.get(key=assoc_key) - if assoc.user == request.user: - request.session['auth_error'] = _("These login credentials are already associated with your account.") - else: - request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.") - except: - uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider) - uassoc.save() - request.user.message_set.create(message=_('The new credentials are now associated with your account')) - return HttpResponseRedirect(reverse('user_authsettings')) - - return HttpResponseRedirect(reverse('auth_signin')) - else: - if isinstance(assoc_key, (type, User)): - return login_and_forward(request, assoc_key) - - try: - assoc = AuthKeyUserAssociation.objects.get(key=assoc_key) - user_ = assoc.user - return login_and_forward(request, user_) - except: - request.session['assoc_key'] = assoc_key - request.session['auth_provider'] = provider - return HttpResponseRedirect(reverse('auth_external_register')) - - return HttpResponseRedirect(reverse('auth_signin')) - -def external_register(request): - if request.method == 'POST' and 'bnewaccount' in request.POST: - form1 = SimpleRegistrationForm(request.POST) - email_feeds_form = SimpleEmailSubscribeForm(request.POST) - - if (form1.is_valid() and email_feeds_form.is_valid()): - user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email']) - user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email'] - user_.set_unusable_password() - - user_.save() - - if not user_.email_isvalid: - send_validation_email(user_) - - try: - assoc_key = request.session['assoc_key'] - auth_provider = request.session['auth_provider'] - except: - request.session['auth_error'] = _("Oops, something went wrong in the middle of this process. Please try again.") - return HttpResponseRedirect(request.session.get('on_signin_url', reverse('auth_signin'))) - - uassoc = AuthKeyUserAssociation(user=user_, key=request.session['assoc_key'], provider=request.session['auth_provider']) - uassoc.save() - - email_feeds_form.save(user_) - - del request.session['assoc_key'] - del request.session['auth_provider'] - - if user_.email_isvalid: - return login_and_forward(request, user_) - else: - return HttpResponseRedirect(reverse('index')) - else: - provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer - user_data = provider_class.get_user_data(request.session['assoc_key']) - - username = user_data.get('username', '') - email = user_data.get('email', '') - - if not email: - email = request.session.get('auth_email_request', '') - - if email: - request.session['auth_validated_email'] = email - - form1 = SimpleRegistrationForm(initial={ - 'next': '/', - 'username': username, - 'email': email, - }) - email_feeds_form = SimpleEmailSubscribeForm() - - provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context - - return render_to_response('auth/complete.html', { - 'form1': form1, - 'email_feeds_form': email_feeds_form, - 'provider':mark_safe(provider_context.human_name), - 'login_type':provider_context.id, - 'gravatar_faq_url':reverse('faq') + '#gravatar', - }, context_instance=RequestContext(request)) - -def request_temp_login(request): - if request.method == 'POST': - form = TemporaryLoginRequestForm(request.POST) - - if form.is_valid(): - user = form.user_cache - - try: - hash = get_object_or_404(ValidationHash, user=user, type='templogin') - if hash.expiration < datetime.datetime.now(): - hash.delete() - return request_temp_login(request) - except: - hash = ValidationHash.objects.create_new(user, 'templogin', [user.id]) - - send_email(_("Temporary login link"), [user.email], "auth/temp_login_email.html", { - 'temp_login_code': hash, - 'user': user - }) - - request.user.message_set.create(message=_("An email has been sent with your temporary login key")) - - return HttpResponseRedirect(reverse('index')) - else: - form = TemporaryLoginRequestForm() - - return render_to_response( - 'auth/temp_login_request.html', {'form': form}, - context_instance=RequestContext(request)) - -def temp_signin(request, user, code): - user = get_object_or_404(User, id=user) - - if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])): - return login_and_forward(request, user, reverse('user_authsettings'), - _("You are logged in with a temporary access key, please take the time to fix your issue with authentication.")) - else: - raise Http404() - -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 remove_external_provider(request, id): - association = get_object_or_404(AuthKeyUserAssociation, id=id) - request.user.message_set.create(message=_("You removed the association with %s") % association.provider) - association.delete() - return HttpResponseRedirect(reverse('user_authsettings')) - -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 user_logged_in - 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'))
\ No newline at end of file diff --git a/forum_modules/authentication/README b/forum_modules/authentication/README new file mode 100644 index 00000000..a602dc2c --- /dev/null +++ b/forum_modules/authentication/README @@ -0,0 +1,3 @@ +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 new file mode 100755 index 00000000..96025dc1 --- /dev/null +++ b/forum_modules/authentication/auth.py @@ -0,0 +1,144 @@ +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 user_logged_in + 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 index a182c87c..c51a2bfb 100755 --- a/forum_modules/books/__init__.py +++ b/forum_modules/books/__init__.py @@ -1,3 +1,3 @@ -NAME = 'Osqa Books' +NAME = 'Books' DESCRIPTION = "Allows discussion around books." CAN_ENABLE = True diff --git a/locale/es/LC_MESSAGES/django.mo b/locale/es/LC_MESSAGES/django.mo Binary files differindex d051df29..98a5c635 100644 --- a/locale/es/LC_MESSAGES/django.mo +++ b/locale/es/LC_MESSAGES/django.mo diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index 840b2757..f8d1fa97 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # msgid "" msgstr "" -"Project-Id-Version: OSQA\n" +"Project-Id-Version: Askbot\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-03-23 10:41-0400\n" "PO-Revision-Date: 2010-03-28 22:15-0600\n" @@ -597,7 +597,7 @@ msgid "okay, let's try!" msgstr "bien, vamos a probar!" #: forum/authentication/forms.py:42 -msgid "no OSQA community email please, thanks" +msgid "no community email please, thanks" msgstr "no usar un email de la comunidad, por favor" #: forum/authentication/forms.py:45 @@ -2841,20 +2841,20 @@ msgstr "Enviar enlace" msgid "Connect to %(APP_SHORT_NAME)s with Facebook!" msgstr "Conectar con %(APP_SHORT_NAME)s Facebook!" -#: forum/skins/default/templates/osqaadmin/base.html:12 -msgid "OSQA administration area" +#: forum/skins/default/templates/askbotadmin/base.html:12 +msgid "administration area" msgstr "Área de Administración" -#: forum/skins/default/templates/osqaadmin/base.html:36 +#: forum/skins/default/templates/askbotadmin/base.html:36 msgid "Administration menu" msgstr "Menú de administración" -#: forum/skins/default/templates/osqaadmin/index.html:6 +#: forum/skins/default/templates/askbotadmin/index.html:6 msgid "Basic settings" msgstr "Configuraciones básicas" -#: forum/skins/default/templates/osqaadmin/index.html:9 -msgid "Welcome to the OSQA administration area." +#: forum/skins/default/templates/askbotadmin/index.html:9 +msgid "Welcome to the administration area." msgstr "Bienvenido al área de adminstración" #: forum/templatetags/extra_tags.py:167 @@ -0,0 +1 @@ +python manage.py runserver `hostname -i`:8000 diff --git a/settings.py b/settings.py index a26d666b..f9b776cd 100644 --- a/settings.py +++ b/settings.py @@ -5,7 +5,7 @@ import sys SITE_ID = 1 -ADMIN_MEDIA_PREFIX = '/admin_media/' +ADMIN_MEDIA_PREFIX = '/admin/media/' SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( diff --git a/settings_local.py.dist b/settings_local.py.dist index e44c1e72..137ad6ec 100755 --- a/settings_local.py.dist +++ b/settings_local.py.dist @@ -99,9 +99,10 @@ USE_FB_CONNECT=False FB_API_KEY='' #your api key from facebook FB_SECRET='' #your application secret +USE_EXTERNAL_LEGACY_LOGIN = False #DO NOT USE, and do not delete this line, will be removed later #counter colors -VOTE_COUNTER_EXPECTED_MAXIMUM = 3 from forum_modules.grapefruit import Color +VOTE_COUNTER_EXPECTED_MAXIMUM = 5 COLORS_VOTE_COUNTER_EMPTY_BG = 'white' COLORS_VOTE_COUNTER_EMPTY_FG = 'gray' COLORS_VOTE_COUNTER_MIN_BG = 'white' @@ -116,11 +117,11 @@ COLORS_VIEW_COUNTER_MIN_FG = Color.NewFromHtml(COLORS_VIEW_COUNTER_MIN_BG).Darke COLORS_VIEW_COUNTER_MAX_BG = '#FF8000'#'#F7BE81' COLORS_VIEW_COUNTER_MAX_FG = Color.NewFromHtml(COLORS_VIEW_COUNTER_MAX_BG).DarkerColor(0.7).html ANSWER_COUNTER_EXPECTED_MAXIMUM = 4 -COLORS_ANSWER_COUNTER_EMPTY_BG = '#a40000' +COLORS_ANSWER_COUNTER_EMPTY_BG = Color.NewFromHtml('#a40000').Blend(Color.NewFromHtml('white'),0.8).html COLORS_ANSWER_COUNTER_EMPTY_FG = 'yellow' COLORS_ANSWER_COUNTER_MIN_BG = '#AEB404'#'#81F7F3'#'#A9D0F5'#'#045FB4' COLORS_ANSWER_COUNTER_MIN_FG = 'white'#'#81F7F3' -COLORS_ANSWER_COUNTER_MAX_BG = '#61380B'#'#4B088A'#'#0B3861'#'#045FB4' +COLORS_ANSWER_COUNTER_MAX_BG = Color.NewFromHtml('#61380B').Blend(Color.NewFromHtml('white'),0.75).html COLORS_ANSWER_COUNTER_MAX_FG = '#ffff00' -COLORS_ANSWER_COUNTER_ACCEPTED_BG = 'darkgreen' +COLORS_ANSWER_COUNTER_ACCEPTED_BG = Color.NewFromHtml('darkgreen').Blend(Color.NewFromHtml('white'),0.8).html COLORS_ANSWER_COUNTER_ACCEPTED_FG = '#D0F5A9' |