WIP pdf embedding

This commit is contained in:
vabene1111
2020-02-19 16:55:13 +01:00
parent fc1cc70870
commit 88dc713683
12 changed files with 134 additions and 90 deletions

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.0.2 on 2020-02-19 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0025_userpreference_nav_color'),
]
operations = [
migrations.AddField(
model_name='recipe',
name='cors_link',
field=models.CharField(blank=True, max_length=1024, null=True),
),
migrations.AlterField(
model_name='recipe',
name='link',
field=models.CharField(blank=True, max_length=512, null=True),
),
]

View File

@@ -84,7 +84,8 @@ class Recipe(models.Model):
storage = models.ForeignKey(Storage, on_delete=models.PROTECT, blank=True, null=True) storage = models.ForeignKey(Storage, on_delete=models.PROTECT, blank=True, null=True)
file_uid = models.CharField(max_length=256, default="") file_uid = models.CharField(max_length=256, default="")
file_path = models.CharField(max_length=512, default="") file_path = models.CharField(max_length=512, default="")
link = models.CharField(max_length=512, default="") link = models.CharField(max_length=512, null=True, blank=True)
cors_link = models.CharField(max_length=1024, null=True, blank=True)
keywords = models.ManyToManyField(Keyword, blank=True) keywords = models.ManyToManyField(Keyword, blank=True)
working_time = models.IntegerField(default=0) working_time = models.IntegerField(default=0)
waiting_time = models.IntegerField(default=0) waiting_time = models.IntegerField(default=0)

View File

@@ -88,6 +88,16 @@ class Dropbox(Provider):
response = Dropbox.create_share_link(recipe) response = Dropbox.create_share_link(recipe)
return response['url'] return response['url']
@staticmethod
def get_cors_link(recipe):
if not recipe.link:
recipe.link = Dropbox.get_share_link(recipe)
recipe.save()
recipe.cors_link = recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.')
return recipe.cors_link
@staticmethod @staticmethod
def rename_file(recipe, new_name): def rename_file(recipe, new_name):
url = "https://api.dropboxapi.com/2/files/move_v2" url = "https://api.dropboxapi.com/2/files/move_v2"

View File

@@ -11,10 +11,14 @@ class Provider:
def get_share_link(recipe): def get_share_link(recipe):
raise Exception('Method not implemented in storage provider') raise Exception('Method not implemented in storage provider')
@staticmethod
def get_cors_link(recipe):
raise Exception('Method not implemented in storage provider')
@staticmethod @staticmethod
def rename_file(recipe, new_name): def rename_file(recipe, new_name):
raise Exception('Method not implemented in storage provider') raise Exception('Method not implemented in storage provider')
@staticmethod @staticmethod
def delete_file(recipe, new_name): def delete_file(recipe):
raise Exception('Method not implemented in storage provider') raise Exception('Method not implemented in storage provider')

View File

@@ -8,8 +8,7 @@ from .models import *
class RecipeTable(tables.Table): class RecipeTable(tables.Table):
id = tables.LinkColumn('edit_recipe', args=[A('id')]) id = tables.LinkColumn('edit_recipe', args=[A('id')])
name = tables.TemplateColumn( name = tables.LinkColumn('view_recipe', args=[A('id')])
"<a href='#' onClick='openRecipe({{record.id}})'>{{record.name}}</a>")
all_tags = tables.Column( all_tags = tables.Column(
attrs={'td': {'class': 'd-none d-lg-table-cell'}, 'th': {'class': 'd-none d-lg-table-cell'}}) attrs={'td': {'class': 'd-none d-lg-table-cell'}, 'th': {'class': 'd-none d-lg-table-cell'}})

View File

@@ -41,7 +41,7 @@
{% for r in b.recipes %} {% for r in b.recipes %}
<div class="row"> <div class="row">
<div class="col col-md-10"> <div class="col col-md-10">
<li><a href="#" onClick='openRecipe({{ r.recipe.pk }})'>{{ r.recipe.name }}</a></li> <li><a href="{% url 'view_recipe' r.recipe.pk %}">{{ r.recipe.name }}</a></li>
</div> </div>
<div class="col col-md-2" style="text-align: right"> <div class="col col-md-2" style="text-align: right">
<a href="{% url 'delete_recipe_book_entry' r.pk %}"><i class="fas fa-trash-alt"></i></a> <a href="{% url 'delete_recipe_book_entry' r.pk %}"><i class="fas fa-trash-alt"></i></a>
@@ -58,5 +58,4 @@
<br/> <br/>
{% endfor %} {% endfor %}
{% include 'include/recipe_open_modal.html' %}
{% endblock %} {% endblock %}

