예제 #1
0
 class IntegerTestSchema(StrictSchema):
     field = validators.Int(not_empty=True)
예제 #2
0
class RootController(BaseController, DispatchIndex, FeedController):

    def __init__(self):
        self._discuss = AppDiscussionController()

    def _check_security(self):
        require_access(c.app, 'read')

    @with_trailing_slash
    @expose()
    def index(self, **kw):
        redirect(h.really_unicode(c.app.root_page_name).encode('utf-8') + '/')

    @expose()
    def _lookup(self, pname, *remainder):
        """Instantiate a Page object, and continue dispatch there."""
        return PageController(pname), remainder

    @expose()
    def new_page(self, title):
        redirect(h.really_unicode(title).encode('utf-8') + '/')

    @with_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/search.html')
    @validate(dict(q=validators.UnicodeString(if_empty=None),
                   history=validators.StringBool(if_empty=False),
                   search_comments=validators.StringBool(if_empty=False),
                   project=validators.StringBool(if_empty=False)))
    def search(self, q=None, history=None, search_comments=None, project=None, limit=None, page=0, **kw):
        'local wiki search'
        c.search_results = W.search_results
        c.help_modal = W.help_modal
        search_params = kw
        search_params.update({
            'q': q or '',
            'history': history,
            'search_comments': search_comments,
            'project': project,
            'limit': limit,
            'page': page,
            'allowed_types': ['WikiPage', 'WikiPage Snapshot'],
        })
        return search_app(**search_params)

    @with_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/browse.html')
    @validate(dict(sort=validators.UnicodeString(if_empty='alpha'),
                   show_deleted=validators.StringBool(if_empty=False),
                   page=validators.Int(if_empty=0, if_invalid=0),
                   limit=validators.Int(if_empty=None, if_invalid=None)))
    def browse_pages(self, sort='alpha', show_deleted=False, page=0, limit=None, **kw):
        'list of all pages in the wiki'
        c.page_list = W.page_list
        c.page_size = W.page_size
        limit, pagenum, start = g.handle_paging(limit, page, default=25)
        count = 0
        pages = []
        uv_pages = []
        criteria = dict(app_config_id=c.app.config._id)
        can_delete = has_access(c.app, 'delete')()
        show_deleted = show_deleted and can_delete
        if not can_delete:
            criteria['deleted'] = False
        q = WM.Page.query.find(criteria)
        if sort == 'alpha':
            q = q.sort('title')
        count = q.count()
        q = q.skip(start).limit(int(limit))
        for page in q:
            recent_edit = page.history().first()
            p = dict(title=page.title, url=page.url(), deleted=page.deleted)
            if recent_edit:
                p['updated'] = recent_edit.timestamp
                p['user_label'] = recent_edit.author.display_name
                p['user_name'] = recent_edit.author.username
                pages.append(p)
            else:
                if sort == 'recent':
                    uv_pages.append(p)
                else:
                    pages.append(p)
        if sort == 'recent':
            pages.sort(reverse=True, key=lambda x: (x['updated']))
            pages = pages + uv_pages
        return dict(
            pages=pages, can_delete=can_delete, show_deleted=show_deleted,
            limit=limit, count=count, page=pagenum)

    @with_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/browse_tags.html')
    @validate(dict(sort=validators.UnicodeString(if_empty='alpha'),
                   page=validators.Int(if_empty=0, if_invalid=0),
                   limit=validators.Int(if_empty=None, if_invalid=None)))
    def browse_tags(self, sort='alpha', page=0, limit=None, **kw):
        'list of all labels in the wiki'
        c.page_list = W.page_list
        c.page_size = W.page_size
        limit, pagenum, start = g.handle_paging(limit, page, default=25)
        count = 0
        page_tags = {}
        q = WM.Page.query.find(dict(app_config_id=c.app.config._id,
                                    deleted=False,
                                    labels={'$ne': []}))
        for page in q:
            if page.labels:
                for label in page.labels:
                    if label not in page_tags:
                        page_tags[label] = []
                    page_tags[label].append(page)
        count = len(page_tags)
        name_labels = list(page_tags)
        name_labels.sort()
        return dict(labels=page_tags,
                    limit=limit,
                    count=count,
                    page=pagenum,
                    name_labels=name_labels[start:start + limit])

    @with_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/create_page.html')
    def create_wiki_page(self):
        return {}

    @with_trailing_slash
    @expose('jinja:allura:templates/markdown_syntax.html')
    def markdown_syntax(self):
        'Display a page about how to use markdown.'
        return dict(example=MARKDOWN_EXAMPLE)

    @with_trailing_slash
    @expose('jinja:allura:templates/markdown_syntax_dialog.html')
    def markdown_syntax_dialog(self):
        'Display a page about how to use markdown.'
        return dict(example=MARKDOWN_EXAMPLE)

    @expose()
    @require_post()
    @validate(W.subscribe_form)
    def subscribe(self, subscribe=None, unsubscribe=None):
        if subscribe:
            M.Mailbox.subscribe(type='direct')
        elif unsubscribe:
            M.Mailbox.unsubscribe()
        redirect(request.referer)
                mr, 'merge_request',
                subject='Merge request: ' + mr.summary)
            t = M.Thread(
                discussion_id=c.app.config.discussion_id,
                artifact_reference=mr.index_id(),
                subject='Discussion for Merge Request #:%s: %s' % (
                    mr.request_number, mr.summary))
            session(t).flush()
            redirect(mr.url())

    @without_trailing_slash
    @expose()
    @validate(dict(
            since=h.DateTimeConverter(if_empty=None, if_invalid=None),
            until=h.DateTimeConverter(if_empty=None, if_invalid=None),
            offset=validators.Int(if_empty=None),
            limit=validators.Int(if_empty=None)))
    def feed(self, since=None, until=None, offset=None, limit=None):
        if request.environ['PATH_INFO'].endswith('.atom'):
            feed_type = 'atom'
        else:
            feed_type = 'rss'
        title = 'Recent changes to %s' % c.app.config.options.mount_point
        feed = M.Feed.feed(
            dict(project_id=c.project._id,app_config_id=c.app.config._id),
            feed_type,
            title,
            c.app.url,
            title,
            since, until, offset, limit)
        response.headers['Content-Type'] = ''
예제 #4
0
class BasicTGController(TGController):
    @expose()
    @validate(ColonLessGenericValidator())
    def validator_without_columns(self, **kw):
        return tg.request.validation.errors['_the_form']

    @expose('json:')
    @validate(validators={"some_int": validators.Int()})
    def validated_int(self, some_int):
        assert isinstance(some_int, int)
        return dict(response=some_int)

    @expose('json:')
    @validate(validators={"a": validators.Int()})
    def validated_and_unvalidated(self, a, b):
        assert isinstance(a, int)
        assert isinstance(b, unicode_text)
        return dict(int=a, str=b)

    @expose()
    @controller_based_validate()
    def validate_controller_based_validator(self, *args, **kw):
        return 'ok'

    @expose('json:')
    @validate(validators={"a": validators.Int(), "someemail": validators.Email()})
    def two_validators(self, a=None, someemail=None, *args):
        errors = tg.request.validation.errors
        values = tg.request.validation.values
        return dict(a=a, someemail=someemail,
                errors=str(errors), values=str(values))

    @expose('json:')
    @validate(validators={"a": validators.Int()})
    def with_default_shadow(self, a, b=None ):
        """A default value should not cause the validated value to disappear"""
        assert isinstance( a, int ), type(a)
        return {
            'int': a,
        }

    @expose('json:')
    @validate(validators={"e": ColonValidator()})
    def error_with_colon(self, e):
        errors = tg.request.validation.errors
        return dict(errors=str(errors))

    @expose('json:')
    @validate(validators={
        "a": validators.Int(),"b":validators.Int(),"c":validators.Int(),"d":validators.Int()
    })
    def with_default_shadow_long(self, a, b=None,c=None,d=None ):
        """A default value should not cause the validated value to disappear"""
        assert isinstance( a, int ), type(a)
        assert isinstance( b, int ), type(b)
        assert isinstance( c, int ), type(c)
        assert isinstance( d, int ), type(d)
        return {
            'int': [a,b,c,d],
        }

    @expose()
    def display_form(self, **kwargs):
        return str(myform.render(values=kwargs))

    @expose('json:')
    @validate(form=myform)
    def process_form(self, **kwargs):
        kwargs['errors'] = tg.request.validation.errors
        return dict(kwargs)

    @expose('json:')
    @validate(form=myform, error_handler=process_form)
    def send_to_error_handler(self, **kwargs):
        kwargs['errors'] = tg.request.validation.errors
        return dict(kwargs)

    @expose('json')
    def tw2form_error_handler(self, **kwargs):
        return dict(errors=tg.request.validation.errors)

    @expose('json:')
    @validate(form=movie_form, error_handler=tw2form_error_handler)
    def send_tw2_to_error_handler(self, **kwargs):
        return 'passed validation'

    @expose()
    @validate({'param': tw2c.IntValidator()},
              error_handler=validation_errors_response)
    def tw2_dict_validation(self, **kwargs):
        return 'NO_ERROR'

    @expose()
    @validate({'param': validators.Int()},
              error_handler=validation_errors_response)
    def formencode_dict_validation(self, **kwargs):
        return 'NO_ERROR'

    @expose('text/plain')
    @validate(form=FormWithFieldSet, error_handler=tw2form_error_handler)
    def tw2_fieldset_submit(self, **kwargs):
        return 'passed validation'

    @expose()
    def set_lang(self, lang=None):
        tg.session['tg_lang'] = lang
        tg.session.save()
        return 'ok'

    @expose()
    @validate(validators=Pwd())
    def password(self, pwd1, pwd2):
        if tg.request.validation.errors:
            return "There was an error"
        else:
            return "Password ok!"

    @expose('json:')
    @before_render(lambda rem,params,output:output.update({'GOT_ERROR':'HOOKED'}))
    def hooked_error_handler(self, *args, **kw):
        return dict(GOT_ERROR='MISSED HOOK')

    @expose()
    @validate({'v':validators.Int()}, error_handler=hooked_error_handler)
    def with_hooked_error_handler(self, *args, **kw):
        return dict(GOT_ERROR='NO ERROR')

    @expose('json')
    @validate({'v': validators.Int()})
    def check_tmpl_context_compatibility(self, *args, **kw):
        return dict(tmpl_errors=str(tg.tmpl_context.form_errors),
                    errors=str(tg.request.validation.errors))

    @expose()
    def error_handler(self, *args, **kw):
        return 'ERROR HANDLER!'

    @expose('json:')
    @validate(validators={"some_int": validators.Int()},
              error_handler=error_handler)
    def validate_other_error_handler(self, some_int):
        return dict(response=some_int)

    def unexposed_error_handler(self, uid, **kw):
        return 'UID: %s' % uid

    @expose()
    @validate({'uid': validators.Int(),
               'num': validators.Int()},
              error_handler=unexposed_error_handler)
    def validate_unexposed(self, uid, num):
        return 'HUH'

    @expose()
    @validate({'num': validators.Int()},
              error_handler=partial(unexposed_error_handler,
                                    uid=5))
    def validate_partial(self, num):
        return 'HUH'

    @expose()
    @validate({'uid': tw2c.IntValidator(),
               'num': tw2c.IntValidator()},
              error_handler=error_handler_function)
    def validate_function(self, uid, num):
        return 'HUH'

    @expose()
    @validate({'uid': validators.Int(),
               'num': validators.Int()},
              error_handler=ErrorHandlerCallable())
    def validate_callable(self, uid, num):
        return 'HUH'

    @expose()
    @validate({'uid': validators.Int()},
              error_handler=ErrorHandlerCallable())
    @validate({'num': validators.Int()},
              error_handler=abort(412, error_handler=True))
    def validate_multi(self, uid, num):
        return str(uid+num)

    @expose()
    @validate({'uid': validators.Int()},
              error_handler=abort(412, error_handler=True))
    def abort_error_handler(self):
        return 'HUH'

    @expose()
    @validate({'uid': validators.Int()},
              error_handler=validation_errors_response)
    def validate_json_errors(self):
        return 'HUH'

    @expose()
    def validate_json_errors_complex_types(self, date):
        tg.request.validation.values = {'date': datetime.datetime.utcnow()}
        return validation_errors_response()

    @expose()
    @before_call(lambda remainder, params: params.setdefault('num', 5))
    def hooked_error_handler(self, uid, num):
        return 'UID: %s, NUM: %s' % (uid, num)

    @expose()
    @validate(ThrowAwayValidationIntentValidator(),
              error_handler=abort(412, error_handler=True))
    def throw_away_intent(self, uid):
        if tg.request.validation.exception:
            return 'ERROR'
        return 'UHU?'

    @expose()
    @validate(error_handler=hooked_error_handler)
    def passthrough_validation(self, uid):
        return str(uid)

    @expose()
    @validate({'uid': validators.Int()},
              error_handler=hooked_error_handler)
    def validate_hooked(self, uid):
        return 'HUH'

    # Decorate validate_hooked with a controller wrapper
    Decoration.get_decoration(hooked_error_handler)\
        ._register_controller_wrapper(ControllerWrapperForErrorHandler)

    @expose()
    def manually_handle_validation(self):
        # This is done to check that we don't break compatibility
        # with external modules that perform custom validation like tgext.socketio

        controller = self.__class__.validate_function
        args = (2, 'NaN')
        try:
            output = ''
            validate_params = get_params_with_argspec(controller, {}, args)
            params = DecoratedController._perform_validate(controller,
                                                           validate_params)
        except validation_errors as inv:
            handler, output = DecoratedController._handle_validation_errors(controller,
                                                                            args, {},
                                                                            inv, None)

        return output

    @expose(content_type='text/plain')
    @validate({
        'num': Convert(int, l_('This must be a number'))
    }, error_handler=validation_errors_response)
    def post_pow2(self, num=-1):
        return str(num*num)

    @expose(content_type='text/plain')
    @validate({
        'num': Convert(int, l_('This must be a number'), default=0)
    }, error_handler=validation_errors_response)
    def post_pow2_opt(self, num=-1):
        return str(num*num)

    @expose(content_type='text/plain')
    @validate({
        'num': Convert(int, u_('àèìòù'))
    }, error_handler=validation_errors_response)
    def unicode_error_pow(self, num=-1):
        return str(num*num)

    @expose(content_type='text/plain')
    @validate({
        'num': Convert(int, l_(u_('àèìòù')))
    }, error_handler=validation_errors_response)
    def lazy_unicode_error_pow(self, num=-1):
        return str(num * num)

    @expose(content_type='text/plain')
    @validate({
        'val': Convert(lambda v: int(v) > 0 or int('ERROR'))
    }, error_handler=validation_errors_response)
    def chain_validation_0(self, val):
        return '>0'

    @expose(content_type='text/plain')
    @validate({
        'val': Convert(lambda v: int(v) > 1 or int('ERROR'))
    }, error_handler=chain_validation_0, chain_validation=True)
    def chain_validation_1(self, val):
        return '>1'

    @expose(content_type='text/plain')
    @validate({
        'val': Convert(lambda v: int(v) > 2 or int('ERROR'))
    }, error_handler=chain_validation_1, chain_validation=True)
    def chain_validation_2(self, val):
        return '>2'

    @expose(content_type='text/plain')
    @validate({
        'val': Convert(lambda v: int(v) > 3 or int('ERROR'))
    }, error_handler=chain_validation_2, chain_validation=True)
    def chain_validation_begin(self, val):
        return '>3'
