mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-07 07:08:03 -05:00
display external recipes
This commit is contained in:
@@ -2037,7 +2037,7 @@ def get_external_file_link(request, recipe_id):
|
|||||||
def get_recipe_file(request, recipe_id):
|
def get_recipe_file(request, recipe_id):
|
||||||
recipe = get_object_or_404(Recipe, pk=recipe_id, space=request.space)
|
recipe = get_object_or_404(Recipe, pk=recipe_id, space=request.space)
|
||||||
if recipe.storage:
|
if recipe.storage:
|
||||||
return FileResponse(get_recipe_provider(recipe).get_file(recipe))
|
return FileResponse(get_recipe_provider(recipe).get_file(recipe), filename=f'{recipe.name}.pdf')
|
||||||
else:
|
else:
|
||||||
return FileResponse()
|
return FileResponse()
|
||||||
|
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||||
|
|
||||||
OAUTH2_PROVIDER = {'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'bookmarklet': 'only access to bookmarklet'}}
|
OAUTH2_PROVIDER = {'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'bookmarklet': 'only access to bookmarklet'}}
|
||||||
READ_SCOPE = 'read'
|
READ_SCOPE = 'read'
|
||||||
|
|||||||
47
vue3/src/components/display/ExternalRecipeViewer.vue
Normal file
47
vue3/src/components/display/ExternalRecipeViewer.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<v-card class="mt-1 h-100">
|
||||||
|
<iframe width="100%" height="700px" :src="`${getDjangoUrl('/api/get_recipe_file/')}${props.recipe.id}/`" v-if="isPdf"></iframe>
|
||||||
|
|
||||||
|
<v-img :src="`${getDjangoUrl('/api/get_recipe_file/')}${props.recipe.id}/`" v-if="isImage"></v-img>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed, PropType} from "vue";
|
||||||
|
import {Recipe} from "@/openapi";
|
||||||
|
import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
recipe: {type: {} as PropType<Recipe>, required: true}
|
||||||
|
})
|
||||||
|
|
||||||
|
const {getDjangoUrl} = useDjangoUrls()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines if the file is a PDF based on the path
|
||||||
|
*/
|
||||||
|
const isPdf = computed(() => {
|
||||||
|
let filePath = props.recipe.filePath
|
||||||
|
if (filePath) {
|
||||||
|
return filePath.includes('.pdf')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines if the file is an image based on the path
|
||||||
|
*/
|
||||||
|
const isImage = computed(() => {
|
||||||
|
let filePath = props.recipe.filePath
|
||||||
|
if (filePath) {
|
||||||
|
return filePath.includes('.png') || filePath.includes('.jpg') || filePath.includes('.jpeg') || filePath.includes('.gif')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<recipe-image
|
<recipe-image
|
||||||
max-height="25vh"
|
max-height="25vh"
|
||||||
:recipe="props.recipe"
|
:recipe="props.recipe"
|
||||||
|
v-if="recipe.internal"
|
||||||
>
|
>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<v-chip class="ms-2" color="primary" variant="flat" size="x-small">by {{ props.recipe.createdBy.displayName }}</v-chip>
|
<v-chip class="ms-2" color="primary" variant="flat" size="x-small">by {{ props.recipe.createdBy.displayName }}</v-chip>
|
||||||
@@ -32,39 +33,45 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<v-card class="mt-1">
|
<template v-if="!recipe.internal">
|
||||||
<v-container>
|
<external-recipe-viewer :recipe="recipe"></external-recipe-viewer>
|
||||||
<v-row class="text-center text-body-2">
|
</template>
|
||||||
<v-col class="pt-1 pb-1">
|
|
||||||
<i class="fas fa-cogs fa-fw mr-1"></i> {{ props.recipe.workingTime }} min<br/>
|
|
||||||
<div class="text-grey">{{$t('WorkingTime')}}</div>
|
|
||||||
</v-col>
|
|
||||||
<v-col class="pt-1 pb-1">
|
|
||||||
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ props.recipe.waitingTime }} min</div>
|
|
||||||
<div class="text-grey">{{$t('WaitingTime')}}</div>
|
|
||||||
</v-col>
|
|
||||||
<v-col class="pt-1 pb-1">
|
|
||||||
|
|
||||||
<div class="cursor-pointer">
|
<template v-else>
|
||||||
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
|
<v-card class="mt-1">
|
||||||
<div class="text-grey"><span v-if="props.recipe?.servingsText">{{ props.recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div>
|
<v-container>
|
||||||
<number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings">
|
<v-row class="text-center text-body-2">
|
||||||
</number-scaler-dialog>
|
<v-col class="pt-1 pb-1">
|
||||||
</div>
|
<i class="fas fa-cogs fa-fw mr-1"></i> {{ props.recipe.workingTime }} min<br/>
|
||||||
|
<div class="text-grey">{{ $t('WorkingTime') }}</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col class="pt-1 pb-1">
|
||||||
|
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ props.recipe.waitingTime }} min</div>
|
||||||
|
<div class="text-grey">{{ $t('WaitingTime') }}</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col class="pt-1 pb-1">
|
||||||
|
|
||||||
|
<div class="cursor-pointer">
|
||||||
|
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
|
||||||
|
<div class="text-grey"><span v-if="props.recipe?.servingsText">{{ props.recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div>
|
||||||
|
<number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings">
|
||||||
|
</number-scaler-dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<v-card class="mt-1" v-if="props.recipe.steps.length > 1">
|
<v-card class="mt-1" v-if="props.recipe.steps.length > 1">
|
||||||
<steps-overview :steps="props.recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
|
<steps-overview :steps="props.recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<v-card class="mt-1" v-for="(step, index) in props.recipe.steps" :key="step.id">
|
<v-card class="mt-1" v-for="(step, index) in props.recipe.steps" :key="step.id">
|
||||||
<step :step="step" :step-number="index+1" :ingredientFactor="ingredientFactor"></step>
|
<step :step="step" :step-number="index+1" :ingredientFactor="ingredientFactor"></step>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
<recipe-activity :recipe="recipe"></recipe-activity>
|
<recipe-activity :recipe="recipe"></recipe-activity>
|
||||||
</template>
|
</template>
|
||||||
@@ -83,6 +90,9 @@ import RecipeActivity from "@/components/display/RecipeActivity.vue";
|
|||||||
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
|
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
|
||||||
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
||||||
import RecipeImage from "@/components/display/RecipeImage.vue";
|
import RecipeImage from "@/components/display/RecipeImage.vue";
|
||||||
|
import PdfViewer from "../../../../vue/src/components/PdfViewer.vue";
|
||||||
|
import ImageViewer from "../../../../vue/src/components/ImageViewer.vue";
|
||||||
|
import ExternalRecipeViewer from "@/components/display/ExternalRecipeViewer.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recipe: {
|
recipe: {
|
||||||
|
|||||||
@@ -4023,7 +4023,7 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async apiGetRecipeFileRetrieveRaw(requestParameters: ApiGetRecipeFileRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
async apiGetRecipeFileRetrieveRaw(requestParameters: ApiGetRecipeFileRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction) {
|
||||||
if (requestParameters['recipeId'] == null) {
|
if (requestParameters['recipeId'] == null) {
|
||||||
throw new runtime.RequiredError(
|
throw new runtime.RequiredError(
|
||||||
'recipeId',
|
'recipeId',
|
||||||
@@ -4046,13 +4046,13 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
query: queryParameters,
|
query: queryParameters,
|
||||||
}, initOverrides);
|
}, initOverrides);
|
||||||
|
|
||||||
return new runtime.VoidApiResponse(response);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async apiGetRecipeFileRetrieve(requestParameters: ApiGetRecipeFileRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
async apiGetRecipeFileRetrieve(requestParameters: ApiGetRecipeFileRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction){
|
||||||
await this.apiGetRecipeFileRetrieveRaw(requestParameters, initOverrides);
|
return await this.apiGetRecipeFileRetrieveRaw(requestParameters, initOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user