summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/__init__.py6
-rw-r--r--askbot/doc/source/changelog.rst10
-rw-r--r--askbot/doc/source/optional-modules.rst5
-rw-r--r--askbot/management/commands/clean_session.py52
-rw-r--r--askbot/migrations/0001_initial.py10
-rw-r--r--askbot/setup_templates/settings.py1
-rw-r--r--askbot/setup_templates/settings.py.mustache1
-rw-r--r--askbot/setup_templates/tinymce_sample_config.py1
-rw-r--r--askbot/skins/common/media/js/post.js16
-rw-r--r--askbot/skins/common/media/js/utils.js30
-rw-r--r--askbot/skins/common/media/js/wmd/wmd.js5
-rw-r--r--askbot/skins/default/media/style/style.css27
-rw-r--r--askbot/skins/default/media/style/style.less23
-rw-r--r--askbot/skins/default/templates/macros.html5
-rw-r--r--askbot/skins/default/templates/meta/bottom_scripts.html6
-rw-r--r--askbot/skins/default/templates/meta/html_head_stylesheets.html4
-rw-r--r--askbot/skins/default/templates/question.html11
-rw-r--r--askbot/skins/default/templates/question/javascript.html2
-rw-r--r--askbot/startup_procedures.py19
-rw-r--r--askbot/urls.py6
-rw-r--r--askbot/utils/forms.py9
-rw-r--r--askbot/views/writers.py26
-rw-r--r--askbot_requirements.txt1
23 files changed, 230 insertions, 46 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py
index c79d19f9..97b5ee92 100644
--- a/askbot/__init__.py
+++ b/askbot/__init__.py
@@ -5,6 +5,7 @@ Functions in the askbot module perform various
basic actions on behalf of the forum application
"""
import os
+import platform
VERSION = (0, 7, 43)
@@ -30,11 +31,14 @@ REQUIREMENTS = {
'recaptcha_works': 'django-recaptcha-works',
'openid': 'python-openid',
'pystache': 'pystache==0.3.1',
- 'lamson': 'Lamson',
'pytz': 'pytz',
'tinymce': 'django-tinymce',
+ 'longerusername': 'longerusername',
}
+if platform.system() != 'Windows':
+ REQUIREMENTS['lamson'] = 'Lamson'
+
#necessary for interoperability of django and coffin
try:
from askbot import patches
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst
index eb45eed2..f90fdc73 100644
--- a/askbot/doc/source/changelog.rst
+++ b/askbot/doc/source/changelog.rst
@@ -3,11 +3,19 @@ Changes in Askbot
Development version
-------------------
- adding "extra options" to the ldap session (Evgeny)
+* Repost comment as answer (Adolfo)
+* Question list widget (Adolfo)
+* Ask a question widget (Adolfo)
+* Embeddable widget generator (Adolfo)
+* Groups are shown in the dropdown menu in the header (Adolfo)
+* Added group moderation requests to the moderators inboxes (Evgeny)
+* Group joining may be open/closed or moderated (Evgeny)
+* Adding "extra options" to the ldap session (Evgeny)
* Tag moderation (Evgeny)
* Editable optional three level category selector for the tags (Evgeny)
* Tag editor adding tags as they are typed (Evgeny)
* Added optional support for unicode slugs (Evgeny)
+* Allow user names longer than 30 characters (Evgeny)
* Option to disable feedback form for the anonymos users (Evgeny)
* Optional restriction to have confirmed email address to join forum (Evgeny)
* Optional list of allowed email addresses and email domain name for the new users (Evgeny)
diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst
index c8f2dba3..4ba7b925 100644
--- a/askbot/doc/source/optional-modules.rst
+++ b/askbot/doc/source/optional-modules.rst
@@ -276,6 +276,11 @@ Askbot supports posting replies by email. For this feature to work ``Lamson`` a
pip install django-lamson
+.. note::
+ On Windows installation of the Lamson module may require
+ additional work. Askbot does not support this feature
+ on Windows automatically.
+
The lamson daemon needs a folder to store it's mail queue files and a folder to store log files, create the folders folder named ``run`` and ``logs`` within your project folder by executing the following commands:
mkdir run
diff --git a/askbot/management/commands/clean_session.py b/askbot/management/commands/clean_session.py
index 6ba9352c..2e663b22 100644
--- a/askbot/management/commands/clean_session.py
+++ b/askbot/management/commands/clean_session.py
@@ -1,13 +1,18 @@
+"""deletes expired sessions from the session database table
+works only when sessions are stored in the database
+"""
from django.core.management.base import NoArgsCommand
from django.contrib.sessions.models import Session
from django.db import transaction
from optparse import make_option
-from askbot.utils.console import print_progress
+from askbot.utils.console import ProgressBar
from datetime import datetime
-DELETE_LIMIT = 1000
+ITEMS_PER_TRANSACTION = 1000
class Command(NoArgsCommand):
+ """Django management command class"""
+
option_list = NoArgsCommand.option_list + (
make_option('--quiet',
action='store_true',
@@ -19,32 +24,23 @@ class Command(NoArgsCommand):
@transaction.commit_manually
def handle_noargs(self, **options):
- '''deletes old sessions'''
+ """deletes old sessions"""
quiet = options.get('quiet', False)
- expired_session_count = Session.objects.filter(expire_date__lt=datetime.now()).count()
- expired_session_list= Session.objects.filter(expire_date__lt=datetime.now()).values_list('session_key', flat=True)
- transaction.commit()
-
- if not quiet:
- print "There are %d expired sessions" % expired_session_count
- range_limit = len(expired_session_list) - 1
- higher_limit = lower_limit = 0
+ expired_sessions = Session.objects.filter(
+ expire_date__lt=datetime.now()
+ )
+ count = expired_sessions.count()
+ expired_sessions = expired_sessions.iterator()
+ if quiet is False:
+ message = 'There are %d expired sessions' % count
+ expired_sessions = ProgressBar(expired_sessions, count, message)
+
+ deleted_count = 0
+ for session in expired_sessions:
+ session.delete()
+ deleted_count += 1
+ if deleted_count % ITEMS_PER_TRANSACTION == 0:
+ transaction.commit()
- for i in range(DELETE_LIMIT, range_limit, DELETE_LIMIT):
- lower_limit = i
- higher_limit = lower_limit + DELETE_LIMIT
- sublist = expired_session_list[lower_limit:higher_limit]
- Session.objects.filter(session_key__in = sublist).delete()
- transaction.commit()
- if not quiet:
- print_progress(higher_limit-1, expired_session_count)
-
- if higher_limit < expired_session_list:
- sublist = expired_session_list[higher_limit:expired_session_count]
- Session.objects.filter(session_key__in = sublist).delete()
- print_progress(expired_session_count, expired_session_count)
- transaction.commit()
-
- if not quiet:
- print "sessions cleared"
+ transaction.commit()
diff --git a/askbot/migrations/0001_initial.py b/askbot/migrations/0001_initial.py
index 0fc434f8..d11c8f2f 100644
--- a/askbot/migrations/0001_initial.py
+++ b/askbot/migrations/0001_initial.py
@@ -12,17 +12,17 @@ class Migration(SchemaMigration):
def forwards(self, orm):
#1) patch the existing auth_user table
- safe_add_column('auth_user', 'website', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True), keep_default = False)
- safe_add_column('auth_user', 'about', self.gf('django.db.models.fields.TextField')(blank=True), keep_default = False)
+ safe_add_column('auth_user', 'website', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True, null=True), keep_default = False)
+ safe_add_column('auth_user', 'about', self.gf('django.db.models.fields.TextField')(blank=True, null=True), keep_default = False)
safe_add_column('auth_user', 'hide_ignored_questions', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default = False)
safe_add_column('auth_user', 'gold', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default = False)
safe_add_column('auth_user', 'email_isvalid', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default = False)
- safe_add_column('auth_user', 'real_name', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True), keep_default = False)
- safe_add_column('auth_user', 'location', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True), keep_default = False)
+ safe_add_column('auth_user', 'real_name', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True, null=True), keep_default = False)
+ safe_add_column('auth_user', 'location', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True, null=True), keep_default = False)
safe_add_column('auth_user', 'email_key', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default = False)
safe_add_column('auth_user', 'date_of_birth', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default = False)
safe_add_column('auth_user', 'reputation', self.gf('django.db.models.fields.PositiveIntegerField')(default=1), keep_default = False)
- safe_add_column('auth_user', 'gravatar', self.gf('django.db.models.fields.CharField')(max_length=32), keep_default = False)
+ safe_add_column('auth_user', 'gravatar', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default = False)
safe_add_column('auth_user', 'bronze', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default = False)
safe_add_column('auth_user', 'tag_filter_setting', self.gf('django.db.models.fields.CharField')(default='ignored', max_length=16), keep_default = False)
safe_add_column('auth_user', 'last_seen', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), keep_default = False)
diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py
index d0cb6e2d..0af52df1 100644
--- a/askbot/setup_templates/settings.py
+++ b/askbot/setup_templates/settings.py
@@ -150,6 +150,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
INSTALLED_APPS = (
+ 'longerusername',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache
index e9d9245e..82ff1d92 100644
--- a/askbot/setup_templates/settings.py.mustache
+++ b/askbot/setup_templates/settings.py.mustache
@@ -149,6 +149,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
INSTALLED_APPS = (
+ 'longerusername',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
diff --git a/askbot/setup_templates/tinymce_sample_config.py b/askbot/setup_templates/tinymce_sample_config.py
index d3ac1d12..c75170b0 100644
--- a/askbot/setup_templates/tinymce_sample_config.py
+++ b/askbot/setup_templates/tinymce_sample_config.py
@@ -21,5 +21,6 @@ TINYMCE_DEFAULT_CONFIG = {
'theme_advanced_resizing': True,
'theme_advanced_resize_horizontal': False,
'theme_advanced_statusbar_location': 'bottom',
+ 'width': '723',
'height': '250'
}
diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js
index 22b8cf9b..4cbb9f7e 100644
--- a/askbot/skins/common/media/js/post.js
+++ b/askbot/skins/common/media/js/post.js
@@ -1665,6 +1665,8 @@ var Comment = function(widget, data){
this._data = data || {};
this._blank = true;//set to false by setContent
this._element = null;
+ this._is_convertible = askbot['data']['userIsAdminOrMod'];
+ this.convert_link = null;
this._delete_prompt = gettext('delete this comment');
if (data && data['is_deletable']){
this._deletable = data['is_deletable'];
@@ -1701,6 +1703,12 @@ Comment.prototype.decorate = function(element){
this._edit_link.decorate(edit_link);
}
+ var convert_link = this._element.find('form.convert');
+ if (this._is_convertible){
+ this._convert_link = new ConvertLink(comment_id);
+ this._convert_link.decorate(convert_link);
+ }
+
var vote = new CommentVoteButton(this);
vote.decorate(this._element.find('.comment-votes .upvote'));
@@ -1784,6 +1792,11 @@ Comment.prototype.setContent = function(data){
this._edit_link.setHandler(this.getEditHandler())
this._comment_body.append(this._edit_link.getElement());
}
+
+ if (this._is_convertible){
+ this._convert_link = new ConvertLink(this._data['id']);
+ this._comment_body.append(this._convert_link.getElement());
+ }
this._element.append(this._comment_body);
this._blank = false;
@@ -1808,6 +1821,9 @@ Comment.prototype.dispose = function(){
if (this._edit_link){
this._edit_link.dispose();
}
+ if (this._convert_link){
+ this._convert_link.dispose();
+ }
this._data = null;
Comment.superClass_.dispose.call(this);
};
diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js
index 837f4e69..0e23963e 100644
--- a/askbot/skins/common/media/js/utils.js
+++ b/askbot/skins/common/media/js/utils.js
@@ -655,6 +655,36 @@ EditLink.prototype.decorate = function(element){
this.setHandlerInternal();
};
+var ConvertLink = function(comment_id){
+ WrappedElement.call(this)
+ this._comment_id = comment_id;
+};
+inherits(ConvertLink, WrappedElement);
+
+ConvertLink.prototype.createDom = function(){
+ var element = this.makeElement('form');
+ element.addClass('convert');
+ element.attr('method', 'POST');
+ element.attr('action', askbot['urls']['convertComment']);
+ var hidden_input = this.makeElement('input');
+ hidden_input.attr('type', 'hidden');
+ hidden_input.attr('value', this._comment_id);
+ hidden_input.attr('name', 'comment_id');
+ hidden_input.attr('id', 'id_comment_id');
+ element.append(hidden_input);
+
+ var submit = this.makeElement('input');
+ submit.attr('type', 'submit');
+ submit.attr('value', gettext('convert to answer'));
+ element.append(submit);
+ this.decorate(element);
+};
+
+
+ConvertLink.prototype.decorate = function(element){
+ this._element = element;
+};
+
var DeleteIcon = function(title){
SimpleControl.call(this);
this._title = title;
diff --git a/askbot/skins/common/media/js/wmd/wmd.js b/askbot/skins/common/media/js/wmd/wmd.js
index b9f35c03..5aeacd98 100644
--- a/askbot/skins/common/media/js/wmd/wmd.js
+++ b/askbot/skins/common/media/js/wmd/wmd.js
@@ -21,6 +21,7 @@ Attacklab.wmdBase = function(){
// Used to work around some browser bugs where we can't use feature testing.
+ global.isChrome = /chrome/.test(nav.userAgent.toLowerCase());
global.isIE = /msie/.test(nav.userAgent.toLowerCase());
global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
global.isIE_7plus = global.isIE && !global.isIE_5or6;
@@ -1611,6 +1612,10 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){
var regexText;
var replacementText;
+
+ if (global.isChrome) {//Chrome bug workaround
+ 'X'.match(/()./);
+ }
this.selection = this.selection.replace(/(^\n*)/, "");
this.startTag = this.startTag + re.$1;
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index bdba3c28..2e558988 100644
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -204,7 +204,8 @@ body.user-messages {
}
.notify .notification {
margin-top: 6px;
- margin-bottom: 6px;
+ /*margin-bottom: 6px;*/
+
font-size: 16px;
color: #424242;
}
@@ -1319,7 +1320,6 @@ ul#related-tags li {
.groups-page h1,
.moderate-tags-page h1 {
float: left;
- padding-top: 7px;
}
.moderate-tags-page button {
line-height: 18px;
@@ -2306,6 +2306,26 @@ ul#related-tags li {
.question-page .comments .comment-body .edit {
padding-left: 6px;
}
+.question-page .comments .comment-body .convert {
+ display: inline;
+ white-space: nowrap;
+ padding-left: 0px;
+}
+.question-page .comments .comment-body .convert input {
+ background: none;
+ padding: 0px;
+ color: #1B79BD;
+ border: none;
+ width: auto;
+ font-family: Arial;
+ line-height: 14px;
+ margin-left: 6px;
+ font-size: 13px;
+}
+.question-page .comments .comment-body .convert input:hover {
+ text-decoration: underline;
+ cursor: pointer;
+}
.question-page .comments .comment-body p {
font-size: 13px;
line-height: 1.3;
@@ -2773,6 +2793,9 @@ ul#related-tags li {
/*.users-page .tabBar{
width:375px;
}*/
+.users-page #group-openness-selector {
+ width: 200px;
+}
.user {
padding: 5px 10px 5px 0;
line-height: 140%;
diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less
index e7f219b2..71570ee1 100644
--- a/askbot/skins/default/media/style/style.less
+++ b/askbot/skins/default/media/style/style.less
@@ -2158,6 +2158,29 @@ ul#related-tags li {
.edit{
padding-left:6px;
}
+
+ .convert {
+ display: inline;
+ white-space: nowrap;
+ padding-left: 0px;
+ }
+
+ .convert input{
+ background: none;
+ padding: 0px;
+ color: #1B79BD;
+ border:none;
+ width:auto;
+ font-family: Arial;
+ line-height: 14px;
+ margin-left: 6px;
+ font-size: 13px;
+ }
+
+ .convert input:hover{
+ text-decoration:underline;
+ cursor:pointer;
+ }
}
.comment-body p{
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index afdde6b6..e976241c 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -424,6 +424,11 @@ for the purposes of the AJAX comment editor #}
<span class="age">&nbsp;({{ timeago(comment.added_at) }})</span>
<a id="post-{{comment.id}}-edit"
class="edit">{% trans %}edit{% endtrans %}</a>
+ <form action="{% url comment_to_answer %}" method="POST" accept-charset="utf-8" class='convert'>
+ {% csrf_token %}
+ <input type="hidden" value="{{comment.id}}" name="comment_id" id="id_comment_id">
+ <input type="submit" value="{% trans %}convert to answer{% endtrans %}">
+ </form>
</div>
</div>
<script type="text/javascript">
diff --git a/askbot/skins/default/templates/meta/bottom_scripts.html b/askbot/skins/default/templates/meta/bottom_scripts.html
index de829bb7..a36a63ec 100644
--- a/askbot/skins/default/templates/meta/bottom_scripts.html
+++ b/askbot/skins/default/templates/meta/bottom_scripts.html
@@ -6,6 +6,12 @@
<noscript class="noscript">
{% trans app_name = settings.APP_SHORT_NAME %}Please note: {{app_name}} requires javascript to work properly, please enable javascript in your browser, <a href="{{noscript_url}}">here is how</a>{% endtrans %}
</noscript>
+ <script type="text/javascript">
+ //IE fix to hide the red margin
+ var noscript = document.getElementsByTagName('noscript')[0];
+ noscript.style.padding = '0px';
+ noscript.style.backgroundColor = 'transparent';
+ </script>
</div>
<script type="text/javascript">
var i18nLang = '{{settings.LANGUAGE_CODE}}';
diff --git a/askbot/skins/default/templates/meta/html_head_stylesheets.html b/askbot/skins/default/templates/meta/html_head_stylesheets.html
index cadc69e0..85bb489c 100644
--- a/askbot/skins/default/templates/meta/html_head_stylesheets.html
+++ b/askbot/skins/default/templates/meta/html_head_stylesheets.html
@@ -12,7 +12,9 @@
{% if settings.USE_LOCAL_FONTS %}
{% include "meta/fonts.html" %}
{% else %}
- <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:400,700&amp;subset=latin,cyrillic-ext,latin-ext' rel='stylesheet' type='text/css' />
+ {# note: IE8 fix - a combined font link wont work so we have two #}
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=latin-ext' rel='stylesheet' type='text/css'>
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=cyrillic-ext' rel='stylesheet' type='text/css'>
{% endif %}
{{ skin.get_extra_css_link() }}
{% if settings.USE_CUSTOM_CSS %}
diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html
index 28de61e7..651392fc 100644
--- a/askbot/skins/default/templates/question.html
+++ b/askbot/skins/default/templates/question.html
@@ -156,11 +156,22 @@
);
}
}
+
+ function hide_convert_links(){
+ if (!askbot['data']['userIsAdminOrMod']){
+ var links = document.getElementsByClassName('convert');
+ for (i=0; i<links.length; i++){
+ links[i].setAttribute('style', 'display:none;');
+ }
+ }
+ }
+
askbot['functions'] = askbot['functions'] || {};
askbot['functions']['renderPostVoteButtons'] = render_vote_buttons;
askbot['functions']['renderPostControls'] = render_post_controls;
askbot['functions']['renderAddCommentButton'] = render_add_comment_button;
askbot['functions']['renderAddAnswerButton'] = render_add_answer_button;
+ askbot['functions']['hideConvertLinks'] = hide_convert_links;
})();
/*]]>*/
</script>
diff --git a/askbot/skins/default/templates/question/javascript.html b/askbot/skins/default/templates/question/javascript.html
index bc640623..19a7269c 100644
--- a/askbot/skins/default/templates/question/javascript.html
+++ b/askbot/skins/default/templates/question/javascript.html
@@ -9,6 +9,7 @@
askbot['urls']['postComments'] = '{% url post_comments %}';
askbot['urls']['editComment'] = '{% url edit_comment %}';
askbot['urls']['deleteComment'] = '{% url delete_comment %}';
+ askbot['urls']['convertComment'] = '{% url comment_to_answer %}';
askbot['urls']['getComment'] = '{% url get_comment %}';
askbot['urls']['saveDraftAnswer'] = '{% url save_draft_answer %}';
askbot['urls']['question_url_template'] = scriptUrl + '{{ 'question/'|transurl }}{{ "{{QuestionID}}/{{questionSlug}}" }}';{# yes it needs to be that whacky #}
@@ -67,6 +68,7 @@
draftHandler.setThreadId({{ thread.id }});
draftHandler.decorate($(document));
}
+ askbot['functions']['hideConvertLinks']();
});
$(window).bind('hashchange', animate_hashes);
diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py
index 33c33da4..a0c60800 100644
--- a/askbot/startup_procedures.py
+++ b/askbot/startup_procedures.py
@@ -573,6 +573,24 @@ def test_tinymce():
'as template:\n\n' + get_tinymce_sample_config()
print_errors(errors, header=header, footer=footer)
+def test_longerusername():
+ """tests proper installation of the "longerusername" app
+ """
+ errors = list()
+ if 'longerusername' not in django_settings.INSTALLED_APPS:
+ errors.append(
+ "add 'longerusername', as the first item in the INSTALLED_APPS"
+ )
+ else:
+ index = django_settings.INSTALLED_APPS.index('longerusername')
+ if index != 0:
+ message = "move 'longerusername', to the beginning of INSTALLED_APPS"
+ raise AskbotConfigError(message)
+
+ if errors:
+ errors.append('run "python manage.py migrate longerusername"')
+ print_errors(errors)
+
def run_startup_tests():
"""function that runs
all startup tests, mainly checking settings config so far
@@ -589,6 +607,7 @@ def run_startup_tests():
#test_csrf_cookie_domain()
test_tinymce()
test_staticfiles()
+ test_longerusername()
test_avatar()
settings_tester = SettingsTester({
'CACHE_MIDDLEWARE_ANONYMOUS_ONLY': {
diff --git a/askbot/urls.py b/askbot/urls.py
index d69db744..808bf84c 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -71,6 +71,7 @@ urlpatterns = patterns('',
name='questions'
),
# END main page urls
+
url(
r'^api/get_questions/',
views.commands.api_get_questions,
@@ -187,6 +188,11 @@ urlpatterns = patterns('',
views.readers.get_comment,
name='get_comment'
),
+ url(#post only
+ r'^comment/convert/$',
+ views.writers.comment_to_answer,
+ name='comment_to_answer'
+ ),
url(
r'^%s$' % _('tags/'),
views.readers.tags,
diff --git a/askbot/utils/forms.py b/askbot/utils/forms.py
index e8f9e622..381ea717 100644
--- a/askbot/utils/forms.py
+++ b/askbot/utils/forms.py
@@ -9,6 +9,7 @@ from askbot.conf import settings as askbot_settings
from askbot.utils.slug import slugify
from askbot.utils.functions import split_list
from askbot import const
+from longerusername import MAX_USERNAME_LENGTH
import logging
import urllib
@@ -86,12 +87,14 @@ class UserNameField(StrippedNonEmptyCharField):
else:
widget_attrs = login_form_widget_attrs
- super(UserNameField,self).__init__(max_length=30,
- widget=forms.TextInput(attrs=widget_attrs),
+ max_length = MAX_USERNAME_LENGTH
+ super(UserNameField,self).__init__(
+ max_length=max_length,
+ widget=forms.TextInput(attrs=login_form_widget_attrs),
label=label,
error_messages=error_messages,
**kw
- )
+ )
def clean(self,username):
""" validate username """
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index a9f1c4a0..9cde2c39 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -50,7 +50,7 @@ QUESTIONS_PAGE_SIZE = 10
ANSWERS_PAGE_SIZE = 10
@csrf.csrf_exempt
-def upload(request):#ajax upload file to a question or answer
+def upload(request):#ajax upload file to a question or answer
"""view that handles file upload via Ajax
"""
@@ -71,6 +71,7 @@ def upload(request):#ajax upload file to a question or answer
if file_name_prefix not in ('', 'group_logo_'):
raise exceptions.PermissionDenied('invalid upload file name prefix')
+ #todo: check file type
f = request.FILES['file-upload']#take first file
#todo: extension checking should be replaced with mimetype checking
#and this must be part of the form validation
@@ -121,14 +122,14 @@ def __import_se_data(dump_file):
"""non-view function that imports the SE data
in the future may import other formats as well
- In this function stdout is temporarily
+ In this function stdout is temporarily
redirected, so that the underlying importer management
command could stream the output to the browser
todo: maybe need to add try/except clauses to restore
the redirects in the exceptional situations
"""
-
+
fake_stdout = tempfile.NamedTemporaryFile()
real_stdout = sys.stdout
sys.stdout = fake_stdout
@@ -226,7 +227,7 @@ def ask(request):#view used to ask a new question
author=request.user
)
drafts.delete()
-
+
user = form.get_post_user(request.user)
try:
question = user.post_question(
@@ -421,7 +422,7 @@ def edit_question(request, id):
body_text = form.cleaned_data['text'],
revision_comment = form.cleaned_data['summary'],
tags = form.cleaned_data['tags'],
- wiki = is_wiki,
+ wiki = is_wiki,
edit_anonymously = is_anon_edit,
is_private = post_privately
)
@@ -720,3 +721,18 @@ def delete_comment(request):
unicode(e),
mimetype = 'application/json'
)
+
+@decorators.admins_only
+@decorators.post_only
+def comment_to_answer(request):
+ comment_id = request.POST.get('comment_id')
+ if comment_id:
+ comment_id = int(comment_id)
+ comment = get_object_or_404(models.Post, post_type = 'comment', id=comment_id)
+ comment.post_type = 'answer'
+ comment.save()
+ comment.thread.invalidate_cached_data()
+
+ return HttpResponseRedirect(comment.get_absolute_url())
+ else:
+ raise Http404
diff --git a/askbot_requirements.txt b/askbot_requirements.txt
index 1e851490..8771a7e6 100644
--- a/askbot_requirements.txt
+++ b/askbot_requirements.txt
@@ -20,3 +20,4 @@ python-openid
pystache==0.3.1
pytz
django-tinymce
+longerusername