예제 #5
0
    def save(self):
        request = self.request
        user = request.user

        chk_type = self.request.context.chk_type
        all_values = not self.request.dboptions.OtherMembersActive
        shared_values = not not (
            all_values
            or not self.request.matched_route.name.endswith("_local"))
        SuperUserGlobal = self._check_security(
            chk_type, chk_type.Shared == "full" or shared_values)

        if shared_values and not SuperUserGlobal:
            self._security_failure()

        if chk_type.ShowAdd:
            extra_validators = {
                chk_type.ID:
                Any(ciocvalidators.IDValidator(), validators.OneOf(["NEW"]))
            }
        else:
            extra_validators = {
                chk_type.ID:
                ciocvalidators.IDValidator(not_empty=chk_type.CanDelete)
            }
        if chk_type.CodeTitle:
            if not chk_type.CodeValidator:
                extra_validators[chk_type.CodeField] = ciocvalidators.String(
                    max=chk_type.CodeMaxLength)
            else:
                extra_validators[chk_type.CodeField] = chk_type.CodeValidator

        if chk_type.DisplayOrder:
            extra_validators["DisplayOrder"] = validators.Int(min=0,
                                                              max=256,
                                                              if_empty=0)
        if chk_type.ShowOnForm:
            extra_validators["ShowOnForm"] = validators.Bool()

        for field in chk_type.ExtraFields or []:
            extra_validators[field["field"]] = field["validator"]

        extra_name_validators = {}
        for field in chk_type.ExtraNameFields or []:
            extra_name_validators[field["field"]] = field["validator"]

        base_schema = make_checklist_base_schema(extra_name_validators,
                                                 **extra_validators)
        schema_params = {"chkitem": foreach.ForEach(base_schema)}
        schema = PostSchema(**schema_params)

        model_state = request.model_state
        model_state.form.state.chk_type = chk_type
        model_state.schema = schema

        model_state.form.variable_decode = True

        domain, shown_cultures = viewbase.get_domain_and_show_cultures(
            request.params)
        if model_state.validate():
            # valid. Save changes and redirect

            root = ET.Element("CHECKLIST")
            for i, chkitem in enumerate(model_state.form.data["chkitem"]):
                if should_skip_item(chk_type, chkitem):
                    continue

                chk_el = ET.SubElement(root, "CHK")
                ET.SubElement(chk_el, "CNT").text = str(i)

                for key, value in chkitem.items():
                    if key == chk_type.ID and value == "NEW":
                        value = -1

                    elif isinstance(value, bool):
                        value = int(value)

                    if key != "Descriptions":
                        if value is not None:
                            ET.SubElement(chk_el, key).text = str(value)
                        continue

                    descs = ET.SubElement(chk_el, "DESCS")
                    for culture, data in value.items():
                        culture = culture.replace("_", "-")
                        if culture not in shown_cultures:
                            continue

                        desc = ET.SubElement(descs, "DESC")
                        ET.SubElement(desc, "Culture").text = culture
                        for key, value in data.items():
                            if value:
                                ET.SubElement(desc, key).text = value

            args = [
                request.dboptions.MemberID,
                user.Mod,
                ET.tostring(root, encoding="unicode"),
            ]

            with request.connmgr.get_connection("admin") as conn:
                sql = chk_type.UpdateSQL(shared_values)
                log.debug("sql: %s", sql)
                log.debug("args: %s", args)
                cursor = conn.execute(sql, *args)
                result = cursor.fetchone()
                cursor.close()

            if not result.Return:

                self._go_to_route(
                    request.matched_route.name,
                    _query=(
                        (
                            "InfoMsg",
                            _("The record(s) were successfully updated.",
                              request),
                        ),
                        ("ShowCultures", shown_cultures),
                        ("chk", chk_type.FieldCode),
                    ),
                )

            ErrMsg = _("Unable to save: ") + result.ErrMsg

        else:
            ErrMsg = _("There were validation errors.")

        chkitems, chkusage = self._get_edit_info(
            chk_type,
            not all_values and not shared_values,
            not all_values and shared_values,
            not SuperUserGlobal,
        )

        record_cultures = syslanguage.active_record_cultures()

        chkitems = variabledecode.variable_decode(request.POST)["chkitem"]
        model_state.form.data["chkitem"] = chkitems

        type_name = ""
        if chk_type.Shared == "partial" and not all_values:
            if shared_values:
                type_name = _("Shared", self.request)
            else:
                type_name = _("Local", self.request)

        title_template = _(chk_type.PageTitleTemplate, self.request) % {
            "type": type_name
        }
        title = (chk_type.CheckListName if request.viewdata.PrintMode else
                 title_template.format(chk_type.CheckListName))
        return self._create_response_namespace(
            title,
            title,
            dict(
                chkitems=chkitems,
                record_cultures=record_cultures,
                shown_cultures=shown_cultures,
                SuperUserGlobal=SuperUserGlobal,
                chkusage=chkusage,
                chk_type=chk_type,
                ErrMsg=ErrMsg,
            ),
            no_index=True,
        )
예제 #6
0
class InvoiceItemValidator(BaseSchema):
    product = ProductValidator()
    qty = validators.Int() 
    cost = validators.Int(min=-2000000, max=2000000)
    description = validators.String(not_empty=False)
    chained_validators = [InvoiceItemProductDescriptionValidator()]
예제 #7
0
class PayInvoiceSchema(BaseSchema):
    payment_id = validators.Int(min=1)
예제 #8
0
파일: hosts.py 프로젝트: nomed/rnms
class HostsController(BaseTableController):
    allow_only = permissions.host_ro

    @expose('rnms.templates.host.index')
    @validate(validators={'z': validators.Int(min=1)})
    def index(self, z=None, *args, **kw):
        if tmpl_context.form_errors:
            self.process_form_errors()
            return {}
        if z is not None:
            table_filter = {'z': z}
        else:
            table_filter = {}

        class HostTile(PanelTile):
            title = 'Host List'
            fullwidth = True

            class MyTable(BootstrapTable):
                data_url = url('/hosts/tabledata.json')
                columns = [('id', 'ID'), ('display_name', 'Name'),
                           ('mgmt_address', 'Management Address')]
                filter_params = table_filter
                detail_url = url('/hosts/')

        return dict(page='host', hosttable=HostTile())

    @expose('json')
    @validate(
        validators={
            'z': validators.Int(min=1),
            'offset': validators.Int(min=0),
            'limit': validators.Int(min=1)
        })
    def tabledata(self, **kw):
        """ Provides the JSON data for the standard bootstrap table
        """
        if tmpl_context.form_errors:
            self.process_form_errors()
            return {}

        conditions = []
        if kw['z'] is not None:
            conditions.append(Host.zone_id == kw['z'])
        table_data = self._get_tabledata(Host, conditions=conditions, **kw)
        if table_data is None:
            return {}
        rows = [{
            'id': row.id,
            'display_name': row.display_name,
            'mgmt_address': row.mgmt_address
        } for row in table_data[1]]
        return dict(total=table_data[0], rows=rows)

    @expose('rnms.templates.host.detail')
    @validate(validators={'h': validators.Int(min=1)})
    def _default(self, h):
        if tmpl_context.form_errors:
            self.process_form_errors()
            return dict(page='host')
        host = Host.by_id(h)
        if host is None:
            flash('Host ID#{} not found'.format(h), 'error')
            return dict(page='host')
        vendor, devmodel = host.snmp_type()
        highest_alarm = Event.host_alarm(host.id)
        if highest_alarm is None:
            host_state = 'Up'
        else:
            host_state = highest_alarm.event_state.display_name.capitalize()

        thehost = host

        class HostDetailPanel(PanelTile):
            title = 'Host Details'

            class MyHostDetails(HostDetails):
                host = thehost
                extra = {
                    'host_state': host_state,
                    'vendor': vendor,
                    'devmodel': devmodel,
                }

        class AttributeStatusPanel(PanelTile):
            title = 'Attribute Status'

            class AttributeStatus(AttributeStateDoughnut):
                host_id = h

        class HostEventPanel(PanelTile):
            title = 'Events for ' + host.display_name
            fullwidth = True

            class HostEventTable(EventTable):
                filter_params = {'h': h}

        class AttributesPanel(PanelTile):
            title = 'Host Attributes'

            class AttributesTable(BootstrapTable):
                data_url = url('/attributes/namelist.json', {'h': h})
                detail_url = url('/attributes/')
                columns = [
                    ('display_name', 'Name'),
                ]
                sort_name = 'display_name'
                fit_panel = True

        return dict(page='host',
                    details_panel=HostDetailPanel(),
                    status_panel=AttributeStatusPanel(),
                    attributes_panel=AttributesPanel(),
                    host=host,
                    events_panel=HostEventPanel)

    @expose('rnms.templates.host.map')
    @validate(
        validators={
            'z': validators.Int(min=1),
            'events': validators.Bool(),
            'alarmed': validators.Bool()
        })
    def map(self, z=None, events=False, alarmed=False, **kw):
        """ Display a map of the Hosts, optionally filtered by Zone id
        and optionally showing events for those hosts
        """
        if tmpl_context.form_errors:
            self.process_form_errors()
            return dict(page='host')

        class HostMapTile(PanelTile):
            title = 'Host Map'
            fullwidth = True

            class HostMap2(HostMap):
                zone_id = z
                alarmed_only = (alarmed == 1)

        if events:

            class HostEventTile(PanelTile):
                title = 'Host Events'
                fullwidth = True

                class HostEventTable(EventTable):
                    filter_params = {'z': z}

            events_panel = HostEventTile()
        else:
            events_panel = None

        return dict(page='hosts',
                    host_map=HostMapTile(),
                    events_panel=events_panel)

    @expose('rnms.templates.widgets.select')
    def option(self):
        """ Return a list of hosts. If user has required
        permission it shows all, else just their ones """
        if permissions.host_ro:
            hosts = DBSession.query(Host.id, Host.display_name)
        else:
            hosts = DBSession.query(Host.id, Host.display_name).filter(
                Host.id.in_(
                    DBSession.query(Attribute.host_id).filter(
                        Attribute.user_id ==
                        request.identity['user'].user_id)))
        items = hosts.all()
        items.insert(0, ('', '-- Choose Host --'))
        return dict(items=items)
예제 #9
0
class ProjectController(object):
    def __init__(self):
        setattr(self, 'feed.rss', self.feed)
        setattr(self, 'feed.atom', self.feed)
        setattr(self, '_nav.json', self._nav)
        self.screenshot = ScreenshotsController()

    @expose('json:')
    def _nav(self):
        return dict(menu=[
            dict(name=s.label, url=s.url, icon=s.ui_icon)
            for s in c.project.sitemap()
        ])

    @expose()
    def _lookup(self, name, *remainder):
        name = unquote(name)
        if not h.re_path_portion.match(name):
            raise exc.HTTPNotFound, name
        subproject = M.Project.query.get(
            shortname=c.project.shortname + '/' + name,
            neighborhood_id=c.project.neighborhood_id)
        if subproject:
            c.project = subproject
            c.app = None
            return ProjectController(), remainder
        app = c.project.app_instance(name)
        if app is None:
            raise exc.HTTPNotFound, name
        c.app = app
        if not app.root:
            raise exc.HTTPNotFound, name

        return app.root, remainder

    def _check_security(self):
        require_access(c.project, 'read')

    @expose()
    @with_trailing_slash
    def index(self, **kw):
        mount = c.project.first_mount('read')
        activity_enabled = config.get('activitystream.enabled', False)
        activity_enabled = request.cookies.get('activitystream.enabled',
                                               activity_enabled)
        activity_enabled = asbool(activity_enabled)
        if activity_enabled and c.project.app_instance('activity'):
            redirect('activity/')
        elif mount is not None:
            if 'ac' in mount:
                redirect(mount['ac'].options.mount_point + '/')
            elif 'sub' in mount:
                redirect(mount['sub'].url())
        elif c.project.app_instance('profile'):
            redirect('profile/')
        else:
            redirect(c.project.app_configs[0].options.mount_point + '/')

    @expose('jinja:allura:templates/project_sitemap.html')
    @without_trailing_slash
    def sitemap(self):  # pragma no cover
        raise NotImplementedError, 'sitemap'

    @without_trailing_slash
    @expose()
    @validate(
        dict(since=h.DateTimeConverter(if_empty=None, if_invalid=None),
             until=h.DateTimeConverter(if_empty=None, if_invalid=None),
             page=validators.Int(if_empty=None),
             limit=validators.Int(if_empty=None)))
    def feed(self, since=None, until=None, page=None, limit=None):
        if request.environ['PATH_INFO'].endswith('.atom'):
            feed_type = 'atom'
        else:
            feed_type = 'rss'
        title = 'Recent changes to Project %s' % c.project.name
        feed = M.Feed.feed(dict(project_id=c.project._id), feed_type, title,
                           c.project.url(), title, since, until, page, limit)
        response.headers['Content-Type'] = ''
        response.content_type = 'application/xml'
        return feed.writeString('utf-8')

    @expose()
    def icon(self):
        icon = c.project.icon
        if not icon:
            raise exc.HTTPNotFound
        return icon.serve()

    @expose()
    def user_icon(self):
        try:
            return self.icon()
        except exc.HTTPNotFound:
            redirect(g.forge_static('images/user.png'))

    @expose('json:')
    def user_search(self, term=''):
        if len(term) < 3:
            raise exc.HTTPBadRequest('"term" param must be at least length 3')
        users = M.User.by_display_name(term)
        named_roles = RoleCache(
            g.credentials,
            g.credentials.project_roles(
                project_id=c.project.root_project._id).named)
        result = [[], []]
        for u in users:
            if u._id in named_roles.userids_that_reach:
                result[0].append(u)
            else:
                pass
                # comment this back in if you want non-project-member users
                # in the search results
                #result[1].append(u)
        result = list(islice(chain(*result), 10))
        return dict(users=[
            dict(label='%s (%s)' % (u.get_pref('display_name'), u.username),
                 value=u.username,
                 id=u.username) for u in result
        ])
예제 #10
0
파일: main.py 프로젝트: vclisunlang/allura
class RootController(BaseController):
    def __init__(self):
        c.short_url_lightbox = W.short_url_lightbox

    def _check_security(self):
        require_access(c.app, 'read')

    @expose('jinja:forgeshorturl:templates/index.html')
    @validate(
        dict(page=validators.Int(if_empty=0, if_invalid=0),
             limit=validators.Int(if_empty=None, if_invalid=None)))
    def index(self, page=0, limit=None, **kw):
        c.page_list = W.page_list
        c.page_size = W.page_size
        limit, pagenum, start = g.handle_paging(limit, page, default=100)
        p = {'app_config_id': c.app.config._id}
        if not has_access(c.app, 'view_private'):
            p['private'] = False
        short_urls = (ShortUrl.query.find(p))
        count = short_urls.count()

        short_urls = short_urls.skip(start).limit(limit)

        return {
            'short_urls': short_urls,
            'limit': limit,
            'pagenum': pagenum,
            'count': count,
            'url_len': len(ShortUrl.build_short_url(c.app, short_name='')),
        }

    @expose('jinja:forgeshorturl:templates/search.html')
    @validate(
        dict(q=validators.UnicodeString(if_empty=None),
             project=validators.StringBool(if_empty=False)))
    def search(self, q=None, project=None, limit=None, page=0, **kw):
        c.search_results = W.search_results
        c.help_modal = W.search_help
        search_params = kw
        search_params.update({
            'q': q or '',
            'project': project,
            'limit': limit,
            'page': page,
            'allowed_types': ['ShortUrl'],
        })
        if not has_access(c.app, 'view_private'):
            search_params['fq'] = ['private_b:False']
        d = search_app(**search_params)
        d['search_comments_disable'] = True
        d['search_history_disable'] = True
        d['url_len'] = len(ShortUrl.build_short_url(c.app, short_name=''))
        return d

    @expose()
    def _lookup(self, pname, *remainder):
        query = {'app_config_id': c.app.config._id, 'short_name': pname}
        if not has_access(c.app, 'view_private'):
            query['private'] = False
        short_url = ShortUrl.query.find(query).first()
        if short_url:
            redirect(short_url.full_url)
        raise exc.HTTPNotFound()
예제 #11
0
from datetime import datetime, timedelta

from formencode import validators as fev
from formencode import schema as fes
from formencode import foreach as fef


class DateTime(fev.FancyValidator):
    def _to_python(self, value, state=None):
        if '.' in value:
            iso, frac_s = value.split('.')
        else:
            iso, frac_s = value, '0'
        us = float('0.' + frac_s) * 1e6
        ts = datetime.strptime(iso, '%Y-%m-%d %H:%M:%S')
        return ts + timedelta(microseconds=us)


message_schema = fes.Schema(priority=fev.Int(if_empty=10, if_missing=10),
                            delay=fev.Int(if_missing=0),
                            timeout=fev.Int(if_missing=300),
                            tags=fef.ForEach(fev.UnicodeString,
                                             convert_to_list=True,
                                             if_missing=[]))

get_schema = fes.Schema(client=fev.UnicodeString(required=True),
                        count=fev.Int(if_missing=1),
                        timeout=fev.Int(if_missing=0))

retry_schema = fes.Schema(delay=fev.Int(if_empty=5, if_missing=5))
예제 #12
0
class SitemapsController(BaseController):
    """
    Sitemap generation
    """

    @validate(validators={
        'page': validators.Int(if_empty=None, if_missing=None, if_invalid=None), 
        'limit': validators.Int(if_empty=10000, if_missing=10000, if_invalid=10000)
    })
    @beaker_cache(expire=60 * 60 * 4)
    @expose('sitemaps/google.xml')
    @observable(events.SitemapsController.google)
    def google(self, page=None, limit=10000, **kwargs):
        """Generate a sitemap which contains googles Video Sitemap information.

        This action may return a <sitemapindex> or a <urlset>, depending
        on how many media items are in the database, and the values of the
        page and limit params.

        :param page: Page number, defaults to 1.
        :type page: int
        :param page: max records to display on page, defaults to 10000.
        :type page: int

        """
        if request.settings['sitemaps_display'] != 'True':
            abort(404)

        response.content_type = \
            content_type_for_response(['application/xml', 'text/xml'])

        media = viewable_media(Media.query.published())

        if page is None:
            if media.count() > limit:
                return dict(pages=math.ceil(media.count() / float(limit)))
        else:
            page = int(page)
            media = media.offset(page * limit).limit(limit)

        if page:
            links = []
        else:
            links = [
                url_for(controller='/', qualified=True),
                url_for(controller='/media', show='popular', qualified=True),
                url_for(controller='/media', show='latest', qualified=True),
                url_for(controller='/categories', qualified=True),
            ]

        return dict(
            media = media,
            page = page,
            links = links,
        )

    @beaker_cache(expire=60 * 60, query_args=True)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.mrss)
    def mrss(self, **kwargs):
        """Generate a media rss (mRSS) feed of all the sites media."""
        if request.settings['sitemaps_display'] != 'True':
            abort(404)


        response.content_type = content_type_for_response(
            ['application/rss+xml', 'application/xml', 'text/xml'])

        media = viewable_media(Media.query.published())

        return dict(
            media = media,
            title = 'MediaRSS Sitemap',
        )

    @validate(validators={
        'limit': LimitFeedItemsValidator(),
        'skip': validators.Int(if_empty=0, if_missing=0, if_invalid=0)
    })
    @beaker_cache(expire=60 * 3)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.latest)
    def latest(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of all the sites media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = content_type_for_response(
            ['application/rss+xml', 'application/xml', 'text/xml'])

        media_query = Media.query.published().order_by(Media.publish_on.desc())
        media = viewable_media(media_query)
        if limit is not None:
            media = media.limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Latest Media',
        )

    @validate(validators={
        'limit': LimitFeedItemsValidator(),
        'skip': validators.Int(if_empty=0, if_missing=0, if_invalid=0)
    })
    @beaker_cache(expire=60 * 3)
    @expose('sitemaps/mrss.xml')
    @observable(events.SitemapsController.featured)
    def featured(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = content_type_for_response(
            ['application/rss+xml', 'application/xml', 'text/xml'])

        media_query = Media.query.in_category(get_featured_category())\
            .published()\
            .order_by(Media.publish_on.desc())
        media = viewable_media(media_query)
        if limit is not None:
            media = media.limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Featured Media',
        )

    @expose()
    def crossdomain_xml(self, **kwargs):
        """Serve the crossdomain XML file manually if static_files is disabled.

        If someone forgets to add this Alias we might as well serve this file
        for them and save everyone the trouble. This only works when MediaDrop
        is served out of the root of a domain and if Cooliris is enabled.
        """
        global crossdomain_app

        if not request.settings['appearance_enable_cooliris']:
            # Ensure the cache is cleared if cooliris is suddenly disabled
            if crossdomain_app:
                crossdomain_app = None
            raise HTTPNotFound()

        if not crossdomain_app:
            relpath = 'mediadrop/public/crossdomain.xml'
            abspath = os.path.join(config['here'], relpath)
            crossdomain_app = FileApp(abspath)

        return forward(crossdomain_app)
예제 #13
0
class CommitBrowser(BaseController):
    TreeBrowserClass=None
    revision_widget = SCMRevisionWidget()
    log_widget=SCMLogWidget()
    page_list=ffw.PageList()
    DEFAULT_PAGE_LIMIT = 25

    def __init__(self, revision):
        self._revision = revision
        self._commit = c.app.repo.commit(revision)
        c.revision = revision
        if self._commit is None:
            raise exc.HTTPNotFound
        self.tree = self.TreeBrowserClass(self._commit, tree=self._commit.tree)

    @expose('jinja:allura:templates/repo/commit.html')
    @validate(dict(page=validators.Int(if_empty=0),
                   limit=validators.Int(if_empty=DEFAULT_PAGE_LIMIT)))
    def index(self, page=0, limit=DEFAULT_PAGE_LIMIT, **kw):
        c.revision_widget = self.revision_widget
        c.page_list = self.page_list
        result = dict(commit=self._commit)
        if self._commit:
            result.update(self._commit.context())
        tree = self._commit.tree
        limit, page, start = g.handle_paging(limit, page,
                                             default=self.DEFAULT_PAGE_LIMIT)
        diffs = self._commit.paged_diffs(start=start, end=start+limit)
        result['artifacts'] = [
                (t,f) for t in ('added', 'removed', 'changed', 'copied')
                    for f in diffs[t]
                        if t == 'removed' or tree.get_blob_by_path(f)]
        count = diffs['total']
        result.update(dict(page=page, limit=limit, count=count))
        return result

    @expose('jinja:allura:templates/repo/commit_basic.html')
    def basic(self, **kw):
        c.revision_widget = self.revision_widget
        result = dict(commit=self._commit)
        if self._commit:
            result.update(self._commit.context())
        return result

    @expose('jinja:allura:templates/repo/tarball.html')
    def tarball(self, **kw):
        path = request.params.get('path')
        if not asbool(tg.config.get('scm.repos.tarball.enable', False)):
            raise exc.HTTPNotFound()
        rev = self._commit.url().split('/')[-2]
        status = c.app.repo.get_tarball_status(rev, path)
        if status is None and request.method == 'POST':
            allura.tasks.repo_tasks.tarball.post(revision=rev, path=path)
            redirect('tarball' + '?path={0}'.format(path) if path else '')
        return dict(commit=self._commit, revision=rev, status=status or 'na')

    @expose('json:')
    def tarball_status(self, path=None, **kw):
        if not asbool(tg.config.get('scm.repos.tarball.enable', False)):
            raise exc.HTTPNotFound()
        rev = self._commit.url().split('/')[-2]
        return dict(status=c.app.repo.get_tarball_status(rev, path) or 'na')


    @expose('jinja:allura:templates/repo/log.html')
    @with_trailing_slash
    @validate(dict(page=validators.Int(if_empty=0),
                   limit=validators.Int(if_empty=25)))
    def log(self, limit=25, path=None, **kw):
        is_file = False
        if path:
            is_file = c.app.repo.is_file(path, self._commit._id)
        commits = list(islice(c.app.repo.log(
                revs=self._commit._id,
                path=path,
                id_only=False,
                page_size=limit+1), limit+1))
        next_commit = None
        if len(commits) > limit:
            next_commit = commits.pop()
        c.log_widget = self.log_widget
        return dict(
            username=c.user._id and c.user.username,
            branch=None,
            log=commits,
            next_commit=next_commit,
            limit=limit,
            path=path,
            is_file=is_file,
            **kw)
예제 #14
0
파일: location.py 프로젝트: tmfox/zookeepr
class LocationSchema(BaseSchema):
    display_name = validators.String(not_empty=True)
    display_order = validators.Int()
    capacity = validators.Int()
