diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 40366a955c..37e30fcfc8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,7 @@ Unreleased - Added support to switch between language comments - Added support to easily post ratings in multiple languages - Added support for running New Relic on servers +- Added ability to transfer app ownership **Fixed** diff --git a/README.rst b/README.rst index c70af0ed24..b8eca689fb 100644 --- a/README.rst +++ b/README.rst @@ -11,6 +11,9 @@ Nextcloud App Store .. image:: https://requires.io/github/nextcloud/appstore/requirements.svg?branch=master :target: https://requires.io/github/nextcloud/appstore/requirements/?branch=master :alt: Requirements Status +.. image:: https://david-dm.org/nextcloud/appstore.svg + :target: https://github.com/nextcloud/appstore/blob/master/package.json + :alt: Package.json Status .. image:: https://img.shields.io/badge/license-AGPLv3+-blue.svg :target: https://www.gnu.org/licenses/agpl-3.0.en.html :alt: License @@ -18,6 +21,7 @@ Nextcloud App Store :target: https://webchat.freenode.net/?channels=nextcloud-dev :alt: IRC + A new app store for Nextcloud apps built with Django. Documentation including Setup and API Specification are `available on Read the Docs `_ diff --git a/docs/developer.rst b/docs/developer.rst index 0efc4f8f2b..2daabf5e06 100644 --- a/docs/developer.rst +++ b/docs/developer.rst @@ -72,6 +72,9 @@ After we approved your certificate, we will post your signed public certificate .. note:: Be sure to follow the directory and naming structure for certificates. All our documentation examples and tools will assert this structure. + +.. _app-register: + Registering an App ~~~~~~~~~~~~~~~~~~ After you've obtained your signed public certificate you can use it to register your app id on the App Store. To do that either use the :ref:`REST API ` or use the App Store's `register app web interface `_. @@ -115,6 +118,8 @@ We then download the archive and verify the signature. In addition we try to ver If everything went well the release is then either created or updated. The downloaded archive will be deleted from our server. +.. _app-revoke-cert: + Revoking a Certificate ~~~~~~~~~~~~~~~~~~~~~~ If you've lost or leaked your private certificate you want to revoke your certificate. @@ -123,6 +128,16 @@ You can revoke your previous certificate by either posting your public certifica After you've obtained a new certificate, simply use it to register your app id again (only owners are allowed to do this). This will delete all previous releases from our server since their signature has become invalid. +Transferring Your App to a New Owner +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Transferring an app works similar to :ref:`registering an app `: The new owner simply needs to register the app again using the public certificate and the signature. + +However by default this is restricted to the app's owner. To disable this restriction you first need to unlock your app for the owner transfer. You can do this by going to your **account** settings and choosing `Transfer app ownership `_. On that page you can lock or unlock your apps for being transferred. + +After you unlocked your app for transfer, the new owner can then proceed to register the app again. If everything went fine the app is now transferred to the new owner and the transfer setting for that app is locked again. + + .. _app-metadata: App Metadata diff --git a/nextcloudappstore/core/admin.py b/nextcloudappstore/core/admin.py index 943ab4a9fe..c1d6c20d0c 100644 --- a/nextcloudappstore/core/admin.py +++ b/nextcloudappstore/core/admin.py @@ -45,9 +45,10 @@ class CategoryAdmin(TranslatableAdmin): @admin.register(App) class AppAdmin(TranslatableAdmin): list_display = ('id', 'owner', 'name', 'last_release', 'rating_recent', - 'rating_overall', 'summary', 'ocsid', 'is_featured') + 'rating_overall', 'summary', 'ocsid', 'is_featured', + 'ownership_transfer_enabled') list_filter = ('owner', 'co_maintainers', 'categories', 'created', - 'is_featured', 'last_release') + 'is_featured', 'last_release', 'ownership_transfer_enabled') ordering = ('id',) diff --git a/nextcloudappstore/core/facades.py b/nextcloudappstore/core/facades.py index 73d7e072fb..a498911737 100644 --- a/nextcloudappstore/core/facades.py +++ b/nextcloudappstore/core/facades.py @@ -1,6 +1,5 @@ import os from itertools import chain -from typing import List, Callable """ Contains small utility and shortcut functions diff --git a/nextcloudappstore/core/templates/app/detail.html b/nextcloudappstore/core/templates/app/detail.html index 2a422f4216..8e425be4a0 100644 --- a/nextcloudappstore/core/templates/app/detail.html +++ b/nextcloudappstore/core/templates/app/detail.html @@ -5,7 +5,7 @@ {% block head %} - + diff --git a/nextcloudappstore/core/templates/app/list.html b/nextcloudappstore/core/templates/app/list.html index 86c513c2b0..3dd23aee13 100644 --- a/nextcloudappstore/core/templates/app/list.html +++ b/nextcloudappstore/core/templates/app/list.html @@ -4,7 +4,7 @@ {% block head-title %}{% if current_category %}{{ current_category.name }} - {% else %}{% trans 'All apps' %} - {% endif %}{% endblock %} {% block head %} - + {% endblock %} {% block apps %} diff --git a/nextcloudappstore/core/templates/app/register.html b/nextcloudappstore/core/templates/app/register.html index 6bc8577646..dbb0ac5b0c 100644 --- a/nextcloudappstore/core/templates/app/register.html +++ b/nextcloudappstore/core/templates/app/register.html @@ -5,7 +5,7 @@ {% block head-title %}{% trans 'Register app' %} - {% endblock %} {% block head %} - + {% endblock %} {% block content %} diff --git a/nextcloudappstore/core/templates/app/releases.html b/nextcloudappstore/core/templates/app/releases.html index fca5c9358a..04970a1cb6 100644 --- a/nextcloudappstore/core/templates/app/releases.html +++ b/nextcloudappstore/core/templates/app/releases.html @@ -5,7 +5,7 @@ {% block head %} - + {% endblock %} {% block apps %} diff --git a/nextcloudappstore/core/templates/app/upload.html b/nextcloudappstore/core/templates/app/upload.html index f83b9b646a..555fac0189 100644 --- a/nextcloudappstore/core/templates/app/upload.html +++ b/nextcloudappstore/core/templates/app/upload.html @@ -5,7 +5,7 @@ {% block head-title %}{% trans 'Upload app release' %} - {% endblock %} {% block head %} - + {% endblock %} {% block content %} diff --git a/nextcloudappstore/core/templates/base.html b/nextcloudappstore/core/templates/base.html index 6c7569650a..2bf9e40671 100644 --- a/nextcloudappstore/core/templates/base.html +++ b/nextcloudappstore/core/templates/base.html @@ -16,11 +16,9 @@ - - - - + + + {% block head %}{% endblock %} diff --git a/nextcloudappstore/core/templates/user/api-token.html b/nextcloudappstore/core/templates/user/api-token.html index 9d61411a19..2dd3e28be2 100644 --- a/nextcloudappstore/core/templates/user/api-token.html +++ b/nextcloudappstore/core/templates/user/api-token.html @@ -3,7 +3,7 @@ {% load staticfiles %} {% block head %} - + {% endblock %} {% block head-title %}{% trans 'API Token' %} - {% endblock %} diff --git a/nextcloudappstore/core/templates/user/base.html b/nextcloudappstore/core/templates/user/base.html index 0713e2b2ee..2244d3df8b 100644 --- a/nextcloudappstore/core/templates/user/base.html +++ b/nextcloudappstore/core/templates/user/base.html @@ -11,6 +11,9 @@
  • {% trans 'Account' %}
  • +
  • + {% trans 'Transfer app ownership' %} +
  • {% trans 'Change language' %}
  • diff --git a/nextcloudappstore/core/templates/user/transfer-apps.html b/nextcloudappstore/core/templates/user/transfer-apps.html new file mode 100644 index 0000000000..2a6db6f4a3 --- /dev/null +++ b/nextcloudappstore/core/templates/user/transfer-apps.html @@ -0,0 +1,46 @@ +{% extends "user/base.html" %} +{% load i18n %} +{% load staticfiles %} + +{% block head-title %}{% trans 'Transfer Apps' %} - {% endblock %} + +{% block account-content %} +

    {% trans "Transfer Apps" %}

    +
    +

    {% trans 'To transfer an app to a new owner you must first unlock the app. A user can then take control of the app by registering it again on the app register page in the app developer menu.' %}

    + + {% if apps %} + + + + + + + {% for app in apps %} + + + + + + {% endfor %} +
    {% trans 'App name' %}{% trans 'Ownership transfer status' %}{% trans 'Change ownership transfer status' %}
    {{ app.name }} + {% if app.ownership_transfer_enabled %} + {% trans 'Unlocked for transfer' %} + {% else %} + {% trans 'Locked, no transfer possible' %} + {% endif %} + +
    + {% csrf_token %} + {% if app.ownership_transfer_enabled %} + + {% else %} + + {% endif %} +
    +
    + {% else %} +

    {% trans 'You have not uploaded any apps yet!' %}

    + {% endif %} +
    +{% endblock %} diff --git a/nextcloudappstore/core/user/urls.py b/nextcloudappstore/core/user/urls.py index a0c29cd183..e38a8473d3 100644 --- a/nextcloudappstore/core/user/urls.py +++ b/nextcloudappstore/core/user/urls.py @@ -1,10 +1,14 @@ from django.conf.urls import url from nextcloudappstore.core.user.views import PasswordView, AccountView, \ - APITokenView, DeleteAccountView, ChangeLanguageView + APITokenView, DeleteAccountView, ChangeLanguageView, TransferAppsView urlpatterns = [ url(r'^$', AccountView.as_view(), name='account'), + url(r'^transfer-apps/?$', TransferAppsView.as_view(), + name='account-transfer-apps'), + url(r'^transfer-apps/(?P[a-z0-9_]+)/?$', TransferAppsView.as_view(), + name='account-transfer-app'), url(r'^password/?$', PasswordView.as_view(), name='account-password'), url(r'^token/?$', APITokenView.as_view(), name='account-api-token'), url(r'^delete/?$', DeleteAccountView.as_view(), name='account-deletion'), diff --git a/nextcloudappstore/core/user/views.py b/nextcloudappstore/core/user/views.py index b06d4592cd..e50222a3e8 100644 --- a/nextcloudappstore/core/user/views.py +++ b/nextcloudappstore/core/user/views.py @@ -3,13 +3,31 @@ from allauth.account.views import PasswordChangeView from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.core.urlresolvers import reverse_lazy -from django.shortcuts import redirect, render +from django.shortcuts import redirect, render, get_object_or_404 +from django.urls import reverse from django.views.generic import TemplateView from django.views.generic import UpdateView +from nextcloudappstore.core.models import App from nextcloudappstore.core.user.forms import DeleteAccountForm, AccountForm +class TransferAppsView(LoginRequiredMixin, TemplateView): + template_name = 'user/transfer-apps.html' + + def post(self, request, pk): + app = get_object_or_404(App, pk=pk, owner=self.request.user) + app.ownership_transfer_enabled = not app.ownership_transfer_enabled + app.save() + return redirect(reverse('user:account-transfer-apps')) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['apps'] = App.objects.filter(owner=self.request.user) + context['acc_page'] = 'account-transfer-apps' + return context + + class ChangeLanguageView(LoginRequiredMixin, TemplateView): template_name = 'user/set-language.html'