From c8df93dd5fb8a6d3f1657dc3386a6f4ce4122819 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Thu, 14 Nov 2019 12:42:26 +0100 Subject: [PATCH] nextcloud links 90% working --- cookbook/forms.py | 2 +- .../migrations/0007_auto_20191114_1044.py | 23 +++++ .../migrations/0008_auto_20191114_1046.py | 18 ++++ cookbook/models.py | 5 +- cookbook/provider/nextcloud.py | 96 +++++++++++++++++++ cookbook/provider/webdav.py | 0 cookbook/views/api.py | 11 ++- requirements.txt | 9 +- 8 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 cookbook/migrations/0007_auto_20191114_1044.py create mode 100644 cookbook/migrations/0008_auto_20191114_1046.py create mode 100644 cookbook/provider/nextcloud.py delete mode 100644 cookbook/provider/webdav.py diff --git a/cookbook/forms.py b/cookbook/forms.py index bf906c91..291b2a8c 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -81,7 +81,7 @@ class StorageForm(forms.ModelForm): class SyncForm(forms.ModelForm): class Meta: model = Sync - fields = ('storage', 'path') + fields = ('storage', 'path', 'active') class BatchEditForm(forms.Form): diff --git a/cookbook/migrations/0007_auto_20191114_1044.py b/cookbook/migrations/0007_auto_20191114_1044.py new file mode 100644 index 00000000..6f7a3c4d --- /dev/null +++ b/cookbook/migrations/0007_auto_20191114_1044.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.7 on 2019-11-14 09:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0006_comment'), + ] + + operations = [ + migrations.AddField( + model_name='sync', + name='active', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='storage', + name='method', + field=models.CharField(choices=[('DB', 'Dropbox'), ('DAV', 'WebDAV')], default='DB', max_length=128), + ), + ] diff --git a/cookbook/migrations/0008_auto_20191114_1046.py b/cookbook/migrations/0008_auto_20191114_1046.py new file mode 100644 index 00000000..79e03ef0 --- /dev/null +++ b/cookbook/migrations/0008_auto_20191114_1046.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2019-11-14 09:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0007_auto_20191114_1044'), + ] + + operations = [ + migrations.AlterField( + model_name='storage', + name='method', + field=models.CharField(choices=[('DB', 'Dropbox'), ('NEXTCLOUD', 'Nextcloud')], default='DB', max_length=128), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 13c1ef48..eb33f73a 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -4,8 +4,8 @@ from django.db import models class Storage(models.Model): DROPBOX = 'DB' - DAV = 'DAV' - STORAGE_TYPES = ((DROPBOX, 'Dropbox'), (DAV, 'WebDAV')) + NEXTCLOUD = 'NEXTCLOUD' + STORAGE_TYPES = ((DROPBOX, 'Dropbox'), (NEXTCLOUD, 'Nextcloud')) name = models.CharField(max_length=128) method = models.CharField(choices=STORAGE_TYPES, max_length=128, default=DROPBOX) @@ -21,6 +21,7 @@ class Storage(models.Model): class Sync(models.Model): storage = models.ForeignKey(Storage, on_delete=models.PROTECT) path = models.CharField(max_length=512, default="") + active = models.BooleanField(default=True) last_checked = models.DateTimeField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) diff --git a/cookbook/provider/nextcloud.py b/cookbook/provider/nextcloud.py new file mode 100644 index 00000000..d8ea36fd --- /dev/null +++ b/cookbook/provider/nextcloud.py @@ -0,0 +1,96 @@ +import os +from datetime import datetime +import json +import webdav3.client as wc +import requests +import xmltodict + +from requests.auth import HTTPBasicAuth + +from cookbook.models import Recipe, RecipeImport, SyncLog +from cookbook.provider.provider import Provider + + +class Nextcloud(Provider): + + @staticmethod + def import_all(monitor): + options = { + 'webdav_hostname': monitor.storage.url + '/remote.php/dav/files/' + monitor.storage.username, + 'webdav_login': monitor.storage.username, + 'webdav_password': monitor.storage.password + } + client = wc.Client(options) + + files = client.list(monitor.path) + files.pop(0) # remove first element because its the folder itself + + import_count = 0 + for file in files: + path = monitor.path + '/' + file + if not Recipe.objects.filter(file_path=path).exists() and not RecipeImport.objects.filter(file_path=path).exists(): + name = os.path.splitext(file)[0] + new_recipe = RecipeImport(name=name, file_path=path, storage=monitor.storage) + new_recipe.save() + import_count += 1 + + log_entry = SyncLog(status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', sync=monitor) + log_entry.save() + + monitor.last_checked = datetime.now() + monitor.save() + + return True + + @staticmethod + def url_from_ocs_response(response): + if response['ocs']['data']: + elements = response['ocs']['data']['element'] + if isinstance(elements, list): + for element in elements: + if element['share_type'] == '3': + return element['url'] + else: + if elements['share_type'] == '3': + return elements['url'] + + return False + + @staticmethod + def create_share_link(recipe): + url = recipe.storage.url + '/ocs/v2.php/apps/files_sharing/api/v1/shares' + + headers = { + "OCS-APIRequest": "true", + "Content-Type": "application/x-www-form-urlencoded" + } + + data = { + "path": recipe.file_path, + "shareType ": 3 + } + + r = requests.post(url, headers=headers, auth=HTTPBasicAuth(recipe.storage.username, recipe.storage.password), data=json.dumps(data)) + + json_response = xmltodict.parse(r.text) + + return Nextcloud.url_from_ocs_response(json_response) + + @staticmethod + def get_share_link(recipe): + url = recipe.storage.url + '/ocs/v2.php/apps/files_sharing/api/v1/shares?path=' + recipe.file_path + + headers = { + "OCS-APIRequest": "true", + "Content-Type": "application/xml" + } + + r = requests.get(url, headers=headers, auth=HTTPBasicAuth(recipe.storage.username, recipe.storage.password)) + + json_response = xmltodict.parse(r.text) + + url = Nextcloud.url_from_ocs_response(json_response) + if url: + return url + + return Nextcloud.create_share_link(recipe) diff --git a/cookbook/provider/webdav.py b/cookbook/provider/webdav.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cookbook/views/api.py b/cookbook/views/api.py index bd0cb4e2..3751b488 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -8,6 +8,7 @@ from django.shortcuts import redirect from cookbook.models import Recipe, Sync, Storage from cookbook.provider import dropbox from cookbook.provider.dropbox import Dropbox +from cookbook.provider.nextcloud import Nextcloud @login_required @@ -20,13 +21,17 @@ def get_file_link(request, recipe_id): if recipe.link == "": recipe.link = Dropbox.get_share_link(recipe) # TODO response validation recipe.save() + if recipe.storage.method == Storage.NEXTCLOUD: + if recipe.link == "": + recipe.link = Nextcloud.get_share_link(recipe) # TODO response validation + #recipe.save() return HttpResponse(recipe.link) @login_required def sync_all(request): - monitors = Sync.objects.all() + monitors = Sync.objects.filter(active=True) error = False for monitor in monitors: @@ -34,6 +39,10 @@ def sync_all(request): ret = Dropbox.import_all(monitor) if not ret: error = True + if monitor.storage.method == Storage.NEXTCLOUD: + ret = Nextcloud.import_all(monitor) + if not ret: + error = True if not error: messages.add_message(request, messages.SUCCESS, _('Sync successful!')) diff --git a/requirements.txt b/requirements.txt index 9f7a0e42..b0e9579d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,15 @@ -six -requests -markdown +lxml django django-tables2 django-filter django-crispy-forms djangorestframework django-autocomplete-light +six +requests +markdown +webdavclient3 +xmltodict python-dotenv==0.7.1 psycopg2==2.7.4 gunicorn==19.7.1 \ No newline at end of file