예제 #15
0
class RootController(BaseController, DispatchIndex, FeedController):
    class W(object):
        new_topic = DW.NewTopicPost(submit_text='Post')

        announcements_table = FW.AnnouncementsTable()
        add_forum = AddForumShort()
        search_results = SearchResults()
        search_help = SearchHelp(
            comments=False,
            history=False,
            fields={
                'author_user_name_t': 'Username',
                'text': '"Post text"',
                'timestamp_dt':
                'Date posted.  Example: timestamp_dt:[2018-01-01T00:00:00Z TO *]',
                'name_s': 'Subject'
            })

    def _check_security(self):
        require_access(c.app, 'read')

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def index(self, new_forum=False, **kw):
        c.add_forum = self.W.add_forum
        c.announcements_table = self.W.announcements_table
        announcements = model.ForumThread.query.find(
            dict(
                app_config_id=c.app.config._id,
                flags='Announcement',
            )).all()
        forums = model.Forum.query.find(
            dict(app_config_id=c.app.config._id, parent_id=None,
                 deleted=False)).all()
        forums = [f for f in forums if h.has_access(f, 'read')()]
        return dict(forums=forums,
                    announcements=announcements,
                    hide_forum=(not new_forum))

    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def new_forum(self, **kw):
        require_access(c.app, 'configure')
        return self.index(new_forum=True, **kw)

    @h.vardec
    @expose()
    @require_post()
    @validate(form=W.add_forum, error_handler=index)
    def add_forum_short(self, add_forum=None, **kw):
        require_access(c.app, 'configure')
        f = utils.create_forum(c.app, add_forum)
        redirect(f.url())

    @with_trailing_slash
    @expose(
        'jinja:forgediscussion:templates/discussionforums/create_topic.html')
    def create_topic(self, forum_name=None, new_forum=False, **kw):
        forums = model.Forum.query.find(
            dict(app_config_id=c.app.config._id, parent_id=None,
                 deleted=False))
        c.new_topic = self.W.new_topic
        my_forums = []
        forum_name = h.really_unicode(
            unquote(forum_name)) if forum_name else None
        current_forum = None
        for f in forums:
            if forum_name == f.shortname:
                current_forum = f
            if has_access(f, 'post')():
                my_forums.append(f)
        return dict(
            forums=my_forums,
            current_forum=current_forum,
            subscribed=M.Mailbox.subscribed(artifact=current_forum),
            subscribed_to_tool=M.Mailbox.subscribed(),
        )

    @memorable_forget()
    @h.vardec
    @expose()
    @require_post()
    @validate(W.new_topic, error_handler=create_topic)
    @AntiSpam.validate('Spambot protection engaged')
    def save_new_topic(self,
                       subject=None,
                       text=None,
                       forum=None,
                       subscribe=False,
                       **kw):
        self.rate_limit(model.ForumPost, 'Topic creation', request.referer)
        discussion = model.Forum.query.get(app_config_id=c.app.config._id,
                                           shortname=forum)
        if discussion.deleted and not has_access(c.app, 'configure')():
            flash('This forum has been removed.')
            redirect(request.referrer)
        require_access(discussion, 'post')
        thd = discussion.get_discussion_thread(
            dict(headers=dict(Subject=subject)))[0]
        p = thd.post(subject, text, subscribe=subscribe)
        if 'attachment' in kw:
            p.add_multiple_attachments(kw['attachment'])
        thd.post_to_feed(p)
        flash('Message posted')
        redirect(thd.url())

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/search.html')
    @validate(
        dict(q=validators.UnicodeString(if_empty=None),
             history=validators.StringBool(if_empty=False),
             project=validators.StringBool(if_empty=False),
             limit=validators.Int(if_empty=None, if_invalid=None),
             page=validators.Int(if_empty=0, if_invalid=0)))
    def search(self,
               q=None,
               history=None,
               project=None,
               limit=None,
               page=0,
               **kw):
        c.search_results = self.W.search_results
        c.help_modal = self.W.search_help
        search_params = kw
        search_params.update({
            'q':
            q or '',
            'history':
            history,
            'project':
            project,
            'limit':
            limit,
            'page':
            page,
            'allowed_types': ['Post', 'Post Snapshot', 'Discussion', 'Thread'],
        })
        d = search_app(**search_params)
        d['search_comments_disable'] = True
        return d

    @expose('jinja:allura:templates/markdown_syntax.html')
    def markdown_syntax(self, **kw):
        'Static page explaining markdown.'
        return dict()

    @with_trailing_slash
    @expose('jinja:allura:templates/markdown_syntax_dialog.html')
    def markdown_syntax_dialog(self, **kw):
        'Static dialog page about how to use markdown.'
        return dict()

    @expose()
    def _lookup(self, id=None, *remainder):
        if id:
            id = unquote(id)
            forum = model.Forum.query.get(app_config_id=c.app.config._id,
                                          shortname=id)
            if forum is None:
                raise exc.HTTPNotFound()
            c.forum = forum
            return ForumController(id), remainder
        else:
            raise exc.HTTPNotFound()

    def get_feed(self, project, app, user):
        """Return a :class:`allura.controllers.feed.FeedArgs` object describing
        the xml feed for this controller.

        Overrides :meth:`allura.controllers.feed.FeedController.get_feed`.

        """
        return FeedArgs(
            dict(project_id=project._id, app_config_id=app.config._id),
            'Recent posts to %s' % app.config.options.mount_label, app.url)

    @without_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/stats_graph.html'
            )
    def stats(self, dates=None, forum=None, **kw):
        if not dates:
            dates = "{} to {}".format(
                (date.today() - timedelta(days=60)).strftime('%Y-%m-%d'),
                date.today().strftime('%Y-%m-%d'))
        return dict(
            dates=dates,
            selected_forum=forum,
        )

    @expose('json:')
    @validate(
        dict(
            begin=h.DateTimeConverter(if_empty=None, if_invalid=None),
            end=h.DateTimeConverter(if_empty=None, if_invalid=None),
        ))
    def stats_data(self, begin=None, end=None, forum=None, **kw):
        end = end or date.today()
        begin = begin or end - timedelta(days=60)

        discussion_id_q = {
            '$in':
            [d._id for d in c.app.forums if d.shortname == forum or not forum]
        }
        # must be ordered dict, so that sorting by this works properly
        grouping = OrderedDict()
        grouping['year'] = {'$year': '$timestamp'}
        grouping['month'] = {'$month': '$timestamp'}
        grouping['day'] = {'$dayOfMonth': '$timestamp'}
        mongo_data = model.ForumPost.query.aggregate([
            {
                '$match': {
                    'discussion_id': discussion_id_q,
                    'status': 'ok',
                    'timestamp': {
                        # convert date to datetime to make pymongo happy
                        '$gte': datetime.combine(begin, time.min),
                        '$lte': datetime.combine(end, time.max),
                    },
                    'deleted': False,
                }
            },
            {
                '$group': {
                    '_id': grouping,
                    'posts': {
                        '$sum': 1
                    },
                }
            },
            {
                '$sort': {
                    '_id': pymongo.ASCENDING,
                }
            },
        ])['result']

        def reformat_data(mongo_data):
            def item(day, val):
                return [calendar.timegm(day.timetuple()) * 1000, val]

            next_expected_date = begin
            for d in mongo_data:
                this_date = datetime(d['_id']['year'], d['_id']['month'],
                                     d['_id']['day'])
                for day in h.daterange(next_expected_date, this_date):
                    yield item(day, 0)
                yield item(this_date, d['posts'])
                next_expected_date = this_date + timedelta(days=1)
            for day in h.daterange(next_expected_date,
                                   end + timedelta(days=1)):
                yield item(day, 0)

        return dict(
            begin=begin,
            end=end,
            data=list(reformat_data(mongo_data)),
        )
예제 #16
0
class FeedController(object):
    """Mixin class which adds RSS and Atom feed endpoints to an existing
    controller.

    Feeds will be accessible at the following URLs:

        http://host/path/to/controller/feed -> RSS
        http://host/path/to/controller/feed.rss -> RSS
        http://host/path/to/controller/feed.atom -> Atom

    A default feed is provided by :meth:`get_feed`. Subclasses that need
    a customized feed should override :meth:`get_feed`.

    """
    FEED_TYPES = ['.atom', '.rss']
    FEED_NAMES = ['feed{0}'.format(typ) for typ in FEED_TYPES]

    def __getattr__(self, name):
        if name in self.FEED_NAMES:
            return self.feed
        raise AttributeError(name)

    def _get_feed_type(self, request):
        for typ in self.FEED_TYPES:
            if request.environ['PATH_INFO'].endswith(typ):
                return typ.lstrip('.')
        return 'rss'

    @without_trailing_slash
    @expose()
    @validate(
        dict(since=h.DateTimeConverter(if_empty=None, if_invalid=None),
             until=h.DateTimeConverter(if_empty=None, if_invalid=None),
             page=V.Int(if_empty=None, if_invalid=None),
             limit=V.Int(if_empty=None, if_invalid=None)))
    def feed(self, since=None, until=None, page=None, limit=None, **kw):
        """Return a utf8-encoded XML feed (RSS or Atom) to the browser.
        """
        feed_def = self.get_feed(c.project, c.app, c.user)
        if not feed_def:
            raise exc.HTTPNotFound
        feed = M.Feed.feed(feed_def.query, self._get_feed_type(request),
                           feed_def.title, feed_def.url, feed_def.description,
                           since, until, page, limit)
        response.headers['Content-Type'] = ''
        response.content_type = 'application/xml'
        return feed.writeString('utf-8')

    def get_feed(self, project, app, user):
        """Return a default :class:`FeedArgs` for this controller.

        Subclasses should override to customize the feed.

        :param project: :class:`allura.model.project.Project`
        :param app: :class:`allura.app.Application`
        :param user: :class:`allura.model.auth.User`
        :rtype: :class:`FeedArgs`

        """
        return FeedArgs(
            dict(project_id=project._id, app_config_id=app.config._id),
            'Recent changes to %s' % app.config.options.mount_point, app.url)
예제 #17
0
 class SubSchema(Schema):
     age = validators.Int()
     name = validators.String(not_empty=True)
예제 #18
0
class FormSchema(Schema):
    name = validators.String(not_empty=True)
    age = validators.Int(min=13, max=99)
    color = validators.OneOf(['red', 'blue', 'black', 'green'])
    filter_extra_fields = True
    allow_extra_fields = True
예제 #19
0
class InvoiceSchema(BaseSchema):
    person = ExistingPersonValidator(not_empty=True)
    due_date = validators.DateConverter(month_style='dd/mm/yyyy')
    items = ForEach(InvoiceItemValidator())

    item_count = validators.Int(min=0) # no max, doesn't hit database
예제 #20
0
class RootController(BaseController):
    """
    The root controller for the rubrica application.
    All the other controllers and WSGI applications should be mounted on this
    controller. For example::

        panel = ControlPanelController()
        another_app = AnotherWSGIApplication()

    Keep in mind that WSGI applications shouldn't be mounted directly: They
    must be wrapped around with :class:`tg.controllers.WSGIAppController`.
    """
    secc = SecureController()
    admin = AdminController(model, DBSession, config_type=TGAdminConfig)

    error = ErrorController()

    check = not_anonymous(msg='Solo gli utenti loggati possono accedere')

    def _before(self, *args, **kw):
        tmpl_context.project_name = "rubrica"

    @paginate("data", items_per_page=10)
    @expose('rubrica.templates.standard_index')
    def index(self, **kw):
        """handle index page"""
        if not request.identity:
            redirect('/login')
        data = DBSession.query(Contatto).filter_by(owner=request.identity['user'].user_id)
        ordering = kw.get('ordercol')
        if ordering and ordering[0] == '+':
            data = data.order_by(asc(ordering[1:]))
        elif ordering and ordering[0] == '-':
            data = data.order_by(desc(ordering[1:]))
        return dict(page='index', grid=tabella, data=data)

    @expose('json')
    @require(check)
    def esponi(self):
        """Espone la rubrica in formato JSON"""
        data = DBSession.query(Contatto).filter_by(owner=request.identity['user'].user_id).all()
        return dict(data=data)

    @expose('json')
    @require(check)
    def download(self):
        """Download della rubrica"""
        data = DBSession.query(Contatto).filter_by(owner=request.identity['user'].user_id).all()
        response.content_type = 'applications/json'
        response.headerlist.append(('Content-Disposition', 'attachment;filename=rubrica.json'))
        return dict(data=data)

    @expose('rubrica.templates.submitForm')
    @require(check)
    def add(self, **kw):
        """Aggiunge contatto"""
        return dict(page='add', form=SubmitForm)

    @expose()
    @require(check)
    @validate(SubmitForm, error_handler=add)
    def save(self, **kw):
        """Salva il contatto aggiunto con add"""
        contatto = Contatto(name=kw['nome'], phone=kw['telefono'])
        request.identity['user'].contacts.append(contatto)
        DBSession.add(contatto)
        redirect('/index')

    @expose()
    @require(check)
    @validate({"item_id": validators.Int(not_empty=True)})
    def delete(self, item_id):
        """Elimina contatto"""
        if not DBSession.query(exists().where(Contatto.id == item_id)).scalar():
            flash(_("Il contatto non esiste"))
            redirect('/index')
        if DBSession.query(Contatto).get(item_id) in request.identity['user'].contacts: 
            contatto = DBSession.query(Contatto).get(item_id)
            DBSession.delete(contatto)
            redirect('/index')
        else:
            flash(_("Non puoi eliminare questo contatto"))
            redirect('/index')

    @expose('rubrica.templates.about')
    def about(self):
        """Handle the 'about' page."""
        return dict(page='about')

    @expose('rubrica.templates.environ')
    def environ(self):
        """This method showcases TG's access to the wsgi environment."""
        return dict(page='environ', environment=request.environ)

    @expose('json')
    @expose('rubrica.templates.data')
    def data(self, **kw):
        """
        This method showcases how you can use the same controller
        for a data page and a display page.
        """
        return dict(page='data', params=kw)

    @expose('rubrica.templates.index')
    @require(predicates.has_permission('manage', msg=l_('Only for managers')))
    def manage_permission_only(self, **kw):
        """Illustrate how a page for managers only works."""
        return dict(page='managers stuff')

    @expose('rubrica.templates.index')
    @require(predicates.is_user('editor', msg=l_('Only for the editor')))
    def editor_user_only(self, **kw):
        """Illustrate how a page exclusive for the editor works."""
        return dict(page='editor stuff')

    @expose('rubrica.templates.login')
    def login(self, came_from=lurl('/'), failure=None, login=''):
        """Start the user login."""
        if failure is not None:
            if failure == 'user-not-found':
                flash(_('User not found'), 'error')
            elif failure == 'invalid-password':
                flash(_('Invalid Password'), 'error')

        login_counter = request.environ.get('repoze.who.logins', 0)
        if failure is None and login_counter > 0:
            flash(_('Wrong credentials'), 'warning')

        return dict(page='login', login_counter=str(login_counter),
                    came_from=came_from, login=login)

    @expose()
    def post_login(self, came_from=lurl('/')):
        """
        Redirect the user to the initially requested page on successful
        authentication or redirect her back to the login page if login failed.

        """
        if not request.identity:
            login_counter = request.environ.get('repoze.who.logins', 0) + 1
            redirect('/login',
                     params=dict(came_from=came_from, __logins=login_counter))
        userid = request.identity['repoze.who.userid']
        flash(_('Welcome back, %s!') % userid)

        # Do not use tg.redirect with tg.url as it will add the mountpoint
        # of the application twice.
        return HTTPFound(location=came_from)

    @expose()
    def post_logout(self, came_from=lurl('/')):
        """
        Redirect the user to the initially requested page on logout and say
        goodbye as well.

        """
        flash(_('We hope to see you soon!'))
        return HTTPFound(location=came_from)