View File

@@ -43,15 +43,12 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
function openRecipe(id, force_external = false) { function openRecipe(id) {
var link = $('#a_recipe_open'); var link = $('#a_recipe_open');
link.hide(); link.hide();
$('#div_loader').show(); $('#div_loader').show();
var url = "{% url 'api_get_file_link' recipe_id=12345 %}".replace(/12345/, id); var url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, id);
if (force_external) {
url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, id);
}
link.text("{% trans 'Open Recipe' %}"); link.text("{% trans 'Open Recipe' %}");
$('#modal_recipe').modal('show'); $('#modal_recipe').modal('show');

View File

@@ -63,6 +63,4 @@
</div> </div>
{% endif %} {% endif %}
{% include 'include/recipe_open_modal.html' %}
{% endblock %} {% endblock %}

View File

@@ -56,7 +56,7 @@
<td> <td>
{% for mp in days_value %} {% for mp in days_value %}
<a href="{% url 'edit_plan' mp.pk %}"><i class="fas fa-edit"></i></a> <a href="{% url 'edit_plan' mp.pk %}"><i class="fas fa-edit"></i></a>
<a href="#" onclick="openRecipe({{ mp.recipe.id }})">{{ mp.recipe.name }}</a><br/> <a href="{% url 'view_recipe' mp.recipe.id %}">{{ mp.recipe.name }}</a><br/>
{% endfor %} {% endfor %}
</td> </td>
{% endfor %} {% endfor %}
@@ -67,6 +67,4 @@
</div> </div>
</div> </div>
{% include 'include/recipe_open_modal.html' %}
{% endblock %} {% endblock %}

View File

@@ -134,84 +134,96 @@
{% if recipe.storage %} {% if recipe.storage %}
<div class="row"> <div class="row">
{% if recipe.internal %} {% if recipe.internal %}
<a href='#' onClick='openRecipe({{ recipe.id }}, true)' <a href='#' onClick='openRecipe({{ recipe.id }})'
class="d-print-none">{% trans 'View external recipe' %} <i class="fas fa-external-link-alt"></i></a> class="d-print-none">{% trans 'View external recipe' %} <i class="fas fa-external-link-alt"></i></a>
{% else %} {% else %}
<canvas id="id_pdf_canvas" class="border"></canvas> <div class="col col-md-12" style="margin-top: 2vh">
<div class="loader" id="id_loader"></div>
<canvas id="id_pdf_canvas" class="border"></canvas>
</div>
<br/> <div class="col col-md-12" style="margin-top: 2vh">
<br/> <div class="card border-info">
<br/> <div class="card-body text-info">
<div class="card border-info"> <h5 class="card-title">{% trans 'External recipe' %}</h5>
<div class="card-body text-info"> <p class="card-text">
<h5 class="card-title">{% trans 'External recipe' %}</h5> {% blocktrans %}
<p class="card-text"> This is an external recipe, which means you can only view it by opening the link
{% blocktrans %} above.
This is an external recipe, which means you can only view it by opening the link above. You can convert this recipe to a fancy recipe by pressing the convert button. The
You can convert this recipe to a fancy recipe by pressing the convert button. The original
original file
file will still be accessible.
will still be accessible. {% endblocktrans %}.
{% endblocktrans %}. <br/>
<br/> <br/>
<br/> <a href="{% url 'edit_convert_recipe' recipe.pk %}"
<a href="{% url 'edit_convert_recipe' recipe.pk %}" class="card-link btn btn-info">{% trans 'Convert now!' %}</a>
class="card-link btn btn-info">{% trans 'Convert now!' %}</a> <a href='#' onClick='openRecipe({{ recipe.id }})'
</p> class="d-print-none btn btn-warning">{% trans 'View external recipe' %} <i
class="fas fa-external-link-alt"></i></a>
</p>
</div>
</div> </div>
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.3.200/pdf.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.3.200/pdf.min.js"
integrity="sha256-J4Z8Fhj2MITUakMQatkqOVdtqodUlwHtQ/ey6fSsudE=" integrity="sha256-J4Z8Fhj2MITUakMQatkqOVdtqodUlwHtQ/ey6fSsudE="
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script type="text/javascript"> <script type="text/javascript">
var url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, {{ recipe.id }}); var url = "{% url 'api_get_cors_file_link' recipe_id=12345 %}".replace(/12345/, {{ recipe.id }});
$('#id_pdf_canvas').hide();
var xhttp = new XMLHttpRequest(); var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () { xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) { if (this.readyState === 4 && this.status === 200) {
var url = this.responseText; var url = this.responseText;
$('#id_loader').hide();
var loadingTask = pdfjsLib.getDocument(url.replace('www.dropbox.', 'dl.dropboxusercontent.')); if (url === "None") {
loadingTask.promise.then(function (pdf) { // direct previews are not supported for this provider
console.log('PDF loaded'); //TODO implement something useful for providers not allowing cors links
} else {
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function (pdf) {
$('#id_pdf_canvas').show();
// Fetch the first page // Fetch the first page
var pageNumber = 1; var pageNumber = 1;
pdf.getPage(pageNumber).then(function (page) { pdf.getPage(pageNumber).then(function (page) {
console.log('Page loaded');
var scale = 1.5;
var viewport = page.getViewport({scale: scale*0.8});
// Prepare canvas using PDF page dimensions var scale = 1.5;
var canvas = document.getElementById('id_pdf_canvas'); var viewport = page.getViewport({scale: scale * 0.8});
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context // Prepare canvas using PDF page dimensions
var renderContext = { var canvas = document.getElementById('id_pdf_canvas');
canvasContext: context, var context = canvas.getContext('2d');
viewport: viewport canvas.height = viewport.height;
}; canvas.width = viewport.width;
var renderTask = page.render(renderContext);
renderTask.promise.then(function () { // Render PDF page into canvas context
console.log('Page rendered'); var renderContext = {
canvasContext: context,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
console.log('Page rendered');
});
}); });
}, function (reason) {
// PDF loading error
console.error(reason);
}); });
}, function (reason) { }
// PDF loading error
console.error(reason);
});
} }
}; };
xhttp.open("GET", url, true); xhttp.open("GET", url, true);
xhttp.send(); xhttp.send();
</script> </script>
{% endif %} {% endif %}
</div> </div>

