diff options
-rw-r--r-- | askbot/api.py | 3 | ||||
-rw-r--r-- | askbot/conf/group_settings.py | 2 | ||||
-rw-r--r-- | askbot/context.py | 16 | ||||
-rw-r--r-- | askbot/models/__init__.py | 10 | ||||
-rw-r--r-- | askbot/models/group.py | 39 | ||||
-rw-r--r-- | askbot/models/post.py | 16 | ||||
-rw-r--r-- | askbot/models/question.py | 20 | ||||
-rw-r--r-- | askbot/models/tag.py | 37 | ||||
-rw-r--r-- | askbot/models/widgets.py | 2 | ||||
-rw-r--r-- | askbot/skins/common/media/js/utils.js | 116 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.css | 155 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 28 | ||||
-rw-r--r-- | askbot/skins/default/templates/meta/bottom_scripts.html | 8 | ||||
-rw-r--r-- | askbot/skins/default/templates/widgets/meta_nav.html | 5 | ||||
-rw-r--r-- | askbot/tests/db_api_tests.py | 12 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/views/commands.py | 20 | ||||
-rw-r--r-- | askbot/views/users.py | 14 |
18 files changed, 409 insertions, 99 deletions
diff --git a/askbot/api.py b/askbot/api.py index 9f37995e..52bdab3e 100644 --- a/askbot/api.py +++ b/askbot/api.py @@ -5,8 +5,9 @@ api must become a place to manupulate the data in the askbot application so that other implementations of the data storage could be possible """ from django.db.models import Q -from askbot import models + from askbot import const +from askbot import models def get_info_on_moderation_items(user): """returns a dictionary with diff --git a/askbot/conf/group_settings.py b/askbot/conf/group_settings.py index 2933b831..75323136 100644 --- a/askbot/conf/group_settings.py +++ b/askbot/conf/group_settings.py @@ -20,7 +20,7 @@ settings.register( ) def group_name_update_callback(old_name, new_name): - from askbot.models.tag import get_global_group, clean_group_name + from askbot.models.group import get_global_group, clean_group_name cleaned_new_name = clean_group_name(new_name.strip()) if new_name == '': diff --git a/askbot/context.py b/askbot/context.py index 402183ea..30718972 100644 --- a/askbot/context.py +++ b/askbot/context.py @@ -4,6 +4,9 @@ and the application available for the templates """ import sys from django.conf import settings +from django.core.urlresolvers import reverse +from django.utils import simplejson + import askbot from askbot import api from askbot import models @@ -11,6 +14,7 @@ from askbot import const from askbot.conf import settings as askbot_settings from askbot.skins.loaders import get_skin from askbot.utils import url_utils +from askbot.utils.slug import slugify def application_settings(request): """The context processor function""" @@ -55,9 +59,17 @@ def application_settings(request): } if askbot_settings.GROUPS_ENABLED: - context['group_list'] = models.Tag.group_tags.get_all().filter( + groups = models.Tag.group_tags.get_all().filter( deleted=False ).exclude( - name__startswith='_internal_') + name__startswith='_internal_').values('id', 'name') + group_list = [] + for group in groups: + group_slug = slugify(group['name']) + link = reverse('users_by_group', + kwargs={'group_id': group['id'], + 'group_slug': group_slug}) + group_list.append({'name': group['name'], 'link': link}) + context['group_list'] = simplejson.dumps(group_list) return context diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 549f3277..adf7fb90 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -38,10 +38,8 @@ from askbot.models.question import QuestionView, AnonymousQuestion from askbot.models.question import DraftQuestion from askbot.models.question import FavoriteQuestion from askbot.models.tag import Tag, MarkedTag -from askbot.models.tag import get_global_group -from askbot.models.tag import get_group_names -from askbot.models.tag import get_groups from askbot.models.tag import format_personal_group_name +from askbot.models.group import get_groups, get_global_group from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity from askbot.models.user import GroupMembership, GroupProfile from askbot.models.post import Post, PostRevision @@ -3352,7 +3350,6 @@ def add_user_to_global_group(sender, instance, created, **kwargs): ``instance`` is an instance of ``User`` class """ if created: - from askbot.models.tag import get_global_group instance.edit_group_membership( group=get_global_group(), user=instance, @@ -3500,6 +3497,7 @@ signals.post_updated.connect(record_post_update_activity) signals.post_revision_published.connect(notify_author_of_published_revision) signals.site_visited.connect(record_user_visit) + __all__ = [ 'signals', @@ -3538,6 +3536,6 @@ __all__ = [ 'get_model', 'get_admins_and_moderators', - 'get_group_names', - 'get_groups' + 'get_groups', + 'get_global_group', ] diff --git a/askbot/models/group.py b/askbot/models/group.py new file mode 100644 index 00000000..768b19bb --- /dev/null +++ b/askbot/models/group.py @@ -0,0 +1,39 @@ +import re +from askbot.models.tag import Tag +from askbot.conf import settings as askbot_settings + +def get_global_group(): + """Returns the global group, + if necessary, creates one + """ + #todo: when groups are disconnected from tags, + #find comment as shown below in the test cases and + #revert the values + #todo: change groups to django groups + group_name = askbot_settings.GLOBAL_GROUP_NAME + try: + return Tag.group_tags.get(name=group_name) + except Tag.DoesNotExist: + from askbot.models import get_admin + return Tag.group_tags.get_or_create( + group_name=group_name, + user=get_admin(), + is_open=False + ) + +def get_groups(): + return Tag.group_tags.get_all() + +def get_group_names(): + #todo: cache me + return get_groups().values_list('name', flat = True) + +def get_group_manager(): + #This will be the place to replace with the new model + return Tag.group_tags + +def clean_group_name(name): + """group names allow spaces, + tag names do not, so we use this method + to replace spaces with dashes""" + return re.sub('\s+', '-', name.strip()) diff --git a/askbot/models/post.py b/askbot/models/post.py index 9c24637e..72a64fa7 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -29,8 +29,8 @@ from askbot.models.user import Activity from askbot.models.user import EmailFeedSetting from askbot.models.user import GroupMembership from askbot.models.tag import Tag, MarkedTag -from askbot.models.tag import get_groups, tags_match_some_wildcard -from askbot.models.tag import get_global_group +from askbot.models.tag import tags_match_some_wildcard +from askbot.models.group import get_groups, get_global_group from askbot.conf import settings as askbot_settings from askbot import exceptions from askbot.utils import markup @@ -242,7 +242,7 @@ class PostManager(BaseQuerySetManager): comment = const.POST_STATUS['default_version'], by_email = by_email ) - + return post #todo: instead of this, have Thread.add_answer() @@ -578,7 +578,7 @@ class Post(models.Model): for group in groups: for comment in comments: PostToGroup.objects.get_or_create(post=comment, tag=group) - + def remove_from_groups(self, groups): PostToGroup.objects.filter(post=self, tag__in=groups).delete() @@ -658,7 +658,7 @@ class Post(models.Model): post=self, recipients=notify_sets['for_email'], ) - + def make_private(self, user, group_id = None): """makes post private within user's groups todo: this is a copy-paste in thread and post @@ -1248,7 +1248,7 @@ class Post(models.Model): result['for_mentions'] = set(mentioned_users) - set(exclude_list) #what users are included depends on the post type #for example for question - all Q&A contributors - #are included, for comments only authors of comments and parent + #are included, for comments only authors of comments and parent #post are included result['for_inbox'] = self.get_response_receivers(exclude_list=exclude_list) @@ -1945,7 +1945,7 @@ class PostRevision(models.Model): text = models.TextField() approved = models.BooleanField(default=False, db_index=True) - approved_by = models.ForeignKey(User, null = True, blank = True) + approved_by = models.ForeignKey(User, null = True, blank = True) approved_at = models.DateTimeField(null = True, blank = True) by_email = models.BooleanField(default = False)#true, if edited by email @@ -2026,7 +2026,7 @@ class PostRevision(models.Model): body_text = body_text, recipient_list = [self.author.email,], ) - + else: message = _( 'Your post was placed on the moderation queue ' diff --git a/askbot/models/question.py b/askbot/models/question.py index 17e334f4..a3704107 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -16,13 +16,13 @@ from askbot.conf import settings as askbot_settings from askbot import mail from askbot.mail import messages from askbot.models.tag import Tag -from askbot.models.tag import get_groups -from askbot.models.tag import get_global_group +from askbot.models.group import get_global_group +from askbot.models.group import get_groups from askbot.models.tag import get_tags_by_names from askbot.models.tag import filter_accepted_tags, filter_suggested_tags from askbot.models.tag import delete_tags, separate_unused_tags from askbot.models.base import DraftContent, BaseQuerySetManager -from askbot.models.tag import Tag, get_groups +from askbot.models.tag import Tag from askbot.models.post import Post, PostRevision from askbot.models.post import PostToGroup from askbot.models import signals @@ -197,7 +197,7 @@ class ThreadManager(BaseQuerySetManager): # TODO: add a possibility to see deleted questions qs = self.filter( - posts__post_type='question', + posts__post_type='question', posts__deleted=False ) # (***) brings `askbot_post` into the SQL query, see the ordering section below @@ -235,7 +235,7 @@ class ThreadManager(BaseQuerySetManager): ) # TODO: unify with search_state.author ? #unified tags - is list of tags taken from the tag selection - #plus any tags added to the query string with #tag or [tag:something] + #plus any tags added to the query string with #tag or [tag:something] #syntax. #run tag search in addition to these unified tags meta_data = {} @@ -477,7 +477,7 @@ class Thread(models.Model): score = models.IntegerField(default = 0) objects = ThreadManager() - + class Meta: app_label = 'askbot' @@ -663,7 +663,7 @@ class Thread(models.Model): return 'thread-data-%s-%s' % (self.id, sort_method) def invalidate_cached_post_data(self): - """needs to be called when anything notable + """needs to be called when anything notable changes in the post data - on votes, adding, deleting, editing content""" #we can call delete_many() here if using Django > 1.2 @@ -823,8 +823,8 @@ class Thread(models.Model): url = question_post.get_absolute_url() title = thread.get_title(question_post) result.append({'url': url, 'title': title}) - - return result + + return result def get_cached_data(): """similar thread data will expire @@ -859,7 +859,7 @@ class Thread(models.Model): return False def add_child_posts_to_groups(self, groups): - """adds questions and answers of the thread to + """adds questions and answers of the thread to given groups, comments are taken care of implicitly by the underlying ``Post`` methods """ diff --git a/askbot/models/tag.py b/askbot/models/tag.py index 7c5d8d97..2c6e5e67 100644 --- a/askbot/models/tag.py +++ b/askbot/models/tag.py @@ -8,25 +8,6 @@ from askbot import const from askbot.conf import settings as askbot_settings from askbot.utils import category_tree -def get_global_group(): - """Returns the global group, - if necessary, creates one - """ - #todo: when groups are disconnected from tags, - #find comment as shown below in the test cases and - #revert the values - #todo: change groups to django groups - group_name = askbot_settings.GLOBAL_GROUP_NAME - try: - return Tag.group_tags.get(name=group_name) - except Tag.DoesNotExist: - from askbot.models import get_admin - return Tag.group_tags.get_or_create( - group_name=group_name, - user=get_admin(), - is_open=False - ) - def delete_tags(tags): """deletes tags in the list""" tag_ids = [tag.id for tag in tags] @@ -89,7 +70,7 @@ def separate_unused_tags(tags): return used, unused def tags_match_some_wildcard(tag_names, wildcard_tags): - """Same as + """Same as :meth:`~askbot.models.tag.TagQuerySet.tags_match_some_wildcard` except it works on tag name strings """ @@ -291,6 +272,7 @@ class GroupTagQuerySet(TagQuerySet): def get_for_user(self, user=None, private=False): if private: + from askbot.models.group import get_global_group global_group = get_global_group() return self.filter( user_memberships__user=user @@ -306,15 +288,10 @@ class GroupTagQuerySet(TagQuerySet): ) def get_by_name(self, group_name = None): + from askbot.models.group import clean_group_name return self.get(name = clean_group_name(group_name)) -def clean_group_name(name): - """group names allow spaces, - tag names do not, so we use this method - to replace spaces with dashes""" - return re.sub('\s+', '-', name.strip()) - class GroupTagManager(BaseQuerySetManager): """manager for group tags""" @@ -326,6 +303,7 @@ class GroupTagManager(BaseQuerySetManager): #todo: here we might fill out the group profile #replace spaces with dashes + from askbot.models.group import clean_group_name group_name = clean_group_name(group_name) try: #iexact is important!!! b/c we don't want case variants @@ -390,10 +368,3 @@ class MarkedTag(models.Model): class Meta: app_label = 'askbot' - -def get_groups(): - return Tag.group_tags.get_all() - -def get_group_names(): - #todo: cache me - return get_groups().values_list('name', flat = True) diff --git a/askbot/models/widgets.py b/askbot/models/widgets.py index f062e317..274cc9b1 100644 --- a/askbot/models/widgets.py +++ b/askbot/models/widgets.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import User from django.utils.translation import ugettext as _ from askbot.conf import settings as askbot_settings from askbot.models import Tag -from askbot.models.tag import get_groups +from askbot.models.group import get_groups from askbot.forms import FormWithHideableFields, TagNamesField from askbot.conf import settings as askbot_settings from django import forms diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js index 9b08d66c..53775ecb 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -1557,6 +1557,122 @@ SelectBox.prototype.decorate = function(element){ }); }; +/** + * This is a dropdown list elment + */ + +var GroupDropdown = function(groups){ + WrappedElement.call(this); + this._group_list = groups; + this._input_box = new TippedInput(); + this._input_box.setInstruction('group name'); + this._input_box.createDom(); + this._input_box_element = this._input_box.getElement(); + this._input_box_element.attr('class', 'group-name'); + this._input_box_element.hide(); + this._add_link = this.makeElement('a'); + this._add_link.attr('href', '#'); + this._add_link.attr('class', 'group-name'); + this._add_link.text('add new group'); +}; +inherits(GroupDropdown, WrappedElement); + +GroupDropdown.prototype.createDom = function(){ + this._element = this.makeElement('ul'); + this._element.attr('class', 'dropdown-menu'); + this._element.attr('id', 'groups-dropdown'); + this._element.attr('role', 'menu'); + this._element.attr('aria-labelledby', 'navGroups'); + + for (i=0; i<this._group_list.length; i++){ + li_element = this.makeElement('li'); + a_element = this.makeElement('a'); + a_element.text(this._group_list[i].name); + a_element.attr('href', this._group_list[i].link); + a_element.attr('class', 'group-name'); + li_element.append(a_element); + this._element.append(li_element); + } +}; + +GroupDropdown.prototype.decorate = function(element){ + this._element = element; + this._element.attr('class', 'dropdown-menu'); + this._element.attr('id', 'groups-dropdown'); + this._element.attr('role', 'menu'); + this._element.attr('aria-labelledby', 'navGroups'); + + for (i=0; i<this._group_list.length; i++){ + li_element = this.makeElement('li'); + a_element = this.makeElement('a'); + a_element.text(this._group_list[i].name); + a_element.attr('href', this._group_list[i].link); + a_element.attr('class', 'group-name'); + li_element.append(a_element); + this._element.append(li_element); + } +}; + +GroupDropdown.prototype.prependGroup = function(group_name, url){ + new_group_li = this.makeElement('li'); + new_group_a = this.makeElement('a'); + new_group_a.attr('href', url); + new_group_a.attr('class', 'group-name'); + new_group_a.text(group_name); + new_group_li.append(new_group_a); + this._element.prepend(new_group_li); +}; + +GroupDropdown.prototype._add_group_handler = function(group_name){ + var group_name = this._input_box_element.val(); + self = this; + if (!group_name){ + return; + } + + $.ajax({ + type: 'POST', + url: askbot['urls']['add_group'], + data: {group: group_name}, + success: function(data){ + if (data.success){ + self.prependGroup(data.group_name, data.url); + self._input_box_element.hide(); + self._add_link.show(); + return true; + } else{ + return false; + } + }, + error: function(){console.log('error');} + }); +}; + +GroupDropdown.prototype.enableAddGroups = function(){ + var self = this; + this._add_link.click(function(){ + self._add_link.hide(); + self._input_box_element.show(); + self._input_box_element.focus(); + }); + this._input_box_element.keydown(function(event){ + if (event.which == 13 || event.keyCode==13){ + self._add_group_handler(); + self._input_box_element.val(''); + } + }); + + var divider = this.makeElement('li'); + divider.attr('class', 'divider'); + this._element.append(divider); + + var container = this.makeElement('li'); + container.append(this._add_link); + container.append(this._input_box_element); + + this._element.append(container); +}; + var Tag = function(){ SimpleControl.call(this); this._deletable = false; diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index 55f5806f..ca9ed050 100644 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -68,7 +68,8 @@ select { margin-left: 0px; } input[type="text"].prompt, -input[type="password"].prompt { +input[type="password"].prompt, +input.tipped-input.blank { font-style: italic; color: #707070; } @@ -313,14 +314,36 @@ body.user-messages { #metaNav #navTags { background: 0px -95px url(../images/sprites.png) no-repeat; } -#metaNav #navUsers { - background: 3px -132px url(../images/sprites.png) no-repeat; -} +#metaNav #navUsers, #metaNav #navGroups { - background: 3px -132px url(../images/sprites.png) no-repeat; + background: 3px -133px url(../images/sprites.png) no-repeat; +} +#metaNav #navBadges { + background: 3px -170px url(../images/sprites.png) no-repeat; } #metaNav a.group-name { padding: 0px; + float: center; + margin: 5px 0px 5px 10px; +} +#metaNav input.group-name { + border-top: none; + border-left: none; + border-right: none; + border-bottom: #e2e2ae 1px solid; + color: #e2e2ae; + height: 25px; + font-size: 18px; + font-weight: 100; + text-decoration: none; + display: block; + float: left; +} +#metaNav input.group-name:focus { + border: none; +} +#metaNav a.group-name:hover { + background-color: transparent; } #metaNav span.dropdown:hover ul.dropdown-menu { display: block; @@ -329,8 +352,6 @@ body.user-messages { float: left; } #metaNav .dropdown-menu { - /*top: 120%;*/ - left: 7%; } #header.with-logo #userToolsNav { @@ -1432,7 +1453,7 @@ ul#related-tags li { color: #1b79bd; border-top: #f0f0ec 1px solid; border-left: #f0f0ec 1px solid; - height: 30px; + min-height: 30px; line-height: 30px; font-weight: normal; } @@ -1491,6 +1512,7 @@ ul#related-tags li { .edit-question-page table.proxy-user-info, .edit-answer-page table.proxy-user-info { border-spacing: 0px; + width: 100%; } .ask-page table.proxy-user-info .form-item, .question-page table.proxy-user-info .form-item, @@ -1577,6 +1599,80 @@ ul#related-tags li { width: 395px; font-size: 14px; } +.groups-input, +.users-input { + width: 152px; + padding-left: 5px; + border: #c9c9b5 1px solid; + height: 25px; + font-size: 14px; +} +.add-groups, +.add-users { + border: 0; + font-weight: bold; + margin-top: -2px; + height: 27px; + font-size: 14px; + text-align: center; + text-decoration: none; + cursor: pointer; + color: #4a757f; + font-family: 'Open Sans Condensed', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + border-radius: 4px; + -ms-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; + border-radius: 4px; + -ms-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; +} +.add-everyone-group { + text-align: center; + margin: auto; + display: block; + padding: 0 10px; +} +.add-groups:hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; +} +#id_user, +#id_user_author { + border: #cce6ec 3px solid; + height: 25px; + padding-left: 5px; + width: 395px; + font-size: 14px; +} .title-desc { color: #707070; font-size: 13px; @@ -2302,6 +2398,7 @@ ul#related-tags li { } .question-page .answer .vote-buttons { float: left; + margin-top: 10px; } .question-page .accepted-answer { background-color: #f7fecc; @@ -3133,9 +3230,6 @@ table.form-as-table th { table.ab-subscr-form { width: 45em; } -table.ab-tag-filter-form { - width: 45em; -} .submit-row { line-height: 30px; padding-top: 10px; @@ -3146,10 +3240,16 @@ table.ab-tag-filter-form { line-height: 20px; color: red; } -.error { +.error, +.openid-signin p.error { color: darkred; margin: 0; - font-size: 10px; + font-size: 12px; + font-weight: bold; + text-align: left; +} +.openid-signin p.error { + text-align: center; } label.retag-error { color: darkred; @@ -3549,6 +3649,9 @@ p.signup_p { list-style-position: outside; margin: 0; } +.simple-subscribe-options input { + display: inline; +} /* a workaround to set link colors correctly */ .wmd-preview a { color: #1b79bd; @@ -3764,6 +3867,11 @@ body.anon.lang-es #searchBar .searchInputCancelable { color: white; background: #b32f2f; } +.question-page .post-update-info a.primary-group-name, +a.primary-group-name { + color: #990E08; + font-weight: bold; +} .users-page .wmd-prompt-dialog { background: #ccc; } @@ -3795,6 +3903,17 @@ img.group-logo { #groups-list td { padding-bottom: 5px; } +.groups-page #groups-list th, +.groups-page #groups-list td { + padding-right: 20px; +} +.groups-page #groups-list th { + font-weight: bold; +} +.groups-page #groups-list th:nth-child(2), +.groups-page #groups-list td:nth-child(2) { + text-align: center; +} #reject-edit-modal input, #reject-edit-modal textarea { width: 514px; @@ -3897,12 +4016,18 @@ textarea.tipped-input { margin-top: 8px; height: 13px; } -.tag-editor input.new-tags-input { - border-style: none; +.tag-editor input.new-tags-input, +.tag-editor input.new-tags-input:focus { + border: none; font-size: 15px; font-color: #707070; line-height: 16px; margin-top: 9px; + -webkit-box-shadow: none; + /* undo bootstrap glow */ + + -moz-box-shadow: none; + box-shadow: none; } /* fixes for bootstrap */ .caret { diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 8b443c73..a381c29a 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -313,7 +313,7 @@ body.user-messages { } #navTags{ - .sprites(0px,-95px) + .sprites(0px,-95px); } #navUsers, @@ -327,6 +327,31 @@ body.user-messages { a.group-name { padding: 0px; + float:center; + margin:5px 0px 5px 10px; + } + + input.group-name{ + border-top:none; + border-left:none; + border-right:none; + border-bottom: #e2e2ae 1px solid; + color: #e2e2ae; + height: 25px; + font-size: 18px; + font-weight: 100; + text-decoration: none; + display: block; + float: left; + + } + + input.group-name:focus{ + border:none; + } + + a.group-name:hover{ + background-color: transparent; } span.dropdown:hover ul.dropdown-menu { @@ -338,7 +363,6 @@ body.user-messages { } .dropdown-menu{ - /*top: 120%;*/ left: 7%; } diff --git a/askbot/skins/default/templates/meta/bottom_scripts.html b/askbot/skins/default/templates/meta/bottom_scripts.html index ea763b76..de829bb7 100644 --- a/askbot/skins/default/templates/meta/bottom_scripts.html +++ b/askbot/skins/default/templates/meta/bottom_scripts.html @@ -75,6 +75,14 @@ if (askbot['data']['userIsAdminOrMod']) { $('body').addClass('admin'); } + {%if settings.GROUPS_ENABLED %} + askbot['urls']['add_group'] = "{% url add_group %}"; + var group_dropdown = new GroupDropdown({{group_list}}); + $('.dropdown').append(group_dropdown.getElement()); + {%if request.user.is_superuser%} + group_dropdown.enableAddGroups(); + {%endif%} + {% endif %} }); {% if user_messages %} $('#validate_email_alert').click(function(){notify.close(true)}) diff --git a/askbot/skins/default/templates/widgets/meta_nav.html b/askbot/skins/default/templates/widgets/meta_nav.html index 659780c4..20c22491 100644 --- a/askbot/skins/default/templates/widgets/meta_nav.html +++ b/askbot/skins/default/templates/widgets/meta_nav.html @@ -11,11 +11,6 @@ href="{% url groups %}" data-target="#" > {% trans %}people & groups{% endtrans %} </a> -<ul id="groups-dropdown" class="dropdown-menu" role="menu" aria-labelledby="navGroups"> - {%for group in group_list%} - <li>{{ macros.user_group_link(group) }}</li> - {%endfor%} -</ul> </span> {%else%} <a diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index 07590487..32c4ebb3 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -1,4 +1,4 @@ -"""Tests database api - the basic data entry +"""Tests database api - the basic data entry functions that happen on behalf of users e.g. ``some_user.do_something(...)`` @@ -13,7 +13,7 @@ from askbot.tests.utils import AskbotTestCase from askbot import models from askbot import const from askbot.conf import settings as askbot_settings -from askbot.models.tag import get_global_group +from askbot.models.group import get_global_group import datetime class DBApiTests(AskbotTestCase): @@ -249,7 +249,7 @@ class UserLikeTagTests(AskbotTestCase): self.setup_wildcard('aouaou* o* on* oeu*', 'bad') self.assert_affinity_is('like', False) self.assert_affinity_is('dislike', True) - + self.setup_wildcard('one*', 'good') self.assert_affinity_is('like', True) self.assert_affinity_is('dislike', False) @@ -297,7 +297,7 @@ class GlobalTagSubscriberGetterTests(AskbotTestCase): self.assertEquals(actual_subscribers, expected_subscribers) def test_nobody_likes_any_tags(self): - """no-one had marked tags, so the set + """no-one had marked tags, so the set of subscribers must be empty """ self.assert_subscribers_are( @@ -430,7 +430,7 @@ class TagAndGroupTests(AskbotTestCase): 'question_comment': question_comment, 'answer_comment': answer_comment } - + def test_group_cannot_create_case_variant_tag(self): self.post_question(user = self.u1, tags = 'one two three') models.Tag.group_tags.get_or_create(user = self.u1, group_name = 'One') @@ -546,7 +546,7 @@ class TagAndGroupTests(AskbotTestCase): self.assertObjectGroupsEqual(data['question_comment'], groups) self.assertObjectGroupsEqual(data['answer'], groups) self.assertObjectGroupsEqual(data['answer_comment'], groups) - + data['thread'].make_public(recursive=True) global_group = get_global_group() diff --git a/askbot/urls.py b/askbot/urls.py index 62ae8b54..e553f947 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -273,6 +273,11 @@ urlpatterns = patterns('', name = 'delete_group_logo' ), url(#ajax only + r'^add-group/', + views.commands.add_group, + name = 'add_group' + ), + url(#ajax only r'^toggle-group-profile-property/', views.commands.toggle_group_profile_property, name = 'toggle_group_profile_property' diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 50a94d6a..14990b52 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -20,11 +20,12 @@ from django.utils import simplejson from django.utils.html import escape from django.utils.translation import ugettext as _ from django.utils.translation import string_concat +from askbot.utils.slug import slugify from askbot import models from askbot import forms from askbot.conf import should_show_sort_by_relevance from askbot.conf import settings as askbot_settings -from askbot.models.tag import get_global_group +from askbot.models.group import get_global_group from askbot.utils import category_tree from askbot.utils import decorators from askbot.utils import url_utils @@ -932,6 +933,23 @@ def save_group_logo_url(request): else: raise ValueError('invalid data found when saving group logo') +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def add_group(request): + group_name = request.POST.get('group') + if group_name: + group = models.Tag.group_tags.get_or_create( + group_name=group_name, + user=request.user, + ) + + url = reverse('users_by_group', kwargs={'group_id': group.id, + 'group_slug': slugify(group_name)}) + response_dict = dict(group_name = group_name, + url = url ) + return response_dict @csrf.csrf_exempt @decorators.ajax_only diff --git a/askbot/views/users.py b/askbot/views/users.py index c5b95390..ddb60f13 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -37,8 +37,8 @@ from askbot.conf import settings as askbot_settings from askbot import models from askbot import exceptions from askbot.models.badges import award_badges_signal -from askbot.models.tag import get_global_group -from askbot.models.tag import get_groups +from askbot.models.group import get_global_group, get_groups +from askbot.models.group import get_group_manager from askbot.skins.loaders import render_into_skin from askbot.search.state_manager import SearchState from askbot.utils import url_utils @@ -81,7 +81,7 @@ def show_users(request, by_group=False, group_id=None, group_slug=None): return HttpResponseRedirect('groups') else: try: - group = models.Tag.group_tags.get(id = group_id) + group = get_group_manager().get(id = group_id) group_email_moderation_enabled = \ ( askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED \ @@ -462,7 +462,7 @@ def user_stats(request, user, context): badges = badges_dict.items() badges.sort(key=operator.itemgetter(1), reverse=True) - user_groups = models.Tag.group_tags.get_for_user(user = user) + user_groups = get_group_manager().get_for_user(user = user) user_groups = user_groups.exclude(name__startswith='_internal_') global_group = get_global_group() user_groups = user_groups.exclude(name=global_group.name) @@ -1011,11 +1011,9 @@ def groups(request, id = None, slug = None): scope = 'all-groups' if scope == 'all-groups': - groups = models.Tag.group_tags.get_all() + groups = get_groups() else: - groups = models.Tag.group_tags.get_for_user( - user = request.user - ) + groups = get_group_manager().get_for_user(user=request.user) groups = groups.exclude(name__startswith='_internal_') groups = groups.annotate(users_count=Count('user_memberships')) |