예제 #21
0
class RootController(BaseController):
    class W(object):
        forum_subscription_form = FW.ForumSubscriptionForm()
        new_topic = DW.NewTopicPost(submit_text='Post')
        announcements_table = FW.AnnouncementsTable()
        add_forum = AddForumShort()
        search_results = SearchResults()

    def _check_security(self):
        require_access(c.app, 'read')

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def index(self, new_forum=False, **kw):
        c.new_topic = self.W.new_topic
        c.new_topic = self.W.new_topic
        c.add_forum = self.W.add_forum
        c.announcements_table = self.W.announcements_table
        announcements = model.ForumThread.query.find(
            dict(
                app_config_id=c.app.config._id,
                flags='Announcement',
            )).all()
        forums = model.Forum.query.find(
            dict(app_config_id=c.app.config._id, parent_id=None,
                 deleted=False)).all()
        forums = [f for f in forums if h.has_access(f, 'read')()]
        threads = dict()
        for forum in forums:
            threads[forum._id] = model.ForumThread.query.find(
                dict(discussion_id=forum._id,
                     num_replies={'$gt':
                                  0})).sort('mod_date',
                                            pymongo.DESCENDING).limit(6).all()
        return dict(forums=forums,
                    threads=threads,
                    announcements=announcements,
                    hide_forum=(not new_forum))

    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def new_forum(self, **kw):
        require_access(c.app, 'configure')
        return self.index(new_forum=True, **kw)

    @h.vardec
    @expose()
    @require_post()
    @validate(form=W.add_forum, error_handler=index)
    def add_forum_short(self, add_forum=None, **kw):
        require_access(c.app, 'configure')
        f = utils.create_forum(c.app, add_forum)
        redirect(f.url())

    @with_trailing_slash
    @expose(
        'jinja:forgediscussion:templates/discussionforums/create_topic.html')
    def create_topic(self, new_forum=False, **kw):
        c.new_topic = self.W.new_topic
        forums = [
            f for f in model.Forum.query.find(
                dict(app_config_id=c.app.config._id, parent_id=None)).all()
            if has_access(f, 'post')() and not f.deleted
        ]
        return dict(forums=forums)

    @h.vardec
    @expose()
    @require_post()
    @validate(W.new_topic, error_handler=create_topic)
    @AntiSpam.validate('Spambot protection engaged')
    def save_new_topic(self, subject=None, text=None, forum=None, **kw):
        discussion = model.Forum.query.get(app_config_id=c.app.config._id,
                                           shortname=forum)
        if discussion.deleted and not has_access(c.app, 'configure')():
            flash('This forum has been removed.')
            redirect(request.referrer)
        require_access(discussion, 'post')
        thd = discussion.get_discussion_thread(
            dict(headers=dict(Subject=subject)))[0]
        post = thd.post(subject, text)
        flash('Message posted')
        redirect(thd.url())

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/search.html')
    @validate(
        dict(q=validators.UnicodeString(if_empty=None),
             history=validators.StringBool(if_empty=False),
             project=validators.StringBool(if_empty=False),
             limit=validators.Int(if_empty=None),
             page=validators.Int(if_empty=0)))
    def search(self,
               q=None,
               history=False,
               project=False,
               limit=None,
               page=0,
               **kw):
        'local tool search'
        if project:
            redirect(c.project.url() + 'search?' +
                     urlencode(dict(q=q, history=history)))
        results = []
        count = 0
        limit, page, start = g.handle_paging(limit, page, default=25)
        if not q:
            q = ''
        else:
            results = search(
                q,
                rows=limit,
                start=start,
                fq=[
                    'is_history_b:%s' % history,
                    'project_id_s:%s' % c.project._id,
                    'mount_point_s:%s' % c.app.config.options.mount_point,
                    '-deleted_b:true'
                ])
            if results: count = results.hits
        c.search_results = self.W.search_results
        return dict(q=q,
                    history=history,
                    results=results or [],
                    count=count,
                    limit=limit,
                    page=page)

    @expose('jinja:allura:templates/markdown_syntax.html')
    def markdown_syntax(self):
        'Static page explaining markdown.'
        return dict()

    @with_trailing_slash
    @expose('jinja:allura:templates/markdown_syntax_dialog.html')
    def markdown_syntax_dialog(self):
        'Static dialog page about how to use markdown.'
        return dict()

    @expose()
    def _lookup(self, id=None, *remainder):
        if id:
            id = unquote(id)
            return ForumController(id), remainder
        else:
            raise exc.HTTPNotFound()

    @h.vardec
    @expose()
    @validate(W.forum_subscription_form)
    def subscribe(self, **kw):
        require_authenticated()
        forum = kw.pop('forum', [])
        thread = kw.pop('thread', [])
        objs = []
        for data in forum:
            objs.append(
                dict(obj=model.Forum.query.get(shortname=data['shortname'],
                                               app_config_id=c.app.config._id),
                     subscribed=bool(data.get('subscribed'))))
        for data in thread:
            objs.append(
                dict(obj=model.Thread.query.get(_id=data['id']),
                     subscribed=bool(data.get('subscribed'))))
        for obj in objs:
            if obj['subscribed']:
                obj['obj'].subscriptions[str(c.user._id)] = True
            else:
                obj['obj'].subscriptions.pop(str(c.user._id), None)
        redirect(request.referer)

    @expose()
    @validate(
        dict(since=h.DateTimeConverter(if_empty=None),
             until=h.DateTimeConverter(if_empty=None),
             page=validators.Int(if_empty=None),
             limit=validators.Int(if_empty=None)))
    def feed(self, since=None, until=None, page=None, limit=None):
        if request.environ['PATH_INFO'].endswith('.atom'):
            feed_type = 'atom'
        else:
            feed_type = 'rss'
        title = 'Recent posts to %s' % c.app.config.options.mount_label

        feed = Feed.feed(
            dict(project_id=c.project._id, app_config_id=c.app.config._id),
            feed_type, title, c.app.url, title, since, until, page, limit)
        response.headers['Content-Type'] = ''
        response.content_type = 'application/xml'
        return feed.writeString('utf-8')
예제 #22
0
파일: forms.py 프로젝트: TuringTux/muesli
 def __init__(self, request, lecture):
     self.request = request
     formfields = [
         FormField('type',
                   label='Typ',
                   type='select',
                   options=[[type, data['name']] for type, data in list(
                       request.config['lecture_types'].items())],
                   value=lecture.type,
                   required=True),
         FormField('name',
                   label='Name',
                   type='text',
                   size=100,
                   value=lecture.name,
                   required=True,
                   placeholder="Lineare Algebra II"),
         FormField('term',
                   label='Semester',
                   type='select',
                   options=utils.getTerms(),
                   value=lecture.term),
         FormField('lsf_id',
                   label='Veranstaltungsnummer',
                   type='text',
                   size=20,
                   value=lecture.lsf_id,
                   placeholder="LSF#123456"),
         FormField('lecturer',
                   label='Dozent',
                   type='text',
                   size=40,
                   value=lecture.lecturer),
         FormField('url',
                   label='Homepage',
                   size=100,
                   value=lecture.url,
                   placeholder="https://example.com"),
         FormField('mode',
                   label='Anmeldemodus',
                   type='select',
                   options=utils.modes,
                   value=lecture.mode),
         FormField('minimum_preferences',
                   label='Minimum möglicher Termine',
                   size=5,
                   comment='''Bei Präferenzenanmeldung: Studenten müssen
             mindestens an soviel Terminen können. (Leer:
             Defaultformel)''',
                   value=lecture.minimum_preferences,
                   validator=validators.Int()),
         FormField('tutor_rights',
                   label='Tutorenrechte',
                   type='select',
                   options=utils.tutorRights,
                   value=lecture.tutor_rights),
         FormField(
             'password',
             label='Passwort für Übungsleiter',
             size=40,
             comment=
             'Bei leerem Passwort keine Anmeldung als Übungsleiter möglich',
             value=lecture.password),
         FormField('is_visible',
                   label='Sichtbar',
                   type='radio',
                   options=[[1, 'Ja'], [0, 'Nein']],
                   value=boolToValue(lecture.is_visible))
     ]
     #if request.permissionInfo.has_permission('change_assistant'):
     #assistants = request.db.query(models.User).filter(models.User.is_assistant==1).order_by(models.User.last_name).all()
     #formfields.append(
     #FormField('assistant',
     #label='Assistent',
     #type='select',
     #options=[[a.id, unicode(a)] for a in assistants],
     #value=lecture.assistant.id,
     #required=True,
     #))
     ObjectForm.__init__(self, lecture, formfields, request, send='Ändern')
예제 #23
0
 class fields(WidgetsList):
     """This WidgetsList is just a container."""
     title=TextField(validator = validators.NotEmpty())
     year = TextField(size=4, validator=validators.Int())
예제 #24
0
class RootController(BaseController, DispatchIndex, FeedController):
    class W(object):
        forum_subscription_form = FW.ForumSubscriptionForm()
        new_topic = DW.NewTopicPost(submit_text='Post')
        announcements_table = FW.AnnouncementsTable()
        add_forum = AddForumShort()
        search_results = SearchResults()
        search_help = SearchHelp(comments=False, history=False)

    def _check_security(self):
        require_access(c.app, 'read')

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def index(self, new_forum=False, **kw):
        c.new_topic = self.W.new_topic
        c.new_topic = self.W.new_topic
        c.add_forum = self.W.add_forum
        c.announcements_table = self.W.announcements_table
        announcements = model.ForumThread.query.find(
            dict(
                app_config_id=c.app.config._id,
                flags='Announcement',
            )).all()
        forums = model.Forum.query.find(
            dict(app_config_id=c.app.config._id, parent_id=None,
                 deleted=False)).all()
        forums = [f for f in forums if h.has_access(f, 'read')()]
        return dict(forums=forums,
                    announcements=announcements,
                    hide_forum=(not new_forum))

    @expose('jinja:forgediscussion:templates/discussionforums/index.html')
    def new_forum(self, **kw):
        require_access(c.app, 'configure')
        return self.index(new_forum=True, **kw)

    @h.vardec
    @expose()
    @require_post()
    @validate(form=W.add_forum, error_handler=index)
    def add_forum_short(self, add_forum=None, **kw):
        require_access(c.app, 'configure')
        f = utils.create_forum(c.app, add_forum)
        redirect(f.url())

    @with_trailing_slash
    @expose(
        'jinja:forgediscussion:templates/discussionforums/create_topic.html')
    def create_topic(self, forum_name=None, new_forum=False, **kw):
        forums = model.Forum.query.find(
            dict(app_config_id=c.app.config._id, parent_id=None,
                 deleted=False))
        c.new_topic = self.W.new_topic
        my_forums = []
        forum_name = h.really_unicode(
            unquote(forum_name)) if forum_name else None
        current_forum = None
        for f in forums:
            if forum_name == f.shortname:
                current_forum = f
            if has_access(f, 'post')():
                my_forums.append(f)
        return dict(forums=my_forums, current_forum=current_forum)

    @h.vardec
    @expose()
    @require_post()
    @validate(W.new_topic, error_handler=create_topic)
    @AntiSpam.validate('Spambot protection engaged')
    def save_new_topic(self, subject=None, text=None, forum=None, **kw):
        discussion = model.Forum.query.get(app_config_id=c.app.config._id,
                                           shortname=forum)
        if discussion.deleted and not has_access(c.app, 'configure')():
            flash('This forum has been removed.')
            redirect(request.referrer)
        require_access(discussion, 'post')
        thd = discussion.get_discussion_thread(
            dict(headers=dict(Subject=subject)))[0]
        post = thd.post(subject, text)
        flash('Message posted')
        redirect(thd.url())

    @with_trailing_slash
    @expose('jinja:forgediscussion:templates/discussionforums/search.html')
    @validate(
        dict(q=validators.UnicodeString(if_empty=None),
             history=validators.StringBool(if_empty=False),
             project=validators.StringBool(if_empty=False),
             limit=validators.Int(if_empty=None),
             page=validators.Int(if_empty=0)))
    def search(self,
               q=None,
               history=None,
               project=None,
               limit=None,
               page=0,
               **kw):
        c.search_results = self.W.search_results
        c.help_modal = self.W.search_help
        search_params = kw
        search_params.update({
            'q':
            q or '',
            'history':
            history,
            'project':
            project,
            'limit':
            limit,
            'page':
            page,
            'allowed_types': ['Post', 'Post Snapshot', 'Discussion', 'Thread'],
        })
        d = search_app(**search_params)
        d['search_comments_disable'] = True
        return d

    @expose('jinja:allura:templates/markdown_syntax.html')
    def markdown_syntax(self):
        'Static page explaining markdown.'
        return dict()

    @with_trailing_slash
    @expose('jinja:allura:templates/markdown_syntax_dialog.html')
    def markdown_syntax_dialog(self):
        'Static dialog page about how to use markdown.'
        return dict()

    @expose()
    def _lookup(self, id=None, *remainder):
        if id:
            id = unquote(id)
            forum = model.Forum.query.get(app_config_id=c.app.config._id,
                                          shortname=id)
            if forum is None:
                raise exc.HTTPNotFound()
            c.forum = forum
            return ForumController(id), remainder
        else:
            raise exc.HTTPNotFound()

    @h.vardec
    @expose()
    @validate(W.forum_subscription_form)
    def subscribe(self, **kw):
        require_authenticated()
        forum = kw.pop('forum', [])
        thread = kw.pop('thread', [])
        objs = []
        for data in forum:
            objs.append(
                dict(obj=model.Forum.query.get(shortname=data['shortname'],
                                               app_config_id=c.app.config._id),
                     subscribed=bool(data.get('subscribed'))))
        for data in thread:
            objs.append(
                dict(obj=model.Thread.query.get(_id=data['id']),
                     subscribed=bool(data.get('subscribed'))))
        for obj in objs:
            if obj['subscribed']:
                obj['obj'].subscriptions[str(c.user._id)] = True
            else:
                obj['obj'].subscriptions.pop(str(c.user._id), None)
        redirect(request.referer)

    def get_feed(self, project, app, user):
        """Return a :class:`allura.controllers.feed.FeedArgs` object describing
        the xml feed for this controller.

        Overrides :meth:`allura.controllers.feed.FeedController.get_feed`.

        """
        return FeedArgs(
            dict(project_id=project._id, app_config_id=app.config._id),
            'Recent posts to %s' % app.config.options.mount_label, app.url)
