diff --git a/.travis.yml b/.travis.yml index f45510c7..84be7c79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,10 +70,9 @@ deploy: on: tags: true all_branches: true - condition: $TRAVIS_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+ + condition: $TRAVIS_TAG =~ ^v([0-9]+\.?){2,3}(b[0-9]+|rc[0-9]+){0,1} notifications: email: - hisham.karam@cartologic.com - - hishamwaleedkaram@gmail.com - ahmednosman@cartologic.com - ahmedNourElDeen@cartologic.com diff --git a/Dockerfile b/Dockerfile index aa2677d8..72217d0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,18 @@ -FROM ubuntu:16.04 +FROM python:2.7-slim LABEL "MAINTAINER"="Cartologic Development Team" -ENV TERM xterm -RUN apt-get update -RUN apt-get install locales -y -RUN locale-gen en_US.UTF-8 && update-locale -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 -RUN apt-get install software-properties-common python-software-properties -y -RUN add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable -RUN apt-get update -RUN apt-get upgrade -y -RUN apt-get update && apt-get install -y \ - gcc gettext \ - python-pip libpq-dev \ - sqlite3 git gdal-bin lsof psmisc \ - python-gdal python-psycopg2 \ - python-imaging python-lxml \ - python-dev libgdal-dev libgeoip-dev \ - python-ldap libxml2 libxml2-dev libxslt-dev \ - libmemcached-dev libsasl2-dev zlib1g-dev \ - python-pylibmc python-setuptools \ - curl build-essential build-essential python-dev \ - --no-install-recommends -RUN mkdir /code -WORKDIR /code -RUN pip install --upgrade pip -RUN pip install --ignore-installed geoip django-geonode-client \ - geonode==2.8rc11 django-jsonfield django-jsonfield-compat \ - cartoview==1.8.3 cherrypy==11.0.0 cheroot==5.8.3 \ - django-autocomplete-light==2.3.3 --no-cache-dir -RUN pip install git+https://github.com/GeoNode/django-osgeo-importer.git -RUN apt autoremove --purge -y && apt autoclean -y -RUN rm -rf ~/.cache/pip -RUN rm -rf /var/lib/apt/lists/* && apt-get clean && \ - rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -RUN echo "Yes, do as I say!" | apt-get remove --force-yes login \ - && dpkg --remove --force-depends wget unzip +ENV PYTHONUNBUFFERED 1 +ARG GEONODE_DEV=true +ARG GEONODE_SHA1=992daf724e83cdb0c1eb776d147eba841ad02cd9 +ARG APP_DIR=/usr/src/carto_app +# include GDAL HEADER Files +# CPATH specifies a list of directories to be searched as if specified with -I, +# but after any paths given with -I options on the command line. +# This environment variable is used regardless of which language is being preprocessed. +ENV CPATH "$CPATH:/usr/include/gdal:/usr/include" +COPY scripts/docker/setup.sh ./ +COPY . /cartoview +RUN chmod +x setup.sh +RUN ./setup.sh +# switch to project dir +WORKDIR ${APP_DIR} CMD ["/bin/bash"] diff --git a/README.md b/README.md index a372c2f3..12df7d74 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@

