Ejemplo n.º 1
0
    def get(self, request, *args, **kwargs):
        self.show_recent = kwargs.pop('show_recent', False)

        content = kwargs.pop('content', None)
        if not content:
            return HttpResponseBadRequest('No content type supplied')
        self.model = SEARCH_MODEL_NAMES_REVERSE.get(content, None)
        if not self.model:
            return HttpResponseBadRequest(
                'Unknown content type supplied: "%s"' % content)

        self.page_size = int(
            request.GET.get('page_size', self.default_page_size))
        if not is_number(self.page_size):
            return HttpResponseBadRequest(
                'Malformed parameter: "page_size": %s' % self.page_size)
        self.page_size = max(self.min_page_size,
                             min(self.max_page_size, self.page_size))

        self.offset_timestamp = request.GET.get('offset_timestamp',
                                                self.default_offset_timestamp)
        if self.offset_timestamp is not None and not is_number(
                self.offset_timestamp):
            return HttpResponseBadRequest(
                'Malformed parameter: "offset_timestamp"')
        if self.offset_timestamp is not None and isinstance(
                self.offset_timestamp, six.string_types):
            self.offset_timestamp = float(self.offset_timestamp)

        return super(TypedContentWidgetView, self).get(request, *args,
                                                       **kwargs)
Ejemplo n.º 2
0
    def get(self, request, *args, **kwargs):
        """ Accepted GET-params: 
            `page_size` int (optional): Number of items to be returned. If a value larger than
                `self.max_page_size` is supplied, `self.max_page_size`is used instead.
                Default: `self.default_page_size`
            `offset_timestamp` float (optional): If supplied, only items older than the given 
                timestamp are returned. Items with the exact timestamp are excluded.
                Use this parameter in conjunction with the return value `last_timestamp` for 
                pagination.
            `only_mine` bool (optional): if True, will only show objects that belong to groups 
                or projects the `user` is a member of.  If False, will include all visible items 
                in this portal for the user.
        """
        # require authenticated user
        self.user = request.user
        if not request.user.is_authenticated:
            return HttpResponseForbidden('Not authenticated.')
        content = kwargs.pop('content', None)
        if content:
            self.filter_model = SEARCH_MODEL_NAMES_REVERSE.get(content, None)
            if not self.filter_model:
                return HttpResponseBadRequest(
                    'Unknown content type supplied: "%s"' % content)

        self.page_size = int(
            request.GET.get('page_size', self.default_page_size))
        if not is_number(self.page_size):
            return HttpResponseBadRequest(
                'Malformed parameter: "page_size": %s' % self.page_size)
        self.page_size = max(self.min_page_size,
                             min(self.max_page_size, self.page_size))

        self.offset_timestamp = request.GET.get('offset_timestamp',
                                                self.default_offset_timestamp)
        if self.offset_timestamp is not None and not is_number(
                self.offset_timestamp):
            return HttpResponseBadRequest(
                'Malformed parameter: "offset_timestamp"')
        if self.offset_timestamp is not None and isinstance(
                self.offset_timestamp, six.string_types):
            self.offset_timestamp = float(self.offset_timestamp)

        self.only_mine = request.GET.get('only_mine', self.only_mine_default)
        if isinstance(self.only_mine, six.string_types):
            self.only_mine = bool(json.loads(self.only_mine))

        items = self.get_items()
        response = self.render_to_response(items)
        return response
Ejemplo n.º 3
0
    def get(self, request, *args, **kwargs):
        """ Accepted GET-params: 
            `q` str: the query to search for
            `page_size` int (optional): Number of items to be returned. If a value larger than
                `self.max_page_size` is supplied, `self.max_page_size`is used instead.
                Default: `self.default_page_size`
        """
        self.user = request.user

        query = self.request.GET.get('q', '').strip()
        if not query:
            return HttpResponseBadRequest('Missing parameter: "q"!')
        self.query = query
        self.query_terms = self.query.lower().split(' ')

        self.page_size = int(
            request.GET.get('page_size', self.default_page_size))
        if not is_number(self.page_size):
            return HttpResponseBadRequest(
                'Malformed parameter: "page_size": %s' % self.page_size)
        self.page_size = max(self.min_page_size,
                             min(self.max_page_size, self.page_size))

        items = self.get_items()
        response = self.render_to_response(items)
        return response
Ejemplo n.º 4
0
def alerts_mark_seen(request, before_timestamp=None):
    """ Marks all NotificationAlerts of the current user as seen.
        @param before_timestamp: if kwarg is given, only marks alerts older than the given timestamp as seen. 
    """
    if request and not request.user.is_authenticated:
        return HttpResponseForbidden('Not authenticated')
    if not request.method == 'POST':
        return HttpResponseNotAllowed(['POST'])
    if before_timestamp is not None and not is_number(before_timestamp):
        return HttpResponseBadRequest('Malformed parameter: "before_timestamp"')
    
    if before_timestamp:
        before_timestamp = float(before_timestamp)
        before_dt = datetime_from_timestamp(before_timestamp)
    else:
        before_dt = now()
    
    unseen_alerts = NotificationAlert.objects.filter(
        portal=CosinnusPortal.get_current(), 
        user=request.user,
        last_event_at__lte=before_dt, 
        seen=False
    )
    unseen_alerts.update(seen=True)
    return HttpResponse('ok')
Ejemplo n.º 5
0
 def get(self, request, *args, **kwargs):
     self.newer_than_timestamp = kwargs.pop('newer_than_timestamp', None)
     if self.newer_than_timestamp is not None and not is_number(self.newer_than_timestamp):
         return HttpResponseBadRequest('Malformed parameter: "newer_than_timestamp"')
     if self.newer_than_timestamp is not None and isinstance(self.newer_than_timestamp, six.string_types):
         self.newer_than_timestamp = float(self.newer_than_timestamp)
     
     return super(AlertsRetrievalView, self).get(request, *args, **kwargs)
Ejemplo n.º 6
0
    def get(self, request, *args, **kwargs):
        self.page_size = int(
            request.GET.get('page_size', self.default_page_size))
        if not is_number(self.page_size):
            return HttpResponseBadRequest(
                'Malformed parameter: "page_size": %s' % self.page_size)
        self.page_size = max(self.min_page_size,
                             min(self.max_page_size, self.page_size))

        self.offset_timestamp = request.GET.get('offset_timestamp',
                                                self.default_offset_timestamp)
        if self.offset_timestamp is not None and not is_number(
                self.offset_timestamp):
            return HttpResponseBadRequest(
                'Malformed parameter: "offset_timestamp"')
        if self.offset_timestamp is not None and isinstance(
                self.offset_timestamp, six.string_types):
            self.offset_timestamp = float(self.offset_timestamp)

        self.set_options()
        return super(BasePagedOffsetWidgetView,
                     self).get(request, *args, **kwargs)
Ejemplo n.º 7
0
def _get_validated_amount(amount):
    """ Validates if a given amount is valid for payment (is a number, and in limits).
        @return: The float amount if valid, a JsonResponse with an error otherwise. """
    
    if not is_number(amount):
        return JsonResponse({'error': _('The amount submitted does not seem to be a number!')}, status=500)
    amount = float(amount)
    
    # check min/max payment amounts
    if amount > settings.PAYMENTS_MAXIMUM_ALLOWED_PAYMENT_AMOUNT:
        return JsonResponse({'error': _('The payment amount is higher than the allowed maximum amount!')}, status=500)
    if amount < settings.PAYMENTS_MINIMUM_ALLOWED_PAYMENT_AMOUNT:
        return JsonResponse({'error': _('The payment amount is lower than the allowed minimum amount!')}, status=500)
    return amount
