fixed popular tags in home
Co-authored-by: Maicol Battistini <maicolbattistini@live.it>
|
@ -0,0 +1,285 @@
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,pycharm+iml
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,pycharm+iml
|
||||||
|
|
||||||
|
### PyCharm+iml ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### PyCharm+iml Patch ###
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
### Python Patch ###
|
||||||
|
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||||
|
poetry.toml
|
||||||
|
|
||||||
|
# ruff
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# LSP config files
|
||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,pycharm+iml
|
||||||
|
|
||||||
|
.git-old/*
|
||||||
|
.github
|
|
@ -0,0 +1,3 @@
|
||||||
|
ignored:
|
||||||
|
- DL3018
|
||||||
|
- DL3008
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*html": "jinja-html"
|
||||||
|
},
|
||||||
|
"emmet.includeLanguages": {
|
||||||
|
"jinja-html": "*html"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at info@keitaro.com. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
|
@ -0,0 +1,27 @@
|
||||||
|
## How to contribute to Dockerized CKAN
|
||||||
|
|
||||||
|
#### **Did you find a bug?**
|
||||||
|
|
||||||
|
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/keitaroinc/docker-ckan/issues).
|
||||||
|
|
||||||
|
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/keitaroinc/docker-ckan/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, we include an issue template to help out in filling-in the issue.
|
||||||
|
|
||||||
|
#### **Did you write a patch that fixes a bug?**
|
||||||
|
|
||||||
|
* Open a new GitHub pull request with the patch.
|
||||||
|
|
||||||
|
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
|
||||||
|
|
||||||
|
#### **Do you intend to add a new feature or change an existing one?**
|
||||||
|
|
||||||
|
* [Create a new feature issue](https://github.com/keitaroinc/docker-ckan/issues/new) using the Feature Request template and describe your proposed changes
|
||||||
|
|
||||||
|
* Submit a pull request referring to the relevant feature issue/s
|
||||||
|
|
||||||
|
#### **Do you have questions about the source code?**
|
||||||
|
|
||||||
|
* Ask any question about how to use Dockerized CKAN in our [gitter chat](https://gitter.im/keitaroinc/docker-ckan).
|
||||||
|
|
||||||
|
Thanks!
|
||||||
|
|
||||||
|
Keitaro Team
|
|
@ -0,0 +1,190 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2020 Keitaro AB
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,9 @@
|
||||||
|
VERSION := $(shell git describe --tags --exact-match 2>/dev/null || echo latest)
|
||||||
|
DOCKERHUB_NAMESPACE ?= keitaro
|
||||||
|
IMAGE := ${DOCKERHUB_NAMESPACE}/ckan:${VERSION}
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build -t ${IMAGE} rootfs
|
||||||
|
|
||||||
|
push: build
|
||||||
|
docker push ${IMAGE}
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
- [ ] Fix Filters in search pages
|
||||||
|
- [ ] fix tags page and popular tags section in homepage
|
||||||
|
- [ ] check if all requested plugins are working
|
||||||
|
- [ ]
|
|
@ -0,0 +1,115 @@
|
||||||
|
# Dockerized CKAN
|
||||||
|
|
||||||
|
[![build-status](https://github.com/keitaroinc/docker-ckan/workflows/Docker%20Image%20Build/badge.svg?branch=master)](https://github.com/keitaroinc/docker-ckan/actions) [![License][]][1] [![Docker Pulls][]][2] [![Chat on Gitter][]][3]
|
||||||
|
|
||||||
|
This repository contains base docker images, examples and docker-compose used to build and run CKAN.
|
||||||
|
|
||||||
|
We build and publish docker images built using this repository to Dockerhub:
|
||||||
|
- [CKAN docker images](https://hub.docker.com/r/keitaro/ckan).
|
||||||
|
- [Datapusher docker images](https://hub.docker.com/r/keitaro/ckan-datapusher)
|
||||||
|
|
||||||
|
and Github Container Registry:
|
||||||
|
- [CKAN docker images on GHCR](https://github.com/orgs/keitaroinc/packages/container/package/ckan)
|
||||||
|
- [Datapusher docker images on GHCR](https://github.com/orgs/keitaroinc/packages/container/package/datapusher)
|
||||||
|
|
||||||
|
Looking to run CKAN on Kubernetes? Check out our [CKAN Helm Chart](https://github.com/keitaroinc/ckan-helm)!
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Images are provided in two flavors:
|
||||||
|
- [Alpine Linux](https://alpinelinux.org/) based images
|
||||||
|
- [Ubuntu Focal](https://ubuntu.com/) based images are the ones ending with `-focal` in the tag name
|
||||||
|
|
||||||
|
The Docker containers include only the required extensions to start a CKAN instance. The docker images are built using a multi-stage docker approach in order to produce slim production grade docker images with the right libraries and configuration. This multi-stage approach allows us to build python binary wheels in the build stages that later on we install in the main stage.
|
||||||
|
|
||||||
|
Directory layout:
|
||||||
|
- [compose](./compose) - contains a docker-compose setup allowing users to spin up a CKAN setup easily using [docker-compose](https://docs.docker.com/compose/)
|
||||||
|
- [images](./images) - includes docker contexts for building all supported CKAN versions and datapusher
|
||||||
|
- [examples](./examples) - includes examples on how to extend the CKAN docker images and how to run them
|
||||||
|
|
||||||
|
## Running CKAN using docker-compose
|
||||||
|
To start CKAN using docker-compose, simply change into the *compose* directory and run
|
||||||
|
```sh
|
||||||
|
cd compose/2.9
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
Check if CKAN was succesfuly started on http://localhost:5000.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
In order to configure CKAN within docker-compose we use both build/up time variables loaded via the [.env](./compose/2.9/.env) file, and runtime variables loaded via the [.ckan-env](./compose/2.9/.ckan-env) file.
|
||||||
|
|
||||||
|
Variables in the [.env](./compose/2.9/.env) file are loaded when running `docker-compose build` and `docker-compose up`, while variables in [.ckan-env](./compose/2.9/.ckan-env) file are used withing the CKAN container at runtime to configure CKAN and CKAN extensions using [ckanext-envvars](https://github.com/okfn/ckanext-envvars).
|
||||||
|
|
||||||
|
## Extending CKAN docker images
|
||||||
|
Check some examples of extending CKAN docker images in the [examples](./examples) directory.
|
||||||
|
|
||||||
|
We recommend to use a multi-stage approach to extend the docker images that we provide here. To extend the images the following Dockerfile structure is recommended:
|
||||||
|
```docker
|
||||||
|
###################
|
||||||
|
### Extensions ####
|
||||||
|
###################
|
||||||
|
FROM ghcr.io/keitaroinc/ckan:2.9.9 as extbuild
|
||||||
|
|
||||||
|
# Switch to the root user
|
||||||
|
USER root
|
||||||
|
|
||||||
|
# Install any system packages necessary to build extensions
|
||||||
|
# Make sure we install python 3.8, cause CKAN is not compatible with 3.9
|
||||||
|
RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/v3.13/main \
|
||||||
|
python3-dev=3.8.10-r0
|
||||||
|
|
||||||
|
# Fetch and build the custom CKAN extensions
|
||||||
|
RUN pip wheel --wheel-dir=/wheels git+https://github.com/acmecorp/ckanext-acme@0.0.1#egg=ckanext-acme
|
||||||
|
|
||||||
|
############
|
||||||
|
### MAIN ###
|
||||||
|
############
|
||||||
|
FROM ghcr.io/keitaroinc/ckan:2.9.9
|
||||||
|
|
||||||
|
# Add the custom extensions to the plugins list
|
||||||
|
ENV CKAN__PLUGINS envvars image_view text_view recline_view datastore datapusher acme
|
||||||
|
|
||||||
|
# Switch to the root user
|
||||||
|
USER root
|
||||||
|
|
||||||
|
COPY --from=extbuild /wheels /srv/app/ext_wheels
|
||||||
|
|
||||||
|
# Install and enable the custom extensions
|
||||||
|
RUN pip install --no-index --find-links=/srv/app/ext_wheels ckanext-acme && \
|
||||||
|
ckan config-tool ${APP_DIR}/production.ini "ckan.plugins = ${CKAN__PLUGINS}" && \
|
||||||
|
chown -R ckan:ckan /srv/app
|
||||||
|
|
||||||
|
# Remove wheels
|
||||||
|
RUN rm -rf /srv/app/ext_wheels
|
||||||
|
|
||||||
|
# Switch to the ckan user
|
||||||
|
USER ckan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding init and afterinit scripts
|
||||||
|
You can add scripts to CKAN custom images and copy them to the *docker-entrypoint.d* directory. Any *.sh or *.py file in that directory will be executed before the main initialization script (prerun.py) is executed.
|
||||||
|
|
||||||
|
You can add scripts to CKAN custom images and copy them to the *docker-afterinit.d* directory. Any *.sh or *.py file in that directory will be executed after the main initialization script (prerun.py) is executed.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
To build a CKAN image run:
|
||||||
|
```sh
|
||||||
|
docker build --tag ghcr.io/keitaroinc/ckan:2.9.9 images/ckan/2.9
|
||||||
|
```
|
||||||
|
The –-tag ghcr.io/keitaroinc/ckan:2.9.9 flag sets the image name to ghcr.io/keitaroinc/ckan:2.9.9 and 'images/ckan/2.9' at the end tells docker build to use the context into the specified directory where the Dockerfile and related contents are.
|
||||||
|
|
||||||
|
## Upload to DockerHub
|
||||||
|
>*It's recommended to upload built images to DockerHub*
|
||||||
|
|
||||||
|
To upload the image to DockerHub run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker push [options] <docker-hub-namespace>/ckan:<image-tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
[License]: https://img.shields.io/badge/license-Apache--2.0-blue.svg?style=flat
|
||||||
|
[1]: https://opensource.org/licenses/Apache-2.0
|
||||||
|
[Docker Pulls]: https://img.shields.io/docker/pulls/keitaro/ckan.svg?style=flat
|
||||||
|
[2]: https://hub.docker.com/r/keitaro/ckan
|
||||||
|
[Chat on Gitter]: https://badges.gitter.im/gitterHQ/gitter.svg
|
||||||
|
[3]: https://gitter.im/keitaroinc/docker-ckan
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Runtime configuration of CKAN enabled through ckanext-envvars
|
||||||
|
# Information about how it works: https://github.com/okfn/ckanext-envvars
|
||||||
|
# Note that variables here take presedence over build/up time variables in .env
|
||||||
|
|
||||||
|
DEVELOPMENT=true
|
||||||
|
|
||||||
|
# Set to true to disable CKAN from starting and serve a maintenance page
|
||||||
|
MAINTENANCE_MODE=false
|
||||||
|
|
||||||
|
# General Settings
|
||||||
|
CKAN_SITE_ID=default
|
||||||
|
CKAN_SITE_URL=http://localhost:5000
|
||||||
|
CKAN_PORT=5000
|
||||||
|
CKAN_MAX_UPLOAD_SIZE_MB=200
|
||||||
|
CKAN___BEAKER__SESSION__SECRET=CHANGE_ME
|
||||||
|
# See https://docs.ckan.org/en/latest/maintaining/configuration.html#api-token-settings
|
||||||
|
CKAN___API_TOKEN__JWT__ENCODE__SECRET=string:CHANGE_ME
|
||||||
|
CKAN___API_TOKEN__JWT__DECODE__SECRET=string:CHANGE_ME
|
||||||
|
# CKAN Plugins
|
||||||
|
CKAN__PLUGINS="envvars image_view text_view recline_view datastore datapusher spatial_metadata spatial_query d4science d4science_theme"
|
||||||
|
# CKAN requires storage path to be set in order for filestore to be enabled
|
||||||
|
CKAN__STORAGE_PATH=/srv/app/data
|
||||||
|
CKAN__WEBASSETS__PATH=/srv/app/data/webassets
|
||||||
|
# SYSADMIN settings, a sysadmin user is created automatically with the below credentials
|
||||||
|
CKAN_SYSADMIN_NAME=sysadmin
|
||||||
|
CKAN_SYSADMIN_PASSWORD=password
|
||||||
|
CKAN_SYSADMIN_EMAIL=sysadmin@ckantest.com
|
||||||
|
|
||||||
|
# Email settings
|
||||||
|
CKAN_SMTP_SERVER=smtp.corporateict.domain:25
|
||||||
|
CKAN_SMTP_STARTTLS=True
|
||||||
|
CKAN_SMTP_USER=user
|
||||||
|
CKAN_SMTP_PASSWORD=pass
|
||||||
|
CKAN_SMTP_MAIL_FROM=ckan@localhost
|
||||||
|
|
||||||
|
# Datapusher configuration
|
||||||
|
CKAN__DATAPUSHER__URL=http://datapusher:8000
|
||||||
|
CKAN__DATAPUSHER__CALLBACK_URL_BASE=http://ckan:5000/
|
||||||
|
CKAN__DATAPUSHER__API_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ6M0lGRGw4SVdFR3lVRzFYRHJ0cm56WmNmcDlzbmFuUHlUMjdqLXRVSW9nIiwiaWF0IjoxNjc3MDYwNjQ1fQ.ECCa0wU-FOOGpdE9_TTVj__2J9SfANRBD6gRClBvPk0
|
||||||
|
# CKAN__DATAPUSHER__API_TOKEN=replace_this_with_api_token_once_ckan_starts
|
||||||
|
|
||||||
|
# Solr configuration
|
||||||
|
CKAN_VERSION=2.10
|
||||||
|
CKAN_CORE_NAME=ckan
|
|
@ -0,0 +1,39 @@
|
||||||
|
###################
|
||||||
|
### Extensions
|
||||||
|
###################
|
||||||
|
FROM registry.trust-itservices.com/d4science/ckan-docker/ckan:2.10.4-focal as extbuild
|
||||||
|
|
||||||
|
# Switch to the root user
|
||||||
|
USER root
|
||||||
|
|
||||||
|
#install pip
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install python3-dev python3-pip libxml2-dev libxslt1-dev libgeos-c1v5 -y
|
||||||
|
|
||||||
|
############
|
||||||
|
### MAIN
|
||||||
|
############
|
||||||
|
FROM registry.trust-itservices.com/d4science/ckan-docker/ckan:2.10.4-focal
|
||||||
|
|
||||||
|
# Add the custom extensions to the plugins list
|
||||||
|
ENV CKAN__PLUGINS envvars image_view text_view recline_view datastore datapusher spatial_metadata spatial_query d4science d4science_theme
|
||||||
|
|
||||||
|
# Switch to the root user
|
||||||
|
USER root
|
||||||
|
|
||||||
|
#COPY --from=extbuild /wheels /srv/app/ext_wheels
|
||||||
|
|
||||||
|
# Add requirements.txt to the image
|
||||||
|
COPY requirements.txt /srv/app/requirements.txt
|
||||||
|
|
||||||
|
# Install and enable the custom extensions
|
||||||
|
RUN pip install -r /srv/app/requirements.txt
|
||||||
|
|
||||||
|
RUN ckan config-tool ${APP_DIR}/production.ini "ckan.plugins = ${CKAN__PLUGINS}" && \
|
||||||
|
chown -R ckan:ckan /srv/app
|
||||||
|
|
||||||
|
# Remove wheels
|
||||||
|
RUN rm -rf /srv/app/ext_wheels
|
||||||
|
|
||||||
|
# Switch to the ckan user
|
||||||
|
# USER ckan
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "../.."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
# docker-compose build && docker-compose up -d
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ckan_data:
|
||||||
|
pg_data:
|
||||||
|
solr_data:
|
||||||
|
|
||||||
|
services:
|
||||||
|
ckan:
|
||||||
|
container_name: ckan
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
- solr
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:${CKAN_PORT}:5000"
|
||||||
|
env_file:
|
||||||
|
- ./.ckan.env
|
||||||
|
environment:
|
||||||
|
- CKAN_SQLALCHEMY_URL=postgresql://ckan:${POSTGRES_PASSWORD}@db/ckan
|
||||||
|
- CKAN_DATASTORE_WRITE_URL=postgresql://ckan:${POSTGRES_PASSWORD}@db/datastore
|
||||||
|
- CKAN_DATASTORE_READ_URL=postgresql://datastore_ro:${DATASTORE_READONLY_PASSWORD}@db/datastore
|
||||||
|
- CKAN_SOLR_URL=http://solr:8983/solr/ckan
|
||||||
|
- CKAN_REDIS_URL=redis://redis:6379/1
|
||||||
|
- CKAN_SITE_URL=${CKAN_SITE_URL}
|
||||||
|
- CKAN_MAX_UPLOAD_SIZE_MB=${CKAN_MAX_UPLOAD_SIZE_MB}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- DS_RO_PASS=${DATASTORE_READONLY_PASSWORD}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ckan_data:/srv/app/data
|
||||||
|
- ./src:/srv/app/src_extensions
|
||||||
|
- ./docker-entrypoint.d:/srv/app/docker-entrypoint.d
|
||||||
|
|
||||||
|
datapusher:
|
||||||
|
container_name: datapusher
|
||||||
|
image: ghcr.io/keitaroinc/datapusher:${DATAPUSHER_VERSION}
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
environment:
|
||||||
|
- DATAPUSHER_MAX_CONTENT_LENGTH=${DATAPUSHER_MAX_CONTENT_LENGTH}
|
||||||
|
- DATAPUSHER_CHUNK_SIZE=${DATAPUSHER_CHUNK_SIZE}
|
||||||
|
- DATAPUSHER_CHUNK_INSERT_ROWS=${DATAPUSHER_CHUNK_INSERT_ROWS}
|
||||||
|
- DATAPUSHER_DOWNLOAD_TIMEOUT=${DATAPUSHER_DOWNLOAD_TIMEOUT}
|
||||||
|
- DATAPUSHER_SSL_VERIFY=${DATA_PUSHER_SSL_VERIFY}
|
||||||
|
- DATAPUSHER_REWRITE_RESOURCES=${DATAPUSHER_REWRITE_RESOURCES}
|
||||||
|
- DATAPUSHER_REWRITE_URL=${DATAPUSHER_REWRITE_URL}
|
||||||
|
|
||||||
|
|
||||||
|
db:
|
||||||
|
container_name: db
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: postgresql/Dockerfile
|
||||||
|
args:
|
||||||
|
- DS_RO_PASS=${DATASTORE_READONLY_PASSWORD}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
environment:
|
||||||
|
- DS_RO_PASS=${DATASTORE_READONLY_PASSWORD}
|
||||||
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||||
|
- PGDATA=/var/lib/postgresql/data/db
|
||||||
|
volumes:
|
||||||
|
- pg_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "ckan"]
|
||||||
|
|
||||||
|
solr:
|
||||||
|
container_name: solr
|
||||||
|
image: ckan/ckan-solr:2.10-solr9-spatial
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
env_file:
|
||||||
|
- ./.ckan.env
|
||||||
|
environment:
|
||||||
|
- CKAN_CORE_NAME=${CKAN_CORE_NAME}
|
||||||
|
- CKAN_VERSION=${CKAN_VERSION}
|
||||||
|
volumes:
|
||||||
|
- solr_data:/var/solr
|
||||||
|
- ${PWD}/solr8/ckan_init_solr.sh:/docker-entrypoint-initdb.d/ckan_init_solr.sh
|
||||||
|
|
||||||
|
redis:
|
||||||
|
container_name: redis
|
||||||
|
image: redis:${REDIS_VERSION}
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
backend:
|
|
@ -0,0 +1 @@
|
||||||
|
ckan config-tool ${APP_DIR}/production.ini "ckan.devserver.watch_patterns = **/*.py **/*.html"
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ $CKAN__PLUGINS == *"spatial_metadata"* ]]; then
|
||||||
|
echo "Configuring CKAN spatial ..."
|
||||||
|
|
||||||
|
ckan config-tool ${APP_DIR}/production.ini "ckanext.spatial.common_map.type=Stadia.StamenTerrain"
|
||||||
|
ckan config-tool ${APP_DIR}/production.ini "ckanext.spatial.common_map.max_zoom=18"
|
||||||
|
# ckan config-tool $CKAN_INI "ckanext.spatial.search_backend = solr-spatial-field"
|
||||||
|
ckan config-tool ${APP_DIR}/production.ini "ckan.search.solr_allowed_query_parsers = frange"
|
||||||
|
# forse è da aggiungere un qualche tipo di key per production
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Not configuring spatial, plugin not enabled."
|
||||||
|
fi
|
|
@ -0,0 +1,42 @@
|
||||||
|
SRC_EXTENSIONS_DIR=/srv/app/src_extensions
|
||||||
|
|
||||||
|
# Install any local extensions in the src_extensions volume
|
||||||
|
echo "Looking for local extensions to install..."
|
||||||
|
echo "Extension dir ($SRC_EXTENSIONS_DIR) contents:"
|
||||||
|
ls -la $SRC_EXTENSIONS_DIR
|
||||||
|
for i in $SRC_EXTENSIONS_DIR/*
|
||||||
|
do
|
||||||
|
if [ -d $i ];
|
||||||
|
then
|
||||||
|
|
||||||
|
if [ -f $i/pip-requirements.txt ];
|
||||||
|
then
|
||||||
|
pip install -r $i/pip-requirements.txt
|
||||||
|
echo "Found requirements file in $i"
|
||||||
|
fi
|
||||||
|
if [ -f $i/requirements.txt ];
|
||||||
|
then
|
||||||
|
pip install -r $i/requirements.txt
|
||||||
|
echo "Found requirements file in $i"
|
||||||
|
fi
|
||||||
|
if [ -f $i/dev-requirements.txt ];
|
||||||
|
then
|
||||||
|
pip install -r $i/dev-requirements.txt
|
||||||
|
echo "Found dev-requirements file in $i"
|
||||||
|
fi
|
||||||
|
if [ -f $i/setup.py ];
|
||||||
|
then
|
||||||
|
cd $i
|
||||||
|
python3 $i/setup.py develop
|
||||||
|
echo "Found setup.py file in $i"
|
||||||
|
cd $APP_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Point `use` in test.ini to location of `test-core.ini`
|
||||||
|
if [ -f $i/test.ini ];
|
||||||
|
then
|
||||||
|
echo "Updating \`test.ini\` reference to \`test-core.ini\` for plugin $i"
|
||||||
|
ckan config-tool $i/test.ini "use = config:../../src/ckan/test-core.ini"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
|
@ -0,0 +1,13 @@
|
||||||
|
FROM postgis/postgis:14-3.2-alpine
|
||||||
|
|
||||||
|
# Allow connections; we don't map out any ports so only linked docker containers can connect
|
||||||
|
RUN echo "host all all 0.0.0.0/0 md5" >> /var/lib/postgresql/data/pg_hba.conf
|
||||||
|
|
||||||
|
# Customize default user/pass/db
|
||||||
|
ENV POSTGRES_DB ckan
|
||||||
|
ENV POSTGRES_USER ckan
|
||||||
|
ARG POSTGRES_PASSWORD
|
||||||
|
ARG DS_RO_PASS
|
||||||
|
|
||||||
|
# Include datastore setup scripts
|
||||||
|
COPY ./postgresql/docker-entrypoint-initdb.d /docker-entrypoint-initdb.d
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
|
||||||
|
CREATE ROLE datastore_ro NOSUPERUSER NOCREATEDB NOCREATEROLE LOGIN PASSWORD '$DS_RO_PASS';
|
||||||
|
CREATE DATABASE datastore OWNER ckan ENCODING 'utf-8';
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE datastore TO ckan;
|
||||||
|
EOSQL
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER VIEW geometry_columns OWNER TO ckan;
|
||||||
|
ALTER TABLE spatial_ref_sys OWNER TO ckan;
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Spatial
|
||||||
|
-e git+https://github.com/ckan/ckanext-spatial.git@v2.1.1#egg=ckanext-spatial
|
||||||
|
-r https://raw.githubusercontent.com/ckan/ckanext-spatial/v2.1.1/requirements.txt
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Initialize SOLR for CKAN by creating a ckan core
|
||||||
|
# Arguments are supplied via environment variables: CKAN_CORE_NAME CKAN_VERSION
|
||||||
|
# Example:
|
||||||
|
# CKAN_CORE_NAME=ckan
|
||||||
|
# CKAN_VERSION=2.9.7
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
MAJOR_VERSION=`echo $CKAN_VERSION | cut -d "." -f 1`
|
||||||
|
MINOR_VERSION=`echo $CKAN_VERSION | cut -d "." -f 2`
|
||||||
|
|
||||||
|
CKAN_SOLR_SCHEMA_URL=https://raw.githubusercontent.com/ckan/ckan/`echo $MAJOR_VERSION.$MINOR_VERSION`/ckan/config/solr/schema.xml
|
||||||
|
|
||||||
|
echo "Check whether managed schema exists for CKAN $CKAN_VERSION"
|
||||||
|
if ! curl --output /dev/null --silent --head --fail "$CKAN_SOLR_SCHEMA_URL"; then
|
||||||
|
echo "Can't find CKAN SOLR schema at URL: $CKAN_SOLR_SCHEMA_URL. Exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Check whether SOLR is initialized for CKAN"
|
||||||
|
CORESDIR=/var/solr/data
|
||||||
|
|
||||||
|
COREDIR="$CORESDIR/$CKAN_CORE_NAME"
|
||||||
|
if [ -d "$COREDIR" ]; then
|
||||||
|
echo "SOLR already initialized, skipping initialization"
|
||||||
|
else
|
||||||
|
echo "Initializing SOLR core $CKAN_CORE_NAME for CKAN $CKAN_VERSION"
|
||||||
|
# init script for handling an empty /var/solr
|
||||||
|
/opt/docker-solr/scripts/init-var-solr
|
||||||
|
|
||||||
|
# Precreate CKAN core
|
||||||
|
/opt/docker-solr/scripts/precreate-core $CKAN_CORE_NAME
|
||||||
|
|
||||||
|
# Replace the managed schema with CKANs schema
|
||||||
|
echo "Adding CKAN managed schema"
|
||||||
|
curl $CKAN_SOLR_SCHEMA_URL -o /var/solr/data/$CKAN_CORE_NAME/conf/managed-schema -s
|
||||||
|
|
||||||
|
echo "SOLR initialized"
|
||||||
|
fi
|
|
@ -0,0 +1,5 @@
|
||||||
|
[report]
|
||||||
|
omit =
|
||||||
|
*/site-packages/*
|
||||||
|
*/python?.?/*
|
||||||
|
ckan/*
|
|
@ -0,0 +1,42 @@
|
||||||
|
.ropeproject
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
sdist/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
|
@ -0,0 +1,661 @@
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,5 @@
|
||||||
|
include README.rst
|
||||||
|
include LICENSE
|
||||||
|
include requirements.txt
|
||||||
|
recursive-include ckanext/d4science *.html *.json *.js *.less *.css *.mo *.yml
|
||||||
|
recursive-include ckanext/d4science/migration *.ini *.py *.mako
|
|
@ -0,0 +1,123 @@
|
||||||
|
[![Tests](https://github.com/d4science/ckanext-d4science/workflows/Tests/badge.svg?branch=main)](https://github.com/d4science/ckanext-d4science/actions)
|
||||||
|
|
||||||
|
# ckanext-d4science
|
||||||
|
|
||||||
|
**TODO:** Put a description of your extension here: What does it do? What features does it have? Consider including some screenshots or embedding a video!
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
**TODO:** For example, you might want to mention here which versions of CKAN this
|
||||||
|
extension works with.
|
||||||
|
|
||||||
|
If your extension works across different versions you can add the following table:
|
||||||
|
|
||||||
|
Compatibility with core CKAN versions:
|
||||||
|
|
||||||
|
| CKAN version | Compatible? |
|
||||||
|
| --------------- | ------------- |
|
||||||
|
| 2.6 and earlier | not tested |
|
||||||
|
| 2.7 | not tested |
|
||||||
|
| 2.8 | not tested |
|
||||||
|
| 2.9 | not tested |
|
||||||
|
|
||||||
|
Suggested values:
|
||||||
|
|
||||||
|
* "yes"
|
||||||
|
* "not tested" - I can't think of a reason why it wouldn't work
|
||||||
|
* "not yet" - there is an intention to get it working
|
||||||
|
* "no"
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
**TODO:** Add any additional install steps to the list below.
|
||||||
|
For example installing any non-Python dependencies or adding any required
|
||||||
|
config settings.
|
||||||
|
|
||||||
|
To install ckanext-d4science:
|
||||||
|
|
||||||
|
1. Activate your CKAN virtual environment, for example:
|
||||||
|
|
||||||
|
. /usr/lib/ckan/default/bin/activate
|
||||||
|
|
||||||
|
2. Clone the source and install it on the virtualenv
|
||||||
|
|
||||||
|
git clone https://github.com/d4science/ckanext-d4science.git
|
||||||
|
cd ckanext-d4science
|
||||||
|
pip install -e .
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
3. Add `d4science` to the `ckan.plugins` setting in your CKAN
|
||||||
|
config file (by default the config file is located at
|
||||||
|
`/etc/ckan/default/ckan.ini`).
|
||||||
|
|
||||||
|
4. Restart CKAN. For example if you've deployed CKAN with Apache on Ubuntu:
|
||||||
|
|
||||||
|
sudo service apache2 reload
|
||||||
|
|
||||||
|
|
||||||
|
## Config settings
|
||||||
|
|
||||||
|
None at present
|
||||||
|
|
||||||
|
**TODO:** Document any optional config settings here. For example:
|
||||||
|
|
||||||
|
# The minimum number of hours to wait before re-checking a resource
|
||||||
|
# (optional, default: 24).
|
||||||
|
ckanext.d4science.some_setting = some_default_value
|
||||||
|
|
||||||
|
|
||||||
|
## Developer installation
|
||||||
|
|
||||||
|
To install ckanext-d4science for development, activate your CKAN virtualenv and
|
||||||
|
do:
|
||||||
|
|
||||||
|
git clone https://github.com/d4science/ckanext-d4science.git
|
||||||
|
cd ckanext-d4science
|
||||||
|
python setup.py develop
|
||||||
|
pip install -r dev-requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
To run the tests, do:
|
||||||
|
|
||||||
|
pytest --ckan-ini=test.ini
|
||||||
|
|
||||||
|
|
||||||
|
## Releasing a new version of ckanext-d4science
|
||||||
|
|
||||||
|
If ckanext-d4science should be available on PyPI you can follow these steps to publish a new version:
|
||||||
|
|
||||||
|
1. Update the version number in the `setup.py` file. See [PEP 440](http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers) for how to choose version numbers.
|
||||||
|
|
||||||
|
2. Make sure you have the latest version of necessary packages:
|
||||||
|
|
||||||
|
pip install --upgrade setuptools wheel twine
|
||||||
|
|
||||||
|
3. Create a source and binary distributions of the new version:
|
||||||
|
|
||||||
|
python setup.py sdist bdist_wheel && twine check dist/*
|
||||||
|
|
||||||
|
Fix any errors you get.
|
||||||
|
|
||||||
|
4. Upload the source distribution to PyPI:
|
||||||
|
|
||||||
|
twine upload dist/*
|
||||||
|
|
||||||
|
5. Commit any outstanding changes:
|
||||||
|
|
||||||
|
git commit -a
|
||||||
|
git push
|
||||||
|
|
||||||
|
6. Tag the new release of the project on GitHub with the version number from
|
||||||
|
the `setup.py` file. For example if the version number in `setup.py` is
|
||||||
|
0.0.1 then do:
|
||||||
|
|
||||||
|
git tag 0.0.1
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[AGPL](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
@ -0,0 +1,9 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# this is a namespace package
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
pkg_resources.declare_namespace(__name__)
|
||||||
|
except ImportError:
|
||||||
|
import pkgutil
|
||||||
|
__path__ = pkgutil.extend_path(__path__, __name__)
|
|
@ -0,0 +1,10 @@
|
||||||
|
ckan.module("d4science-module", function ($, _) {
|
||||||
|
"use strict";
|
||||||
|
return {
|
||||||
|
options: {
|
||||||
|
debug: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function () {},
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
body {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,14 @@
|
||||||
|
# d4science-js:
|
||||||
|
# filter: rjsmin
|
||||||
|
# output: ckanext-d4science/%(version)s-d4science.js
|
||||||
|
# contents:
|
||||||
|
# - js/d4science.js
|
||||||
|
# extra:
|
||||||
|
# preload:
|
||||||
|
# - base/main
|
||||||
|
|
||||||
|
# d4science-css:
|
||||||
|
# filter: cssrewrite
|
||||||
|
# output: ckanext-d4science/%(version)s-d4science.css
|
||||||
|
# contents:
|
||||||
|
# - css/d4science.css
|
|
@ -0,0 +1,20 @@
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
@click.group(short_help="d4science CLI.")
|
||||||
|
def d4science():
|
||||||
|
"""d4science CLI.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@d4science.command()
|
||||||
|
@click.argument("name", default="d4science")
|
||||||
|
def command(name):
|
||||||
|
"""Docs.
|
||||||
|
"""
|
||||||
|
click.echo("Hello, {name}!".format(name=name))
|
||||||
|
|
||||||
|
|
||||||
|
def get_commands():
|
||||||
|
return [d4science]
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
def d4science_hello():
|
||||||
|
return "Hello, d4science!"
|
||||||
|
|
||||||
|
|
||||||
|
def get_helpers():
|
||||||
|
return {
|
||||||
|
"d4science_hello": d4science_hello,
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
import ckanext.d4science.logic.schema as schema
|
||||||
|
|
||||||
|
|
||||||
|
@tk.side_effect_free
|
||||||
|
def d4science_get_sum(context, data_dict):
|
||||||
|
tk.check_access(
|
||||||
|
"d4science_get_sum", context, data_dict)
|
||||||
|
data, errors = tk.navl_validate(
|
||||||
|
data_dict, schema.d4science_get_sum(), context)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise tk.ValidationError(errors)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": data["left"],
|
||||||
|
"right": data["right"],
|
||||||
|
"sum": data["left"] + data["right"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_actions():
|
||||||
|
return {
|
||||||
|
'd4science_get_sum': d4science_get_sum,
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
|
|
||||||
|
@tk.auth_allow_anonymous_access
|
||||||
|
def d4science_get_sum(context, data_dict):
|
||||||
|
return {"success": True}
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_functions():
|
||||||
|
return {
|
||||||
|
"d4science_get_sum": d4science_get_sum,
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
|
|
||||||
|
def d4science_get_sum():
|
||||||
|
not_empty = tk.get_validator("not_empty")
|
||||||
|
convert_int = tk.get_validator("convert_int")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": [not_empty, convert_int],
|
||||||
|
"right": [not_empty, convert_int]
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
|
|
||||||
|
def d4science_required(value):
|
||||||
|
if not value or value is tk.missing:
|
||||||
|
raise tk.Invalid(tk._("Required"))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def get_validators():
|
||||||
|
return {
|
||||||
|
"d4science_required": d4science_required,
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import ckan.plugins as plugins
|
||||||
|
import ckan.plugins.toolkit as toolkit
|
||||||
|
from ckan.views.resource import Blueprint
|
||||||
|
from ckanext.d4science import helpers
|
||||||
|
from ckanext.d4science.logic import action, auth
|
||||||
|
from ckanext.d4science.logic import validators
|
||||||
|
|
||||||
|
|
||||||
|
# import ckanext.d4science.cli as cli
|
||||||
|
# import ckanext.d4science.helpers as helpers
|
||||||
|
# import ckanext.d4science.views as views
|
||||||
|
# from ckanext.d4science.logic import (
|
||||||
|
# action, auth, validators
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
class D4SciencePlugin(plugins.SingletonPlugin):
|
||||||
|
plugins.implements(plugins.IConfigurer)
|
||||||
|
|
||||||
|
plugins.implements(plugins.IAuthFunctions)
|
||||||
|
plugins.implements(plugins.IActions)
|
||||||
|
# plugins.implements(plugins.IClick)
|
||||||
|
plugins.implements(plugins.ITemplateHelpers)
|
||||||
|
plugins.implements(plugins.IValidators)
|
||||||
|
|
||||||
|
#ckan 2.10
|
||||||
|
plugins.implements(plugins.IBlueprint)
|
||||||
|
|
||||||
|
|
||||||
|
# IConfigurer
|
||||||
|
|
||||||
|
def update_config(self, config_):
|
||||||
|
toolkit.add_template_directory(config_, "templates")
|
||||||
|
toolkit.add_public_directory(config_, "public")
|
||||||
|
toolkit.add_resource("assets", "d4science")
|
||||||
|
|
||||||
|
|
||||||
|
# IAuthFunctions
|
||||||
|
|
||||||
|
def get_auth_functions(self):
|
||||||
|
return auth.get_auth_functions()
|
||||||
|
|
||||||
|
# IActions
|
||||||
|
|
||||||
|
def get_actions(self):
|
||||||
|
return action.get_actions()
|
||||||
|
|
||||||
|
# IBlueprint
|
||||||
|
|
||||||
|
# def get_blueprint(self):
|
||||||
|
# return views.get_blueprints()
|
||||||
|
|
||||||
|
def get_blueprint(self):
|
||||||
|
blueprint = Blueprint('foo', self.__module__)
|
||||||
|
# rules = [
|
||||||
|
# ('/group', 'group_index', custom_group_index),
|
||||||
|
# ]
|
||||||
|
# for rule in rules:
|
||||||
|
# blueprint.add_url_rule(*rule)
|
||||||
|
|
||||||
|
return blueprint
|
||||||
|
|
||||||
|
# IClick
|
||||||
|
|
||||||
|
# def get_commands(self):
|
||||||
|
# return cli.get_commands()
|
||||||
|
|
||||||
|
# ITemplateHelpers
|
||||||
|
|
||||||
|
def get_helpers(self):
|
||||||
|
return helpers.get_helpers()
|
||||||
|
|
||||||
|
# IValidators
|
||||||
|
|
||||||
|
def get_validators(self):
|
||||||
|
return validators.get_validators()
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""Tests for action.py."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import ckan.tests.helpers as test_helpers
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.ckan_config("ckan.plugins", "d4science")
|
||||||
|
@pytest.mark.usefixtures("with_plugins")
|
||||||
|
def test_d4science_get_sum():
|
||||||
|
result = test_helpers.call_action(
|
||||||
|
"d4science_get_sum", left=10, right=30)
|
||||||
|
assert result["sum"] == 40
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""Tests for auth.py."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import ckan.tests.factories as factories
|
||||||
|
import ckan.tests.helpers as test_helpers
|
||||||
|
import ckan.model as model
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.ckan_config("ckan.plugins", "d4science")
|
||||||
|
@pytest.mark.usefixtures("with_request_context", "with_plugins", "clean_db")
|
||||||
|
def test_d4science_get_sum():
|
||||||
|
user = factories.User()
|
||||||
|
context = {
|
||||||
|
"user": user["name"],
|
||||||
|
"model": model
|
||||||
|
}
|
||||||
|
assert test_helpers.call_auth(
|
||||||
|
"d4science_get_sum", context=context)
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""Tests for validators.py."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
|
from ckanext.d4science.logic import validators
|
||||||
|
|
||||||
|
|
||||||
|
def test_d4science_reauired_with_valid_value():
|
||||||
|
assert validators.d4science_required("value") == "value"
|
||||||
|
|
||||||
|
|
||||||
|
def test_d4science_reauired_with_invalid_value():
|
||||||
|
with pytest.raises(tk.Invalid):
|
||||||
|
validators.d4science_required(None)
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""Tests for helpers.py."""
|
||||||
|
|
||||||
|
import ckanext.d4science.helpers as helpers
|
||||||
|
|
||||||
|
|
||||||
|
def test_d4science_hello():
|
||||||
|
assert helpers.d4science_hello() == "Hello, d4science!"
|
|
@ -0,0 +1,56 @@
|
||||||
|
"""
|
||||||
|
Tests for plugin.py.
|
||||||
|
|
||||||
|
Tests are written using the pytest library (https://docs.pytest.org), and you
|
||||||
|
should read the testing guidelines in the CKAN docs:
|
||||||
|
https://docs.ckan.org/en/2.9/contributing/testing.html
|
||||||
|
|
||||||
|
To write tests for your extension you should install the pytest-ckan package:
|
||||||
|
|
||||||
|
pip install pytest-ckan
|
||||||
|
|
||||||
|
This will allow you to use CKAN specific fixtures on your tests.
|
||||||
|
|
||||||
|
For instance, if your test involves database access you can use `clean_db` to
|
||||||
|
reset the database:
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ckan.tests import factories
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("clean_db")
|
||||||
|
def test_some_action():
|
||||||
|
|
||||||
|
dataset = factories.Dataset()
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
For functional tests that involve requests to the application, you can use the
|
||||||
|
`app` fixture:
|
||||||
|
|
||||||
|
from ckan.plugins import toolkit
|
||||||
|
|
||||||
|
def test_some_endpoint(app):
|
||||||
|
|
||||||
|
url = toolkit.url_for('myblueprint.some_endpoint')
|
||||||
|
|
||||||
|
response = app.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
To temporary patch the CKAN configuration for the duration of a test you can use:
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.mark.ckan_config("ckanext.myext.some_key", "some_value")
|
||||||
|
def test_some_action():
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
import ckanext.d4science.plugin as plugin
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.ckan_config("ckan.plugins", "d4science")
|
||||||
|
@pytest.mark.usefixtures("with_plugins")
|
||||||
|
def test_plugin():
|
||||||
|
assert plugin_loaded("d4science")
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""Tests for views.py."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import ckanext.d4science.validators as validators
|
||||||
|
|
||||||
|
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.ckan_config("ckan.plugins", "d4science")
|
||||||
|
@pytest.mark.usefixtures("with_plugins")
|
||||||
|
def test_d4science_blueprint(app, reset_db):
|
||||||
|
resp = app.get(tk.h.url_for("d4science.page"))
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.body == "Hello, d4science!"
|
|
@ -0,0 +1,17 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
|
||||||
|
d4science = Blueprint(
|
||||||
|
"d4science", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def page():
|
||||||
|
return "Hello, d4science!"
|
||||||
|
|
||||||
|
|
||||||
|
d4science.add_url_rule(
|
||||||
|
"/d4science/page", view_func=page)
|
||||||
|
|
||||||
|
|
||||||
|
def get_blueprints():
|
||||||
|
return [d4science]
|
|
@ -0,0 +1 @@
|
||||||
|
pytest-ckan
|
|
@ -0,0 +1,62 @@
|
||||||
|
[metadata]
|
||||||
|
name = ckanext-d4science
|
||||||
|
version = 0.0.1
|
||||||
|
description = d4science custom extensions
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
url = https://github.com/d4science/ckanext-d4science
|
||||||
|
author = d4science
|
||||||
|
author_email = info@d4science.org
|
||||||
|
license = AGPL
|
||||||
|
classifiers =
|
||||||
|
Development Status :: 4 - Beta
|
||||||
|
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
||||||
|
Programming Language :: Python :: 3.7
|
||||||
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
keywords = CKAN
|
||||||
|
|
||||||
|
[options]
|
||||||
|
packages = find:
|
||||||
|
namespace_packages = ckanext
|
||||||
|
install_requires =
|
||||||
|
include_package_data = True
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
ckan.plugins =
|
||||||
|
d4science = ckanext.d4science.plugin:D4SciencePlugin
|
||||||
|
|
||||||
|
babel.extractors =
|
||||||
|
ckan = ckan.lib.extract:extract_ckan
|
||||||
|
|
||||||
|
[options.extras_require]
|
||||||
|
|
||||||
|
[extract_messages]
|
||||||
|
keywords = translate isPlural
|
||||||
|
add_comments = TRANSLATORS:
|
||||||
|
output_file = ckanext/d4science/i18n/ckanext-d4science.pot
|
||||||
|
width = 80
|
||||||
|
|
||||||
|
[init_catalog]
|
||||||
|
domain = ckanext-d4science
|
||||||
|
input_file = ckanext/d4science/i18n/ckanext-d4science.pot
|
||||||
|
output_dir = ckanext/d4science/i18n
|
||||||
|
|
||||||
|
[update_catalog]
|
||||||
|
domain = ckanext-d4science
|
||||||
|
input_file = ckanext/d4science/i18n/ckanext-d4science.pot
|
||||||
|
output_dir = ckanext/d4science/i18n
|
||||||
|
previous = true
|
||||||
|
|
||||||
|
[compile_catalog]
|
||||||
|
domain = ckanext-d4science
|
||||||
|
directory = ckanext/d4science/i18n
|
||||||
|
statistics = true
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
filterwarnings =
|
||||||
|
ignore::sqlalchemy.exc.SADeprecationWarning
|
||||||
|
ignore::sqlalchemy.exc.SAWarning
|
||||||
|
ignore::DeprecationWarning
|
||||||
|
addopts = --ckan-ini test.ini
|
|
@ -0,0 +1,16 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
# If you are changing from the default layout of your extension, you may
|
||||||
|
# have to change the message extractors, you can read more about babel
|
||||||
|
# message extraction at
|
||||||
|
# http://babel.pocoo.org/docs/messages/#extraction-method-mapping-and-configuration
|
||||||
|
message_extractors={
|
||||||
|
'ckanext': [
|
||||||
|
('**.py', 'python', None),
|
||||||
|
('**.js', 'javascript', None),
|
||||||
|
('**/templates/**.html', 'ckan', None),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,45 @@
|
||||||
|
[DEFAULT]
|
||||||
|
debug = false
|
||||||
|
smtp_server = localhost
|
||||||
|
error_email_from = ckan@localhost
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = config:../../src/ckan/test-core.ini
|
||||||
|
|
||||||
|
# Insert any custom config settings to be used when running your extension's
|
||||||
|
# tests here. These will override the one defined in CKAN core's test-core.ini
|
||||||
|
ckan.plugins = d4science
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root, ckan, sqlalchemy
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
|
||||||
|
[logger_ckan]
|
||||||
|
qualname = ckan
|
||||||
|
handlers =
|
||||||
|
level = INFO
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
level = WARN
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ckanext-d4science_theme</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.python.pydev.PyDevBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.python.pydev.pythonNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/setup.py=utf-8
|
|
@ -0,0 +1,661 @@
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,2 @@
|
||||||
|
include README.rst
|
||||||
|
recursive-include ckanext/d4science_theme *.html *.json *.js *.less *.css
|
|
@ -0,0 +1,129 @@
|
||||||
|
|
||||||
|
============
|
||||||
|
ckanext-d4science_theme
|
||||||
|
============
|
||||||
|
|
||||||
|
The CKAN extension that implements the D4Science theme template used by D4Science Catalogues
|
||||||
|
|
||||||
|
|
||||||
|
------------
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
------------
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
To install ckanext-d4science_theme:
|
||||||
|
|
||||||
|
1. Activate your CKAN virtual environment, for example::
|
||||||
|
|
||||||
|
. /usr/lib/ckan/default/bin/activate
|
||||||
|
|
||||||
|
2. Install the ckanext-d4science_theme Python package into your virtual environment::
|
||||||
|
|
||||||
|
pip install ckanext-d4science_theme
|
||||||
|
|
||||||
|
3. Add ``d4science_theme`` to the ``ckan.plugins`` setting in your CKAN
|
||||||
|
config file (by default the config file is located at
|
||||||
|
``/etc/ckan/default/production.ini``).
|
||||||
|
|
||||||
|
4. Restart CKAN. For example if you've deployed CKAN with Apache on Ubuntu::
|
||||||
|
|
||||||
|
sudo service apache2 reload
|
||||||
|
|
||||||
|
|
||||||
|
---------------
|
||||||
|
Config Settings
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Document any optional config settings here. For example::
|
||||||
|
|
||||||
|
# The minimum number of hours to wait before re-checking a resource
|
||||||
|
# (optional, default: 24).
|
||||||
|
ckanext.d4science_theme.some_setting = some_default_value
|
||||||
|
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
Development Installation
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
To install ckanext-d4science_theme for development, activate your CKAN virtualenv and
|
||||||
|
do::
|
||||||
|
|
||||||
|
git clone https://code-repo.d4science.org/CKAN-Extensions/ckanext-d4science_theme.git
|
||||||
|
cd ckanext-d4science_theme
|
||||||
|
python setup.py develop
|
||||||
|
pip install -r dev-requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Running the Tests
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
To run the tests, do::
|
||||||
|
|
||||||
|
nosetests --nologcapture --with-pylons=test.ini
|
||||||
|
|
||||||
|
To run the tests and produce a coverage report, first make sure you have
|
||||||
|
coverage installed in your virtualenv (``pip install coverage``) then run::
|
||||||
|
|
||||||
|
nosetests --nologcapture --with-pylons=test.ini --with-coverage --cover-package=ckanext.d4science_theme --cover-inclusive --cover-erase --cover-tests
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------
|
||||||
|
Registering ckanext-d4science_theme on PyPI
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
ckanext-d4science_theme should be availabe on PyPI as
|
||||||
|
https://pypi.python.org/pypi/ckanext-d4science_theme. If that link doesn't work, then
|
||||||
|
you can register the project on PyPI for the first time by following these
|
||||||
|
steps:
|
||||||
|
|
||||||
|
1. Create a source distribution of the project::
|
||||||
|
|
||||||
|
python setup.py sdist
|
||||||
|
|
||||||
|
2. Register the project::
|
||||||
|
|
||||||
|
python setup.py register
|
||||||
|
|
||||||
|
3. Upload the source distribution to PyPI::
|
||||||
|
|
||||||
|
python setup.py sdist upload
|
||||||
|
|
||||||
|
4. Tag the first release of the project on GitHub with the version number from
|
||||||
|
the ``setup.py`` file. For example if the version number in ``setup.py`` is
|
||||||
|
0.0.1 then do::
|
||||||
|
|
||||||
|
git tag 0.0.1
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
Releasing a New Version of ckanext-d4science_theme
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
ckanext-d4science_theme is availabe on PyPI as https://pypi.python.org/pypi/ckanext-d4science_theme.
|
||||||
|
To publish a new version to PyPI follow these steps:
|
||||||
|
|
||||||
|
1. Update the version number in the ``setup.py`` file.
|
||||||
|
See `PEP 440 <http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers>`_
|
||||||
|
for how to choose version numbers.
|
||||||
|
|
||||||
|
2. Create a source distribution of the new version::
|
||||||
|
|
||||||
|
python setup.py sdist
|
||||||
|
|
||||||
|
3. Upload the source distribution to PyPI::
|
||||||
|
|
||||||
|
python setup.py sdist upload
|
||||||
|
|
||||||
|
4. Tag the new release of the project on GitHub with the version number from
|
||||||
|
the ``setup.py`` file. For example if the version number in ``setup.py`` is
|
||||||
|
0.0.2 then do::
|
||||||
|
|
||||||
|
git tag 0.0.2
|
||||||
|
git push --tags
|
|
@ -0,0 +1,7 @@
|
||||||
|
# this is a namespace package
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
pkg_resources.declare_namespace(__name__)
|
||||||
|
except ImportError:
|
||||||
|
import pkgutil
|
||||||
|
__path__ = pkgutil.extend_path(__path__, __name__)
|
|
@ -0,0 +1,19 @@
|
||||||
|
# from flask import Flask, g
|
||||||
|
# from ckan.lib.app_globals import app_globals
|
||||||
|
# import ckan.plugins.toolkit as toolkit
|
||||||
|
|
||||||
|
# app = Flask("d4Science")
|
||||||
|
|
||||||
|
# def base_context():
|
||||||
|
# return {
|
||||||
|
# 'helpers': toolkit.h,
|
||||||
|
# }
|
||||||
|
|
||||||
|
# @app.context_processor
|
||||||
|
# def inject_base_context():
|
||||||
|
# return base_context()
|
||||||
|
|
||||||
|
# # Aggiungi main_css al contesto globale di Flask
|
||||||
|
# # @app.before_request
|
||||||
|
# # def before_request():
|
||||||
|
# # g.main_css = app_globals.main_css
|
|
@ -0,0 +1,844 @@
|
||||||
|
/* =====================================================
|
||||||
|
The "account masthead" bar across the top of the site
|
||||||
|
===================================================== */
|
||||||
|
|
||||||
|
.account-masthead {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
/* The "bubble" containing the number of new notifications. */
|
||||||
|
.account-masthead .account .notifications a span {
|
||||||
|
background-color: #9fa0a2;
|
||||||
|
}
|
||||||
|
/* The text and icons in the user account info. */
|
||||||
|
.account-masthead .account ul li a {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
/* The user account info text and icons, when the user's pointer is hovering
|
||||||
|
over them. */
|
||||||
|
.account-masthead .account ul li a:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
/* background-color: black;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
The main masthead bar that contains the site logo, nav links, and search
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
.masthead {
|
||||||
|
background: #eee url("/bg-noise.png") repeat scroll 0 0;
|
||||||
|
border-top: 1px solid #555;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 15px !important;
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
/* background-image: url("/bg-pattern.min.svg") !important; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead .navigation .nav-pills li a{
|
||||||
|
color: #187794;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logo > img{
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "navigation pills" in the masthead (the links to Datasets,
|
||||||
|
Organizations, etc) when the user's pointer hovers over them. */
|
||||||
|
.masthead .navigation .nav-pills li a:hover {
|
||||||
|
/* background-color: rgb(48, 48, 48);*/
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
/* The "active" navigation pill (for example, when you're on the /dataset page
|
||||||
|
the "Datasets" link is active). */
|
||||||
|
.masthead .navigation .nav-pills li.active a {
|
||||||
|
background-color: #d2d2d5;
|
||||||
|
}
|
||||||
|
/* The "box shadow" effect that appears around the search box when it
|
||||||
|
has the keyboard cursor's focus. */
|
||||||
|
.masthead input[type="text"]:focus {
|
||||||
|
-webkit-box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.7);
|
||||||
|
box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
The content in the middle of the front page
|
||||||
|
=========================================== */
|
||||||
|
|
||||||
|
/* Remove the "box shadow" effect around various boxes on the page. */
|
||||||
|
.box {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
background: #FEFEFE repeat scroll 0 0 !important;
|
||||||
|
}
|
||||||
|
/* Remove the borders around the "Welcome to CKAN" and "Search Your Data"
|
||||||
|
boxes. */
|
||||||
|
.hero .box {
|
||||||
|
/*border: none;*/
|
||||||
|
margin-top: 10px !important;
|
||||||
|
}
|
||||||
|
/* Change the colors of the "Search Your Data" box. */
|
||||||
|
.homepage .module-search .module-content {
|
||||||
|
color: rgb(68, 68, 68);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
/* Change the background color of the "Popular Tags" box. */
|
||||||
|
.homepage .module-search .tags {
|
||||||
|
background-color: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage-title{
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #202020;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change the background color of the "Popular Tags" box. */
|
||||||
|
.homepage .module-search h3{
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove some padding. This makes the bottom edges of the "Welcome to CKAN"
|
||||||
|
and "Search Your Data" boxes line up. */
|
||||||
|
.module-content:last-child {
|
||||||
|
/*padding-bottom: 0px;*/
|
||||||
|
}
|
||||||
|
.homepage .module-search {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
/* Add a border line between the top and bottom halves of the front page. */
|
||||||
|
.homepage [role="main"] {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage .stats ul li a b{
|
||||||
|
font-size: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="main"], .main {
|
||||||
|
/* background: #f5f6fa url("/bg-pattern.min.svg") repeat; scroll 0 0;*/
|
||||||
|
/*background: #fafafa url("/bg-pattern.svg") repeat; scroll 0 0;*/
|
||||||
|
background: #fdfdfd none repeat scroll 0 0;
|
||||||
|
min-height: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-item-homepage {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 3px;
|
||||||
|
float: left;
|
||||||
|
margin: 15px 0 0 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-heading-homepage {
|
||||||
|
font-size: 16px;
|
||||||
|
hyphens: auto;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-grid-homepage {
|
||||||
|
-moz-border-bottom-colors: none;
|
||||||
|
-moz-border-left-colors: none;
|
||||||
|
-moz-border-right-colors: none;
|
||||||
|
-moz-border-top-colors: none;
|
||||||
|
/* background: #fbfbfb url("../../../base/images/bg.png") repeat scroll 0 0;
|
||||||
|
border-color: #dddddd;
|
||||||
|
border-image: none;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px 0;*/
|
||||||
|
list-style: outside none none;
|
||||||
|
margin: 0 -10px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
.media-grid-homepage::before, .media-grid::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.media-grid-homepage::after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-circle{
|
||||||
|
padding: 10px 10px;
|
||||||
|
display: inline-block !important;
|
||||||
|
-webkit-border-radius: 90px;
|
||||||
|
-moz-border-radius: 90px;
|
||||||
|
border-radius: 90px;
|
||||||
|
background-color: #4679b2;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-white{
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-circle {
|
||||||
|
border-radius: 50% 50% 50% 50% !important;
|
||||||
|
height: 60px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 65px;
|
||||||
|
background-color: #4679b2;
|
||||||
|
display: inline-block !important;
|
||||||
|
padding-top: 5px;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
The footer at the bottom of the site
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
.site-footer, body {
|
||||||
|
background-color: #bbb;
|
||||||
|
font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
/* The text in the footer. */
|
||||||
|
.site-footer,
|
||||||
|
.site-footer label,
|
||||||
|
.site-footer small {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
/* The link texts in the footer. */
|
||||||
|
.site-footer a {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal{
|
||||||
|
min-height: 10px;
|
||||||
|
padding: 2px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal {
|
||||||
|
/*background-color: rgba(255, 255, 255, 0.6);*/
|
||||||
|
text-align: center;
|
||||||
|
/*display: inline-block;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal,
|
||||||
|
.site-footer-internal label,
|
||||||
|
.site-footer-internal small {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal a {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-hide-text {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0 none;
|
||||||
|
color: transparent;
|
||||||
|
font: 0px/0 a;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4science-footer-logo {
|
||||||
|
background: url("/gCube_70.png") no-repeat scroll left top rgba(0, 0, 0, 0);
|
||||||
|
height: 32px;
|
||||||
|
margin-top: 2px;
|
||||||
|
text-indent: -900em;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-ckan-footer-logo {
|
||||||
|
background: rgba(0, 0, 0, 0) url("/ckan-logo-footer.png") no-repeat scroll left top;
|
||||||
|
height: 21px;
|
||||||
|
margin-top: 2px;
|
||||||
|
text-indent: -900em;
|
||||||
|
width: 69px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-d4science {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #f5f5f5;
|
||||||
|
text-align: center;
|
||||||
|
height: 25px;
|
||||||
|
padding-top: 5px;
|
||||||
|
background-color: #7F7F7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-d4science a {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Base elements of the site
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
div .principaltitle {
|
||||||
|
color: inherit;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 15px 0;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
word-break: break-all;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .notes {
|
||||||
|
color: #444444;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-align: justify;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .infotitle {
|
||||||
|
font-size: 15px;
|
||||||
|
hyphens: auto;
|
||||||
|
line-height: 1.3;
|
||||||
|
word-break: break-all;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar .breadcrumb{
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box{
|
||||||
|
border: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .sectiontitle{
|
||||||
|
color: #9F9F9F;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 20px 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
section .well {
|
||||||
|
background-color: #fdfdfd !important;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-heading {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataset-resources .resource-list{
|
||||||
|
background-color: #fdfdfd !important;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin: -1px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper{
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 3px
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-popular{
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-homepage{
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-show{
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #444444;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-center-cropped{
|
||||||
|
text-align: center;
|
||||||
|
background-color: #eee;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Acquired Dataset
|
||||||
|
==================================== */
|
||||||
|
.label-acquired {
|
||||||
|
background-color: #55a1ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-owner {
|
||||||
|
background-color: #e0051e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin-left:10px;
|
||||||
|
height:auto;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
List Dataset
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
/*LEFT
|
||||||
|
.show_meatadatatype {
|
||||||
|
color: white;
|
||||||
|
display: inline-block; // Inline elements with width and height. TL;DR they make the icon buttons stack from left-to-right instead of top-to-bottom
|
||||||
|
position: relative; // All 'absolute'ly positioned elements are relative to this one
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*RIGHT*/
|
||||||
|
.show_meatadatatype {
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-top: -20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* LEFT
|
||||||
|
* Position the badge within the relatively positioned button
|
||||||
|
.button__badge {
|
||||||
|
background-color: #fa3e3e;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
padding: 1px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* RIGTH */
|
||||||
|
.button__badge {
|
||||||
|
color: #808080;
|
||||||
|
padding: 0px 2px;
|
||||||
|
font-size: 10px;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
font-family: sans-serif, times, georgia;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Modal Popup
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
/* The Modal (background) */
|
||||||
|
.d4s_modal {
|
||||||
|
display: none; /* Hidden by default */
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
z-index: 10001; /* Sit on top (NB. At 1000 there is the zoom in/out of the Map Widget)*/
|
||||||
|
/*padding-top: 100px;*/ /* Location of the box */
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%; /* Full width */
|
||||||
|
height: 100%; /* Full height */
|
||||||
|
overflow: auto; /* Enable scroll if needed */
|
||||||
|
background-color: rgb(0,0,0); /* Fallback color */
|
||||||
|
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Content */
|
||||||
|
.d4s_modal-content {
|
||||||
|
background-color: #fefefe;
|
||||||
|
/*margin: auto;*/
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -225px;
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Close Button */
|
||||||
|
.d4s_close {
|
||||||
|
color: #aaaaaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s_close:hover,
|
||||||
|
.d4s_close:focus {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s_div_clickable{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
D4S POPUP
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
.popupD4SNoArrow {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual popup */
|
||||||
|
.popupD4SNoArrow .popuptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 300px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle this class - hide and show the popup */
|
||||||
|
.popupD4SNoArrow .show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadeIn 1s;
|
||||||
|
animation: fadeIn 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
.popupD4S {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual popup */
|
||||||
|
.popupD4S .popuptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 300px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Popup arrow */
|
||||||
|
.popupD4S .popuptext::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #555 transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle this class - hide and show the popup */
|
||||||
|
.popupD4S .show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadeIn 1s;
|
||||||
|
animation: fadeIn 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add animation (fade in the popup) */
|
||||||
|
@-webkit-keyframes fadeIn {
|
||||||
|
from {opacity: 0;}
|
||||||
|
to {opacity: 1;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {opacity: 0;}
|
||||||
|
to {opacity:1 ;}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
D4S PACKAGE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.graphic-preview-style {
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px dotted #DDD;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style a{
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style img{
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style #graphic-title{
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item{
|
||||||
|
word-wrap:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
RESOURCE_LIST RESOURCE_ITEM INTO PACKAGE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.required-access {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
LINK TO RESOURCES FROM PACKAGE LIST
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.dataset-resources li a {
|
||||||
|
background-color: #187794;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label[data-format="csw"], .label[data-format*="csw"] {
|
||||||
|
background-color: #e6b800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED TO Similar GRSF Records
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.my-grsf-table{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-grsf-table tr td{
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-grsf-table tr td:first-child{
|
||||||
|
width: 82px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in base.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
#ckan-page-loading {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -130px;
|
||||||
|
margin-left: -130px;
|
||||||
|
width: 260px;
|
||||||
|
height: 260px;
|
||||||
|
z-index: 100000;
|
||||||
|
background-image: url("/pageloading.gif");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in search_for_location.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
div#search-for-location {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-location #dataset-map {
|
||||||
|
position: relative !important;
|
||||||
|
top: +0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-location #dataset-map-container {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#search-for-location .module-heading {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-extent{
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in additional_info.html
|
||||||
|
======================================*/
|
||||||
|
.qr-code-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td {
|
||||||
|
width: 85%;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td:first-child {
|
||||||
|
padding-left: 10px;
|
||||||
|
border-right-style: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td:last-child {
|
||||||
|
width: 105px;
|
||||||
|
text-align: center;
|
||||||
|
border-left-style: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAX-WITH APPIED TO QR_CODE */
|
||||||
|
.qr-code-table img {
|
||||||
|
max-width: 100px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED FROM JSON TO HTML TABLE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.json-to-html-table-column{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-to-html-table-column tr td{
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-to-html-table-column tr td:first-child{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #5a5a5a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED into custom_form_fields
|
||||||
|
======================================*/
|
||||||
|
.disabled-div{
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-div input[type="text"]{
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED into extra_table.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.read-more-state {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-target {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
transition: .25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-wrap .read-more-target {
|
||||||
|
opacity: 1;
|
||||||
|
font-size: inherit;
|
||||||
|
max-height: 999em;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state ~ .read-more-trigger:before {
|
||||||
|
content: 'Show more';
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-trigger:before {
|
||||||
|
content: 'Show less';
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-wrap::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-trigger {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 .5em;
|
||||||
|
color: #187794;
|
||||||
|
font-size: .9em;
|
||||||
|
line-height: 2;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: .25em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-trigger::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-wrap {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-wrap::after{
|
||||||
|
content: " ...";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,305 @@
|
||||||
|
/* =====================================================
|
||||||
|
JavaScript used by CKAN plugin: 'd4science_theme'
|
||||||
|
Created by Francesco Mangiacrapa ISTI-CNR Pisa, Italy
|
||||||
|
===================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
//old script starts
|
||||||
|
CKAN_D4S_Breadcrumb_Manager = {
|
||||||
|
|
||||||
|
breadcrumbShow : function (show) {
|
||||||
|
|
||||||
|
var breadcrumb = document.getElementById('ckan-breadcrumb');
|
||||||
|
console.log('breadcrumb is '+breadcrumb)
|
||||||
|
if(breadcrumb){
|
||||||
|
if(show){
|
||||||
|
breadcrumb.style.display = 'block';
|
||||||
|
this.organizationTreeShow(true);
|
||||||
|
} else{
|
||||||
|
breadcrumb.style.display = 'none';
|
||||||
|
this.organizationTreeShow(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//var elements = document.getElementsByTagName('a');
|
||||||
|
//for(var i = 0, len = elements.length; i < len; i++) {
|
||||||
|
// elements[i].onclick = function () {
|
||||||
|
// //alert("You clicked an external link to: " + this.href);
|
||||||
|
// //window.parent.add_hide_breadcrumb_to_dom(false);
|
||||||
|
// this.add_hide_breadcrumb_to_dom(false);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
},
|
||||||
|
|
||||||
|
organizationTreeShow : function (show) {
|
||||||
|
var trees = document.getElementsByClassName("hierarchy-tree-top");
|
||||||
|
|
||||||
|
if (trees){
|
||||||
|
for (i = 0; i < trees.length; i++) {
|
||||||
|
if(show){
|
||||||
|
trees[i].style.display = 'block';
|
||||||
|
} else{
|
||||||
|
trees[i].style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkBreadcrumbShow : function () {
|
||||||
|
|
||||||
|
var showBdc = this.getSessionStorageItem("showbreadcrumb")
|
||||||
|
//console.log("showBdc is: "+showBdc)
|
||||||
|
if(showBdc != undefined && showBdc=="false"){
|
||||||
|
console.log("Show breadcrumb false");
|
||||||
|
this.breadcrumbShow(false);
|
||||||
|
}else{
|
||||||
|
console.log("Show breadcrumb true");
|
||||||
|
this.breadcrumbShow(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
setSessionStorageItem : function (item_key, item_value) {
|
||||||
|
|
||||||
|
// Check browser support
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
// Store
|
||||||
|
sessionStorage.setItem(item_key, item_value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log("Sorry, your browser does not support Web Storage...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getSessionStorageItem : function (item_key) {
|
||||||
|
|
||||||
|
// Check browser support
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
// Store
|
||||||
|
return sessionStorage.getItem(item_key);
|
||||||
|
} else {
|
||||||
|
console.log("Sorry, your browser does not support Web Storage...");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CKAN_D4S_Functions_Util = {
|
||||||
|
|
||||||
|
getPosition : function(canvas, event){
|
||||||
|
var x = new Number();
|
||||||
|
var y = new Number();
|
||||||
|
try {
|
||||||
|
if (event.clientX != undefined && event.clientY != undefined)
|
||||||
|
{
|
||||||
|
|
||||||
|
x = event.clientX;
|
||||||
|
y = event.clientY;
|
||||||
|
}
|
||||||
|
else // Firefox method to get the position
|
||||||
|
{
|
||||||
|
x = event.clientX + document.body.scrollLeft +
|
||||||
|
document.documentElement.scrollLeft;
|
||||||
|
y = event.clientY + document.body.scrollTop +
|
||||||
|
document.documentElement.scrollTop;
|
||||||
|
}
|
||||||
|
x -= canvas.offsetLeft;
|
||||||
|
y -= canvas.offsetTop;
|
||||||
|
}catch (err) {
|
||||||
|
//silent error
|
||||||
|
}
|
||||||
|
return '{"posX": "'+x+'", "posY": "'+y+'"}';
|
||||||
|
},
|
||||||
|
|
||||||
|
// When the user clicks on div, open the popup
|
||||||
|
showPopupD4S : function(event, my_div, my_popup_left_position) {
|
||||||
|
var popup = document.getElementById(my_div);
|
||||||
|
var clickPosition = this.getPosition(my_div, event)
|
||||||
|
var myPosition = JSON.parse(clickPosition);
|
||||||
|
this.closePopups(my_div);
|
||||||
|
// When the user clicks anywhere outside of the modal, close it
|
||||||
|
/*window.onclick = function(event) {
|
||||||
|
if (event.target != popup) {
|
||||||
|
popup.style.display = "none";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
popup.classList.toggle("show");
|
||||||
|
|
||||||
|
if(my_popup_left_position){
|
||||||
|
popup.style.left = my_popup_left_position;
|
||||||
|
}
|
||||||
|
else if (myPosition.posX){
|
||||||
|
popup.style.left = myPosition.posX + "px";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closePopups : function ($target) {
|
||||||
|
var popups = document.getElementsByClassName('popuptext');
|
||||||
|
for (i = 0; i < popups.length; i++) {
|
||||||
|
if (popups[i].getAttribute('id') != $target) {
|
||||||
|
popups[i].classList.remove('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkURL : function (url) {
|
||||||
|
//console.log('checking url: '+url)
|
||||||
|
var regex = new RegExp('^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/|www\.|ftp:\/\/)+[^ "]+$');
|
||||||
|
if (regex.test(url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CKAN_D4S_HTMLMessage_Util = {
|
||||||
|
|
||||||
|
postHeightToPortlet : function (selectedProduct, product) {
|
||||||
|
var h = document.body.scrollHeight + "px";
|
||||||
|
var p = "";
|
||||||
|
var msg = "";
|
||||||
|
//WORK AROUND IF TWO MESSAGES ARE SENT FROM A PAGE OF A PRODUCT
|
||||||
|
//THE MESSAGE WITH 'NULL' PRODUCT IS SKIPPED
|
||||||
|
//console.log("window.location.pathname? "+window.location.pathname);
|
||||||
|
var pathArray = window.location.pathname.split('/');
|
||||||
|
var productContext = "dataset";
|
||||||
|
if(pathArray.length>1){
|
||||||
|
//console.log("pathArray is: "+pathArray);
|
||||||
|
var secondLevelLocation = pathArray[1]; //it is the second level location
|
||||||
|
//console.log("secondLevelLocation is: "+secondLevelLocation);
|
||||||
|
//console.log("h is: "+h);
|
||||||
|
if(secondLevelLocation == productContext){ //is it product context?
|
||||||
|
if(product !== 'undefined' && product !== null){
|
||||||
|
p = product;
|
||||||
|
//console.log("product selected is: "+p);
|
||||||
|
}else{
|
||||||
|
//console.log("product is null or undefined, passing only height");
|
||||||
|
msg = "{\"height\": \""+h+"\"}";
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
this.postMessageToParentWindow(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//msg = "{'height': '"+h+"', 'product': '"+p+"'}";
|
||||||
|
msg = "{\"height\": \""+h+"\", \"product\": \""+p+"\"}";
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
//console.log("posting message in the window: "+msg);
|
||||||
|
this.postMessageToParentWindow(msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
postMessageToParentWindow : function (msg) {
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
//console.log("posting message in the window: "+msg);
|
||||||
|
if(window.parent!=null){
|
||||||
|
console.log("posting message in the parent window: "+msg);
|
||||||
|
window.parent.postMessage(msg,'*');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CKAN_D4S_JSON_Util = {
|
||||||
|
|
||||||
|
|
||||||
|
//ADDED by Francesco Mangiacrapa
|
||||||
|
appendHTMLToElement : function(containerID, elementHTML){
|
||||||
|
|
||||||
|
var divContainer = document.getElementById(containerID);
|
||||||
|
divContainer.innerHTML = "";
|
||||||
|
divContainer.appendChild(elementHTML);
|
||||||
|
},
|
||||||
|
|
||||||
|
//ADDED by Francesco Mangiacrapa
|
||||||
|
jsonToHTML : function(containerID, cssClassToTable) {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonTxt = document.getElementById(containerID).innerHTML;
|
||||||
|
var jsonObj = JSON.parse(jsonTxt);
|
||||||
|
//console.log(jsonObj.length)
|
||||||
|
|
||||||
|
if(jsonObj.length==undefined)
|
||||||
|
jsonObj = [jsonObj]
|
||||||
|
//console.log(jsonObj.length)
|
||||||
|
|
||||||
|
// EXTRACT VALUE FOR HTML HEADER.
|
||||||
|
var col = [];
|
||||||
|
for (var i = 0; i < jsonObj.length; i++) {
|
||||||
|
for (var key in jsonObj[i]) {
|
||||||
|
//console.log('key json' +key)
|
||||||
|
if (col.indexOf(key) === -1) {
|
||||||
|
col.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE DYNAMIC TABLE.
|
||||||
|
var table = document.createElement("table");
|
||||||
|
var addDefaultCss = "json-to-html-table-column";
|
||||||
|
if(cssClassToTable){
|
||||||
|
addDefaultCss = cssClassToTable;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
table.classList.add(addDefaultCss);
|
||||||
|
}catch(e){
|
||||||
|
console.log('invalid css add', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADD JSON DATA TO THE TABLE AS ROWS.
|
||||||
|
for (var i = 0; i < col.length; i++) {
|
||||||
|
|
||||||
|
tr = table.insertRow(-1);
|
||||||
|
var firstCell = tr.insertCell(-1);
|
||||||
|
//firstCell.style.cssText="font-weight: bold; text-align: center; vertical-align: middle;";
|
||||||
|
firstCell.innerHTML = col[i];
|
||||||
|
for (var j = 0; j < jsonObj.length; j++) {
|
||||||
|
var tabCell = tr.insertCell(-1);
|
||||||
|
var theValue = jsonObj[j][col[i]];
|
||||||
|
/* console.log(theValue + ' is url? '+isUrl);*/
|
||||||
|
if(CKAN_D4S_Functions_Util.checkURL(theValue)){
|
||||||
|
theValue = '<a target="_blank" href='+theValue+'>'+theValue+'</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
tabCell.innerHTML = theValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
|
||||||
|
this.appendHTMLToElement(containerID, table);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log('invalid json', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Task #8032
|
||||||
|
window.addEventListener("message",
|
||||||
|
function (e) {
|
||||||
|
|
||||||
|
var curr_loc = window.location.toString()
|
||||||
|
var orgin = e.origin.toString()
|
||||||
|
if(curr_loc.startsWith(orgin)){
|
||||||
|
//alert("ignoring message from myself");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//console.log("origin: "+e.data)
|
||||||
|
if(e.data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pMess = JSON.parse(e.data)
|
||||||
|
//console.log(pMess.explore_vres_landing_page)
|
||||||
|
window.linktogateway = pMess.explore_vres_landing_page;
|
||||||
|
},false);
|
||||||
|
//old script ends
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
d4science-js:
|
||||||
|
filter: rjsmin
|
||||||
|
output: ckanext-d4science_theme/ckanext/d4science_theme/assets/js/d4science_scripts.js
|
||||||
|
contents:
|
||||||
|
- js/d4science_scripts.js
|
||||||
|
extra:
|
||||||
|
preload:
|
||||||
|
- base/main
|
||||||
|
|
||||||
|
d4science-css:
|
||||||
|
filter: cssrewrite
|
||||||
|
output: ckanext-d4science_theme/ckanext/d4science_theme/assets/css/d4science_theme.css
|
||||||
|
contents:
|
||||||
|
- css/d4science_theme.css
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#The __init__.py files are required to make Python
|
||||||
|
#treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path.
|
||||||
|
#See: https://docs.python.org/3/tutorial/modules.html#packages
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import logging
|
||||||
|
# from ckan.controllers.home import HomeController
|
||||||
|
import ckan.plugins as p
|
||||||
|
from ckan.common import _, g, c
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import ckan.lib.search as search
|
||||||
|
import ckan.model as model
|
||||||
|
import ckan.logic as logic
|
||||||
|
import ckan.lib.maintain as maintain
|
||||||
|
import ckan.lib.base as base
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
class d4SHomeController():
|
||||||
|
|
||||||
|
#Overriding controllers.HomeController.index method
|
||||||
|
def index(self):
|
||||||
|
try:
|
||||||
|
# package search
|
||||||
|
context = {'model': model, 'session': model.Session,'user': c.user, 'auth_user_obj': c.userobj}
|
||||||
|
|
||||||
|
facets = OrderedDict()
|
||||||
|
|
||||||
|
default_facet_titles = {
|
||||||
|
'organization': _('Organizations'),
|
||||||
|
'groups': _('Groups'),
|
||||||
|
'types':_('Types'),
|
||||||
|
'tags': _('Tags'),
|
||||||
|
'res_format': _('Formats'),
|
||||||
|
'license_id': _('Licenses'),
|
||||||
|
}
|
||||||
|
|
||||||
|
for facet in g.facets:
|
||||||
|
if facet in default_facet_titles:
|
||||||
|
facets[facet] = default_facet_titles[facet]
|
||||||
|
else:
|
||||||
|
facets[facet] = facet
|
||||||
|
|
||||||
|
# Facet titles
|
||||||
|
for plugin in p.PluginImplementations(p.IFacets):
|
||||||
|
facets = plugin.dataset_facets(facets, 'dataset')
|
||||||
|
|
||||||
|
c.facet_titles = facets
|
||||||
|
|
||||||
|
data_dict = {
|
||||||
|
'q': '*:*',
|
||||||
|
'facet.field': list(facets.keys()),
|
||||||
|
'rows': 4,
|
||||||
|
'start': 0,
|
||||||
|
'sort': 'views_recent desc',
|
||||||
|
'fq': 'capacity:"public"'
|
||||||
|
}
|
||||||
|
query = logic.get_action('package_search')(context, data_dict)
|
||||||
|
c.search_facets = query['search_facets']
|
||||||
|
c.package_count = query['count']
|
||||||
|
c.datasets = query['results']
|
||||||
|
|
||||||
|
#print "c.search_facets: "
|
||||||
|
#print " ".join(c.search_facets)
|
||||||
|
|
||||||
|
except search.SearchError:
|
||||||
|
c.package_count = 0
|
||||||
|
|
||||||
|
if c.userobj and not c.userobj.email:
|
||||||
|
url = h.url_for(controller='user', action='edit')
|
||||||
|
msg = _('Please <a href="%s">update your profile</a>'
|
||||||
|
' and add your email address. ') % url + \
|
||||||
|
_('%s uses your email address'
|
||||||
|
' if you need to reset your password.') \
|
||||||
|
% g.site_title
|
||||||
|
h.flash_notice(msg, allow_html=True)
|
||||||
|
|
||||||
|
#return base.render('home/index.html', cache_force=True)
|
||||||
|
#ckan 2.10 :
|
||||||
|
return render_template('home/index.html')
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
#import ckan.controllers.group as group
|
||||||
|
import ckan.plugins as plugins
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import ckan.plugins.toolkit as toolkit
|
||||||
|
import ckan.lib.base as base
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
import ckan.lib.maintain as maintain
|
||||||
|
import ckan.lib.navl.dictization_functions as dict_fns
|
||||||
|
import ckan.logic as logic
|
||||||
|
import ckan.lib.search as search
|
||||||
|
import ckan.model as model
|
||||||
|
import ckan.authz as authz
|
||||||
|
import ckan.lib.plugins
|
||||||
|
import ckan.plugins as plugins
|
||||||
|
from ckan.common import c, request, _
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Created by Francesco Mangiacrapa, see: #8964
|
||||||
|
'''
|
||||||
|
class OrganizationVREController(plugins.toolkit.DefaultOrganizationForm): #changed for 2.10 : GroupController -> defaultOrganizationForm
|
||||||
|
''' The organization controller is for Organizations, which are implemented
|
||||||
|
as Groups with is_organization=True and group_type='organization'. It works
|
||||||
|
the same as the group controller apart from:
|
||||||
|
* templates and logic action/auth functions are sometimes customized
|
||||||
|
(switched using _replace_group_org)
|
||||||
|
* 'bulk_process' action only works for organizations
|
||||||
|
|
||||||
|
Nearly all the code for both is in the GroupController (for historical
|
||||||
|
reasons).
|
||||||
|
'''
|
||||||
|
|
||||||
|
group_types = ['organization']
|
||||||
|
|
||||||
|
def _guess_group_type(self, expecting_name=False):
|
||||||
|
return 'organization'
|
||||||
|
|
||||||
|
def _replace_group_org(self, string):
|
||||||
|
''' substitute organization for group if this is an org'''
|
||||||
|
return re.sub('^group', 'organization', string)
|
||||||
|
|
||||||
|
def _update_facet_titles(self, facets, group_type):
|
||||||
|
for plugin in plugins.PluginImplementations(plugins.IFacets):
|
||||||
|
facets = plugin.organization_facets(
|
||||||
|
facets, group_type, None)
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
group_type = self._guess_group_type()
|
||||||
|
|
||||||
|
page = h.get_page_number(request.args) or 1
|
||||||
|
items_per_page = 21
|
||||||
|
|
||||||
|
context = {'model': model, 'session': model.Session,
|
||||||
|
'user': c.user, 'for_view': True,
|
||||||
|
'with_private': False}
|
||||||
|
|
||||||
|
q = c.q = request.args.get('q', '')
|
||||||
|
sort_by = c.sort_by_selected = request.args.get('sort')
|
||||||
|
try:
|
||||||
|
logic.check_access('site_read', context)
|
||||||
|
logic.check_access('group_list', context)
|
||||||
|
except ckan.plugins.toolkit.NotAuthorized:
|
||||||
|
abort(403, _('Not authorized to see this page'))
|
||||||
|
|
||||||
|
# pass user info to context as needed to view private datasets of
|
||||||
|
# orgs correctly
|
||||||
|
if c.userobj:
|
||||||
|
context['user_id'] = c.userobj.id
|
||||||
|
context['user_is_admin'] = c.userobj.sysadmin
|
||||||
|
|
||||||
|
data_dict_global_results = {
|
||||||
|
'all_fields': False,
|
||||||
|
'q': q,
|
||||||
|
'sort': sort_by,
|
||||||
|
'type': group_type or 'group',
|
||||||
|
}
|
||||||
|
global_results = toolkit.get_action('group_list')(context,
|
||||||
|
data_dict_global_results)
|
||||||
|
|
||||||
|
data_dict_page_results = {
|
||||||
|
'all_fields': True,
|
||||||
|
'q': q,
|
||||||
|
'sort': sort_by,
|
||||||
|
'type': group_type or 'group',
|
||||||
|
'limit': items_per_page,
|
||||||
|
'offset': items_per_page * (page - 1),
|
||||||
|
}
|
||||||
|
page_results = toolkit.get_action('group_list')(context,
|
||||||
|
data_dict_page_results)
|
||||||
|
|
||||||
|
c.page = h.Page(
|
||||||
|
collection=global_results,
|
||||||
|
page=page,
|
||||||
|
url=h.pager_url,
|
||||||
|
items_per_page=items_per_page,
|
||||||
|
)
|
||||||
|
|
||||||
|
c.page.items = page_results
|
||||||
|
return base.render('organization_vre/index.html',
|
||||||
|
extra_vars={'group_type': group_type})
|
||||||
|
|
||||||
|
|
||||||
|
def read(self, id, limit=20):
|
||||||
|
group_type = self._ensure_controller_matches_group_type(
|
||||||
|
id.split('@')[0])
|
||||||
|
|
||||||
|
context = {'model': model, 'session': model.Session,
|
||||||
|
'user': c.user,
|
||||||
|
'schema': self._db_to_form_schema(group_type=group_type),
|
||||||
|
'for_view': True}
|
||||||
|
data_dict = {'id': id, 'type': group_type}
|
||||||
|
|
||||||
|
# unicode format (decoded from utf8)
|
||||||
|
c.q = request.args.get('q', '')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Do not query for the group datasets when dictizing, as they will
|
||||||
|
# be ignored and get requested on the controller anyway
|
||||||
|
data_dict['include_datasets'] = False
|
||||||
|
c.group_dict = toolkit.get_action('group_show')(context, data_dict)
|
||||||
|
c.group = context['group']
|
||||||
|
except (NotFound, NotAuthorized, ckan.logic.NotFound):
|
||||||
|
abort(404, _('Group not found'))
|
||||||
|
|
||||||
|
self._read(id, limit, group_type)
|
||||||
|
return base.render('organization_vre/read.html',
|
||||||
|
extra_vars={'group_type': group_type})
|
|
@ -0,0 +1,96 @@
|
||||||
|
import logging
|
||||||
|
import ckan.plugins as p
|
||||||
|
from ckan.common import _, g, c
|
||||||
|
import ckan.lib.search as search
|
||||||
|
import ckan.model as model
|
||||||
|
import ckan.logic as logic
|
||||||
|
import ckan.lib.maintain as maintain
|
||||||
|
import ckan.lib.base as base
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
|
||||||
|
import ckan.lib.base as base
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
import ckan.lib.maintain as maintain
|
||||||
|
import ckan.lib.navl.dictization_functions as dict_fns
|
||||||
|
import ckan.logic as logic
|
||||||
|
import ckan.lib.search as search
|
||||||
|
import ckan.model as model
|
||||||
|
import ckan.authz as authz
|
||||||
|
import ckan.lib.plugins
|
||||||
|
import ckan.plugins as plugins
|
||||||
|
from ckan.common import c, g, request, _
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
class d4STypeController():
|
||||||
|
|
||||||
|
#Overriding controllers.HomeController.index method
|
||||||
|
def index(self):
|
||||||
|
try:
|
||||||
|
# package search
|
||||||
|
context = {'model': model, 'session': model.Session,'user': c.user, 'auth_user_obj': c.userobj}
|
||||||
|
|
||||||
|
facets = OrderedDict()
|
||||||
|
|
||||||
|
default_facet_titles = {
|
||||||
|
'organization': _('Organizations'),
|
||||||
|
'groups': _('Groups'),
|
||||||
|
'types':_('Types'),
|
||||||
|
'tags': _('Tags'),
|
||||||
|
'res_format': _('Formats'),
|
||||||
|
'license_id': _('Licenses'),
|
||||||
|
}
|
||||||
|
|
||||||
|
for facet in g.facets:
|
||||||
|
if facet in default_facet_titles:
|
||||||
|
facets[facet] = default_facet_titles[facet]
|
||||||
|
else:
|
||||||
|
facets[facet] = facet
|
||||||
|
|
||||||
|
# Facet titles
|
||||||
|
for plugin in p.PluginImplementations(p.IFacets):
|
||||||
|
facets = plugin.dataset_facets(facets, 'dataset')
|
||||||
|
|
||||||
|
c.facet_titles = facets
|
||||||
|
|
||||||
|
data_dict = {
|
||||||
|
'q': '*:*',
|
||||||
|
'facet.field': list(facets.keys()),
|
||||||
|
'rows': 4,
|
||||||
|
'start': 0,
|
||||||
|
'sort': 'views_recent desc',
|
||||||
|
'fq': 'capacity:"public"'
|
||||||
|
}
|
||||||
|
query = logic.get_action('package_search')(context, data_dict)
|
||||||
|
c.search_facets = query['search_facets']
|
||||||
|
c.package_count = query['count']
|
||||||
|
c.datasets = query['results']
|
||||||
|
|
||||||
|
#print "c.search_facets: "
|
||||||
|
#print " ".join(c.search_facets)
|
||||||
|
|
||||||
|
except search.SearchError:
|
||||||
|
c.package_count = 0
|
||||||
|
|
||||||
|
if c.userobj and not c.userobj.email:
|
||||||
|
url = h.url_for(controller='user', action='edit')
|
||||||
|
msg = _('Please <a href="%s">update your profile</a>'
|
||||||
|
' and add your email address. ') % url + \
|
||||||
|
_('%s uses your email address'
|
||||||
|
' if you need to reset your password.') \
|
||||||
|
% g.site_title
|
||||||
|
h.flash_notice(msg, allow_html=True)
|
||||||
|
|
||||||
|
# return base.render('type/index.html', cache_force=True)
|
||||||
|
return render_template('type/index.html')
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import csv
|
||||||
|
|
||||||
|
from .icproxycontroller import NAMESPACE_ID_LABEL
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CATALINA_HOME = 'CATALINA_HOME'
|
||||||
|
temp_dir = None
|
||||||
|
namespaces_dir = None
|
||||||
|
NAMESPACES_DIR_NAME = "namespaces_for_catalogue"
|
||||||
|
NAMESPACES_CACHE_FILENAME = "Namespaces_Catalogue_Categories.csv"
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
|
||||||
|
# D4S_Cache_Controller
|
||||||
|
class D4S_Cache_Controller():
|
||||||
|
namespaces_cache_path = None
|
||||||
|
__scheduler = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Virtually private constructor. """
|
||||||
|
log.debug("__init__ D4S_Cache_Controller")
|
||||||
|
self._check_cache()
|
||||||
|
|
||||||
|
def _check_cache(self):
|
||||||
|
|
||||||
|
if self.namespaces_cache_path is None:
|
||||||
|
self.init_temp_dir()
|
||||||
|
self.namespaces_cache_path = os.path.join(namespaces_dir, NAMESPACES_CACHE_FILENAME)
|
||||||
|
log.info("The namespaces cache is located at: %s" % self.namespaces_cache_path)
|
||||||
|
|
||||||
|
if not os.path.exists(self.namespaces_cache_path):
|
||||||
|
log.info("File does not exists creating it")
|
||||||
|
try:
|
||||||
|
with open(self.namespaces_cache_path, mode='w') as namespaces_file:
|
||||||
|
csv.writer(namespaces_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
|
||||||
|
log.info("Cache created at %s" % self.namespaces_cache_path)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
''' Write the list of dictionary with namespaces'''
|
||||||
|
def write_namespaces(self, namespace_list_of_dict):
|
||||||
|
# Insert Data
|
||||||
|
with open(self.namespaces_cache_path, 'w') as namespaces_file:
|
||||||
|
writer = csv.writer(namespaces_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
|
||||||
|
writer.writerow([NAMESPACE_ID_LABEL, 'name', 'title', 'description'])
|
||||||
|
for namespace_dict in namespace_list_of_dict:
|
||||||
|
#print("namespace %s" % namespace_dict)
|
||||||
|
writer.writerow([namespace_dict[NAMESPACE_ID_LABEL], namespace_dict['name'], namespace_dict['title'], namespace_dict['description']])
|
||||||
|
|
||||||
|
log.info("Inserted %d namespaces in the Cache" % len(namespace_list_of_dict))
|
||||||
|
|
||||||
|
'''Returns the list of dictionary with namespaces'''
|
||||||
|
def read_namespaces(self):
|
||||||
|
# Read Data
|
||||||
|
namespace_list_of_dict = []
|
||||||
|
try:
|
||||||
|
with open(self.namespaces_cache_path, 'r') as namespaces_file:
|
||||||
|
reader = csv.DictReader(namespaces_file)
|
||||||
|
for row in reader:
|
||||||
|
#print("read namespace %s" % row)
|
||||||
|
namespace_list_of_dict.append(dict(row))
|
||||||
|
|
||||||
|
log.debug("from Cache returning namespace_list_of_dict %s: " % namespace_list_of_dict)
|
||||||
|
log.info("from Cache read namespace_list_of_dict with %d item/s " % len(namespace_list_of_dict))
|
||||||
|
return namespace_list_of_dict
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
log.info("no namespace in the Cache returning empty list of dict")
|
||||||
|
return namespace_list_of_dict
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_namespaces_cache_path(self):
|
||||||
|
return self.namespaces_cache_path
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_temp_dir(cls):
|
||||||
|
global temp_dir
|
||||||
|
global NAMESPACES_DIR_NAME
|
||||||
|
global namespaces_dir
|
||||||
|
try:
|
||||||
|
temp_dir = str(os.environ[CATALINA_HOME])
|
||||||
|
temp_dir = os.path.join(temp_dir, "temp")
|
||||||
|
except KeyError as error:
|
||||||
|
log.error("No environment variable for: %s" % CATALINA_HOME)
|
||||||
|
|
||||||
|
if temp_dir is None:
|
||||||
|
temp_dir = tempfile.gettempdir() # using system tmp dir
|
||||||
|
|
||||||
|
log.debug("Temp dir is: %s" % temp_dir)
|
||||||
|
|
||||||
|
namespaces_dir = os.path.join(temp_dir, NAMESPACES_DIR_NAME)
|
||||||
|
|
||||||
|
if not os.path.exists(namespaces_dir):
|
||||||
|
os.makedirs(namespaces_dir)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class D4S_Extras():
|
||||||
|
|
||||||
|
def __init__(self, category_dict={}, extras=[]):
|
||||||
|
self._category = category_dict
|
||||||
|
self._extras = extras
|
||||||
|
|
||||||
|
def append_extra(self, k, v):
|
||||||
|
#print ("self._extras: %s" %self._extras)
|
||||||
|
if k is not None:
|
||||||
|
self._extras.append({k:v})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
return self._category
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extras(self):
|
||||||
|
return self._extras
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'category: %s'%self._category+' ' \
|
||||||
|
'extras: %s'%self._extras
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
#OrderedDict([(u'@id', u'extra_information'), (u'name', u'Extra Information'), (u'title', u'Extras'), (u'description', u'This section is about Extra(s)')]), u'contact': OrderedDict([(u'@id', u'contact'), (u'name', u'Contact'), (u'title', u'Contact Title'), (u'description', u'This section is about Contact(s)')]), u'developer_information': OrderedDict([(u'@id', u'developer_information'), (u'name', u'Developer'), (u'title', u'Developer Information'), (u'description', u'This section is about Developer(s)')])}
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class D4S_Namespaces():
|
||||||
|
|
||||||
|
def __init__(self, id=None, name=None, title=None, description=None):
|
||||||
|
self._id = id
|
||||||
|
self._name = name
|
||||||
|
self._title = title
|
||||||
|
self._description = description
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
return self._title
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self._description
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '{id: %s'%self.id+', ' \
|
||||||
|
'name: %s'%self.name+ ', ' \
|
||||||
|
'title: %s'%self.title+ ', ' \
|
||||||
|
'description: %s'%self.description+ '}'
|
|
@ -0,0 +1,130 @@
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
from .d4s_cache_controller import D4S_Cache_Controller
|
||||||
|
from .icproxycontroller import D4S_IS_DiscoveryCatalogueNamespaces
|
||||||
|
from threading import Event, Thread
|
||||||
|
|
||||||
|
CATEGORY = 'category'
|
||||||
|
NOCATEOGORY = 'nocategory'
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
cancel_future_calls = None
|
||||||
|
|
||||||
|
# Refreshing time for namespaces cache in secs.
|
||||||
|
NAMESPACES_CACHE_REFRESHING_TIME = 60 * 60
|
||||||
|
|
||||||
|
|
||||||
|
# Funtion to call repeatedly another function
|
||||||
|
def call_repeatedly(interval, func, *args):
|
||||||
|
log.info("call_repeatedly called on func '{}' with interval {} sec".format(func.__name__, interval))
|
||||||
|
stopped = Event()
|
||||||
|
|
||||||
|
def loop():
|
||||||
|
while not stopped.wait(interval): # the first call is in `interval` secs
|
||||||
|
func(*args)
|
||||||
|
|
||||||
|
th = Thread(name='daemon_caching_namespaces', target=loop)
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
return stopped.set
|
||||||
|
|
||||||
|
|
||||||
|
def reload_namespaces_from_IS(urlICProxy, resourceID, gcubeToken):
|
||||||
|
log.info("_reload_namespaces_from_IS called")
|
||||||
|
try:
|
||||||
|
discovery_ctg_namespaces = D4S_IS_DiscoveryCatalogueNamespaces(urlICProxy, resourceID, gcubeToken)
|
||||||
|
namespaces_list_of_dict = discovery_ctg_namespaces.getNamespacesDictFromResource()
|
||||||
|
|
||||||
|
if namespaces_list_of_dict is not None and len(namespaces_list_of_dict) > 0:
|
||||||
|
log.debug("namespaces read from IS are: %s" % namespaces_list_of_dict)
|
||||||
|
D4S_Cache_Controller().write_namespaces(namespaces_list_of_dict)
|
||||||
|
else:
|
||||||
|
log.info("namespaces list read from IS is empty. Skipping caching update")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Error occurred on reading namespaces from IS and refilling the cache!")
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
|
||||||
|
# D4S_IS_DiscoveryCatalogueNamespacesController is used to discovery namespaces for Catalogue Categories (implemented as a Singleton)
|
||||||
|
# @param: urlICProxy is the URI of IC proxy rest-full service provided by IS
|
||||||
|
# @param: resourceID is the resource ID of the Generic Resource: "Namespaces Catalogue Categories"
|
||||||
|
# @param: gcubeToken the gcube token used to contact the IC proxy
|
||||||
|
class D4S_Namespaces_Controller():
|
||||||
|
__instance = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getInstance():
|
||||||
|
""" Static access method. """
|
||||||
|
if D4S_Namespaces_Controller.__instance is None:
|
||||||
|
D4S_Namespaces_Controller()
|
||||||
|
|
||||||
|
return D4S_Namespaces_Controller.__instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Virtually private constructor. """
|
||||||
|
log.debug("__init__ D4S_Namespaces_Controller")
|
||||||
|
|
||||||
|
if D4S_Namespaces_Controller.__instance is not None:
|
||||||
|
raise Exception("This class is a singleton!")
|
||||||
|
else:
|
||||||
|
D4S_Namespaces_Controller.__instance = self
|
||||||
|
|
||||||
|
self._d4s_cache_controller = D4S_Cache_Controller()
|
||||||
|
self._urlICProxy = None
|
||||||
|
self._resourceID = None
|
||||||
|
self._gcubeToken = None
|
||||||
|
|
||||||
|
def load_namespaces(self, urlICProxy, resourceID, gcubeToken):
|
||||||
|
log.debug("readNamespaces called")
|
||||||
|
self._urlICProxy = urlICProxy
|
||||||
|
self._resourceID = resourceID
|
||||||
|
self._gcubeToken = gcubeToken
|
||||||
|
return self._check_namespaces()
|
||||||
|
|
||||||
|
def _read_namespaces(self):
|
||||||
|
return self._d4s_cache_controller.read_namespaces()
|
||||||
|
|
||||||
|
def _check_namespaces(self):
|
||||||
|
log.debug("_check_namespaces called")
|
||||||
|
|
||||||
|
if self._d4s_cache_controller is None:
|
||||||
|
self._d4s_cache_controller = D4S_Cache_Controller()
|
||||||
|
|
||||||
|
namespace_list = self._read_namespaces()
|
||||||
|
|
||||||
|
# when the Cache is empty
|
||||||
|
if namespace_list is None or not namespace_list:
|
||||||
|
# reading namespaces from IS and filling the DB
|
||||||
|
log.info("The Cache is empty. Reading the namespace from IS and filling the Cache")
|
||||||
|
reload_namespaces_from_IS(self._urlICProxy, self._resourceID, self._gcubeToken)
|
||||||
|
# reloading the namespaces from the cache
|
||||||
|
namespace_list = self._read_namespaces()
|
||||||
|
|
||||||
|
# starting Thread daemon for refreshing the namespaces Cache
|
||||||
|
global cancel_future_calls
|
||||||
|
if cancel_future_calls is None:
|
||||||
|
cancel_future_calls = call_repeatedly(NAMESPACES_CACHE_REFRESHING_TIME, reload_namespaces_from_IS,
|
||||||
|
self._urlICProxy,
|
||||||
|
self._resourceID,
|
||||||
|
self._gcubeToken)
|
||||||
|
|
||||||
|
return namespace_list
|
||||||
|
|
||||||
|
def get_dict_ctg_namespaces(self):
|
||||||
|
log.debug("get_dict_ctg_namespaces called")
|
||||||
|
namespace_list_of_dict = self._check_namespaces()
|
||||||
|
return self.convert_namespaces_to_d4s_namespacedict(namespace_list_of_dict)
|
||||||
|
|
||||||
|
# Private method
|
||||||
|
@staticmethod
|
||||||
|
def convert_namespaces_to_d4s_namespacedict(namespace_list_of_dict):
|
||||||
|
log.debug("convert_namespaces_to_d4s_namespacedict called on %s" % namespace_list_of_dict)
|
||||||
|
return D4S_IS_DiscoveryCatalogueNamespaces.to_namespaces_dict_index_for_id(namespace_list_of_dict)
|
|
@ -0,0 +1,89 @@
|
||||||
|
import logging
|
||||||
|
import collections
|
||||||
|
from .d4s_extras import D4S_Extras
|
||||||
|
|
||||||
|
CATEGORY = 'category'
|
||||||
|
NOCATEOGORY = 'nocategory'
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
|
||||||
|
# D4S_Namespaces_Extra_Util is used to get the extra fields indexed for D4Science namespaces
|
||||||
|
# @param: namespace_dict is the namespace dict of D4Science namespaces (defined in the Generic Resource: "Namespaces Catalogue Categories")
|
||||||
|
# @param: extras is the dictionary of extra fields for a certain item
|
||||||
|
class D4S_Namespaces_Extra_Util():
|
||||||
|
|
||||||
|
def get_extras_indexed_for_namespaces(self, namespace_dict, extras):
|
||||||
|
extras_for_categories = collections.OrderedDict()
|
||||||
|
|
||||||
|
# ADDING ALL EXTRAS WITH NAMESPACE
|
||||||
|
for namespaceid in list(namespace_dict.keys()):
|
||||||
|
dict_extras = None
|
||||||
|
nms = namespaceid + ":"
|
||||||
|
#has_namespace_ref = None
|
||||||
|
for key, value in extras:
|
||||||
|
k = key
|
||||||
|
v = value
|
||||||
|
# print "key: " + k
|
||||||
|
# print "value: " + v
|
||||||
|
if k.startswith(nms):
|
||||||
|
|
||||||
|
if namespaceid not in extras_for_categories:
|
||||||
|
extras_for_categories[namespaceid] = collections.OrderedDict()
|
||||||
|
|
||||||
|
dict_extras = extras_for_categories[namespaceid]
|
||||||
|
log.debug("dict_extras %s "%dict_extras)
|
||||||
|
|
||||||
|
if (dict_extras is None) or (not dict_extras):
|
||||||
|
dict_extras = D4S_Extras(namespace_dict.get(namespaceid), [])
|
||||||
|
log.debug("dict_extras after init %s " % dict_extras)
|
||||||
|
|
||||||
|
#print ("dict_extras after init %s " % dict_extras)
|
||||||
|
log.debug("replacing namespace into key %s " % k +" with empty string")
|
||||||
|
nms = namespaceid + ":"
|
||||||
|
k = k.replace(nms, "")
|
||||||
|
dict_extras.append_extra(k, v)
|
||||||
|
extras_for_categories[namespaceid] = dict_extras
|
||||||
|
log.debug("adding d4s_extra: %s " % dict_extras+ " - to namespace id: %s" %namespaceid)
|
||||||
|
#has_namespace_ref = True
|
||||||
|
#break
|
||||||
|
|
||||||
|
#ADDING ALL EXTRAS WITHOUT NAMESPACE
|
||||||
|
for key, value in extras:
|
||||||
|
k = key
|
||||||
|
v = value
|
||||||
|
|
||||||
|
has_namespace_ref = None
|
||||||
|
for namespaceid in list(namespace_dict.keys()):
|
||||||
|
nms = namespaceid + ":"
|
||||||
|
#IF KEY NOT STARTING WITH NAMESPACE
|
||||||
|
if k.startswith(nms):
|
||||||
|
has_namespace_ref = True
|
||||||
|
log.debug("key: %s " % k + " - have namespace: %s" % nms)
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_namespace_ref is None:
|
||||||
|
log.debug("key: %s " % k + " - have not namespace")
|
||||||
|
if NOCATEOGORY not in extras_for_categories:
|
||||||
|
extras_for_categories[NOCATEOGORY] = collections.OrderedDict()
|
||||||
|
|
||||||
|
dict_extras_no_cat = extras_for_categories[NOCATEOGORY]
|
||||||
|
#print ("dict_extras_no_cat %s " % dict_extras_no_cat)
|
||||||
|
|
||||||
|
if (dict_extras_no_cat is None) or (not dict_extras_no_cat):
|
||||||
|
dict_extras_no_cat = D4S_Extras(NOCATEOGORY, [])
|
||||||
|
|
||||||
|
#print ("adding key: %s "%k+" - value: %s"%v)
|
||||||
|
log.debug("NOCATEOGORY adding key: %s " % k + " - value: %s" % v)
|
||||||
|
|
||||||
|
dict_extras_no_cat.append_extra(k, v)
|
||||||
|
log.debug("dict_extras_no_cat %s " % dict_extras_no_cat)
|
||||||
|
extras_for_categories[NOCATEOGORY] = dict_extras_no_cat
|
||||||
|
log.debug("extras_for_categories NOCATEOGORY %s " % extras_for_categories)
|
||||||
|
|
||||||
|
return extras_for_categories
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import logging
|
||||||
|
import urllib.request, urllib.error, urllib.parse
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
|
import xmltodict
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from .d4s_namespaces import D4S_Namespaces
|
||||||
|
|
||||||
|
XPATH_NAMESPACES = "/Resource/Profile/Body/namespaces"
|
||||||
|
gcubeTokenParam = "gcube-token"
|
||||||
|
NAMESPACE_ID_LABEL = '@id'
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
def getResponseBody(uri):
|
||||||
|
req = urllib.request.Request(uri)
|
||||||
|
try:
|
||||||
|
resp = urllib.request.urlopen(req, timeout=20)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
log.error("Error on contacting URI: %s" % uri)
|
||||||
|
log.error("HTTPError: %d" % e.code)
|
||||||
|
return None
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
# Not an HTTP-specific error (e.g. connection refused)
|
||||||
|
log.error("URLError - Input URI: %s " % uri + " is not valid!!")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
# 200
|
||||||
|
body = resp.read()
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
# D4S_IS_DiscoveryCatalogueNamespaces is used to discovery namespaces for Catalogue Categories.
|
||||||
|
# @param: urlICProxy is the URI of IC proxy rest-full service provided by IS
|
||||||
|
# @param: resourceID is the resource ID of the Generic Resource: "Namespaces Catalogue Categories"
|
||||||
|
# @param: gcubeToken the gcube token used to contact the IC proxy
|
||||||
|
class D4S_IS_DiscoveryCatalogueNamespaces():
|
||||||
|
|
||||||
|
def __init__(self, urlICProxy, resourceID, gcubeToken):
|
||||||
|
self.urlICProxy = urlICProxy
|
||||||
|
self.resourceID = resourceID
|
||||||
|
self.gcubeToken = gcubeToken
|
||||||
|
|
||||||
|
def getNamespacesDictFromResource(self):
|
||||||
|
|
||||||
|
doc = {}
|
||||||
|
namespace_list = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
# print("proxy: "+self.urlICProxy)
|
||||||
|
# print("resourceID: " + self.resourceID)
|
||||||
|
# print("gcubeTokenParam: " + gcubeTokenParam)
|
||||||
|
# print("gcubeToken: " + self.gcubeToken)
|
||||||
|
|
||||||
|
uri = self.urlICProxy + "/" + self.resourceID + "?" + gcubeTokenParam + "=" + self.gcubeToken
|
||||||
|
log.info("Contacting URL: %s" % uri)
|
||||||
|
theResource = getResponseBody(uri)
|
||||||
|
log.debug("Resource returned %s " % theResource)
|
||||||
|
theResourceXML = etree.XML(theResource)
|
||||||
|
theNamespaces = theResourceXML.xpath(XPATH_NAMESPACES)
|
||||||
|
log.debug("The body %s" % etree.tostring(theNamespaces[0]))
|
||||||
|
|
||||||
|
if theNamespaces is not None and theNamespaces[0] is not None:
|
||||||
|
bodyToString = etree.tostring(theNamespaces[0])
|
||||||
|
doc = xmltodict.parse(bodyToString)
|
||||||
|
else:
|
||||||
|
log.warn("No Namespace for Catalogue Categories found, returning None")
|
||||||
|
except Exception as inst:
|
||||||
|
log.error("Error on getting catalogue namespaces: " + str(inst))
|
||||||
|
log.info("Returning empty list of namespaces")
|
||||||
|
return namespace_list
|
||||||
|
|
||||||
|
log.debug("IS namespaces resource to dict is: %s" % doc)
|
||||||
|
|
||||||
|
|
||||||
|
if ('namespaces' in doc):
|
||||||
|
# log.debug('Namespaces obj %s:' % doc['namespaces'])
|
||||||
|
namespaces = doc['namespaces']
|
||||||
|
if doc is not None and 'namespace' in namespaces:
|
||||||
|
namespace_list = namespaces['namespace']
|
||||||
|
|
||||||
|
log.info("Loaded %d namespaces from IS resource" % len(namespace_list))
|
||||||
|
return namespace_list
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_namespaces_dict_index_for_id(namespace_list):
|
||||||
|
namespace_dict = collections.OrderedDict()
|
||||||
|
log.debug("namespaces to dict: %s" % namespace_list)
|
||||||
|
try:
|
||||||
|
if namespace_list is not None and len(namespace_list) > 0:
|
||||||
|
for namespace in namespace_list:
|
||||||
|
try:
|
||||||
|
if NAMESPACE_ID_LABEL in namespace:
|
||||||
|
namespace_dict[namespace[NAMESPACE_ID_LABEL]] = D4S_Namespaces(
|
||||||
|
namespace[NAMESPACE_ID_LABEL],
|
||||||
|
namespace['name'],
|
||||||
|
namespace['title'],
|
||||||
|
namespace['description'])
|
||||||
|
except Exception as inst:
|
||||||
|
log.error("Error on converting catalogue namespaces: " + str(inst))
|
||||||
|
except Exception as inst:
|
||||||
|
log.error("Error on checking namespace_list: " + str(inst))
|
||||||
|
# print "namespace_dict to Nam: %s"%namespace_dict
|
||||||
|
return namespace_dict
|
0
compose/2.10/src/ckanext-d4science_theme/ckanext/d4science_theme/fanstatic/.gitignore
vendored
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
/* =====================================================
|
||||||
|
JavaScript used by CKAN plugin: 'd4science_theme'
|
||||||
|
Created by Francesco Mangiacrapa ISTI-CNR Pisa, Italy
|
||||||
|
===================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
CKAN_D4S_Breadcrumb_Manager = {
|
||||||
|
|
||||||
|
breadcrumbShow : function (show) {
|
||||||
|
|
||||||
|
var breadcrumb = document.getElementById('ckan-breadcrumb');
|
||||||
|
console.log('breadcrumb is '+breadcrumb)
|
||||||
|
if(breadcrumb){
|
||||||
|
if(show){
|
||||||
|
breadcrumb.style.display = 'block';
|
||||||
|
this.organizationTreeShow(true);
|
||||||
|
} else{
|
||||||
|
breadcrumb.style.display = 'none';
|
||||||
|
this.organizationTreeShow(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//var elements = document.getElementsByTagName('a');
|
||||||
|
//for(var i = 0, len = elements.length; i < len; i++) {
|
||||||
|
// elements[i].onclick = function () {
|
||||||
|
// //alert("You clicked an external link to: " + this.href);
|
||||||
|
// //window.parent.add_hide_breadcrumb_to_dom(false);
|
||||||
|
// this.add_hide_breadcrumb_to_dom(false);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
},
|
||||||
|
|
||||||
|
organizationTreeShow : function (show) {
|
||||||
|
var trees = document.getElementsByClassName("hierarchy-tree-top");
|
||||||
|
|
||||||
|
if (trees){
|
||||||
|
for (i = 0; i < trees.length; i++) {
|
||||||
|
if(show){
|
||||||
|
trees[i].style.display = 'block';
|
||||||
|
} else{
|
||||||
|
trees[i].style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkBreadcrumbShow : function () {
|
||||||
|
|
||||||
|
var showBdc = this.getSessionStorageItem("showbreadcrumb")
|
||||||
|
//console.log("showBdc is: "+showBdc)
|
||||||
|
if(showBdc != undefined && showBdc=="false"){
|
||||||
|
console.log("Show breadcrumb false");
|
||||||
|
this.breadcrumbShow(false);
|
||||||
|
}else{
|
||||||
|
console.log("Show breadcrumb true");
|
||||||
|
this.breadcrumbShow(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
setSessionStorageItem : function (item_key, item_value) {
|
||||||
|
|
||||||
|
// Check browser support
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
// Store
|
||||||
|
sessionStorage.setItem(item_key, item_value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log("Sorry, your browser does not support Web Storage...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getSessionStorageItem : function (item_key) {
|
||||||
|
|
||||||
|
// Check browser support
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
// Store
|
||||||
|
return sessionStorage.getItem(item_key);
|
||||||
|
} else {
|
||||||
|
console.log("Sorry, your browser does not support Web Storage...");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CKAN_D4S_Functions_Util = {
|
||||||
|
|
||||||
|
getPosition : function(canvas, event){
|
||||||
|
var x = new Number();
|
||||||
|
var y = new Number();
|
||||||
|
try {
|
||||||
|
if (event.clientX != undefined && event.clientY != undefined)
|
||||||
|
{
|
||||||
|
|
||||||
|
x = event.clientX;
|
||||||
|
y = event.clientY;
|
||||||
|
}
|
||||||
|
else // Firefox method to get the position
|
||||||
|
{
|
||||||
|
x = event.clientX + document.body.scrollLeft +
|
||||||
|
document.documentElement.scrollLeft;
|
||||||
|
y = event.clientY + document.body.scrollTop +
|
||||||
|
document.documentElement.scrollTop;
|
||||||
|
}
|
||||||
|
x -= canvas.offsetLeft;
|
||||||
|
y -= canvas.offsetTop;
|
||||||
|
}catch (err) {
|
||||||
|
//silent error
|
||||||
|
}
|
||||||
|
return '{"posX": "'+x+'", "posY": "'+y+'"}';
|
||||||
|
},
|
||||||
|
|
||||||
|
// When the user clicks on div, open the popup
|
||||||
|
showPopupD4S : function(event, my_div, my_popup_left_position) {
|
||||||
|
var popup = document.getElementById(my_div);
|
||||||
|
var clickPosition = this.getPosition(my_div, event)
|
||||||
|
var myPosition = JSON.parse(clickPosition);
|
||||||
|
this.closePopups(my_div);
|
||||||
|
// When the user clicks anywhere outside of the modal, close it
|
||||||
|
/*window.onclick = function(event) {
|
||||||
|
if (event.target != popup) {
|
||||||
|
popup.style.display = "none";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
popup.classList.toggle("show");
|
||||||
|
|
||||||
|
if(my_popup_left_position){
|
||||||
|
popup.style.left = my_popup_left_position;
|
||||||
|
}
|
||||||
|
else if (myPosition.posX){
|
||||||
|
popup.style.left = myPosition.posX + "px";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closePopups : function ($target) {
|
||||||
|
var popups = document.getElementsByClassName('popuptext');
|
||||||
|
for (i = 0; i < popups.length; i++) {
|
||||||
|
if (popups[i].getAttribute('id') != $target) {
|
||||||
|
popups[i].classList.remove('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
checkURL : function (url) {
|
||||||
|
//console.log('checking url: '+url)
|
||||||
|
var regex = new RegExp('^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/|www\.|ftp:\/\/)+[^ "]+$');
|
||||||
|
if (regex.test(url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CKAN_D4S_HTMLMessage_Util = {
|
||||||
|
|
||||||
|
postHeightToPortlet : function (selectedProduct, product) {
|
||||||
|
var h = document.body.scrollHeight + "px";
|
||||||
|
var p = "";
|
||||||
|
var msg = "";
|
||||||
|
//WORK AROUND IF TWO MESSAGES ARE SENT FROM A PAGE OF A PRODUCT
|
||||||
|
//THE MESSAGE WITH 'NULL' PRODUCT IS SKIPPED
|
||||||
|
//console.log("window.location.pathname? "+window.location.pathname);
|
||||||
|
var pathArray = window.location.pathname.split('/');
|
||||||
|
var productContext = "dataset";
|
||||||
|
if(pathArray.length>1){
|
||||||
|
//console.log("pathArray is: "+pathArray);
|
||||||
|
var secondLevelLocation = pathArray[1]; //it is the second level location
|
||||||
|
//console.log("secondLevelLocation is: "+secondLevelLocation);
|
||||||
|
//console.log("h is: "+h);
|
||||||
|
if(secondLevelLocation == productContext){ //is it product context?
|
||||||
|
if(product !== 'undefined' && product !== null){
|
||||||
|
p = product;
|
||||||
|
//console.log("product selected is: "+p);
|
||||||
|
}else{
|
||||||
|
//console.log("product is null or undefined, passing only height");
|
||||||
|
msg = "{\"height\": \""+h+"\"}";
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
this.postMessageToParentWindow(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//msg = "{'height': '"+h+"', 'product': '"+p+"'}";
|
||||||
|
msg = "{\"height\": \""+h+"\", \"product\": \""+p+"\"}";
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
//console.log("posting message in the window: "+msg);
|
||||||
|
this.postMessageToParentWindow(msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
postMessageToParentWindow : function (msg) {
|
||||||
|
//window.postMessage(msg,'*');
|
||||||
|
//console.log("posting message in the window: "+msg);
|
||||||
|
if(window.parent!=null){
|
||||||
|
console.log("posting message in the parent window: "+msg);
|
||||||
|
window.parent.postMessage(msg,'*');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CKAN_D4S_JSON_Util = {
|
||||||
|
|
||||||
|
|
||||||
|
//ADDED by Francesco Mangiacrapa
|
||||||
|
appendHTMLToElement : function(containerID, elementHTML){
|
||||||
|
|
||||||
|
var divContainer = document.getElementById(containerID);
|
||||||
|
divContainer.innerHTML = "";
|
||||||
|
divContainer.appendChild(elementHTML);
|
||||||
|
},
|
||||||
|
|
||||||
|
//ADDED by Francesco Mangiacrapa
|
||||||
|
jsonToHTML : function(containerID, cssClassToTable) {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonTxt = document.getElementById(containerID).innerHTML;
|
||||||
|
var jsonObj = JSON.parse(jsonTxt);
|
||||||
|
//console.log(jsonObj.length)
|
||||||
|
|
||||||
|
if(jsonObj.length==undefined)
|
||||||
|
jsonObj = [jsonObj]
|
||||||
|
//console.log(jsonObj.length)
|
||||||
|
|
||||||
|
// EXTRACT VALUE FOR HTML HEADER.
|
||||||
|
var col = [];
|
||||||
|
for (var i = 0; i < jsonObj.length; i++) {
|
||||||
|
for (var key in jsonObj[i]) {
|
||||||
|
//console.log('key json' +key)
|
||||||
|
if (col.indexOf(key) === -1) {
|
||||||
|
col.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE DYNAMIC TABLE.
|
||||||
|
var table = document.createElement("table");
|
||||||
|
var addDefaultCss = "json-to-html-table-column";
|
||||||
|
if(cssClassToTable){
|
||||||
|
addDefaultCss = cssClassToTable;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
table.classList.add(addDefaultCss);
|
||||||
|
}catch(e){
|
||||||
|
console.log('invalid css add', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADD JSON DATA TO THE TABLE AS ROWS.
|
||||||
|
for (var i = 0; i < col.length; i++) {
|
||||||
|
|
||||||
|
tr = table.insertRow(-1);
|
||||||
|
var firstCell = tr.insertCell(-1);
|
||||||
|
//firstCell.style.cssText="font-weight: bold; text-align: center; vertical-align: middle;";
|
||||||
|
firstCell.innerHTML = col[i];
|
||||||
|
for (var j = 0; j < jsonObj.length; j++) {
|
||||||
|
var tabCell = tr.insertCell(-1);
|
||||||
|
var theValue = jsonObj[j][col[i]];
|
||||||
|
/* console.log(theValue + ' is url? '+isUrl);*/
|
||||||
|
if(CKAN_D4S_Functions_Util.checkURL(theValue)){
|
||||||
|
theValue = '<a target="_blank" href='+theValue+'>'+theValue+'</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
tabCell.innerHTML = theValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
|
||||||
|
this.appendHTMLToElement(containerID, table);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log('invalid json', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Task #8032
|
||||||
|
window.addEventListener("message",
|
||||||
|
function (e) {
|
||||||
|
|
||||||
|
var curr_loc = window.location.toString()
|
||||||
|
var orgin = e.origin.toString()
|
||||||
|
if(curr_loc.startsWith(orgin)){
|
||||||
|
//alert("ignoring message from myself");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//console.log("origin: "+e.data)
|
||||||
|
if(e.data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pMess = JSON.parse(e.data)
|
||||||
|
//console.log(pMess.explore_vres_landing_page)
|
||||||
|
window.linktogateway = pMess.explore_vres_landing_page;
|
||||||
|
},false);
|
||||||
|
|
|
@ -0,0 +1,844 @@
|
||||||
|
/* =====================================================
|
||||||
|
The "account masthead" bar across the top of the site
|
||||||
|
===================================================== */
|
||||||
|
|
||||||
|
.account-masthead {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
/* The "bubble" containing the number of new notifications. */
|
||||||
|
.account-masthead .account .notifications a span {
|
||||||
|
background-color: #9fa0a2;
|
||||||
|
}
|
||||||
|
/* The text and icons in the user account info. */
|
||||||
|
.account-masthead .account ul li a {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
/* The user account info text and icons, when the user's pointer is hovering
|
||||||
|
over them. */
|
||||||
|
.account-masthead .account ul li a:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
/* background-color: black;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================
|
||||||
|
The main masthead bar that contains the site logo, nav links, and search
|
||||||
|
======================================================================== */
|
||||||
|
|
||||||
|
.masthead {
|
||||||
|
background: #eee url("/bg-noise.png") repeat scroll 0 0;
|
||||||
|
border-top: 1px solid #555;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 15px !important;
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
/* background-image: url("/bg-pattern.min.svg") !important; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.masthead .navigation .nav-pills li a{
|
||||||
|
color: #187794;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.logo > img{
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "navigation pills" in the masthead (the links to Datasets,
|
||||||
|
Organizations, etc) when the user's pointer hovers over them. */
|
||||||
|
.masthead .navigation .nav-pills li a:hover {
|
||||||
|
/* background-color: rgb(48, 48, 48);*/
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
/* The "active" navigation pill (for example, when you're on the /dataset page
|
||||||
|
the "Datasets" link is active). */
|
||||||
|
.masthead .navigation .nav-pills li.active a {
|
||||||
|
background-color: #d2d2d5;
|
||||||
|
}
|
||||||
|
/* The "box shadow" effect that appears around the search box when it
|
||||||
|
has the keyboard cursor's focus. */
|
||||||
|
.masthead input[type="text"]:focus {
|
||||||
|
-webkit-box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.7);
|
||||||
|
box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
The content in the middle of the front page
|
||||||
|
=========================================== */
|
||||||
|
|
||||||
|
/* Remove the "box shadow" effect around various boxes on the page. */
|
||||||
|
.box {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
background: #FEFEFE repeat scroll 0 0 !important;
|
||||||
|
}
|
||||||
|
/* Remove the borders around the "Welcome to CKAN" and "Search Your Data"
|
||||||
|
boxes. */
|
||||||
|
.hero .box {
|
||||||
|
/*border: none;*/
|
||||||
|
margin-top: 10px !important;
|
||||||
|
}
|
||||||
|
/* Change the colors of the "Search Your Data" box. */
|
||||||
|
.homepage .module-search .module-content {
|
||||||
|
color: rgb(68, 68, 68);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
/* Change the background color of the "Popular Tags" box. */
|
||||||
|
.homepage .module-search .tags {
|
||||||
|
background-color: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage-title{
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #202020;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change the background color of the "Popular Tags" box. */
|
||||||
|
.homepage .module-search h3{
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove some padding. This makes the bottom edges of the "Welcome to CKAN"
|
||||||
|
and "Search Your Data" boxes line up. */
|
||||||
|
.module-content:last-child {
|
||||||
|
/*padding-bottom: 0px;*/
|
||||||
|
}
|
||||||
|
.homepage .module-search {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
/* Add a border line between the top and bottom halves of the front page. */
|
||||||
|
.homepage [role="main"] {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage .stats ul li a b{
|
||||||
|
font-size: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="main"], .main {
|
||||||
|
/* background: #f5f6fa url("/bg-pattern.min.svg") repeat; scroll 0 0;*/
|
||||||
|
/*background: #fafafa url("/bg-pattern.svg") repeat; scroll 0 0;*/
|
||||||
|
background: #fdfdfd none repeat scroll 0 0;
|
||||||
|
min-height: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-item-homepage {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 3px;
|
||||||
|
float: left;
|
||||||
|
margin: 15px 0 0 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-heading-homepage {
|
||||||
|
font-size: 16px;
|
||||||
|
hyphens: auto;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-grid-homepage {
|
||||||
|
-moz-border-bottom-colors: none;
|
||||||
|
-moz-border-left-colors: none;
|
||||||
|
-moz-border-right-colors: none;
|
||||||
|
-moz-border-top-colors: none;
|
||||||
|
/* background: #fbfbfb url("../../../base/images/bg.png") repeat scroll 0 0;
|
||||||
|
border-color: #dddddd;
|
||||||
|
border-image: none;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px 0;*/
|
||||||
|
list-style: outside none none;
|
||||||
|
margin: 0 -10px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
.media-grid-homepage::before, .media-grid::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.media-grid-homepage::after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-circle{
|
||||||
|
padding: 10px 10px;
|
||||||
|
display: inline-block !important;
|
||||||
|
-webkit-border-radius: 90px;
|
||||||
|
-moz-border-radius: 90px;
|
||||||
|
border-radius: 90px;
|
||||||
|
background-color: #4679b2;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-white{
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-circle {
|
||||||
|
border-radius: 50% 50% 50% 50% !important;
|
||||||
|
height: 60px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 65px;
|
||||||
|
background-color: #4679b2;
|
||||||
|
display: inline-block !important;
|
||||||
|
padding-top: 5px;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
The footer at the bottom of the site
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
.site-footer, body {
|
||||||
|
background-color: #bbb;
|
||||||
|
font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
/* The text in the footer. */
|
||||||
|
.site-footer,
|
||||||
|
.site-footer label,
|
||||||
|
.site-footer small {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
/* The link texts in the footer. */
|
||||||
|
.site-footer a {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal{
|
||||||
|
min-height: 10px;
|
||||||
|
padding: 2px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal {
|
||||||
|
/*background-color: rgba(255, 255, 255, 0.6);*/
|
||||||
|
text-align: center;
|
||||||
|
/*display: inline-block;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal,
|
||||||
|
.site-footer-internal label,
|
||||||
|
.site-footer-internal small {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-internal a {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-hide-text {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0 none;
|
||||||
|
color: transparent;
|
||||||
|
font: 0px/0 a;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4science-footer-logo {
|
||||||
|
background: url("/gCube_70.png") no-repeat scroll left top rgba(0, 0, 0, 0);
|
||||||
|
height: 32px;
|
||||||
|
margin-top: 2px;
|
||||||
|
text-indent: -900em;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-ckan-footer-logo {
|
||||||
|
background: rgba(0, 0, 0, 0) url("/ckan-logo-footer.png") no-repeat scroll left top;
|
||||||
|
height: 21px;
|
||||||
|
margin-top: 2px;
|
||||||
|
text-indent: -900em;
|
||||||
|
width: 69px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-d4science {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #f5f5f5;
|
||||||
|
text-align: center;
|
||||||
|
height: 25px;
|
||||||
|
padding-top: 5px;
|
||||||
|
background-color: #7F7F7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer-d4science a {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Base elements of the site
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
div .principaltitle {
|
||||||
|
color: inherit;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 15px 0;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
word-break: break-all;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .notes {
|
||||||
|
color: #444444;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-align: justify;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .infotitle {
|
||||||
|
font-size: 15px;
|
||||||
|
hyphens: auto;
|
||||||
|
line-height: 1.3;
|
||||||
|
word-break: break-all;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar .breadcrumb{
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box{
|
||||||
|
border: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .sectiontitle{
|
||||||
|
color: #9F9F9F;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 20px 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
section .well {
|
||||||
|
background-color: #fdfdfd !important;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-heading {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataset-resources .resource-list{
|
||||||
|
background-color: #fdfdfd !important;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin: -1px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper{
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 3px
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-popular{
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-homepage{
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-show{
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #444444;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s-center-cropped{
|
||||||
|
text-align: center;
|
||||||
|
background-color: #eee;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Acquired Dataset
|
||||||
|
==================================== */
|
||||||
|
.label-acquired {
|
||||||
|
background-color: #55a1ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-owner {
|
||||||
|
background-color: #e0051e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin-left:10px;
|
||||||
|
height:auto;
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
List Dataset
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
/*LEFT
|
||||||
|
.show_meatadatatype {
|
||||||
|
color: white;
|
||||||
|
display: inline-block; // Inline elements with width and height. TL;DR they make the icon buttons stack from left-to-right instead of top-to-bottom
|
||||||
|
position: relative; // All 'absolute'ly positioned elements are relative to this one
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*RIGHT*/
|
||||||
|
.show_meatadatatype {
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-top: -20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* LEFT
|
||||||
|
* Position the badge within the relatively positioned button
|
||||||
|
.button__badge {
|
||||||
|
background-color: #fa3e3e;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
padding: 1px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* RIGTH */
|
||||||
|
.button__badge {
|
||||||
|
color: #808080;
|
||||||
|
padding: 0px 2px;
|
||||||
|
font-size: 10px;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
font-family: sans-serif, times, georgia;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================
|
||||||
|
Modal Popup
|
||||||
|
==================================== */
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
/* The Modal (background) */
|
||||||
|
.d4s_modal {
|
||||||
|
display: none; /* Hidden by default */
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
z-index: 10001; /* Sit on top (NB. At 1000 there is the zoom in/out of the Map Widget)*/
|
||||||
|
/*padding-top: 100px;*/ /* Location of the box */
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%; /* Full width */
|
||||||
|
height: 100%; /* Full height */
|
||||||
|
overflow: auto; /* Enable scroll if needed */
|
||||||
|
background-color: rgb(0,0,0); /* Fallback color */
|
||||||
|
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Content */
|
||||||
|
.d4s_modal-content {
|
||||||
|
background-color: #fefefe;
|
||||||
|
/*margin: auto;*/
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -225px;
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Close Button */
|
||||||
|
.d4s_close {
|
||||||
|
color: #aaaaaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s_close:hover,
|
||||||
|
.d4s_close:focus {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d4s_div_clickable{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
D4S POPUP
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
.popupD4SNoArrow {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual popup */
|
||||||
|
.popupD4SNoArrow .popuptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 300px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle this class - hide and show the popup */
|
||||||
|
.popupD4SNoArrow .show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadeIn 1s;
|
||||||
|
animation: fadeIn 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Popup container - can be anything you want */
|
||||||
|
.popupD4S {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual popup */
|
||||||
|
.popupD4S .popuptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 300px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Popup arrow */
|
||||||
|
.popupD4S .popuptext::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #555 transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle this class - hide and show the popup */
|
||||||
|
.popupD4S .show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadeIn 1s;
|
||||||
|
animation: fadeIn 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add animation (fade in the popup) */
|
||||||
|
@-webkit-keyframes fadeIn {
|
||||||
|
from {opacity: 0;}
|
||||||
|
to {opacity: 1;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {opacity: 0;}
|
||||||
|
to {opacity:1 ;}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
D4S PACKAGE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.graphic-preview-style {
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px dotted #DDD;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style a{
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style img{
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphic-preview-style #graphic-title{
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item{
|
||||||
|
word-wrap:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
RESOURCE_LIST RESOURCE_ITEM INTO PACKAGE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.required-access {
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
LINK TO RESOURCES FROM PACKAGE LIST
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.dataset-resources li a {
|
||||||
|
background-color: #187794;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label[data-format="csw"], .label[data-format*="csw"] {
|
||||||
|
background-color: #e6b800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED TO Similar GRSF Records
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.my-grsf-table{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-grsf-table tr td{
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-grsf-table tr td:first-child{
|
||||||
|
width: 82px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in base.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
#ckan-page-loading {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -130px;
|
||||||
|
margin-left: -130px;
|
||||||
|
width: 260px;
|
||||||
|
height: 260px;
|
||||||
|
z-index: 100000;
|
||||||
|
background-image: url("/pageloading.gif");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in search_for_location.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
div#search-for-location {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-location #dataset-map {
|
||||||
|
position: relative !important;
|
||||||
|
top: +0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-location #dataset-map-container {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div#search-for-location .module-heading {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#search-for-extent{
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED in additional_info.html
|
||||||
|
======================================*/
|
||||||
|
.qr-code-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td {
|
||||||
|
width: 85%;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td:first-child {
|
||||||
|
padding-left: 10px;
|
||||||
|
border-right-style: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-table td:last-child {
|
||||||
|
width: 105px;
|
||||||
|
text-align: center;
|
||||||
|
border-left-style: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAX-WITH APPIED TO QR_CODE */
|
||||||
|
.qr-code-table img {
|
||||||
|
max-width: 100px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED FROM JSON TO HTML TABLE
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.json-to-html-table-column{
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-to-html-table-column tr td{
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-to-html-table-column tr td:first-child{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #5a5a5a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED into custom_form_fields
|
||||||
|
======================================*/
|
||||||
|
.disabled-div{
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-div input[type="text"]{
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================
|
||||||
|
CSS APPLIED into extra_table.html
|
||||||
|
======================================*/
|
||||||
|
|
||||||
|
.read-more-state {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-target {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
transition: .25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-wrap .read-more-target {
|
||||||
|
opacity: 1;
|
||||||
|
font-size: inherit;
|
||||||
|
max-height: 999em;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state ~ .read-more-trigger:before {
|
||||||
|
content: 'Show more';
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-trigger:before {
|
||||||
|
content: 'Show less';
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-state:checked ~ .read-more-wrap::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-trigger {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 .5em;
|
||||||
|
color: #187794;
|
||||||
|
font-size: .9em;
|
||||||
|
line-height: 2;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: .25em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-trigger::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-wrap {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-more-wrap::after{
|
||||||
|
content: " ...";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,721 @@
|
||||||
|
from multiprocessing import context
|
||||||
|
import ckan.authz as authz
|
||||||
|
import ckan.model as model
|
||||||
|
from webhelpers2.html import literal
|
||||||
|
from webhelpers2.text import truncate
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
import ckan.logic as logic
|
||||||
|
from ckan.common import config
|
||||||
|
from ckanext.d4science_theme.d4sdiscovery.d4s_namespaces_controller import D4S_Namespaces_Controller
|
||||||
|
from ckanext.d4science_theme.d4sdiscovery.d4s_namespaces_extras_util import D4S_Namespaces_Extra_Util
|
||||||
|
from ckanext.d4science_theme.qrcodelink.generate_qrcode import D4S_QrCode
|
||||||
|
import urllib.request, urllib.error, urllib.parse
|
||||||
|
|
||||||
|
from ckan.common import (
|
||||||
|
_, g, c, request, session
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
from operator import itemgetter
|
||||||
|
from logging import getLogger
|
||||||
|
import base64
|
||||||
|
import sys, os, re
|
||||||
|
import configparser
|
||||||
|
import collections
|
||||||
|
import ckan.plugins.toolkit as tk
|
||||||
|
import ckan.logic as logic
|
||||||
|
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
systemtype_field = 'systemtypefield'
|
||||||
|
systemtype_field_default_value = 'system:type'
|
||||||
|
ic_proxy_url_field = 'ic_proxy_url'
|
||||||
|
ic_proxy_url_field_default_value = "https://registry.d4science.org/icproxy/gcube/service"
|
||||||
|
application_token_field = 'application_token'
|
||||||
|
namespaces_generic_resource_id_default_value = "23d827cd-ba8e-4d8c-9ab4-6303bdb7d1db"
|
||||||
|
namespaces_gr_id_fieldname = "namespaces_generic_resource_id"
|
||||||
|
namespaceseparator_field = 'namespace_separator'
|
||||||
|
namespaceseparator_field_default_value = ':'
|
||||||
|
systemtype_rgb_colors = ['#c0392b ', '#585858', '#04407C', '#9b59b6', '#2ecc71', '#16a085', '#7f8c8d ', '#2ecc71',
|
||||||
|
'#FA8072', '#00FFFF', '#C76611', '#f39c12', '#800000']
|
||||||
|
systemtype_field_colors = 'systemtype_field_colors'
|
||||||
|
|
||||||
|
systemtype_cms_fields_placeholders = {'prefix': 'system:cm_', 'item_status': 'system:cm_item_status'}
|
||||||
|
|
||||||
|
NOCATEOGORY = 'nocategory'
|
||||||
|
TRANSLATE_OF_ = 'translate_of_'
|
||||||
|
|
||||||
|
ctg_namespace_ctrl = None
|
||||||
|
|
||||||
|
# def get_tag_list()
|
||||||
|
# return logic.get_action('tag_list')
|
||||||
|
|
||||||
|
# ADDED BY FRANCESCO.MANGIACRAPA, related to Task #5196
|
||||||
|
def get_user_role_for_group_or_org(group_id, user_name):
|
||||||
|
''' Returns the user's role for the group. (Ignores privileges that cascade
|
||||||
|
in a group hierarchy.)'''
|
||||||
|
return authz.users_role_for_group_or_org(group_id, user_name)
|
||||||
|
|
||||||
|
|
||||||
|
# ADDED BY FRANCESCO.MANGIACRAPA, related to breadcrumb for Group
|
||||||
|
def get_parents_for_group(group_name_or_id):
|
||||||
|
''' Returns the user's role for the group. (Ignores privileges that cascade
|
||||||
|
in a group hierarchy.)'''
|
||||||
|
group = model.Group.get(group_name_or_id)
|
||||||
|
if group:
|
||||||
|
return model.Group.get_parent_group_hierarchy(group)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ADDED BY FRANCESCO.MANGIACRAPA
|
||||||
|
def get_header_param(parameter_name, default=None):
|
||||||
|
''' This function allows templates to access header string parameters
|
||||||
|
from the request. '''
|
||||||
|
return request.headers.get(parameter_name, default)
|
||||||
|
|
||||||
|
|
||||||
|
# ADDED BY FRANCESCO.MANGIACRAPA
|
||||||
|
def get_request_param(parameter_name, default=None):
|
||||||
|
''' This function allows templates to access query string parameters
|
||||||
|
from the request. '''
|
||||||
|
return request.args.get(parameter_name, default)
|
||||||
|
|
||||||
|
|
||||||
|
# ADDED BY FRANCESCO.MANGIACRAPA
|
||||||
|
def get_cookie_value(cookie_name, default=None):
|
||||||
|
''' This function allows templates to access cookie by cookie_name parameter
|
||||||
|
from the request. '''
|
||||||
|
|
||||||
|
value = request.cookies.get(cookie_name)
|
||||||
|
|
||||||
|
if value is None:
|
||||||
|
print(('cookie: ' + cookie_name + ', has value None'))
|
||||||
|
else:
|
||||||
|
print(('cookie: ' + cookie_name + ', has value ' + value))
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# Updated BY FRANCESCO.MANGIACRAPA, added allow_html
|
||||||
|
def markdown_extract_html(text, extract_length=190, allow_html=False):
|
||||||
|
''' Returns the plain text representation of markdown encoded text. That
|
||||||
|
is the texted without any html tags. If extract_length is 0 then it
|
||||||
|
will not be truncated.'''
|
||||||
|
if not text:
|
||||||
|
return ''
|
||||||
|
if allow_html:
|
||||||
|
plain = h.markdown(text.strip())
|
||||||
|
else:
|
||||||
|
plain = h.RE_MD_HTML_TAGS.sub('', h.markdown(text))
|
||||||
|
|
||||||
|
if not extract_length or len(plain) < extract_length:
|
||||||
|
return literal(plain)
|
||||||
|
return literal(str(truncate(plain, length=extract_length, indicator='...', whole_word=True)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_systemtype_field_dict_from_session():
|
||||||
|
'''Return the value of 'ckan.d4science_theme.metadatatypefield'
|
||||||
|
read from production.ini'''
|
||||||
|
|
||||||
|
systemtype_fieldname = session.get(systemtype_field)
|
||||||
|
|
||||||
|
if systemtype_fieldname is None:
|
||||||
|
log.info(systemtype_field + " not found in session, loading from config")
|
||||||
|
else:
|
||||||
|
log.debug(systemtype_field + " found in session having value: %s" % systemtype_fieldname)
|
||||||
|
return systemtype_fieldname
|
||||||
|
|
||||||
|
systemtype_fieldname = config.get('ckan.d4science_theme.' + systemtype_field)
|
||||||
|
|
||||||
|
if systemtype_fieldname is None:
|
||||||
|
log.info(
|
||||||
|
systemtype_field + " field does not exist in production.ini, returning default value %s" % systemtype_field_default_value)
|
||||||
|
systemtype_fieldname = systemtype_field_default_value
|
||||||
|
|
||||||
|
separator = get_namespace_separator_from_session()
|
||||||
|
log.debug("Replacing %s" % separator + " with empty string for key %s" % systemtype_field)
|
||||||
|
systemtype_fieldname_name = systemtype_fieldname.replace(separator, "")
|
||||||
|
purgedfieldname = purge_namespace_to_fieldname(systemtype_fieldname)
|
||||||
|
log.debug("Setting %s" % systemtype_fieldname + " in session for key %s" % systemtype_field)
|
||||||
|
session[systemtype_field] = {'id': systemtype_fieldname, 'name': systemtype_fieldname_name,
|
||||||
|
'title': purgedfieldname}
|
||||||
|
session.save()
|
||||||
|
return session[systemtype_field]
|
||||||
|
|
||||||
|
|
||||||
|
def get_d4s_namespace_controller():
|
||||||
|
'''Instance the D4S_Namespaces_Controller and check that the namespaces are not empty reading it from IS and/or using a Caching system.
|
||||||
|
The ic-proxy-url is built by reading the configurations from production.ini'''
|
||||||
|
|
||||||
|
d4s_extras_controller = D4S_Namespaces_Controller.getInstance()
|
||||||
|
global ctg_namespace_ctrl
|
||||||
|
|
||||||
|
if ctg_namespace_ctrl is not None:
|
||||||
|
log.info("ctg_namespace_ctrl with configurations is NOT None")
|
||||||
|
the_namespaces = d4s_extras_controller.load_namespaces(ctg_namespace_ctrl['ic_proxy_url'],
|
||||||
|
ctg_namespace_ctrl['resource_id'],
|
||||||
|
ctg_namespace_ctrl['application_token'])
|
||||||
|
log.debug("the_namespaces are %s" % the_namespaces)
|
||||||
|
|
||||||
|
if the_namespaces is None or len(the_namespaces) == 0:
|
||||||
|
log.info("D4S_Namespaces_Controller obj with none or empty namespaces, going to read them")
|
||||||
|
else:
|
||||||
|
log.info("d4s_namespaces_controller found and the namespaces property is not empty: %s" % d4s_extras_controller)
|
||||||
|
return d4s_extras_controller
|
||||||
|
else:
|
||||||
|
log.info("ctg_namespace_ctrl with configurations is None, instancing it")
|
||||||
|
|
||||||
|
ic_proxy_url_value = config.get('ckan.d4science_theme.' + ic_proxy_url_field)
|
||||||
|
|
||||||
|
if ic_proxy_url_value is None:
|
||||||
|
log.info(
|
||||||
|
"ckan.d4science_theme." + ic_proxy_url_field + " field does not exist in production.ini, returning default value %s" % ic_proxy_url_field_default_value)
|
||||||
|
ic_proxy_url_value = ic_proxy_url_field_default_value
|
||||||
|
|
||||||
|
application_token_fieldname = config.get('ckan.d4science_theme.' + application_token_field)
|
||||||
|
|
||||||
|
if application_token_fieldname is None:
|
||||||
|
log.error("ckan.d4science_theme." + application_token_field + " field does not exist in production.ini!!!")
|
||||||
|
application_token_fieldname = None
|
||||||
|
|
||||||
|
namespaces_gr_id_fieldname_value = config.get('ckan.d4science_theme.' + namespaces_gr_id_fieldname)
|
||||||
|
|
||||||
|
if namespaces_gr_id_fieldname_value is None:
|
||||||
|
log.error("ckan.d4science_theme." + application_token_field + " field does not exist in production.ini!!!")
|
||||||
|
namespaces_gr_id_fieldname_value = namespaces_generic_resource_id_default_value
|
||||||
|
|
||||||
|
# filling the ctg_namespace_ctrl with IS configurations to perform the query for loading the namespaces from IS
|
||||||
|
ctg_namespace_ctrl = {'ic_proxy_url': ic_proxy_url_value,
|
||||||
|
'application_token': application_token_fieldname,
|
||||||
|
'resource_id': namespaces_gr_id_fieldname_value}
|
||||||
|
|
||||||
|
d4s_extras_controller.load_namespaces(ctg_namespace_ctrl['ic_proxy_url'], ctg_namespace_ctrl['resource_id'],
|
||||||
|
ctg_namespace_ctrl['application_token'])
|
||||||
|
|
||||||
|
return d4s_extras_controller
|
||||||
|
|
||||||
|
|
||||||
|
def get_extras_indexed_for_namespaces(extras):
|
||||||
|
namespace_dict = get_namespaces_dict()
|
||||||
|
# log.info("my_namespace_dict %s" % namespace_dict)
|
||||||
|
my_extra = get_extras(extras)
|
||||||
|
# log.info("my_extra is %s" % my_extra)
|
||||||
|
# d4s_extras_controller = D4S_Namespaces_Controller.getInstance()
|
||||||
|
# extras_indexed_for_categories = d4s_extras_controller.get_extras_indexed_for_namespaces(namespace_dict, my_extra)
|
||||||
|
|
||||||
|
extras_indexed_for_categories = D4S_Namespaces_Extra_Util().get_extras_indexed_for_namespaces(namespace_dict,
|
||||||
|
my_extra)
|
||||||
|
return extras_indexed_for_categories
|
||||||
|
|
||||||
|
|
||||||
|
def get_namespaces_dict():
|
||||||
|
d4s_extras_controller = get_d4s_namespace_controller()
|
||||||
|
|
||||||
|
if d4s_extras_controller is not None:
|
||||||
|
return d4s_extras_controller.get_dict_ctg_namespaces()
|
||||||
|
else:
|
||||||
|
log.info("local_extras_controller is null, returning empty dictionary for namespaces")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_extra_for_category(extras_indexed_for_categories, key_category):
|
||||||
|
if key_category in extras_indexed_for_categories:
|
||||||
|
catalogue_namespace = extras_indexed_for_categories[key_category]
|
||||||
|
return catalogue_namespace.extras
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_systemtype_value_from_extras(package, extras=None):
|
||||||
|
'''Returns the value of metadata fied read from key 'metadatatype'
|
||||||
|
stored into extra fields if it exists, 'No Type' otherwise'''
|
||||||
|
systemtype_dict = get_systemtype_field_dict_from_session()
|
||||||
|
|
||||||
|
no_type = 'No Type'
|
||||||
|
|
||||||
|
if extras is None:
|
||||||
|
return no_type
|
||||||
|
|
||||||
|
for extra in extras:
|
||||||
|
k, v = extra['key'], extra['value']
|
||||||
|
log.debug("key is %s" % k)
|
||||||
|
log.debug("value is %s" % v)
|
||||||
|
if k == str(systemtype_dict['id']):
|
||||||
|
return v
|
||||||
|
|
||||||
|
return no_type
|
||||||
|
|
||||||
|
|
||||||
|
def get_namespace_separator_from_session():
|
||||||
|
'''Returns the character used to separate namespace from fieldname'''
|
||||||
|
|
||||||
|
separator = session.get(namespaceseparator_field)
|
||||||
|
|
||||||
|
if separator is None:
|
||||||
|
log.info(namespaceseparator_field + " not found in session, loading from config")
|
||||||
|
else:
|
||||||
|
log.debug(namespaceseparator_field + " found in session: %s" % separator)
|
||||||
|
return separator
|
||||||
|
|
||||||
|
namespace_sep = config.get('ckan.d4science_theme.' + namespaceseparator_field)
|
||||||
|
|
||||||
|
if namespace_sep is None:
|
||||||
|
log.info(
|
||||||
|
namespaceseparator_field + " field does not exist in production.ini, returning default value %s" % namespaceseparator_field_default_value)
|
||||||
|
namespace_sep = namespaceseparator_field_default_value
|
||||||
|
|
||||||
|
log.debug("Setting %s" % namespace_sep + " in session for key %s" % namespaceseparator_field)
|
||||||
|
session[namespaceseparator_field] = namespace_sep
|
||||||
|
return namespace_sep
|
||||||
|
|
||||||
|
|
||||||
|
def get_extras(package_extras, auto_clean=False, subs=None, exclude=None):
|
||||||
|
''' Used for outputting package extras
|
||||||
|
|
||||||
|
:param package_extras: the package extras
|
||||||
|
:type package_extras: dict
|
||||||
|
:param auto_clean: If true capitalize and replace -_ with spaces
|
||||||
|
:type auto_clean: bool
|
||||||
|
:param subs: substitutes to use instead of given keys
|
||||||
|
:type subs: dict {'key': 'replacement'}
|
||||||
|
:param exclude: keys to exclude
|
||||||
|
:type exclude: list of strings
|
||||||
|
'''
|
||||||
|
|
||||||
|
# If exclude is not supplied use values defined in the config
|
||||||
|
if not exclude:
|
||||||
|
exclude = g.package_hide_extras
|
||||||
|
output = []
|
||||||
|
for extra in package_extras:
|
||||||
|
if extra.get('state') == 'deleted':
|
||||||
|
continue
|
||||||
|
k, v = extra['key'], extra['value']
|
||||||
|
if k in exclude:
|
||||||
|
continue
|
||||||
|
if subs and k in subs:
|
||||||
|
k = subs[k]
|
||||||
|
elif auto_clean:
|
||||||
|
k = k.replace('_', ' ').replace('-', ' ').title()
|
||||||
|
if isinstance(v, (list, tuple)):
|
||||||
|
v = ", ".join(map(str, v))
|
||||||
|
output.append((k, v))
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def purge_namespace_to_fieldname(fieldname):
|
||||||
|
separator = get_namespace_separator_from_session()
|
||||||
|
|
||||||
|
if fieldname is None:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if separator not in fieldname:
|
||||||
|
return fieldname
|
||||||
|
|
||||||
|
end = fieldname.index(separator) + 1
|
||||||
|
max_l = len(fieldname)
|
||||||
|
if end < max_l:
|
||||||
|
return fieldname[end:max_l]
|
||||||
|
return fieldname
|
||||||
|
|
||||||
|
|
||||||
|
def purge_namespace_to_string(facet):
|
||||||
|
if not c.search_facets or \
|
||||||
|
not c.search_facets.get(facet) or \
|
||||||
|
not c.search_facets.get(facet).get('items'):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
facet_name = c.search_facets.get(facet)
|
||||||
|
print(("facet_name " + str(facet_name)))
|
||||||
|
end = str(facet_name).index(":")
|
||||||
|
if end <= len(facet_name):
|
||||||
|
return facet_name[:end]
|
||||||
|
return facet_name
|
||||||
|
|
||||||
|
|
||||||
|
def count_facet_items_dict(facet, limit=None, exclude_active=False):
|
||||||
|
if not c.search_facets or \
|
||||||
|
not c.search_facets.get(facet) or \
|
||||||
|
not c.search_facets.get(facet).get('items'):
|
||||||
|
return 0
|
||||||
|
facets = []
|
||||||
|
for facet_item in c.search_facets.get(facet)['items']:
|
||||||
|
if not len(facet_item['name'].strip()):
|
||||||
|
continue
|
||||||
|
if not (facet, facet_item['name']) in list(request.args.items()):
|
||||||
|
facets.append(dict(active=False, **facet_item))
|
||||||
|
elif not exclude_active:
|
||||||
|
facets.append(dict(active=True, **facet_item))
|
||||||
|
|
||||||
|
# for count,
|
||||||
|
# print "facets " + str(facets)
|
||||||
|
total = len(facets)
|
||||||
|
log.debug("total facet: %s" % facet + " are %d" % total)
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def random_color():
|
||||||
|
rgbl = [255, 0, 0]
|
||||||
|
random.shuffle(rgbl)
|
||||||
|
return tuple(rgbl)
|
||||||
|
|
||||||
|
|
||||||
|
def check_url(the_url):
|
||||||
|
try:
|
||||||
|
urllib.request.urlopen(the_url)
|
||||||
|
return True
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
# print(e.code)
|
||||||
|
return False
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
# print(e.args)
|
||||||
|
return False
|
||||||
|
except Exception as error:
|
||||||
|
# print(error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_color_for_type(systemtype_field_value):
|
||||||
|
'''Return a color assigned to a system type'''
|
||||||
|
|
||||||
|
systemtypecolors = session.get(systemtype_field_colors)
|
||||||
|
# log.info("color: getting color for type: %s" %systemtype_field_value)
|
||||||
|
|
||||||
|
if systemtypecolors is None:
|
||||||
|
log.info("color: " + systemtype_field_colors + " not found in session, creating new one")
|
||||||
|
systemtypecolors = {}
|
||||||
|
session[systemtype_field_colors] = systemtypecolors
|
||||||
|
else:
|
||||||
|
log.debug("color: " + systemtype_field_colors + " found in session having value: %s" % systemtypecolors)
|
||||||
|
|
||||||
|
e_color = systemtypecolors.get(systemtype_field_value)
|
||||||
|
|
||||||
|
if e_color is None:
|
||||||
|
usedcolorsLen = len(systemtypecolors)
|
||||||
|
colorsLen = len(systemtype_rgb_colors)
|
||||||
|
index = usedcolorsLen if usedcolorsLen < colorsLen else random.randint(0, colorsLen - 1)
|
||||||
|
e_color = systemtype_rgb_colors[index]
|
||||||
|
# log.debug("color: adding color %s" %e_color +" index is: "+str(index))
|
||||||
|
systemtypecolors[systemtype_field_value] = e_color
|
||||||
|
session[systemtype_field_colors] = systemtypecolors
|
||||||
|
|
||||||
|
session.save()
|
||||||
|
# log.debug("color: returning color %s" %e_color +" for type: "+systemtype_field_value)
|
||||||
|
return e_color
|
||||||
|
|
||||||
|
|
||||||
|
def ordered_dictionary(list_to_be_sorted, property='name', ordering="asc"):
|
||||||
|
# print ("dict %s" %list_to_be_sorted)
|
||||||
|
|
||||||
|
ord = False if ordering == "asc" else True
|
||||||
|
|
||||||
|
if list_to_be_sorted:
|
||||||
|
return sorted(list_to_be_sorted, key=itemgetter(property), reverse=ord)
|
||||||
|
|
||||||
|
return list_to_be_sorted
|
||||||
|
|
||||||
|
|
||||||
|
def qrcode_for_url(url):
|
||||||
|
if url:
|
||||||
|
try:
|
||||||
|
qr_code = D4S_QrCode(url)
|
||||||
|
image_path = qr_code.get_qrcode_path()
|
||||||
|
with open(image_path, "rb") as image_file:
|
||||||
|
return base64.b64encode(image_file.read())
|
||||||
|
return ""
|
||||||
|
except Exception as error:
|
||||||
|
log.error("Error on getting qrcode for url: " + url + "error: %s" % error)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_list_of_organizations(limit=10, sort='packages'):
|
||||||
|
to_browse_organizations = []
|
||||||
|
try:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if sort:
|
||||||
|
data['sort'] = sort
|
||||||
|
|
||||||
|
data['limit'] = limit
|
||||||
|
data['all_fields'] = True
|
||||||
|
ordered_organizations = []
|
||||||
|
ordered_organizations = logic.get_action('organization_list')({}, data)
|
||||||
|
|
||||||
|
for organization in ordered_organizations:
|
||||||
|
try:
|
||||||
|
to_browse_obj = {}
|
||||||
|
|
||||||
|
if not organization['name']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_browse_obj['name'] = organization['name']
|
||||||
|
|
||||||
|
if 'package_count' in organization:
|
||||||
|
to_browse_obj['package_count'] = organization['package_count']
|
||||||
|
|
||||||
|
if 'display_name' in organization:
|
||||||
|
to_browse_obj['display_name'] = organization['display_name']
|
||||||
|
|
||||||
|
image_url = get_url_to_icon_for_ckan_entity(organization['name'], 'organization', False)
|
||||||
|
|
||||||
|
# Using ICON as first option
|
||||||
|
if image_url:
|
||||||
|
to_browse_obj['url'] = image_url
|
||||||
|
# Using object image_url as second one
|
||||||
|
elif 'image_url' in organization and organization['image_url']:
|
||||||
|
to_browse_obj['url'] = organization['image_url']
|
||||||
|
# Default placeholder
|
||||||
|
else:
|
||||||
|
to_browse_obj['url'] = h.url_for_static('/images/organisations/icon/placeholder-organization.png')
|
||||||
|
|
||||||
|
to_browse_organizations.append(to_browse_obj)
|
||||||
|
except (logic.NotFound, logic.ValidationError, logic.NotAuthorized) as error:
|
||||||
|
# SILENT
|
||||||
|
log.warn("Error on putting organization: %s" % error)
|
||||||
|
|
||||||
|
log.info("browse %d" % len(ordered_organizations) + " organisation/s")
|
||||||
|
except (logic.NotFound, logic.ValidationError, logic.NotAuthorized) as error:
|
||||||
|
log.error("Error on getting organizations: %s" % error)
|
||||||
|
return []
|
||||||
|
|
||||||
|
return to_browse_organizations
|
||||||
|
|
||||||
|
|
||||||
|
def get_list_of_groups(limit=10, sort='package_count'):
|
||||||
|
to_browse_groups = []
|
||||||
|
try:
|
||||||
|
data = {}
|
||||||
|
if sort:
|
||||||
|
data['sort'] = sort
|
||||||
|
|
||||||
|
data['limit'] = limit
|
||||||
|
data['all_fields'] = True
|
||||||
|
ordered_groups = []
|
||||||
|
ordered_groups = logic.get_action('group_list')({}, data)
|
||||||
|
|
||||||
|
for group in ordered_groups:
|
||||||
|
# print "\n\ngroup %s" %group
|
||||||
|
try:
|
||||||
|
to_browse_obj = {}
|
||||||
|
|
||||||
|
if not group['name']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_browse_obj['name'] = group['name']
|
||||||
|
|
||||||
|
if 'package_count' in group:
|
||||||
|
to_browse_obj['package_count'] = group['package_count']
|
||||||
|
|
||||||
|
if 'display_name' in group:
|
||||||
|
to_browse_obj['display_name'] = group['display_name']
|
||||||
|
|
||||||
|
if 'image_url' in group and group['image_url']:
|
||||||
|
to_browse_obj['url'] = group['image_url']
|
||||||
|
else:
|
||||||
|
to_browse_obj['url'] = get_url_to_icon_for_ckan_entity(group['name'], 'group')
|
||||||
|
|
||||||
|
to_browse_groups.append(to_browse_obj)
|
||||||
|
except (logic.NotFound, logic.ValidationError, logic.NotAuthorized) as error:
|
||||||
|
# SILENT
|
||||||
|
log.warn("Error on putting group: %s" % error)
|
||||||
|
|
||||||
|
log.info("browse %d" % len(ordered_groups) + " organisation/s")
|
||||||
|
except (logic.NotFound, logic.ValidationError, logic.NotAuthorized) as error:
|
||||||
|
log.error("Error on getting group: %s" % error)
|
||||||
|
return []
|
||||||
|
|
||||||
|
return to_browse_groups
|
||||||
|
|
||||||
|
|
||||||
|
def get_browse_info_for_organisations_or_groups(type='organization', limit=10, sort_field=None):
|
||||||
|
sort = None
|
||||||
|
if sort_field:
|
||||||
|
sort = sort_field
|
||||||
|
|
||||||
|
if type == 'organization':
|
||||||
|
if sort:
|
||||||
|
return get_list_of_organizations(limit, sort)
|
||||||
|
else:
|
||||||
|
return get_list_of_organizations(limit)
|
||||||
|
|
||||||
|
elif type == 'group':
|
||||||
|
if sort:
|
||||||
|
return get_list_of_groups(limit, sort)
|
||||||
|
else:
|
||||||
|
return get_list_of_groups(limit)
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_image_display_for_group(item_id):
|
||||||
|
if item_id:
|
||||||
|
try:
|
||||||
|
item_obj = model.Group.get(item_id)
|
||||||
|
|
||||||
|
if item_obj and item_obj.image_url:
|
||||||
|
return item_obj.image_url
|
||||||
|
else:
|
||||||
|
return h.url_for_static('/images/groups/icon/placeholder-group.png')
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
log.error("Error on getting item obj: %s" % item_id + "error: %s" % error)
|
||||||
|
|
||||||
|
|
||||||
|
def get_application_path():
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# If the application is run as a bundle, the pyInstaller bootloader
|
||||||
|
# extends the sys module by a flag frozen=True and sets the app
|
||||||
|
# path into variable _MEIPASS'.
|
||||||
|
application_path = sys._MEIPASS
|
||||||
|
else:
|
||||||
|
application_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
return application_path
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Get icon url for input entity type
|
||||||
|
@:param default_placeholder if True returns the URL of default image, otherwise None.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_to_icon_for_ckan_entity(item_name, entity_type=None, default_placeholder=True):
|
||||||
|
if not entity_type or not item_name:
|
||||||
|
return None
|
||||||
|
|
||||||
|
dir_images_full_path = get_application_path() + "/public/images"
|
||||||
|
dir_images_relative_path = "/images"
|
||||||
|
|
||||||
|
if entity_type == 'group':
|
||||||
|
dir_images_full_path += "/groups"
|
||||||
|
dir_images_relative_path += "/groups"
|
||||||
|
placeholder_icon = "placeholder-group.png"
|
||||||
|
elif entity_type == 'organization':
|
||||||
|
dir_images_full_path += "/organisations"
|
||||||
|
dir_images_relative_path += "/organisations"
|
||||||
|
placeholder_icon = "placeholder-organization.png"
|
||||||
|
elif entity_type == 'type':
|
||||||
|
dir_images_full_path += "/types"
|
||||||
|
dir_images_relative_path += "/types"
|
||||||
|
placeholder_icon = "placeholder-type.png"
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
icon_path = dir_images_full_path + "/icon/" + item_name.lower() + ".png"
|
||||||
|
if os.path.isfile(icon_path):
|
||||||
|
return h.url_for_static(dir_images_relative_path + "/icon/" + item_name.lower() + ".png")
|
||||||
|
elif default_placeholder:
|
||||||
|
return h.url_for_static(dir_images_relative_path + "/icon/" + placeholder_icon)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_info(user_id_or_name):
|
||||||
|
if user_id_or_name:
|
||||||
|
try:
|
||||||
|
|
||||||
|
item_obj = model.User.get(user_id_or_name)
|
||||||
|
|
||||||
|
if item_obj:
|
||||||
|
return item_obj
|
||||||
|
|
||||||
|
return None
|
||||||
|
except Exception as error:
|
||||||
|
log.error("Error on getting item obj: %s" % user_id_or_name + "error: %s" % error)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Search the value of my_search_string into input file {ckan_po_file} or the default file ckan.po provided as CKAN language
|
||||||
|
and returns its translate
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def get_ckan_translate_for(ckan_po_file, my_search_string):
|
||||||
|
my_translate = session.get(TRANSLATE_OF_ + my_search_string)
|
||||||
|
|
||||||
|
if not my_search_string:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if my_translate:
|
||||||
|
log.info("Translate of '%s' " % my_search_string + " found in session as: %s" % my_translate)
|
||||||
|
return my_translate
|
||||||
|
|
||||||
|
if not ckan_po_file:
|
||||||
|
ckan_po_file = "/usr/lib/ckan/default/src/ckan/ckan/i18n/en_GB/LC_MESSAGES/ckan.po"
|
||||||
|
|
||||||
|
numlines = 0
|
||||||
|
numfound = 0
|
||||||
|
found = 0
|
||||||
|
line_text = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
infile = open(ckan_po_file, "r")
|
||||||
|
|
||||||
|
for line in infile:
|
||||||
|
numlines += 1
|
||||||
|
if found > 0:
|
||||||
|
numfound += 1
|
||||||
|
line_text += str(line)
|
||||||
|
found = 0 # reset found
|
||||||
|
|
||||||
|
# found += line.upper().count(my_search_string.upper())
|
||||||
|
found += line.count(my_search_string)
|
||||||
|
|
||||||
|
if found > 0:
|
||||||
|
log.debug("The search string '%s'" % my_search_string + " was found. Read the line: %s" % str(line))
|
||||||
|
|
||||||
|
infile.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(("Exception during parsing the file %s" % ckan_po_file, e))
|
||||||
|
|
||||||
|
log.info("Recap: '%s' was found" % my_search_string + " %i times " % numfound + "in %i lines" % numlines)
|
||||||
|
log.debug("Line text is: %s" % line_text)
|
||||||
|
|
||||||
|
pattern = '"([A-Za-z0-9_ \./\\-]*)"'
|
||||||
|
m = re.search(pattern, line_text)
|
||||||
|
|
||||||
|
try:
|
||||||
|
my_translate = m.group()
|
||||||
|
except Exception as e:
|
||||||
|
print(("Pattern %s" % my_search_string + " not found ", e))
|
||||||
|
|
||||||
|
if my_translate:
|
||||||
|
log.debug("Replacing quotas...")
|
||||||
|
my_translate = my_translate.replace("\"", "")
|
||||||
|
|
||||||
|
log.info("Found the string '%s'" % my_translate + " that translating '%s'" % my_search_string)
|
||||||
|
|
||||||
|
session[TRANSLATE_OF_ + my_search_string] = my_translate
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
return my_translate
|
||||||
|
|
||||||
|
|
||||||
|
def get_location_to_bboxes():
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.optionxform = str
|
||||||
|
location_to_bboxes = {}
|
||||||
|
try:
|
||||||
|
bboxes_file = get_application_path() + "/public/location_to_bboxes.ini"
|
||||||
|
log.debug("bboxes_file is: '%s'" % bboxes_file)
|
||||||
|
config.read(bboxes_file)
|
||||||
|
for section_name in config.sections():
|
||||||
|
log.debug('Location to bboxes Section: ' + section_name)
|
||||||
|
# print ' Options:', parser.options(section_name)
|
||||||
|
for name, value in config.items(section_name):
|
||||||
|
location_to_bboxes[name] = value.replace(",", "%2C")
|
||||||
|
|
||||||
|
ordDictBboxes = collections.OrderedDict(sorted(location_to_bboxes.items()))
|
||||||
|
log.debug("Ordered 'bboxes_file' dict: '%s'" % ordDictBboxes)
|
||||||
|
return ordDictBboxes
|
||||||
|
except Exception as error:
|
||||||
|
log.error("Error on reading file: %s" % bboxes_file + "error: %s" % error)
|
||||||
|
|
||||||
|
def get_content_moderator_system_placeholder():
|
||||||
|
return systemtype_cms_fields_placeholders
|
||||||
|
|
|
@ -0,0 +1,460 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
import ckan.plugins as plugins
|
||||||
|
from ckanext.d4science_theme import helpers
|
||||||
|
import ckan.plugins.toolkit as toolkit
|
||||||
|
import ckan.lib.dictization.model_save as model_save
|
||||||
|
import ckan.model as model
|
||||||
|
import ckan.lib.helpers as h
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from ckanext.d4science_theme.controllers.organization import OrganizationVREController
|
||||||
|
from ckanext.d4science_theme.controllers.home import d4SHomeController
|
||||||
|
from ckanext.d4science_theme.controllers.systemtype import d4STypeController
|
||||||
|
from ckanext.d4science_theme.controllers.organization import OrganizationVREController
|
||||||
|
#from ckan.controllers.home import HomeController
|
||||||
|
from ckan.config.middleware.common_middleware import TrackingMiddleware
|
||||||
|
#from ckan.plugins import IRoutes
|
||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
from ckan.common import (
|
||||||
|
g
|
||||||
|
)
|
||||||
|
from flask import Flask, g
|
||||||
|
from ckan.lib.app_globals import app_globals
|
||||||
|
import ckan.plugins.toolkit as toolkit
|
||||||
|
|
||||||
|
|
||||||
|
# Created by Francesco Mangiacrapa
|
||||||
|
# francesco.mangiacrapa@isti.cnr.it
|
||||||
|
# ISTI-CNR Pisa (ITALY)
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
d4s_ctg_namespaces_controller = None
|
||||||
|
|
||||||
|
def remove_check_replicated_custom_key(schema):
|
||||||
|
if schema is not None:
|
||||||
|
schema.pop('__before', None)
|
||||||
|
|
||||||
|
return schema
|
||||||
|
|
||||||
|
#CREATED BY FRANCESCO MANGIACRAPA FOR OVERRIDING THE package_extras_save FROM dictization.model_save.py
|
||||||
|
def _package_extras_save(extra_dicts, obj, context):
|
||||||
|
''' It can save repeated extras as key-value '''
|
||||||
|
allow_partial_update = context.get("allow_partial_update", False)
|
||||||
|
if extra_dicts is None and allow_partial_update:
|
||||||
|
return
|
||||||
|
|
||||||
|
model = context["model"]
|
||||||
|
session = context["session"]
|
||||||
|
|
||||||
|
#ADDED BY FRANCESCO MANGIACRAPA
|
||||||
|
log.debug("extra_dicts: "+ str(extra_dicts))
|
||||||
|
#print "extra_dicts: "+str(extra_dicts)
|
||||||
|
|
||||||
|
extras_list = obj.extras_list
|
||||||
|
#extras = dict((extra.key, extra) for extra in extras_list)
|
||||||
|
old_extras = {}
|
||||||
|
extras = {}
|
||||||
|
for extra in extras_list or []:
|
||||||
|
old_extras.setdefault(extra.key, []).append(extra.value)
|
||||||
|
extras.setdefault(extra.key, []).append(extra)
|
||||||
|
|
||||||
|
#ADDED BY FRANCESCO MANGIACRAPA
|
||||||
|
#print "old_extras: "+str(old_extras)
|
||||||
|
|
||||||
|
new_extras = {}
|
||||||
|
for extra_dict in extra_dicts or []:
|
||||||
|
#print 'extra_dict key: '+extra_dict["key"] + ', value: '+extra_dict["value"]
|
||||||
|
#new_extras.setdefault(extra_dict["key"], []).append(extra_dict["value"])
|
||||||
|
if extra_dict.get("deleted"):
|
||||||
|
log.debug("extra_dict deleted: "+str(extra_dict["key"]))
|
||||||
|
#print 'extra_dict deleted: '+extra_dict["key"]
|
||||||
|
continue
|
||||||
|
|
||||||
|
#if extra_dict['value'] is not None and not extra_dict["value"] == "":
|
||||||
|
if extra_dict['value'] is not None:
|
||||||
|
new_extras.setdefault(extra_dict["key"], []).append(extra_dict["value"])
|
||||||
|
|
||||||
|
#ADDED BY FRANCESCO MANGIACRAPA
|
||||||
|
log.debug("new_extras: "+str(new_extras))
|
||||||
|
#print "new_extras: "+str(new_extras)
|
||||||
|
|
||||||
|
#new
|
||||||
|
for key in set(new_extras.keys()) - set(old_extras.keys()):
|
||||||
|
state = 'active'
|
||||||
|
log.debug("adding key: "+str(key))
|
||||||
|
#print "adding key: "+str(key)
|
||||||
|
extra_lst = new_extras[key]
|
||||||
|
for extra in extra_lst:
|
||||||
|
extra = model.PackageExtra(state=state, key=key, value=extra)
|
||||||
|
session.add(extra)
|
||||||
|
extras_list.append(extra)
|
||||||
|
|
||||||
|
#deleted
|
||||||
|
for key in set(old_extras.keys()) - set(new_extras.keys()):
|
||||||
|
log.debug("deleting key: "+str(key))
|
||||||
|
#print "deleting key: "+str(key)
|
||||||
|
extra_lst = extras[key]
|
||||||
|
for extra in extra_lst:
|
||||||
|
state = 'deleted'
|
||||||
|
extra.state = state
|
||||||
|
extras_list.remove(extra)
|
||||||
|
|
||||||
|
#changed
|
||||||
|
for key in set(new_extras.keys()) & set(old_extras.keys()):
|
||||||
|
#for each value of new list
|
||||||
|
for value in new_extras[key]:
|
||||||
|
old_occur = old_extras[key].count(value)
|
||||||
|
new_occur = new_extras[key].count(value)
|
||||||
|
log.debug("value: "+str(value) + ", new_occur: "+str(new_occur)+ ", old_occur: "+str(old_occur))
|
||||||
|
#print "value: "+str(value) + ", new_occur: "+str(new_occur) + ", old_occur: "+str(old_occur)
|
||||||
|
# it is an old value deleted or not
|
||||||
|
if value in old_extras[key]:
|
||||||
|
if old_occur == new_occur:
|
||||||
|
#print "extra - occurrences of: "+str(value) +", are equal into both list"
|
||||||
|
log.debug("extra - occurrences of: "+str(value) +", are equal into both list")
|
||||||
|
#there is a little bug, this code return always the first element, so I'm fixing with #FIX-STATUS
|
||||||
|
extra_values = get_package_for_value(extras[key], value)
|
||||||
|
#extras_list.append(extra)
|
||||||
|
for extra in extra_values:
|
||||||
|
state = 'active'
|
||||||
|
extra.state = state
|
||||||
|
session.add(extra)
|
||||||
|
#print "extra updated: "+str(extra)
|
||||||
|
log.debug("extra updated: "+str(extra))
|
||||||
|
|
||||||
|
elif new_occur > old_occur:
|
||||||
|
#print "extra - a new occurrence of: "+str(value) +", is present into new list, adding it to old list"
|
||||||
|
log.debug("extra - a new occurrence of: "+str(value) +", is present into new list, adding it to old list")
|
||||||
|
state = 'active'
|
||||||
|
extra = model.PackageExtra(state=state, key=key, value=value)
|
||||||
|
extra.state = state
|
||||||
|
session.add(extra)
|
||||||
|
extras_list.append(extra)
|
||||||
|
old_extras[key].append(value)
|
||||||
|
log.debug("old extra values updated: "+str(old_extras[key]))
|
||||||
|
#print "old extra values updated: "+str(old_extras[key])
|
||||||
|
|
||||||
|
else:
|
||||||
|
#remove all occurrences deleted - this code could be optimized, it is run several times but could be performed one shot
|
||||||
|
countDelete = old_occur-new_occur
|
||||||
|
log.debug("extra - occurrence of: "+str(value) +", is not present into new list, removing "+str(countDelete) + " occurrence/s from old list")
|
||||||
|
#print "extra - occurrence of: "+str(value) +", is not present into new list, removing "+str(countDelete)+" occurrence/s from old list"
|
||||||
|
extra_values = get_package_for_value(extras[key], value)
|
||||||
|
for idx, extra in enumerate(extra_values):
|
||||||
|
if idx < countDelete:
|
||||||
|
#print "extra - occurrence of: "+str(value) +", is not present into new list, removing it from old list"
|
||||||
|
log.debug("pkg extra deleting: "+str(extra.value))
|
||||||
|
#print "pkg extra deleting: "+str(extra.value)
|
||||||
|
state = 'deleted'
|
||||||
|
extra.state = state
|
||||||
|
|
||||||
|
else:
|
||||||
|
#print "pkg extra reactivating: "+str(extra.value)
|
||||||
|
log.debug("pkg extra reactivating: "+str(extra.value))
|
||||||
|
state = 'active'
|
||||||
|
extra.state = state
|
||||||
|
session.add(extra)
|
||||||
|
|
||||||
|
else:
|
||||||
|
#print "extra new value: "+str(value)
|
||||||
|
log.debug("extra new value: "+str(value))
|
||||||
|
state = 'active'
|
||||||
|
extra = model.PackageExtra(state=state, key=key, value=value)
|
||||||
|
extra.state = state
|
||||||
|
session.add(extra)
|
||||||
|
extras_list.append(extra)
|
||||||
|
|
||||||
|
|
||||||
|
#for each value of old list
|
||||||
|
for value in old_extras[key]:
|
||||||
|
#if value is not present in new list
|
||||||
|
if value not in new_extras[key]:
|
||||||
|
extra_values = get_package_for_value(extras[key], value)
|
||||||
|
for extra in extra_values:
|
||||||
|
#print "not present extra deleting: "+str(extra)
|
||||||
|
log.debug("not present extra deleting: "+str(extra))
|
||||||
|
state = 'deleted'
|
||||||
|
extra.state = state
|
||||||
|
|
||||||
|
|
||||||
|
#ADDED BY FRANCESCO MANGIACRAPA
|
||||||
|
def get_package_for_value(list_package, value):
|
||||||
|
''' Returns a list of packages containing the value passed in input
|
||||||
|
'''
|
||||||
|
lst = []
|
||||||
|
for x in list_package:
|
||||||
|
if x.value == value:
|
||||||
|
lst.append(x)
|
||||||
|
else:
|
||||||
|
return lst
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
#OVERRIDING BASE SQL ALCHEMY ENGINE INSTANCE
|
||||||
|
def _init_TrackingMiddleware(self, app, config):
|
||||||
|
self.app = app
|
||||||
|
log.debug('TrackingMiddleware d4Science instance')
|
||||||
|
sqlalchemy_url = config.get('sqlalchemy.url')
|
||||||
|
log.debug('sqlalchemy_url read: '+str(sqlalchemy_url))
|
||||||
|
|
||||||
|
sqlalchemy_pool = config.get('sqlalchemy.pool_size')
|
||||||
|
if sqlalchemy_pool is None:
|
||||||
|
sqlalchemy_pool = 5
|
||||||
|
|
||||||
|
log.debug('sqlalchemy_pool read: '+str(sqlalchemy_pool))
|
||||||
|
sqlalchemy_overflow = config.get('sqlalchemy.max_overflow')
|
||||||
|
|
||||||
|
if sqlalchemy_overflow is None:
|
||||||
|
sqlalchemy_overflow = 10;
|
||||||
|
|
||||||
|
log.debug('sqlalchemy_overflow read: '+str(sqlalchemy_overflow))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.engine = sa.create_engine(sqlalchemy_url, pool_size=int(sqlalchemy_pool), max_overflow=int(sqlalchemy_overflow))
|
||||||
|
except TypeError as e:
|
||||||
|
log.error('pool size does not work: ' +str(e.args))
|
||||||
|
self.engine = sa.create_engine(sqlalchemy_url)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm):
|
||||||
|
plugins.implements(plugins.IConfigurer)
|
||||||
|
plugins.implements(plugins.IDatasetForm)
|
||||||
|
plugins.implements(plugins.ITemplateHelpers)
|
||||||
|
plugins.implements(plugins.IFacets)
|
||||||
|
#plugins.implements(IRoutes, inherit=True)
|
||||||
|
|
||||||
|
#ckan 2.10
|
||||||
|
plugins.implements(plugins.IBlueprint)
|
||||||
|
|
||||||
|
# IConfigurer
|
||||||
|
def update_config(self, config_):
|
||||||
|
# Add this plugin's templates dir to CKAN's extra_template_paths, so
|
||||||
|
# that CKAN will use this plugin's custom templates.
|
||||||
|
toolkit.add_template_directory(config_, 'templates')
|
||||||
|
|
||||||
|
# Add this plugin's public dir to CKAN's extra_public_paths, so
|
||||||
|
# that CKAN will use this plugin's custom static files.
|
||||||
|
toolkit.add_public_directory(config_, 'public')
|
||||||
|
|
||||||
|
# Register this plugin's fanstatic directory with CKAN.
|
||||||
|
# Here, 'fanstatic' is the path to the fanstatic directory
|
||||||
|
# (relative to this plugin.py file), and 'example_theme' is the name
|
||||||
|
# that we'll use to refer to this fanstatic directory from CKAN
|
||||||
|
# templates.
|
||||||
|
toolkit.add_resource('assets', 'd4science_theme')
|
||||||
|
# toolkit.add_resource('assets', 'd4science_scripts')
|
||||||
|
|
||||||
|
#IDatasetForm
|
||||||
|
def create_package_schema(self):
|
||||||
|
# let's grab the default schema in our plugin
|
||||||
|
schema = super(D4Science_ThemePlugin, self).create_package_schema()
|
||||||
|
schema = remove_check_replicated_custom_key(schema)
|
||||||
|
#d.package_dict_save = _package_dict_save
|
||||||
|
return schema
|
||||||
|
|
||||||
|
#IDatasetForm
|
||||||
|
def update_package_schema(self):
|
||||||
|
schema = super(D4Science_ThemePlugin, self).update_package_schema()
|
||||||
|
schema = remove_check_replicated_custom_key(schema)
|
||||||
|
return schema
|
||||||
|
|
||||||
|
#IDatasetForm
|
||||||
|
def show_package_schema(self):
|
||||||
|
schema = super(D4Science_ThemePlugin, self).show_package_schema()
|
||||||
|
schema = remove_check_replicated_custom_key(schema)
|
||||||
|
return schema
|
||||||
|
|
||||||
|
#IDatasetForm
|
||||||
|
def is_fallback(self):
|
||||||
|
# Return True to register this plugin as the default handler for package types not handled by any other IDatasetForm plugin
|
||||||
|
return False
|
||||||
|
|
||||||
|
#IDatasetForm
|
||||||
|
def package_types(self):
|
||||||
|
# This plugin doesn't handle any special package types, it just
|
||||||
|
# registers itself as the default (above).
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
#ITemplateHelpers
|
||||||
|
def get_helpers(self):
|
||||||
|
log.info("get_helpers called...")
|
||||||
|
'''Register functions as a template
|
||||||
|
helper function.
|
||||||
|
'''
|
||||||
|
# Template helper function names should begin with the name of the
|
||||||
|
# extension they belong to, to avoid clashing with functions from
|
||||||
|
# other extensions.
|
||||||
|
return {
|
||||||
|
'd4science_theme_get_user_role_for_group_or_org': helpers.get_user_role_for_group_or_org,
|
||||||
|
'd4science_theme_get_parents_for_group': helpers.get_parents_for_group,
|
||||||
|
'get_header_param': helpers.get_header_param,
|
||||||
|
'get_request_param': helpers.get_request_param,
|
||||||
|
'get_cookie_value': helpers.get_cookie_value,
|
||||||
|
'd4science_theme_markdown_extract_html' : helpers.markdown_extract_html,
|
||||||
|
'd4science_theme_get_systemtype_value_from_extras' : helpers.get_systemtype_value_from_extras,
|
||||||
|
'd4science_theme_get_systemtype_field_dict_from_session' : helpers.get_systemtype_field_dict_from_session,
|
||||||
|
'd4science_theme_get_namespace_separator_from_session' : helpers.get_namespace_separator_from_session,
|
||||||
|
'd4science_theme_get_extras' : helpers.get_extras,
|
||||||
|
'd4science_theme_count_facet_items_dict' : helpers.count_facet_items_dict,
|
||||||
|
'd4science_theme_purge_namespace_to_facet': helpers.purge_namespace_to_fieldname,
|
||||||
|
'd4science_get_color_for_type': helpers.get_color_for_type,
|
||||||
|
'd4science_get_d4s_namespace_controller': helpers.get_d4s_namespace_controller,
|
||||||
|
'd4science_get_extras_indexed_for_namespaces': helpers.get_extras_indexed_for_namespaces,
|
||||||
|
'd4science_get_namespaces_dict': helpers.get_namespaces_dict,
|
||||||
|
'd4science_get_extra_for_category' : helpers.get_extra_for_category,
|
||||||
|
'd4science_get_ordered_dictionary': helpers.ordered_dictionary,
|
||||||
|
'd4science_get_qrcode_for_url': helpers.qrcode_for_url,
|
||||||
|
'd4science_get_list_of_organizations': helpers.get_list_of_organizations,
|
||||||
|
'd4science_get_image_display_for_group': helpers.get_image_display_for_group,
|
||||||
|
'd4science_get_list_of_groups': helpers.get_list_of_groups,
|
||||||
|
'd4science_get_browse_info_for_organisations_or_groups': helpers.get_browse_info_for_organisations_or_groups,
|
||||||
|
'd4science_get_user_info': helpers.get_user_info,
|
||||||
|
'd4science_get_url_to_icon_for_ckan_entity' : helpers.get_url_to_icon_for_ckan_entity,
|
||||||
|
'd4science_get_ckan_translate_for' : helpers.get_ckan_translate_for,
|
||||||
|
'd4science_get_location_to_bboxes' : helpers.get_location_to_bboxes,
|
||||||
|
'd4science_get_content_moderator_system_placeholder': helpers.get_content_moderator_system_placeholder,
|
||||||
|
}
|
||||||
|
|
||||||
|
#Overriding package_extras_save method
|
||||||
|
model_save.package_extras_save = _package_extras_save
|
||||||
|
|
||||||
|
#Overriding index home controller - rimosso in ckan 2.10
|
||||||
|
#d4sHC = d4SHomeController()
|
||||||
|
# HomeController.index = d4sHC.index
|
||||||
|
|
||||||
|
#OVERRIDING BASE SQL ALCHEMY ENGINE INSTANCE
|
||||||
|
TrackingMiddleware.__init__ = _init_TrackingMiddleware
|
||||||
|
|
||||||
|
global d4s_ctg_namespaces_controller
|
||||||
|
|
||||||
|
#if d4s_ctg_namespaces_controller is None:
|
||||||
|
# log.info("d4s_ctg_namespaces_controller instancing...")
|
||||||
|
# d4s_ctg_namespaces_controller = helpers.get_d4s_namespace_controller()
|
||||||
|
# log.info("d4s_ctg_namespaces_controller instancied %s" % d4s_ctg_namespaces_controller)
|
||||||
|
|
||||||
|
|
||||||
|
#IFacets
|
||||||
|
def dataset_facets(self, facets_dict, package_type):
|
||||||
|
facets_dict = self._update_facets(facets_dict)
|
||||||
|
return facets_dict
|
||||||
|
|
||||||
|
def group_facets(self, facets_dict, group_type, package_type):
|
||||||
|
# facets_dict = self._update_facets(facets_dict)
|
||||||
|
return facets_dict
|
||||||
|
|
||||||
|
def organization_facets(self, facets_dict, organization_type, package_type):
|
||||||
|
# facets_dict = self._update_facets(facets_dict)
|
||||||
|
return facets_dict
|
||||||
|
|
||||||
|
def _update_facets(self, facets_dict):
|
||||||
|
'''Add 'metadatatype' to facets if not already present.'''
|
||||||
|
|
||||||
|
log.debug("facets_dict: ")
|
||||||
|
log.debug(', '.join(facets_dict))
|
||||||
|
|
||||||
|
metadatatype = helpers.get_systemtype_field_dict_from_session()
|
||||||
|
|
||||||
|
'''Adding system:type'''
|
||||||
|
facet_title = helpers.purge_namespace_to_fieldname(str(metadatatype['id']))
|
||||||
|
facet_title = plugins.toolkit._(facet_title.capitalize() + 's')
|
||||||
|
facets_dict = self._add_or_update_facet(metadatatype['name'],facet_title, facets_dict)
|
||||||
|
|
||||||
|
log.info("facets dict after update: " + ', '.join(facets_dict))
|
||||||
|
log.info("site_url is: " + g.site_url)
|
||||||
|
|
||||||
|
#ADD IT IN THE CUSTOMIZATION?
|
||||||
|
if g.site_url:
|
||||||
|
|
||||||
|
dev_sites = ['https://ckan-d-d4s.d4science.org']
|
||||||
|
#GRSF Catalogues. 'Status of the Record' must be distributed everywhere, see #23398
|
||||||
|
grsf_sites = ['https://ckan-grsf-admin2.d4science.org', 'https://ckan-grsf.pre.d4science.org', 'https://ckan-grsf.d4science.org', 'https://ckan-grsf-pre.d4science.org']
|
||||||
|
sbd_sites = ['https://ckan.sobigdata.d4science.net', 'https://ckan-sobigdata.d4science.org',
|
||||||
|
'https://ckan-sobigdata2.d4science.org']
|
||||||
|
|
||||||
|
if g.site_url in dev_sites:
|
||||||
|
'''Adding Status of the GRSF record'''
|
||||||
|
facets_dict = self._add_or_update_facet("StatusoftheRecord", "Status of the Record", facets_dict,
|
||||||
|
display_after_facet='groups')
|
||||||
|
|
||||||
|
facets_dict = self._add_or_update_facet("Anno", "Anno", facets_dict,
|
||||||
|
display_after_facet='groups')
|
||||||
|
|
||||||
|
elif g.site_url in grsf_sites:
|
||||||
|
'''Adding Status of the GRSF record'''
|
||||||
|
# facets_dict = self._add_or_update_facet("StatusoftheGRSFrecord", "Status of the GRSF record", facets_dict, display_after_facet='groups')
|
||||||
|
# Fixing #23348
|
||||||
|
facets_dict = self._add_or_update_facet("StatusoftheRecord", "Status of the Record", facets_dict,
|
||||||
|
display_after_facet='groups')
|
||||||
|
elif g.site_url in sbd_sites:
|
||||||
|
'''Adding the field Availability '''
|
||||||
|
facets_dict = self._add_or_update_facet("Availability", "Availability", facets_dict,
|
||||||
|
display_after_facet='groups')
|
||||||
|
|
||||||
|
return facets_dict
|
||||||
|
|
||||||
|
def init_template_globals(app):
|
||||||
|
from ckan.lib.app_globals import app_globals
|
||||||
|
app.jinja_env.globals.update(g=app_globals)
|
||||||
|
|
||||||
|
#changed to migrate to ckan 2.10:
|
||||||
|
def get_blueprint(self):
|
||||||
|
d4sHC = d4SHomeController()
|
||||||
|
d4sTC = d4STypeController()
|
||||||
|
d4sOC = OrganizationVREController()
|
||||||
|
blueprint = Blueprint('d4s', self.__module__)
|
||||||
|
rules = [
|
||||||
|
('/', 'index', d4sHC.index),
|
||||||
|
('/type', 'type', d4sTC.index),
|
||||||
|
('/organization_vre', 'organization_vre', d4sOC.index),
|
||||||
|
]
|
||||||
|
for rule in rules:
|
||||||
|
blueprint.add_url_rule(*rule)
|
||||||
|
|
||||||
|
return blueprint
|
||||||
|
|
||||||
|
# def before_map(self, map):
|
||||||
|
# """This IRoutes implementation overrides the standard
|
||||||
|
# ``/user/register`` behaviour with a custom controller. You
|
||||||
|
# might instead use it to provide a completely new page, for
|
||||||
|
# example.
|
||||||
|
# Note that we have also provided a custom register form
|
||||||
|
# template at ``theme/templates/user/register.html``.
|
||||||
|
# """
|
||||||
|
# # Hook in our custom user controller at the points of creation
|
||||||
|
# # and edition.
|
||||||
|
# #
|
||||||
|
# #map.connect('/type', controller='ckanext.d4science_theme.controllers.type::d4STypeController', action='index')
|
||||||
|
# map.connect('/type', controller='ckanext.d4science_theme.controllers.systemtype:d4STypeController', action='index')
|
||||||
|
# ''' Added by Francesco Mangiacrapa, see: #8964 '''
|
||||||
|
# organization_vre = OrganizationVREController()
|
||||||
|
# map.connect('/organization_vre', controller='ckanext.d4science_theme.controllers.organization:OrganizationVREController', action='index')
|
||||||
|
# map.connect('/organization_vre/{id}', controller='ckanext.d4science_theme.controllers.organization:OrganizationVREController', action='read')
|
||||||
|
# map.redirect('/types', "/type")
|
||||||
|
# return map
|
||||||
|
|
||||||
|
|
||||||
|
def _add_or_update_facet(self, facet_key, facet_value, facets_dict, display_after_facet='organization'):
|
||||||
|
|
||||||
|
#Updating ordering of facets_dict OrderedDict
|
||||||
|
if str(facet_key) not in facets_dict:
|
||||||
|
|
||||||
|
new_orderded_facets_dict=facets_dict.__class__()
|
||||||
|
for key, value in list(facets_dict.items()):
|
||||||
|
new_orderded_facets_dict[key]=value
|
||||||
|
# #the field 'metadatatype' will be inserted after following key
|
||||||
|
if key==display_after_facet:
|
||||||
|
new_orderded_facets_dict[facet_key]=facet_value
|
||||||
|
|
||||||
|
facets_dict.clear()
|
||||||
|
facets_dict.update(new_orderded_facets_dict)
|
||||||
|
log.debug("facets_dict ordered: ")
|
||||||
|
log.debug(', '.join(facets_dict))
|
||||||
|
|
||||||
|
return facets_dict
|
||||||
|
|
||||||
|
|
0
compose/2.10/src/ckanext-d4science_theme/ckanext/d4science_theme/public/.gitignore
vendored
Normal file
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="385.2" width="845.7" viewBox="0 0 845.7 385.2"><g stroke="#fff" stroke-miterlimit="10" fill="none"><path d="M179.2 85.2L0 170.5l28.1-85.3L137 12.4z"/><path d="M219.9 150l-40.7-64.8L137 12.4 219.9 50zM304 75l-84.1 75V50l150-25.3z"/><path d="M448 124.8L304 75l65.9-50.3 50 25.3zM759.2 98.4L619.9 150V50l70.3-50z"/><path d="M800 170.5l-40.8-72.1-69-98.4 137.9 85.2zM569.9 91.4L448 124.8 419.9 50l100-37.6z"/><path d="M619.9 150l-50-58.6-50-79 100 37.6zM89.6 200l-43.9 50L0 170.5l179.2-85.3z"/><path d="M294.9 282.9L89.6 200l89.6-114.8 40.7 64.8z"/><path d="M319.9 250l-25 32.9-75-132.9L304 75z"/><path d="M464.9 158.2l-145 91.8L304 75l144 49.8zM697.5 217.9l-50.9-8.8-26.7-59.1 139.3-51.6z"/><path d="M845.7 250l-148.2-32.1 61.7-119.5 40.8 72.1zM519.9 250l-55-91.8-16.9-33.4 121.9-33.4zM646.6 209.1L519.9 250l50-158.6 50 58.6zM137 312.4L28.1 385.2 45.7 250l43.9-50z"/><path d="M219.9 350L137 312.4 89.6 200l205.3 82.9z"/><path d="M369.9 324.7l-150 25.3 75-67.1 25-32.9z"/><path d="M419.9 350l-50-25.3-50-74.7 145-91.8zM690.2 300l-70.3 50 26.7-140.9 50.9 8.8z"/><path d="M828.1 385.2L690.2 300l7.3-82.1L845.7 250zM519.9 312.4l-100 37.6 45-191.8 55 91.8zM619.9 350l-100-37.6V250l126.7-40.9z"/></g></svg>
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 115 KiB |
|
@ -0,0 +1,58 @@
|
||||||
|
/*!
|
||||||
|
* d4s_tagcloud.js
|
||||||
|
* D4science Tag Cloud which using Tag Cloud Plugin for JQuery
|
||||||
|
*
|
||||||
|
* jquery.tagcloud.js
|
||||||
|
* created by Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
|
||||||
|
*/
|
||||||
|
loadTagCloudJS = function (elementIDtoCloud, rgb_start, rgb_end) {
|
||||||
|
|
||||||
|
//console.log('start: '+rgb_start)
|
||||||
|
//console.log('end: '+rgb_end)
|
||||||
|
|
||||||
|
if(!rgb_start)
|
||||||
|
rgb_start = '#C0C0C0';
|
||||||
|
|
||||||
|
if(!rgb_end)
|
||||||
|
rgb_end = '#000066';
|
||||||
|
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.onload = function() {
|
||||||
|
/*console.log("TagCloud json loaded and ready");*/
|
||||||
|
$.fn.tagcloud.defaults = {
|
||||||
|
size: {start: 13, end: 20, unit: 'px'},
|
||||||
|
color: {start: rgb_start, end: rgb_end}
|
||||||
|
}
|
||||||
|
$('#'+elementIDtoCloud +' a').tagcloud();
|
||||||
|
};
|
||||||
|
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.src = "jquery.tagcloud.js";
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCloud = function (elementIDtoCloud, rgb_start, rgb_end) {
|
||||||
|
|
||||||
|
if(!window.jQuery){
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.onload = function() {
|
||||||
|
/*console.log("JQuery loaded and ready");*/
|
||||||
|
loadTagCloudJS(elementIDtoCloud,rgb_start,rgb_end);
|
||||||
|
};
|
||||||
|
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.src = "https://code.jquery.com/jquery-1.11.0.min.js";
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
|
||||||
|
}else {
|
||||||
|
loadTagCloudJS(elementIDtoCloud,rgb_start,rgb_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*SHUFFLE TAGS*/
|
||||||
|
var cloud = document.querySelector('#'+elementIDtoCloud);
|
||||||
|
if (cloud == null)
|
||||||
|
return;
|
||||||
|
for (var i = cloud.children.length; i >= 0; i--) {
|
||||||
|
cloud.appendChild(cloud.children[Math.random() * i | 0]);
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 5.2 KiB |