зеркало из https://github.com/nextcloud/appstore.git
Owner transfer frontend (#462)
* Add machinery for enabling app transfer * fix scripts * use defer instead of async * add changelog entry * add alert if user has no apps to transfer * wording fixes * wording * wording * make urls consistent * add badge for npm requirements
This commit is contained in:
Родитель
d00cb2db33
Коммит
6dcf34417e
|
@ -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**
|
||||
|
||||
|
|
|
@ -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 <https://nextcloudappstore.readthedocs.io/en/latest/>`_
|
||||
|
|
|
@ -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 <api-register-app>` or use the App Store's `register app web interface <https://apps.nextcloud.com/developer/apps/new>`_.
|
||||
|
@ -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 <app-register>`: 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 <https://apps.nextcloud.com/account/transfer-apps>`_. 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
|
||||
|
|
|
@ -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',)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
from itertools import chain
|
||||
from typing import List, Callable
|
||||
|
||||
"""
|
||||
Contains small utility and shortcut functions
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block head %}
|
||||
<link rel="stylesheet" href="{% static 'assets/css/img-slider.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'vendor/github.css' %}">
|
||||
<script async src="{% static 'public/app/detail.js' %}"></script>
|
||||
<script defer src="{% static 'public/app/detail.js' %}"></script>
|
||||
<meta name="description-url" content="{% url 'app-description' object.id %}">
|
||||
<meta name="ratings-url" content="{% url 'app-ratings' object.id %}">
|
||||
<meta name="language-code" content="{{ request.LANGUAGE_CODE }}">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{% block head-title %}{% if current_category %}{{ current_category.name }} - {% else %}{% trans 'All apps' %} - {% endif %}{% endblock %}
|
||||
{% block head %}
|
||||
<script async src="{% static 'public/app/list.js' %}"></script>
|
||||
<script defer src="{% static 'public/app/list.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block apps %}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block head-title %}{% trans 'Register app' %} - {% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script async src="{% static 'public/app/register.js' %}"></script>
|
||||
<script defer src="{% static 'public/app/register.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{% static 'assets/css/accordion.css' %}">
|
||||
<script async src="{% static 'public/app/releases.js' %}"></script>
|
||||
<script defer src="{% static 'public/app/releases.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block apps %}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block head-title %}{% trans 'Upload app release' %} - {% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script async src="{% static 'public/app/upload.js' %}"></script>
|
||||
<script defer src="{% static 'public/app/upload.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
<link rel="stylesheet" href="{% static 'assets/css/theme.css' %}">
|
||||
<link rel="alternate" type="application/atom+xml" title="News" href="{% url 'feeds-releases-atom' %}" />
|
||||
<link rel="alternate" type="application/rss+xml" title="News" href="{% url 'feeds-releases-rss' %}" />
|
||||
<script src="{% static 'vendor/jquery/dist/jquery.min.js' %}" async></script>
|
||||
<script src="{% static 'vendor/fetch/fetch.js' %}" async></script>
|
||||
<!-- used to migrate js code from previous to new model -->
|
||||
<script
|
||||
src="{% static 'vendor/bootstrap/dist/js/bootstrap.min.js' %}" async></script>
|
||||
<script src="{% static 'vendor/fetch/fetch.js' %}" defer></script>
|
||||
<script src="{% static 'vendor/jquery/dist/jquery.min.js' %}" defer></script>
|
||||
<script src="{% static 'vendor/bootstrap/dist/js/bootstrap.min.js' %}" defer></script>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% load staticfiles %}
|
||||
|
||||
{% block head %}
|
||||
<script async src="{% static 'public/user/token.js' %}"></script>
|
||||
<script defer src="{% static 'public/user/token.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block head-title %}{% trans 'API Token' %} - {% endblock %}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
<li class="{% if acc_page == 'account' %}active{% endif %}">
|
||||
<a href="{% url 'user:account' %}">{% trans 'Account' %}</a>
|
||||
</li>
|
||||
<li class="{% if acc_page == 'account-transfer-apps' %}active{% endif %}">
|
||||
<a href="{% url 'user:account-transfer-apps' %}">{% trans 'Transfer app ownership' %}</a>
|
||||
</li>
|
||||
<li class="{% if acc_page == 'account-change-language' %}active{% endif %}">
|
||||
<a href="{% url 'user:account-change-language' %}">{% trans 'Change language' %}</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
{% extends "user/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block head-title %}{% trans 'Transfer Apps' %} - {% endblock %}
|
||||
|
||||
{% block account-content %}
|
||||
<h1>{% trans "Transfer Apps" %}</h1>
|
||||
<section>
|
||||
<p>{% 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.' %}</p>
|
||||
|
||||
{% if apps %}
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>{% trans 'App name' %}</th>
|
||||
<th>{% trans 'Ownership transfer status' %}</th>
|
||||
<th>{% trans 'Change ownership transfer status' %}</th>
|
||||
</tr>
|
||||
{% for app in apps %}
|
||||
<tr>
|
||||
<td>{{ app.name }}</td>
|
||||
<td class="{% if app.ownership_transfer_enabled %}bg-success{% else %}bg-danger{% endif %}">
|
||||
{% if app.ownership_transfer_enabled %}
|
||||
{% trans 'Unlocked for transfer' %}
|
||||
{% else %}
|
||||
{% trans 'Locked, no transfer possible' %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<form action="{% url 'user:account-transfer-app' pk=app.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
{% if app.ownership_transfer_enabled %}
|
||||
<button class="btn btn-primary btn-block" type="submit">{% trans 'Lock ownership transfer' %}</button>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-block" type="submit">{% trans 'Unlock ownership transfer' %}</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="alert alert-info">{% trans 'You have not uploaded any apps yet!' %}</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -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<pk>[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'),
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче