| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| from tg import expose, validate, redirect, flash, request, config |
| from tg.decorators import without_trailing_slash |
| |
| from allura.app import Application, SitemapEntry, DefaultAdminController |
| from allura import model as M |
| from allura.lib.security import require_access, has_access |
| from allura.lib import helpers as h |
| from allura.lib.search import search_app |
| from allura.controllers import BaseController |
| from allura.lib.widgets import form_fields as ffw |
| from allura.lib.widgets.search import SearchResults, SearchHelp |
| |
| from webob import exc |
| from pylons import tmpl_context as c, app_globals as g |
| from datetime import datetime |
| from formencode import validators |
| from formencode.compound import All |
| |
| from forgeshorturl.model.shorturl import ShortUrl |
| import forgeshorturl.widgets.short_url as suw |
| |
| import logging |
| |
| |
| log = logging.getLogger(__name__) |
| |
| |
| class W: |
| search_results = SearchResults() |
| search_help = SearchHelp(comments=False, history=False) |
| page_list = ffw.PageList() |
| page_size = ffw.PageSize() |
| short_url_lightbox = suw.ShortUrlFormWidget() |
| |
| |
| class ForgeShortUrlApp(Application): |
| permissions = ['read', 'create', 'update', 'view_private'] |
| searchable = True |
| tool_label = 'URL shortener' |
| default_mount_label = 'URL shortener' |
| default_mount_point = 'url' |
| sitemap = [] |
| ordinal = 14 |
| installable = False |
| icons = { |
| 24: 'images/ext_24.png', |
| 32: 'images/ext_32.png', |
| 48: 'images/ext_48.png' |
| } |
| |
| def __init__(self, project, config): |
| Application.__init__(self, project, config) |
| self.root = RootController() |
| self.admin = ShortURLAdminController(self) |
| |
| def is_visible_to(self, user): |
| '''Whether the user can view the app.''' |
| return has_access(c.project, 'create')(user=user) |
| |
| @property |
| @h.exceptionless([], log) |
| def sitemap(self): |
| menu_id = self.config.options.mount_label |
| return [SitemapEntry(menu_id, '.')] |
| |
| def sidebar_menu(self): |
| links = [] |
| if has_access(c.app, "create"): |
| url = '%sadmin/%s/add/' % \ |
| (c.project.url(), self.config.options.mount_point) |
| links = [SitemapEntry('Add Short URL', |
| url, |
| ui_icon=g.icons['plus'], |
| className="add-short-url"), ] |
| return links |
| |
| def admin_menu(self): |
| links = [] |
| if has_access(c.app, "create"): |
| links = [SitemapEntry('Add Short URL', |
| c.project.url() + |
| 'admin/' + |
| self.config.options.mount_point + |
| '/add/', |
| className='admin_modal'), ] |
| links += [SitemapEntry('Browse', |
| c.project.url() + |
| self.config.options.mount_point), ] |
| |
| links += super(ForgeShortUrlApp, self).admin_menu() |
| return links |
| |
| def install(self, project): |
| 'Set up any default permissions and roles here' |
| self.config.options['project_name'] = project.name |
| super(ForgeShortUrlApp, self).install(project) |
| # Setup permissions |
| role_anon = M.ProjectRole.anonymous()._id |
| role_admin = M.ProjectRole.by_name('Admin')._id |
| self.config.acl = [ |
| M.ACE.allow(role_anon, 'read'), |
| M.ACE.allow(role_admin, 'create'), |
| M.ACE.allow(role_admin, 'update'), |
| M.ACE.allow(role_admin, 'view_private'), |
| M.ACE.allow(role_admin, 'configure'), ] |
| |
| def uninstall(self, project): |
| "Remove all the tool's artifacts from the database" |
| ShortUrl.query.remove(dict(app_config_id=c.app.config._id)) |
| super(ForgeShortUrlApp, self).uninstall(project) |
| |
| |
| 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), |
| limit=validators.Int(if_empty=100))) |
| def index(self, page=0, limit=100, **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() |
| |
| |
| class ShortURLAdminController(DefaultAdminController): |
| |
| shorturl_validators = All( |
| validators.NotEmpty(), |
| validators.Regex( |
| r'^[-_a-zA-Z0-9]+$', |
| messages={'invalid': 'must include only letters, numbers, dashes and underscores.'} |
| ) |
| ) |
| |
| |
| def __init__(self, app): |
| self.app = app |
| |
| @expose() |
| def index(self, **kw): |
| redirect(c.project.url() + 'admin/tools') |
| |
| @without_trailing_slash |
| @expose('json:') |
| def remove(self, shorturl): |
| require_access(self.app, 'update') |
| ShortUrl.query.remove({ |
| 'app_config_id': self.app.config._id, |
| 'short_name': shorturl}) |
| return dict(status='ok') |
| |
| @expose('jinja:forgeshorturl:templates/form.html') |
| @validate(dict(full_url=All(validators.URL(add_http=True), |
| validators.NotEmpty()), |
| short_url=shorturl_validators)) |
| def add(self, short_url='', full_url='', description='', private='off', |
| update=False, **kw): |
| if update: |
| require_access(self.app, 'update') |
| else: |
| require_access(self.app, 'create') |
| if request.method == 'POST': |
| if c.form_errors: |
| error_msg = 'Error: ' |
| for msg in list(c.form_errors): |
| names = {'short_url': 'Short url', 'full_url': 'Full URL'} |
| error_msg += '%s: %s ' % (names[msg], c.form_errors[msg]) |
| flash(error_msg, 'error') |
| redirect(request.referer) |
| |
| shorturl = ShortUrl.query.find({ |
| 'app_config_id': self.app.config._id, |
| 'short_name': short_url}).first() |
| |
| if shorturl is not None: |
| if not update: |
| flash('Short url %s already exists' % short_url, 'error') |
| redirect(request.referer) |
| else: |
| msg = ('update short url %s from %s to %s' |
| % (short_url, shorturl.full_url, full_url)) |
| flash("Short url updated") |
| |
| else: |
| shorturl = ShortUrl() |
| shorturl.created = datetime.utcnow() |
| shorturl.app_config_id = self.app.config._id |
| msg = 'create short url %s for %s' % (short_url, full_url) |
| flash("Short url created") |
| |
| shorturl.short_name = short_url |
| shorturl.full_url = full_url |
| shorturl.description = description |
| shorturl.create_user = c.user._id |
| shorturl.private = private == 'on' |
| shorturl.last_updated = datetime.utcnow() |
| |
| M.AuditLog.log(msg) |
| redirect(request.referer) |
| return dict( |
| app=self.app, |
| url_len=len(ShortUrl.build_short_url(c.app, short_name=''))) |