diff --git a/ckan/config/deployment.ini_tmpl b/ckan/config/deployment.ini_tmpl index ca717788f02..53ddc80c108 100644 --- a/ckan/config/deployment.ini_tmpl +++ b/ckan/config/deployment.ini_tmpl @@ -82,7 +82,7 @@ ckan.site_id = default # Add ``pdf_preview`` to enable the resource preview for PDFs # Add ``resource_proxy`` to enable resorce proxying and get around the # same origin policy -ckan.plugins = stats json_preview recline_preview +ckan.plugins = stats text_preview recline_preview ## Front-End Settings diff --git a/ckan/lib/datapreview.py b/ckan/lib/datapreview.py index 9d93d794fdd..27e7804bc83 100644 --- a/ckan/lib/datapreview.py +++ b/ckan/lib/datapreview.py @@ -11,8 +11,10 @@ import ckan.plugins as p -DEFAULT_DIRECT_EMBED = ['png', 'jpg', 'gif'] -DEFAULT_LOADABLE_IFRAME = ['html', 'htm', 'rdf+xml', 'owl+xml', 'xml', 'n3', 'n-triples', 'turtle', 'plain', 'atom', 'rss', 'txt'] +DEFAULT_DIRECT_EMBED = ['png', 'jpg', 'jpeg', 'gif'] +DEFAULT_LOADABLE_IFRAME = ['html', 'htm', 'rdf+xml', 'owl+xml', 'xml', + 'n3', 'n-triples', 'turtle', 'plain', + 'atom', 'rss', 'txt'] def compare_domains(urls): diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index b82be9d20cf..76a9eb6c888 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -1504,7 +1504,8 @@ def resource_preview(resource, pkg_id): reason = None if format_lower: log.info( - _(u'No preview handler for resource of type {0}'.format(format_lower)) + _(u'No preview handler for resource of type {0}'.format( + format_lower)) ) else: reason = _(u'The resource format is not specified.') diff --git a/ckan/tests/test_coding_standards.py b/ckan/tests/test_coding_standards.py index aab479fe166..34bd996f9dd 100644 --- a/ckan/tests/test_coding_standards.py +++ b/ckan/tests/test_coding_standards.py @@ -250,9 +250,7 @@ class TestImportFromCkan(object): 'ckan/tests/test_plugins.py', 'ckan/tests/test_wsgi_ckanclient.py', 'ckan/websetup.py', - 'ckanext/jsonpreview/tests/test_preview.py', 'ckanext/multilingual/plugin.py', - 'ckanext/pdfpreview/tests/test_preview.py', 'ckanext/reclinepreview/tests/test_preview.py', 'ckanext/stats/controller.py', 'ckanext/stats/stats.py', @@ -761,12 +759,9 @@ class TestPep8(object): 'ckanext/datastore/tests/test_upsert.py', 'ckanext/example_idatasetform/plugin.py', 'ckanext/example_itemplatehelpers/plugin.py', - 'ckanext/jsonpreview/plugin.py', - 'ckanext/jsonpreview/tests/test_preview.py', 'ckanext/multilingual/plugin.py', 'ckanext/multilingual/tests/test_multilingual_plugin.py', 'ckanext/pdfpreview/plugin.py', - 'ckanext/pdfpreview/tests/test_preview.py', 'ckanext/reclinepreview/plugin.py', 'ckanext/reclinepreview/tests/test_preview.py', 'ckanext/resourceproxy/controller.py', diff --git a/ckanext/jsonpreview/plugin.py b/ckanext/jsonpreview/plugin.py deleted file mode 100644 index 37b471b215c..00000000000 --- a/ckanext/jsonpreview/plugin.py +++ /dev/null @@ -1,59 +0,0 @@ -from logging import getLogger - -import ckan.plugins as p - -log = getLogger(__name__) - -proxy = False -try: - import ckanext.resourceproxy.plugin as proxy -except ImportError: - pass - - -class JsonPreview(p.SingletonPlugin): - """This extension previews JSON(P) - - This extension implements two interfaces - - - ``IConfigurer`` allows to modify the configuration - - ``IConfigurable`` get the configuration - - ``IResourcePreview`` allows to add previews - """ - p.implements(p.IConfigurer, inherit=True) - p.implements(p.IConfigurable, inherit=True) - p.implements(p.IResourcePreview, inherit=True) - - JSON_FORMATS = ['json'] - JSONP_FORMATS = ['jsonp'] - proxy_is_enabled = False - - def update_config(self, config): - ''' Set up the resource library, public directory and - template directory for the preview - ''' - p.toolkit.add_public_directory(config, 'theme/public') - p.toolkit.add_template_directory(config, 'theme/templates') - p.toolkit.add_resource('theme/public', 'ckanext-jsonpreview') - - def configure(self, config): - self.proxy_is_enabled = config.get('ckan.resource_proxy_enabled', False) - - def can_preview(self, data_dict): - resource = data_dict['resource'] - format_lower = resource['format'].lower() - if format_lower in self.JSONP_FORMATS: - return True - elif format_lower in self.JSON_FORMATS and (self.proxy_is_enabled or resource['on_same_domain']): - return True - return False - - def setup_template_variables(self, context, data_dict): - assert self.can_preview(data_dict) - resource = data_dict['resource'] - format_lower = resource['format'].lower() - if format_lower in self.JSON_FORMATS and self.proxy_is_enabled and not resource['on_same_domain']: - p.toolkit.c.resource['url'] = proxy.get_proxified_resource_url(data_dict) - - def preview_template(self, context, data_dict): - return 'json.html' diff --git a/ckanext/jsonpreview/theme/public/css/json.css b/ckanext/jsonpreview/theme/public/css/json.css deleted file mode 100644 index 7ef8f90a457..00000000000 --- a/ckanext/jsonpreview/theme/public/css/json.css +++ /dev/null @@ -1,33 +0,0 @@ -body { - width: 500px; -} - -pre { - font-size: 13px; -} - -.loading { - font-weight: bold; - font-family: sans-serif; - font-size: 16px; - position: fixed; - left: -20px; - top: 20px; -} - -.string { - color: #009900; -} -.number { - color: #0066FF; -} -.boolean { - color: #E62E00; -} -.null { - color: #E62E00; -} -.key { - color: #222; - font-weight: bold; -} \ No newline at end of file diff --git a/ckanext/jsonpreview/theme/public/preview_json.js b/ckanext/jsonpreview/theme/public/preview_json.js deleted file mode 100644 index 0d6175ef830..00000000000 --- a/ckanext/jsonpreview/theme/public/preview_json.js +++ /dev/null @@ -1,50 +0,0 @@ -// json preview module -ckan.module('jsonpreview', function (jQuery, _) { - return { - options: { - i18n: { - error: _('An error occurred: %(text)s %(error)s') - } - }, - initialize: function () { - var self = this; - jQuery.ajax(preload_resource['url'], { - type: 'GET', - async: false, - contentType: "application/json", - dataType: preload_resource['format'], - success: function(data, textStatus, jqXHR) { - var html = JSON.stringify(data, null, 4); - var pretty = self._syntaxHighlight(html); - self.el.html(pretty); - }, - error: function(jqXHR, textStatus, errorThrown) { - self.el.html(self.i18n('error', {text: textStatus, error: errorThrown})); - } - }); - }, - - // from: http://stackoverflow.com/a/7220510/214950 - _syntaxHighlight: function(json) { - if (typeof json != 'string') { - json = JSON.stringify(json, undefined, 2); - } - json = json.replace(/&/g, '&').replace(//g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'key'; - } else { - cls = 'string'; - } - } else if (/true|false/.test(match)) { - cls = 'boolean'; - } else if (/null/.test(match)) { - cls = 'null'; - } - return '' + match + ''; - }); - } - }; -}); \ No newline at end of file diff --git a/ckanext/jsonpreview/theme/public/resource.config b/ckanext/jsonpreview/theme/public/resource.config deleted file mode 100644 index 91aa72ed365..00000000000 --- a/ckanext/jsonpreview/theme/public/resource.config +++ /dev/null @@ -1,10 +0,0 @@ -[depends] - -main = base/main - -[groups] - -main = - preview_json.js - - css/json.css diff --git a/ckanext/jsonpreview/theme/templates/json.html b/ckanext/jsonpreview/theme/templates/json.html deleted file mode 100644 index a9a241944ff..00000000000 --- a/ckanext/jsonpreview/theme/templates/json.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends 'dataviewer/base.html' %} - -{% block page %} -
-
-      
- {{ _('Loading...') }} -
-
-
- - {% resource 'ckanext-jsonpreview/main' %} -{% endblock %} \ No newline at end of file diff --git a/ckanext/pdfpreview/tests/test_preview.py b/ckanext/pdfpreview/tests/test_preview.py index b2d0931a3b3..fb6aa643332 100644 --- a/ckanext/pdfpreview/tests/test_preview.py +++ b/ckanext/pdfpreview/tests/test_preview.py @@ -9,8 +9,8 @@ import ckan.plugins as plugins import ckan.lib.helpers as h import ckanext.pdfpreview.plugin as previewplugin -from ckan.lib.create_test_data import CreateTestData -from ckan.config.middleware import make_app +import ckan.lib.create_test_data as create_test_data +import ckan.config.middleware as middleware class TestPdfPreview(tests.WsgiAppCase): @@ -19,13 +19,12 @@ class TestPdfPreview(tests.WsgiAppCase): def setup_class(cls): cls._original_config = config.copy() config['ckan.plugins'] = 'pdf_preview' - wsgiapp = make_app(config['global_conf'], **config) + wsgiapp = middleware.make_app(config['global_conf'], **config) cls.app = paste.fixture.TestApp(wsgiapp) cls.p = previewplugin.PdfPreview() - # create test resource - CreateTestData.create() + create_test_data.CreateTestData.create() context = { 'model': model, @@ -34,8 +33,10 @@ def setup_class(cls): } cls.package = model.Package.get('annakarenina') - cls.resource = logic.get_action('resource_show')(context, {'id': cls.package.resources[1].id}) - cls.resource['url'] = pylons.config.get('ckan.site_url', '//localhost:5000') + cls.resource = logic.get_action('resource_show')( + context, {'id': cls.package.resources[1].id}) + cls.resource['url'] = pylons.config.get( + 'ckan.site_url', '//localhost:5000') cls.resource['format'] = 'pdf' logic.action.update.resource_update(context, cls.resource) @@ -44,7 +45,7 @@ def teardown_class(cls): config.clear() config.update(cls._original_config) plugins.reset() - CreateTestData.delete() + create_test_data.CreateTestData.delete() def test_can_preview(self): data_dict = { @@ -86,12 +87,14 @@ def test_js_included(self): result = self.app.get(url, status='*') assert result.status == 200, result.status - assert (('preview_pdf.js' in result.body) or ('preview_pdf.min.js' in result.body)) + assert (('preview_pdf.js' in result.body) or ( + 'preview_pdf.min.js' in result.body)) assert 'preload_resource' in result.body assert 'data-module="pdfpreview"' in result.body def test_iframe_is_shown(self): - url = h.url_for(controller='package', action='resource_read', id=self.package.name, resource_id=self.resource['id']) + url = h.url_for(controller='package', action='resource_read', + id=self.package.name, resource_id=self.resource['id']) result = self.app.get(url) assert 'data-module="data-viewer"' in result.body assert '/gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs); \ No newline at end of file diff --git a/ckanext/textpreview/theme/public/preview_text.js b/ckanext/textpreview/theme/public/preview_text.js new file mode 100644 index 00000000000..03da83e5c05 --- /dev/null +++ b/ckanext/textpreview/theme/public/preview_text.js @@ -0,0 +1,80 @@ +// json preview module +ckan.module('textpreview', function (jQuery, _) { + return { + options: { + i18n: { + error: _('An error occurred: %(text)s %(error)s') + }, + parameters: { + json: { + contentType: 'application/json', + dataType: 'json', + dataConverter: function (data) { return JSON.stringify(data, null, 2); }, + language: 'json' + }, + jsonp: { + contentType: 'application/javascript', + dataType: 'jsonp', + dataConverter: function (data) { return JSON.stringify(data, null, 2); }, + language: 'json' + }, + xml: { + contentType: 'text/xml', + dataType: 'text', + language: 'xml' + }, + text: { + contentType: 'text/plain', + dataType: 'text', + language: '' + } + } + }, + initialize: function () { + var self = this; + var format = preload_resource['format'].toLowerCase(); + + var TEXT_FORMATS = preview_metadata['text_formats']; + var XML_FORMATS = preview_metadata['xml_formats']; + var JSON_FORMATS = preview_metadata['json_formats']; + var JSONP_FORMATS = preview_metadata['jsonp_formats']; + + var p; + + if (JSON_FORMATS.indexOf(format) !== -1) { + p = this.options.parameters.json; + } else if (JSONP_FORMATS.indexOf(format) !== -1) { + p = this.options.parameters.jsonp; + } else if(XML_FORMATS.indexOf(format) !== -1) { + p = this.options.parameters.xml; + } else { + p = this.options.parameters.text; + } + + jQuery.ajax(preload_resource['url'], { + type: 'GET', + contentType: p.contentType, + dataType: p.dataType, + success: function(data, textStatus, jqXHR) { + data = p.dataConverter ? p.dataConverter(data) : data; + var highlighted; + + if (p.language) { + highlighted = hljs.highlight(p.language, data, true).value; + } else { + highlighted = '
' + data + '
'; + } + + self.el.html(highlighted); + }, + error: function(jqXHR, textStatus, errorThrown) { + if (textStatus == 'error' && jqXHR.responseText.length) { + self.el.html(jqXHR.responseText); + } else { + self.el.html(self.i18n('error', {text: textStatus, error: errorThrown})); + } + } + }); + } + }; +}); diff --git a/ckanext/textpreview/theme/public/resource.config b/ckanext/textpreview/theme/public/resource.config new file mode 100644 index 00000000000..8f6d20329bf --- /dev/null +++ b/ckanext/textpreview/theme/public/resource.config @@ -0,0 +1,12 @@ +[depends] + +main = base/main + +[groups] + +main = + highlight.pack.js + preview_text.js + + styles/github.css + css/text.css diff --git a/ckanext/textpreview/theme/public/styles/default.css b/ckanext/textpreview/theme/public/styles/default.css new file mode 100644 index 00000000000..e417fc17995 --- /dev/null +++ b/ckanext/textpreview/theme/public/styles/default.css @@ -0,0 +1,135 @@ +/* + +Original style from softwaremaniacs.org (c) Ivan Sagalaev + +*/ + +pre code { + display: block; padding: 0.5em; + background: #F0F0F0; +} + +pre code, +pre .subst, +pre .tag .title, +pre .lisp .title, +pre .clojure .built_in, +pre .nginx .title { + color: black; +} + +pre .string, +pre .title, +pre .constant, +pre .parent, +pre .tag .value, +pre .rules .value, +pre .rules .value .number, +pre .preprocessor, +pre .ruby .symbol, +pre .ruby .symbol .string, +pre .aggregate, +pre .template_tag, +pre .django .variable, +pre .smalltalk .class, +pre .addition, +pre .flow, +pre .stream, +pre .bash .variable, +pre .apache .tag, +pre .apache .cbracket, +pre .tex .command, +pre .tex .special, +pre .erlang_repl .function_or_atom, +pre .markdown .header { + color: #800; +} + +pre .comment, +pre .annotation, +pre .template_comment, +pre .diff .header, +pre .chunk, +pre .markdown .blockquote { + color: #888; +} + +pre .number, +pre .date, +pre .regexp, +pre .literal, +pre .smalltalk .symbol, +pre .smalltalk .char, +pre .go .constant, +pre .change, +pre .markdown .bullet, +pre .markdown .link_url { + color: #080; +} + +pre .label, +pre .javadoc, +pre .ruby .string, +pre .decorator, +pre .filter .argument, +pre .localvars, +pre .array, +pre .attr_selector, +pre .important, +pre .pseudo, +pre .pi, +pre .doctype, +pre .deletion, +pre .envvar, +pre .shebang, +pre .apache .sqbracket, +pre .nginx .built_in, +pre .tex .formula, +pre .erlang_repl .reserved, +pre .prompt, +pre .markdown .link_label, +pre .vhdl .attribute, +pre .clojure .attribute, +pre .coffeescript .property { + color: #88F +} + +pre .keyword, +pre .id, +pre .phpdoc, +pre .title, +pre .built_in, +pre .aggregate, +pre .css .tag, +pre .javadoctag, +pre .phpdoc, +pre .yardoctag, +pre .smalltalk .class, +pre .winutils, +pre .bash .variable, +pre .apache .tag, +pre .go .typename, +pre .tex .command, +pre .markdown .strong, +pre .request, +pre .status { + font-weight: bold; +} + +pre .markdown .emphasis { + font-style: italic; +} + +pre .nginx .built_in { + font-weight: normal; +} + +pre .coffeescript .javascript, +pre .javascript .xml, +pre .tex .formula, +pre .xml .javascript, +pre .xml .vbscript, +pre .xml .css, +pre .xml .cdata { + opacity: 0.5; +} diff --git a/ckanext/textpreview/theme/public/styles/github.css b/ckanext/textpreview/theme/public/styles/github.css new file mode 100644 index 00000000000..81272f9d545 --- /dev/null +++ b/ckanext/textpreview/theme/public/styles/github.css @@ -0,0 +1,127 @@ +/* + +github.com style (c) Vasily Polovnyov + +*/ + +pre code { + display: block; padding: 0.5em; + color: #333; + background: #f8f8ff +} + +pre .comment, +pre .template_comment, +pre .diff .header, +pre .javadoc { + color: #998; + font-style: italic +} + +pre .keyword, +pre .css .rule .keyword, +pre .winutils, +pre .javascript .title, +pre .nginx .title, +pre .subst, +pre .request, +pre .status { + color: #333; + font-weight: bold +} + +pre .number, +pre .hexcolor, +pre .ruby .constant { + color: #099; +} + +pre .string, +pre .tag .value, +pre .phpdoc, +pre .tex .formula { + color: #d14 +} + +pre .title, +pre .id { + color: #900; + font-weight: bold +} + +pre .javascript .title, +pre .lisp .title, +pre .clojure .title, +pre .subst { + font-weight: normal +} + +pre .class .title, +pre .haskell .type, +pre .vhdl .literal, +pre .tex .command { + color: #458; + font-weight: bold +} + +pre .tag, +pre .tag .title, +pre .rules .property, +pre .django .tag .keyword { + color: #000080; + font-weight: normal +} + +pre .attribute, +pre .variable, +pre .lisp .body { + color: #008080 +} + +pre .regexp { + color: #009926 +} + +pre .class { + color: #458; + font-weight: bold +} + +pre .symbol, +pre .ruby .symbol .string, +pre .lisp .keyword, +pre .tex .special, +pre .prompt { + color: #990073 +} + +pre .built_in, +pre .lisp .title, +pre .clojure .built_in { + color: #0086b3 +} + +pre .preprocessor, +pre .pi, +pre .doctype, +pre .shebang, +pre .cdata { + color: #999; + font-weight: bold +} + +pre .deletion { + background: #fdd +} + +pre .addition { + background: #dfd +} + +pre .diff .change { + background: #0086b3 +} + +pre .chunk { + color: #aaa +} \ No newline at end of file diff --git a/ckanext/textpreview/theme/templates/text.html b/ckanext/textpreview/theme/templates/text.html new file mode 100644 index 00000000000..57d3d72c7e6 --- /dev/null +++ b/ckanext/textpreview/theme/templates/text.html @@ -0,0 +1,21 @@ +{% extends 'dataviewer/base.html' %} + +{% block page %} +
+
+      
+ {{ _('Loading...') }} +
+
+
+ + {% block scripts %} + {{ super() }} + + {% endblock %} + + + {% resource 'ckanext-textpreview/main' %} +{% endblock %} diff --git a/doc/configuration.rst b/doc/configuration.rst index 4fdf1e29f48..6d6c7d29191 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -32,7 +32,7 @@ settings, for reference. [app:main] # This setting will work. - ckan.plugins = stats json_preview recline_preview + ckan.plugins = stats text_preview recline_preview If the same option is set more than once in your config file, the last setting given in the file will override the others. @@ -513,6 +513,8 @@ Example:: ckan.plugins = disqus datapreview googleanalytics follower +Default value: ``stats text_preview recline_preview`` + Specify which CKAN plugins are to be enabled. .. warning:: If you specify a plugin but have not installed the code, CKAN will not start. @@ -734,9 +736,9 @@ ckan.preview.direct Example:: - ckan.preview.direct = png jpg gif + ckan.preview.direct = png jpg jpeg gif -Default value: ``png jpg gif`` +Default value: ``png jpg jpeg gif`` Defines the resource formats which should be embedded directly in an ``img`` tag when previewing them. @@ -753,7 +755,7 @@ Example:: Default value: ``html htm rdf+xml owl+xml xml n3 n-triples turtle plain atom rss txt`` Defines the resource formats which should be loaded directly in an ``iframe`` -tag when previewing them. +tag when previewing them if no :doc:`data-viewer` can preview it. .. _ckan.dumps_url: diff --git a/doc/data-viewer.rst b/doc/data-viewer.rst index e76416f421a..2a446de4724 100644 --- a/doc/data-viewer.rst +++ b/doc/data-viewer.rst @@ -47,12 +47,14 @@ the resource read page: * ``png`` * ``jpg`` +* ``jpeg`` * ``gif`` The types of resources that are embedded directly can be specified in the CKAN config file. See :ref:`ckan.preview.direct` for more information. -The following types of resources will be loaded in an iframe: +The following types of resources will be loaded in an iframe if there is no +extension that can preview these types: * ``plain`` * ``txt`` @@ -70,6 +72,10 @@ The following types of resources will be loaded in an iframe: The types of resources that are loaded in an iframe can be specified in the CKAN config file. See :ref:`ckan.preview.loadable` for more information. +Note that these documents will be directly linked by the browser, so the +way in which they are shown may vary. If you want to ensure for instance that +XML based documents are correctly previewed, have a look at `Viewing highlighted XML, JSON and plain text data`_. + Viewing structured data: the Data Explorer ------------------------------------------ @@ -106,25 +112,34 @@ Or: reliable than viewing data that is in the DataStore. -Viewing JSON data ------------------ +Viewing highlighted XML, JSON and plain text data +------------------------------------------------- -**Configuration required:** The ``json_preview`` extension must be added to +**Configuration required:** The ``text_preview`` extension must be added to ``ckan.plugins`` in your CKAN configuration file. If you wish to view -external JSON resources as well, the ``resource_proxy`` extension must also +external files resources as well, the ``resource_proxy`` extension must also be enabled. These extensions are part of CKAN and so do not need to be installed separately. -**Resource formats:** ``json``, ``jsonp``. +**Resource formats:** + +* ``json``, ``gjson``, ``geojson`` + (can be configured by setting ``ckan.preview.json_formats``) +* ``jsonp`` + (can be configured by setting ``ckan.preview.jsonp_formats``) +* ``xml``, ``rdf``, ``rdf+xml``, ``owl+xml``, ``atom``, ``rss`` + (can be configured by setting ``ckan.preview.xml_formats``) +* ``text/plain``, ``txt``, ``plain`` + (can be configured by setting ``ckan.preview.text_formats``) -The ``json_preview`` extension provides previews of any JSON data that has been +The ``text_preview`` extension provides previews of many file types that have been added to a CKAN instance (and so are stored in the `Filestore `_). -To view the data the resource format must be set to "json" or "jsonp" -(case insensitive). +To view the data the resource format must be set to one of the resource formats +from above (case insensitive). -To also view remote JSON resources, the ``resource_proxy`` extension must be +To also view remote resources, the ``resource_proxy`` extension must be enabled as well (this is required in order to get around the `same origin policy `_). diff --git a/setup.py b/setup.py index 6ae91c771bf..47da4ab1726 100644 --- a/setup.py +++ b/setup.py @@ -121,7 +121,7 @@ datastore=ckanext.datastore.plugin:DatastorePlugin test_tag_vocab_plugin=ckanext.test_tag_vocab_plugin:MockVocabTagsPlugin resource_proxy=ckanext.resourceproxy.plugin:ResourceProxy - json_preview=ckanext.jsonpreview.plugin:JsonPreview + text_preview=ckanext.textpreview.plugin:TextPreview pdf_preview=ckanext.pdfpreview.plugin:PdfPreview recline_preview=ckanext.reclinepreview.plugin:ReclinePreview example_itemplatehelpers=ckanext.example_itemplatehelpers.plugin:ExampleITemplateHelpersPlugin @@ -131,7 +131,7 @@ domain_object_mods = ckan.model.modification:DomainObjectModificationExtension [babel.extractors] - ckan = ckan.lib.extract:extract_ckan + ckan = ckan.lib.extract:extract_ckan """, # setup.py test command needs a TestSuite so does not work with py.test # test_suite = 'nose.collector',