188 lines
7.9 KiB
Python
188 lines
7.9 KiB
Python
import json
|
|
import logging
|
|
import urllib.parse
|
|
|
|
from django.core.exceptions import ValidationError
|
|
from django.db.models import Q
|
|
from django.db.utils import OperationalError
|
|
from django.http import HttpResponseRedirect, Http404, HttpResponse, HttpResponseServerError
|
|
from django.urls import reverse_lazy
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.views.generic import DetailView
|
|
from django.views.generic.edit import CreateView
|
|
from fdroidserver import index
|
|
from modeltranslation.utils import get_language
|
|
|
|
from repomaker.models import Repository, RemoteRepository, RemoteApp
|
|
from repomaker.models.category import Category
|
|
from repomaker.models.screenshot import PHONE, RemoteScreenshot
|
|
from . import BaseModelForm, AppScrollListView, LoginOrSingleUserRequiredMixin, LanguageMixin
|
|
from .repository import RepositoryAuthorizationMixin
|
|
|
|
|
|
class RemoteRepositoryForm(BaseModelForm):
|
|
class Meta:
|
|
model = RemoteRepository
|
|
fields = ['url']
|
|
labels = {
|
|
'url': _('Repository URL'),
|
|
}
|
|
|
|
|
|
class RemoteRepositoryCreateView(LoginOrSingleUserRequiredMixin, CreateView):
|
|
model = RemoteRepository
|
|
form_class = RemoteRepositoryForm
|
|
template_name = "repomaker/repo/add.html"
|
|
|
|
def form_valid(self, form):
|
|
user = self.request.user
|
|
|
|
# ensure that URL contains a fingerprint
|
|
url = urllib.parse.urlsplit(form.instance.url)
|
|
query = urllib.parse.parse_qs(url.query)
|
|
if 'fingerprint' not in query:
|
|
form.add_error('url', _("Please use a URL with a fingerprint at the end" +
|
|
", so we can validate the authenticity of the repository."))
|
|
return self.form_invalid(form)
|
|
|
|
# check if the user is trying to add their own repo here
|
|
fingerprint = query['fingerprint'][0]
|
|
if Repository.objects.filter(user=user, fingerprint=fingerprint).exists():
|
|
form.add_error('url', _("Please don't add one of your own repositories here."))
|
|
return self.form_invalid(form)
|
|
|
|
# update URL and fingerprint to final values
|
|
new_url = urllib.parse.SplitResult(url.scheme, url.netloc, url.path, '', '')
|
|
form.instance.url = new_url.geturl()
|
|
form.instance.fingerprint = fingerprint
|
|
|
|
# check if this remote repo already exists and if so, re-use it
|
|
existing_repo_query = RemoteRepository.objects.filter(fingerprint=fingerprint)
|
|
if existing_repo_query.exists():
|
|
existing_repo = existing_repo_query.get()
|
|
existing_repo.users.add(user)
|
|
existing_repo.save()
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
# download repo index and apply information to instance
|
|
try:
|
|
form.instance.update_index(update_apps=False)
|
|
form.instance.update_async() # schedule an async update for the apps as well
|
|
except index.VerificationException as e:
|
|
form.add_error('url', _("Could not validate repository: %s") % e)
|
|
return self.form_invalid(form)
|
|
|
|
result = super(RemoteRepositoryCreateView, self).form_valid(form)
|
|
form.instance.users.add(user)
|
|
form.instance.save()
|
|
return result
|
|
|
|
def get_success_url(self):
|
|
# TODO point this to some sort of remote repo overview or detail view
|
|
return reverse_lazy('index')
|
|
|
|
|
|
class AppRemoteAddView(RepositoryAuthorizationMixin, AppScrollListView):
|
|
model = RemoteApp
|
|
context_object_name = 'apps'
|
|
template_name = "repomaker/repo/remotes.html"
|
|
|
|
def get_queryset(self):
|
|
qs = RemoteApp.objects.filter(repo__users__id=self.request.user.id) \
|
|
.order_by('added_date')
|
|
if 'remote_repo_id' in self.kwargs:
|
|
qs = qs.filter(repo__pk=self.kwargs['remote_repo_id'])
|
|
if 'search' in self.request.GET:
|
|
query = self.request.GET['search']
|
|
qs = qs.filter(Q(name__icontains=query) | Q(summary__icontains=query))
|
|
if 'category_id' in self.kwargs:
|
|
qs = qs.filter(category__id=self.kwargs['category_id'])
|
|
return qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(AppRemoteAddView, self).get_context_data(**kwargs)
|
|
context['repo'] = self.get_repo()
|
|
context['remote_repos'] = RemoteRepository.objects.filter(users__id=self.request.user.id)
|
|
context['categories'] = Category.objects.filter(Q(user=None) | Q(user=self.request.user))
|
|
if 'remote_repo_id' in self.kwargs:
|
|
context['remote_repo'] = RemoteRepository.objects.get(pk=self.kwargs['remote_repo_id'])
|
|
if 'category_id' in self.kwargs:
|
|
context['category'] = context['categories'].get(pk=self.kwargs['category_id'])
|
|
if 'search' in self.request.GET and self.request.GET['search'] != '':
|
|
context['search_params'] = 'search=%s' % self.request.GET['search']
|
|
for app in context['apps']:
|
|
app.added = app.is_in_repo(context['repo'])
|
|
return context
|
|
|
|
def post(self, request, *args, **kwargs): # pylint: disable=unused-argument
|
|
if request.is_ajax():
|
|
apps_to_add = json.loads(request.body.decode("utf-8"))
|
|
for app in apps_to_add:
|
|
app_id = app['appId']
|
|
remote_repo_id = app['appRepoId']
|
|
remote_app = RemoteApp.objects.language().fallbacks() \
|
|
.get(repo__id=remote_repo_id, pk=app_id, repo__users__id=request.user.id)
|
|
try:
|
|
remote_app.add_to_repo(self.get_repo())
|
|
except OperationalError as e:
|
|
logging.error(e)
|
|
return HttpResponseServerError(e)
|
|
except ValidationError as e:
|
|
logging.error(e)
|
|
return HttpResponse(e.message, status=400)
|
|
self.get_repo().update_async() # schedule repository update
|
|
return HttpResponse(status=204)
|
|
return Http404()
|
|
|
|
|
|
class RemoteAppImportView(RepositoryAuthorizationMixin, LanguageMixin, DetailView):
|
|
model = RemoteApp
|
|
pk_url_kwarg = 'app_id'
|
|
context_object_name = 'app'
|
|
template_name = "repomaker/app/remote_add.html"
|
|
|
|
def get_queryset(self):
|
|
# restricting query set for security and to add language selector
|
|
remote_repo_id = self.kwargs['remote_repo_id']
|
|
qs = RemoteApp.objects.all()
|
|
return qs.filter(repo__id=remote_repo_id, repo__users__id=self.request.user.id)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['repo'] = self.get_repo()
|
|
context['screenshots'] = RemoteScreenshot.objects.filter(app=self.get_object(), type=PHONE,
|
|
language_code=self.get_language())
|
|
context['language_code'] = self.get_language() or get_language()
|
|
return context
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
"""
|
|
raise a 404 if the requested app isn't tranlated in to the requested
|
|
language
|
|
"""
|
|
app = self.get_object()
|
|
self.activate_language()
|
|
language = get_language()
|
|
if language not in app.get_available_languages():
|
|
raise Http404('App is not translated in to the requested language')
|
|
return super(RemoteAppImportView, self).get(request, *args, **kwargs)
|
|
|
|
def post(self, request, *args, **kwargs): # pylint: disable=unused-argument
|
|
if not self.get_queryset().exists():
|
|
return Http404()
|
|
|
|
remote_app = self.get_object()
|
|
self.activate_language()
|
|
# TODO catch ValidationError and display proper error message on a page
|
|
app = remote_app.add_to_repo(self.get_repo())
|
|
|
|
return HttpResponseRedirect(app.get_absolute_url())
|
|
|
|
|
|
class RemoteAppImportViewScreenshots(RemoteAppImportView):
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['show_screenshots'] = True
|
|
return context
|