Ejemplo n.º 8
0
def map_search_endpoint(request, filter_group_id=None):
    """ Maps API search endpoint using haystack search results. For parameters see ``MAP_SEARCH_PARAMETERS``
        returns JSON with the contents of type ``HaystackMapResult``
        
        @param filter_group_id: Will filter all items by group relation, where applicable 
                (i.e. users are filtered by group memberships for that group, events as events in that group)
    """
    implicit_ignore_location = not any([
        loc_param in request.GET
        for loc_param in ['sw_lon', 'sw_lat', 'ne_lon', 'ne_lat']
    ])
    params = _collect_parameters(request.GET, MAP_SEARCH_PARAMETERS)
    query = force_text(params['q'])
    limit = params['limit']
    page = params['page']
    item_id = params['item']
    prefer_own_portal = getattr(settings, 'MAP_API_HACKS_PREFER_OWN_PORTAL',
                                False)

    if not is_number(limit) or limit < 0:
        return HttpResponseBadRequest(
            '``limit`` param must be a positive number or 0!')
    limit = min(limit, SERVER_SIDE_SEARCH_LIMIT)
    if not is_number(page) or page < 0:
        return HttpResponseBadRequest(
            '``page`` param must be a positive number or 0!')

    # filter for requested model types
    model_list = [
        klass for klass, param_name in list(SEARCH_MODEL_NAMES.items())
        if params.get(param_name, False)
    ]
    sqs = SearchQuerySet().models(*model_list)
    # filter for map bounds (Points are constructed ith (lon, lat)!!!)
    if not params['ignore_location'] and not implicit_ignore_location:
        sqs = sqs.within('location', Point(params['sw_lon'], params['sw_lat']),
                         Point(params['ne_lon'], params['ne_lat']))
    # filter for user's own content
    if params['mine'] and request.user.is_authenticated:
        user_id = request.user.id
        sqs = sqs.filter_and(
            Q(creator=user_id) | Q(user_id=user_id) | Q(group_members=user_id))
    # filter for search terms
    if query:
        sqs = sqs.auto_query(query)
    # group-filtered-map view for on-group pages
    if filter_group_id:
        group = get_object_or_None(get_cosinnus_group_model(),
                                   id=filter_group_id)
        if group:
            filtered_groups = [filter_group_id]
            # get child projects of this group
            filtered_groups += [
                subproject.id for subproject in group.get_children()
                if subproject.is_active
            ]
            sqs = sqs.filter_and(
                Q(membership_groups__in=filtered_groups)
                | Q(group__in=filtered_groups))
    # filter topics
    topics = ensure_list_of_ints(params.get('topics', ''))
    if topics:
        sqs = sqs.filter_and(mt_topics__in=topics)
    # filter for portal visibility
    sqs = filter_searchqueryset_for_portal(
        sqs, restrict_multiportals_to_current=prefer_own_portal)
    # filter for read access by this user
    sqs = filter_searchqueryset_for_read_access(sqs, request.user)
    # filter events by upcoming status
    if params['events'] and Event is not None:
        sqs = filter_event_searchqueryset_by_upcoming(sqs)

    # filter all default user groups if the new dashboard is being used (they count as "on plattform" and aren't shown)
    if getattr(settings, 'COSINNUS_USE_V2_DASHBOARD', False):
        sqs = sqs.exclude(is_group_model=True,
                          slug__in=get_default_user_group_slugs())

    # if we hae no query-boosted results, use *only* our custom sorting (haystack's is very random)
    if not query:
        if prefer_own_portal:
            sqs = sqs.order_by('-portal', '-local_boost')
        else:
            sqs = sqs.order_by('-local_boost')

    # sort results into one list per model
    total_count = sqs.count()
    sqs = sqs[limit * page:limit * (page + 1)]
    results = []
    for result in sqs:
        # if we hae no query-boosted results, use *only* our custom sorting (haystack's is very random)
        if not query:
            result.score = result.local_boost
            if prefer_own_portal and is_number(result.portal) and int(
                    result.portal) == CosinnusPortal.get_current().id:
                result.score += 100.0
        results.append(HaystackMapResult(result, user=request.user))

    # if the requested item (direct select) is not in the queryset snippet
    # (might happen because of an old URL), then mix it in as first item and drop the last
    if item_id:
        item_id = str(item_id)
        if not any([res['id'] == item_id for res in results]):
            item_result = get_searchresult_by_itemid(item_id, request.user)
            if item_result:
                results = [HaystackMapResult(item_result, user=request.user)
                           ] + results[:-1]

    page_obj = None
    if results:
        page_obj = {
            'index': page,
            'count': len(results),
            'total_count': total_count,
            'start': (limit * page) + 1,
            'end': (limit * page) + len(results),
            'has_next': total_count > (limit * (page + 1)),
            'has_previous': page > 0,
        }

    data = {
        'results': results,
        'page': page_obj,
    }
    return JsonResponse(data)