View File

@@ -60,8 +60,8 @@ urlpatterns = [
path('data/sync/wait', data.sync_wait, name='data_sync_wait'), path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
path('data/statistics', data.statistics, name='data_stats'), path('data/statistics', data.statistics, name='data_stats'),
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'), path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
path('api/get_cors_file_link/<int:recipe_id>/', api.get_cors_file_link, name='api_get_cors_file_link'),
path('api/sync_all/', api.sync_all, name='api_sync'), path('api/sync_all/', api.sync_all, name='api_sync'),

View File

@@ -10,40 +10,43 @@ from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud from cookbook.provider.nextcloud import Nextcloud
@login_required def update_recipe_links(recipe):
def get_file_link(request, recipe_id): if recipe.storage.method == Storage.DROPBOX:
recipe = Recipe.objects.get(id=recipe_id) provider = Dropbox
elif recipe.storage.method == Storage.NEXTCLOUD:
provider = Nextcloud
else:
raise Exception('Provider not implemented')
if recipe.internal: if not recipe.link:
return HttpResponse(reverse('view_recipe', args=[recipe_id])) recipe.link = provider.get_share_link(recipe) # TODO response validation in apis
if recipe.storage.method == Storage.DROPBOX: # TODO move to central location (as all provider related functions) if not recipe.cors_link:
if recipe.link == "": try:
recipe.link = Dropbox.get_share_link(recipe) # TODO response validation recipe.cors_link = provider.get_cors_link(recipe)
recipe.save() except Exception:
if recipe.storage.method == Storage.NEXTCLOUD: pass
if recipe.link == "":
recipe.link = Nextcloud.get_share_link(recipe) # TODO response validation
recipe.save()
return HttpResponse(recipe.link) recipe.save()
@login_required @login_required
def get_external_file_link(request, recipe_id): def get_external_file_link(request, recipe_id):
recipe = Recipe.objects.get(id=recipe_id) recipe = Recipe.objects.get(id=recipe_id)
if not recipe.link:
if recipe.storage.method == Storage.DROPBOX: # TODO move to central location (as all provider related functions) update_recipe_links(recipe)
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) return HttpResponse(recipe.link)
@login_required
def get_cors_file_link(request, recipe_id):
recipe = Recipe.objects.get(id=recipe_id)
if not recipe.cors_link:
update_recipe_links(recipe)
return HttpResponse(recipe.cors_link)
@login_required @login_required
def sync_all(request): def sync_all(request):
monitors = Sync.objects.filter(active=True) monitors = Sync.objects.filter(active=True)