diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..92a64b56 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space + +# 4 space indentation +[*.{py,rst,js}] +indent_style = space +indent_size = 4 + +[*.html] +indent_style = space +indent_size = 2 + +[*.txt] +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 2c7ab86c..52ec392b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ downloads .pydevproject .settings lib/python* -.Python +.Pythonpl include/python* *.egg-info *.swp @@ -31,5 +31,11 @@ MANIFEST /build/* .hg/* .hgtags +*~ +*# celerybeat-schedule celerybeat.pid +.python-version +.idea +Vagrantfile +.vagrant \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index c9a24e19..a83081cf 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -52,7 +52,7 @@ The license below applies to the following dependencies: Eric Seidel - eric@webkit.org The Mozilla Foundation (contributions from Henri Sivonen since 2008) * httplib2: Copyright (c) 2006 by Joe Gregorio - * jquery.autocomplete.js: Copyright (c) 2009 Jörn Zaefferer + * jquery.autocomplete.js: Copyright (c) 2009 J�rn Zaefferer * jquery.bgiframe.min.js: Copyright 2010, Brandon Aaron (http://brandonaaron.net/) * jquery.form.js: Contributors list taken from http://malsup.com/jquery/form/#download: John Resig @@ -61,21 +61,17 @@ The license below applies to the following dependencies: Klaus Hartl Matt Grimm Yehuda Katz - Jörn Zaefferer + J�rn Zaefferer Sam Collett Gilles van den Hoven Kevin Glowacz Alex Andrienko - * jquery.highlight.js: Johann Burkard - * jquery.jeditable.js: Copyright (c) 2006-2009 Mika Tuupola, Dylan Verheul * jquery.json.js: Brantley Harris (2009-08-14) - * jquery.machineTag.js: Copyright (c) 2009 Gabriel Horner * jquery-1.4.2.js: Copyright (c) 2011 John Resig, http://jquery.com/ * jquery-ui-1.8.min.js: Copyright (c) 2011 Paul Bakaus, http://jqueryui.com/ * Pinax: Copyright (c) 2008-2010 James Tauber and contributors * pytz: Copyright (c) 2003-2005 Stuart Bishop * ui.multiselect.js: Copyright (c) 2009 Michael Aufreiter, http://www.quasipartikel.at - * simplejson: Copyright (c) 2006 Bob Ippolito Permission is hereby granted, free of charge, to any person obtaining a copy @@ -291,33 +287,33 @@ The license below applies to the following bundled dependencies: Copyright (c) 1997-2011 by Secret Labs AB Copyright (c) 1995-2011 by Fredrik Lundh -By obtaining, using, and/or copying this software and/or its associated -documentation, you agree that you have read, understood, and will comply -with the following terms and conditions: - -Permission to use, copy, modify, and distribute this software and its -associated documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appears in all copies, and that -both that copyright notice and this permission notice appear in supporting -documentation, and that the name of Secret Labs AB or the author not be -used in advertising or publicity pertaining to distribution of the software -without specific, written prior permission. - -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. -IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, -INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +By obtaining, using, and/or copying this software and/or its associated +documentation, you agree that you have read, understood, and will comply +with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies, and that +both that copyright notice and this permission notice appear in supporting +documentation, and that the name of Secret Labs AB or the author not be +used in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------- -lightbox.js is distributed under the Creative Commons Attribution 2.5 License +lightbox.js is distributed under the Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/). -Lightbox JS: Fullsize Image Overlays +Lightbox JS: Fullsize Image Overlays by Lokesh Dhakar - http://www.huddletogether.com For more information on this script, visit: @@ -339,9 +335,7 @@ The license below applies to the following bundled dependencies: * django-pagination: Copyright (c) 2008, Eric Florenzano * django-template-utils: Copyright (c) 2007, James Bennett * django-classy-tags: Copyright (c) 2010, Jonas Obrist - * django-cms: Copyright (c) 2008, Batiste Bieler * django-registration: Copyright (c) 2007-2011, James Bennett - * django-sekizai: Copyright (c) 2010, Jonas Obrist All rights reserved. @@ -375,7 +369,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- -The django-ajax-validation dependency is distributed under the following +The django-ajax-validation dependency is distributed under the following license terms: Copyright (c) 2009, Alex Gaynor @@ -414,61 +408,94 @@ The django-redmine dependency is distributed under the following license terms: Copyright (c) 2010, Zepheira, LLC All rights reserved. -Redistribution and use in source and binary forms, with or without modification, +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Zepheira, LLC nor the names of its contributors may be - used to endorse or promote products derived from this software without + * Neither the name of Zepheira, LLC nor the names of its contributors may be + used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- -The jquery.dataTables.js dependency is distributed under the following +The jquery.dataTables.js dependency is distributed under the following license terms: Copyright (c) 2008-2010, Allan Jardine All rights reserved. -Redistribution and use in source and binary forms, with or without modification, +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Allan Jardine nor SpryMedia UK may be used to endorse - or promote products derived from this software without specific prior + * Neither the name of Allan Jardine nor SpryMedia UK may be used to endorse + or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------- + +The django-reversion dependency is distributed under the following license terms: + +Copyright (c) 2009, David Hall. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of David Hall nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------- The OpenLayers dependency is distributed under the following license terms: @@ -510,7 +537,6 @@ either expressed or implied, of OpenLayers Contributors. The following dependencies are distributed under the following license terms: * Simile Exhibit and Timeline (c) Copyright The SIMILE Project 2008. All rights reserved. - * Exhibit Piechart extension (c) Copyright Eric Abouaf 2009. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -536,3 +562,32 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +The license below applies to the following dependencies: + * jquery-sortable + +Copyright (c) 2012 Jonas von Andrian +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/NOTICE.txt b/NOTICE.txt index a61e4daf..537ff920 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -11,7 +11,6 @@ This software uses code from the following projects: * django-announcements (http://code.google.com/p/django-announcements) * django-atompub (http://code.google.com/p/django-atompub/) * django-classy-tags (https://github.com/ojii/django-classy-tags) - * django-cms (http://github.com/divio/django-cms/) * django-compressor (http://github.com/mintchaos/django_compressor) * django-email-confirmation (http://code.google.com/p/django-email-confirmation/) * django-extensions (http://code.google.com/p/django-command-extensions/) @@ -25,7 +24,6 @@ This software uses code from the following projects: * django-piston (http://bitbucket.org/jespern/django-piston/wiki/Home) * django-registration (https://bitbucket.org/ubernostrum/django-registration) * django-robots (http://bitbucket.org/jezdez/django-robots/) - * django-sekizai (https://github.com/ojii/django-sekizai) * django-sorting (http://github.com/directeur/django-sorting/tree/master) * django-staticfiles (http://bitbucket.org/jezdez/django-staticfiles/) * django-template-utils (http://code.google.com/p/django-template-utils/) @@ -40,10 +38,7 @@ This software uses code from the following projects: * jquery.bgiframe.min.js (http://brandonaaron.net) * jquery.dataTables.js (http://www.datatables.net) * jquery.form.js (http://malsup.com/jquery/form/) - * jquery.highlight.js (http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html) - * jquery.jeditable.js (http://www.appelsiini.net/projects/jeditable) * jquery.json.js (http://code.google.com/p/jquery-json/) - * jquery.machineTag.js (http://tagaholic.me/2009/04/14/i-am-machine-tag-and-so-can-you.html) * jquery (http://jquery.com/) * jquery UI (http://jqueryui.com/) * lightbox.js (http://www.huddletogether.com/projects/lightbox/) @@ -52,9 +47,7 @@ This software uses code from the following projects: * Pinax (http://pinaxproject.com/) * pytz (http://pytz.sourceforge.net) * Simile (http://simile-widgets.org/) - * simplejson (http://undefined.org/python/#simplejson) * South (http://south.aeracode.org/) - * ui.multiselect.js (http://www.quasipartikel.at/multiselect/) Please see LICENSE.txt for the licenses that apply to these dependencies. diff --git a/README b/README index f31378d6..3c5423ea 100644 --- a/README +++ b/README @@ -1,88 +1,18 @@ -Viewshare Base README +About Viewshare +=============== -For UNIX-like systems, including Mac OS X and Linux: +Viewshare is an open source web application for generating and customizing unique, +dynamic views through which users can experience cultural heritage digital +collections. The intended users of Viewshare are individuals managing and +creating access to digital collections of cultural heritage materials. -Requires: -* Python 2.6/2.7 -* virtualenv 1.4.3+ (easy_install2.6 virtualenv) +viewshare.org +============= + runs an instance of Viewshare. The site is administrated by the +National Digital Information Infrastructure and Preservation Program at the +Library of Congress. -To build: -> virtualenv --no-site-packages --distribute viewshare -> cd viewshare -> source bin/activate -> pip install -e git+git://loc-recollect.git.sourceforge.net/gitroot/loc-recollect/loc-recollect#egg=viewshare -> pip install -r src/viewshare/requirements/develop.txt +Installation +============ -To create the database: -> cd src/viewshare/example_project/ -> manage.py syncdb -> manage.py migrate - -Load default view themes and canvases -> manage.py loaddata exhibit_themes -> manage.py loaddata canvases - -To run the server on port 8000 -> manage.py runserver - -The following should be run at a regular interval: -> manage.py send_mail - -Production deployment has been tested in conjunction with the libraries, -detailed in requirements/deploy.txt. Gunicorn in conjunction with the meinheld worker is -recommended for deployment on Linux. The eventlet worker can be substituted on other platforms, -but this configuration has not been tested. There are currently issues with Gevent's patching -of DNS in httplib. - -For Windows: - -Download and run the latest Python 2.6 installer from - http://www.python.org/download/ - -Download and run the latest setuptools installer from - http://pypi.python.org/pypi/setuptools#files - -Download and run the latest Mercurial and TortoiseHg installer from - http://tortoisehg.bitbucket.org/download/index.html - -Download and run the latest MinGW installer from - http://sourceforge.net/projects/mingw/files/ -The most user-friendly download is labelled "Automated MinGW Installer". -Make sure to select both g++ and MinGW Make from the options. - -Some packages will require C/C++ compilation, which is what MinGW is for. -In your Windows home directory, create a file called pydistutils.cfg and -save it with the contents: - -[build] -compiler=mingw32 - -Set PYTHON_PATH to C:\Python26\ (or wherever you choose to install it), -and add ";%PYTHON_PATH%\Scripts\;C:\MinGW\bin\" to the Windows PATH -variable (again depending on where you installed MinGW). - -You should now be able to open up the DOS Prompt and run gcc and -easy_install from the command line (getting normal errors about arguments -as opposed to unknown commands). - -At the prompt, run: -> easy_install virtualenv -> virtualenv --no-site-packages --distribute viewshare -> cd viewshare/Scripts -> activate.bat -> cd .. -> pip install -e git+git://loc-recollect.git.sourceforge.net/gitroot/loc-recollect/loc-recollect#egg=viewshare -> pip install -r src/viewshare/requirements/requirements.txt - -To create the database: -> cd src/viewshare/example_project/ -> python manage.py syncdb -> python manage.py migrate - -You may get notice of pywin32 not being installed. This won't get -in the way of functioning. It is difficult to overcome as pywin32 -is not currently available in a form easily compatiable with -virtualenv. - -To run the server on port 8000 -> python manage.py runserver +See docs/installation.rst for installation instructions. diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 7fe25d01..fca1ead1 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -17,16 +17,6 @@ Third-party Apps * announcements * Make site-wide announcements * https://github.com/brosner/django-announcements -* CMS Apps - * cms - * mptt - * menus - * sekizai - * cms.plugins.text - * cms.plugins.picture - * cms.plugins.link - * cms.plugins.file - * cms.plugins.snippet * compressor * Compresses linked and inline javascript or CSS into a single cached file. * https://github.com/jezdez/django_compressor @@ -37,65 +27,46 @@ Third-party Apps * Display sortable data * https://github.com/directeur/django-sorting * NOTE: Is this being used by users? I see examples of the {% anchor %} templatetag in the templates but don't see these templates on the site -* emailconfirmation - * Confirms that email addresses associated with user accounts are valid - * https://github.com/jtauber/django-email-confirmation * friends * Friendship, contact and invitation management * https://github.com/jtauber/django-friends * candidate for elimination * notification * user notification management - * https://github.com/jtauber/django-notification + * https://github.com/jtauber/django-notification * pagination * Break lists into paged results * https://github.com/ericflo/django-pagination -* piston - * A framework for creating RESTful APIs - * https://bitbucket.org/jespern/django-piston/wiki/Home * registration * user registration application * https://bitbucket.org/ubernostrum/django-registration -* robots - * Manages robots.txt files - * https://github.com/jezdez/django-robots * south * Provides schema and data migrations * https://bitbucket.org/andrewgodwin/south/ -* timezones - * Deal with timezone localization for users - * https://github.com/brosner/django-timezones -* uni_form +* django-crispy-forms * Build programmatic reusable layouts out of components, having full control of the rendered HTML - * https://github.com/pydanny/django-uni-form - * NOTE: uni_form is deprecated for django-crispy-forms + * http://django-crispy-forms.readthedocs.org/en/latest/ Custom apps ----------- * Freemix specific * freemix - * freemix.dataset + * viewshare.apps.legacy.dataset * A DataSet can be thought of as a group of information related to uploaded data. A DataSet contains information about: * The raw data that was uploaded to Viewshare * Descriptions of the columns in uploaded data * Type of data (CSV file, MODS file, etc.) * Descriptions of the data properties which are compatible with exhibit and used to visualize the DataSet through SIMILE's Exhibit - * freemix.dataset.augment + * viewshare.apps.augment * Stores and provides augmentation patterns for dataset cells. For example, a DataSet data cell may contain a list of values separated by a comma. This app provides a pattern that recognizes comma separated data and parses it accordingly. * freemix.exhibit - * An Exhibit is a visualization of a DataSet. An Exhibit consists of a Theme and a Canvas: - * Theme - color scheme of the Exhibit - * Canvas - layout of the Exhibit e.g. "A view container with a facet container to the left" - * freemix.exhibit.share - * Allows an Exhibit owner to generate a unique URL for their Exhibit. + * An Exhibit is a visualization of a DataSet. + * viewshare.apps.share + * Allows an Exhibit owner to generate a unique obfuscated URL for their Exhibit. * Viewshare specific * viewshare.apps.notices * User can control email settings for system events like invitations and announcements. - * viewshare.apps.site_theme - * Admin can create or select one of multiple CSS files to be used throughout the site. - * viewshare.apps.collection_catalog - * Admin can group Exhibits that have been published on the system into Collections. Collections can be grouped into Projects. Collections can be tagged with Topics and can be attributed to Organizations. * viewshare.utilities * This is a general, catch-all app to store utilities used throughout the system. Some of the code includes: * context processors to add settings values to a ''RequestContext''. diff --git a/docs/installation.rst b/docs/installation.rst index ddecfca8..8f0daa0a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -6,18 +6,18 @@ Linux based Installation Requires: - * Python 2.6.x - * virtualenv 1.4.3+ (easy_install2.6 virtualenv) + * Python 2.7.x + * virtualenv * Linux derivative (e.g. Ubuntu) * Web server (e.g. Apache 2.2 - optional) * Database (Postgres, MySQL, Oracle - optional) To build:: - $ virtualenv --no-site-packages --distribute viewshare + $ virtualenv viewshare $ cd viewshare $ source bin/activate - $ pip install git+git://loc-recollect.git.sourceforge.net/gitroot/loc-recollect/loc-recollect#egg=viewshare + $ pip install -e git+https://github.com/LibraryOfCongress/viewshare.git#egg=viewshare $ pip install -r src/viewshare/requirements/requirements.txt The "example_project" directory provides the starting point for the @@ -37,15 +37,11 @@ on which the database server resides, and the user credentials. Then create the database:: - $ cd src/freemix/freemix/ + $ cd src/viewshare $ ./manage.py syncdb --noinput $ ./manage.py migrate $ ./manage.py createsuperuser -And load the default data:: - - $ ./manage.py loaddata exhibit_themes - $ ./manage.py loaddata canvases At this point, you can run the site using the included lightweight Web server:: @@ -72,7 +68,7 @@ The following settings should be customized on a per-site basis: * ANONYMOUS_USERNAME: the user name used by anonymous users, e.g. "guest" * ACCOUNT_REQUIRED_EMAIL: require email addresses for new accounts * ACCOUNT_EMAIL_VERIFICATION: require an email address verification step for new accounts - +* OMNITURE_TRACKING: Set to True to enable the LOC omniture tracking script Transformation Server --------------------- @@ -82,14 +78,10 @@ providing data transformation and augmentation services. To install Akara, perform the following steps:: - $ virtualenv --no-site-packages --distribute akara-viewshare + $ virtualenv akara-viewshare $ cd akara-viewshare $ source bin/activate - $ pip install html5lib httplib2 python-dateutil simplejson feedparser xlrd - $ pip install hg+http://foundry.zepheira.com/hg/zenpub#egg=zen - $ pip install git+http://github.com/zepheira/akara.git#egg=akara - $ pip install git+http://github.com/zepheira/amara.git#egg=amara - $ pip install git+http://github.com/zepheira/freemix-akara.git#egg=freemix-akara + $ pip install -r https://raw.githubusercontent.com/zepheira/freemix-akara/master/requirements.txt Create an akara.conf file with these contents (making the path substitution for ConfigRoot):: @@ -99,34 +91,51 @@ Create an akara.conf file with these contents (making the path substitution for PidFile = "logs/akara.pid" ModuleDir = "modules" ModuleCache = "caches" - MaxServers = 50 - MinSpareServers = 5 - MaxSpareServers = 10 - MaxRequestsPerServer = 1000 + MaxServers = 150 + MinSpareServers = 5 + MaxSpareServers = 10 + MaxRequestsPerServer = 10 ErrorLog = "logs/error.log" AccessLog = "logs/access.log" LogLevel = "INFO" MODULES = [ + "akara.demo.cache_proxy", + "zen.akamod.geocoding", "freemix_akara.load_data", "freemix_akara.augment_data", "freemix_akara.contentdm", "freemix_akara.oai", ] - class augment_data: + class geocoding: + + # Comment out the following line for direct geonames + geocoder = 'http://purl.org/com/zepheira/services/geocoders/local-geonames' + + ## Uncomment the following two lines for direct geonames, + ## substituting a geonames.org username + # geocoder = 'http://purl.org/com/zepheira/services/geocoders/geonames-service' + # geonames_service_user = '' + geonames_dbfile = Akara.ConfigRoot+'/caches/geonames.sqlite3' + cache_max_age = 86400 + class load_data: magic_file_command="file -i -" dataload_diagnostics=(not 0) -Install the geo database used by the augmentation service:: + class cache_proxy: + maxlen = { None: 8*24*3600, } + +Install the geo cache used by the augmentation service:: $ mkdir caches $ cd caches $ wget -O caches/geonames.sqlite3 http://dl.dropbox.com/u/19247598/Akara/geonames.sqlite3 + Then initialize and run Akara:: $ akara -f akara.conf setup diff --git a/freemix/__init__.py b/freemix/__init__.py deleted file mode 100644 index d8431e77..00000000 --- a/freemix/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from viewshare import __version__ - diff --git a/freemix/dataset/admin.py b/freemix/dataset/admin.py deleted file mode 100644 index ed1077d9..00000000 --- a/freemix/dataset/admin.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.contrib import admin -from . import models - -def make_json_data_inline(m): - class JSONDataFileInline(admin.TabularInline): - model=m - return JSONDataFileInline - -class DatasetAdmin(admin.ModelAdmin): - inlines = (make_json_data_inline(models.DatasetProfile), - make_json_data_inline(models.DatasetJSONFile), - make_json_data_inline(models.DatasetPropertiesCache) - ) - list_display = ('slug','owner',) - search_fields = ('slug','title', 'description','owner__username') -admin.site.register(models.Dataset, DatasetAdmin) - -class TransactionInline(admin.TabularInline): - model = models.DataSourceTransaction - -class DataSourceAdmin(admin.ModelAdmin): - inlines=[TransactionInline] \ No newline at end of file diff --git a/freemix/dataset/augment/admin.py b/freemix/dataset/augment/admin.py deleted file mode 100644 index 3eb97c8c..00000000 --- a/freemix/dataset/augment/admin.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.contrib import admin -from .models import ListPattern, AugmentationErrorCode -from .forms import ListPatternForm - -class ListPatternAdmin(admin.ModelAdmin): - form = ListPatternForm - list_display = ("title", "description") -admin.site.register(ListPattern, ListPatternAdmin) - - -class AugmentationErrorCodeAdmin(admin.ModelAdmin): - list_display = ("error",) -admin.site.register(AugmentationErrorCode, AugmentationErrorCodeAdmin) diff --git a/freemix/dataset/augment/conf.py b/freemix/dataset/augment/conf.py deleted file mode 100644 index 1c441bc0..00000000 --- a/freemix/dataset/augment/conf.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.conf import settings -from urlparse import urljoin -from freemix.dataset.transform import AKARA_URL_PREFIX - -AKARA_AUGMENT_URL = getattr(settings, "AKARA_AUGMENT_URL", urljoin(AKARA_URL_PREFIX, "augment.freemix.json")) - diff --git a/freemix/dataset/augment/forms.py b/freemix/dataset/augment/forms.py deleted file mode 100644 index e2cfa061..00000000 --- a/freemix/dataset/augment/forms.py +++ /dev/null @@ -1,9 +0,0 @@ -from django import forms -from .models import ListPattern - -class ListPatternForm(forms.ModelForm): - - type = forms.ChoiceField(choices=(("delimiter", "Delimiter"), - ("pattern", "Regular Expression Pattern"))) - class Meta: - model=ListPattern diff --git a/freemix/dataset/augment/models.py b/freemix/dataset/augment/models.py deleted file mode 100644 index 9b427958..00000000 --- a/freemix/dataset/augment/models.py +++ /dev/null @@ -1,38 +0,0 @@ -from django.db import models -from django_extensions.db.models import TitleSlugDescriptionModel -from django.utils.translation import ugettext_lazy as _ - -class ListPattern(TitleSlugDescriptionModel): - type = models.CharField(_('type'), max_length=30) - pattern=models.CharField(_('pattern'), max_length=100) - - @classmethod - def to_dict(cls): - result = {} - for pattern in cls.objects.all(): - result[pattern.slug] = {"title": pattern.title, "type": - pattern.type, "description": - pattern.description, "pattern": - pattern.pattern} - return result - -class AugmentationErrorCode(models.Model): - error = models.CharField(_('error'), max_length=100) - url = models.URLField(_('url')) - - @classmethod - def to_dict(cls): - result = {} - for error in cls.objects.all(): - result[error.error] = error.url - return result - -def list_patterns(): - result = {} - for pattern in ListPattern.objects.all(): - result[pattern.slug] = {"title": pattern.title, "type": - pattern.type, "description": - pattern.description, "pattern": - pattern.pattern} - return result - diff --git a/freemix/dataset/augment/templatetags/augment_tags.py b/freemix/dataset/augment/templatetags/augment_tags.py deleted file mode 100644 index cdf459f5..00000000 --- a/freemix/dataset/augment/templatetags/augment_tags.py +++ /dev/null @@ -1,12 +0,0 @@ -from django import template -from django.conf import settings -from freemix.dataset.augment.models import ListPattern -from django.template.loader import render_to_string -from django.template import Variable - -register = template.Library() - -@register.inclusion_tag("dataset/augment/dialogs.html", takes_context=True) -def augment_dialogs(context): - return {"patterns": ListPattern.objects.all() , "request": - context['request'], "STATIC_URL": context["STATIC_URL"]} diff --git a/freemix/dataset/augment/urls.py b/freemix/dataset/augment/urls.py deleted file mode 100644 index 2d02023f..00000000 --- a/freemix/dataset/augment/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.conf.urls.defaults import * -import views - -urlpatterns = patterns('', - url(r'^transform/$', views.transform, name="akara_augment"), - url(r'^patterns.json$', views.pattern_json, name="list_pattern_json"), - url(r'^errors.json$', views.error_json, name="augmentation_error_json"), -) diff --git a/freemix/dataset/augment/views.py b/freemix/dataset/augment/views.py deleted file mode 100644 index a2971e62..00000000 --- a/freemix/dataset/augment/views.py +++ /dev/null @@ -1,38 +0,0 @@ -from freemix.dataset.transform import RawTransformView, AkaraTransformClient -from freemix.dataset.augment import models -from freemix.dataset.augment import conf - -from django.views.generic.base import View -from freemix.views import JSONResponse - - -class JSONView(View): - - template=None - def get_dict(self, *args, **kwargs): - return {} - - def get(self, *args, **kwargs): - content = self.get_dict(*args, **kwargs) - return JSONResponse(content, self.template) - - - -class ListPatternJSONView(JSONView): - def get_dict(self, *args, **kwargs): - return models.ListPattern.to_dict() - - -pattern_jsonp = ListPatternJSONView.as_view(template="dataset/augment/patterns.js") -pattern_json = ListPatternJSONView.as_view() - - -class AugmentationErrorJSONView(JSONView): - def get_dict(self, *args, **kwargs): - return models.AugmentationErrorCode.to_dict() - - -error_json = AugmentationErrorJSONView.as_view() - - -transform = RawTransformView.as_view(transform=AkaraTransformClient(conf.AKARA_AUGMENT_URL)) diff --git a/freemix/dataset/conf.py b/freemix/dataset/conf.py deleted file mode 100644 index 96e1a51b..00000000 --- a/freemix/dataset/conf.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.conf import settings - -FILE_DOWNLOAD_NGINX_OPTIMIZATION = getattr(settings, "FILE_DOWNLOAD_NGINX_OPTIMIZATION", False) diff --git a/freemix/dataset/forms.py b/freemix/dataset/forms.py deleted file mode 100644 index 3cbbf081..00000000 --- a/freemix/dataset/forms.py +++ /dev/null @@ -1,54 +0,0 @@ -from django import forms -from django.db import transaction -from freemix.dataset.models import Dataset -from django.utils.translation import ugettext_lazy as _ - -published_choices = ((True, "Public"), (False, "Private")) - - -class CreateDatasetForm(forms.Form): - - title = forms.CharField(label=_('Title'), max_length=255) - - description = forms.CharField(label=_('Description'), - required=False) - - published = forms.ChoiceField(label=_("Published"), - widget=forms.RadioSelect(), - choices=published_choices, - initial=True) - - profile = forms.CharField(widget=forms.HiddenInput()) - data = forms.CharField(widget=forms.HiddenInput()) - - def __init__(self, *args, **kwargs): - self.owner = kwargs.pop('owner') - self.datasource_transaction = kwargs.pop('datasource_transaction') - super(CreateDatasetForm, self).__init__(*args, **kwargs) - - def save(self, commit=True): - with transaction.commit_on_success(): - instance = Dataset.objects.create( - title=self.cleaned_data["title"], - description=self.cleaned_data["description"], - owner=self.owner) - - instance.published = (self.cleaned_data["published"] == "True") - instance.save() - instance.update_data(self.cleaned_data["data"]) - instance.update_profile(self.cleaned_data["profile"]) - self.datasource_transaction.source.dataset = instance - self.datasource_transaction.source.save() - self.datasource_transaction.complete() - - return instance - - -class EditDatasetDetailForm(forms.ModelForm): - class Meta: - model = Dataset - fields = ("title", "description", "published",) - - widgets = { - "published": forms.RadioSelect(choices=published_choices) - } diff --git a/freemix/dataset/management/commands/delete_expired_datasources.py b/freemix/dataset/management/commands/delete_expired_datasources.py deleted file mode 100644 index fb071a5e..00000000 --- a/freemix/dataset/management/commands/delete_expired_datasources.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging - -from django.core.management.base import NoArgsCommand - -from freemix.dataset.models import DataSource - - -class Command(NoArgsCommand): - help = ("Delete DataSource records that have a modified " - " date older than the expiration time and no" - " associated dataset") - - def handle_noargs(self, **options): - logging.basicConfig(level=logging.DEBUG, format="%(message)s") - logging.debug("Deleting expired " - "freemix.dataset.models.DataSource models") - transactions = DataSource.objects.all() - expired_transactions = [x for x in transactions if x.is_expired()] - record_count = len(expired_transactions) - for expired in expired_transactions: - expired.delete() - logging.debug("Deleted %s DataSource records" % record_count) diff --git a/freemix/dataset/management/commands/delete_expired_transactions.py b/freemix/dataset/management/commands/delete_expired_transactions.py deleted file mode 100644 index 95c3b13a..00000000 --- a/freemix/dataset/management/commands/delete_expired_transactions.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging - -from django.core.management.base import NoArgsCommand - -from freemix.dataset.models import DataSourceTransaction - - -class Command(NoArgsCommand): - help = ("Delete DataSourceTransaction records that have a modified " - " date older than the expiration time") - - def handle_noargs(self, **options): - logging.basicConfig(level=logging.DEBUG, format="%(message)s") - logging.debug("Deleting expired " - "freemix.dataset.models.DataSourceTransaction models") - transactions = DataSourceTransaction.objects.all() - expired_transactions = [x for x in transactions if x.is_expired()] - record_count = len(expired_transactions) - for expired in expired_transactions: - expired.delete() - logging.debug( - "Deleted %s DataSourceTransaction records" % record_count) diff --git a/freemix/dataset/models.py b/freemix/dataset/models.py deleted file mode 100644 index ba2763d9..00000000 --- a/freemix/dataset/models.py +++ /dev/null @@ -1,409 +0,0 @@ -from datetime import datetime, timedelta -import logging -import urllib2 -import uuid -import os -from django.contrib.auth.models import User -from django.db.models import permalink -from django_extensions.db.fields import UUIDField -from django.db.models import signals - -from django.conf import settings -from django.db import models, transaction as db_tx -from django_extensions.db.fields.json import JSONField -from django_extensions.db.models import ( - TimeStampedModel, TitleSlugDescriptionModel) -from freemix.dataset.transform import AKARA_TRANSFORM_URL -from freemix.dataset.transform import AkaraTransformClient -from freemix.models import JSONDataModel - - -logger = logging.getLogger(__name__) - -TRANSACTION_LIFESPAN = getattr(settings, "TRANSACTION_EXPIRATION_INTERVAL", - timedelta(weeks=1)) - -UNSAVED_DATASOURCE_LIFESPAN = getattr(settings, "UNSAVED_DATASOURCE_LIFESPAN", - timedelta(hours=24)) - - -class Dataset(TitleSlugDescriptionModel, TimeStampedModel): - """ - A dataset consists of an Exhibit JSON document along with an accompanying - profile that describes the properties contained in each record in a format - that the exhibit builder can ingest - """ - owner = models.ForeignKey(User, null=True, related_name="datasets") - published = models.BooleanField(default=True) - - class Meta: - unique_together = ("slug", "owner") - verbose_name_plural = "Data Sets" - verbose_name = "Data Set" - ordering = ('-modified', ) - - def __unicode__(self): - return self.title - - def natural_key(self): - return [self.owner, self.title] - - @permalink - def get_absolute_url(self): - return ("dataset_summary", (), { - 'owner': self.owner.username, - 'slug': self.slug}) - - @permalink - def get_data_url(self): - return ("dataset_data_json", (), { - 'owner': self.owner.username, - 'slug': self.slug}) - - @permalink - def get_properties_url(self): - return ("dataset_properties_cache_json", (), { - 'owner': self.owner.username, - 'slug': self.slug}) - - def update_data(self, json): - data, created = DatasetJSONFile.objects.get_or_create( - dataset=self, defaults={'data': json}) - if not created: - data.data = json - data.save() - - def update_profile(self, json): - profile, created = DatasetProfile.objects.get_or_create( - dataset=self, defaults={'data': json}) - if not created: - profile.data = json - profile.save() - - -class DatasetJSONFile(JSONDataModel): - """ - The data associated with this dataset in the Exhibit JSON format - """ - dataset = models.OneToOneField(Dataset, related_name="data") - - -class DatasetProfile(JSONDataModel): - """ - A JSON document representing the properties defined in the dataset - """ - dataset = models.OneToOneField(Dataset, related_name="profile") - - -class DatasetPropertiesCache(JSONDataModel): - """ - An exhibit compatible representation of the properties defined in - DatasetProfile. - - This is a temporary duplication of the data in DatasetProfile. - The property definitions need to be harmonized with what exhibit expects. - """ - - dataset = models.OneToOneField(Dataset, related_name="properties_cache") - - -def sync_properties(sender, instance=None, **kwargs): - # convert freemix properties to exhibit properties - if instance is None: - return - profile = instance.data - result = {} - for prop in profile["properties"]: - type = "text" - for tag in prop["tags"]: - if (tag.startswith("property:type=") - and not tag == "property:type=shredded_list"): - type = tag[len("property:type="):] - - result[prop["property"]] = { - "label": prop["label"], - "valueType": type - - } - - property_cache, created = DatasetPropertiesCache.objects.get_or_create( - dataset=instance.dataset, defaults={'data': result}) - if not created: - property_cache.data = {"properties": result} - property_cache.save() - -signals.post_save.connect(sync_properties, sender=DatasetProfile) - - -#-----------------------------------------------------------------------------# - -TX_STATUS = { - "pending": 1, - "scheduled": 2, - "running": 3, - "success": 4, - "failure": 5, - "cancelled": 6, -} - - -class DataSourceTransaction(TimeStampedModel): - """Stores the the status and raw result of a remote data transaction for a - particular data source. - - This implementation is temporary, to be replaced with a solution with - pluggable backends. - """ - tx_id = UUIDField() - - status = models.IntegerField( - choices=[(v, k) for k, v in TX_STATUS.iteritems()], - default=TX_STATUS["pending"]) - - source = models.ForeignKey('DataSource', related_name="transactions") - - result = JSONField(null=True, blank=True) - - is_complete = models.BooleanField(default=False) - - def is_expired(self): - return self.modified < (datetime.now() - TRANSACTION_LIFESPAN) - - def is_ready(self): - return self.status in (TX_STATUS["success"], TX_STATUS["cancelled"]) - - @models.permalink - def get_absolute_url(self): - return ('datasource_transaction', (), { - "tx_id": self.tx_id - }) - - def validate(self): - if not len(self.result.get("items", [])): - self.failure("No Data") - return False - self.success() - return True - - def schedule(self): - with db_tx.commit_manually(): - try: - self.status = TX_STATUS["scheduled"] - self.save() - db_tx.commit() - except Exception as ex: - logger.error("Error for transaction %s: %s" % ( - self.tx_id, ex.message)) - db_tx.rollback() - raise ex - else: - from freemix.dataset.tasks import run_transaction - run_transaction.delay(self.tx_id) - - def run(self): - with db_tx.commit_manually(): - try: - self.status = TX_STATUS["running"] - self.save() - db_tx.commit() - - source = self.source.get_concrete() - self.result = source.refresh() - self.validate() - self.save() - except Exception as ex: - self.failure(ex.message) - - db_tx.commit() - - return self - - def failure(self, message): - logger.error("Error for transaction %s: %s" % (self.tx_id, message)) - self.status = TX_STATUS["failure"] - self.result = {"message": message} - self.save() - - def success(self): - self.status = TX_STATUS["success"] - self.save() - - def complete(self): - self.is_complete = True - self.result = None - self.save() - - def cancel(self): - self.status = TX_STATUS["cancelled"] - self.result = None - self.is_complete = True - self.save() - - -class DataSource(TimeStampedModel): - """ - This class should be extended to define the source from which the data in - Datasets are derived. - - Extending subclasses should include any variable parameters that define a - dataset. In addition, they should override the `refresh()` method to - simply perform the data generation and return the result. - """ - classname = models.CharField(max_length=32, editable=False, null=True) - - owner = models.ForeignKey( - User, - null=True, - blank=True, - related_name="data_sources") - - dataset = models.OneToOneField( - Dataset, - null=True, - blank=True, - related_name="source") - - uuid = UUIDField() - - @models.permalink - def get_absolute_url(self): - return ('datasource_detail', (), { - "uuid": self.uuid - }) - - def get_concrete(self): - if self.classname == "DataSource": - return self - return self.__getattribute__(self.classname.lower()) - - def is_concrete(self): - return self.classname == self.__class__.__name__ - - def __unicode__(self): - return self.classname + " " + self.uuid - - def create_transaction(self): - with db_tx.commit_manually(): - try: - # Ensure that existing transactions are marked as complete - # when creating a new one - DataSourceTransaction.objects\ - .filter(source=self)\ - .update(is_complete=True, result=None) - tx = DataSourceTransaction(source=self) - tx.save() - except: - db_tx.rollback() - raise - else: - db_tx.commit() - tx.schedule() - return tx - - def open_transaction(self): - transaction = DataSourceTransaction.objects.filter( - source=self, is_complete=False)[:1] - if not transaction: - return self.create_transaction() - return transaction[0] - - def refresh(self): - return None - - def save(self, *args, **kwargs): - with db_tx.commit_manually(): - try: - if self.classname is None: - self.classname = self.__class__.__name__ - super(DataSource, self).save(*args, **kwargs) - except: - db_tx.rollback() - raise - else: - db_tx.commit() - - def is_expired(self): - return self.modified < (datetime.now() - UNSAVED_DATASOURCE_LIFESPAN) \ - and self.dataset is None - - -class TransformMixin(models.Model): - """ - Contains the methods and parameters necessary to create a simple data - source that posts to a service and returns the result - """ - - transform = AkaraTransformClient(AKARA_TRANSFORM_URL) - - class Meta: - abstract = True - - def get_transform_params(self): - return {} - - def get_transform_body(self): - return None - - def refresh(self): - return self.transform( - body=self.get_transform_body(), - params=self.get_transform_params()) - - -class URLDataSourceMixin(TransformMixin, models.Model): - - url = models.URLField(verify_exists=False) - - class Meta: - abstract = True - - def get_transform_body(self): - r = urllib2.urlopen(self.url) - return r.read() - - def __unicode__(self): - return self.url - - -def make_file_data_source_mixin(storage, upload_to): - """ - Generate a mixin for a file based data source allowing for custom - storage and file path. - - storage -- A django FileStorage implementation - upload_to -- the default path for an uploaded file - """ - class FileDataSourceMixin(TransformMixin, models.Model): - file = models.FileField( - storage=storage, - upload_to=upload_to, - max_length=255) - - class Meta: - abstract = True - - def get_transform_body(self): - return self.file.read() - - def get_filename(self): - return os.path.basename(self.file.name) - - def __unicode__(self): - return self.file.name - return FileDataSourceMixin - - -def parse_profile_json(owner, contents, published=True): - profile = contents.get("data_profile") - title = profile.get("label", str(uuid.uuid4())) - description = profile.get("description", None) - - with db_tx.commit_on_success(): - ds = Dataset.objects.create(owner=owner, - published=published, - title=title, - description=description) - ds.update_data({"items": contents.get("items", [])}) - ds.update_profile({"properties": profile["properties"]}) - - return ds diff --git a/freemix/dataset/tasks.py b/freemix/dataset/tasks.py deleted file mode 100644 index dee35127..00000000 --- a/freemix/dataset/tasks.py +++ /dev/null @@ -1,36 +0,0 @@ -from celery.task import task -from celery.task import periodic_task -from celery.task.schedules import crontab -from django.core.management import call_command - -from freemix.dataset.models import DataSourceTransaction - - -@task -def run_transaction(transaction_id): - """ - Handles creating a DataSourceTransaction for a valid DataSource. Reports - updates to dataset.views.DataSourceTransactionView. - """ - tx = DataSourceTransaction.objects.get(tx_id=transaction_id) - tx.run() - return tx - - -@periodic_task(run_every=crontab(hour='*/4')) -def delete_expired_transactions(): - """ - Delete DataSourceTransaction records that have a modified - date older than the expiration time - """ - call_command('delete_expired_transactions') - - -@periodic_task(run_every=crontab(hour='*/4')) -def delete_expired_datasources(): - """ - Delete DataSource records that have a modified - date older than the expiration time and have no - associated dataset - """ - call_command('delete_expired_datasources') \ No newline at end of file diff --git a/freemix/dataset/templatetags/dataset_tags.py b/freemix/dataset/templatetags/dataset_tags.py deleted file mode 100644 index 77569a34..00000000 --- a/freemix/dataset/templatetags/dataset_tags.py +++ /dev/null @@ -1,38 +0,0 @@ -from django import template -from freemix.permissions import PermissionsRegistry - - -register = template.Library() - - -@register.inclusion_tag("dataset/dataset_list_item.html", takes_context=True) -def dataset_list_item(context, dataset): - request = context["request"] - user = request.user - - can_edit = user.has_perm("dataset.can_edit", dataset) - can_delete = user.has_perm("dataset.can_delete", dataset) - can_view = user.has_perm("dataset.can_view", dataset) - can_inspect = user.has_perm("dataset.can_inspect", dataset) - - can_build = user.has_perm("dataset.can_build", dataset) - - return {"dataset": dataset, - "dataset_url": dataset.get_absolute_url(), - "exhibits": dataset.exhibits.filter( - PermissionsRegistry.get_filter("exhibit.can_view", user)), - "request": request, - "can_view": can_view, - "can_edit": can_edit, - "can_delete": can_delete, - "can_build": can_build, - "can_inspect": can_inspect - } - - -@register.inclusion_tag("dataset/dataset_list.html", takes_context=True) -def dataset_list(context, datasets, max_count=10, pageable=True): - return {"object_list": datasets, - "max_count": max_count, - "pageable": pageable, - "request": context['request']} diff --git a/freemix/dataset/templatetags/datasource_tags.py b/freemix/dataset/templatetags/datasource_tags.py deleted file mode 100644 index 616bf933..00000000 --- a/freemix/dataset/templatetags/datasource_tags.py +++ /dev/null @@ -1,33 +0,0 @@ -from django import template -from freemix.dataset.views import DataSourceRegistry - -register = template.Library() - - -@register.inclusion_tag("dataset/datasource_list.html", takes_context=True) -def datasource_list(context, sources, max_count=10, pageable=True): - return { - "object_list": sources, - "max_count": max_count, - "pageable": pageable, - "request": context['request']} - - -@register.inclusion_tag("dataset/datasource_list_item.html", takes_context=True) -def datasource_list_item(context, datasource): - request = context["request"] - user = request.user - - can_edit = user.has_perm("datasource.can_edit", datasource) - can_delete = user.has_perm("datasource.can_delete", datasource) - can_view = user.has_perm("datasource.can_view", datasource) - - template = DataSourceRegistry.get_detail_template(datasource.get_concrete()) - return {"template_name": template, - "datasource": datasource, - "datasource_url": datasource.get_absolute_url(), - "request": request, - "can_view": can_view, - "can_edit": can_edit, - "can_delete": can_delete - } diff --git a/freemix/dataset/templatetags/datasource_transaction_tags.py b/freemix/dataset/templatetags/datasource_transaction_tags.py deleted file mode 100644 index 9535bc7c..00000000 --- a/freemix/dataset/templatetags/datasource_transaction_tags.py +++ /dev/null @@ -1,36 +0,0 @@ -from django import template -from django.core.urlresolvers import reverse - -from freemix.dataset.utils import pretty_print_transaction_status - -register = template.Library() - - -@register.inclusion_tag( - 'dataset/datasource_transaction_list_item.html', takes_context=True) -def datasource_transaction_list_item(context, datasource_transaction): - """ - Display details for a DataSourceTransaction model - """ - transaction_url = datasource_transaction.get_absolute_url() - transaction_result_url = reverse( - 'datasource_transaction_status', - kwargs={'tx_id': datasource_transaction.tx_id}) - status = pretty_print_transaction_status(datasource_transaction.status) - - return {'datasource_transaction': datasource_transaction, - 'datasource_transaction_url': transaction_url, - 'datasource_transaction_result_url': transaction_result_url, - 'status': status} - - -@register.inclusion_tag( - 'dataset/datasource_transaction_list.html', takes_context=True) -def datasource_transaction_list( - context, datasource_transactions, max_count=None): - """ - Display list of given "datasource_transactions" - """ - if max_count: - datasource_transactions = datasource_transactions[:max_count] - return {'object_list': datasource_transactions} diff --git a/freemix/dataset/urls/__init__.py b/freemix/dataset/urls/__init__.py deleted file mode 100644 index 5428cb50..00000000 --- a/freemix/dataset/urls/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.conf import settings -from django.conf.urls.defaults import patterns, include - -urlpatterns = patterns('', - - (r'^', include('freemix.dataset.urls.list')), - (r'^source/', include('freemix.dataset.urls.datasource')), - (r'^', include('freemix.dataset.urls.base')), - (r'^', include('freemix.dataset.urls.viewer')), - (r'^', include('freemix.dataset.urls.editor')), - -) - -if "freemix.exhibit" in settings.INSTALLED_APPS: - urlpatterns += patterns('', - (r'^', include('freemix.exhibit.urls.dataset')), - ) diff --git a/freemix/dataset/urls/base.py b/freemix/dataset/urls/base.py deleted file mode 100644 index 557ee0a9..00000000 --- a/freemix/dataset/urls/base.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.conf.urls.defaults import url, patterns -from freemix.dataset import views - -# Dataset parameters -urlpatterns = patterns('', - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/profile.json$", - views.dataset_profile_json, - name="dataset_profile_json"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/data.json$", - views.dataset_data_json, - name="dataset_data_json"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/properties.json$", - views.dataset_properties_json, - name="dataset_properties_cache_json"), -) - diff --git a/freemix/dataset/urls/datasource.py b/freemix/dataset/urls/datasource.py deleted file mode 100644 index 199dc8eb..00000000 --- a/freemix/dataset/urls/datasource.py +++ /dev/null @@ -1,41 +0,0 @@ -from django.conf.urls.defaults import url, patterns -from django.contrib.auth.decorators import login_required - -# Dataset editor -from freemix.dataset import views - -urlpatterns = patterns('', - - url(r'^(?P[a-zA-Z0-9_.-]+)$', - login_required(views.DataSourceDetailView.as_view()), - name="datasource_detail"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/edit', - login_required(views.UpdateDataSourceView.as_view()), - name="datasource_update"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/refresh', - login_required(views.RefreshDataSourceView.as_view()), - name="datasource_refresh"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/latest.json', - login_required(views.DataSourceTransactionResultView.as_view()), - name="datasource_transaction_result" - ), - - url(r'^(?P[a-zA-Z0-9_.-]+)/latest$', - login_required(views.DataSourceTransactionView.as_view()), - name="datasource_transaction"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/latest_status.json$', - views.DataSourceTransactionStatusView.as_view(), - name="datasource_transaction_status"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/file$', - login_required(views.FileDataSourceDownloadView.as_view()), - name="file_datasource_file_url"), - - url(r'^(?P[a-f0-9-]+)/create/$', - login_required(views.DatasetCreateFormView.as_view()), - name="dataset_create_form"), -) diff --git a/freemix/dataset/urls/editor.py b/freemix/dataset/urls/editor.py deleted file mode 100644 index 0c37d073..00000000 --- a/freemix/dataset/urls/editor.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.conf.urls.defaults import url, patterns -from django.contrib.auth.decorators import login_required -from django.views.generic.base import TemplateView - -# Dataset editor -from freemix.dataset import views - -urlpatterns = patterns('', - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/editor/$', - login_required(views.DatasetProfileEditView.as_view()), - name="dataset_edit"), - - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/detail/edit/$', - login_required(views.DatasetDetailEditView.as_view()), - name="dataset_edit_form"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/editor/create_success/$', - TemplateView.as_view(template_name="dataset/create/success.html"), - name="dataset_create_success"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/source/$', - login_required(views.RedirectUpdateDataSourceView.as_view()), - name="dataset_source_update"), -) diff --git a/freemix/dataset/urls/list.py b/freemix/dataset/urls/list.py deleted file mode 100644 index 9d8eb5f4..00000000 --- a/freemix/dataset/urls/list.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.conf import settings -from django.conf.urls.defaults import url, patterns -from django.contrib.auth.decorators import login_required -from freemix.dataset import views - -# Dataset List URLs -urlpatterns = patterns('', - url(r"^datasets/(?P[a-zA-Z0-9_.-]+)/$", - views.dataset_list_by_owner, - name="dataset_list_by_owner" - ), - # url(r"^transactions/(?P[a-zA-Z0-9_.-]+)/$", - # views.datasource_transaction_list_by_owner, - # name="datasource_transaction_list_by_owner" - # ), - url(r"^pending/$", - login_required(views.datasource_list_pending), - name="datasource_list_pending") -) diff --git a/freemix/dataset/urls/transaction.py b/freemix/dataset/urls/transaction.py deleted file mode 100644 index dd8c1212..00000000 --- a/freemix/dataset/urls/transaction.py +++ /dev/null @@ -1,9 +0,0 @@ -from freemix.dataset import views -from django.conf.urls.defaults import url, patterns - -urlpatterns = patterns('', - url(r'^tx/(?P[a-f0-9-]+)/$', views.ProcessTransactionView.as_view(), name='datasource_transaction'), - url(r'^tx/(?P[a-f0-9-]+)/result.json$', views.DataSourceTransactionResultView.as_view(), name='datasource_transaction_result'), - url(r'^tx/(?P[a-f0-9-]+)/status.json$', views.DataSourceTransactionStatusView.as_view(), name='datasource_transaction_status'), - url(r'^tx/(?P[a-f0-9-]+)/publish$', views.DataSourceTransactionResultView.as_view(), name='dataset_publish'), -) diff --git a/freemix/dataset/urls/viewer.py b/freemix/dataset/urls/viewer.py deleted file mode 100644 index 5b5c30f6..00000000 --- a/freemix/dataset/urls/viewer.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.conf.urls.defaults import url, patterns - -# Dataset parameters -from freemix.dataset import views - -urlpatterns = patterns('', - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/$", - views.DatasetSummaryView.as_view(), - name="dataset_summary"), - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/detail/$", - views.DatasetDetailView.as_view(), - name="dataset_detail"), - -) \ No newline at end of file diff --git a/freemix/dataset/utils.py b/freemix/dataset/utils.py deleted file mode 100644 index f818b02e..00000000 --- a/freemix/dataset/utils.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -from freemix.dataset.models import TX_STATUS - - -def pretty_print_transaction_status(status_id): - """ - Return a description for a status given a 'status_id' - """ - if status_id == TX_STATUS['pending']: - status = _('Pending') - elif status_id == TX_STATUS['scheduled']: - status = _('Scheduled') - elif status_id == TX_STATUS['running']: - status = _('Running') - elif status_id == TX_STATUS['success']: - status = _('Successful') - elif status_id == TX_STATUS['failure']: - status = _('Failure') - elif status_id == TX_STATUS['cancelled']: - status = _('Cancelled') - else: - status = _('Unknown') - - return status - diff --git a/freemix/dataset/views.py b/freemix/dataset/views.py deleted file mode 100644 index b9b5a2fe..00000000 --- a/freemix/dataset/views.py +++ /dev/null @@ -1,584 +0,0 @@ -from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import reverse -from django.db import transaction -from django.db.models.query_utils import Q -from django.http import ( - HttpResponse, - HttpResponseRedirect, - HttpResponseServerError, - Http404, - HttpResponseForbidden - ) -from django.shortcuts import get_object_or_404, render -from django.views.decorators.http import last_modified -from django.views.generic.base import View, RedirectView -from django.utils import simplejson as json -from django.utils.translation import ugettext_lazy as _ -from django.views.generic.detail import DetailView -from django.views.generic.edit import CreateView, UpdateView -from django.views.generic.list import ListView - -from freemix.dataset import conf, forms, models -from freemix.dataset.utils import pretty_print_transaction_status -from freemix.permissions import PermissionsRegistry -from freemix.views import ( - OwnerListView, - OwnerSlugPermissionMixin, - JSONResponse, - BaseJSONView - ) - - -#----------------------------------------------------------------------------------------------------------------------# -# Data Profile Views - -def get_request_instance(request, *args, **kwargs): - if not hasattr(request, "parent_object"): - owner = kwargs["owner"] - slug = kwargs["slug"] - request.parent_object = get_object_or_404(models.Dataset, owner__username=owner, slug=slug) - return request.parent_object - - -class DatasetJSONView(BaseJSONView): - model = None - - def get_doc(self): - ds = self.get_parent_object() - return self.model.objects.filter(dataset=ds).values_list("data")[0][0] - - def get_parent_object(self): - return get_request_instance(self.request, *self.args, **self.kwargs) - - def check_perms(self): - if not self.request.user.has_perm("dataset.can_view", self.get_parent_object()): - return False - return True - - def cache_control_header(self): - cache_control = super(DatasetJSONView, self).cache_control_header() - if not self.get_parent_object().published: - cache_control += ", private" - else: - cache_control += ", public" - return cache_control - - -lmdec = last_modified(lambda request, *args, **kwargs: get_request_instance(request, *args, **kwargs).modified) - -dataset_profile_json = lmdec(DatasetJSONView.as_view(model=models.DatasetProfile)) - -dataset_data_json = lmdec(DatasetJSONView.as_view(model=models.DatasetJSONFile)) - -dataset_properties_json = lmdec(DatasetJSONView.as_view(model=models.DatasetPropertiesCache)) - -dataset_list_by_owner = OwnerListView.as_view(template_name="dataset/dataset_list_by_owner.html", - model=models.Dataset, - permission = "dataset.can_view", - related=("exhibits","owner")) - -#----------------------------------------------------------------------------------------------------------------------# -# Dataset views - - -class DatasetView(OwnerSlugPermissionMixin, DetailView): - - model = models.Dataset - object_perm = "dataset.can_view" - template_name = "dataset/dataset_summary.html" - - def get_queryset(self): - return self.model.objects.select_related("owner", "source") - - def get_context_data(self, **kwargs): - context = dict(super(DatasetView, self).get_context_data(**kwargs)) - dataset = self.get_object(models.Dataset.objects.select_related("source")) - user = self.request.user - filter = PermissionsRegistry.get_filter("dataset.can_view", user) - - context["can_view"] = user.has_perm("dataset.can_view", dataset), - context["can_inspect"] = user.has_perm("dataset.can_inspect", dataset), - - context["can_build"] = user.has_perm("dataset.can_build", dataset) - context["can_edit"] = user.has_perm("dataset.can_edit", dataset) - context["can_delete"] = user.has_perm("dataset.can_delete", dataset) - - try: - source = dataset.source - context["can_refresh"] = user.has_perm("datasource.can_edit", source) - except ObjectDoesNotExist, ex: - pass - context["exhibits"] = dataset.exhibits.filter(filter) - return context - - -class DatasetSummaryView(DatasetView): - template_name = "dataset/dataset_summary.html" - - def delete(self, request, *args, **kwargs): - ds = self.get_object() - - if request.user.has_perm("dataset.can_delete", ds): - for exhibit in ds.exhibits.filter(~Q(owner__username=self.kwargs["owner"])): - exhibit.dataset = None - exhibit.save() - ds.delete() - return HttpResponse(_("%(file_id)s deleted") % {'file_id': ds.get_absolute_url()}) - return HttpResponseForbidden() - - -class DatasetDetailView(DatasetView): - template_name = "dataset/dataset_detail.html" - object_perm = "dataset.can_inspect" - - -class DatasetCreateFormView(CreateView): - form_class = forms.CreateDatasetForm - template_name = "dataset/create/dataset_metadata_form.html" - - def get_success_url(self): - owner = getattr(self.object.owner, "username", None) - return reverse('dataset_create_success', - kwargs={"owner": owner, - "slug": self.object.slug}) - - def get_context_data(self, **kwargs): - ctx = super(DatasetCreateFormView, self).get_context_data(**kwargs) - ctx["uuid"] = self.kwargs["uuid"] - return ctx - - def get_form_kwargs(self): - kwargs = super(DatasetCreateFormView, self).get_form_kwargs() - del kwargs["instance"] - kwargs["owner"] = self.request.user - tx = get_object_or_404(models.DataSourceTransaction, - source__uuid=self.kwargs["uuid"], - is_complete=False) - kwargs["datasource_transaction"] = tx - return kwargs - - def get_initial(self): - initial = dict(super(DatasetCreateFormView, self).get_initial()) - source = get_object_or_404(models.DataSource, uuid=self.kwargs["uuid"]) - if source: - source = source.get_concrete() - if hasattr(source, "title"): - initial["title"] = getattr(source, "title") - - return initial - - def form_valid(self, form): - self.object = form.save() - return HttpResponseRedirect(self.get_success_url()) - - -class DatasetDetailEditView(OwnerSlugPermissionMixin, UpdateView): - form_class = forms.EditDatasetDetailForm - object_perm="dataset.can_edit" - model = models.Dataset - template_name = "dataset/edit/dataset_metadata_form.html" - - def form_valid(self, form): - self.object = form.save() - return render(self.request, "dataset/detail/dataset_metadata.html", { - "can_edit": True, - "object": self.object, - "is_saved": True - }) - - -class DatasetProfileEditView(OwnerSlugPermissionMixin, View): - object_perm="dataset.can_edit" - template_name="dataset/dataset_update.html" - - def get(self, request, *args, **kwargs): - dataset = self.get_object() - ds_kwargs = {'owner': dataset.owner.username, 'slug': dataset.slug} - context = { - "dataset": dataset, - "dataurl": reverse('dataset_data_json', kwargs=ds_kwargs), - "profileurl": reverse('dataset_profile_json', kwargs=ds_kwargs), - "cancel_url": reverse('dataset_summary', kwargs=ds_kwargs), - "save_url": reverse('dataset_edit', kwargs=ds_kwargs), - "dataset_transaction_status_url": reverse( - "datasource_transaction_status", - kwargs={"uuid": dataset.source.uuid}) - } - - user = self.request.user - filter = PermissionsRegistry.get_filter("dataset.can_view", user) - - context["can_view"] = user.has_perm("dataset.can_view", dataset), - context["can_inspect"] = user.has_perm("dataset.can_inspect", dataset), - - context["can_build"] = user.has_perm("dataset.can_build", dataset) - context["can_edit"] = user.has_perm("dataset.can_edit", dataset) - context["can_delete"] = user.has_perm("dataset.can_delete", dataset) - context["exhibits"] = dataset.exhibits.filter(filter) - - response = render(request, self.template_name, context) - - return response - - def post(self, request, *args, **kwargs): - try: - data = json.loads(request.raw_post_data) - if not data.has_key("properties") or not data.has_key("items"): - return HttpResponseServerError("Invalid Request") - with transaction.commit_on_success(): - ds = self.get_object() - ds.update_profile({"properties": data["properties"]}) - ds.update_data({"items": data["items"]}) - ds.save() - return render(request, "dataset/edit/success.html", { - "owner": ds.owner.username, - "slug": ds.slug - }) - - except Exception, ex: - return HttpResponseServerError("Invalid Request") - - - def get_queryset(self): - return models.Dataset.objects.all() - - -# Data Source Views - -class CreateDataSourceView(CreateView): - model_class = None - form_class = None - template_name = None - - def get_form_kwargs(self, **kwargs): - kwargs = super(CreateDataSourceView, self).get_form_kwargs(**kwargs) - kwargs['user'] = self.request.user - return kwargs - - def form_valid(self, form): - self.object = form.save() - - return HttpResponseRedirect(self.get_success_url()) - - def get_success_url(self): - return reverse("datasource_refresh", kwargs={"uuid": self.object.uuid}) - - -class DataSourceRegistry: - _registry = {} - - @classmethod - def register(cls, model_class, form_class, form_template, detail_template): - key = model_class.__name__ - cls._registry[key] = (model_class, form_class, form_template, detail_template) - - @classmethod - def create_view(cls, model_class): - key = model_class.__name__ - entry = cls._registry.get(key) - return CreateDataSourceView.as_view(model_class=entry[0], - form_class=entry[1], - template_name=entry[2]) - - @classmethod - def get_value(cls, instance, index): - key = instance.__class__.__name__ - if key not in cls._registry: - return None - return cls._registry[key][index] - - @classmethod - def get_form_class(cls, instance): - return cls.get_value(instance, 1) - - @classmethod - def get_form(cls, instance): - form_class = cls.get_form_class(instance) - return form_class(instance=instance) - - @classmethod - def get_form_template(cls, instance): - return cls.get_value(instance, 2) - - @classmethod - def get_detail_template(cls, instance): - return cls.get_value(instance, 3) - - -class DataSourceDetailView(DetailView): - template_name = "dataset/datasource_detail.html" - - def get_object(self, queryset=None): - uuid = self.kwargs["uuid"] - - ds = get_object_or_404(models.DataSource, uuid=uuid) - if not self.request.user.has_perm("datasource.can_view", ds): - raise Http404() - return ds.get_concrete() - - def get_context_data(self, **kwargs): - context = dict(super(DetailView, self).get_context_data(**kwargs)) - source = self.get_object() - user = self.request.user - filter = PermissionsRegistry.get_filter("datasource.can_view", user) - - context["can_view"] = user.has_perm("datasource.can_view", source), - context["can_inspect"] = user.has_perm("datasource.can_inspect", source), - - context["can_build"] = user.has_perm("datasource.can_build", source) - context["can_edit"] = user.has_perm("datasource.can_edit", source) - context["can_delete"] = user.has_perm("datasource.can_delete", source) - - try: - context["can_refresh"] = user.has_perm("datasource.can_edit", source) - except ObjectDoesNotExist, ex: - pass - return context - - -class UpdateDataSourceView(UpdateView): - - def get_object(self, queryset=None): - uuid = self.kwargs["uuid"] - - ds = get_object_or_404(models.DataSource, uuid=uuid) - if not self.request.user.has_perm("datasource.can_edit", ds): - raise Http404() - return ds.get_concrete() - - def get_form_class(self): - source = self.get_object() - return DataSourceRegistry.get_form_class(source) - - def get_template_names(self): - return [DataSourceRegistry.get_form_template(self.get_object()),] - - def form_valid(self, form): - self.object = form.save() - - return HttpResponseRedirect(self.get_success_url()) - - def get_success_url(self): - return reverse("datasource_refresh", kwargs={"uuid": self.get_object().uuid}) - - -class RedirectUpdateDataSourceView(RedirectView): - def get_redirect_url(self, **kwargs): - ds = get_object_or_404(models.Dataset, slug=self.kwargs["slug"], owner__username=self.kwargs["owner"]) - if not ds.source: - raise Http404() - return reverse("datasource_update", kwargs={"uuid": ds.source.uuid}) - - -class PendingDataSourceListView(ListView): - - model = models.DataSource - - template_name = "dataset/datasource_list_pending.html" - - def get_queryset(self): - return self.model.objects.filter(owner=self.request.user, dataset=None).order_by("-created") - -datasource_list_pending = PendingDataSourceListView.as_view() - - -# Data Source Transaction Views -class DataSourceTransactionView(View): - def redirect(self): - status = self.transaction.status - for key in models.TX_STATUS.keys(): - if status == models.TX_STATUS[key]: - return getattr(self, key)() - return HttpResponseServerError("Invalid transaction status for %s"%self.transaction.tx_id) - - def get(self, request, *args, **kwargs): - uuid = kwargs["uuid"] - user = request.user - source = get_object_or_404(models.DataSource, uuid=uuid) - if not user.has_perm('datasource.can_edit', source): - raise Http404 - self.transaction = source.open_transaction() - self.source = source - return self.redirect() - - def display_transaction_result(self): - """ - Render 'datasource_transaction_result'. - """ - source = self.source - save_url = None - if source.dataset: - template_name="dataset/dataset_update.html" - dataset = source.dataset - profile_url = reverse( - 'dataset_profile_json', - kwargs={ - 'owner': dataset.owner.username, - 'slug': dataset.slug - } - ) - cancel_url = reverse( - 'dataset_summary', - kwargs={ - 'owner': dataset.owner.username, - 'slug': dataset.slug - } - ) - save_url = reverse( - 'dataset_edit', - kwargs={ - 'owner': source.dataset.owner.username, - 'slug': source.dataset.slug - } - ) - else: - template_name="dataset/dataset_create.html" - save_url = reverse( - 'datasource_transaction', - kwargs={"uuid": source.uuid} - ) - profile_url = reverse( - 'datasource_transaction_result', - kwargs={'uuid': source.uuid} - ) - cancel_url = reverse('upload_dataset', kwargs={}) - dataset_transaction_status_url = reverse( - "datasource_transaction_status", kwargs={"uuid": source.uuid}) - dataurl = reverse( - 'datasource_transaction_result', - kwargs={'uuid': source.uuid} - ) - return render(self.request, template_name, { - "transaction": self.transaction, - "dataset": source.dataset, - "save_url": save_url, - "dataurl": dataurl, - "profileurl": profile_url, - "dataset_transaction_status_url": dataset_transaction_status_url, - "cancel_url": cancel_url - }) - - def success(self): - return self.display_transaction_result() - - def failure(self): - source = self.transaction.source.get_concrete() - form = DataSourceRegistry.get_form(source) - form_url = reverse("datasource_update", kwargs={"uuid": source.uuid}) - template_name = DataSourceRegistry.get_form_template(source) - return render(self.request, template_name, { - "form": form, - "form_url": form_url, - "object": source, - "transaction": self.transaction, - "show_error": True - }) - - def cancelled(self): - return HttpResponseRedirect(reverse('dataset_upload')) - - def running(self): - return self.display_transaction_result() - - def pending(self): - return self.display_transaction_result() - - def scheduled(self): - return self.display_transaction_result() - - -class DataSourceTransactionResultView(View): - """ - Return the JSON document representing the result of a DataSourceTransaction - """ - def get(self, request, *args, **kwargs): - uuid = kwargs["uuid"] - source = get_object_or_404(models.DataSource, uuid=uuid) - if not self.request.user.has_perm('datasource.can_edit', source): - raise Http404 - tx = source.open_transaction() - return JSONResponse(tx.result) - - -class RefreshDataSourceView(View): - """ - Force the creation of a new DataSourceTransaction - """ - def get(self, request, *args, **kwargs): - uuid = kwargs["uuid"] - source = get_object_or_404(models.DataSource, uuid=uuid) - if not self.request.user.has_perm('datasource.can_edit', source): - raise Http404 - source.create_transaction() - return HttpResponseRedirect(reverse("datasource_transaction", - kwargs={ - "uuid": uuid - })) - - -class DataSourceTransactionStatusView(View): - """ - Return a status string for the open DataSourceTransaction for the - given data source. - - Useful for polling from the client. Also return a boolean indicating if - the DataSourceTransaction's status will continue to change. - """ - def get(self, request, *args, **kwargs): - uuid = kwargs["uuid"] - source = get_object_or_404(models.DataSource, uuid=uuid) - if not self.request.user.has_perm('datasource.can_edit', source): - raise Http404 - - tx = source.open_transaction() - if tx.is_complete: - raise Http404 - - status = pretty_print_transaction_status(tx.status) - - return JSONResponse({ - 'status': unicode(status), - 'isReady': tx.is_ready()}) - - -class FileDataSourceDownloadView(View): - """Serve an uploaded file associated with a data source - - Currently this depends on nginx's X-Accel-Redirect functionality - (http://wiki.nginx.org/XSendfile) - - TODO: Make this pluggable - """ - def nginx_response(self, source): - response = HttpResponse() - url = '/fileuploads/%s' % source.file.name - response["Content-Type"] = "application/binary" - response["X-Accel-Redirect"] = url - return response - - def naive_response(self,source): - contents = source.file.read() - response = HttpResponse(contents) - response["Content-Type"] = "application/binary" - - return response - - def get(self, request, *args, **kwargs): - uuid = kwargs["uuid"] - - source = get_object_or_404(models.DataSource, uuid=uuid) - source = source.get_concrete() - if not hasattr(source, "file"): - raise Http404 - - if not self.request.user.has_perm('datasource.can_edit', source): - raise Http404 - - if conf.FILE_DOWNLOAD_NGINX_OPTIMIZATION: - response = self.nginx_response(source) - else: - response = self.naive_response(source) - - response["Content-Disposition"] = 'attachment; filename=%s' % source.get_filename() - - return response diff --git a/freemix/exhibit/admin.py b/freemix/exhibit/admin.py deleted file mode 100644 index 8e75e21a..00000000 --- a/freemix/exhibit/admin.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.contrib import admin -from freemix.exhibit import models - -class CanvasAdmin(admin.ModelAdmin): - list_display = ('title', 'description') - search_fields = ('title', 'description',) - -admin.site.register(models.Canvas, CanvasAdmin) - - -class ExhibitAdmin(admin.ModelAdmin): - list_display = ('slug', 'owner',) - search_fields = ('slug', 'title', 'description', 'owner__username') - -admin.site.register(models.Exhibit, ExhibitAdmin) - - -class ThemeAdmin(admin.ModelAdmin): - list_display = ('title', 'description') - search_fields = ('title', 'description',) - -admin.site.register(models.Theme, ThemeAdmin) diff --git a/freemix/exhibit/conf.py b/freemix/exhibit/conf.py deleted file mode 100644 index 4b4b7165..00000000 --- a/freemix/exhibit/conf.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.conf import settings - -DEFAULT_EXHIBIT_CANVAS = getattr(settings, "DEFAULT_EXHIBIT_CANVAS", "three-column") -DEFAULT_EXHIBIT_THEME = getattr(settings, "DEFAULT_EXHIBIT_THEME", "smoothness") - -SIMILE_PAINTER_SERVICE_URL = getattr(settings, - "SIMILE_PAINTER_SERVICE_URL", - "http://service.simile.zepheira.com" - "/painter/painter") \ No newline at end of file diff --git a/freemix/exhibit/fixtures/exhibit_themes.json b/freemix/exhibit/fixtures/exhibit_themes.json deleted file mode 100644 index c97e6223..00000000 --- a/freemix/exhibit/fixtures/exhibit_themes.json +++ /dev/null @@ -1,69 +0,0 @@ -[ - - { - "pk": 1, - "model": "exhibit.theme", - "fields": { - "slug": "darkness", - "title": "Darkness", - "url": "/static/exhibit/css/darkness/darkness.css", - "thumbnail": "/static/exhibit/css/darkness/images/theme_30_ui_dark.png" - } - }, - { - "pk": 2, - "model": "exhibit.theme", - "fields": - { - "slug": "pepper-grinder", - "title": "Pepper Grinder", - "url": "/static/exhibit/css/pepper-grinder/pepper-grinder.css", - "thumbnail": "/static/exhibit/css/pepper-grinder/images/theme_30_pepper_grinder.png" - } - }, - { - - "pk": 3, - "model": "exhibit.theme", - "fields": - { - "slug": "smoothness", - "title": "Smoothness", - "url": "/static/exhibit/css/smoothness/smoothness.css", - "thumbnail": "/static/exhibit/css/smoothness/images/theme_30_smoothness.png" - } - }, - { - "pk": 4, - "model": "exhibit.theme", - "fields": - { - "slug": "sunny", - "title": "Sunny", - "url": "/static/exhibit/css/sunny/sunny.css", - "thumbnail": "/static/exhibit/css/sunny/images/theme_30_sunny.png" - } - }, - { - "pk": 5, - "model": "exhibit.theme", - "fields": - { - "slug": "trontastic", - "title": "Trontastic", - "url": "/static/exhibit/css/trontastic/trontastic.css", - "thumbnail": "/static/exhibit/css/trontastic/images/theme_30_trontastic.png" - } - }, - { - "pk": 6, - "model": "exhibit.theme", - "fields": { - "slug": "humanity", - "title": "Humanity", - "url": "/static/view_theme/css/humanity/humanity.css", - "thumbnail": "/static/exhibit/css/humanity/images/theme_30_humanity.png" - } - } - -] diff --git a/freemix/exhibit/forms.py b/freemix/exhibit/forms.py deleted file mode 100644 index 4b553206..00000000 --- a/freemix/exhibit/forms.py +++ /dev/null @@ -1,37 +0,0 @@ -from django import forms -from freemix.exhibit import conf -from freemix.exhibit.models import Exhibit, Theme, Canvas - -class CreateExhibitForm(forms.ModelForm): - - def __init__(self, *args, **kwargs): - self.owner = kwargs.pop('owner') - self.dataset = kwargs.pop('dataset') - self.canvas = kwargs.pop('canvas') - super(CreateExhibitForm, self).__init__(*args, **kwargs) - - def save(self, commit=True): - instance = super(CreateExhibitForm, self).save(commit=False) - instance.owner = self.owner - instance.dataset = self.dataset - instance.canvas = self.canvas - instance.theme = Theme.objects.get(slug=instance.profile.get("theme", conf.DEFAULT_EXHIBIT_THEME)) - - instance.save() - return instance - - class Meta: - model = Exhibit - fields = ("title", "description", "published", "profile",) - widgets= { - "profile": forms.HiddenInput(), - "published": forms.RadioSelect(choices=((True, "Public"), (False, "Private"))) - } - -class UpdateExhibitDetailForm(forms.ModelForm): - class Meta: - model = Exhibit - fields = ("title", "description", "published",) - widgets = { - "published": forms.RadioSelect(choices=((True, "Public"), (False, "Private"))) - } diff --git a/freemix/exhibit/migrations/0001_initial.py b/freemix/exhibit/migrations/0001_initial.py deleted file mode 100644 index 5f9d797a..00000000 --- a/freemix/exhibit/migrations/0001_initial.py +++ /dev/null @@ -1,168 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding model 'Canvas' - db.create_table('exhibit_canvas', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=50, blank=True)), - ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('location', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)), - ('thumbnail', self.gf('django.db.models.fields.URLField')(max_length=200)), - ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), - )) - db.send_create_signal('exhibit', ['Canvas']) - - # Adding model 'Theme' - db.create_table('exhibit_theme', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=50, blank=True)), - ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('url', self.gf('django.db.models.fields.URLField')(default='/static/exhibit/css/smoothness/smoothness.css', max_length=100)), - ('thumbnail', self.gf('django.db.models.fields.files.ImageField')(default='static/images/thumbnails/three-column/smoothness.png', max_length=100)), - ('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)), - )) - db.send_create_signal('exhibit', ['Theme']) - - # Adding model 'Exhibit' - db.create_table('exhibit_exhibit', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, blank=True)), - ('modified', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, blank=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), - ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=50, blank=True)), - ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), - ('owner', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='exhibits', null=True, to=orm['auth.User'])), - ('profile', self.gf('django.db.models.fields.TextField')(default='{}')), - ('dataset', self.gf('django.db.models.fields.related.ForeignKey')(related_name='exhibits', to=orm['dataset.Dataset'])), - ('canvas', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['exhibit.Canvas'])), - ('theme', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['exhibit.Theme'])), - ('published', self.gf('django.db.models.fields.BooleanField')(default=True)), - )) - db.send_create_signal('exhibit', ['Exhibit']) - - # Adding unique constraint on 'Exhibit', fields ['slug', 'owner'] - db.create_unique('exhibit_exhibit', ['slug', 'owner_id']) - - - def backwards(self, orm): - - # Removing unique constraint on 'Exhibit', fields ['slug', 'owner'] - db.delete_unique('exhibit_exhibit', ['slug', 'owner_id']) - - # Deleting model 'Canvas' - db.delete_table('exhibit_canvas') - - # Deleting model 'Theme' - db.delete_table('exhibit_theme') - - # Deleting model 'Exhibit' - db.delete_table('exhibit_exhibit') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'dataset.dataset': { - 'Meta': {'ordering': "('-modified',)", 'unique_together': "(('slug', 'owner'),)", 'object_name': 'Dataset'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'data': ('django.db.models.fields.TextField', [], {'default': '\'{"items": []}\''}), - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'datasets'", 'null': 'True', 'to': "orm['auth.User']"}), - 'profile': ('django.db.models.fields.TextField', [], {'default': '\'{"properties": []}\''}), - 'published': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'source': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'datasets'", 'null': 'True', 'to': "orm['dataset.DataSource']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'dataset.datasource': { - 'Meta': {'object_name': 'DataSource'}, - 'classname': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'data_sources'", 'null': 'True', 'to': "orm['auth.User']"}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'blank': 'True'}) - }, - 'exhibit.canvas': { - 'Meta': {'object_name': 'Canvas'}, - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'location': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'thumbnail': ('django.db.models.fields.URLField', [], {'max_length': '200'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'exhibit.exhibit': { - 'Meta': {'ordering': "('-modified',)", 'unique_together': "(('slug', 'owner'),)", 'object_name': 'Exhibit'}, - 'canvas': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['exhibit.Canvas']"}), - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'dataset': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'exhibits'", 'to': "orm['dataset.Dataset']"}), - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'exhibits'", 'null': 'True', 'to': "orm['auth.User']"}), - 'profile': ('django.db.models.fields.TextField', [], {'default': "'{}'"}), - 'published': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['exhibit.Theme']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'exhibit.theme': { - 'Meta': {'object_name': 'Theme'}, - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'default': "'static/images/thumbnails/three-column/smoothness.png'", 'max_length': '100'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'url': ('django.db.models.fields.URLField', [], {'default': "'/static/exhibit/css/smoothness/smoothness.css'", 'max_length': '100'}) - } - } - - complete_apps = ['exhibit'] diff --git a/freemix/exhibit/models.py b/freemix/exhibit/models.py deleted file mode 100644 index a0dd2034..00000000 --- a/freemix/exhibit/models.py +++ /dev/null @@ -1,91 +0,0 @@ -from django.contrib.auth.models import User -from django.db import models -from django.shortcuts import get_object_or_404 -from django.utils.translation import ugettext_lazy as _ -from django.template.loader import render_to_string -from django_extensions.db.fields.json import JSONField -from django_extensions.db.models import TitleSlugDescriptionModel, TimeStampedModel -from freemix.dataset.models import Dataset -from freemix.models import JSONDataModel - - -class Canvas(TitleSlugDescriptionModel): - location = models.CharField(_('location'), unique=True, max_length=100, - help_text=_("Example: 'exhibit/canvas/three-column.html'")) - - thumbnail = models.URLField(_('thumbnail'), verify_exists = False) - - enabled = models.BooleanField(_('enabled'), null=False,default=True) - - def get_html(self): - return render_to_string(self.location, {}) - - def __unicode__(self): - return self.title - - class Meta: - verbose_name_plural = "Canvases" - - -class Theme(TitleSlugDescriptionModel, models.Model): - url = models.URLField(_('url'), unique=False, max_length=100, - help_text=_("Example: '/static/exhibit/css/" - "smoothness/smoothness.css'"), - default="/static/exhibit/css/" - "smoothness/smoothness.css", - verify_exists=False) - thumbnail = models.ImageField(_('thumbnail'), upload_to='view_theme/img', - default="static/images/thumbnails" - "/three-column/smoothness.png") - enabled = models.BooleanField(_('enabled'), null=False, default=True) - - def __unicode__(self): - return self.title - - -class Exhibit(TitleSlugDescriptionModel, TimeStampedModel): - owner = models.ForeignKey(User, null=True, blank=True, related_name="exhibits") - - profile = JSONField() - - dataset = models.ForeignKey(Dataset, null=True, blank=True, related_name="exhibits") - - canvas = models.ForeignKey(Canvas) - - theme = models.ForeignKey(Theme) - - published = models.BooleanField(default=True) - - def natural_key(self): - return self.owner, self.slug - - @models.permalink - def get_absolute_url(self): - return ('exhibit_display', (), { - 'owner': self.owner.username, - 'slug': self.slug}) - - def __unicode__(self): - return self.title - - def dataset_available(self, user): - """True if the provided user is able to view the dataset associated with this exhibit - """ - ds = self.dataset - if not ds or not user.has_perm("dataset.can_view", ds): - return False - return True - - def update_from_profile(self, profile): - self.theme = get_object_or_404(Theme, slug=profile.get("theme")) - self.profile = profile - self.save() - - class Meta: - unique_together = ("slug", "owner") - verbose_name_plural = "Exhibits" - verbose_name = "Exhibit" - ordering = ('-modified', ) - -#class ExhibitProfile(JSONDataModel): -# pass \ No newline at end of file diff --git a/freemix/exhibit/share/admin.py b/freemix/exhibit/share/admin.py deleted file mode 100644 index b057c7b6..00000000 --- a/freemix/exhibit/share/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin -from freemix.exhibit.share.models import SharedExhibitKey - -class SharedExhibitKeyAdmin(admin.ModelAdmin): - list_display = ('slug','label', 'exhibit',) - search_fields = ('slug','label', ) -admin.site.register(SharedExhibitKey, SharedExhibitKeyAdmin) \ No newline at end of file diff --git a/freemix/exhibit/share/forms.py b/freemix/exhibit/share/forms.py deleted file mode 100644 index d87dec71..00000000 --- a/freemix/exhibit/share/forms.py +++ /dev/null @@ -1,20 +0,0 @@ -from django import forms -from freemix.exhibit.share import models - -class CreateSharedExhibitKeyForm(forms.ModelForm): - - def __init__(self, *args, **kwargs): - self.exhibit = kwargs.pop('exhibit') - super(CreateSharedExhibitKeyForm, self).__init__(*args, **kwargs) - - def save(self, commit=True): - instance = super(CreateSharedExhibitKeyForm,self).save(commit=False) - instance.exhibit = self.exhibit - instance.save() - return instance - - class Meta: - model = models.SharedExhibitKey - fields = ("label",) - widgets = {"label": forms.TextInput} - \ No newline at end of file diff --git a/freemix/exhibit/share/migrations/0001_initial.py b/freemix/exhibit/share/migrations/0001_initial.py deleted file mode 100644 index 22430ff3..00000000 --- a/freemix/exhibit/share/migrations/0001_initial.py +++ /dev/null @@ -1,131 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding model 'SharedExhibitKey' - db.create_table('share_sharedexhibitkey', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('slug', self.gf('django.db.models.fields.CharField')(max_length=36, blank=True)), - ('label', self.gf('django.db.models.fields.TextField')(max_length=255, null=True, blank=True)), - ('exhibit', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['exhibit.Exhibit'])), - )) - db.send_create_signal('share', ['SharedExhibitKey']) - - - def backwards(self, orm): - - # Deleting model 'SharedExhibitKey' - db.delete_table('share_sharedexhibitkey') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'dataset.dataset': { - 'Meta': {'ordering': "('-modified',)", 'unique_together': "(('slug', 'owner'),)", 'object_name': 'Dataset'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'data': ('django.db.models.fields.TextField', [], {'default': '\'{"items": []}\''}), - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'datasets'", 'null': 'True', 'to': "orm['auth.User']"}), - 'profile': ('django.db.models.fields.TextField', [], {'default': '\'{"properties": []}\''}), - 'published': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'source': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'datasets'", 'null': 'True', 'to': "orm['dataset.DataSource']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'dataset.datasource': { - 'Meta': {'object_name': 'DataSource'}, - 'classname': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'data_sources'", 'null': 'True', 'to': "orm['auth.User']"}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'blank': 'True'}) - }, - 'exhibit.canvas': { - 'Meta': {'object_name': 'Canvas'}, - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'location': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'thumbnail': ('django.db.models.fields.URLField', [], {'max_length': '200'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'exhibit.exhibit': { - 'Meta': {'ordering': "('-modified',)", 'unique_together': "(('slug', 'owner'),)", 'object_name': 'Exhibit'}, - 'canvas': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['exhibit.Canvas']"}), - 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'dataset': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'exhibits'", 'null': 'True', 'to': "orm['dataset.Dataset']"}), - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'exhibits'", 'null': 'True', 'to': "orm['auth.User']"}), - 'profile': ('django.db.models.fields.TextField', [], {'default': "'{}'"}), - 'published': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['exhibit.Theme']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'exhibit.theme': { - 'Meta': {'object_name': 'Theme'}, - 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), - 'thumbnail': ('django.db.models.fields.files.ImageField', [], {'default': "'static/images/thumbnails/three-column/smoothness.png'", 'max_length': '100'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'url': ('django.db.models.fields.URLField', [], {'default': "'/static/exhibit/css/smoothness/smoothness.css'", 'max_length': '100'}) - }, - 'share.sharedexhibitkey': { - 'Meta': {'object_name': 'SharedExhibitKey'}, - 'exhibit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['exhibit.Exhibit']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'label': ('django.db.models.fields.TextField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'slug': ('django.db.models.fields.CharField', [], {'max_length': '36', 'blank': 'True'}) - } - } - - complete_apps = ['share'] diff --git a/freemix/exhibit/share/models.py b/freemix/exhibit/share/models.py deleted file mode 100644 index 53bff001..00000000 --- a/freemix/exhibit/share/models.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import models -from django_extensions.db.fields import UUIDField -from django_extensions.db.models import TimeStampedModel -from freemix.exhibit.models import Exhibit - -class SharedExhibitKey(TimeStampedModel,models.Model): - - slug = UUIDField() - - label = models.TextField(max_length=255, blank=True, null=True, help_text="An optional descriptive label") - - exhibit = models.ForeignKey(Exhibit, related_name="shared_keys") - - @models.permalink - def get_absolute_url(self): - return ('shared_exhibit_display', (), { - 'slug': self.slug, - }) - - diff --git a/freemix/exhibit/share/urls.py b/freemix/exhibit/share/urls.py deleted file mode 100644 index fff64958..00000000 --- a/freemix/exhibit/share/urls.py +++ /dev/null @@ -1,33 +0,0 @@ -from django.conf.urls.defaults import patterns, url -from django.views.generic.base import TemplateView -from freemix.exhibit.share import views - - -urlpatterns = patterns('', - url(r"^(?P[a-zA-Z0-9_.-]+)/$", - views.SharedExhibitDisplayView.as_view(), - name="shared_exhibit_display"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/data.json$", - views.shared_dataset_data_json, - name="shared_dataset_data_json"), - url(r"^(?P[a-zA-Z0-9_.-]+)/data_profile.json$", - views.shared_dataset_profile_json, - name="shared_dataset_profile_json"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/properties.json$", - views.shared_dataset_properties_json, - name="shared_dataset_properties_cache_json"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/profile.json$", - views.shared_exhibit_profile_view, - name="shared_exhibit_profile_json"), - - url(r"^view/(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/$", - views.SharedKeyCreateFormView.as_view(), - name="shared_key_create_form"), - - url(r"(?P[a-zA-Z0-9_.-]+)/create_success/$", - TemplateView.as_view(template_name="share/create_success.html"), - name="shared_key_create_success") -) diff --git a/freemix/exhibit/share/views.py b/freemix/exhibit/share/views.py deleted file mode 100644 index 8effb74c..00000000 --- a/freemix/exhibit/share/views.py +++ /dev/null @@ -1,136 +0,0 @@ -from django.core.urlresolvers import reverse -from django.http import HttpResponse, HttpResponseForbidden, Http404, HttpResponseRedirect -from django.shortcuts import get_object_or_404 -from django.views.decorators.http import last_modified -from django.views.generic.detail import DetailView -from django.utils.translation import ugettext_lazy as _ -from django.views.generic.edit import CreateView -from freemix.dataset import models as dataset_models -from freemix.exhibit.models import Exhibit -from freemix.views import BaseJSONView -from freemix.exhibit.share import models, forms - -class SharedExhibitDisplayView(DetailView): - - model = models.SharedExhibitKey - - template_name = "share/exhibit_display.html" - - def get_object(self, queryset=None): - if queryset is None: - queryset = self.get_queryset() - queryset = queryset.select_related("exhibit__dataset", - "exhibit__theme", - "exhibit__canvas", - "exhibit__owner") - obj = get_object_or_404(queryset, slug=self.kwargs.get("slug")) - return obj - - def delete(self, request, *args, **kwargs): - key = self.get_object() - exhibit = key.exhibit - if request.user.has_perm("exhibit.can_share", exhibit): - key.delete() - return HttpResponse(_("%s deleted") % key.get_absolute_url()) - return HttpResponseForbidden() - - def get_context_data(self, **kwargs): - context = super(SharedExhibitDisplayView, self).get_context_data(**kwargs) - key = self.object - - context["exhibit"] = key.exhibit - context["object"] = key - context["can_view"] = True - context["dataset_available"] = key.exhibit.dataset_available(key.exhibit.owner) - return context - - -#-----------------------------------------------------------------------------# -# Shared data profile json - -def get_shared_key(request, *args, **kwargs): - """ - Shared key retrieval function for use by the last_modified decorator - """ - if not hasattr(request, "shared_key"): - slug = kwargs["slug"] - qs = models.SharedExhibitKey.objects.select_related("exhibit__owner","dataset") - request.shared_key = get_object_or_404(qs, slug=slug) - return request.shared_key - - -class SharedKeyDatasetJSONView(BaseJSONView): - model = None - - def get_parent_object(self): - return get_shared_key(self.request, *self.args, **self.kwargs) - - def get_doc(self): - key = self.get_parent_object() - qs = self.model.objects.filter(dataset=key.exhibit.dataset) - return qs.values_list("data", flat=True)[0] - - def check_perms(self): - key = self.get_parent_object() - return key.exhibit.dataset_available(key.exhibit.owner) - -def _dataset_modified(r, *a, **kwa): - key = get_shared_key(r, *a, **kwa) - return key.exhibit.dataset.modified -_lm = last_modified(_dataset_modified) - -shared_dataset_profile_json = _lm(SharedKeyDatasetJSONView.as_view(model=dataset_models.DatasetProfile)) -shared_dataset_data_json = _lm(SharedKeyDatasetJSONView.as_view(model=dataset_models.DatasetJSONFile)) -shared_dataset_properties_json = _lm(SharedKeyDatasetJSONView.as_view(model=dataset_models.DatasetPropertiesCache)) - - -#-----------------------------------------------------------------------------# -# Shared exhibit profile json - -class SharedExhibitProfileJSONView(BaseJSONView): - """ - Returns the exhibit profile associated with a particular shared key. - - There is no permissions check, as the owner of the key is the owner - of the exhibit. - """ - def get_doc(self): - slug = self.kwargs["slug"] - qs = models.SharedExhibitKey.objects.filter(slug=slug) - qs = qs.values_list("exhibit__profile", flat=True) - if not len(qs): - raise Http404 - return qs[0] - -# The last_modified decorator requires a function -def _exhibit_modified(r, *a, **kwa): - qs = models.SharedExhibitKey.objects.filter(slug=kwa["slug"]) - qs = qs.values_list("exhibit__modified", flat=True)[0] - return qs -lm = last_modified(_exhibit_modified) -shared_exhibit_profile_view = lm(SharedExhibitProfileJSONView.as_view()) - -#-----------------------------------------------------------------------------# - -class SharedKeyCreateFormView(CreateView): - form_class = forms.CreateSharedExhibitKeyForm - template_name = "share/shared_key_form.html" - def get_success_url(self): - return reverse('shared_key_create_success', - kwargs={"slug": self.object.slug}) - - def get_form_kwargs(self): - kwargs = super(SharedKeyCreateFormView, self).get_form_kwargs() - exhibit = get_object_or_404(Exhibit, - slug=self.kwargs["slug"], - owner__username=self.kwargs["owner"]) - kwargs["exhibit"] = exhibit - - return kwargs - - def form_valid(self, form): - if not self.request.user.has_perm("exhibit.can_share", form.exhibit): - return HttpResponseForbidden() - self.object = form.save() - return HttpResponseRedirect(self.get_success_url()) - \ No newline at end of file diff --git a/freemix/exhibit/templatetags/embed_tags.py b/freemix/exhibit/templatetags/embed_tags.py deleted file mode 100644 index 31676b55..00000000 --- a/freemix/exhibit/templatetags/embed_tags.py +++ /dev/null @@ -1,91 +0,0 @@ -from compressor.base import SOURCE_FILE, SOURCE_HUNK -from compressor.css import CssCompressor -from compressor.js import JsCompressor -from compressor.signals import post_compress -from compressor.templatetags.compress import CompressorNode -from compressor.templatetags.compress import compress as base_compress -from compressor.conf import settings - -from django import template -from django.template.context import Context -from django.template.loader import render_to_string - -register = template.Library() - - -class LabJsCompressor(JsCompressor): - template_name = "exhibit/embed/js_file.html" - template_name_inline = "exhibit/embed/js_inline.html" - - - def render_output(self, mode, context=None): - """ - Renders the compressor output with the appropriate template for - the given mode and template context. - """ - # Just in case someone renders the compressor outside - # the usual template rendering cycle - if 'compressed' not in self.context: - self.context['compressed'] = {} - - self.context['compressed'].update(context or {}) - self.context['compressed'].update(self.extra_context) - final_context = Context(self.context) - post_compress.send(sender=self.__class__, type=self.type, - mode=mode, context=final_context) - return render_to_string("exhibit/embed/%s_%s.html" % - (self.type, mode), final_context) - - - -class LabCssCompressor(CssCompressor): - template_name="exhibit/embed/css_file.html" - template_name_inline="exhibit/embed/css_inline.html" - - def render_output(self, mode, context=None): - """ - Renders the compressor output with the appropriate template for - the given mode and template context. - """ - # Just in case someone renders the compressor outside - # the usual template rendering cycle - if 'compressed' not in self.context: - self.context['compressed'] = {} - - self.context['compressed'].update(context or {}) - self.context['compressed'].update(self.extra_context) - final_context = Context(self.context) - post_compress.send(sender=self.__class__, type=self.type, - mode=mode, context=final_context) - return render_to_string("exhibit/embed/%s_%s.html" % - (self.type, mode), final_context) - - - def output(self, *args, **kwargs): - # Revert to the base output method - return super(CssCompressor, self).output(*args, **kwargs) - - -class LabJSCompressorNode(CompressorNode): - - def compressor_cls(self, kind, *args, **kwargs): - compressors = { - "css": LabCssCompressor, - "js": LabJsCompressor, - } - if kind not in compressors.keys(): - raise template.TemplateSyntaxError( - "The compress tag's argument must be 'js' or 'css'.") - return compressors.get(kind)(*args, **kwargs) - - def debug_mode(self, context): - return False - - def render(self, context, forced=True): - return super(LabJSCompressorNode, self).render(context, forced=forced) - - -@register.tag -def compress(parser, token): - node = base_compress(parser, token) - return LabJSCompressorNode(node.nodelist,node.kind,node.mode,node.name) \ No newline at end of file diff --git a/freemix/exhibit/templatetags/exhibit_tags.py b/freemix/exhibit/templatetags/exhibit_tags.py deleted file mode 100644 index 60fc5066..00000000 --- a/freemix/exhibit/templatetags/exhibit_tags.py +++ /dev/null @@ -1,53 +0,0 @@ -from django import template -from django.conf import settings -from django.template.loader import render_to_string -from freemix.exhibit import models -from freemix.exhibit import conf - -register = template.Library() - - -@register.inclusion_tag("exhibit/exhibit_list_item.html", takes_context=True) -def exhibit_list_item(context, exhibit): - request = context['request'] - visible = exhibit.dataset_available(request.user) - user = request.user - - can_edit = user.has_perm("exhibit.can_edit", exhibit) - can_delete = user.has_perm("exhibit.can_delete", exhibit) - can_view = user.has_perm("exhibit.can_view", exhibit) - can_inspect = user.has_perm("exhibit.can_inspect", exhibit) - - return {"exhibit": exhibit, - "request": request, - "visible": visible, - "dataset_available": visible, - "can_edit": can_edit, - "can_delete": can_delete, - "can_view": can_view, - "can_inspect": can_inspect} - -@register.inclusion_tag("exhibit/exhibit_list.html", takes_context=True) -def exhibit_list(context, exhibits, max_count=10, pageable=True): - return {"object_list": exhibits, "max_count": max_count, "pageable": pageable, - "request": context['request']} - -@register.inclusion_tag("exhibit/exhibit_create_dialog.html", takes_context=True) -def new_exhibit(context): - return {'STATIC_URL': settings.STATIC_URL} - -@register.simple_tag -def simile_painter_url(): - return conf.SIMILE_PAINTER_SERVICE_URL - -# Theme tags -@register.tag -def theme_list(parser, token): - return ThemeListNode("exhibit/theme_list.html" ) - -class ThemeListNode( template.Node ): - def __init__ (self, template): - self.template = template - - def render(self, context): - return render_to_string(self.template, {"theme_list": models.Theme.objects.filter(enabled=True)}) diff --git a/freemix/exhibit/urls/__init__.py b/freemix/exhibit/urls/__init__.py deleted file mode 100644 index afe02e7f..00000000 --- a/freemix/exhibit/urls/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.conf.urls.defaults import * -from django.contrib.auth.decorators import login_required -from freemix.exhibit import views -urlpatterns = patterns('', - - - (r'^', include('freemix.exhibit.urls.display')), - - (r'^', include('freemix.exhibit.urls.list')), - (r'^', include('freemix.exhibit.urls.embed')), - (r'^', include('freemix.exhibit.urls.editor')), - -) diff --git a/freemix/exhibit/urls/dataset.py b/freemix/exhibit/urls/dataset.py deleted file mode 100644 index 6b597807..00000000 --- a/freemix/exhibit/urls/dataset.py +++ /dev/null @@ -1,30 +0,0 @@ -# URL patterns for linking exhibits to their dataset - -from django.conf.urls.defaults import url -from django.contrib.auth.decorators import login_required -from freemix.exhibit import views - -urlpatterns = ( - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/views.html$', - views.ExhibitsByDatasetListView.as_view(), - name='exhibits_by_dataset' - ), - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/view/profile.json$', - views.StockExhibitProfileJSONView.as_view(), - name='exhibit_profile_template' - ), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/view/canvases.html', - views.CanvasListView.as_view(), - name='exhibit_canvas_chooser' - ), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/view/create/(?P[a-zA-Z0-9_.-]+)/$', - login_required(views.ExhibitCreateFormView.as_view()), - name="exhibit_create_form"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/view/$', - login_required(views.ExhibitCreateView.as_view()), - name='exhibit_create_editor' - ), -) \ No newline at end of file diff --git a/freemix/exhibit/urls/display.py b/freemix/exhibit/urls/display.py deleted file mode 100644 index 4796e696..00000000 --- a/freemix/exhibit/urls/display.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.conf.urls.defaults import url, patterns -from freemix.exhibit import views - -urlpatterns = patterns('', - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/profile.json$", - views.ExhibitProfileJSONView.as_view(), - name="exhibit_profile_json"), - - url(r"^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/$", - views.ExhibitDisplayView.as_view(), - name="exhibit_display"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/detail/$', - views.ExhibitDetailView.as_view(), - name='exhibit_detail'), - -) - diff --git a/freemix/exhibit/urls/editor.py b/freemix/exhibit/urls/editor.py deleted file mode 100644 index 93a20493..00000000 --- a/freemix/exhibit/urls/editor.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.conf.urls.defaults import url, patterns -from django.contrib.auth.decorators import login_required -from django.views.generic.base import TemplateView - -# Dataset editor -from freemix.exhibit import views - -urlpatterns = patterns('', - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/editor/$', - login_required(views.ExhibitProfileUpdateView.as_view()), - name='exhibit_edit'), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/detail/edit/$', - login_required(views.ExhibitDetailEditView.as_view()), - name="exhibit_edit_form"), - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/editor/create_success/$', - TemplateView.as_view(template_name="exhibit/create/success.html"), - name="exhibit_create_success"), -) diff --git a/freemix/exhibit/urls/embed.py b/freemix/exhibit/urls/embed.py deleted file mode 100644 index 2db79da8..00000000 --- a/freemix/exhibit/urls/embed.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.conf.urls.defaults import url, patterns - -from freemix.exhibit import views - -urlpatterns = patterns('', - - url(r'^(?P[a-zA-Z0-9_.-]+)/(?P[a-zA-Z0-9_.-]+)/embed.js$', - views.embedded_exhibit_view, - name='exhibit_embed_js'), - -) \ No newline at end of file diff --git a/freemix/exhibit/urls/list.py b/freemix/exhibit/urls/list.py deleted file mode 100644 index 200e1fef..00000000 --- a/freemix/exhibit/urls/list.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.conf.urls.defaults import url, patterns - -from freemix.exhibit import views - -urlpatterns = patterns('', - - url(r'^(?P[a-zA-Z0-9_.-]+)/$', - views.ExhibitListView.as_view(), - name='exhibit_list_by_owner'), - -) \ No newline at end of file diff --git a/freemix/exhibit/views.py b/freemix/exhibit/views.py deleted file mode 100644 index 3ca0189c..00000000 --- a/freemix/exhibit/views.py +++ /dev/null @@ -1,421 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -from django.http import * -from django.views.decorators.http import last_modified -from django.views.generic.base import View -from django.shortcuts import get_object_or_404, render -from django.template.loader import render_to_string -from django.core.urlresolvers import reverse -from django.views.generic.detail import DetailView -from django.views.generic.edit import CreateView, UpdateView -from django.views.generic.list import ListView - -from freemix.exhibit import models, forms, conf -from freemix.dataset.models import Dataset -from freemix.exhibit.models import Canvas -from freemix.permissions import PermissionsRegistry -from freemix.utils import get_site_url - -from django.utils import simplejson as json -from django.core.cache import cache -import uuid -from freemix.views import JSONResponse, OwnerListView, OwnerSlugPermissionMixin, BaseJSONView - - -# Edit Views -class ExhibitCreateFormView(CreateView): - form_class = forms.CreateExhibitForm - template_name = "exhibit/create/exhibit_metadata_form.html" - - def get_success_url(self): - owner = getattr(self.object.owner, "username", None) - return reverse('exhibit_create_success', - kwargs={"owner": owner, - "slug": self.object.slug}) - - def get_context_data(self, **kwargs): - ctx = super(ExhibitCreateFormView, self).get_context_data(**kwargs) - ctx["owner"] = self.kwargs["owner"] - ctx["slug"] = self.kwargs["slug"] - ctx["canvas"] = self.kwargs["canvas"] - return ctx - - def get_form_kwargs(self): - kwargs = super(ExhibitCreateFormView, self).get_form_kwargs() - kwargs["owner"] = self.request.user - dataset = get_object_or_404(Dataset, owner__username=self.kwargs["owner"], slug=self.kwargs["slug"]) - kwargs["dataset"] = dataset - kwargs["canvas"] = get_object_or_404(Canvas, slug=self.kwargs["canvas"]) - return kwargs - - def get_initial(self): - initial = dict(super(ExhibitCreateFormView, self).get_initial()) - dataset = get_object_or_404(Dataset, owner__username=self.kwargs["owner"], slug=self.kwargs["slug"]) - - initial["title"] = getattr(dataset, "title") - return initial - - def form_valid(self, form): - if not self.request.user.has_perm("dataset.can_view", form.dataset): - return HttpResponseForbidden() - self.object = form.save() - return HttpResponseRedirect(self.get_success_url()) - - -class ExhibitDetailEditView(OwnerSlugPermissionMixin, UpdateView): - form_class = forms.UpdateExhibitDetailForm - object_perm="exhibit.can_edit" - model = models.Exhibit - template_name = "exhibit/edit/exhibit_metadata_form.html" - - def form_valid(self, form): - self.object = form.save() - return render(self.request, "exhibit/detail/exhibit_metadata.html", { - "can_edit": True, - "object": self.object, - "is_saved": True - }) - - -class ExhibitCreateView(View): - - template_name="exhibit/exhibit_create.html" - - def check_permissions(self): - return self.request.user.has_perm("dataset.can_view", self.dataset) - - def get(self, request, *args, **kwargs): - self.dataset_args = {"owner": self.kwargs["owner"], "slug": self.kwargs["slug"]} - - self.dataset = get_object_or_404(Dataset.objects.select_related("owner"), - owner__username=self.kwargs["owner"], - slug=self.kwargs["slug"]) - - if not self.check_permissions(): - raise Http404() - - profile_url = reverse("exhibit_profile_template", kwargs=self.dataset_args) - dataset_profile_url = reverse("dataset_profile_json", kwargs=self.dataset_args) - data_url = reverse("dataset_data_json", kwargs=self.dataset_args) - dataset_properties_cache = reverse("dataset_properties_cache_json", kwargs=self.dataset_args) - - canvas_slug = self.request.GET.get("canvas", conf.DEFAULT_EXHIBIT_CANVAS) - canvas = get_object_or_404(models.Canvas, slug=canvas_slug) - - save_form_url = reverse("exhibit_create_form", kwargs={ - "owner": self.dataset.owner, - "slug": self.dataset.slug, - "canvas": canvas.slug - }) - - return render(request, self.template_name, { - "exhibit_profile_url": profile_url, - "dataset_profile_url": dataset_profile_url, - "dataset_properties_cache_json": dataset_properties_cache, - "cancel_url": self.dataset.get_absolute_url(), - "save_form_url": save_form_url, - "data_url":data_url, - "canvas": canvas, - "dataset": self.dataset, - "owner": request.user.username, - "can_edit_dataset": request.user.has_perm("dataset.can_edit", self.dataset) - - }) - - -class ExhibitProfileUpdateView(View): - template_name="exhibit/exhibit_update.html" - - def check_permissions(self): - return self.request.user.has_perm("exhibit.can_edit", self.exhibit) - - def setup(self): - qs = models.Exhibit.objects.select_related("dataset", - "dataset__owner", - "owner") - - self.exhibit = get_object_or_404(qs, - owner__username=self.kwargs["owner"], - slug=self.kwargs["slug"]) - - self.dataset = self.exhibit.dataset - self.dataset_args = {"owner": self.dataset.owner.username, "slug": self.dataset.slug} - - def get(self, request, *args, **kwargs): - self.setup() - - profile_url = reverse("exhibit_profile_json", kwargs={"owner": self.kwargs["owner"], - "slug": self.kwargs["slug"]}) - dataset_profile_url = reverse("dataset_profile_json", kwargs=self.dataset_args) - dataset_properties_cache = reverse("dataset_properties_cache_json", kwargs=self.dataset_args) - - data_url = reverse("dataset_data_json", kwargs=self.dataset_args) - canvas = self.exhibit.canvas - - if not self.check_permissions(): - raise Http404() - - context = { - "exhibit_profile_url": profile_url, - "dataset_profile_url": dataset_profile_url, - "dataset_properties_cache_json": dataset_properties_cache, - - "cancel_url": self.exhibit.get_absolute_url(), - "data_url": data_url, - "canvas": canvas, - "owner": request.user.username, - "dataset": self.dataset, - "exhibit": self.exhibit - } - - user = self.request.user - exhibit = self.exhibit - - context["dataset_available"] = exhibit.dataset_available(user) - context["can_edit_dataset"] = user.has_perm("dataset.can_edit", self.dataset) - context["can_view"] = user.has_perm("exhibit.can_view", exhibit) - context["can_inspect"] = user.has_perm("exhibit.can_inspect", exhibit) - - context["can_edit"] = user.has_perm("exhibit.can_edit", exhibit) - context["can_delete"] = user.has_perm("exhibit.can_delete", exhibit) - return render(request, self.template_name, context) - - def post(self, request, *args, **kwargs): - self.setup() - user = request.user - - if not self.check_permissions(): - raise Http404() - - contents = json.loads(self.request.raw_post_data) - self.exhibit.update_from_profile(contents) - url = self.exhibit.get_absolute_url() - return render(request, "exhibit/edit/success.html", { - "owner": self.exhibit.owner.username, - "slug": self.exhibit.slug - }) - -############################################################################################# -# Display Views - -class ExhibitView(OwnerSlugPermissionMixin, DetailView): - - select_related = ("owner", "dataset", "dataset__owner") - - def get_queryset(self): - return models.Exhibit.objects.select_related(*self.select_related) - - object_perm = "exhibit.can_view" - template_name = "exhibit/exhibit_display.html" - - def get_context_data(self, **kwargs): - context = super(ExhibitView, self).get_context_data(**kwargs) - user = self.request.user - exhibit = self.get_object() - - context["exhibit"] = exhibit - context["dataset_available"] = exhibit.dataset_available(user) - context["can_view"] = user.has_perm("exhibit.can_view", exhibit) - context["can_inspect"] = user.has_perm("exhibit.can_inspect", exhibit) - - context["can_edit"] = user.has_perm("exhibit.can_edit", exhibit) - context["can_delete"] = user.has_perm("exhibit.can_delete", exhibit) - return context - -class ExhibitDisplayView(ExhibitView): - - select_related = ("owner", "dataset", "dataset__owner", "theme", "canvas") - - - def delete(self, request, *args, **kwargs): - exhibit = self.get_object() - if request.user.has_perm("exhibit.can_delete", exhibit): - exhibit.delete() - return HttpResponse(_("%s deleted") % exhibit.get_absolute_url()) - return HttpResponseForbidden() - - def get_context_data(self, **kwargs): - context = super(ExhibitDisplayView, self).get_context_data(**kwargs) - user = self.request.user - can_embed = user.has_perm("exhibit.can_embed", self.get_object()) - context["can_embed"] = can_embed - context["can_share"] = user.has_perm("exhibit.can_share", self.get_object()) - - if can_embed: - context["exhibit_embed_url"] = get_site_url(reverse('exhibit_embed_js', - kwargs={ - "owner": self.get_object().owner, - "slug": self.get_object().slug - })) - return context - - -class ExhibitDetailView(ExhibitView): - template_name = "exhibit/exhibit_detail.html" - object_perm = "exhibit.can_inspect" - - def get_context_data(self, **kwargs): - context = super(ExhibitDetailView, self).get_context_data(**kwargs) - user = self.request.user - can_embed = user.has_perm("exhibit.can_embed", self.get_object()) - context["can_embed"] = can_embed - context["can_share"] = user.has_perm("exhibit.can_share", self.get_object()) - if can_embed: - context["exhibit_embed_url"] = get_site_url(reverse('exhibit_embed_js', - kwargs={ - "owner": self.get_object().owner, - "slug": self.get_object().slug - })) - return context - - -def get_published_exhibit(request, owner, slug): - if not hasattr(request, "exhibit"): - qs = models.Exhibit.objects.select_related("owner", "dataset") - request.exhibit = get_object_or_404(qs, slug=slug, owner__username=owner, published=True) - return request.exhibit - - -class EmbeddedExhibitView(View): - """Generate the javascript necessary to embed an exhibit on an external site - """ - # The - template_name = "exhibit/embed/show.js" - - def get(self, request, owner, slug): - where = request.GET.get('where', 'freemix-embed') - exhibit = get_published_exhibit(request, owner, slug) - - metadata = exhibit.profile - - canvas = exhibit.canvas - canvas_html = render_to_string(canvas.location, {}).replace("\n", " ") - dataset = exhibit.dataset - - data_dict = Dataset.objects.filter(id=dataset.id).values("data__data", - "profile__data", - "properties_cache__data")[0] - - response = render(request, self.template_name, { - "data": data_dict["data__data"], - "title": exhibit.title, - "description": exhibit.description, - "metadata": json.dumps(metadata), - "data_profile": data_dict["profile__data"], - "properties": data_dict["properties_cache__data"], - "where": where, - "permalink": get_site_url(reverse("exhibit_display", - kwargs={'owner': owner, - 'slug': slug})), - "canvas": canvas_html}) - response['Content-Type'] = "application/javascript" - response['Cache-Control'] = "no-cache, must-revalidate, public" - return response - -def lmfunc(r, owner, slug): - ex = get_published_exhibit(r, owner, slug) - if ex.modified > ex.dataset.modified: - return ex.modified - return ex.dataset.modified -embedded_exhibit_view = last_modified(lmfunc)(EmbeddedExhibitView.as_view()) - -# Exhibit Profile Views - -class StockExhibitProfileJSONView(View): - """Generate the default profile description of an exhibit for a particular dataset and canvas - """ - def get(self, request, *args, **kwargs): - owner = kwargs["owner"] - slug = kwargs["slug"] - - ds = get_object_or_404(models.Dataset, owner__username=owner, slug=slug) - user = self.request.user - - if not user.has_perm("dataset.can_view", ds): - raise Http404 - - return JSONResponse({ - "theme": conf.DEFAULT_EXHIBIT_THEME, - "facets": {}, - "views": { - "views": [{ - "id": str(uuid.uuid4()), - "type": "list", - "name": "List"}]}}) - - - -def get_exhibit(request, owner, slug): - if not hasattr(request, "exhibit"): - qs = models.Exhibit.objects.select_related("owner", "dataset") - request.exhibit = get_object_or_404(qs, slug=slug, owner__username=owner) - return request.exhibit - -class ExhibitProfileJSONView(BaseJSONView): - - def get_parent_object(self): - return get_exhibit(self.request, self.kwargs["owner"], self.kwargs["slug"]) - - def get_doc(self): - ex = self.get_parent_object() - return models.Exhibit.objects.filter(id=ex.id).values_list("profile", flat=True)[0] - - def check_perms(self): - return self.request.user.has_perm("exhibit.can_view", self.get_parent_object()) - - - def cache_control_header(self): - cache_control = super(ExhibitProfileJSONView, self).cache_control_header() - if not self.request.exhibit.published: - cache_control += ", private" - else: - cache_control += ", public" - return cache_control -lmdec = last_modified(lambda request, *args, **kwargs: get_exhibit(request, kwargs["owner"], kwargs["slug"]).modified) -exhibit_profile_json_view = lmdec(ExhibitProfileJSONView.as_view()) - -# List Views - -class ExhibitListView(OwnerListView): - template_name = "exhibit/list/exhibit_list_by_owner.html" - - permission = "exhibit.can_view" - - model = models.Exhibit - - related = ("dataset", "dataset__owner", "owner", "owner__profile") - - -class ExhibitsByDatasetListView(ListView): - template_name = "exhibit/list/exhibit_list_by_dataset.html" - - def get_dataset(self): - slug = self.kwargs["slug"] - owner = self.kwargs["owner"] - return get_object_or_404(Dataset, slug=slug,owner__username=owner) - - def get_queryset(self): - perm_filter = PermissionsRegistry.get_filter("exhibit.can_view", self.request.user) - - return models.Exhibit.objects.filter(dataset=self.get_dataset()).filter(perm_filter) - - def get_context_data(self, **kwargs): - kwargs = super(ExhibitsByDatasetListView, self).get_context_data(**kwargs) - - kwargs["dataset"] = self.get_dataset() - return kwargs - - -class CanvasListView(ListView): - template_name="exhibit/canvas_list.html" - - def get_queryset(self): - return models.Canvas.objects.filter(enabled=True) - - def get_context_data(self, **kwargs): - kwargs = super(CanvasListView, self).get_context_data(**kwargs) - kwargs["base_url"] = reverse("exhibit_create_editor", - kwargs={"owner": self.kwargs["owner"], - "slug": self.kwargs["slug"]}) - return kwargs - diff --git a/freemix/models.py b/freemix/models.py deleted file mode 100644 index 44636ea2..00000000 --- a/freemix/models.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.db import models -from django_extensions.db.fields.json import JSONField - -class JSONDataModel(models.Model): - - data =JSONField() - - class Meta: - abstract=True \ No newline at end of file diff --git a/freemix/permissions.py b/freemix/permissions.py deleted file mode 100644 index 209804df..00000000 --- a/freemix/permissions.py +++ /dev/null @@ -1,159 +0,0 @@ -from django.core.exceptions import ObjectDoesNotExist -from django.db.models.expressions import F -from django.db.models.query_utils import Q - - -def generate_context_field(context): - if len(context) > 0: - context += "__" - def _f(key): - return F(context+key) - return _f - -def generate_context_filter(context): - """generates a field filter string based on the passed in context, which should be a - parent field filter. For example: - >>> _c("dataset", "name") - dataset__name - - This is useful for building generic filters for a particular model and being able to filter - querysets on models with foreign keys to it. - """ - if len(context) > 0: - context += "__" - def _q(key, value): - return Q(**{F("%s%s"%(context,key)).name: value}) - return _q - - -class PermissionsRegistry: - - _filter_registry = {} - _perm_registry = {} - - @classmethod - def register_filter(cls, perm_dict): - cls._filter_registry = dict(cls._filter_registry, **perm_dict) - - @classmethod - def register(cls, perm, callback=None, filter=None): - if filter is None: - filter = Q() - if callback: - cls._perm_registry[perm] = callback - cls._filter_registry[perm] = filter - - - @classmethod - def get_callback(cls, perm): - return cls._perm_registry.get(perm, lambda x,y: False) - - @classmethod - def get_filter(cls, perm, user, context=""): - return cls._filter_registry[perm](user, context) - - -class RegistryBackend: - supports_object_permissions=True - supports_anonymous_user = True - - def authenticate(self, username, password): - return None - - - def has_perm(self, user_obj, perm, obj=None): - if obj is None: - return False - pfunc = PermissionsRegistry.get_callback(perm) - return pfunc(user_obj, obj) - -def owner_filter(user, context=""): - _c = generate_context_filter(context) - return _c("owner", user) - -def check_owner(user_obj, obj): - return user_obj.id == obj.owner_id - -def check_published(user_obj, obj): - if obj.published: - return True - return check_owner(user_obj, obj) - -def published_query_filter(user, context=""): - _c = generate_context_filter(context) - p = _c("published", True) - if user.is_authenticated(): - return p|owner_filter(user, context) - return p - -def dataset_can_build(user, obj): - if user.is_authenticated(): - return check_published(user, obj) - return False - - -PermissionsRegistry.register('dataset.can_view', check_published, published_query_filter) -PermissionsRegistry.register('dataset.can_inspect', check_published, published_query_filter) -PermissionsRegistry.register('dataset.can_edit', check_owner, owner_filter) - -PermissionsRegistry.register('dataset.can_delete', check_owner, owner_filter) -PermissionsRegistry.register('dataset.can_build', dataset_can_build, published_query_filter) -PermissionsRegistry.register('datasource.can_view', check_owner, owner_filter) -PermissionsRegistry.register('datasource.can_edit', check_owner, owner_filter) -PermissionsRegistry.register('datasource.can_delete', check_owner, owner_filter) - -def exhibit_can_view(user, obj): - if user.is_authenticated() and check_owner(user,obj): - return True - else: - return obj.dataset_available(obj.owner) and check_published(user,obj) - - -def exhibit_can_edit(user, obj): - if user.is_authenticated() and user.id==obj.owner.id: - return obj.dataset_available(user) - return False - - - -def exhibit_can_embed(user,obj): - return obj.published - -def exhibit_embed_filter(user, context=""): - _q = generate_context_filter(context) - return _q("published", True) - - -def exhibit_view_filter(user, context=""): - _q = generate_context_filter(context) - _f = generate_context_field(context) - - owner = owner_filter(user, context) - published = _q("published", True) - - dataset_owner = _q("dataset__owner", _f("owner")) - dataset_published = _q("dataset__published", True) - - if user.is_authenticated(): - # The user is the owner of the exhibit, or has access to the dataset and the exhibit is published - return owner|((dataset_owner|dataset_published)&published) - return dataset_published&published - -def exhibit_edit_filter(user, context=""): - _q = generate_context_filter(context) - owner = owner_filter(user, context) - - dataset_owner = _q("dataset__owner", user) - dataset_published = _q("dataset__published", True) - - - if user.is_authenticated(): - return owner&(dataset_owner|dataset_published) - return _q("owner", None) - -PermissionsRegistry.register('exhibit.can_view', exhibit_can_view, exhibit_view_filter) -PermissionsRegistry.register('exhibit.can_inspect', exhibit_can_view, exhibit_view_filter) -PermissionsRegistry.register('exhibit.can_embed', exhibit_can_embed , exhibit_embed_filter) -PermissionsRegistry.register('exhibit.can_edit', exhibit_can_edit, exhibit_edit_filter) -PermissionsRegistry.register('exhibit.can_delete', check_owner, owner_filter) -PermissionsRegistry.register('exhibit.can_share', check_owner , owner_filter) diff --git a/freemix/static/dataset/augment/js/augment.js b/freemix/static/dataset/augment/js/augment.js deleted file mode 100644 index 8b1d5001..00000000 --- a/freemix/static/dataset/augment/js/augment.js +++ /dev/null @@ -1,593 +0,0 @@ -/*global jQuery */ -(function($, Freemix) { - var root; - var refreshRequired = false; - - function showDescribeMessage(id) { - $("#describe_messages li").hide(); - $("#describe_messages, #describe_messages #" + id).fadeIn().effect("highlight"); - } - - function validate() { - root.find(".error").removeClass("error"); - root.find(".errorField").hide(); - - var success = true; - if (!($.trim(root.find("#id_name").val()).length > 0)) { - $("#div_id_name").addClass("error"); - $("#div_id_name").find(".errorField").show(); - success = false; - } - if (root.find(".field_type.selected").length === 0) { - $("#div_id_type").addClass("error"); - $("#div_id_type").find(".errorField").show(); - success = false; - } - - if (success) { - success = form_handler().validate(); - } - - return success; - - } - - function form_handler() { - return root.find(".field_type.selected").data("handler"); - } - - function CompositeProperty() { - - } - - CompositeProperty.prototype = { - showForm: function() { - root.find(".pattern_form").hide(); - root.find("#edit_field_dialog_create,.composite_form").show(); - - }, - validate: function() { - var success=true; - if (!root.find("#edit_field_dialog_fields").val() || - root.find("#edit_field_dialog_fields").val().length === 0) { - - $(".composite_form #div_id_fields").addClass("error"); - $(".composite_form #div_id_fields").find(".errorField").show(); - success = false; - } - return success; - }, - serialize: function() { - return { - property: slugify(root.find("#id_name").val()), - label: root.find("#id_name").val(), - tags: ["property:type=" + root.find(".field_type.selected").attr("id")], - composite: root.find("#edit_field_dialog_fields").val(), - enabled:true - }; - }, - reset: function() { - - root.find("#edit_field_dialog_create").hide(); - root.find(".advanced_pattern").hide(); - root.find("select#edit_field_dialog_fields").empty(); - $.each(Freemix.property.enabledPropertiesArray(), function() { - var prop = this; - $("