예제 #25
0
파일: forum.py 프로젝트: phraniiac/allura
class ForumController(DiscussionController):
    M = ModelConfig
    W = WidgetConfig

    def _check_security(self):
        require_access(self.discussion, 'read')

    def __init__(self, forum_id):
        self.ThreadController = ForumThreadController
        self.PostController = ForumPostController
        self.moderate = ForumModerationController(self)
        self.discussion = DM.Forum.query.get(app_config_id=c.app.config._id,
                                             shortname=forum_id)
        if not self.discussion:
            raise exc.HTTPNotFound()
        super(ForumController, self).__init__()

    @expose()
    def _lookup(self, id=None, *remainder):
        if id and self.discussion:
            return ForumController(self.discussion.shortname + '/' +
                                   id), remainder
        else:
            raise exc.HTTPNotFound()

    @expose('jinja:forgediscussion:templates/index.html')
    @validate(
        dict(page=validators.Int(if_empty=0, if_invalid=0),
             limit=validators.Int(if_empty=None, if_invalid=None)))
    def index(self, threads=None, limit=None, page=0, count=0, **kw):
        if self.discussion.deleted:
            redirect(self.discussion.url() + 'deleted')
        limit, page, start = g.handle_paging(limit, page)
        c.subscribed = M.Mailbox.subscribed(artifact=self.discussion)
        threads = DM.ForumThread.query.find(dict(discussion_id=self.discussion._id, num_replies={'$gt': 0})) \
                                      .sort([('flags', pymongo.DESCENDING), ('last_post_date', pymongo.DESCENDING)])
        c.discussion = self.W.discussion
        c.discussion_header = self.W.discussion_header
        c.whole_forum_subscription_form = self.W.subscribe_form
        return dict(discussion=self.discussion,
                    count=threads.count(),
                    threads=threads.skip(start).limit(int(limit)).all(),
                    limit=limit,
                    page=page)

    @expose('jinja:forgediscussion:templates/discussionforums/deleted.html')
    def deleted(self):
        return dict()

    @expose('json:')
    @require_post()
    @validate(W.subscribe_form)
    def subscribe_to_forum(self,
                           subscribe=None,
                           unsubscribe=None,
                           shortname=None,
                           **kw):
        if subscribe:
            self.discussion.subscribe(type='direct')
        elif unsubscribe:
            self.discussion.unsubscribe()
        return {
            'status': 'ok',
            'subscribed': M.Mailbox.subscribed(artifact=self.discussion),
            'subscribed_to_tool': M.Mailbox.subscribed(),
        }
예제 #26
0
파일: forum.py 프로젝트: vclisunlang/allura
class ForumThreadController(ThreadController):
    W = WidgetConfig

    @expose('jinja:forgediscussion:templates/discussionforums/thread.html')
    @validate(
        dict(page=validators.Int(if_empty=0, if_invalid=0),
             limit=validators.Int(if_empty=25, if_invalid=25)))
    def index(self, limit=25, page=0, count=0, **kw):
        if self.thread.discussion.deleted and not has_access(
                c.app, 'configure')():
            redirect(self.thread.discussion.url() + 'deleted')
        c.thread_subscription_form = self.W.subscribe_form
        return super(ForumThreadController, self).index(limit=limit,
                                                        page=page,
                                                        count=count,
                                                        show_moderate=True,
                                                        **kw)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, index)
    def moderate(self, **kw):
        require_access(self.thread, 'moderate')
        if self.thread.discussion.deleted and not has_access(
                c.app, 'configure')():
            redirect(self.thread.discussion.url() + 'deleted')
        args = self.W.moderate_thread.validate(kw, None)
        tasks.calc_forum_stats.post(self.thread.discussion.shortname)
        if args.pop('delete', None):
            url = self.thread.discussion.url()
            self.thread.delete()
            redirect(url)
        forum = args.pop('discussion')
        if forum != self.thread.discussion:
            tasks.calc_forum_stats.post(forum.shortname)
            self.thread.set_forum(forum)
        self.thread.flags = args.pop('flags', [])
        self.thread.subject = args.pop('subject', self.thread.subject)
        redirect(self.thread.url())

    @expose('json:')
    @require_post()
    @validate(W.subscribe_form)
    def subscribe(self, subscribe=None, unsubscribe=None, **kw):
        if subscribe:
            self.thread.subscribe()
        elif unsubscribe:
            self.thread.unsubscribe()

        sub_tool = M.Mailbox.subscribed()
        sub_forum = M.Mailbox.subscribed(artifact=self.discussion)
        return {
            'status':
            'ok',
            'subscribed':
            M.Mailbox.subscribed(artifact=self.thread),
            'subscribed_to_tool':
            sub_tool or sub_forum,
            'subscribed_to_entire_name':
            'forum' if sub_forum else 'discussion tool',
        }
예제 #27
0
class PageController(BaseController, FeedController):

    def __init__(self, title):
        self.title = h.really_unicode(unquote(title))
        self.page = WM.Page.query.get(
            app_config_id=c.app.config._id, title=self.title)
        if self.page is not None:
            self.attachment = WikiAttachmentsController(self.page)

    def _check_security(self):
        if self.page:
            require_access(self.page, 'read')
            if self.page.deleted:
                require_access(self.page, 'delete')
        else:
            require_access(c.app, 'create')
            self.rate_limit()

    def rate_limit(self):
        if WM.Page.is_limit_exceeded(c.app.config):
            msg = 'Page creation rate limit exceeded. '
            log.warn(msg + c.app.config.url())
            flash(msg + 'Please try again later.', 'error')
            redirect('..')

    def fake_page(self):
        return dict(
            title=self.title,
            text='',
            labels=[],
            viewable_by=['all'],
            attachments=[])

    def get_version(self, version):
        if not version:
            return self.page
        try:
            return self.page.get_version(version)
        except (ValueError, IndexError):
            return None

    @expose()
    def _lookup(self, pname, *remainder):
        page = WM.Page.query.get(
            app_config_id=c.app.config._id, title=pname)
        if page:
            redirect(page.url())
        else:
            raise exc.HTTPNotFound

    @with_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/page_view.html')
    @validate(dict(version=validators.Int(if_empty=None, if_invalid=None),
                   page=validators.Int(if_empty=0, if_invalid=0),
                   limit=validators.Int(if_empty=None, if_invalid=None)))
    def index(self, version=None, page=0, limit=None, **kw):
        if not self.page:
            redirect(c.app.url + h.urlquote(self.title) + '/edit')
        c.confirmation = W.confirmation
        c.thread = W.thread
        c.attachment_list = W.attachment_list
        c.subscribe_form = W.page_subscribe_form
        post_count = self.page.discussion_thread.post_count
        limit, pagenum, _ = g.handle_paging(limit, page)
        limit, pagenum = h.paging_sanitizer(limit, pagenum, post_count)
        page = self.get_version(version)
        if page is None:
            if version:
                redirect('.?version=%d' % (version - 1))
            else:
                redirect('.')
        elif 'all' not in page.viewable_by and c.user.username not in page.viewable_by:
            raise exc.HTTPForbidden(detail="You may not view this page.")
        cur = page.version
        if cur > 1:
            prev = cur - 1
        else:
            prev = None
        next = cur + 1
        hide_left_bar = not (c.app.show_left_bar)
        subscribed_to_page = M.Mailbox.subscribed(artifact=self.page)
        c.subscribe_form.tool_subscribed = M.Mailbox.subscribed()
        return dict(
            page=page,
            cur=cur, prev=prev, next=next,
            page_subscribed=subscribed_to_page,
            hide_left_bar=hide_left_bar, show_meta=c.app.show_right_bar,
            pagenum=pagenum, limit=limit, count=post_count)

    @without_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/page_edit.html')
    def edit(self):
        page_exists = self.page
        if self.page:
            require_access(self.page, 'edit')
            page = self.page
        else:
            page = self.fake_page()
        c.confirmation = W.confirmation
        c.markdown_editor = W.markdown_editor
        c.attachment_add = W.attachment_add
        c.attachment_list = W.attachment_list
        c.label_edit = W.label_edit
        hide_left_bar = not c.app.show_left_bar
        return dict(page=page, page_exists=page_exists,
                    hide_left_bar=hide_left_bar)

    @without_trailing_slash
    @expose('json:')
    @require_post()
    def delete(self, **kw):
        require_access(self.page, 'delete')
        self.page.delete()
        return dict(location='../' + self.page.title + '/?deleted=True')

    @without_trailing_slash
    @expose('json:')
    @require_post()
    def undelete(self, **kw):
        require_access(self.page, 'delete')
        self.page.deleted = False
        M.Shortlink.from_artifact(self.page)
        return dict(location='./edit')

    @without_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/page_history.html')
    @validate(dict(page=validators.Int(if_empty=0, if_invalid=0),
                   limit=validators.Int(if_empty=None, if_invalid=None)))
    def history(self, page=0, limit=None, **kw):
        if not self.page:
            raise exc.HTTPNotFound
        c.page_list = W.page_list
        c.page_size = W.page_size
        c.confirmation = W.confirmation
        limit, pagenum, start = g.handle_paging(limit, page, default=25)
        count = 0
        pages = self.page.history()
        count = pages.count()
        pages = pages.skip(start).limit(int(limit))
        return dict(title=self.title, pages=pages,
                    limit=limit, count=count, page=pagenum)

    @without_trailing_slash
    @expose('jinja:forgewiki:templates/wiki/page_diff.html')
    @validate(dict(
        v1=validators.Int(),
        v2=validators.Int()))
    def diff(self, v1, v2, **kw):
        if not self.page:
            raise exc.HTTPNotFound
        p1 = self.get_version(v1)
        p2 = self.get_version(v2)
        result = h.diff_text(p1.text, p2.text)
        return dict(p1=p1, p2=p2, edits=result)

    @without_trailing_slash
    @expose(content_type='text/plain')
    def raw(self):
        if not self.page:
            raise exc.HTTPNotFound
        return pformat(self.page)

    def get_feed(self, project, app, user):
        """Return a :class:`allura.controllers.feed.FeedArgs` object describing
        the xml feed for this controller.

        Overrides :meth:`allura.controllers.feed.FeedController.get_feed`.

        """
        if not self.page:
            return None
        return FeedArgs(
            {'ref_id': self.page.index_id()},
            'Recent changes to %s' % self.page.title,
            self.page.url())

    @without_trailing_slash
    @expose('json:')
    @require_post()
    @validate(dict(version=validators.Int(if_empty=1, if_invalid=1)))
    def revert(self, version, **kw):
        if not self.page:
            raise exc.HTTPNotFound
        require_access(self.page, 'edit')
        orig = self.get_version(version)
        if orig:
            self.page.text = orig.text
        self.page.commit()
        return dict(location='.')

    @without_trailing_slash
    @h.vardec
    @expose()
    @require_post()
    def update(self, title=None, text=None,
               labels=None,
               viewable_by=None,
               new_viewable_by=None, **kw):
        activity_verb = 'created'
        if not title:
            flash('You must provide a title for the page.', 'error')
            redirect('edit')
        title = title.replace('/', '-')
        if not self.page:
            # the page doesn't exist yet, so create it
            self.page = WM.Page.upsert(self.title)
            self.page.viewable_by = ['all']
        else:
            require_access(self.page, 'edit')
            activity_verb = 'modified'
        name_conflict = None
        if self.page.title != title:
            name_conflict = WM.Page.query.find(
                dict(app_config_id=c.app.config._id, title=title, deleted=False)).first()
            if name_conflict:
                flash('There is already a page named "%s".' % title, 'error')
            else:
                if self.page.title == c.app.root_page_name:
                    WM.Globals.query.get(
                        app_config_id=c.app.config._id).root = title
                self.page.title = title
                activity_verb = 'renamed'
        self.page.text = text
        if labels:
            self.page.labels = labels.split(',')
        else:
            self.page.labels = []
        self.page.commit()
        g.spam_checker.check(text, artifact=self.page,
                             user=c.user, content_type='wiki')
        g.director.create_activity(c.user, activity_verb, self.page,
                                   related_nodes=[c.project], tags=['wiki'])
        if new_viewable_by:
            if new_viewable_by == 'all':
                self.page.viewable_by.append('all')
            else:
                user = c.project.user_in_project(str(new_viewable_by))
                if user:
                    self.page.viewable_by.append(user.username)
        if viewable_by:
            for u in viewable_by:
                if u.get('delete'):
                    if u['id'] == 'all':
                        self.page.viewable_by.remove('all')
                    else:
                        user = M.User.by_username(str(u['id']))
                        if user:
                            self.page.viewable_by.remove(user.username)
        redirect('../' + h.really_unicode(self.page.title)
                 .encode('utf-8') + ('/' if not name_conflict else '/edit'))

    @without_trailing_slash
    @expose()
    @require_post()
    def attach(self, file_info=None, **kw):
        if not self.page:
            raise exc.HTTPNotFound
        require_access(self.page, 'edit')
        self.page.add_multiple_attachments(file_info)
        if is_ajax(request):
            return
        redirect(request.referer)

    @expose('json:')
    @require_post()
    @validate(W.subscribe_form)
    def subscribe(self, subscribe=None, unsubscribe=None, **kw):
        if not self.page:
            raise exc.HTTPNotFound
        if subscribe:
            self.page.subscribe(type='direct')
        elif unsubscribe:
            self.page.unsubscribe()
        return {
            'status': 'ok',
            'subscribed': M.Mailbox.subscribed(artifact=self.page),
            'subscribed_to_tool': M.Mailbox.subscribed(),
        }
