Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
['name' => 'main#home', 'url' => '/home', 'verb' => 'GET'],
['name' => 'main#keywords', 'url' => '/keywords', 'verb' => 'GET'],
['name' => 'main#categories', 'url' => '/categories', 'verb' => 'GET'],
['name' => 'main#category', 'url' => '/category/{category}', 'verb' => 'GET'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really no more needed? I just found this line but I am not sure if this is related (not JS/VUE expert). Just the red warn lamp when changing the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API is, at the moment, a mix of the old PHP routes (which are reachable only through manually writing them on the address line) and the new JSON API routes. I have not just gone ahead and pruned it because I am not familiar with the back-end and don't want to remove anything that is actually still in use. Unless the goal is to maintain both the Vue front-end and a backup synchronous front-end for people who cannot or refuse to use javascript, the obsolete routes should be removed at some point. Maintaining two front-ends is too big an undertaking for me at least, so unless there is someone else on the team to maintain the PHP template side, I suggest we get rid of it sooner rather than later. Again, this should probably have its own issue.

And yes, the line you spotted in the Vue router does replace the line that was removed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, you moved the routes to the /api prefix here. This is perfectly fine for me. I was just curious why in the vue router the prefix is not mentioned.

['name' => 'main#search', 'url' => '/search/{query}', 'verb' => 'GET'],
['name' => 'main#error', 'url' => '/error', 'verb' => 'GET'],
['name' => 'main#create', 'url' => '/recipes/create', 'verb' => 'GET'],
['name' => 'main#new', 'url' => '/recipes/create', 'verb' => 'POST'],
Expand All @@ -26,9 +24,12 @@
['name' => 'config#reindex', 'url' => '/reindex', 'verb' => 'POST'],
['name' => 'config#list', 'url' => '/config', 'verb' => 'GET'],
['name' => 'config#config', 'url' => '/config', 'verb' => 'POST'],
/* API routes */
['name' => 'main#category', 'url' => '/api/category/{category}', 'verb' => 'GET'],
['name' => 'main#search', 'url' => '/api/search/{query}', 'verb' => 'GET'],
],
/* API routes */

/* API resources */
'resources' => [
'recipe' => ['url' => '/api/recipes']
]
Expand Down
10 changes: 6 additions & 4 deletions lib/Controller/MainController.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public function search($query)
$recipes = $this->service->findRecipesInSearchIndex($query);

foreach ($recipes as $i => $recipe) {
$recipes[$i]['image_url'] = $this->urlGenerator->linkToRoute(
$recipes[$i]['imageUrl'] = $this->urlGenerator->linkToRoute(
'cookbook.recipe.image',
[
'id' => $recipe['recipe_id'],
Expand All @@ -153,6 +153,8 @@ public function search($query)
);
}

return new DataResponse($recipes, 200, ['Content-Type' => 'application/json']);
// TODO: Remove obsolete code below when this is ready
$response = new TemplateResponse($this->appName, 'content/search', ['query' => $query, 'recipes' => $recipes]);
$response->renderAs('blank');

Expand Down Expand Up @@ -183,7 +185,7 @@ public function category($category)
]
);
}

return new DataResponse($recipes, Http::STATUS_OK, ['Content-Type' => 'application/json']);
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), 500);
Expand Down Expand Up @@ -319,7 +321,7 @@ public function update($id)

