Example #1
0
 def team_ips(self):
     """A list of IPs per team."""
     form = ButtonForm(self.request.POST, csrf_context=self.request)
     redir = self.redirect("admin_teams", int(self.request.GET.get("page", 1)))
     if not form.validate():
         return redir
     team = DBSession.query(Team).filter(Team.id == form.id.data).options(subqueryload("team_ips")).one()
     return {"team": team}
Example #2
0
 def team_resend_activation(self):
     """Resend the activation mail for a team."""
     form = ButtonForm(self.request.POST, csrf_context=self.request)
     redir = self.redirect("admin_teams", int(self.request.GET.get("page", 1)))
     if not form.validate():
         return redir
     team = DBSession.query(Team).filter(Team.id == form.id.data).one()
     send_activation_mail(team, self.request)
     self.request.session.flash('Activation mail for team "%s" resent.' % team.name)
     return redir
Example #3
0
    def team_regenerate_token(self):
        """Manually regenerate the teams challenge token"""
        current_page = int(self.request.GET.get("page", 1))
        redirect = self.redirect("admin_teams", current_page)
        button_form = ButtonForm(self.request.POST, csrf_context=self.request)
        if not button_form.validate():
            self.request.session.flash("Regenerate failed.")
            return redirect

        team = DBSession.query(Team).filter(Team.id == button_form.id.data).one()
        log.info("Generating new token for team %s, old token: %s" % (team.name, team.challenge_token))
        team.challenge_token = str(uuid.uuid4()).decode("ascii")

        self.request.session.flash("New token created for team %s" % team.name)
        return redirect
Example #4
0
    def _admin_delete(self, route_name, DatabaseClass, title, title_plural=None):
        """
        Generic function to delete a single item from the database. Its
        arguments have the same meaning as explained in :meth:`_admin_list`
        with the addition of ``title_plural`` which is just a pluraized
        version of the ``title`` argument. Also returns something that can
        be returned directly to the application.

        .. note::
            To avoid problems with cascade instead of just emitting an SQL
            ``DELETE`` statement, this queries for all affected objects
            (should be one) and deletes them afterwards. This ensures that the
            Python-side cascades appropriately delete all dependent objects.
        """
        # We don't accept GET or others here
        assert self.request.method == "POST"

        # Prepare parameters
        delete_form = ButtonForm(self.request.POST, csrf_context=self.request)
        current_page = int(self.request.GET.get("page", 1))
        redirect = self.redirect(route_name, current_page)
        if title_plural is None:
            title_plural = title + "s"

        # Check for errors
        if not delete_form.validate():
            self.request.session.flash("Delete failed.")
            return redirect

        # Load the ID to delete
        item_id = delete_form.id.data

        # Delete the item
        item = DBSession.query(DatabaseClass).filter(DatabaseClass.id == item_id)
        self._delete_item(DBSession, item, title, title_plural)
        return redirect
Example #5
0
    def _admin_toggle_status(
        self,
        route_name,
        DatabaseClass,
        title="",
        status_types={False: False, True: True},
        status_variable_name="published",
        status_messages={False: "Unpublished %(title)s", True: "Published %(title)s"},
    ):
        """
        Generic function that allows to toggle a special status on the
        challenge. By default it toggles the ``published`` property of any
        given item.

        Many arguments are the same as in :meth:`_admin_list` with these
        additional arguments:

            ``status_types``: A two-element dictionary that contains ``True``
            and ``False`` as keys and any value that describes the given
            status. For example: If the "unpublished" status is described by
            the string "offline", then the value for key ``False`` would be
            ``"offline"``. It depends on the database model, which value
            is used here. The default is just a boolean mapping.

            ``status_variable_name``: What is the name of the property in the
            model that contains the status to be changed. Defaults to
            "published".

            ``status_messages``: The same keys as for ``status_types`` but as
            values contains messages to be displayed, based on which action
            was the result. Gives access to the ``title`` variable via
            ``%(title)s`` inside the string. The defaults are sensible values
            for the the default status. Most likely you want to change this if
            changing ``status_variable_name``.

        Returns:
            A dictionary or similar that can be directly returned from a view.
        """
        # We don't accept GET or others here
        assert self.request.method == "POST"

        # Prepare parameters
        current_page = int(self.request.GET.get("page", 1))
        redirect = self.redirect(route_name, current_page)

        # Load and check form (csrf check!)
        toggle_form = ButtonForm(self.request.POST, csrf_context=self.request)
        if not toggle_form.validate():
            self.request.session.flash("Toggle failed.")
            return redirect

        # Generate a dict of inverted status types
        inverse_status_types = dict((value, key) for key, value in status_types.items())

        # Fetch the item to toggle
        item = DBSession.query(DatabaseClass).filter(DatabaseClass.id == toggle_form.id.data).one()

        # Read the current status
        status = inverse_status_types[getattr(item, status_variable_name)]
        # Set the inverse
        setattr(item, status_variable_name, status_types[not status])

        # Finish the request
        self.request.session.flash(status_messages[not status] % {"title": title.lower()})
        return redirect
Example #6
0
    def _admin_list(self, route_name, FormClass, DatabaseClass, title, change_query=None):
        """
        A generic function for all views that contain a list of things and also
        a form to edit or add entries.

        .. note::
            This only handles items with their own single primary key and not
            anything with composite foreign keys.

        Args:
            ``route_name``: A string containing the name of the route to which
            the admin should be redirected aver an edit was saved. For example
            ``"admin_challenges"``.

            ``FormClass``: The class of the form that should be displayed at
            the bottom of the page to edit or add items. For example
            :class:`fluxscoreboard.forms.admin.ChallengeForm`.

            ``DatabaseClass``: The ORM class from the model that is used to
            add and fetch items. For example
            :class:`fluxscoreboard.models.challenge.Challenge`.

            ``title``: A string that expresses a singular item, for example
            ``"Challenge"``. Will be used for flash messages.

            ``change_query``: A function that receives one parameter (a query),
            modifies it and returns the new query. May for example be used to
            modify the order or refine results. Optional.

        Returns:
            A dictionary or similar that can be directly returned to the
            application to be rendered as a view.

        An example usage might be like this:

        .. code-block:: python

            def challenges(self):
            return self._admin_list('admin_challenges', ChallengeForm,
                                    Challenge, "Challenge")
        """
        # Prepare some paramters
        items = self.items(DatabaseClass)
        if change_query:
            items = change_query(items)
        page = self.page(items)
        redirect = self.redirect(route_name, page.page)
        item_id = None
        if self.request.method == "POST":
            # Someone wants to edit so make sure to load and check the form
            # for CSRF and get the ID.
            edit_form = ButtonForm(self.request.POST, csrf_context=self.request)
            if not edit_form.validate():
                # Error, most likely CSRF
                return redirect
            item_id = edit_form.id.data

        # Either load a new form or load old data into it.
        if item_id is None:
            form = FormClass(self.request.POST, csrf_context=self.request)
        else:
            db_item = DBSession.query(DatabaseClass).filter(DatabaseClass.id == item_id).one()
            form = FormClass(None, db_item, csrf_context=self.request)

        # Display the page
        return self._list_retparams(page, form)