예제 #28
0
파일: forum.py 프로젝트: vclisunlang/allura
class ForumController(DiscussionController):
    M = ModelConfig
    W = WidgetConfig

    def _check_security(self):
        require_access(self.discussion, 'read')

    def __init__(self, forum_id):
        self.ThreadController = ForumThreadController
        self.PostController = ForumPostController
        self.moderate = ForumModerationController(self)
        self.discussion = DM.Forum.query.get(app_config_id=c.app.config._id,
                                             shortname=forum_id)
        if not self.discussion:
            raise exc.HTTPNotFound()
        super(ForumController, self).__init__()

    @expose()
    def _lookup(self, id=None, *remainder):
        if id and self.discussion:
            return ForumController(self.discussion.shortname + '/' +
                                   id), remainder
        else:
            raise exc.HTTPNotFound()

    @expose('jinja:forgediscussion:templates/index.html')
    @validate(
        dict(page=validators.Int(if_empty=0, if_invalid=0),
             limit=validators.Int(if_empty=None, if_invalid=None)))
    def index(self, threads=None, limit=None, page=0, count=0, **kw):
        if self.discussion.deleted:
            redirect(self.discussion.url() + 'deleted')
        limit, page, start = g.handle_paging(limit, page)
        if not c.user.is_anonymous():
            c.subscribed = M.Mailbox.subscribed(artifact=self.discussion)
            c.tool_subscribed = M.Mailbox.subscribed()
        threads = DM.ForumThread.query.find(dict(discussion_id=self.discussion._id, num_replies={'$gt': 0})) \
                                      .sort([('flags', pymongo.DESCENDING), ('last_post_date', pymongo.DESCENDING)])
        c.discussion = self.W.discussion
        c.discussion_header = self.W.discussion_header
        c.whole_forum_subscription_form = self.W.subscribe_form
        return dict(discussion=self.discussion,
                    count=threads.count(),
                    threads=threads.skip(start).limit(int(limit)).all(),
                    limit=limit,
                    page=page)

    @expose('jinja:forgediscussion:templates/discussionforums/deleted.html')
    def deleted(self):
        return dict()

    @expose('json:')
    @require_post()
    @validate(W.subscribe_form)
    def subscribe_to_forum(self,
                           subscribe=None,
                           unsubscribe=None,
                           shortname=None,
                           **kw):
        if subscribe:
            self.discussion.subscribe(type='direct')

            # unsubscribe from all individual threads that are part of this forum, so you don't have overlapping subscriptions
            forumthread_index_prefix = (DM.ForumThread.__module__ + '.' +
                                        DM.ForumThread.__name__).replace(
                                            '.', '/') + '#'
            thread_mboxes = M.Mailbox.query.find(
                dict(
                    user_id=c.user._id,
                    project_id=c.project._id,
                    app_config_id=c.app.config._id,
                    artifact_index_id=re.compile(
                        '^' + re.escape(forumthread_index_prefix)),
                )).all()
            # get the ForumThread objects from the subscriptions
            thread_index_ids = [
                mbox.artifact_index_id for mbox in thread_mboxes
            ]
            threads_by_id = mapped_artifacts_from_index_ids(thread_index_ids,
                                                            DM.ForumThread,
                                                            objectid_id=False)
            for mbox in thread_mboxes:
                thread_id = mbox.artifact_index_id.split('#')[1]
                thread = threads_by_id[thread_id]
                # only delete if the ForumThread is part of this forum
                if thread.discussion_id == self.discussion._id:
                    mbox.delete()

        elif unsubscribe:
            self.discussion.unsubscribe()

        return {
            'status': 'ok',
            'subscribed': M.Mailbox.subscribed(artifact=self.discussion),
            'subscribed_to_tool': M.Mailbox.subscribed(),
        }
예제 #29
0
class MediaController(BaseController):
    allow_only = has_permission('edit')

    @expose_xhr('admin/media/index.html', 'admin/media/index-table.html')
    @paginate('media', items_per_page=15)
    @observable(events.Admin.MediaController.index)
    def index(self, page=1, search=None, filter=None, podcast=None,
              category=None, tag=None, **kwargs):
        """List media with pagination and filtering.

        :param page: Page number, defaults to 1.
        :type page: int
        :param search: Optional search term to filter by
        :type search: unicode or None
        :param podcast_filter: Optional podcast to filter by
        :type podcast_filter: int or None
        :rtype: dict
        :returns:
            media
                The list of :class:`~mediacore.model.media.Media` instances
                for this page.
            search
                The given search term, if any
            search_form
                The :class:`~mediacore.forms.admin.SearchForm` instance
            podcast
                The podcast object for rendering if filtering by podcast.

        """
        media = Media.query.options(orm.undefer('comment_count_published'))

        if search:
            media = media.admin_search(search)
        else:
            media = media.order_by_status()\
                         .order_by(Media.publish_on.desc(),
                                   Media.modified_on.desc())

        if not filter:
            pass
        elif filter == 'unreviewed':
            media = media.reviewed(False)
        elif filter == 'unencoded':
            media = media.reviewed().encoded(False)
        elif filter == 'drafts':
            media = media.drafts()
        elif filter == 'published':
            media = media.published()

        if category:
            category = fetch_row(Category, slug=category)
            media = media.filter(Media.categories.contains(category))
        if tag:
            tag = fetch_row(Tag, slug=tag)
            media = media.filter(Media.tags.contains(tag))
        if podcast:
            podcast = fetch_row(Podcast, slug=podcast)
            media = media.filter(Media.podcast == podcast)

        return dict(
            media = media,
            search = search,
            search_form = search_form,
            media_filter = filter,
            category = category,
            tag = tag,
            podcast = podcast,
        )

    def json_error(self, *args, **kwargs):
        validation_exception = tmpl_context._current_obj().validation_exception
        return dict(success=False, message=validation_exception.msg)

    @expose('admin/media/edit.html')
    @validate(validators={'podcast': validators.Int()})
    @autocommit
    @observable(events.Admin.MediaController.edit)
    def edit(self, id, **kwargs):
        """Display the media forms for editing or adding.

        This page serves as the error_handler for every kind of edit action,
        if anything goes wrong with them they'll be redirected here.

        :param id: Media ID
        :type id: ``int`` or ``"new"``
        :param \*\*kwargs: Extra args populate the form for ``"new"`` media
        :returns:
            media
                :class:`~mediacore.model.media.Media` instance
            media_form
                The :class:`~mediacore.forms.admin.media.MediaForm` instance
            media_action
                ``str`` form submit url
            media_values
                ``dict`` form values
            file_add_form
                The :class:`~mediacore.forms.admin.media.AddFileForm` instance
            file_add_action
                ``str`` form submit url
            file_edit_form
                The :class:`~mediacore.forms.admin.media.EditFileForm` instance
            file_edit_action
                ``str`` form submit url
            thumb_form
                The :class:`~mediacore.forms.admin.ThumbForm` instance
            thumb_action
                ``str`` form submit url
            update_status_form
                The :class:`~mediacore.forms.admin.media.UpdateStatusForm` instance
            update_status_action
                ``str`` form submit url

        """
        media = fetch_row(Media, id)

        if tmpl_context.action == 'save' or id == 'new':
            # Use the values from error_handler or GET for new podcast media
            media_values = kwargs
            user = request.perm.user
            media_values.setdefault('author_name', user.display_name)
            media_values.setdefault('author_email', user.email_address)
        else:
            # Pull the defaults from the media item
            media_values = dict(
                podcast = media.podcast_id,
                slug = media.slug,
                title = media.title,
                author_name = media.author.name,
                author_email = media.author.email,
                description = media.description,
                tags = ', '.join((tag.name for tag in media.tags)),
                categories = [category.id for category in media.categories],
                notes = media.notes,
            )

        # Re-verify the state of our Media object in case the data is nonsensical
        if id != 'new':
            media.update_status()

        return dict(
            media = media,
            media_form = media_form,
            media_action = url_for(action='save'),
            media_values = media_values,
            category_tree = Category.query.order_by(Category.name).populated_tree(),
            file_add_form = add_file_form,
            file_add_action = url_for(action='add_file'),
            file_edit_form = edit_file_form,
            file_edit_action = url_for(action='edit_file'),
            thumb_form = thumb_form,
            thumb_action = url_for(action='save_thumb'),
            update_status_form = update_status_form,
            update_status_action = url_for(action='update_status'),
        )

    @expose_xhr(request_method='POST')
    @validate_xhr(media_form, error_handler=edit)
    @autocommit
    @observable(events.Admin.MediaController.save)
    def save(self, id, slug, title, author_name, author_email,
             description, notes, podcast, tags, categories,
             delete=None, **kwargs):
        """Save changes or create a new :class:`~mediacore.model.media.Media` instance.

        Form handler the :meth:`edit` action and the
        :class:`~mediacore.forms.admin.media.MediaForm`.

        Redirects back to :meth:`edit` after successful editing
        and :meth:`index` after successful deletion.

        """
        media = fetch_row(Media, id)

        if delete:
            self._delete_media(media)
            DBSession.commit()
            redirect(action='index', id=None)

        if not slug:
            slug = title
        elif slug.startswith('_stub_'):
            slug = slug[len('_stub_'):]
        if slug != media.slug:
            media.slug = get_available_slug(Media, slug, media)
        media.title = title
        media.author = Author(author_name, author_email)
        media.description = description
        media.notes = notes
        media.podcast_id = podcast
        media.set_tags(tags)
        media.set_categories(categories)

        media.update_status()
        DBSession.add(media)
        DBSession.flush()

        if id == 'new' and not has_thumbs(media):
            create_default_thumbs_for(media)

        if request.is_xhr:
            status_form_xhtml = unicode(update_status_form.display(
                action=url_for(action='update_status', id=media.id),
                media=media))

            return dict(
                media_id = media.id,
                values = {'slug': slug},
                link = url_for(action='edit', id=media.id),
                status_form = status_form_xhtml,
            )
        else:
            redirect(action='edit', id=media.id)


    @expose('json', request_method='POST')
    @validate(add_file_form, error_handler=json_error)
    @autocommit
    @observable(events.Admin.MediaController.add_file)
    def add_file(self, id, file=None, url=None, **kwargs):
        """Save action for the :class:`~mediacore.forms.admin.media.AddFileForm`.

        Creates a new :class:`~mediacore.model.media.MediaFile` from the
        uploaded file or the local or remote URL.

        :param id: Media ID. If ``"new"`` a new Media stub is created.
        :type id: :class:`int` or ``"new"``
        :param file: The uploaded file
        :type file: :class:`cgi.FieldStorage` or ``None``
        :param url: A URL to a recognizable audio or video file
        :type url: :class:`unicode` or ``None``
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            media_id
                The :attr:`~mediacore.model.media.Media.id` which is
                important if new media has just been created.
            file_id
                The :attr:`~mediacore.model.media.MediaFile.id` for the newly
                created file.
            edit_form
                The rendered XHTML :class:`~mediacore.forms.admin.media.EditFileForm`
                for this file.
            status_form
                The rendered XHTML :class:`~mediacore.forms.admin.media.UpdateStatusForm`

        """
        if id == 'new':
            media = Media()
            user = request.perm.user
            media.author = Author(user.display_name, user.email_address)
            # Create a temp stub until we can set it to something meaningful
            timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            media.title = u'Temporary stub %s' % timestamp
            media.slug = get_available_slug(Media, '_stub_' + timestamp)
            media.reviewed = True
            DBSession.add(media)
            DBSession.flush()
        else:
            media = fetch_row(Media, id)

        media_file = add_new_media_file(media, file, url)
        if media.slug.startswith('_stub_'):
            media.title = media_file.display_name
            media.slug = get_available_slug(Media, '_stub_' + media.title)

        # The thumbs may have been created already by add_new_media_file
        if id == 'new' and not has_thumbs(media):
            create_default_thumbs_for(media)

        media.update_status()

        # Render some widgets so the XHTML can be injected into the page
        edit_form_xhtml = unicode(edit_file_form.display(
            action=url_for(action='edit_file', id=media.id),
            file=media_file))
        status_form_xhtml = unicode(update_status_form.display(
            action=url_for(action='update_status', id=media.id),
            media=media))

        data = dict(
            success = True,
            media_id = media.id,
            file_id = media_file.id,
            file_type = media_file.type,
            edit_form = edit_form_xhtml,
            status_form = status_form_xhtml,
            title = media.title,
            slug = media.slug,
            description = media.description,
            link = url_for(action='edit', id=media.id),
            duration = helpers.duration_from_seconds(media.duration),
        )

        return data


    @expose('json', request_method='POST')
    @autocommit
    @observable(events.Admin.MediaController.edit_file)
    def edit_file(self, id, file_id, file_type=None, duration=None, delete=None, bitrate=None, width_height=None, **kwargs):
        """Save action for the :class:`~mediacore.forms.admin.media.EditFileForm`.

        Changes or deletes a :class:`~mediacore.model.media.MediaFile`.

        XXX: We do NOT use the @validate decorator due to complications with
             partial validation. The JS sends only the value it wishes to
             change, so we only want to validate that one value.
             FancyValidator.if_missing seems to eat empty values and assign
             them None, but there's an important difference to us between
             None (no value from the user) and an empty value (the user
             is clearing the value of a field).

        :param id: Media ID
        :type id: :class:`int`
        :rtype: JSON dict
        :returns:
            success
                bool
            message
                Error message, if unsuccessful
            status_form
                Rendered XHTML for the status form, updated to reflect the
                changes made.

        """
        media = fetch_row(Media, id)
        data = dict(success=False)
        file_id = int(file_id) # Just in case validation failed somewhere.

        for file in media.files:
            if file.id == file_id:
                break
        else:
            file = None

        fields = edit_file_form.c
        try:
            if file is None:
                data['message'] = _('File "%s" does not exist.') % file_id
            elif file_type:
                file.type = fields.file_type.validate(file_type)
                data['success'] = True
            elif duration is not None:
                media.duration = fields.duration.validate(duration)
                data['success'] = True
                data['duration'] = helpers.duration_from_seconds(media.duration)
            elif width_height is not None:
                width_height = fields.width_height.validate(width_height)
                file.width, file.height = width_height or (0, 0)
                data['success'] = True
            elif bitrate is not None:
                file.bitrate = fields.bitrate.validate(bitrate)
                data['success'] = True
            elif delete:
                file.storage.delete(file.unique_id)
                DBSession.delete(file)
                DBSession.flush()
                # media.files must be updated to reflect the file deletion above
                DBSession.refresh(media)
                data['success'] = True
            else:
                data['message'] = _('No action to perform.')
        except Invalid, e:
            data['success'] = False
            data['message'] = unicode(e)

        if data['success']:
            data['file_type'] = file.type
            media.update_status()
            DBSession.flush()

            # Return the rendered widget for injection
            status_form_xhtml = unicode(update_status_form.display(
                action=url_for(action='update_status'), media=media))
            data['status_form'] = status_form_xhtml
        return data
