# -*- encoding: utf-8 -*-
import exceptions
import re
import urllib2
from urllib import urlencode
import logging
import mimetypes
import paste.fileapp
from lxml import html
from urllib import quote
from urllib2 import HTTPError, URLError
from urlparse import urljoin
from paste.deploy.converters import asbool
from pylons import config
from ckan import logic
from ckan.common import OrderedDict, request
from ckan.lib.base import model, abort, response, g, h, BaseController
from ckanext.dgu.model.package_likes import PackageLikes
import ckanext.dgu.lib.captcha as captcha
from ckanext.dgu.plugins_toolkit import render, c, request, _, ObjectNotFound, NotAuthorized, ValidationError, get_action, check_access
from ckan.lib.field_types import DateType, DateConvertError
import ckan.lib.navl.dictization_functions as dict_fns
from ckan.lib.navl.dictization_functions import Invalid, DataError, unflatten
from ckanext.dgu.schema import GeoCoverageType
from ckan.lib.navl.dictization_functions import missing
import ckan.controllers.package
from ckan.controllers.package import _encode_params
from ckanext.dgu.lib.helpers import get_executive_editors, get_from_flat_dict, get_user_email, get_user_name, free_tags, send_post, refresh_drupal_themes_async
from ckanext.dgu.lib.mailer_opsi import mail_recipient
from ckanext.dgu.lib.reports import refresh_opsi_reports_async
from ckan.lib.package_saver import PackageSaver
from ckan.lib.search import SearchIndexError
import ckan.lib.uploader as uploader
import ckan.plugins as p
from ckanext.dgu.forms.validators import tag_string_to_list as opsi_tag_string_to_list
log = logging.getLogger(__name__)
PISRS_URL = 'http://www.pisrs.si/Pis.web/pregledPredpisa?sop='
redirect = ckan.lib.base.redirect
NotFound = logic.NotFound
NotAuthorized = logic.NotAuthorized
ValidationError = logic.ValidationError
tuplize_dict = logic.tuplize_dict
clean_dict = logic.clean_dict
parse_params = logic.parse_params
flatten_to_string_key = logic.flatten_to_string_key
def _encode_params(params):
return [(k, v.encode('utf-8') if isinstance(v, basestring) else str(v))
for k, v in params]
def url_with_params(url, params):
params = _encode_params(params)
return url + u'?' + urlencode(params)
def recent_url(params, package_type=None):
if not package_type or package_type == 'dataset':
url = h.url_for(controller='ckanext.dgu.controllers.package:PackageController', action='recent')
else:
url = h.url_for('{0}_recent'.format(package_type))
return url_with_params(url, params)
def search_url(params, package_type=None):
if not package_type or package_type == 'dataset':
url = h.url_for(controller='package', action='search')
else:
url = h.url_for('{0}_search'.format(package_type))
return url_with_params(url, params)
class PackageController(ckan.controllers.package.PackageController):
def __before__(self, action, **params):
super(PackageController, self).__before__(action, **params)
c.all_resources = 'all' in request.params.keys()
def history(self, id):
if not c.user:
abort(401, 'Log-in to see this page')
return super(PackageController, self).history(id)
def increase_package_likes(self, id):
try:
PackageLikes.increase_package_likes(package_id=id)
except p.toolkit.ValidationError:
log.debug('ValidationError while increasing package likes.')
def recent(self, podrocje=None):
from ckan.lib.search import SearchError, SearchQueryError
package_type = self._guess_package_type()
try:
context = {'model': model, 'user': c.user or c.author,
'auth_user_obj': c.userobj}
check_access('site_read', context)
except NotAuthorized:
abort(401, _('Not authorized to see this page'))
# unicode format (decoded from utf8)
q = c.q = request.params.get('q', u'')
c.query_error = False
try:
page = int(request.params.get('page', 1))
except ValueError, e:
abort(400, ('"page" parameter must be an integer'))
limit = g.datasets_per_page
# most search operations should reset the page counter:
params_nopage = [(k, v) for k, v in request.params.items()
if k != 'page']
def drill_down_url(alternative_url=None, **by):
return h.add_url_param(alternative_url=alternative_url,
controller='package', action='search',
new_params=by)
c.drill_down_url = drill_down_url
def remove_field(key, value=None, replace=None, alternative_url=None):
return h.remove_url_param(key, value=value, replace=replace,
alternative_url=alternative_url,
controller='package', action='search')
c.remove_field = remove_field
sort_by = 'metadata_modified desc'
c.sort_by_fields = [('metadata_modified', 'desc')]
def pager_url(q=None, page=None):
params = list(params_nopage)
params.append(('page', page))
return recent_url(params, package_type)
c.search_url_params = urlencode(_encode_params(params_nopage))
recent_vars = {}
try:
c.fields = []
# c.fields_grouped will contain a dict of params containing
# a list of values eg {'tags':['tag1', 'tag2']}
c.fields_grouped = {}
search_extras = {}
fq = ''
for (param, value) in request.params.items():
if param == 'all_podrocje':
recent_vars['podrocje'] = value
if param not in ['q', 'page', 'sort'] \
and len(value) and not param.startswith('_'):
if not param.startswith('ext_'):
# DGU has no fields with non-ascii, but not sure how to cope
# with SOLR params with non-ascii - not sure what encoding would
# be, so ignore.
param = param.decode('ascii', 'ignore')
c.fields.append((param, value))
fq += ' %s:"%s"' % (param, value)
if param not in c.fields_grouped:
c.fields_grouped[param] = [value]
else:
c.fields_grouped[param].append(value)
else:
search_extras[param] = value
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'for_view': True,
'auth_user_obj': c.userobj}
if package_type and package_type != 'dataset':
# Only show datasets of this particular type
fq += ' +dataset_type:{type}'.format(type=package_type)
else:
# Unless changed via config options, don't show non standard
# dataset types on the default search page
if not asbool(config.get('ckan.search.show_all_types', 'False')):
fq += ' +dataset_type:dataset'
facets = OrderedDict()
default_facet_titles = {
'organization': _('Organizations'),
'groups': _('Groups'),
'tags': _('Tags'),
'res_format': _('Formats'),
'license_id': _('Licenses'),
}
for facet in g.facets:
if facet in default_facet_titles:
facets[facet] = default_facet_titles[facet]
else:
facets[facet] = facet
# Facet titles
for plugin in p.PluginImplementations(p.IFacets):
facets = plugin.dataset_facets(facets, package_type)
c.facet_titles = facets
data_dict = {
'q': q,
'fq': fq.strip(),
'facet.field': facets.keys(),
'rows': limit,
'start': (page - 1) * limit,
'sort': sort_by,
'extras': search_extras
}
query = get_action('package_search')(context, data_dict)
c.sort_by_selected = query['sort']
c.page = h.Page(
collection=query['results'],
page=page,
url=pager_url,
item_count=query['count'],
items_per_page=limit
)
c.facets = query['facets']
c.search_facets = query['search_facets']
c.page.items = query['results']
except SearchQueryError, se:
# User's search parameters are invalid, in such a way that is not
# achievable with the web interface, so return a proper error to
# discourage spiders which are the main cause of this.
log.info('Dataset search query rejected: %r', se.args)
abort(400, _('Invalid search query: {error_message}')
.format(error_message=str(se)))
except SearchError, se:
# May be bad input from the user, but may also be more serious like
# bad code causing a SOLR syntax error, or a problem connecting to
# SOLR
log.error('Dataset search error %s (%s): %r', request.url, request.environ.get('HTTP_USER_AGENT'), se.args)
c.query_error = True
c.facets = {}
c.search_facets = {}
c.page = h.Page(collection=[])
# DGU fix for negative page number - fixed in a different way on master
except ValidationError, e:
# e.g. ?page=0
abort(400, 'Parameter error: %s' % e)
c.search_facets_limits = {}
for facet in c.search_facets.keys():
try:
limit = int(request.params.get('_%s_limit' % facet,
g.facets_default_number))
except ValueError:
abort(400, _('Parameter "{parameter_name}" is not '
'an integer').format(
parameter_name='_%s_limit' % facet
))
c.search_facets_limits[facet] = limit
self._setup_template_variables(context, {},
package_type=package_type)
return render("package/recent.html", extra_vars=recent_vars)
def get_sop_name(self, id):
try:
page = html.fromstring(urllib2.urlopen(PISRS_URL+id).read())
for title in page.xpath("//h1/text()"):
title = title.strip()
if title.find('\n') > 0:
title = title[0:title.find('\n')].strip()
return '{"name": "'+ title + '" , "url": "' + (PISRS_URL + id)+'"}'
except exceptions.UnicodeError:
return '{"name": "Vnos ni v pravilnem formatu." , "url":""}'
except URLError:
return '{"name": "Napaka pri poizvedbi SOP" , "url":' + (PISRS_URL + id) + '"}'
return '{"name": "Predpis s tem SOP ni bil najden" , "url": "' + (PISRS_URL + id) + '"}'
def all_packages(self):
ctx = {'model': model, 'session': model.Session}
package_list = get_action("package_list")(ctx, {})
def linkify_packages(pkgs):
x = 0
for pkg in pkgs:
yield '{p}
'.format(p=pkg)
c.datasets = linkify_packages(package_list)
return render("package/all_datasets.html")
def _send_message_to_owner(self, pkg_dict, data_dict):
from genshi.template.text import NewTextTemplate
errors = {}
error_summary = {}
owner_id = pkg_dict['creator_user_id']
owner = model.User.get(owner_id)
owner_email = get_user_email(owner)
owner_name = get_user_name(owner)
recipient = 'objavitelj'
if 'notification_recipient' in pkg_dict:
recipient = pkg_dict['notification_recipient']
elif not owner_id:
errors['message'] = [u'Ta zbirka nima avtorja']
error_summary['message'] = u'Ta zbirka nima avtorja, zato ni bilo mogoče poslati sporočila'
if 'message' not in errors:
recipients = []
if recipient == 'oba':
recipients = [
{
'name': owner_name,
'email': owner_email
},
{
'name': pkg_dict['foi-name'],
'email': pkg_dict['foi-email']
}
]
elif recipient == 'skrbnik':
recipients = [
{
'name': pkg_dict['foi-name'],
'email': pkg_dict['foi-email']
}
]
elif recipient == 'objavitelj':
recipients = [
{
'name': owner_name,
'email': owner_email
}
]
for recip in recipients:
if not recip['email']:
errors['message'] = [u'Prejemnik obvestil zbirke nima elektronskega naslova']
error_summary['message'] = u'Prejemnik obvestil zbirke nima elektronskega naslova, zato ni bilo mogoče poslati sporočila'
url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='edit', id=pkg_dict['id']))
if 'message' not in errors:
extra_vars = {
'user': get_user_name(),
'dataset': pkg_dict['title'],
'editor_message': data_dict["message"],
'edit_url': url
}
email_msg = render("email/draft_comment.txt", extra_vars=extra_vars,
loader_class=NewTextTemplate)
for recip in recipients:
mail_recipient(recip['name'],
recip['email'],
subject=u'Obvestilo o vašem osnutku '+pkg_dict['title'],
body=email_msg)
return errors, error_summary
def _report_opsi_issue_to_owner(self, pkg_dict, data_dict):
from genshi.template.text import NewTextTemplate
errors = {}
error_summary = {}
creator_id = pkg_dict['creator_user_id']
creator = model.User.get(creator_id)
creator_email = get_user_email(creator) or ''
creator_name = get_user_name(creator)
recipients = []
# if set, the maintainer_email overrides all other settings
maintainer_email = pkg_dict.get('maintainer_email')
if maintainer_email:
recipients = [{'name': creator_name or maintainer_email, 'email': maintainer_email}]
else:
recipient = 'objavitelj'
if 'notification_recipient' in pkg_dict:
recipient = pkg_dict['notification_recipient']
if recipient == 'objavitelj' or recipient == 'oba':
if creator_email:
recipients.append( { 'name': creator_name or creator_email, 'email': creator_email } )
elif recipient != 'oba' and pkg_dict.get('foi-email'): #fallback
recipients.append( { 'name': pkg_dict.get('foi-name', pkg_dict['foi-email']), 'email': pkg_dict['foi-email'] } )
if recipient == 'skrbnik' or recipient == 'oba':
if pkg_dict.get('foi-email'):
recipients.append( { 'name': pkg_dict.get('foi-name', pkg_dict['foi-email']), 'email': pkg_dict['foi-email'] } )
elif recipient != 'oba' and creator_email: #fallback
recipients.append( { 'name': creator_name or creator_email, 'email': creator_email } )
url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='edit', id=pkg_dict['id']))
view_url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='read', id=pkg_dict['id']))
recipient_text = ''
if recipients:
for recip in recipients:
recipient_text += recip['name'] + ' ('+recip['email']+'), '
recipient_text = recipient_text[0:-2]
extra_vars = {
'user': data_dict.get('name', ''),
'user_email': data_dict.get('email', ''),
'dataset': pkg_dict['title'],
'editor_message': data_dict["message"],
'edit_url': url,
'view_url': view_url,
'owner': recipient_text
}
sent_emails = []
if recipients:
email_msg = render("email/opsi_issue.txt", extra_vars=extra_vars,
loader_class=NewTextTemplate)
for recip in recipients:
if recip['email'] and recip['email'] not in sent_emails:
mail_recipient(recip['name'],
recip['email'],
subject=u'Predlog popravka v vaši zbirki '+pkg_dict['title'],
body=email_msg)
sent_emails.append(recip['email'])
email_msg_exec = render("email/opsi_issue_exec.txt", extra_vars=extra_vars, loader_class=NewTextTemplate)
else:
email_msg_exec = render("email/opsi_issue_exec_error.txt", extra_vars=extra_vars, loader_class=NewTextTemplate)
log.warning("Package " + pkg_dict['id'] + " recipients have no email, notification will be sent to executive editors only.")
exec_editors = get_executive_editors()
for (name,recipient,id) in exec_editors:
if recipient and recipient not in sent_emails:
mail_recipient(name,
recipient,
subject=u'Predlog popravka v zbirki '+pkg_dict['title'],
body=email_msg_exec)
sent_emails.append(recipient)
log.debug('User sent OPSI issue for package %s, notification was sent to %s', pkg_dict['id'], sent_emails)
return errors, error_summary
def report_opsi_issue(self, id, data=None, errors=None, error_summary=None):
"""
Display form for sending message to author
:return: html
"""
package_type = self._get_package_type(id)
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj,
'report': 'report' in request.params}
try:
pkg_dict = get_action('package_show')(context, {'id':id}) # has side-effect of populating context.get('package')
except NotAuthorized:
abort(401, 'Nimate pravic za ogled zbirke')
data_dict = {}
errors = {}
error_summary = {}
c.pkg = context.get('package')
if 'report' in request.params:
# Send message to author
try:
data_dict = logic.clean_dict(unflatten(logic.tuplize_dict(logic.parse_params(request.params))))
except logic.NotAuthorized:
base.abort(401, _('Not authorized to see this page'))
try:
captcha.check_recaptcha(request)
except captcha.CaptchaError:
errors['captcha'] = [u'Napačna captcha']
error_summary['captcha'] = u'Nepravilno izpolnjeno preverjanje ReCAPTCHA.'
except HTTPError:
errors['captcha'] = [u'Napaka pri preverjanju captcha']
error_summary['captcha'] = u'Prišlo je do napake pri preverjanju ReCAPTCHA. Prosimo, poskusite ponovno.'
if data_dict["message"].strip() == '':
errors['message'] = [u'Manjkajoča vrednost']
error_summary['message'] = u'Komentar je obvezen.'
elif data_dict.get('email', '') == '':
errors['email'] = [u'Manjkajoča vrednost']
error_summary['email'] = u'Email je obvezen.'
elif not re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)").match(data_dict.get('email', '')):
errors['email'] = [u'Nepravilno izpolnjena vrednost']
error_summary['email'] = u'Pravilno izpolnjen email je obvezen.'
elif not errors:
er, er_sum = self._report_opsi_issue_to_owner(pkg_dict, data_dict)
errors.update(er)
error_summary.update(er_sum)
if not errors:
h.flash_success(u'Vaše sporočilo je bilo poslano avtorju zbirke \'%s\'.' % pkg_dict['title'], allow_html=True)
p.toolkit.redirect_to(controller='package', action='read', id=id)
vars = {'data_dict': data_dict, 'errors': errors, 'error_summary': error_summary}
return render('issues/opsi_issue.html', extra_vars=vars)
def change_status(self, id, data=None, errors=None, error_summary=None):
"""
Display form for publishing or sending message to author
:return: html
"""
package_type = self._get_package_type(id)
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj,
'publish': 'publish' in request.params,
'reject': 'reject' in request.params}
try:
check_access('package_change_state',context)
except NotAuthorized:
abort(401, _('Not authorized to see this page'))
try:
pkg_dict = get_action('package_show')(context, {'id':id}) # has side-effect of populating context.get('package')
except NotAuthorized:
abort(401, 'Nimate pravic za urejanje zbirke')
data = {}
errors = {}
error_summary = {}
old_state = pkg_dict['state']
if pkg_dict['state'] == 'draft' and ('unpublished' not in pkg_dict or not p.toolkit.asbool(pkg_dict['unpublished'])):
h.flash_success(u'Osnutkom ni mogoče spreminjati stanja, dokler niso v potrjevanju.', allow_html=True)
self._form_save_redirect(pkg_dict['name'], 'edit', package_type=package_type)
return render('package/change_status.html')
c.pkg = context.get('package')
if 'reject' in request.params or 'publish' in request.params:
# Send message to author
try:
data_dict = logic.clean_dict(unflatten(logic.tuplize_dict(logic.parse_params(request.params))))
except logic.NotAuthorized:
base.abort(401, _('Not authorized to see this page'))
if data_dict["message"].strip() == '' and (not 'publish' in request.params):
errors['message'] = [u'Če zbirka ne bo objavljena, je komentar obvezen.']
error_summary['message'] = u'Manjkajoča vrednost'
vars = {'data': data, 'errors': errors, 'error_summary': error_summary}
return p.toolkit.render('package/change_status.html', extra_vars=vars)
if data_dict["message"].strip() != '':
er, er_sum = self._send_message_to_owner(pkg_dict, data_dict)
errors.update(er)
error_summary.update(er_sum)
try: # TODO: have special action package_publish
if not errors and 'publish' in request.params:
#return self._save_edit(id, context, package_type=package_type)
pkg_dict['state'] = 'active'
pkg_dict['unpublished'] = 'False'
pkg_dict['tag_string'] = ', '.join(free_tags(pkg_dict))
get_action('package_update')(context, pkg_dict)
self._on_publish(context, id)
elif not errors and 'reject' in request.params:
pkg_dict['state'] = 'draft'
pkg_dict['unpublished'] = 'False'
pkg_dict['tag_string'] = ', '.join(free_tags(pkg_dict))
get_action('package_update')(context, pkg_dict)
if old_state == 'active':
refresh_drupal_themes_async(context)
except NotAuthorized:
abort(401, 'Nimate dovoljenja za spremembo statusa zbirke %s' % id)
except ObjectNotFound, e:
abort(404, 'Ta zbirka ne obstaja')
except DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
abort(500, _(u'Unable to update search index.') + repr(e.args))
except ValidationError, e:
abort(400, u'Stanja zbirke ni bilo mogoče spremeniti: ' + repr(e.error_dict))
vars = {'data': data, 'errors': errors, 'error_summary': error_summary}
if errors:
return render('package/change_status.html', extra_vars=vars)
elif 'reject' in request.params:
h.flash_success(u'Vaše sporočilo je bilo poslano avtorju zbirke \'%s\'.' % pkg_dict['title'], allow_html=True)
refresh_opsi_reports_async()
self._form_save_redirect(pkg_dict['name'], 'edit', package_type=package_type)
#h.redirect_to(controller='package', action='read', id=id)
elif 'publish' in request.params:
h.flash_success(u'Zbirka \'%s\' je objavljena.' % pkg_dict['title'], allow_html=True)
refresh_opsi_reports_async()
self._form_save_redirect(pkg_dict['name'], 'edit', package_type=package_type)
#h.redirect_to(controller='package', action='read', id=id)
self._setup_template_variables(context, {'id': id}, package_type=package_type)
return render('package/change_status.html', extra_vars=vars)
def delete(self, id):
"""Provide a delete ('withdraw') action, but only for UKLP datasets"""
context = {
'model': model,
'session': model.Session,
'user': c.user,
}
try:
pkg_dict = get_action('package_show')(context, {'id':id}) # has side-effect of populating context.get('package')
except NotAuthorized:
abort(401, 'Nimate pravic za ogled zbirke')
if request.params: # POST
if 'cancel' in request.params:
h.redirect_to(controller='package', action='read', id=id)
elif 'delete' in request.params:
try:
package_name = pkg_dict['name']
get_action('package_delete')(context, {'id':id})
is_uklp = get_from_flat_dict(pkg_dict['extras'], 'UKLP') == 'True'
if is_uklp:
action = 'umaknjena'
resource_type = get_from_flat_dict(pkg_dict['extras'], 'resource-type') + ' record'
else:
action = 'izbrisana'
resource_type = 'zbirka'
h.flash_success(u'%s uspešno %s.' \
% (resource_type, action))
self._form_save_redirect(package_name, 'edit')
except NotAuthorized:
abort(401, _('Unauthorized to delete package %s') % id)
except ObjectNotFound, e:
abort(404, _('Package not found'))
except DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
abort(500, _(u'Unable to update search index.') + repr(e.args))
except ValidationError, e:
abort(400, _('Unable to delete package.') + repr(e.error_dict))
else:
abort(400, 'Parameter error')
# GET
c.pkg = context.get('package')
try:
check_access('package_delete', context)
except NotAuthorized, e:
abort(401, _('Unauthorized to delete package.'))
package_type = self._get_package_type(id)
self._setup_template_variables(context, {'id': id}, package_type=package_type)
return render('package/delete.html')
def new(self, data=None, errors=None, error_summary=None):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj,
'save': 'save' in request.params}
# Package needs to have a organization group in the call to
# check_access and also to save it
try:
check_access('package_create', context)
except NotAuthorized, e:
if 'Ne pripadate nobeni organizaciji' in str(e):
abort(401, _(u'Ne pripadate nobeni organizaciji. Če želite objavljati, vas prosimo, da izpolnete obrazec za članstvo: %s') % urljoin(g.site_url,h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController', action='apply')))
else:
abort(401, _('Unauthorized to create a package'))
return super(PackageController, self).new(data, errors, error_summary)
def _tag_string_to_list(self, tag_string):
''' This is used to change tags from a sting to a list of dicts '''
out = []
for tag in opsi_tag_string_to_list(tag_string):
if tag:
out.append({'name': tag,
'state': 'active'})
return out
def _save_new(self, context, package_type=None):
# The staged add dataset used the new functionality when the dataset is
# partially created so we need to know if we actually are updating or
# this is a real new.
is_an_update = False
ckan_phase = request.params.get('_ckan_phase')
from ckan.lib.search import SearchIndexError
try:
data_dict = clean_dict(dict_fns.unflatten(
tuplize_dict(parse_params(request.POST))))
if request.params['save'] == u'Objavi':
data_dict['state']='active'
data_dict['unpublished']='False'
elif request.params['save'] == u'Shrani osnutek':
data_dict['state']='draft'
data_dict['unpublished']='False'
elif request.params['save'] == u'Pošlji v objavo':
data_dict['state']='draft'
data_dict['unpublished']='True'
if 'tag_string' in data_dict:
data_dict['tags'] = self._tag_string_to_list(
data_dict['tag_string'])
if ckan_phase:
# prevent clearing of groups etc
context['allow_partial_update'] = True
if data_dict.get('pkg_name'):
is_an_update = True
# This is actually an update not a save
data_dict['id'] = data_dict['pkg_name']
del data_dict['pkg_name']
# this is actually an edit not a save
pkg_dict = get_action('package_update')(context, data_dict)
if request.params['save'] == 'go-metadata':
# redirect to add metadata
url = h.url_for(controller='package',
action='new_metadata',
id=pkg_dict['name'])
else:
# redirect to add dataset resources
url = h.url_for(controller='package',
action='new_resource',
id=pkg_dict['name'])
redirect(url)
# Make sure we don't index this dataset
if request.params['save'] not in ['go-resource', 'go-metadata']:
data_dict['state'] = 'draft'
# allow the state to be changed
context['allow_state_change'] = True
data_dict['type'] = package_type
context['message'] = data_dict.get('log_message', '')
pkg_dict = get_action('package_create')(context, data_dict)
if ckan_phase:
# redirect to add dataset resources
url = h.url_for(controller='package',
action='new_resource',
id=pkg_dict['name'])
redirect(url)
self._form_save_redirect(pkg_dict['name'], 'new', package_type=package_type)
except NotAuthorized:
raise
abort(401, _('Unauthorized to read package %s') % '')
except NotFound, e:
abort(404, _('Dataset not found'))
except dict_fns.DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
try:
exc_str = unicode(repr(e.args))
except Exception: # We don't like bare excepts
exc_str = unicode(str(e))
abort(500, _(u'Unable to add package to search index.') + exc_str)
except ValidationError, e:
errors = e.error_dict
error_summary = e.error_summary
if is_an_update:
# we need to get the state of the dataset to show the stage we
# are on.
pkg_dict = get_action('package_show')(context, data_dict)
data_dict['state'] = pkg_dict['state']
return self.edit(data_dict['id'], data_dict,
errors, error_summary)
data_dict['state'] = 'none'
return self.new(data_dict, errors, error_summary)
def _save_edit(self, name_or_id, context, package_type=None):
from ckan.lib.search import SearchIndexError
log.debug('Package save request name: %s POST: %r',
name_or_id, request.POST)
try:
data_dict = clean_dict(dict_fns.unflatten(
tuplize_dict(parse_params(request.POST))))
old_state = data_dict.get('state', 'active')
if request.params['save'] == u'Objavi':
data_dict['state']='active'
data_dict['unpublished']='False'
elif request.params['save'] == u'Shrani osnutek':
data_dict['state']='draft'
data_dict['unpublished']='False'
elif request.params['save'] == u'Pošlji v objavo':
data_dict['state']=old_state #leave state as is
data_dict['unpublished']='True'
if '_ckan_phase' in data_dict:
# we allow partial updates to not destroy existing resources
context['allow_partial_update'] = True
del data_dict['_ckan_phase']
del data_dict['save']
if 'tag_string' in data_dict:
data_dict['tags'] = self._tag_string_to_list(
data_dict['tag_string'])
context['message'] = data_dict.get('log_message', '')
if not context['moderated']:
context['pending'] = False
data_dict['id'] = name_or_id
pkg = get_action('package_update')(context, data_dict)
if request.params.get('save', '') == 'Approve':
get_action('make_latest_pending_package_active')(
context, data_dict)
c.pkg = context['package']
c.pkg_dict = pkg
new_creator_user = data_dict.get('creator_user_id','')
if new_creator_user:
role_show_dict = {'domain_object': data_dict['id'], 'roles': ['admin']}
existing_roles = get_action('roles_show')(context, role_show_dict)
role_update_dict_list = [{'user': new_creator_user, 'roles': ['admin']}]
for role in existing_roles.get('roles',[]):
if role.get('role', '') == 'admin' and role.get('user_id','') != new_creator_user:
role_update_dict_list.append({'user': role.get('user_id'), 'roles': []})
updated_roles = get_action('user_role_bulk_update')(context, {'domain_object': data_dict['id'], 'user_roles': role_update_dict_list})
if old_state == 'active' and data_dict['state'] != 'active':
refresh_drupal_themes_async(context)
self._form_save_redirect(pkg['name'], 'edit', package_type=package_type)
except NotAuthorized:
abort(401, _('Unauthorized to read package %s') % id)
except NotFound, e:
abort(404, _('Dataset not found'))
except dict_fns.DataError:
abort(400, _(u'Integrity Error'))
except SearchIndexError, e:
try:
exc_str = unicode(repr(e.args))
except Exception: # We don't like bare excepts
exc_str = unicode(str(e))
abort(500, _(u'Unable to update search index.') + exc_str)
except ValidationError, e:
errors = e.error_dict
error_summary = e.error_summary
return self.edit(name_or_id, data_dict, errors, error_summary)
def _generate_admin_recipient(self, user):
from pylons import config
if not config.get('email_to'):
log.error('Obvestilo o osnutku uporabnika "%s" ni bilo poslano, ker v '
'nastavitvah CKAN-a ni nastavljen naslov dgu.admin.email.',
str(c.user))
h.flash_error(_("Napaka v sistemski konfiguraciji"))
return None
return [(config.get('error_email_from', 'OPSI Administrator'),
config['email_to'], 0)]
def _send_publication_notification(self, context, pkgname):
from ckan.logic.action import error_summary
from genshi.template.text import NewTextTemplate
pkg_dict = get_action('package_show')(context, {'id': pkgname})
owner_id = pkg_dict['creator_user_id']
owner = model.User.get(owner_id)
owner_email = get_user_email(owner)
owner_name = get_user_name(owner)
recipient = 'objavitelj'
if 'notification_recipient' in pkg_dict:
recipient = pkg_dict['notification_recipient']
recipients = []
if recipient == 'oba':
recipients = [
{
'name': owner_name,
'email': owner_email
},
{
'name': pkg_dict['foi-name'],
'email': pkg_dict['foi-email']
}
]
elif recipient == 'skrbnik':
recipients = [
{
'name': pkg_dict['foi-name'],
'email': pkg_dict['foi-email']
}
]
elif recipient == 'objavitelj':
recipients = [
{
'name': owner_name,
'email': owner_email
}
]
for recip in recipients:
if not recip['email']:
log.warning("Package " + pkgname + " owner with id " + recip['name'] + " has no email, notification " +
"will be sent to executive editors only.")
exec_editors = get_executive_editors()
if not exec_editors:
exec_editors = self._generate_admin_recipient(c.user)
for (ee_name, ee_email, ee_id) in exec_editors:
recipients.append({'name': ee_name, 'email': ee_email})
view_url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='read', id=pkgname))
extra_vars = {
'executive_editor': get_user_name(c.user),
'dataset': pkgname,
'view_url': view_url,
'editor': get_user_name(pkg_dict['creator_user_id'])
}
email_msg = render("email/publication_notification.txt", extra_vars=extra_vars,
loader_class=NewTextTemplate)
try:
sent_emails = [] #prevent duplicate sending if owner is also executive editor
missing_emails = []
for recip in recipients:
if not recip['email']:
missing_emails = recip
elif recip['email'] not in sent_emails:
mail_recipient(recip['name'],
recip['email'],
subject='Obvestilo o objavi',
body=email_msg)
sent_emails.append(recip['email'])
log.debug('User "%s" published %s, notification was sent to %s',
c.user, pkgname, recipients)
if missing_emails:
log.error('Notification to recipients %s rejected, because of missing email addresses.', missing_emails)
except Exception, e:
contact_url = config.get('ckan.site_url', 'https://podatki.gov.si') + '/kontakt'
h.flash_error(
u'Pri pošiljanju vašega zahtevka je prišlo do napake. Prosimo, da nas o tem obvestite s kontaktnim obrazcem na naši strani.',
allow_html=True)
log.error(
'User "%s" prevented from sending publication notification to recipients %s because of mail configuration error: %s',
c.user, str(recipients), e)
def _on_publish(self, context, pkgname):
self._send_publication_notification(context, pkgname)
refresh_drupal_themes_async(context)
def _send_draft_notification(self, pkgname):
from ckan.logic.action import error_summary
from genshi.template.text import NewTextTemplate
recipients = get_executive_editors()
if not recipients:
recipients = self._generate_admin_recipient(c.user)
if not recipients:
return
view_url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='read', id=pkgname))
edit_url = urljoin(g.site_url,
h.url_for(controller='ckanext.dgu.controllers.package:PackageController',
action='edit', id=pkgname))
log.debug('User "%s" requested publication of draft %s, notification was sent to admin %s',
c.user, pkgname, recipients)
extra_vars = {
'user': get_user_name(c.user),
'dataset': pkgname,
'view_url': view_url,
'edit_url': edit_url
}
email_msg = render("email/draft_publication_request.txt", extra_vars=extra_vars,
loader_class=NewTextTemplate)
try:
sent_emails = []
for (name,email,id) in recipients:
if email and email not in sent_emails:
mail_recipient(name,
email,
subject='Obvestilo o novem osnutku',
body=email_msg)
sent_emails.append(email)
except Exception, e:
contact_url = config.get('ckan.site_url', 'https://podatki.gov.si')+'/kontakt'
h.flash_error(u'Pri pošiljanju vašega zahtevka glavnim urednikom je prišlo do napake. Prosimo, da nas o tem obvestite s kontaktnim obrazcem na naši strani.', allow_html=True)
log.error('User "%s" prevented from sending draft confirmation because of mail configuration error: %s', c.user, e)
h.flash_success(u'Vaš zahtevek za objavo zbirke \'%s\' je bil poslan glavnim urednikom v potrditev. Ob spremembi stanja boste dobili obvestilo.' % pkgname, allow_html=True)
def _form_save_redirect(self, pkgname, action, package_type=None):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj,
'publish': True}
if 'save' in request.params:
if request.params['save'] == u'Objavi':
log.debug('Save request from form')
self._on_publish(context, pkgname)
elif request.params['save'] == u'Shrani osnutek':
log.debug('Draft request from form')
elif request.params['save'] == u'Pošlji v objavo':
log.debug('Send request from form')
self._send_draft_notification(pkgname)
refresh_opsi_reports_async()
elif 'delete' in request.params:
refresh_drupal_themes_async(context)
log.debug('Deleted package '+str(pkgname))
else:
log.debug('Package '+str(pkgname)+' redirecting.')
return super(PackageController, self)._form_save_redirect(pkgname, action, package_type=None)
def opsi_resource_download(self, id, resource_id, filename=None):
"""
Provides a direct download by either redirecting the user to the url stored
or downloading an uploaded file directly.
"""
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj}
try:
rsc = get_action('resource_show')(context, {'id': resource_id})
pkg = get_action('package_show')(context, {'id': id})
except NotFound:
abort(404, _('Resource not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read resource %s') % id)
if rsc.get('url_type') == 'upload':
upload = uploader.ResourceUpload(rsc)
filepath = upload.get_path(rsc['id'])
log.debug('File path to retrieve: '+filepath)
fileapp = paste.fileapp.FileApp(filepath)
try:
status, headers, app_iter = request.call_application(fileapp)
except OSError:
abort(404, _('Resource data not found'))
response.headers.update(dict(headers))
content_type, content_enc = mimetypes.guess_type(rsc.get('url',''))
if content_type:
response.headers['Content-Type'] = content_type
response.status = status
return app_iter
elif not 'url' in rsc:
abort(404, _('No download is available'))
redirect(rsc['url'])
class CommentProxy(BaseController):
'''A proxy to Drupal on another server to provide comment HTML. Useful only
for test purposes, when Drupal is not present locally.
'''
def get_comments(self, id):
url = 'https://podatki.gov.si/comment/get/5b3267d8-4307-4eef-a9af-3a4c28224694?comments_per_page=999999'
#url = 'http://co-dev1.dh.bytemark.co.uk/comment/get/%s' % quote(id)
return self._read_url(url)
def _read_url(self, url, post_data=None, content_type=None):
headers = {'Content-Type': content_type} if content_type else {}
request = urllib2.Request(url, post_data, headers)
try:
f = urllib2.urlopen(request)
except HTTPError, e:
response.status_int = 400
return 'Proxied server returned %s: %s' % (e.code, e.msg)
except URLError, e:
err = str(e)
if 'Connection timed out' in err:
response.status_int = 504
return 'Proxied server timed-out: %s' % err
raise e # Send an exception email to handle it better
return f.read()