def get_language(): if askbot.is_multilingual(): return translation.get_language() elif HAS_ASKBOT_LOCALE_MIDDLEWARE: return askbot_settings.ASKBOT_LANGUAGE else: return django_settings.LANGUAGE_CODE
def test_haystack(): if 'haystack' in django_settings.INSTALLED_APPS: try_import('haystack', 'django-haystack', short_message = True) if getattr(django_settings, 'ENABLE_HAYSTACK_SEARCH', False): errors = list() if not hasattr(django_settings, 'HAYSTACK_CONNECTIONS'): message = "Please HAYSTACK_CONNECTIONS to an appropriate value, value 'simple' can be used for basic testing sample:\n" message += """HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', } }""" errors.append(message) if askbot.is_multilingual(): if not hasattr(django_settings, "HAYSTACK_ROUTERS"): message = "Please add HAYSTACK_ROUTERS = ['askbot.search.haystack.routers.LanguageRouter',] to settings.py" errors.append(message) elif 'askbot.search.haystack.routers.LanguageRouter' not in \ getattr(django_settings, 'HAYSTACK_ROUTERS'): message = "'askbot.search.haystack.routers.LanguageRouter' to HAYSTACK_ROUTERS as first element in settings.py" errors.append(message) if getattr(django_settings, 'HAYSTACK_SIGNAL_PROCESSOR', '').endswith('AskbotCelerySignalProcessor'): try_import('celery_haystack', 'celery-haystack', short_message = True) footer = 'Please refer to haystack documentation at https://django-haystack.readthedocs.org/en/latest/settings.html' print_errors(errors, footer=footer)
def test_haystack(): if 'haystack' in django_settings.INSTALLED_APPS: try_import('haystack', 'django-haystack', show_requirements_message=False) if getattr(django_settings, 'ENABLE_HAYSTACK_SEARCH', False): errors = list() if not hasattr(django_settings, 'HAYSTACK_CONNECTIONS'): message = "Please HAYSTACK_CONNECTIONS to an appropriate value, value 'simple' can be used for basic testing sample:\n" message += """HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', }""" errors.append(message) if askbot.is_multilingual(): if not hasattr(django_settings, "HAYSTACK_ROUTERS"): message = "Please add HAYSTACK_ROUTERS = ['askbot.search.haystack.routers.LanguageRouter',] to settings.py" errors.append(message) elif 'askbot.search.haystack.routers.LanguageRouter' not in \ getattr(django_settings, 'HAYSTACK_ROUTERS'): message = "'askbot.search.haystack.routers.LanguageRouter' to HAYSTACK_ROUTERS as first element in settings.py" errors.append(message) if getattr(django_settings, 'HAYSTACK_SIGNAL_PROCESSOR', '').endswith('AskbotCelerySignalProcessor'): try_import('celery_haystack', 'celery-haystack', show_requirements_message=False) footer = 'Please refer to haystack documentation at https://django-haystack.readthedocs.org/en/latest/settings.html' print_errors(errors, footer=footer)
def cached_value_update_handler(setting=None, new_value=None, language_code=None, *args, **kwargs): key=setting.key if setting.localized == False and askbot.is_multilingual(): languages = dict(django_settings.LANGUAGES).keys() for lang in languages: update_cached_value(key, new_value, lang) else: update_cached_value(key, new_value, language_code)
class BaseIndex(indexes.SearchIndex): i18n_enabled = askbot.is_multilingual() text = indexes.CharField(document=True, use_template=True) def _get_backend(self, using): """ We set the backend alias to be able to determine language in multilanguage setup. """ self._backend_alias = using return super(BaseIndex, self)._get_backend(using) def get_language(self, obj): return None def get_default_language(self, using): """ When using multiple languages, this allows us to specify a fallback based on the backend being used. """ return language_from_alias(using) or settings.LANGUAGE_CODE def get_current_language(self, using=None, obj=None): """ Helper method bound to ALWAYS return a language. When obj is not None, this calls self.get_language to try and get a language from obj, this is useful when the object itself defines it's language in a "language" field. If no language was found or obj is None, then we call self.get_default_language to try and get a fallback language. """ language = self.get_language(obj) if obj else None return language or self.get_default_language(using) def get_index_kwargs(self, language): """ This is called to filter the indexed queryset. """ return {} def index_queryset(self, using=None): self._get_backend(using) language = self.get_current_language(using) filter_kwargs = self.get_index_kwargs(language) return self.get_model().objects.filter(**filter_kwargs) def prepare(self, obj): current_language = self.get_current_language(using=self._backend_alias, obj=obj) with override(current_language): self.prepared_data = super(BaseIndex, self).prepare(obj) self.prepared_data['text'] = ' '.join( self.prepared_data['text'].split()) return self.prepared_data
def process_context(self, context): user = context['user'] context.update({ 'name': user.username, 'question_count': len(context['questions']), 'recipient_user': user, 'admin_email': askbot_settings.ADMIN_EMAIL, 'site_name': askbot_settings.APP_SHORT_NAME, 'is_multilingual': askbot.is_multilingual() }) return context
def run_full_text_search(query_set, query_text, text_search_vector_name): """runs full text search against the query set and the search text. All words in the query text are added to the search with the & operator - i.e. the more terms in search, the narrower it is. It is also assumed that we ar searching in the same table as the query set was built against, also it is assumed that the table has text search vector stored in the column called with value of`text_search_vector_name`. """ original_qs = query_set table_name = query_set.model._meta.db_table rank_clause = 'ts_rank(' + table_name + \ '.' + text_search_vector_name + \ ', plainto_tsquery(%s, %s))' where_clause = table_name + '.' + \ text_search_vector_name + \ ' @@ plainto_tsquery(%s, %s)' language_code = get_language() #a hack with japanese search for the short queries if language_code in ['ja', 'zh-cn'] and len(query_text) in (1, 2): mul = 4/len(query_text) #4 for 1 and 2 for 2 query_text = (query_text + ' ')*mul #the table name is a hack, because user does not have the language code if askbot.is_multilingual() and table_name == 'askbot_thread': where_clause += " AND " + table_name + \ '.' + "language_code='" + language_code + "'" search_query = '|'.join(query_text.split())#apply "OR" operator language_name = LANGUAGE_NAMES.get(language_code, 'english') extra_params = (language_name, search_query,) extra_kwargs = { 'select': {'relevance': rank_clause}, 'where': [where_clause,], 'params': extra_params, 'select_params': extra_params, } result_qs = query_set.extra(**extra_kwargs) #added to allow search that can be ignored by postgres FTS. if not result_qs and len(query_text) < 5: return original_qs.filter( models.Q(title__icontains=search_query) | models.Q(tagnames__icontains=search_query) | models.Q(posts__text__icontains = search_query) ).extra(select={'relevance': rank_clause}, select_params=extra_params) return result_qs
def test_locale_middlewares(): django_locale_middleware = 'django.middleware.locale.LocaleMiddleware' askbot_locale_middleware = 'askbot.middleware.locale.LocaleMiddleware' errors = list() if askbot.is_multilingual(): if askbot_locale_middleware in django_settings.MIDDLEWARE_CLASSES: errors.append("Please remove '%s' from your MIDDLEWARE_CLASSES" % askbot_locale_middleware) if django_locale_middleware not in django_settings.MIDDLEWARE_CLASSES: errors.append("Please add '%s' to your MIDDLEWARE_CLASSES" % django_locale_middleware) print_errors(errors)
def user_select_languages(request, id=None, slug=None): if request.user.is_anonymous(): raise django_exceptions.PermissionDenied user = get_object_or_404(models.User, id=id) if not askbot.is_multilingual() or \ not(request.user.id == user.id or request.user.is_administrator()): raise django_exceptions.PermissionDenied if request.method == 'POST': #todo: add form to clean languages form = forms.LanguagePrefsForm(request.POST) if form.is_valid(): user.set_languages(form.cleaned_data['languages']) user.save() profile = user.askbot_profile profile.primary_language = form.cleaned_data['primary_language'] profile.save() redirect_url = reverse( 'user_select_languages', kwargs={ 'id': user.id, 'slug': slugify(user.username) } ) return HttpResponseRedirect(redirect_url) else: languages = user.languages.split() initial={ 'languages': languages, 'primary_language': languages[0] } form = forms.LanguagePrefsForm(initial=initial) data = { 'view_user': user, 'languages_form': form, 'tab_name': 'langs', 'page_class': 'user-profile-page', } return render(request, 'user_profile/user_languages.html', data)
def factory(**options): # JINJA2_EXTENSIONS was a thing in Coffin. We keep it around because it # may be used in Askbot. Should think about deprecating its use. options["extensions"] = DEFAULT_EXTENSIONS \ + list(django_settings.JINJA2_EXTENSIONS) askbot_globals = { 'settings': askbot_settings, 'hasattr': hasattr, 'encode_jwt': encode_jwt } mother_of_all_loaders = options.pop('loader') skins = utils.get_available_skins() if askbot.is_multilingual() or HAS_ASKBOT_LOCALE_MIDDLEWARE: languages = list(dict(django_settings.LANGUAGES).keys()) else: languages = [django_settings.LANGUAGE_CODE] # create an environment for each skin and language we might serve # Jinja2 Environments know a concept called "overlays" which cries for # consideration here. It may greatly simplify loading templatetags ... all_combinations = [(name, lang) for name in skins for lang in languages] for name, lang in all_combinations: skin_basedir = utils.get_path_to_skin(name) options["skin"] = name options["language_code"] = lang options['loader'] = deepcopy(mother_of_all_loaders) options['loader'].searchpath = [f'{skin_basedir}/jinja2' ] + options['loader'].searchpath env = SkinEnvironment(**options) env.globals.update(askbot_globals) env.set_language(lang) load_templatetags() # give Django what it asked for default_sibling = SkinEnvironment.build_sibling_key( [askbot_settings.ASKBOT_DEFAULT_SKIN, get_language()]) return SkinEnvironment.siblings[default_sibling]
def user_select_languages(request, id=None, slug=None): if request.user.is_anonymous(): raise django_exceptions.PermissionDenied user = get_object_or_404(models.User, id=id) if not askbot.is_multilingual() or \ not(request.user.id == user.id or request.user.is_administrator()): raise django_exceptions.PermissionDenied if request.method == 'POST': #todo: add form to clean languages form = forms.LanguagePrefsForm(request.POST) if form.is_valid(): user.set_languages(form.cleaned_data['languages']) user.set_primary_language(form.cleaned_data['primary_language']) user.save() redirect_url = reverse( 'user_select_languages', kwargs={ 'id': user.id, 'slug': slugify(user.username) } ) return HttpResponseRedirect(redirect_url) else: languages = user.languages.split() initial={ 'languages': languages, 'primary_language': languages[0] } form = forms.LanguagePrefsForm(initial=initial) data = { 'view_user': user, 'languages_form': form, 'tab_name': 'langs', 'page_class': 'user-profile-page', } return render(request, 'user_profile/user_languages.html', data)
def process_context(self, context): to_user = context.get('to_user') from_user = context.get('from_user') post = context.get('post') update_activity = context.get('update_activity') update_type = self.get_update_type(update_activity) #unhandled update_type 'post_shared' #user_action = _('%(user)s shared a %(post_link)s.') origin_post = post.get_origin_post() post_url = site_url(post.get_absolute_url()) can_reply = to_user.can_post_by_email() from askbot.models import get_reply_to_addresses reply_address, alt_reply_address = get_reply_to_addresses( to_user, post) alt_reply_subject = urllib.quote( ('Re: ' + post.thread.title).encode('utf-8')) return { 'admin_email': askbot_settings.ADMIN_EMAIL, 'recipient_user': to_user, 'update_author_name': from_user.username, 'receiving_user_name': to_user.username, 'receiving_user_karma': to_user.reputation, 'reply_by_email_karma_threshold': askbot_settings.MIN_REP_TO_POST_BY_EMAIL, 'can_reply': can_reply, 'update_type': update_type, 'update_activity': update_activity, 'post': post, 'post_url': post_url, 'origin_post': origin_post, 'thread_title': origin_post.thread.title, 'reply_address': reply_address, 'alt_reply_address': alt_reply_address, 'alt_reply_subject': alt_reply_subject, 'is_multilingual': askbot.is_multilingual(), 'reply_sep_tpl': const.SIMPLE_REPLY_SEPARATOR_TEMPLATE }
def process_context(self, context): to_user = context.get("to_user") from_user = context.get("from_user") post = context.get("post") update_activity = context.get("update_activity") update_type = self.get_update_type(update_activity) # unhandled update_type 'post_shared' # user_action = _('%(user)s shared a %(post_link)s.') origin_post = post.get_origin_post() post_url = site_url(post.get_absolute_url()) can_reply = to_user.can_post_by_email() from askbot.models import get_reply_to_addresses reply_address, alt_reply_address = get_reply_to_addresses(to_user, post) alt_reply_subject = urllib.quote(("Re: " + post.thread.title).encode("utf-8")) return { "admin_email": askbot_settings.ADMIN_EMAIL, "recipient_user": to_user, "update_author_name": from_user.username, "receiving_user_name": to_user.username, "receiving_user_karma": to_user.reputation, "reply_by_email_karma_threshold": askbot_settings.MIN_REP_TO_POST_BY_EMAIL, "can_reply": can_reply, "update_type": update_type, "update_activity": update_activity, "post": post, "post_url": post_url, "origin_post": origin_post, "thread_title": origin_post.thread.title, "reply_address": reply_address, "alt_reply_address": alt_reply_address, "alt_reply_subject": alt_reply_subject, "is_multilingual": askbot.is_multilingual(), "reply_sep_tpl": const.SIMPLE_REPLY_SEPARATOR_TEMPLATE, }
def process_context(self, context): to_user = context.get('to_user') from_user = context.get('from_user') post = context.get('post') update_activity = context.get('update_activity') update_type = self.get_update_type(update_activity) #unhandled update_type 'post_shared' #user_action = _('%(user)s shared a %(post_link)s.') origin_post = post.get_origin_post() post_url = site_url(post.get_absolute_url()) can_reply = to_user.can_post_by_email() from askbot.models import get_reply_to_addresses reply_address, alt_reply_address = get_reply_to_addresses(to_user, post) alt_reply_subject = urllib.quote(('Re: ' + post.thread.title).encode('utf-8')) return { 'admin_email': askbot_settings.ADMIN_EMAIL, 'recipient_user': to_user, 'update_author_name': from_user.username, 'receiving_user_name': to_user.username, 'receiving_user_karma': to_user.reputation, 'reply_by_email_karma_threshold': askbot_settings.MIN_REP_TO_POST_BY_EMAIL, 'can_reply': can_reply, 'update_type': update_type, 'update_activity': update_activity, 'post': post, 'post_url': post_url, 'origin_post': origin_post, 'thread_title': origin_post.thread.title, 'reply_address': reply_address, 'alt_reply_address': alt_reply_address, 'alt_reply_subject': alt_reply_subject, 'is_multilingual': askbot.is_multilingual(), 'reply_sep_tpl': const.SIMPLE_REPLY_SEPARATOR_TEMPLATE }
def get_alias(): if askbot.is_multilingual(): language = get_language() return alias_from_language(language) return DEFAULT_ALIAS
def get_updated_questions_for_user(self, user): """ retreive relevant question updates for the user according to their subscriptions and recorded question views """ user_feeds = EmailFeedSetting.objects.filter( subscriber=user ).exclude(frequency__in=('n', 'i')) should_proceed = False for feed in user_feeds: if feed.should_send_now() == True: should_proceed = True break #shortcircuit - if there is no ripe feed to work on for this user if should_proceed == False: return {} #these are placeholders for separate query sets per question group #there are four groups - one for each EmailFeedSetting.feed_type #and each group has subtypes A and B #that's because of the strange thing commented below #see note on Q and F objects marked with todo tag q_sel_A = None q_sel_B = None q_ask_A = None q_ask_B = None q_ans_A = None q_ans_B = None q_all_A = None q_all_B = None #base question query set for this user #basic things - not deleted, not closed, not too old #not last edited by the same user base_qs = Post.objects.get_questions().exclude( thread__last_activity_by=user ).exclude( thread__last_activity_at__lt=user.date_joined#exclude old stuff ).exclude( deleted=True ).exclude( thread__closed=True ).order_by('-thread__last_activity_at') if askbot_settings.CONTENT_MODERATION_MODE == 'premoderation': base_qs = base_qs.filter(approved = True) #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | # Q(viewed__who=user,viewed__when__lt=F('thread__last_activity_at')) #returns way more questions than you might think it should #so because of that I've created separate query sets Q_set2 and Q_set3 #plus two separate queries run faster! #build two two queries based #questions that are not seen by the user at all not_seen_qs = base_qs.filter(~Q(viewed__who=user)) #questions that were seen, but before last modification seen_before_last_mod_qs = base_qs.filter( Q(viewed__who=user, viewed__when__lt=F('thread__last_activity_at')) ) #shorten variables for convenience Q_set_A = not_seen_qs Q_set_B = seen_before_last_mod_qs if askbot.is_multilingual(): languages = user.languages.split() else: languages = None for feed in user_feeds: if feed.feed_type == 'm_and_c': #alerts on mentions and comments are processed separately #because comments to questions do not trigger change of last_updated #this may be changed in the future though, see #http://askbot.org/en/question/96/ continue #each group of updates represented by the corresponding #query set has it's own cutoff time #that cutoff time is computed for each user individually #and stored as a parameter "cutoff_time" #we won't send email for a given question if an email has been #sent after that cutoff_time if feed.should_send_now(): if DEBUG_THIS_COMMAND == False: feed.mark_reported_now() cutoff_time = feed.get_previous_report_cutoff_time() if feed.feed_type == 'q_sel': q_sel_A = Q_set_A.filter(thread__followed_by=user) q_sel_A.cutoff_time = cutoff_time #store cutoff time per query set q_sel_B = Q_set_B.filter(thread__followed_by=user) q_sel_B.cutoff_time = cutoff_time #store cutoff time per query set elif feed.feed_type == 'q_ask': q_ask_A = Q_set_A.filter(author=user) q_ask_A.cutoff_time = cutoff_time q_ask_B = Q_set_B.filter(author=user) q_ask_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_ans': q_ans_A = Q_set_A.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_A = q_ans_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_A.cutoff_time = cutoff_time q_ans_B = Q_set_B.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_B = q_ans_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_all': q_all_A = user.get_tag_filtered_questions(Q_set_A) q_all_B = user.get_tag_filtered_questions(Q_set_B) q_all_A = q_all_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_B = q_all_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_A.cutoff_time = cutoff_time q_all_B.cutoff_time = cutoff_time #build ordered list questions for the email report q_list = SortedDict() #todo: refactor q_list into a separate class? extend_question_list(q_sel_A, q_list, languages=languages) extend_question_list(q_sel_B, q_list, languages=languages) #build list of comment and mention responses here #it is separate because posts are not marked as changed #when people add comments #mention responses could be collected in the loop above, but #it is inconvenient, because feed_type m_and_c bundles the two #also we collect metadata for these here try: feed = user_feeds.get(feed_type='m_and_c') if feed.should_send_now(): cutoff_time = feed.get_previous_report_cutoff_time() comments = Post.objects.get_comments().filter( added_at__lt=cutoff_time ).exclude(author=user).select_related('parent') q_commented = list() for c in comments: post = c.parent if post.author_id != user.pk: continue #skip is post was seen by the user after #the comment posting time q_commented.append(post.get_origin_post()) extend_question_list( q_commented, q_list, cutoff_time=cutoff_time, add_comment=True, languages=languages ) mentions = Activity.objects.get_mentions( mentioned_at__lt=cutoff_time, mentioned_whom=user ) #print 'have %d mentions' % len(mentions) #MM = Activity.objects.filter(activity_type = const.TYPE_ACTIVITY_MENTION) #print 'have %d total mentions' % len(MM) #for m in MM: # print m mention_posts = get_all_origin_posts(mentions) q_mentions_id = [q.id for q in mention_posts] q_mentions_A = Q_set_A.filter(id__in = q_mentions_id) q_mentions_A.cutoff_time = cutoff_time extend_question_list( q_mentions_A, q_list, add_mention=True, languages=languages ) q_mentions_B = Q_set_B.filter(id__in = q_mentions_id) q_mentions_B.cutoff_time = cutoff_time extend_question_list( q_mentions_B, q_list, add_mention=True, languages=languages ) except EmailFeedSetting.DoesNotExist: pass if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING: extend_question_list(q_all_A, q_list, languages=languages) extend_question_list(q_all_B, q_list, languages=languages) extend_question_list(q_ask_A, q_list, limit=True, languages=languages) extend_question_list(q_ask_B, q_list, limit=True, languages=languages) extend_question_list(q_ans_A, q_list, limit=True, languages=languages) extend_question_list(q_ans_B, q_list, limit=True, languages=languages) if user.email_tag_filter_strategy == const.EXCLUDE_IGNORED: extend_question_list(q_all_A, q_list, limit=True, languages=languages) extend_question_list(q_all_B, q_list, limit=True, languages=languages) ctype = ContentType.objects.get_for_model(Post) EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT #up to this point we still don't know if emails about #collected questions were sent recently #the next loop examines activity record and decides #for each question, whether it needs to be included or not #into the report for q, meta_data in q_list.items(): #this loop edits meta_data for each question #so that user will receive counts on new edits new answers, etc #and marks questions that need to be skipped #because an email about them was sent recently enough #also it keeps a record of latest email activity per question per user try: #todo: is it possible to use content_object here, instead of #content type and object_id pair? update_info = Activity.objects.get( user=user, content_type=ctype, object_id=q.id, activity_type=EMAIL_UPDATE_ACTIVITY ) emailed_at = update_info.active_at except Activity.DoesNotExist: update_info = Activity( user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY ) emailed_at = datetime.datetime(1970, 1, 1) #long time ago if django_settings.USE_TZ: emailed_at = timezone.make_aware(emailed_at, timezone.utc) except Activity.MultipleObjectsReturned: raise Exception( 'server error - multiple question email activities ' 'found per user-question pair' ) cutoff_time = meta_data['cutoff_time']#cutoff time for the question #skip question if we need to wait longer because #the delay before the next email has not yet elapsed #or if last email was sent after the most recent modification if emailed_at > cutoff_time or emailed_at > q.thread.last_activity_at: meta_data['skip'] = True continue #collect info on all sorts of news that happened after #the most recent emailing to the user about this question q_rev = q.revisions.filter(revised_at__gt=emailed_at) q_rev = q_rev.exclude(author=user) #now update all sorts of metadata per question meta_data['q_rev'] = len(q_rev) if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at: meta_data['q_rev'] = 0 meta_data['new_q'] = True else: meta_data['new_q'] = False new_ans = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ) new_ans = new_ans.exclude(author=user) meta_data['new_ans'] = len(new_ans) ans_ids = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ).values_list('id', flat=True) ans_rev = PostRevision.objects.filter(post__id__in = ans_ids) ans_rev = ans_rev.exclude(author=user).distinct() meta_data['ans_rev'] = len(ans_rev) comments = meta_data.get('comments', 0) mentions = meta_data.get('mentions', 0) #print meta_data #finally skip question if there are no news indeed if len(q_rev) + len(new_ans) + len(ans_rev) + comments + mentions == 0: meta_data['skip'] = True #print 'skipping' else: meta_data['skip'] = False #print 'not skipping' update_info.active_at = timezone.now() if DEBUG_THIS_COMMAND == False: update_info.save() #save question email update activity #q_list is actually an ordered dictionary #print 'user %s gets %d' % (user.username, len(q_list.keys())) #todo: sort question list by update time return q_list
from django.conf import settings try: from django.conf.urls import handler404 from django.conf.urls import include, patterns, url except ImportError: from django.conf.urls.defaults import handler404 from django.conf.urls.defaults import include, patterns, url import askbot from askbot.views.error import internal_error as handler500 from django.conf import settings from django.contrib import admin admin.autodiscover() if askbot.is_multilingual(): from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns('', (r'%s' % settings.ASKBOT_URL, include('askbot.urls')) ) else: urlpatterns = patterns('', (r'%s' % settings.ASKBOT_URL, include('askbot.urls')) ) urlpatterns += patterns('', (r'^admin/', include(admin.site.urls)), #(r'^cache/', include('keyedcache.urls')), - broken views disable for now #(r'^settings/', include('askbot.deps.livesettings.urls')), (r'^followit/', include('followit.urls')), (r'^tinymce/', include('tinymce.urls')),
settings.register( values.ImageValue( GENERAL_SKIN_SETTINGS, 'SITE_LOGO_URL', description = _('Q&A site logo'), help_text = _( 'To change the logo, select new file, ' 'then submit this whole form.' ), default = '/images/logo.gif', url_resolver = skin_utils.get_media_url ) ) #cannot use HAS_ASKBOT_LOCALE_MIDDLEWARE due to circular import error if not askbot.is_multilingual() and \ 'askbot.middleware.locale.LocaleMiddleware' in django_settings.MIDDLEWARE_CLASSES: settings.register( values.StringValue( GENERAL_SKIN_SETTINGS, 'ASKBOT_LANGUAGE', default = django_settings.LANGUAGE_CODE, choices = django_settings.LANGUAGES, description = _('Select Language'), ) ) settings.register( values.BooleanValue( GENERAL_SKIN_SETTINGS, 'SHOW_LOGO',
try: from django.conf.urls import handler404 from django.conf.urls import include, url except ImportError: from django.conf.urls.defaults import handler404 from django.conf.urls.defaults import include, url from askbot import is_multilingual from askbot.views.error import internal_error as handler500 from django.conf import settings from django.contrib import admin from django.views import static as StaticViews admin.autodiscover() if is_multilingual(): from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( url(r'%s' % settings.ASKBOT_URL, include('askbot.urls')) ) else: urlpatterns = [ url(r'%s' % settings.ASKBOT_URL, include('askbot.urls')) ] urlpatterns += [ url(r'^admin/', admin.site.urls), #(r'^cache/', include('keyedcache.urls')), - broken views disable for now #(r'^settings/', include('askbot.deps.livesettings.urls')), url(r'^followit/', include('followit.urls')), url(r'^tinymce/', include('tinymce.urls')),
def application_settings(request): """The context processor function""" #if not request.path.startswith('/' + settings.ASKBOT_URL): # #todo: this is a really ugly hack, will only work # #when askbot is installed not at the home page. # #this will not work for the # #heavy modders of askbot, because their custom pages # #will not receive the askbot settings in the context # #to solve this properly we should probably explicitly # #add settings to the context per page # return {} my_settings = askbot_settings.as_dict() my_settings['LANGUAGE_CODE'] = getattr(request, 'LANGUAGE_CODE', settings.LANGUAGE_CODE) my_settings['LANGUAGE_MODE'] = askbot.get_lang_mode() my_settings['MULTILINGUAL'] = askbot.is_multilingual() my_settings['LANGUAGES_DICT'] = dict(getattr(settings, 'LANGUAGES', [])) my_settings['ALLOWED_UPLOAD_FILE_TYPES'] = \ settings.ASKBOT_ALLOWED_UPLOAD_FILE_TYPES my_settings['ASKBOT_URL'] = settings.ASKBOT_URL my_settings['STATIC_URL'] = settings.STATIC_URL my_settings['IP_MODERATION_ENABLED'] = getattr( settings, 'ASKBOT_IP_MODERATION_ENABLED', False) my_settings['USE_LOCAL_FONTS'] = getattr(settings, 'ASKBOT_USE_LOCAL_FONTS', False) my_settings['CSRF_COOKIE_NAME'] = settings.CSRF_COOKIE_NAME my_settings['DEBUG'] = settings.DEBUG my_settings['USING_RUNSERVER'] = 'runserver' in sys.argv my_settings['ASKBOT_VERSION'] = askbot.get_version() my_settings['LOGIN_URL'] = url_utils.get_login_url() my_settings['LOGOUT_URL'] = url_utils.get_logout_url() if my_settings['EDITOR_TYPE'] == 'tinymce': tinymce_plugins = settings.TINYMCE_DEFAULT_CONFIG.get('plugins', '').split(',') my_settings['TINYMCE_PLUGINS'] = map(lambda v: v.strip(), tinymce_plugins) else: my_settings['TINYMCE_PLUGINS'] = [] my_settings['LOGOUT_REDIRECT_URL'] = url_utils.get_logout_redirect_url() current_language = get_language() #for some languages we will start searching for shorter words if current_language == 'ja': #we need to open the search box and show info message about #the japanese lang search min_search_word_length = 1 else: min_search_word_length = my_settings['MIN_SEARCH_WORD_LENGTH'] need_scope_links = askbot_settings.ALL_SCOPE_ENABLED or \ askbot_settings.UNANSWERED_SCOPE_ENABLED or \ (request.user.is_authenticated() and askbot_settings.FOLLOWED_SCOPE_ENABLED) context = { 'base_url': site_url(''), 'csrf_token': get_or_create_csrf_token(request), 'empty_search_state': SearchState.get_empty(), 'min_search_word_length': min_search_word_length, 'current_language_code': current_language, 'settings': my_settings, 'moderation_items': api.get_info_on_moderation_items(request.user), 'need_scope_links': need_scope_links, 'noscript_url': const.DEPENDENCY_URLS['noscript'], } use_askbot_login = '******' in settings.INSTALLED_APPS my_settings['USE_ASKBOT_LOGIN_SYSTEM'] = use_askbot_login if use_askbot_login and request.user.is_anonymous(): from askbot.deps.django_authopenid import context as login_context context.update(login_context.login_context(request)) if askbot_settings.GROUPS_ENABLED: #calculate context needed to list all the groups def _get_group_url(group): """calculates url to the group based on its id and name""" group_slug = slugify(group['name']) return reverse('users_by_group', kwargs={ 'group_id': group['id'], 'group_slug': group_slug }) #load id's and names of all groups global_group = models.Group.objects.get_global_group() groups = models.Group.objects.exclude_personal() groups = groups.exclude(id=global_group.id) groups_data = list(groups.values('id', 'name')) #sort groups_data alphanumerically, but case-insensitive groups_data = sorted( groups_data, lambda x, y: cmp(x['name'].lower(), y['name'].lower())) #insert data for the global group at the first position groups_data.insert(0, { 'id': global_group.id, 'name': global_group.name }) #build group_list for the context group_list = list() for group in groups_data: link = _get_group_url(group) group_list.append({'name': group['name'], 'link': link}) context['group_list'] = simplejson.dumps(group_list) return context
def application_settings(request): """The context processor function""" #if not request.path.startswith('/' + settings.ASKBOT_URL): # #todo: this is a really ugly hack, will only work # #when askbot is installed not at the home page. # #this will not work for the # #heavy modders of askbot, because their custom pages # #will not receive the askbot settings in the context # #to solve this properly we should probably explicitly # #add settings to the context per page # return {} my_settings = askbot_settings.as_dict() my_settings['LANGUAGE_CODE'] = getattr(request, 'LANGUAGE_CODE', settings.LANGUAGE_CODE) my_settings['LANGUAGE_MODE'] = askbot.get_lang_mode() my_settings['MULTILINGUAL'] = askbot.is_multilingual() my_settings['LANGUAGES_DICT'] = dict(getattr(settings, 'LANGUAGES', [])) my_settings['ALLOWED_UPLOAD_FILE_TYPES'] = \ settings.ASKBOT_ALLOWED_UPLOAD_FILE_TYPES my_settings['ASKBOT_URL'] = settings.ASKBOT_URL my_settings['STATIC_URL'] = settings.STATIC_URL my_settings['IP_MODERATION_ENABLED'] = getattr(settings, 'ASKBOT_IP_MODERATION_ENABLED', False) my_settings['USE_LOCAL_FONTS'] = getattr( settings, 'ASKBOT_USE_LOCAL_FONTS', False ) my_settings['CSRF_COOKIE_NAME'] = settings.CSRF_COOKIE_NAME my_settings['DEBUG'] = settings.DEBUG my_settings['USING_RUNSERVER'] = 'runserver' in sys.argv my_settings['ASKBOT_VERSION'] = askbot.get_version() my_settings['LOGIN_URL'] = url_utils.get_login_url() my_settings['LOGOUT_URL'] = url_utils.get_logout_url() if my_settings['EDITOR_TYPE'] == 'tinymce': tinymce_plugins = settings.TINYMCE_DEFAULT_CONFIG.get('plugins', '').split(',') my_settings['TINYMCE_PLUGINS'] = map(lambda v: v.strip(), tinymce_plugins) my_settings['TINYMCE_EDITOR_DESELECTOR'] = settings.TINYMCE_DEFAULT_CONFIG['editor_deselector'] my_settings['TINYMCE_CONFIG_JSON'] = json.dumps(settings.TINYMCE_DEFAULT_CONFIG) else: my_settings['TINYMCE_PLUGINS'] = [] my_settings['TINYMCE_EDITOR_DESELECTOR'] = '' my_settings['LOGOUT_REDIRECT_URL'] = url_utils.get_logout_redirect_url() current_language = get_language() #for some languages we will start searching for shorter words if current_language == 'ja': #we need to open the search box and show info message about #the japanese lang search min_search_word_length = 1 else: min_search_word_length = my_settings['MIN_SEARCH_WORD_LENGTH'] need_scope_links = askbot_settings.ALL_SCOPE_ENABLED or \ askbot_settings.UNANSWERED_SCOPE_ENABLED or \ (request.user.is_authenticated() and askbot_settings.FOLLOWED_SCOPE_ENABLED) context = { 'base_url': site_url(''), 'csrf_token': get_or_create_csrf_token(request), 'empty_search_state': SearchState.get_empty(), 'min_search_word_length': min_search_word_length, 'current_language_code': current_language, 'settings': my_settings, 'moderation_items': api.get_info_on_moderation_items(request.user), 'need_scope_links': need_scope_links, 'noscript_url': const.DEPENDENCY_URLS['noscript'], } use_askbot_login = '******' in settings.INSTALLED_APPS my_settings['USE_ASKBOT_LOGIN_SYSTEM'] = use_askbot_login if use_askbot_login and request.user.is_anonymous(): from askbot.deps.django_authopenid import context as login_context context.update(login_context.login_context(request)) if askbot_settings.GROUPS_ENABLED: #calculate context needed to list all the groups def _get_group_url(group): """calculates url to the group based on its id and name""" group_slug = slugify(group['name']) return reverse( 'users_by_group', kwargs={'group_id': group['id'], 'group_slug': group_slug} ) #load id's and names of all groups global_group = models.Group.objects.get_global_group() groups = models.Group.objects.exclude_personal() groups = groups.exclude(id=global_group.id) groups_data = list(groups.values('id', 'name')) #sort groups_data alphanumerically, but case-insensitive groups_data = sorted( groups_data, lambda x, y: cmp(x['name'].lower(), y['name'].lower()) ) #insert data for the global group at the first position groups_data.insert(0, {'id': global_group.id, 'name': global_group.name}) #build group_list for the context group_list = list() for group in groups_data: link = _get_group_url(group) group_list.append({'name': group['name'], 'link': link}) context['group_list'] = simplejson.dumps(group_list) if askbot_settings.EDITOR_TYPE == 'tinymce': from tinymce.widgets import TinyMCE context['tinymce'] = TinyMCE() return context
def show_users(request, by_group=False, group_id=None, group_slug=None): """Users view, including listing of users by group""" if askbot_settings.GROUPS_ENABLED and not by_group: default_group = models.Group.objects.get_global_group() group_slug = slugify(default_group.name) new_url = reverse('users_by_group', kwargs={ 'group_id': default_group.id, 'group_slug': group_slug }) return HttpResponseRedirect(new_url) users = models.User.objects.exclude(askbot_profile__status='b').exclude( is_active=False).select_related('askbot_profile') if askbot.is_multilingual(): users = users.filter( localized_askbot_profiles__language_code=get_language(), localized_askbot_profiles__is_claimed=True) group = None group_email_moderation_enabled = False user_acceptance_level = 'closed' user_membership_level = 'none' if by_group == True: if askbot_settings.GROUPS_ENABLED == False: raise Http404 if group_id: if all((group_id, group_slug)) == False: return HttpResponseRedirect('groups') else: try: group = models.Group.objects.get(id=group_id) group_email_moderation_enabled = ( askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED \ and askbot_settings.CONTENT_MODERATION_MODE == 'premoderation' ) user_acceptance_level = group.get_openness_level_for_user( request.user) except models.Group.DoesNotExist: raise Http404 if group_slug == slugify(group.name): #filter users by full group memberships #todo: refactor as Group.get_full_members() full_level = models.GroupMembership.FULL memberships = models.GroupMembership.objects.filter( group=group, level=full_level) user_ids = memberships.values_list('user__id', flat=True) users = users.filter(id__in=user_ids) if request.user.is_authenticated(): membership = request.user.get_group_membership(group) if membership: user_membership_level = membership.get_level_display( ) else: group_page_url = reverse('users_by_group', kwargs={ 'group_id': group.id, 'group_slug': slugify(group.name) }) return HttpResponseRedirect(group_page_url) is_paginated = True form = forms.ShowUsersForm(request.REQUEST) form.full_clean() #always valid sort_method = form.cleaned_data['sort'] page = form.cleaned_data['page'] search_query = form.cleaned_data['query'] if search_query == '': if sort_method == 'newest': order_by_parameter = '-date_joined' elif sort_method == 'last': order_by_parameter = 'date_joined' elif sort_method == 'name': order_by_parameter = 'username' else: # default if askbot.is_multilingual(): order_by_parameter = '-localized_askbot_profiles__reputation' else: order_by_parameter = '-askbot_profile__reputation' objects_list = Paginator(users.order_by(order_by_parameter), askbot_settings.USERS_PAGE_SIZE) base_url = request.path + '?sort=%s&' % sort_method else: sort_method = 'reputation' matching_users = models.get_users_by_text_query(search_query, users) objects_list = Paginator( matching_users.order_by('-askbot_profile__reputation'), askbot_settings.USERS_PAGE_SIZE) base_url = request.path + '?name=%s&sort=%s&' % (search_query, sort_method) try: users_page = objects_list.page(page) except (EmptyPage, InvalidPage): users_page = objects_list.page(objects_list.num_pages) paginator_data = { 'is_paginated': is_paginated, 'pages': objects_list.num_pages, 'current_page_number': page, 'page_object': users_page, 'base_url': base_url } paginator_context = functions.setup_paginator(paginator_data) # #todo: move to contexts #extra context for the groups if askbot_settings.GROUPS_ENABLED: #todo: cleanup this branched code after groups are migrated to auth_group user_groups = models.Group.objects.exclude_personal() if len(user_groups) <= 1: assert (user_groups[0].name == askbot_settings.GLOBAL_GROUP_NAME) user_groups = None group_openness_choices = models.Group().get_openness_choices() else: user_groups = None group_openness_choices = None data = { 'active_tab': 'users', 'group': group, 'group_email_moderation_enabled': group_email_moderation_enabled, 'group_openness_choices': group_openness_choices, 'page_class': 'users-page', 'paginator_context': paginator_context, 'search_query': search_query, 'tab_id': sort_method, 'user_acceptance_level': user_acceptance_level, 'user_count': objects_list.count, 'user_groups': user_groups, 'user_membership_level': user_membership_level, 'users': users_page, } return render(request, 'users.html', data)
def application_settings(request): """The context processor function""" # if not request.path.startswith('/' + settings.ASKBOT_URL): # #todo: this is a really ugly hack, will only work # #when askbot is installed not at the home page. # #this will not work for the # #heavy modders of askbot, because their custom pages # #will not receive the askbot settings in the context # #to solve this properly we should probably explicitly # #add settings to the context per page # return {} my_settings = askbot_settings.as_dict() my_settings['LANGUAGE_CODE'] = getattr(request, 'LANGUAGE_CODE', settings.LANGUAGE_CODE) my_settings['LANGUAGE_MODE'] = askbot.get_lang_mode() my_settings['MULTILINGUAL'] = askbot.is_multilingual() my_settings['LANGUAGES_DICT'] = dict(getattr(settings, 'LANGUAGES', [])) my_settings[ 'ALLOWED_UPLOAD_FILE_TYPES'] = settings.ASKBOT_ALLOWED_UPLOAD_FILE_TYPES my_settings['ASKBOT_URL'] = settings.ASKBOT_URL my_settings['STATIC_URL'] = settings.STATIC_URL my_settings['IP_MODERATION_ENABLED'] = getattr( settings, 'ASKBOT_IP_MODERATION_ENABLED', False) my_settings['USE_LOCAL_FONTS'] = getattr(settings, 'ASKBOT_USE_LOCAL_FONTS', False) my_settings['CSRF_COOKIE_NAME'] = settings.CSRF_COOKIE_NAME my_settings['DEBUG'] = settings.DEBUG my_settings['USING_RUNSERVER'] = 'runserver' in sys.argv my_settings['ASKBOT_VERSION'] = askbot.get_version() my_settings['LOGIN_URL'] = url_utils.get_login_url() my_settings['LOGOUT_URL'] = url_utils.get_logout_url() if my_settings['EDITOR_TYPE'] == 'tinymce': tinymce_plugins = settings.TINYMCE_DEFAULT_CONFIG.get('plugins', '').split(',') my_settings['TINYMCE_PLUGINS'] = [v.strip() for v in tinymce_plugins] my_settings[ 'TINYMCE_EDITOR_DESELECTOR'] = settings.TINYMCE_DEFAULT_CONFIG[ 'editor_deselector'] my_settings['TINYMCE_CONFIG_JSON'] = json.dumps( settings.TINYMCE_DEFAULT_CONFIG) else: my_settings['TINYMCE_PLUGINS'] = [] my_settings['TINYMCE_EDITOR_DESELECTOR'] = '' my_settings['LOGOUT_REDIRECT_URL'] = url_utils.get_logout_redirect_url() current_language = get_language() # for some languages we will start searching for shorter words if current_language == 'ja': # we need to open the search box and show info message about # the japanese lang search min_search_word_length = 1 else: min_search_word_length = my_settings['MIN_SEARCH_WORD_LENGTH'] need_scope_links = askbot_settings.ALL_SCOPE_ENABLED or \ askbot_settings.UNANSWERED_SCOPE_ENABLED or \ (request.user.is_authenticated and askbot_settings.FOLLOWED_SCOPE_ENABLED) context = { 'base_url': site_url(''), 'csrf_token': csrf.get_token(request), 'empty_search_state': SearchState.get_empty(), 'min_search_word_length': min_search_word_length, 'current_language_code': current_language, 'settings': my_settings, 'moderation_items': api.get_info_on_moderation_items(request.user), 'need_scope_links': need_scope_links, 'noscript_url': const.DEPENDENCY_URLS['noscript'], } use_askbot_login = '******' in settings.INSTALLED_APPS my_settings['USE_ASKBOT_LOGIN_SYSTEM'] = use_askbot_login if use_askbot_login and request.user.is_anonymous: from askbot.deps.django_authopenid import context as login_context context.update(login_context.login_context(request)) context['group_list'] = json.dumps(make_group_list()) if askbot_settings.EDITOR_TYPE == 'tinymce': from tinymce.widgets import TinyMCE context['tinymce'] = TinyMCE() return context
from django.conf import settings try: from django.conf.urls import handler404 from django.conf.urls import include, patterns, url except ImportError: from django.conf.urls.defaults import handler404 from django.conf.urls.defaults import include, patterns, url import askbot from askbot.views.error import internal_error as handler500 from django.conf import settings from django.contrib import admin admin.autodiscover() if askbot.is_multilingual(): from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( '', (r'%s' % settings.ASKBOT_URL, include('askbot.urls'))) else: urlpatterns = patterns( '', (r'%s' % settings.ASKBOT_URL, include('askbot.urls'))) urlpatterns += patterns( '', (r'^admin/', include(admin.site.urls)), #(r'^cache/', include('keyedcache.urls')), - broken views disable for now #(r'^settings/', include('askbot.deps.livesettings.urls')), (r'^followit/', include('followit.urls')), (r'^tinymce/', include('tinymce.urls')), (r'^robots.txt$', include('robots.urls')),
skins[skin_code].set_language(language_code) #from askbot.templatetags import extra_filters_jinja as filters #skins[skin_name].filters['media'] = filters.media return skins def get_app_dir_env(language_code): env = AppDirectoryEnvironment( extensions=['jinja2.ext.i18n',], globals={'settings': askbot_settings} ) env.set_language(language_code) return env LOADERS = django_settings.TEMPLATES SKINS = dict() if askbot.is_multilingual() or HAS_ASKBOT_LOCALE_MIDDLEWARE: for lang in dict(django_settings.LANGUAGES).keys(): SKINS.update(load_skins(lang)) else: SKINS = load_skins(django_settings.LANGUAGE_CODE) APP_DIR_ENVS = dict() if 'askbot.skins.loaders.JinjaAppDirectoryLoader' in LOADERS: if askbot.is_multilingual() or HAS_ASKBOT_LOCALE_MIDDLEWARE: for lang in dict(django_settings.LANGUAGES).keys(): APP_DIR_ENVS[lang] = get_app_dir_env(lang) else: lang = django_settings.LANGUAGE_CODE APP_DIR_ENVS[lang] = get_app_dir_env(lang) def get_skin():
default='', description=_('Custom destination URL for the logo'), update_callback=logo_destination_callback)) settings.register( values.ImageValue(GENERAL_SKIN_SETTINGS, 'SITE_LOGO_URL', description=_('Q&A site logo'), help_text=_( 'To change the logo, select new file, then submit' 'this whole form.'), default='/images/logo.gif', url_resolver=skin_utils.get_media_url)) # Cannot use HAS_ASKBOT_LOCALE_MIDDLEWARE due to circular import error if not askbot.is_multilingual() and \ 'askbot.middleware.locale.LocaleMiddleware' in django_settings.MIDDLEWARE_CLASSES: settings.register( values.StringValue( GENERAL_SKIN_SETTINGS, 'ASKBOT_LANGUAGE', default=django_settings.LANGUAGE_CODE, choices=django_settings.LANGUAGES, description=_('Select Language'), )) settings.register( values.BooleanValue( GENERAL_SKIN_SETTINGS, 'SHOW_LOGO', description=_('Show logo'),
def get_updated_questions_for_user(self, user): """ retreive relevant question updates for the user according to their subscriptions and recorded question views """ user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude( frequency__in=('n', 'i')) should_proceed = False for feed in user_feeds: if feed.should_send_now() == True: should_proceed = True break #shortcircuit - if there is no ripe feed to work on for this user if should_proceed == False: return {} #these are placeholders for separate query sets per question group #there are four groups - one for each EmailFeedSetting.feed_type #and each group has subtypes A and B #that's because of the strange thing commented below #see note on Q and F objects marked with todo tag q_sel_A = None q_sel_B = None q_ask_A = None q_ask_B = None q_ans_A = None q_ans_B = None q_all_A = None q_all_B = None #base question query set for this user #basic things - not deleted, not closed, not too old #not last edited by the same user base_qs = Post.objects.get_questions( ).exclude(thread__last_activity_by=user).exclude( thread__last_activity_at__lt=user.date_joined #exclude old stuff ).exclude(deleted=True).exclude( thread__closed=True).order_by('-thread__last_activity_at') if askbot_settings.CONTENT_MODERATION_MODE == 'premoderation': base_qs = base_qs.filter(approved=True) #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | # Q(viewed__who=user,viewed__when__lt=F('thread__last_activity_at')) #returns way more questions than you might think it should #so because of that I've created separate query sets Q_set2 and Q_set3 #plus two separate queries run faster! #build two two queries based #questions that are not seen by the user at all not_seen_qs = base_qs.filter(~Q(viewed__who=user)) #questions that were seen, but before last modification seen_before_last_mod_qs = base_qs.filter( Q(viewed__who=user, viewed__when__lt=F('thread__last_activity_at'))) #shorten variables for convenience Q_set_A = not_seen_qs Q_set_B = seen_before_last_mod_qs if askbot.is_multilingual(): languages = user.languages.split() else: languages = None for feed in user_feeds: if feed.feed_type == 'm_and_c': #alerts on mentions and comments are processed separately #because comments to questions do not trigger change of last_updated #this may be changed in the future though, see #http://askbot.org/en/question/96/ continue #each group of updates represented by the corresponding #query set has it's own cutoff time #that cutoff time is computed for each user individually #and stored as a parameter "cutoff_time" #we won't send email for a given question if an email has been #sent after that cutoff_time if feed.should_send_now(): if DEBUG_THIS_COMMAND == False: feed.mark_reported_now() cutoff_time = feed.get_previous_report_cutoff_time() if feed.feed_type == 'q_sel': q_sel_A = Q_set_A.filter(thread__followed_by=user) q_sel_A.cutoff_time = cutoff_time #store cutoff time per query set q_sel_B = Q_set_B.filter(thread__followed_by=user) q_sel_B.cutoff_time = cutoff_time #store cutoff time per query set elif feed.feed_type == 'q_ask': q_ask_A = Q_set_A.filter(author=user) q_ask_A.cutoff_time = cutoff_time q_ask_B = Q_set_B.filter(author=user) q_ask_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_ans': q_ans_A = Q_set_A.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_A = q_ans_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_A.cutoff_time = cutoff_time q_ans_B = Q_set_B.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_B = q_ans_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_all': q_all_A = user.get_tag_filtered_questions(Q_set_A) q_all_B = user.get_tag_filtered_questions(Q_set_B) q_all_A = q_all_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_B = q_all_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_A.cutoff_time = cutoff_time q_all_B.cutoff_time = cutoff_time #build ordered list questions for the email report q_list = SortedDict() #todo: refactor q_list into a separate class? extend_question_list(q_sel_A, q_list, languages=languages) extend_question_list(q_sel_B, q_list, languages=languages) #build list of comment and mention responses here #it is separate because posts are not marked as changed #when people add comments #mention responses could be collected in the loop above, but #it is inconvenient, because feed_type m_and_c bundles the two #also we collect metadata for these here try: feed = user_feeds.get(feed_type='m_and_c') if feed.should_send_now(): cutoff_time = feed.get_previous_report_cutoff_time() comments = Post.objects.get_comments().filter( added_at__lt=cutoff_time).exclude( author=user).select_related('parent') q_commented = list() for c in comments: post = c.parent if post.author_id != user.pk: continue #skip is post was seen by the user after #the comment posting time q_commented.append(post.get_origin_post()) extend_question_list(q_commented, q_list, cutoff_time=cutoff_time, add_comment=True, languages=languages) mentions = Activity.objects.get_mentions( mentioned_at__lt=cutoff_time, mentioned_whom=user) #print 'have %d mentions' % len(mentions) #MM = Activity.objects.filter(activity_type = const.TYPE_ACTIVITY_MENTION) #print 'have %d total mentions' % len(MM) #for m in MM: # print m mention_posts = get_all_origin_posts(mentions) q_mentions_id = [q.id for q in mention_posts] q_mentions_A = Q_set_A.filter(id__in=q_mentions_id) q_mentions_A.cutoff_time = cutoff_time extend_question_list(q_mentions_A, q_list, add_mention=True, languages=languages) q_mentions_B = Q_set_B.filter(id__in=q_mentions_id) q_mentions_B.cutoff_time = cutoff_time extend_question_list(q_mentions_B, q_list, add_mention=True, languages=languages) except EmailFeedSetting.DoesNotExist: pass if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING: extend_question_list(q_all_A, q_list, languages=languages) extend_question_list(q_all_B, q_list, languages=languages) extend_question_list(q_ask_A, q_list, limit=True, languages=languages) extend_question_list(q_ask_B, q_list, limit=True, languages=languages) extend_question_list(q_ans_A, q_list, limit=True, languages=languages) extend_question_list(q_ans_B, q_list, limit=True, languages=languages) if user.email_tag_filter_strategy == const.EXCLUDE_IGNORED: extend_question_list(q_all_A, q_list, limit=True, languages=languages) extend_question_list(q_all_B, q_list, limit=True, languages=languages) ctype = ContentType.objects.get_for_model(Post) EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT #up to this point we still don't know if emails about #collected questions were sent recently #the next loop examines activity record and decides #for each question, whether it needs to be included or not #into the report for q, meta_data in q_list.items(): #this loop edits meta_data for each question #so that user will receive counts on new edits new answers, etc #and marks questions that need to be skipped #because an email about them was sent recently enough #also it keeps a record of latest email activity per question per user try: #todo: is it possible to use content_object here, instead of #content type and object_id pair? update_info = Activity.objects.get( user=user, content_type=ctype, object_id=q.id, activity_type=EMAIL_UPDATE_ACTIVITY) emailed_at = update_info.active_at except Activity.DoesNotExist: update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY) emailed_at = datetime.datetime(1970, 1, 1) #long time ago if django_settings.USE_TZ: emailed_at = timezone.make_aware(emailed_at, timezone.utc) except Activity.MultipleObjectsReturned: raise Exception( 'server error - multiple question email activities ' 'found per user-question pair') cutoff_time = meta_data[ 'cutoff_time'] #cutoff time for the question #skip question if we need to wait longer because #the delay before the next email has not yet elapsed #or if last email was sent after the most recent modification if emailed_at > cutoff_time or emailed_at > q.thread.last_activity_at: meta_data['skip'] = True continue #collect info on all sorts of news that happened after #the most recent emailing to the user about this question q_rev = q.revisions.filter(revised_at__gt=emailed_at) q_rev = q_rev.exclude(author=user) #now update all sorts of metadata per question meta_data['q_rev'] = len(q_rev) if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at: meta_data['q_rev'] = 0 meta_data['new_q'] = True else: meta_data['new_q'] = False new_ans = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ) new_ans = new_ans.exclude(author=user) meta_data['new_ans'] = len(new_ans) ans_ids = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ).values_list('id', flat=True) ans_rev = PostRevision.objects.filter(post__id__in=ans_ids) ans_rev = ans_rev.exclude(author=user).distinct() meta_data['ans_rev'] = len(ans_rev) comments = meta_data.get('comments', 0) mentions = meta_data.get('mentions', 0) #print meta_data #finally skip question if there are no news indeed if len(q_rev) + len(new_ans) + len( ans_rev) + comments + mentions == 0: meta_data['skip'] = True #print 'skipping' else: meta_data['skip'] = False #print 'not skipping' update_info.active_at = timezone.now() if DEBUG_THIS_COMMAND == False: update_info.save() #save question email update activity #q_list is actually an ordered dictionary #print 'user %s gets %d' % (user.username, len(q_list.keys())) #todo: sort question list by update time return q_list
def show_users(request, by_group=False, group_id=None, group_slug=None): """Users view, including listing of users by group""" if askbot_settings.GROUPS_ENABLED and not by_group: default_group = models.Group.objects.get_global_group() group_slug = slugify(default_group.name) new_url = reverse('users_by_group', kwargs={'group_id': default_group.id, 'group_slug': group_slug}) return HttpResponseRedirect(new_url) users = models.User.objects.exclude( askbot_profile__status='b' ).exclude( is_active=False ).select_related('askbot_profile') if askbot.is_multilingual(): users = users.filter( localized_askbot_profiles__language_code=get_language(), localized_askbot_profiles__is_claimed=True ) group = None group_email_moderation_enabled = False user_acceptance_level = 'closed' user_membership_level = 'none' if by_group == True: if askbot_settings.GROUPS_ENABLED == False: raise Http404 if group_id: if all((group_id, group_slug)) == False: return HttpResponseRedirect('groups') else: try: group = models.Group.objects.get(id = group_id) group_email_moderation_enabled = ( askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED \ and askbot_settings.CONTENT_MODERATION_MODE == 'premoderation' ) user_acceptance_level = group.get_openness_level_for_user( request.user ) except models.Group.DoesNotExist: raise Http404 if group_slug == slugify(group.name): #filter users by full group memberships #todo: refactor as Group.get_full_members() full_level = models.GroupMembership.FULL memberships = models.GroupMembership.objects.filter( group=group, level=full_level ) user_ids = memberships.values_list('user__id', flat=True) users = users.filter(id__in=user_ids) if request.user.is_authenticated(): membership = request.user.get_group_membership(group) if membership: user_membership_level = membership.get_level_display() else: group_page_url = reverse( 'users_by_group', kwargs = { 'group_id': group.id, 'group_slug': slugify(group.name) } ) return HttpResponseRedirect(group_page_url) is_paginated = True form = forms.ShowUsersForm(request.REQUEST) form.full_clean()#always valid sort_method = form.cleaned_data['sort'] page = form.cleaned_data['page'] search_query = form.cleaned_data['query'] if search_query == '': if sort_method == 'newest': order_by_parameter = '-date_joined' elif sort_method == 'last': order_by_parameter = 'date_joined' elif sort_method == 'name': order_by_parameter = 'username' else: # default if askbot.is_multilingual(): order_by_parameter = '-localized_askbot_profiles__reputation' else: order_by_parameter = '-askbot_profile__reputation' objects_list = Paginator( users.order_by(order_by_parameter), askbot_settings.USERS_PAGE_SIZE ) base_url = request.path + '?sort=%s&' % sort_method else: sort_method = 'reputation' matching_users = models.get_users_by_text_query(search_query, users) objects_list = Paginator( matching_users.order_by('-askbot_profile__reputation'), askbot_settings.USERS_PAGE_SIZE ) base_url = request.path + '?name=%s&sort=%s&' % (search_query, sort_method) try: users_page = objects_list.page(page) except (EmptyPage, InvalidPage): users_page = objects_list.page(objects_list.num_pages) paginator_data = { 'is_paginated' : is_paginated, 'pages': objects_list.num_pages, 'current_page_number': page, 'page_object': users_page, 'base_url' : base_url } paginator_context = functions.setup_paginator(paginator_data) # #todo: move to contexts #extra context for the groups if askbot_settings.GROUPS_ENABLED: #todo: cleanup this branched code after groups are migrated to auth_group user_groups = models.Group.objects.exclude_personal() if len(user_groups) <= 1: assert(user_groups[0].name == askbot_settings.GLOBAL_GROUP_NAME) user_groups = None group_openness_choices = models.Group().get_openness_choices() else: user_groups = None group_openness_choices = None data = { 'active_tab': 'users', 'group': group, 'group_email_moderation_enabled': group_email_moderation_enabled, 'group_openness_choices': group_openness_choices, 'page_class': 'users-page', 'paginator_context' : paginator_context, 'search_query' : search_query, 'tab_id' : sort_method, 'user_acceptance_level': user_acceptance_level, 'user_count': objects_list.count, 'user_groups': user_groups, 'user_membership_level': user_membership_level, 'users' : users_page, } return render(request, 'users.html', data)