deleted bookmarks app
parent
0a4a287767
commit
ab55c92bb4
|
@ -1,24 +0,0 @@
|
|||
:mod:`readthedocs.bookmarks`
|
||||
============================
|
||||
|
||||
:mod:`readthedocs.bookmarks.admin`
|
||||
----------------------------------
|
||||
.. automodule:: readthedocs.bookmarks.admin
|
||||
:members:
|
||||
|
||||
:mod:`readthedocs.bookmarks.models`
|
||||
-----------------------------------
|
||||
.. automodule:: readthedocs.bookmarks.models
|
||||
:members:
|
||||
|
||||
:mod:`readthedocs.bookmarks.urls`
|
||||
---------------------------------
|
||||
.. automodule:: readthedocs.bookmarks.urls
|
||||
:members:
|
||||
|
||||
:mod:`readthedocs.bookmarks.views`
|
||||
----------------------------------
|
||||
.. automodule:: readthedocs.bookmarks.views
|
||||
:members:
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
"""Django admin interface for `~bookmarks.models.Bookmark`."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from django.contrib import admin
|
||||
from readthedocs.bookmarks.models import Bookmark
|
||||
|
||||
|
||||
class BookmarkAdmin(admin.ModelAdmin):
|
||||
list_display = ('project', 'date', 'url')
|
||||
|
||||
admin.site.register(Bookmark, BookmarkAdmin)
|
|
@ -1,37 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from __future__ import absolute_import
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('builds', '0001_initial'),
|
||||
('projects', '0002_add_importedfile_model'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Bookmark',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('page', models.CharField(max_length=255, verbose_name='Page')),
|
||||
('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')),
|
||||
('url', models.CharField(max_length=255, null=True, verbose_name='URL', blank=True)),
|
||||
('project', models.ForeignKey(related_name='bookmarks', verbose_name='Project', to='projects.Project')),
|
||||
('user', models.ForeignKey(related_name='bookmarks', verbose_name='User', to=settings.AUTH_USER_MODEL)),
|
||||
('version', models.ForeignKey(related_name='bookmarks', verbose_name='Version', to='builds.Version')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-date'],
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='bookmark',
|
||||
unique_together=set([('user', 'project', 'version', 'page')]),
|
||||
),
|
||||
]
|
|
@ -1,42 +0,0 @@
|
|||
"""Models for the bookmarks app."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import object
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
|
||||
from readthedocs.builds.models import Version
|
||||
from readthedocs.projects.models import Project
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Bookmark(models.Model):
|
||||
|
||||
"""A user's bookmark of a ``Project``, ``Version``, and page."""
|
||||
|
||||
user = models.ForeignKey(User, verbose_name=_('User'),
|
||||
related_name='bookmarks')
|
||||
project = models.ForeignKey(Project, verbose_name=_('Project'),
|
||||
related_name='bookmarks')
|
||||
version = models.ForeignKey(Version, verbose_name=_('Version'),
|
||||
related_name='bookmarks')
|
||||
page = models.CharField(_('Page'), max_length=255)
|
||||
|
||||
date = models.DateTimeField(_('Date'), auto_now_add=True)
|
||||
url = models.CharField(_('URL'), max_length=255, null=True, blank=True)
|
||||
|
||||
class Meta(object):
|
||||
ordering = ['-date']
|
||||
unique_together = ('user', 'project', 'version', 'page')
|
||||
|
||||
def __str__(self):
|
||||
return ugettext(u"Bookmark %(url)s for %(user)s (%(pk)s)") % {
|
||||
'url': self.url,
|
||||
'user': self.user,
|
||||
'pk': self.pk,
|
||||
}
|
||||
|
||||
def get_absolute_url(self):
|
||||
return self.url
|
|
@ -1,29 +0,0 @@
|
|||
"""URL config for the bookmarks app."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from django.conf.urls import url
|
||||
from readthedocs.bookmarks.views import BookmarkListView
|
||||
from readthedocs.bookmarks.views import BookmarkAddView, BookmarkRemoveView
|
||||
from readthedocs.bookmarks.views import BookmarkExistsView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$',
|
||||
BookmarkListView.as_view(),
|
||||
name='bookmark_list'),
|
||||
|
||||
url(r'^add/$',
|
||||
BookmarkAddView.as_view(),
|
||||
name='bookmarks_add'),
|
||||
|
||||
url(r'^remove/(?P<bookmark_pk>[-\w]+)/$',
|
||||
BookmarkRemoveView.as_view(),
|
||||
name='bookmark_remove'),
|
||||
|
||||
url(r'^remove/$',
|
||||
BookmarkRemoveView.as_view(),
|
||||
name='bookmark_remove_json'),
|
||||
|
||||
url(r'^exists/$',
|
||||
BookmarkExistsView.as_view(),
|
||||
name='bookmark_exists'),
|
||||
]
|
|
@ -1,200 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Views for the bookmarks app."""
|
||||
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals)
|
||||
|
||||
import json
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import (
|
||||
HttpResponse, HttpResponseBadRequest, HttpResponseRedirect)
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import ListView, View
|
||||
|
||||
from readthedocs.bookmarks.models import Bookmark
|
||||
from readthedocs.projects.models import Project
|
||||
|
||||
# These views are CSRF exempt because of Django's CSRF middleware failing here
|
||||
# https://github.com/django/django/blob/stable/1.6.x/django/middleware/csrf.py#L135-L159
|
||||
# We don't have a valid referrer because we're on a subdomain
|
||||
|
||||
|
||||
class BookmarkExistsView(View):
|
||||
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(BookmarkExistsView,
|
||||
self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request):
|
||||
return HttpResponse(
|
||||
content=json.dumps({'error': 'You must POST!'}),
|
||||
content_type='application/json',
|
||||
status=405,
|
||||
)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Returns:
|
||||
|
||||
- 200 response with exists = True in json if bookmark exists.
|
||||
- 404 with exists = False in json if no matching bookmark is found.
|
||||
- 400 if json data is missing any one of: project, version, page.
|
||||
"""
|
||||
post_json = json.loads(request.body)
|
||||
try:
|
||||
project = post_json['project']
|
||||
version = post_json['version']
|
||||
page = post_json['page']
|
||||
except KeyError:
|
||||
return HttpResponseBadRequest(
|
||||
content=json.dumps({'error': 'Invalid parameters'}),
|
||||
)
|
||||
try:
|
||||
Bookmark.objects.get(
|
||||
project__slug=project,
|
||||
version__slug=version,
|
||||
page=page,
|
||||
)
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponse(
|
||||
content=json.dumps({'exists': False}),
|
||||
status=404,
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
return HttpResponse(
|
||||
content=json.dumps({'exists': True}),
|
||||
status=200,
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
|
||||
class BookmarkListView(ListView):
|
||||
|
||||
"""Displays all of a logged-in user's bookmarks."""
|
||||
|
||||
model = Bookmark
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(BookmarkListView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return Bookmark.objects.filter(user=self.request.user)
|
||||
|
||||
|
||||
class BookmarkAddView(View):
|
||||
|
||||
"""Adds bookmarks in response to POST requests."""
|
||||
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(BookmarkAddView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request):
|
||||
return HttpResponse(
|
||||
content=json.dumps({'error': 'You must POST!'}),
|
||||
content_type='application/json',
|
||||
status=405,
|
||||
)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Add a new bookmark for the current user.
|
||||
|
||||
Points at ``project``, ``version``, ``page``, and ``url``.
|
||||
"""
|
||||
post_json = json.loads(request.body)
|
||||
try:
|
||||
project_slug = post_json['project']
|
||||
version_slug = post_json['version']
|
||||
page_slug = post_json['page']
|
||||
url = post_json['url']
|
||||
except KeyError:
|
||||
return HttpResponseBadRequest(
|
||||
content=json.dumps({'error': 'Invalid parameters'}),
|
||||
)
|
||||
|
||||
try:
|
||||
project = Project.objects.get(slug=project_slug)
|
||||
version = project.versions.get(slug=version_slug)
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponseBadRequest(
|
||||
content=json.dumps(
|
||||
{'error': 'Project or Version does not exist'}),
|
||||
)
|
||||
|
||||
Bookmark.objects.get_or_create(
|
||||
user=request.user,
|
||||
url=url,
|
||||
project=project,
|
||||
version=version,
|
||||
page=page_slug,
|
||||
)
|
||||
return HttpResponse(
|
||||
json.dumps({'added': True}),
|
||||
status=201,
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
|
||||
class BookmarkRemoveView(View):
|
||||
|
||||
"""
|
||||
Deletes a user's bookmark in response to a POST request.
|
||||
|
||||
Renders a delete? confirmation page in response to a GET request.
|
||||
"""
|
||||
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(BookmarkRemoveView,
|
||||
self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return render(request, 'bookmarks/bookmark_delete.html')
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Delete a bookmark.
|
||||
|
||||
Uses the primary key from the URL or JSON data from the request.
|
||||
"""
|
||||
if 'bookmark_pk' in kwargs:
|
||||
bookmark = get_object_or_404(Bookmark, pk=kwargs['bookmark_pk'])
|
||||
bookmark.delete()
|
||||
return HttpResponseRedirect(reverse('bookmark_list'))
|
||||
try:
|
||||
post_json = json.loads(request.body)
|
||||
project = Project.objects.get(slug=post_json['project'])
|
||||
version = project.versions.get(slug=post_json['version'])
|
||||
url = post_json['url']
|
||||
page = post_json['page']
|
||||
except KeyError:
|
||||
return HttpResponseBadRequest(
|
||||
json.dumps({'error': 'Invalid parameters'}),
|
||||
)
|
||||
|
||||
bookmark = get_object_or_404(
|
||||
Bookmark,
|
||||
user=request.user,
|
||||
url=url,
|
||||
project=project,
|
||||
version=version,
|
||||
page=page,
|
||||
)
|
||||
bookmark.delete()
|
||||
|
||||
return HttpResponse(
|
||||
json.dumps({'removed': True}),
|
||||
status=200,
|
||||
content_type='application/json',
|
||||
)
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.12 on 2018-02-17 11:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gold', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='golduser',
|
||||
name='level',
|
||||
field=models.CharField(choices=[(b'v1-org-5', b'$5/mo'), (b'v1-org-10', b'$10/mo'), (b'v1-org-15', b'$15/mo'), (b'v1-org-20', b'$20/mo'), (b'v1-org-50', b'$50/mo'), (b'v1-org-100', b'$100/mo')], default=b'v1-org-5', max_length=20, verbose_name='Level'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.12 on 2018-02-17 11:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('integrations', '0002_add-webhook'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BitbucketWebhook',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('integrations.integration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GenericAPIWebhook',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('integrations.integration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GitHubWebhook',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('integrations.integration',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GitLabWebhook',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('integrations.integration',),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='integration',
|
||||
name='integration_type',
|
||||
field=models.CharField(choices=[('github_webhook', 'GitHub incoming webhook'), ('bitbucket_webhook', 'Bitbucket incoming webhook'), ('gitlab_webhook', 'GitLab incoming webhook'), ('api_webhook', 'Generic API incoming webhook')], max_length=32, verbose_name='Integration type'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.12 on 2018-02-17 11:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('projects', '0023_migrate-alias-slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='domain',
|
||||
name='canonical',
|
||||
field=models.BooleanField(default=False, help_text='This Domain is the primary one where the documentation is served from'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='domain',
|
||||
name='count',
|
||||
field=models.IntegerField(default=0, help_text='Number of times this domain has been hit'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='allow_promos',
|
||||
field=models.BooleanField(default=True, help_text='If unchecked, users will still see community ads.', verbose_name='Allow paid advertising'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='comment_moderation',
|
||||
field=models.BooleanField(default=False, verbose_name='Comment Moderation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='conf_py_file',
|
||||
field=models.CharField(blank=True, default='', help_text='Path from project root to <code>conf.py</code> file (ex. <code>docs/conf.py</code>). Leave blank if you want us to find it for you.', max_length=255, verbose_name='Python configuration file'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='has_valid_webhook',
|
||||
field=models.BooleanField(default=False, help_text='This project has been built with a webhook'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='programming_language',
|
||||
field=models.CharField(blank=True, choices=[('words', 'Only Words'), ('py', 'Python'), ('js', 'JavaScript'), ('php', 'PHP'), ('ruby', 'Ruby'), ('perl', 'Perl'), ('java', 'Java'), ('go', 'Go'), ('julia', 'Julia'), ('c', 'C'), ('csharp', 'C#'), ('cpp', 'C++'), ('objc', 'Objective-C'), ('css', 'CSS'), ('ts', 'TypeScript'), ('swift', 'Swift'), ('vb', 'Visual Basic'), ('r', 'R'), ('scala', 'Scala'), ('groovy', 'Groovy'), ('coffee', 'CoffeeScript'), ('lua', 'Lua'), ('haskell', 'Haskell'), ('other', 'Other')], default='words', help_text='The primary programming language the project is written in.', max_length=20, verbose_name='Programming Language'),
|
||||
),
|
||||
]
|
|
@ -21,8 +21,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from django.views.generic import ListView, TemplateView, View
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
from vanilla import CreateView, DeleteView, DetailView, GenericView, UpdateView
|
||||
|
||||
from readthedocs.bookmarks.models import Bookmark
|
||||
from readthedocs.builds.forms import AliasForm, VersionForm
|
||||
from readthedocs.builds.models import Version, VersionAlias
|
||||
from readthedocs.core.mixins import ListViewWithForm, LoginRequiredMixin
|
||||
|
@ -61,13 +59,6 @@ class ProjectDashboard(PrivateViewMixin, ListView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super(ProjectDashboard, self).get_context_data(**kwargs)
|
||||
|
||||
bookmarks = Bookmark.objects.filter(user=self.request.user)
|
||||
|
||||
if bookmarks.exists:
|
||||
context['bookmark_list'] = bookmarks[:3]
|
||||
else:
|
||||
bookmarks = None
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.12 on 2018-02-17 11:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('redirects', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='redirect',
|
||||
name='to_url',
|
||||
field=models.CharField(blank=True, db_index=True, help_text='Absolute or relative URL. Examples: <b>/tutorial/install.html</b>', max_length=255, verbose_name='To URL'),
|
||||
),
|
||||
]
|
|
@ -87,7 +87,6 @@ class CommunityBaseSettings(Settings):
|
|||
'tastypie',
|
||||
|
||||
# our apps
|
||||
'readthedocs.bookmarks',
|
||||
'readthedocs.projects',
|
||||
'readthedocs.builds',
|
||||
'readthedocs.comments',
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
{% extends "projects/base_project.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% blocktrans with bookmark.page as name %}Delete {{ name }}?{% endblocktrans %}{% endblock %}
|
||||
|
||||
{% block content-header %}<h1>{% blocktrans with bookmark.page as name %}Delete {{ name }}?{% endblocktrans %}</h1>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "You sure?" %} O_o</p>
|
||||
|
||||
<form method="post" action=".">{% csrf_token %}
|
||||
<input type="submit" value="{% trans "Delete forever" %}">
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,30 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% load pagination_tags %}
|
||||
|
||||
{% block title %}{% trans "Bookmarks" %}{% endblock %}
|
||||
|
||||
{% block nav-browse %}class="active"{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% autopaginate bookmark_list 15 %}
|
||||
|
||||
<!-- BEGIN builds list -->
|
||||
<div class="module">
|
||||
<div class="module-wrapper">
|
||||
|
||||
<div class="module-header">
|
||||
<h2>{% trans "All Bookmarks" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END builds list -->
|
||||
|
||||
{% include "core/bookmark_list_detailed.html" %}
|
||||
|
||||
{% paginate %}
|
||||
|
||||
{% endblock %}
|
|
@ -39,7 +39,6 @@ basic_urls = [
|
|||
]
|
||||
|
||||
rtd_urls = [
|
||||
url(r'^bookmarks/', include('readthedocs.bookmarks.urls')),
|
||||
url(r'^search/$', search_views.elastic_search, name='search'),
|
||||
url(r'^dashboard/', include('readthedocs.projects.urls.private')),
|
||||
url(r'^profiles/', include('readthedocs.profiles.urls.public')),
|
||||
|
|
Loading…
Reference in New Issue