Ejemplo n.º 9
0
def map_detail_endpoint(request):
    """ Maps API object detail endpoint using pSQL results. For parameters see ``MAP_DETAIL_PARAMETERS``
        returns JSON with the contents of type ``DetailedMapResult``
    """
    params = _collect_parameters(request.GET, MAP_DETAIL_PARAMETERS)
    portal = params['portal']
    slug = params['slug']
    model_type = params['type']

    if not is_number(portal) or portal < 0:
        return HttpResponseBadRequest(
            '``portal`` param must be a positive number!')
    if not slug:
        return HttpResponseBadRequest('``slug`` param must be supplied!')
    slug = force_text(slug)  # stringify is necessary for number-only slugs
    if not model_type or not isinstance(model_type, six.string_types):
        return HttpResponseBadRequest(
            '``type`` param must be supplied and be a string!')

    if portal == 0:
        portal = CosinnusPortal.get_current().id

    # try to retrieve the requested object
    model = SEARCH_MODEL_NAMES_REVERSE.get(model_type, None)
    if model is None:
        return HttpResponseBadRequest(
            '``type`` param indicated an invalid data model type!')

    # TODO: for groups/projects we should really use the cache here.
    if model_type == 'people':
        # UserProfiles are retrieved independent of the portal
        obj = get_object_or_None(get_user_profile_model(), user__username=slug)
    elif model_type == 'events':
        group_slug, event_slug = slug.split('*', 1)
        obj = get_object_or_None(model,
                                 group__portal__id=portal,
                                 group__slug=group_slug,
                                 slug=event_slug)
    else:
        obj = get_object_or_None(model, portal__id=portal, slug=slug)
    if obj is None:
        return HttpResponseNotFound(
            'No item found that matches the requested type and slug (obj: %s, %s, %s).'
            % (escape(force_text(model)), portal, slug))

    # check read permission
    if not model_type in SEARCH_MODEL_TYPES_ALWAYS_READ_PERMISSIONS and not check_object_read_access(
            obj, request.user):
        return HttpResponseForbidden(
            'You do not have permission to access this item.')

    # get the basic result data from the search index (as it is already prepared and faster to access there)
    haystack_result = get_searchresult_by_args(portal, model_type, slug)
    if not haystack_result:
        return HttpResponseNotFound(
            'No item found that matches the requested type and slug (index: %s, %s, %s).'
            % (portal, model_type, slug))

    # format data
    result_model = SEARCH_RESULT_DETAIL_TYPE_MAP[model_type]
    result = result_model(haystack_result, obj, request.user)

    data = {
        'result': result,
    }
    return JsonResponse(data)
Ejemplo n.º 10
0
    def get_context_data(self, **kwargs):
        forum_group = None
        forum_slug = getattr(settings, 'NEWW_FORUM_GROUP_SLUG', None)
        note_form = None
        if forum_slug:
            forum_group = get_object_or_None(
                get_cosinnus_group_model(),
                slug=forum_slug,
                portal=CosinnusPortal.get_current())
            try:
                from cosinnus_note.forms import NoteForm
                note_form = NoteForm(group=forum_group)
            except:
                if settings.DEBUG:
                    raise

        announcement = None
        announcement_is_preview = False
        # for superusers, check if we're viewing an announcement_preview
        if self.request.GET.get('show_announcement',
                                None) is not None and check_user_superuser(
                                    self.request.user):
            announcement_id = self.request.GET.get('show_announcement')
            if is_number(announcement_id):
                announcement_is_preview = True
                announcement = get_object_or_None(UserDashboardAnnouncement,
                                                  id=int(announcement_id))
        else:
            announcement = UserDashboardAnnouncement.get_next_for_user(
                self.request.user)

        welcome_screen_expired = self.request.user.date_joined < (now(
        ) - timedelta(days=getattr(
            settings, 'COSINNUS_V2_DASHBOARD_WELCOME_SCREEN_EXPIRY_DAYS', 7)))
        welcome_screen_enabled = getattr(
            settings, 'COSINNUS_V2_DASHBOARD_WELCOME_SCREEN_ENABLED', True)

        options = {
            'ui_prefs': get_ui_prefs_for_user(self.request.user),
            'force_only_mine': getattr(settings, 'COSINNUS_USERDASHBOARD_FORCE_ONLY_MINE', False) or \
                                getattr(settings, 'COSINNUS_FORUM_DISABLED', False),
        }
        ctx = {
            'user_dashboard_options_json':
            json.dumps(options),
            'forum_group':
            forum_group,
            'note_form':
            note_form,
            'announcement':
            announcement,
            'announcement_is_preview':
            announcement_is_preview,
            'show_welcome_screen':
            welcome_screen_enabled and not welcome_screen_expired,
        }
        if settings.COSINNUS_CONFERENCES_ENABLED:
            _now = now()
            my_conferences = CosinnusConference.objects.get_for_user(
                self.request.user)
            my_current_conferences = [(conf, conf.get_icon())
                                      for conf in my_conferences
                                      if conf.get_or_infer_to_date
                                      and conf.get_or_infer_to_date > _now]
            my_pending_conference_applications = [
                (appl.conference, appl.get_icon()) for appl in
                self.request.user.user_applications.pending_current()
            ]
            ctx['my_upcoming_conferences_with_icons'] = my_pending_conference_applications + my_current_conferences
        return ctx
Ejemplo n.º 11
0
    def start(self,
              name,
              meeting_id,
              welcome="Welcome to the conversation",
              moderator_password="",
              attendee_password="",
              max_participants=None,
              voice_bridge=None,
              parent_meeting_id=None,
              options=None,
              presentation_url=""):
        """ This function calls the BigBlueButton API directly to create a meeting with all available parameters available
            in the cosinnus-core.BBBRoom model.
    
        :param name: Human readable name for the meeting
        :type: str
    
        :param meeting_id: Human readable ID for the meeting
        :type: str
    
        :param welcome: Welcome message when joining the meeting
        :type: str
    
        :param moderator_password: Password for users to join with moderator privileges
        :type: str
    
        :param attendee_password: Password for users to join with default attendee privileges
        :type: str
    
        :param max_participants: Number of users allowed in the conference
        :type: int
    
        :param voice_bridge: Dial in PIN for telephone users
        :type: int
    
        :param parent_meeting_id: Breaking room for a running conference
        :type: str
    
        :param options: BBBRoom options according to the listed options in the BigBlueButton API
        :type: dict
    
        :param presentation_url: Publicly available URL of presentation file to be pre-uploaded as slides to BBB room
        :type: str
    
        :return: XML representation of the API result
        :rtype: XML
        """

        call = 'create'

        # set default values
        voice_bridge = voice_bridge if voice_bridge and is_number(
            voice_bridge) else bbb_utils.random_voice_bridge()
        attendee_password = attendee_password if attendee_password else bbb_utils.random_password(
        )
        moderator_password = moderator_password if moderator_password else bbb_utils.random_password(
        )

        query = (
            ("name", name),
            ('meetingID', meeting_id),
            ("welcome", welcome),
            ("voiceBridge", voice_bridge),
            ("attendeePW", attendee_password),
            ("moderatorPW", moderator_password),
        )

        if max_participants and is_number(max_participants):
            query += (("maxParticipants", int(max_participants)), )

        if parent_meeting_id:
            query += (("parentMeetingID", parent_meeting_id), )

        if options:
            for key, value in options.items():
                query += ((key, value), )

        query = urllib.parse.urlencode(query)

        hashed = self.api_call(query, call)
        url = self.api_auth_url + call + '?' + hashed
        # Presentation file has to be sent via POST request with XML body
        if presentation_url:
            headers = {'Content-Type': 'application/xml'}
            xml = "<?xml version='1.0' encoding='UTF-8'?><modules><module name='presentation'>"
            absolute_url = get_domain_for_portal(
                CosinnusPortal.get_current()) + presentation_url
            xml += f"<document url='{absolute_url}' />"
            xml += "</module></modules>"
            response = requests.post(url, data=xml, headers=headers)
        else:
            response = requests.get(url)
        result = bbb_utils.parse_xml(response.content.decode('utf-8'))

        if result:
            return result
        else:
            logger.error(
                'BBB Room error: Server request `start` was not successful.',
                extra={
                    'response_status_code': response.status_code,
                    'result': response.text
                })
            raise Exception(
                'BBB Room exception: Server request was not successful: ' +
                str(response.text))