try {
$recipe_data = [];

parse_str(file_get_contents("php://input"), $recipe_data);

$recipe_data['id'] = $id;
Expand All @@ -331,7 +333,7 @@ public function update($id)

} catch (\Exception $e) {
return new DataResponse($e->getMessage(), 500);

}
}
}
10 changes: 6 additions & 4 deletions lib/Db/RecipeDb.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ public function findRecipes(array $keywords, string $user_id) {
$qb = $this->db->getQueryBuilder();

$qb->select(['r.recipe_id', 'r.name'])
->from(self::DB_TABLE_KEYWORDS, 'k');
->from(self::DB_TABLE_RECIPES, 'r');

$qb->leftJoin('r', self::DB_TABLE_KEYWORDS, 'k', 'k.recipe_id = r.recipe_id');
$qb->leftJoin('r', self::DB_TABLE_CATEGORIES, 'c', 'r.recipe_id = c.recipe_id');

$paramIdx = 1;
$params = [];
Expand All @@ -232,19 +235,18 @@ public function findRecipes(array $keywords, string $user_id) {

$qb->orWhere("LOWER(k.name) LIKE :keyword$paramIdx");
$qb->orWhere("LOWER(r.name) LIKE :keyword$paramIdx");
$qb->orWhere("LOWER(c.name) LIKE :keyword$paramIdx");

$params["keyword$paramIdx"] = "%$lowerKeyword%";
$types["keyword$paramIdx"] = Type::STRING;
$paramIdx++;

}

$qb->andWhere('k.user_id = :user');
$qb->andWhere('r.user_id = :user');

$qb->setParameters($params, $types);
$qb->setParameter('user', $user_id, TYPE::STRING);

$qb->join('k', self::DB_TABLE_RECIPES, 'r', 'k.recipe_id = r.recipe_id');

$qb->groupBy(['r.name', 'r.recipe_id']);
$qb->orderBy('r.name');
Expand Down
20 changes: 14 additions & 6 deletions src/components/AppControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
<Breadcrumbs class="breadcrumbs" rootIcon="icon-category-organization">
<Breadcrumb :title="t('cookbook', 'Home')" :to="'/'" :disableDrop="true" />
<!-- INDEX PAGE -->
<Breadcrumb v-if="isIndex" class="active no-arrow" :title="t('cookbook', 'All recipes')" :disableDrop="true"></Breadcrumb>
<!--
<Breadcrumb v-if="isIndex" class="active no-arrow" :title="t('cookbook', 'All recipes')" :disableDrop="true" />
<Breadcrumb v-if="isIndex" class="no-arrow" title="" :disableDrop="true">
<ActionButton icon="icon-search" class="action-button" :disabled="true" :ariaLabel="t('cookbook', 'Search')" @click="$window.goTo('/search')" />
<!-- This is clumsy design but the component cannot display just one input element on the breadcrumbs bar -->
<ActionInput icon="icon-quota" @update:value="updateFilters" :value="filterValue">{{ t('cookbook', 'Filter') }}</ActionInput>
<ActionInput icon="icon-search" @submit="search">{{ t('cookbook', 'Search') }}</ActionInput>
</Breadcrumb>
-->
<!-- SEARCH PAGE -->
<Breadcrumb v-if="isSearch" class="not-link" :title="searchTitle" :disableDrop="true" />
<Breadcrumb v-if="isSearch && $route.params.value" class="active" :title="$route.params.value" :disableDrop="true" />
Expand Down Expand Up @@ -87,17 +87,18 @@

<script>
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import ActionInput from '@nextcloud/vue/dist/Components/ActionInput'
import Breadcrumbs from '@nextcloud/vue/dist/Components/Breadcrumbs'
import Breadcrumb from '@nextcloud/vue/dist/Components/Breadcrumb'

export default {
name: 'AppControls',
components: {
ActionButton, Breadcrumbs, Breadcrumb
ActionButton, ActionInput, Breadcrumbs, Breadcrumb
},
data () {
return {

filterValue: '',
}
},
computed: {
Expand Down Expand Up @@ -220,9 +221,16 @@ export default {
saveChanges: function() {
this.$root.$emit('saveRecipe')
},
search: function(e) {
this.$window.goTo('/search/'+e.target[1].value)
},
toggleNavigation: function() {
$("#app-navigation").toggleClass("show-navigation")
},
updateFilters: function(e) {
this.filterValue = e
this.$root.$emit('applyRecipeFilter', e)
},
},
mounted () {
},
Expand Down
24 changes: 22 additions & 2 deletions src/components/AppIndex.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<ul>
<li v-for="recipe in recipes" :key="recipe.recipe_id">
<li v-for="recipe in filteredRecipes" :key="recipe.recipe_id">
<router-link :to="'/recipe/'+recipe.recipe_id">
<img v-if="recipe.imageUrl" :src="recipe.imageUrl">
<span>{{ recipe.name }}</span>
Expand All @@ -14,9 +14,25 @@ export default {
name: 'Index',
data () {
return {
recipes: []
filters: "",
recipes: [],
}
},
computed: {
filteredRecipes () {
if (!this.filters) {
return this.recipes
} else if (this.recipes.length) {
let filtered = []
for (let i=0; i<this.recipes.length; i++) {
if (this.recipes[i].name.toLowerCase().indexOf(this.filters.toLowerCase()) >= 0) {
filtered.push(this.recipes[i])
}
}
return filtered
}
},
},
methods: {
/**
* Load all recipes from the database
Expand All @@ -38,6 +54,10 @@ export default {
},
},
mounted () {
this.$root.$off('applyRecipeFilter')
this.$root.$on('applyRecipeFilter', (value) => {
this.filters = value
})
this.loadAll()
},
}
Expand Down
22 changes: 17 additions & 5 deletions src/components/SearchResults.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,18 @@ export default {
},
methods: {
setup: function() {
// TODO: This is a mess of different implementation styles, needs cleanup
if (this.query === 'name') {
// Search by name
console.log("Recipe name search for "+this.$route.params.value)
}
if (this.query === 'tag') {
// Search by tag
console.log("Tag search for "+this.$route.params.value)
}
if (this.query === 'cat') {
// Search by category
console.log("Category search for "+this.$route.params.value)
let $this = this
let cat = this.$route.params.value
$.get(this.$window.baseUrl + '/category/'+cat).done(function(json) {
$.get(this.$window.baseUrl + '/api/category/'+cat).done(function(json) {
$this.results = json
}).fail(function (jqXHR, textStatus, errorThrown) {
$this.results = []
Expand All @@ -43,7 +41,21 @@ export default {
}
})
} else {
// Something else?
// General search
let deferred = $.Deferred()
$.get(this.$window.baseUrl + '/api/search/'+this.$route.params.value).done((recipes) => {
this.results = recipes
deferred.resolve()
}).fail((jqXHR, textStatus, errorThrown) => {
this.results = []
deferred.reject(new Error(jqXHR.responseText))
alert(t('cookbook', 'Failed to load search results'))
if (e && e instanceof Error) {
throw e
}
})
this.$store.dispatch('setPage', { page: 'search' })
return deferred.promise()
}
this.$store.dispatch('setPage', { page: 'search' })
},
Expand Down
5 changes: 3 additions & 2 deletions src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const routes = [
// Search routes
{ path: '/category/:value', name: 'search-category', component: Search, props: { query: 'cat' } },
{ path: '/name/:value', name: 'search-name', component: Search, props: { query: 'name' } },
{ path: '/search/:value', name: 'search-general', component: Search, props: { query: 'general' } },
{ path: '/tag/:value', name: 'search-tag', component: Search, props: { query: 'tag' } },

// Recipe routes
Expand All @@ -41,10 +42,10 @@ const routes = [
{ path: '/recipe/create', name: 'recipe-create', component: RecipeEdit },
{ path: '/recipe/:id/edit', name: 'recipe-edit', component: RecipeEdit },
{ path: '/recipe/:id', name: 'recipe-view', component: RecipeView },

// Index is the last defined route
{ path: '/', name:'index', component: Index },

// Anything not matched goes to NotFound
{ path: '*', name:'not-found', component: NotFound },
];
Expand Down