예제 #30
0
class SiteAdminController(object):
    def __init__(self):
        self.task_manager = TaskManagerController()
        c.site_admin_sidebar_menu = self.sidebar_menu()
        self.user = AdminUserDetailsController()
        self.delete_projects = DeleteProjectsController()

    def _check_security(self):
        with h.push_context(config.get('site_admin_project', 'allura'),
                            neighborhood=config.get('site_admin_project_nbhd',
                                                    'Projects')):
            require_access(c.project, 'admin')

    @expose()
    def _lookup(self, name, *remainder):
        for ep_name in sorted(g.entry_points['site_admin'].keys()):
            admin_extension = g.entry_points['site_admin'][ep_name]
            controller = admin_extension().controllers.get(name)
            if controller:
                return controller(), remainder
        raise HTTPNotFound, name

    def sidebar_menu(self):
        base_url = '/nf/admin/'
        links = [
            SitemapEntry('Home', base_url, ui_icon=g.icons['admin']),
            SitemapEntry('Add Subscribers',
                         base_url + 'add_subscribers',
                         ui_icon=g.icons['admin']),
            SitemapEntry('New Projects',
                         base_url + 'new_projects',
                         ui_icon=g.icons['admin']),
            SitemapEntry('Reclone Repo',
                         base_url + 'reclone_repo',
                         ui_icon=g.icons['admin']),
            SitemapEntry('Task Manager',
                         base_url + 'task_manager?state=busy',
                         ui_icon=g.icons['stats']),
            SitemapEntry('Search Projects',
                         base_url + 'search_projects',
                         ui_icon=g.icons['search']),
            SitemapEntry('Delete Projects',
                         base_url + 'delete_projects',
                         ui_icon=g.icons['delete']),
            SitemapEntry('Search Users',
                         base_url + 'search_users',
                         ui_icon=g.icons['search']),
        ]
        for ep_name in sorted(g.entry_points['site_admin']):
            g.entry_points['site_admin'][ep_name]().update_sidebar_menu(links)
        return links

    @expose('jinja:allura:templates/site_admin_index.html')
    @with_trailing_slash
    def index(self):
        return {}

    def subscribe_artifact(self, url, user):
        artifact_url = urlparse(url).path[1:-1].split("/")
        neighborhood = M.Neighborhood.query.find({
            "url_prefix":
            "/" + artifact_url[0] + "/"
        }).first()

        if artifact_url[0] == "u":
            project = M.Project.query.find({
                "shortname":
                artifact_url[0] + "/" + artifact_url[1],
                "neighborhood_id":
                neighborhood._id
            }).first()
        else:
            project = M.Project.query.find({
                "shortname": artifact_url[1],
                "neighborhood_id": neighborhood._id
            }).first()

        appconf = M.AppConfig.query.find({
            "options.mount_point": artifact_url[2],
            "project_id": project._id
        }).first()

        if appconf.url() == urlparse(url).path:
            M.Mailbox.subscribe(user_id=user._id,
                                app_config_id=appconf._id,
                                project_id=project._id)
            return True

        tool_packages = h.get_tool_packages(appconf.tool_name)
        classes = set()
        for depth, cls in dfs(M.Artifact, build_model_inheritance_graph()):
            for pkg in tool_packages:
                if cls.__module__.startswith(pkg + '.'):
                    classes.add(cls)
        for cls in classes:
            for artifact in cls.query.find({"app_config_id": appconf._id}):
                if artifact.url() == urlparse(url).path:
                    M.Mailbox.subscribe(user_id=user._id,
                                        app_config_id=appconf._id,
                                        project_id=project._id,
                                        artifact=artifact)
                    return True
        return False

    @expose('jinja:allura:templates/site_admin_add_subscribers.html')
    @without_trailing_slash
    def add_subscribers(self, **data):
        if request.method == 'POST':
            url = data['artifact_url']
            user = M.User.by_username(data['for_user'])
            if not user or user == M.User.anonymous():
                flash('Invalid login', 'error')
                return data

            try:
                ok = self.subscribe_artifact(url, user)
            except:
                log.warn("Can't subscribe to artifact", exc_info=True)
                ok = False

            if ok:
                flash('User successfully subscribed to the artifact')
                return {}
            else:
                flash('Artifact not found', 'error')

        return data

    @expose('jinja:allura:templates/site_admin_new_projects.html')
    @without_trailing_slash
    def new_projects(self, **kwargs):
        start_dt = kwargs.pop('start-dt', '')
        end_dt = kwargs.pop('end-dt', '')
        try:
            start_dt = datetime.strptime(start_dt, '%Y/%m/%d %H:%M:%S')
        except ValueError:
            start_dt = datetime.utcnow() + timedelta(days=1)
        try:
            end_dt = datetime.strptime(end_dt, '%Y/%m/%d %H:%M:%S')
        except ValueError:
            end_dt = start_dt - timedelta(days=3) if not end_dt else end_dt
        start = bson.ObjectId.from_datetime(start_dt)
        end = bson.ObjectId.from_datetime(end_dt)
        nb = M.Neighborhood.query.get(name='Users')
        projects = (M.Project.query.find({
            'neighborhood_id': {
                '$ne': nb._id
            },
            'deleted': False,
            '_id': {
                '$lt': start,
                '$gt': end
            },
        }).sort('_id', -1)).all()
        # pre-populate roles cache, so we won't query mongo for roles for every project
        # when getting admins with p.admins() in a template
        Credentials.get().load_project_roles(*[p._id for p in projects])
        step = start_dt - end_dt
        params = request.params.copy()
        params['start-dt'] = (start_dt + step).strftime('%Y/%m/%d %H:%M:%S')
        params['end-dt'] = (end_dt + step).strftime('%Y/%m/%d %H:%M:%S')
        newer_url = tg.url(params=params).lstrip('/')
        params['start-dt'] = (start_dt - step).strftime('%Y/%m/%d %H:%M:%S')
        params['end-dt'] = (end_dt - step).strftime('%Y/%m/%d %H:%M:%S')
        older_url = tg.url(params=params).lstrip('/')
        return {
            'projects': projects,
            'newer_url': newer_url,
            'older_url': older_url,
            'window_start': start_dt,
            'window_end': end_dt,
        }

    @expose('jinja:allura:templates/site_admin_reclone_repo.html')
    @without_trailing_slash
    @validate(
        dict(prefix=validators.NotEmpty(),
             shortname=validators.NotEmpty(),
             mount_point=validators.NotEmpty()))
    def reclone_repo(self,
                     prefix=None,
                     shortname=None,
                     mount_point=None,
                     **data):
        if request.method == 'POST':
            if c.form_errors:
                error_msg = 'Error: '
                for msg in list(c.form_errors):
                    names = {
                        'prefix': 'Neighborhood prefix',
                        'shortname': 'Project shortname',
                        'mount_point': 'Repository mount point'
                    }
                    error_msg += '%s: %s ' % (names[msg], c.form_errors[msg])
                    flash(error_msg, 'error')
                return dict(prefix=prefix,
                            shortname=shortname,
                            mount_point=mount_point)
            nbhd = M.Neighborhood.query.get(url_prefix='/%s/' % prefix)
            if not nbhd:
                flash('Neighborhood with prefix %s not found' % prefix,
                      'error')
                return dict(prefix=prefix,
                            shortname=shortname,
                            mount_point=mount_point)
            c.project = M.Project.query.get(shortname=shortname,
                                            neighborhood_id=nbhd._id)
            if not c.project:
                flash(
                    'Project with shortname %s not found in neighborhood %s' %
                    (shortname, nbhd.name), 'error')
                return dict(prefix=prefix,
                            shortname=shortname,
                            mount_point=mount_point)
            c.app = c.project.app_instance(mount_point)
            if not c.app:
                flash(
                    'Mount point %s not found on project %s' %
                    (mount_point, c.project.shortname), 'error')
                return dict(prefix=prefix,
                            shortname=shortname,
                            mount_point=mount_point)
            source_url = c.app.config.options.get('init_from_url')
            source_path = c.app.config.options.get('init_from_path')
            if not (source_url or source_path):
                flash('%s does not appear to be a cloned repo' % c.app,
                      'error')
                return dict(prefix=prefix,
                            shortname=shortname,
                            mount_point=mount_point)
            allura.tasks.repo_tasks.reclone_repo.post(prefix=prefix,
                                                      shortname=shortname,
                                                      mount_point=mount_point)
            flash('Repository is being recloned')
        else:
            prefix = 'p'
            shortname = ''
            mount_point = ''
        return dict(prefix=prefix,
                    shortname=shortname,
                    mount_point=mount_point)

    def _search(self,
                model,
                fields,
                add_fields,
                q=None,
                f=None,
                page=0,
                limit=None,
                **kw):
        all_fields = fields + [(fld, fld) for fld in add_fields]
        c.search_form = W.admin_search_form(all_fields)
        c.page_list = W.page_list
        c.page_size = W.page_size
        count = 0
        objects = []
        limit, page, start = g.handle_paging(limit, page, default=25)
        if q:
            match = search.site_admin_search(model,
                                             q,
                                             f,
                                             rows=limit,
                                             start=start)
            if match:
                count = match.hits
                objects = match.docs
                ids = [obj['id'].split('#')[1] for obj in objects]
                ids = [bson.ObjectId(_id) for _id in ids if _id != 'None']
                mongo_objects = {}
                for obj in model.query.find({'_id': {'$in': ids}}):
                    mongo_objects[str(obj._id)] = obj

                for i in range(len(objects)):
                    obj = objects[i]
                    _id = obj['id'].split('#')[1]
                    obj['object'] = mongo_objects.get(_id)
                # Some objects can be deleted, but still have index in solr, should skip those
                objects = [o for o in objects if o.get('object')]

        def convert_fields(obj):
            # throw the type away (e.g. '_s' from 'url_s')
            result = {}
            for k, val in obj.iteritems():
                name = k.rsplit('_', 1)
                if len(name) == 2:
                    name = name[0]
                else:
                    name = k
                result[name] = val
            return result

        return {
            'q': q,
            'f': f,
            'objects': map(convert_fields, objects),
            'count': count,
            'page': page,
            'limit': limit,
            'fields': fields,
            'additional_fields': add_fields,
            'type_s': model.type_s,
        }

    @without_trailing_slash
    @expose('jinja:allura:templates/site_admin_search.html')
    @validate(validators=dict(q=validators.UnicodeString(if_empty=None),
                              limit=validators.Int(if_invalid=None),
                              page=validators.Int(if_empty=0, if_invalid=0)))
    def search_projects(self, q=None, f=None, page=0, limit=None, **kw):
        fields = [('shortname', 'shortname'), ('name', 'full name')]
        add_fields = aslist(
            tg.config.get('search.project.additional_search_fields'), ',')
        r = self._search(M.Project, fields, add_fields, q, f, page, limit,
                         **kw)
        r['search_results_template'] = 'allura:templates/site_admin_search_projects_results.html'
        r['additional_display_fields'] = \
            aslist(tg.config.get('search.project.additional_display_fields'), ',')
        r['provider'] = ProjectRegistrationProvider.get()
        return r

    @without_trailing_slash
    @expose('jinja:allura:templates/site_admin_search.html')
    @validate(validators=dict(q=validators.UnicodeString(if_empty=None),
                              limit=validators.Int(if_invalid=None),
                              page=validators.Int(if_empty=0, if_invalid=0)))
    def search_users(self, q=None, f=None, page=0, limit=None, **kw):
        fields = [('username', 'username'), ('display_name', 'display name')]
        add_fields = aslist(
            tg.config.get('search.user.additional_search_fields'), ',')
        r = self._search(M.User, fields, add_fields, q, f, page, limit, **kw)
        r['objects'] = [
            dict(u, status=h.get_user_status(u['object']))
            for u in r['objects']
        ]
        r['search_results_template'] = 'allura:templates/site_admin_search_users_results.html'
        r['additional_display_fields'] = \
            aslist(tg.config.get('search.user.additional_display_fields'), ',')
        r['provider'] = AuthenticationProvider.get(request)
        return r