blob: d26c69e5002fdc074f1ad303f3033082154e8103 [file] [log] [blame]
# 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):
if request.method == 'GET':
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)
flash("We're sorry but we weren't able "
"to process this request.", "error")
raise exc.HTTPNotFound()
class ShortURLAdminController(DefaultAdminController):
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=validators.NotEmpty()))
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='')))