Ejemplo n.º 12
0
def map_search_endpoint(request, filter_group_id=None):
    """ Maps API search endpoint using haystack search results. For parameters see ``MAP_SEARCH_PARAMETERS``
        returns JSON with the contents of type ``HaystackMapResult``
        
        @param filter_group_id: Will filter all items by group relation, where applicable 
                (i.e. users are filtered by group memberships for that group, events as events in that group)
    """
    implicit_ignore_location = not any([
        loc_param in request.GET
        for loc_param in ['sw_lon', 'sw_lat', 'ne_lon', 'ne_lat']
    ])
    params = _collect_parameters(request.GET, MAP_SEARCH_PARAMETERS)
    query = force_text(params['q'])
    limit = params['limit']
    page = params['page']
    item_id = params['item']

    if params.get('cloudfiles', False):
        return map_cloudfiles_endpoint(request, query, limit, page)

    # TODO: set to  params['external'] after the external switch button is in frontend!
    external = settings.COSINNUS_EXTERNAL_CONTENT_ENABLED

    prefer_own_portal = getattr(settings, 'MAP_API_HACKS_PREFER_OWN_PORTAL',
                                False)

    if not is_number(limit) or limit < 0:
        return HttpResponseBadRequest(
            '``limit`` param must be a positive number or 0!')
    limit = min(limit, SERVER_SIDE_SEARCH_LIMIT)
    if not is_number(page) or page < 0:
        return HttpResponseBadRequest(
            '``page`` param must be a positive number or 0!')

    # filter for requested model types
    model_list = [
        klass for klass, param_name in list(SEARCH_MODEL_NAMES.items())
        if params.get(param_name, False)
    ]

    sqs = SearchQuerySet().models(*model_list)

    # filter for map bounds (Points are constructed ith (lon, lat)!!!)
    if not params['ignore_location'] and not implicit_ignore_location:
        sqs = sqs.within('location', Point(params['sw_lon'], params['sw_lat']),
                         Point(params['ne_lon'], params['ne_lat']))
    # filter for user's own content
    if params['mine'] and request.user.is_authenticated:
        user_id = request.user.id
        sqs = sqs.filter_and(
            Q(creator=user_id) | Q(user_id=user_id) | Q(group_members=user_id))
    # filter for search terms
    if query:
        sqs = sqs.auto_query(query)

    # group-filtered-map view for on-group pages
    if filter_group_id:
        group = get_object_or_None(get_cosinnus_group_model(),
                                   id=filter_group_id)
        if group:
            filtered_groups = [filter_group_id]
            # get child projects of this group
            filtered_groups += [
                subproject.id for subproject in group.get_children()
                if subproject.is_active
            ]
            sqs = sqs.filter_and(
                Q(membership_groups__in=filtered_groups)
                | Q(group__in=filtered_groups))

    # filter topics
    topics = ensure_list_of_ints(params.get('topics', ''))
    if topics:
        sqs = sqs.filter_and(mt_topics__in=topics)
    if settings.COSINNUS_ENABLE_SDGS:
        sdgs = ensure_list_of_ints(params.get('sdgs', ''))
        if sdgs:
            sqs = sqs.filter_and(sdgs__in=sdgs)
    if settings.COSINNUS_MANAGED_TAGS_ENABLED:
        managed_tags = ensure_list_of_ints(params.get('managed_tags', ''))
        if managed_tags:
            sqs = sqs.filter_and(managed_tags__in=managed_tags)
    # filter for portal visibility
    sqs = filter_searchqueryset_for_portal(
        sqs,
        restrict_multiportals_to_current=prefer_own_portal,
        external=external)
    # filter for read access by this user
    sqs = filter_searchqueryset_for_read_access(sqs, request.user)
    # filter events by upcoming status and exclude hidden proxies
    if params['events'] and Event is not None:
        sqs = filter_event_searchqueryset_by_upcoming(sqs).exclude(
            is_hidden_group_proxy=True)

    # filter all default user groups if the new dashboard is being used (they count as "on plattform" and aren't shown)
    if getattr(settings, 'COSINNUS_USE_V2_DASHBOARD', False):
        sqs = sqs.exclude(is_group_model=True,
                          slug__in=get_default_user_group_slugs())

    # kip score sorting and only rely on natural ordering?
    skip_score_sorting = False
    # if we hae no query-boosted results, use *only* our custom sorting (haystack's is very random)
    if not query:
        sort_args = ['-local_boost']
        # if we only look at conferences, order them by their from_date, future first!
        if prefer_own_portal:
            sort_args = ['-portal'] + sort_args
        """
        # this would be the way to force-sort a content type by a natural ordering instead of score if its the only type being shown
        if params.get('conferences', False) and sum([1 if params.get(content_key, False) else 0 for content_key in MAP_CONTENT_TYPE_SEARCH_PARAMETERS.keys()]) == 1:
            sort_args = ['-from_date'] + sort_args
            skip_score_sorting = True
        sqs = sqs.order_by(*sort_args)
        """

    # sort results into one list per model
    total_count = sqs.count()
    sqs = sqs[limit * page:limit * (page + 1)]
    results = []

    for i, result in enumerate(sqs):
        if skip_score_sorting:
            # if we skip score sorting and only rely on the natural ordering, we make up fake high scores
            result.score = 100000 - (limit * page) - i
        elif not query:
            # if we hae no query-boosted results, use *only* our custom sorting (haystack's is very random)
            result.score = result.local_boost
            if prefer_own_portal and is_number(result.portal) and int(
                    result.portal) == CosinnusPortal.get_current().id:
                result.score += 100.0
        results.append(HaystackMapResult(result, user=request.user))

    # if the requested item (direct select) is not in the queryset snippet
    # (might happen because of an old URL), then mix it in as first item and drop the last
    if item_id:
        item_id = str(item_id)
        if not any([res['id'] == item_id for res in results]):
            item_result = get_searchresult_by_itemid(item_id, request.user)
            if item_result:
                results = [HaystackMapResult(item_result, user=request.user)
                           ] + results[:-1]

    page_obj = None
    if results:
        page_obj = {
            'index': page,
            'count': len(results),
            'total_count': total_count,
            'start': (limit * page) + 1,
            'end': (limit * page) + len(results),
            'has_next': total_count > (limit * (page + 1)),
            'has_previous': page > 0,
        }

    data = {
        'results': results,
        'page': page_obj,
    }
    return JsonResponse(data)