+| WARNING: be careful this version(1.10.x) of Cartoview is compatibile with geonode 2.10.x only,if you want to install another version please take alook on [this section](https://github.com/cartologic/cartoview/blob/hisham-dev/README.md#previous-versions) | +| --- | + --- ## What is Cartoview? - CartoView is a GIS Web Mapping Application Market. @@ -43,7 +46,7 @@ ## How To Add Cartoview To Existing Geonode: - install cartoview with pip: - - `pip install cartoview==1.8.5 --no-cache-dir` + - `pip install cartoview --no-cache-dir` - open geonode `settings.py` and add the following lines at the end of the file: ```python from cartoview import settings as cartoview_settings @@ -84,3 +87,9 @@ ```sh paver run_test ``` + ___ +# Previous Versions +| Cartoview Version | Geonode Version | docs Link | +|-------------------|-----------------|---------------------------------------------------------------------------------| +| 1.8.x | 2.8.x | [Here](https://github.com/cartologic/cartoview/blob/1.8.x/README.md) | +| 1.6.x | 2.6.x | [Here](https://github.com/cartologic/cartoview/blob/2.6.x_compatible/README.md) | diff --git a/cartoview/__init__.py b/cartoview/__init__.py index ad89171e..7099e15c 100644 --- a/cartoview/__init__.py +++ b/cartoview/__init__.py @@ -1,4 +1,4 @@ -__version__ = (1, 8, 6, 'unstable', 0) +__version__ = (1, 10, 0, 'beta', 0) __compatible_with__ = [] diff --git a/cartoview/app_manager/decorators.py b/cartoview/app_manager/decorators.py index 43ad3cb1..6e233b28 100644 --- a/cartoview/app_manager/decorators.py +++ b/cartoview/app_manager/decorators.py @@ -1,7 +1,9 @@ -from .utils import resolve_appinstance from django.conf import settings from django.utils.translation import ugettext as _ +from .exceptions import AppAlreadyInstalledException +from .utils import resolve_appinstance + PERMISSION_MSG_DELETE = _("You are not permitted to delete this Instance") PERMISSION_MSG_GENERIC = _("You do not have permissions for this Instance.") PERMISSION_MSG_MODIFY = _("You are not permitted to modify this Instance") @@ -40,9 +42,6 @@ def restart_enabled(func): def wrap(*args, **kwargs): if not getattr(settings, "CARTOVIEW_TEST", False): return func(*args, **kwargs) - else: - pass - return wrap @@ -52,8 +51,9 @@ def wrap(*args, **kwargs): try: return func(*args, **kwargs) except BaseException as e: - if hasattr(this, '_rollback'): - this._rollback() - raise e + if not isinstance(e, AppAlreadyInstalledException): + if hasattr(this, '_rollback'): + this._rollback() + raise e return wrap diff --git a/cartoview/app_manager/exceptions.py b/cartoview/app_manager/exceptions.py new file mode 100644 index 00000000..88479836 --- /dev/null +++ b/cartoview/app_manager/exceptions.py @@ -0,0 +1,2 @@ +class AppAlreadyInstalledException(BaseException): + message = "Application is already installed." diff --git a/cartoview/app_manager/installer.py b/cartoview/app_manager/installer.py index 8951a471..a8f0d78d 100644 --- a/cartoview/app_manager/installer.py +++ b/cartoview/app_manager/installer.py @@ -10,7 +10,6 @@ import threading import zipfile from io import BytesIO -from os import R_OK, access from sys import executable, exit from threading import Timer @@ -25,9 +24,12 @@ from cartoview.log_handler import get_logger from cartoview.store_api.api import StoreAppResource, StoreAppVersion +from ..apps_handler.req_installer import (ReqFileException, + ReqFilePermissionException, + ReqInstaller) from .decorators import restart_enabled, rollback_on_failure +from .exceptions import AppAlreadyInstalledException from .models import App, AppStore, AppType -from .req_installer import ReqInstaller logger = get_logger(__name__) install_app_batch = getattr(settings, 'INSTALL_APP_BAT', None) @@ -118,10 +120,6 @@ def get_property_value(self, p): return getattr(self, p, None) -class AppAlreadyInstalledException(BaseException): - message = "Application is already installed." - - class AppInstaller(object): def __init__(self, name, store_id=None, version=None, user=None): self.user = user @@ -277,12 +275,14 @@ def install(self, restart=True): @rollback_on_failure def _install_requirements(self): - # TODO:add requirement file name as settings var - req_file = os.path.join(self.app_dir, "req.txt") - libs_dir = os.path.join(self.app_dir, "libs") - if os.path.exists(req_file) and access(req_file, R_OK): - req_installer = ReqInstaller(req_file, target=libs_dir) + try: + libs_dir = os.path.join(self.app_dir, 'libs') + req_installer = ReqInstaller(self.app_dir, target=libs_dir) req_installer.install_all() + except BaseException as e: + if not (isinstance(e, ReqFileException) or + isinstance(e, ReqFilePermissionException)): + raise e @rollback_on_failure def check_then_finlize(self, restart, installed_apps): diff --git a/cartoview/app_manager/management/commands/install_app.py b/cartoview/app_manager/management/commands/install_app.py index ad8be793..98c71aa2 100644 --- a/cartoview/app_manager/management/commands/install_app.py +++ b/cartoview/app_manager/management/commands/install_app.py @@ -2,6 +2,7 @@ from __future__ import print_function from django.core.management.base import BaseCommand +from pkg_resources import parse_version from cartoview.app_manager.installer import AppInstaller from cartoview.app_manager.models import App, AppStore @@ -10,6 +11,10 @@ logger = get_logger(__name__) +def compare_version(v1, v2): + return parse_version(v1) < parse_version(v2) + + class Command(BaseCommand): help = 'Install App' @@ -29,8 +34,9 @@ def handle(self, *args, **options): store = AppStore.objects.get(is_default=True) q = App.objects.filter(name=app_name) try: - if q.count() == 0 or (q.first() - and q.first().version < app_version): + if q.count() == 0 or (q.first() and + compare_version(q.first().version, + app_version)): installer = AppInstaller(app_name, store.id, app_version) installer.install() except Exception as ex: diff --git a/cartoview/app_manager/management/commands/update_current_apps.py b/cartoview/app_manager/management/commands/update_current_apps.py index 853732c8..6349cdcc 100644 --- a/cartoview/app_manager/management/commands/update_current_apps.py +++ b/cartoview/app_manager/management/commands/update_current_apps.py @@ -1,3 +1,4 @@ +from __future__ import print_function import requests from cartoview.app_manager.installer import AppJson, remove_unwanted from cartoview.app_manager.models import App diff --git a/cartoview/app_manager/migrations/0010_auto_20181118_1333.py b/cartoview/app_manager/migrations/0010_auto_20181118_1333.py new file mode 100644 index 00000000..a26727da --- /dev/null +++ b/cartoview/app_manager/migrations/0010_auto_20181118_1333.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.16 on 2018-11-18 13:33 +from __future__ import unicode_literals + +from django.db import migrations +import django.db.models.manager + + +class Migration(migrations.Migration): + + dependencies = [ + ('app_manager', '0009_auto_20180920_1659'), + ] + + operations = [ + migrations.AlterModelManagers( + name='appinstance', + managers=[ + ('objects', django.db.models.manager.Manager()), + ('base_objects', django.db.models.manager.Manager()), + ], + ), + migrations.RenameField( + model_name='appinstance', + old_name='map', + new_name='related_map', + ), + ] diff --git a/cartoview/app_manager/models.py b/cartoview/app_manager/models.py index 1f50abd7..9f26685e 100644 --- a/cartoview/app_manager/models.py +++ b/cartoview/app_manager/models.py @@ -136,6 +136,7 @@ def get_app_logo_path(instance, filename): ]) +@python_2_unicode_compatible class AppInstance(ResourceBase): """ An App Instance is any kind of App Instance that can be created @@ -146,7 +147,7 @@ class AppInstance(ResourceBase): app = models.ForeignKey( App, null=True, blank=True, on_delete=models.CASCADE) config = models.TextField(null=True, blank=True) - map = models.ForeignKey( + related_map = models.ForeignKey( GeonodeMap, null=True, blank=True, on_delete=models.CASCADE) logo = models.ImageField( upload_to=get_app_logo_path, blank=True, null=True) @@ -154,6 +155,9 @@ class AppInstance(ResourceBase): def get_absolute_url(self): return reverse('appinstance_detail', args=(self.id, )) + def __str__(self): + return self.title + @property def name_long(self): if not self.title: @@ -161,6 +165,24 @@ def name_long(self): else: return '%s (%s)' % (self.title, self.id) + # NOTE:backward compatibility for old map field use by apps \ + # in StandardAppViews + @property + def map_id(self): + return self.related_map_id + + @map_id.setter + def map_id(self, value): + self.related_map_id = value + + @property + def map(self): + return self.related_map + + @map.setter + def map(self, value): + self.rerelated_map = value + @property def config_obj(self): try: diff --git a/cartoview/app_manager/req_installer.py b/cartoview/app_manager/req_installer.py deleted file mode 100644 index 42a5fbbd..00000000 --- a/cartoview/app_manager/req_installer.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -import os -from os import R_OK, access - - -class ReqFileException(Exception): - message = "requirement file doesn't exists!" - - -class ReqFilePermissionException(Exception): - message = "can not read requirement file!" - - -class ReqInstaller(object): - def __init__(self, reqfile, target=None): - if not os.path.exists(reqfile): - raise ReqFileException() - if not access(reqfile, R_OK): - raise ReqFilePermissionException() - self.file = reqfile - self.requirements = [] - self.target = target - - def install_all(self, *args, **kwargs): - from subprocess import call - command = ["pip", "install", "-r", self.file] - if self.target: - command.extend(["-t", self.target]) - call(command) diff --git a/cartoview/app_manager/rest.py b/cartoview/app_manager/rest.py index 8aeca15a..928ce427 100644 --- a/cartoview/app_manager/rest.py +++ b/cartoview/app_manager/rest.py @@ -10,13 +10,6 @@ from django.core.urlresolvers import reverse from django.db import transaction from future import standard_library -from geonode.api.api import ProfileResource -from geonode.api.authorization import GeoNodeAuthorization -from geonode.api.resourcebase_api import (CommonMetaApi, LayerResource, - MapResource) -from geonode.layers.models import Attribute -from geonode.maps.models import MapLayer -from geonode.people.models import Profile from guardian.shortcuts import get_objects_for_user from taggit.models import Tag from tastypie import fields, http @@ -27,6 +20,12 @@ from cartoview.app_manager.models import App, AppInstance, AppStore, AppType from cartoview.apps_handler.handlers import CartoApps from cartoview.log_handler import get_logger +from geonode.api.api import ProfileResource +from geonode.api.authorization import GeoNodeAuthorization +from geonode.api.resourcebase_api import (CommonMetaApi, LayerResource, + MapResource) +from geonode.maps.models import MapLayer +from geonode.people.models import Profile from .installer import AppInstaller, RestartHelper from .resources import FileUploadResource @@ -54,7 +53,8 @@ def build_filters(self, filters=None, **kwargs): return orm_filters def apply_filters(self, request, applicable_filters): - permission = applicable_filters.pop('permission', None) + permission = applicable_filters.pop( + 'permission', None) # NOTE: We change this filter name from type to geom_type because it # overrides geonode type filter(vector,raster) layer_geom_type = applicable_filters.pop('geom_type', None) @@ -62,18 +62,17 @@ def apply_filters(self, request, applicable_filters): request, applicable_filters) if layer_geom_type: filtered = filtered.filter( - attribute_set__in=Attribute.objects.filter( - attribute_type__icontains=layer_geom_type)) + attribute_set__attribute_type__icontains=layer_geom_type) if permission is not None: - filtered = get_objects_for_user( - request.user, permission, klass=filtered) + permitted_ids = get_objects_for_user( + request.user, permission, klass=filtered).values('id') + filtered = filtered.filter(id__in=permitted_ids) return filtered class Meta(LayerResource.Meta): resource_name = "layers" - filtering = dict(LayerResource.Meta.filtering.items() + - {'typename': ALL}.items()) + filtering = dict(LayerResource.Meta.filtering, **dict(typename=ALL)) class GeonodeMapLayerResource(ModelResource): @@ -294,7 +293,7 @@ class AppInstanceResource(ModelResource): launch_app_url = fields.CharField(null=True, blank=True) edit_url = fields.CharField(null=True, blank=True) app = fields.ForeignKey(AppResource, 'app', full=True, null=True) - map = fields.ForeignKey(MapResource, 'map', full=True, null=True) + map = fields.ForeignKey(MapResource, 'related_map', full=True, null=True) owner = fields.ForeignKey( ProfileResource, 'owner', full=True, null=True, blank=True) keywords = fields.ListField(null=True, blank=True) diff --git a/cartoview/app_manager/settings.py b/cartoview/app_manager/settings.py index 8f85a066..980d102d 100644 --- a/cartoview/app_manager/settings.py +++ b/cartoview/app_manager/settings.py @@ -7,10 +7,6 @@ import sys from future import standard_library -from past.builtins import execfile - -from cartoview.apps_handler.handlers import CartoApps, apps_orm -from cartoview.apps_handler.utils import create_apps_dir from cartoview.log_handler import get_logger logger = get_logger(__name__, with_formatter=True) @@ -24,6 +20,8 @@ def load_apps(APPS_DIR): + from cartoview.apps_handler.utils import create_apps_dir + from cartoview.apps_handler.handlers import CartoApps, apps_orm global CARTOVIEW_APPS global APPS_SETTINGS create_apps_dir(APPS_DIR) @@ -49,7 +47,6 @@ def load_apps(APPS_DIR): # circular imports. app_settings_file = os.path.realpath(app_settings_file) APPS_SETTINGS += (app_settings_file, ) - # execfile(app_settings_file) if os.path.exists(libs_dir) and libs_dir not in sys.path: logger.info("Install {} libs folder to the system.\n".format( app.name)) diff --git a/cartoview/app_manager/templates/app_instance/_resourcebase_snippet.html b/cartoview/app_manager/templates/app_instance/_resourcebase_snippet.html index 85e32808..7a6d8d72 100644 --- a/cartoview/app_manager/templates/app_instance/_resourcebase_snippet.html +++ b/cartoview/app_manager/templates/app_instance/_resourcebase_snippet.html @@ -27,13 +27,13 @@

{{ item.title }}