More improvements on the REST API (references #139)

This commit is contained in:
Bernd Bestel 2019-01-21 22:13:42 +01:00
parent bfa59dd29c
commit 276bc94cc6
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
15 changed files with 102 additions and 85 deletions

View File

@ -18,13 +18,13 @@ class FilesApiController extends BaseApiController
{ {
try try
{ {
if (isset($request->getQueryParams()['file_name']) && !empty($request->getQueryParams()['file_name']) && IsValidFileName($request->getQueryParams()['file_name'])) if (IsValidFileName(base64_decode($args['fileName'])))
{ {
$fileName = $request->getQueryParams()['file_name']; $fileName = base64_decode($args['fileName']);
} }
else else
{ {
throw new \Exception('file_name query parameter missing or contains an invalid filename'); throw new \Exception('Invalid filename');
} }
$data = $request->getBody()->getContents(); $data = $request->getBody()->getContents();
@ -42,13 +42,13 @@ class FilesApiController extends BaseApiController
{ {
try try
{ {
if (isset($request->getQueryParams()['file_name']) && !empty($request->getQueryParams()['file_name']) && IsValidFileName($request->getQueryParams()['file_name'])) if (IsValidFileName(base64_decode($args['fileName'])))
{ {
$fileName = $request->getQueryParams()['file_name']; $fileName = base64_decode($args['fileName']);
} }
else else
{ {
throw new \Exception('file_name query parameter missing or contains an invalid filename'); throw new \Exception('Invalid filename');
} }
$filePath = $this->FilesService->GetFilePath($args['group'], $fileName); $filePath = $this->FilesService->GetFilePath($args['group'], $fileName);
@ -74,13 +74,13 @@ class FilesApiController extends BaseApiController
{ {
try try
{ {
if (isset($request->getQueryParams()['file_name']) && !empty($request->getQueryParams()['file_name']) && IsValidFileName($request->getQueryParams()['file_name'])) if (IsValidFileName(base64_decode($args['fileName'])))
{ {
$fileName = $request->getQueryParams()['file_name']; $fileName = base64_decode($args['fileName']);
} }
else else
{ {
throw new \Exception('file_name query parameter missing or contains an invalid filename'); throw new \Exception('Invalid filename');
} }
$filePath = $this->FilesService->GetFilePath($args['group'], $fileName); $filePath = $this->FilesService->GetFilePath($args['group'], $fileName);

View File

@ -26,7 +26,7 @@
"paths": { "paths": {
"/system/db-changed-time": { "/system/db-changed-time": {
"get": { "get": {
"description": "Returns the time when the database was last changed", "summary": "Returns the time when the database was last changed",
"tags": [ "tags": [
"System" "System"
], ],
@ -46,7 +46,8 @@
}, },
"/system/log-missing-localization": { "/system/log-missing-localization": {
"post": { "post": {
"description": "Logs a missing localization string (only when MODE == 'dev', so should only be called then)", "summary": "Logs a missing localization string",
"description": "Only when MODE == 'dev', so should only be called then",
"tags": [ "tags": [
"System" "System"
], ],
@ -80,7 +81,7 @@
}, },
"/objects/{entity}": { "/objects/{entity}": {
"get": { "get": {
"description": "Returns all objects of the given entity", "summary": "Returns all objects of the given entity",
"tags": [ "tags": [
"Generic entity interactions" "Generic entity interactions"
], ],
@ -144,7 +145,7 @@
} }
}, },
"post": { "post": {
"description": "Adds a single object of the given entity", "summary": "Adds a single object of the given entity",
"tags": [ "tags": [
"Generic entity interactions" "Generic entity interactions"
], ],
@ -211,7 +212,7 @@
}, },
"/objects/{entity}/{objectId}": { "/objects/{entity}/{objectId}": {
"get": { "get": {
"description": "Returns a single object of the given entity", "summary": "Returns a single object of the given entity",
"tags": [ "tags": [
"Generic entity interactions" "Generic entity interactions"
], ],
@ -282,7 +283,7 @@
} }
}, },
"put": { "put": {
"description": "Edits the given object of the given entity", "summary": "Edits the given object of the given entity",
"tags": [ "tags": [
"Generic entity interactions" "Generic entity interactions"
], ],
@ -356,7 +357,7 @@
} }
}, },
"delete": { "delete": {
"description": "Deletes a single object of the given entity", "summary": "Deletes a single object of the given entity",
"tags": [ "tags": [
"Generic entity interactions" "Generic entity interactions"
], ],
@ -397,9 +398,10 @@
} }
} }
}, },
"/file/{group}": { "/files/{group}/{fileName}": {
"get": { "get": {
"description": "Serves the given file (with proper Content-Type header)", "summary": "Serves the given file",
"description": "With proper Content-Type header",
"tags": [ "tags": [
"Files" "Files"
], ],
@ -414,10 +416,10 @@
} }
}, },
{ {
"in": "query", "in": "path",
"name": "file_name", "name": "fileName",
"required": true, "required": true,
"description": "The file name (including extension)", "description": "The file name (including extension)<br>**BASE64 encoded**",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -448,7 +450,8 @@
} }
}, },
"put": { "put": {
"description": "Uploads a single file to /data/storage/{group}/{file_name} (you need to remember the group and file name to get or delete it again)", "summary": "Uploads a single file",
"description": "The file will be stored at /data/storage/{group}/{file_name} (you need to remember the group and file name to get or delete it again)",
"tags": [ "tags": [
"Files" "Files"
], ],
@ -463,10 +466,10 @@
} }
}, },
{ {
"in": "query", "in": "path",
"name": "file_name", "name": "fileName",
"required": true, "required": true,
"description": "The file name (including extension)", "description": "The file name (including extension)<br>**BASE64 encoded**",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -499,7 +502,7 @@
} }
}, },
"delete": { "delete": {
"description": "Deletes the given file", "summary": "Deletes the given file",
"tags": [ "tags": [
"Files" "Files"
], ],
@ -514,10 +517,10 @@
} }
}, },
{ {
"in": "query", "in": "path",
"name": "file_name", "name": "fileName",
"required": true, "required": true,
"description": "The file name (including extension)", "description": "The file name (including extension)<br>**BASE64 encoded**",
"schema": { "schema": {
"type": "string" "type": "string"
} }
@ -542,7 +545,7 @@
}, },
"/users": { "/users": {
"get": { "get": {
"description": "Returns all users", "summary": "Returns all users",
"tags": [ "tags": [
"User management" "User management"
], ],
@ -573,7 +576,7 @@
} }
}, },
"post": { "post": {
"description": "Creates a new user", "summary": "Creates a new user",
"tags": [ "tags": [
"User management" "User management"
], ],
@ -607,7 +610,7 @@
}, },
"/users/{userId}": { "/users/{userId}": {
"put": { "put": {
"description": "Edits the given user", "summary": "Edits the given user",
"tags": [ "tags": [
"User management" "User management"
], ],
@ -650,7 +653,7 @@
} }
}, },
"delete": { "delete": {
"description": "Deletes the given user", "summary": "Deletes the given user",
"tags": [ "tags": [
"User management" "User management"
], ],
@ -684,7 +687,7 @@
}, },
"/user/settings/{settingKey}": { "/user/settings/{settingKey}": {
"get": { "get": {
"description": "Gets the given setting of the currently logged in user", "summary": "Gets the given setting of the currently logged in user",
"tags": [ "tags": [
"User settings" "User settings"
], ],
@ -723,7 +726,7 @@
} }
}, },
"put": { "put": {
"description": "Sets the given setting of the currently logged in user", "summary": "Sets the given setting of the currently logged in user",
"tags": [ "tags": [
"User settings" "User settings"
], ],
@ -768,7 +771,7 @@
}, },
"/stock": { "/stock": {
"get": { "get": {
"description": "Returns all products which are currently in stock incl. the next expiring date per product", "summary": "Returns all products which are currently in stock incl. the next expiring date per product",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -791,7 +794,7 @@
}, },
"/stock/volatile": { "/stock/volatile": {
"get": { "get": {
"description": "Returns all products which are expiring soon, are already expired or currently missing", "summary": "Returns all products which are expiring soon, are already expired or currently missing",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -826,7 +829,7 @@
}, },
"/stock/products/{productId}": { "/stock/products/{productId}": {
"get": { "get": {
"description": "Returns details of the given product", "summary": "Returns details of the given product",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -867,7 +870,7 @@
}, },
"/stock/products/{productId}/entries": { "/stock/products/{productId}/entries": {
"get": { "get": {
"description": "Returns all stock entries of the given product in order of next use (first expiring first, then first in first out)", "summary": "Returns all stock entries of the given product in order of next use (first expiring first, then first in first out)",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -911,7 +914,7 @@
}, },
"/stock/products/{productId}/price-history": { "/stock/products/{productId}/price-history": {
"get": { "get": {
"description": "Returns the price history of the given product", "summary": "Returns the price history of the given product",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -955,7 +958,7 @@
}, },
"/stock/products/{productId}/add": { "/stock/products/{productId}/add": {
"post": { "post": {
"description": "Adds the given amount of the given product to stock", "summary": "Adds the given amount of the given product to stock",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1030,7 +1033,7 @@
}, },
"/stock/products/{productId}/consume": { "/stock/products/{productId}/consume": {
"post": { "post": {
"description": "Removes the given amount of the given product from stock", "summary": "Removes the given amount of the given product from stock",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1103,7 +1106,7 @@
}, },
"/stock/products/{productId}/inventory": { "/stock/products/{productId}/inventory": {
"post": { "post": {
"description": "Inventories the given product (adds/removes based on the given new amount)", "summary": "Inventories the given product (adds/removes based on the given new amount)",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1165,7 +1168,7 @@
}, },
"/stock/products/{productId}/open": { "/stock/products/{productId}/open": {
"post": { "post": {
"description": "Marks the given amount of the given product as opened", "summary": "Marks the given amount of the given product as opened",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1229,7 +1232,7 @@
}, },
"/stock/shoppinglist/add-missing-products": { "/stock/shoppinglist/add-missing-products": {
"post": { "post": {
"description": "Adds currently missing products (below defined min. stock amount) to the shopping list", "summary": "Adds currently missing products (below defined min. stock amount) to the shopping list",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1242,7 +1245,7 @@
}, },
"/stock/shoppinglist/clear": { "/stock/shoppinglist/clear": {
"post": { "post": {
"description": "Removes all items from the shopping list", "summary": "Removes all items from the shopping list",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1255,7 +1258,7 @@
}, },
"/stock/bookings/{bookingId}/undo": { "/stock/bookings/{bookingId}/undo": {
"post": { "post": {
"description": "Undoes a booking", "summary": "Undoes a booking",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1289,7 +1292,7 @@
}, },
"/stock/barcodes/external-lookup": { "/stock/barcodes/external-lookup": {
"get": { "get": {
"description": "Executes an external barcode lookoup via the configured plugin with the given barcode", "summary": "Executes an external barcode lookoup via the configured plugin with the given barcode",
"tags": [ "tags": [
"Stock" "Stock"
], ],
@ -1340,7 +1343,7 @@
}, },
"/recipes/{recipeId}/add-not-fulfilled-products-to-shoppinglist": { "/recipes/{recipeId}/add-not-fulfilled-products-to-shoppinglist": {
"post": { "post": {
"description": "Adds all missing products for the given recipe to the shopping list", "summary": "Adds all missing products for the given recipe to the shopping list",
"tags": [ "tags": [
"Recipes" "Recipes"
], ],
@ -1364,7 +1367,7 @@
}, },
"/recipes/{recipeId}/consume": { "/recipes/{recipeId}/consume": {
"post": { "post": {
"description": "Consumes all products of the given recipe", "summary": "Consumes all products of the given recipe",
"tags": [ "tags": [
"Recipes" "Recipes"
], ],
@ -1388,7 +1391,7 @@
}, },
"/chores": { "/chores": {
"get": { "get": {
"description": "Returns all chores incl. the next estimated execution time per chore", "summary": "Returns all chores incl. the next estimated execution time per chore",
"tags": [ "tags": [
"Chores" "Chores"
], ],
@ -1411,7 +1414,7 @@
}, },
"/chores/{choreId}": { "/chores/{choreId}": {
"get": { "get": {
"description": "Returns details of the given chore", "summary": "Returns details of the given chore",
"tags": [ "tags": [
"Chores" "Chores"
], ],
@ -1452,7 +1455,7 @@
}, },
"/chores/{choreId}/execute": { "/chores/{choreId}/execute": {
"post": { "post": {
"description": "Tracks an execution of the given chore", "summary": "Tracks an execution of the given chore",
"tags": [ "tags": [
"Chores" "Chores"
], ],
@ -1517,9 +1520,9 @@
} }
} }
}, },
"/chores/{executionId}/undo": { "/chores/executions/{executionId}/undo": {
"post": { "post": {
"description": "Undoes a chore execution", "summary": "Undoes a chore execution",
"tags": [ "tags": [
"Chores" "Chores"
], ],
@ -1553,7 +1556,7 @@
}, },
"/batteries": { "/batteries": {
"get": { "get": {
"description": "Returns all batteries incl. the next estimated charge time per battery", "summary": "Returns all batteries incl. the next estimated charge time per battery",
"tags": [ "tags": [
"Batteries" "Batteries"
], ],
@ -1576,7 +1579,7 @@
}, },
"/batteries/{batteryId}": { "/batteries/{batteryId}": {
"get": { "get": {
"description": "Returns details of the given battery", "summary": "Returns details of the given battery",
"tags": [ "tags": [
"Batteries" "Batteries"
], ],
@ -1617,7 +1620,7 @@
}, },
"/batteries/{batteryId}/charge": { "/batteries/{batteryId}/charge": {
"post": { "post": {
"description": "Tracks a charge cycle of the given battery", "summary": "Tracks a charge cycle of the given battery",
"tags": [ "tags": [
"Batteries" "Batteries"
], ],
@ -1678,9 +1681,9 @@
} }
} }
}, },
"/batteries/{chargeCycleId}/undo": { "/batteries/charge-cycles/{chargeCycleId}/undo": {
"post": { "post": {
"description": "Undoes a battery charge cycle", "summary": "Undoes a battery charge cycle",
"tags": [ "tags": [
"Batteries" "Batteries"
], ],
@ -1714,7 +1717,7 @@
}, },
"/tasks": { "/tasks": {
"get": { "get": {
"description": "Returns all tasks which are not done yet", "summary": "Returns all tasks which are not done yet",
"tags": [ "tags": [
"Tasks" "Tasks"
], ],
@ -1737,7 +1740,7 @@
}, },
"/tasks/{taskId}/complete": { "/tasks/{taskId}/complete": {
"post": { "post": {
"description": "Marks the given task as completed", "summary": "Marks the given task as completed",
"tags": [ "tags": [
"Tasks" "Tasks"
], ],

View File

@ -270,18 +270,25 @@ Grocy.Api.Delete = function(apiFunction, jsonData, success, error)
Grocy.Api.UploadFile = function(file, group, fileName, success, error) Grocy.Api.UploadFile = function(file, group, fileName, success, error)
{ {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName)); var url = U('/api/files/' + group + '/' + btoa(fileName));
xhr.onreadystatechange = function() xhr.onreadystatechange = function()
{ {
if (xhr.readyState === XMLHttpRequest.DONE) if (xhr.readyState === XMLHttpRequest.DONE)
{ {
if (xhr.status === 200) if (xhr.status === 200 || xhr.status === 204)
{ {
if (success) if (success)
{
if (xhr.status === 200)
{ {
success(JSON.parse(xhr.responseText)); success(JSON.parse(xhr.responseText));
} }
else
{
success({ });
}
}
} }
else else
{ {
@ -301,18 +308,25 @@ Grocy.Api.UploadFile = function(file, group, fileName, success, error)
Grocy.Api.DeleteFile = function(fileName, group, success, error) Grocy.Api.DeleteFile = function(fileName, group, success, error)
{ {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName)); var url = U('/api/files/' + group + '/' + btoa(fileName));
xhr.onreadystatechange = function() xhr.onreadystatechange = function()
{ {
if (xhr.readyState === XMLHttpRequest.DONE) if (xhr.readyState === XMLHttpRequest.DONE)
{ {
if (xhr.status === 200) if (xhr.status === 200 || xhr.status === 204)
{ {
if (success) if (success)
{
if (xhr.status === 200)
{ {
success(JSON.parse(xhr.responseText)); success(JSON.parse(xhr.responseText));
} }
else
{
success({ });
}
}
} }
else else
{ {

View File

@ -56,7 +56,7 @@ $(document).on('click', '.undo-battery-execution-button', function(e)
var element = $(e.currentTarget); var element = $(e.currentTarget);
var chargeCycleId = $(e.currentTarget).attr('data-charge-cycle-id'); var chargeCycleId = $(e.currentTarget).attr('data-charge-cycle-id');
Grocy.Api.Post('batteries/' + chargeCycleId.toString() + '/undo', { }, Grocy.Api.Post('batteries/charge-cycles/' + chargeCycleId.toString() + '/undo', { },
function(result) function(result)
{ {
element.closest("tr").addClass("text-muted"); element.closest("tr").addClass("text-muted");

View File

@ -92,7 +92,7 @@ $('#tracked_time').find('input').on('keypress', function (e)
function UndoChargeCycle(chargeCycleId) function UndoChargeCycle(chargeCycleId)
{ {
Grocy.Api.Post('batteries/' + chargeCycleId.toString() + '/undo', { }, Grocy.Api.Post('batteries/charge-cycles/' + chargeCycleId.toString() + '/undo', { },
function(result) function(result)
{ {
toastr.success(L("Charge cycle successfully undone")); toastr.success(L("Charge cycle successfully undone"));

View File

@ -56,7 +56,7 @@ $(document).on('click', '.undo-chore-execution-button', function(e)
var element = $(e.currentTarget); var element = $(e.currentTarget);
var executionId = $(e.currentTarget).attr('data-execution-id'); var executionId = $(e.currentTarget).attr('data-execution-id');
Grocy.Api.Post('chores/' + executionId.toString() + '/undo', { }, Grocy.Api.Post('chores/executions/' + executionId.toString() + '/undo', { },
function(result) function(result)
{ {
element.closest("tr").addClass("text-muted"); element.closest("tr").addClass("text-muted");

View File

@ -89,7 +89,7 @@ Grocy.Components.DateTimePicker.GetInputElement().on('keypress', function(e)
function UndoChoreExecution(executionId) function UndoChoreExecution(executionId)
{ {
Grocy.Api.Post('chores/' + executionId.toString() + '/undo', { }, Grocy.Api.Post('chores/executions/' + executionId.toString() + '/undo', { },
function(result) function(result)
{ {
toastr.success(L("Chore execution successfully undone")); toastr.success(L("Chore execution successfully undone"));

View File

@ -41,7 +41,7 @@ Grocy.Components.ProductCard.Refresh = function(productId)
{ {
$("#productcard-no-product-picture").addClass("d-none"); $("#productcard-no-product-picture").addClass("d-none");
$("#productcard-product-picture").removeClass("d-none"); $("#productcard-product-picture").removeClass("d-none");
$("#productcard-product-picture").attr("src", U('/api/file/productpictures?file_name=' + productDetails.product.picture_file_name)); $("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name)));
} }
else else
{ {

View File

@ -43,7 +43,7 @@ function DisplayEquipment(id)
if (equipmentItem.instruction_manual_file_name !== null && !equipmentItem.instruction_manual_file_name.isEmpty()) if (equipmentItem.instruction_manual_file_name !== null && !equipmentItem.instruction_manual_file_name.isEmpty())
{ {
var pdfUrl = U('/api/file/equipmentmanuals?file_name=' + equipmentItem.instruction_manual_file_name); var pdfUrl = U('/api/files/equipmentmanuals/' + btoa(equipmentItem.instruction_manual_file_name));
$("#selected-equipment-instruction-manual").attr("src", pdfUrl); $("#selected-equipment-instruction-manual").attr("src", pdfUrl);
$("#selected-equipment-instruction-manual").removeClass("d-none"); $("#selected-equipment-instruction-manual").removeClass("d-none");
$("#selected-equipment-has-no-instruction-manual-hint").addClass("d-none"); $("#selected-equipment-has-no-instruction-manual-hint").addClass("d-none");

View File

@ -197,7 +197,7 @@ $('#new_amount').on('keyup', function(e)
function UndoStockBooking(bookingId) function UndoStockBooking(bookingId)
{ {
Grocy.Api.Get('booking/' + bookingId.toString() + '/undo', Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', { },
function(result) function(result)
{ {
toastr.success(L("Booking successfully undone")); toastr.success(L("Booking successfully undone"));

View File

@ -202,7 +202,7 @@ if (GetUriParam("flow") === "shoppinglistitemtostock")
function UndoStockBooking(bookingId) function UndoStockBooking(bookingId)
{ {
Grocy.Api.Get('sbooking/' + bookingId.toString() + '/undo', Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', { },
function(result) function(result)
{ {
toastr.success(L("Booking successfully undone")); toastr.success(L("Booking successfully undone"));

View File

@ -7,7 +7,7 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.Api.Post('users/create', jsonData, Grocy.Api.Post('users', jsonData,
function(result) function(result)
{ {
window.location.href = U('/users'); window.location.href = U('/users');
@ -21,7 +21,7 @@
} }
else else
{ {
Grocy.Api.Post('users/edit/' + Grocy.EditObjectId, jsonData, Grocy.Api.Put('users/' + Grocy.EditObjectId, jsonData,
function(result) function(result)
{ {
window.location.href = U('/users'); window.location.href = U('/users');

View File

@ -94,9 +94,9 @@ $app->group('/api', function()
$this->delete('/objects/{entity}/{objectId}', '\Grocy\Controllers\GenericEntityApiController:DeleteObject'); $this->delete('/objects/{entity}/{objectId}', '\Grocy\Controllers\GenericEntityApiController:DeleteObject');
// Files // Files
$this->put('/file/{group}', '\Grocy\Controllers\FilesApiController:UploadFile'); $this->put('/files/{group}/{fileName}', '\Grocy\Controllers\FilesApiController:UploadFile');
$this->get('/file/{group}', '\Grocy\Controllers\FilesApiController:ServeFile'); $this->get('/files/{group}/{fileName}', '\Grocy\Controllers\FilesApiController:ServeFile');
$this->delete('/file/{group}', '\Grocy\Controllers\FilesApiController:DeleteFile'); $this->delete('/files/{group}/{fileName}', '\Grocy\Controllers\FilesApiController:DeleteFile');
// Users // Users
$this->get('/users', '\Grocy\Controllers\UsersApiController:GetUsers'); $this->get('/users', '\Grocy\Controllers\UsersApiController:GetUsers');
@ -131,13 +131,13 @@ $app->group('/api', function()
$this->get('/chores', '\Grocy\Controllers\ChoresApiController:Current'); $this->get('/chores', '\Grocy\Controllers\ChoresApiController:Current');
$this->get('/chores/{choreId}', '\Grocy\Controllers\ChoresApiController:ChoreDetails'); $this->get('/chores/{choreId}', '\Grocy\Controllers\ChoresApiController:ChoreDetails');
$this->post('/chores/{choreId}/execute', '\Grocy\Controllers\ChoresApiController:TrackChoreExecution'); $this->post('/chores/{choreId}/execute', '\Grocy\Controllers\ChoresApiController:TrackChoreExecution');
$this->post('/chores/{executionId}/undo', '\Grocy\Controllers\ChoresApiController:UndoChoreExecution'); $this->post('/chores/executions/{executionId}/undo', '\Grocy\Controllers\ChoresApiController:UndoChoreExecution');
// Batteries // Batteries
$this->get('/batteries', '\Grocy\Controllers\BatteriesApiController:Current'); $this->get('/batteries', '\Grocy\Controllers\BatteriesApiController:Current');
$this->get('/batteries/{batteryId}', '\Grocy\Controllers\BatteriesApiController:BatteryDetails'); $this->get('/batteries/{batteryId}', '\Grocy\Controllers\BatteriesApiController:BatteryDetails');
$this->post('/batteries/{batteryId}/charge', '\Grocy\Controllers\BatteriesApiController:TrackChargeCycle'); $this->post('/batteries/{batteryId}/charge', '\Grocy\Controllers\BatteriesApiController:TrackChargeCycle');
$this->post('/batteries/{chargeCycleId}/undo', '\Grocy\Controllers\BatteriesApiController:UndoChargeCycle'); $this->post('/batteries/charge-cycles/{chargeCycleId}/undo', '\Grocy\Controllers\BatteriesApiController:UndoChargeCycle');
// Tasks // Tasks
$this->get('/tasks', '\Grocy\Controllers\TasksApiController:Current'); $this->get('/tasks', '\Grocy\Controllers\TasksApiController:Current');

View File

@ -64,7 +64,7 @@
<label class="mt-2">{{ $L('Current instruction manual') }}</label> <label class="mt-2">{{ $L('Current instruction manual') }}</label>
<button id="delete-current-instruction-manual-button" class="btn btn-sm btn-danger @if(empty($equipment->instruction_manual_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $L('Delete') }}</button> <button id="delete-current-instruction-manual-button" class="btn btn-sm btn-danger @if(empty($equipment->instruction_manual_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $L('Delete') }}</button>
@if(!empty($equipment->instruction_manual_file_name)) @if(!empty($equipment->instruction_manual_file_name))
<embed id="current-equipment-instruction-manual" class="embed-responsive embed-responsive-4by3" src="{{ $U('/api/file/equipmentmanuals?file_name=' . $equipment->instruction_manual_file_name) }}" type="application/pdf"> <embed id="current-equipment-instruction-manual" class="embed-responsive embed-responsive-4by3" src="{{ $U('/api/files/equipmentmanuals/' . base64_encode($equipment->instruction_manual_file_name)) }}" type="application/pdf">
<p id="delete-current-instruction-manual-on-save-hint" class="form-text text-muted font-italic d-none">{{ $L('The current instruction manual will be deleted when you save the equipment') }}</p> <p id="delete-current-instruction-manual-on-save-hint" class="form-text text-muted font-italic d-none">{{ $L('The current instruction manual will be deleted when you save the equipment') }}</p>
@else @else
<p id="no-current-instruction-manual-hint" class="form-text text-muted font-italic">{{ $L('No instruction manual available') }}</p> <p id="no-current-instruction-manual-hint" class="form-text text-muted font-italic">{{ $L('No instruction manual available') }}</p>

View File

@ -143,7 +143,7 @@
<label class="mt-2">{{ $L('Current picture') }}</label> <label class="mt-2">{{ $L('Current picture') }}</label>
<button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $L('Delete') }}</button> <button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $L('Delete') }}</button>
@if(!empty($product->picture_file_name)) @if(!empty($product->picture_file_name))
<p><img id="current-product-picture" src="{{ $U('/api/file/productpictures?file_name=' . $product->picture_file_name) }}" class="img-fluid img-thumbnail mt-2"></p> <p><img id="current-product-picture" src="{{ $U('/api/files/productpictures/' . base64_encode($product->picture_file_name)) }}" class="img-fluid img-thumbnail mt-2"></p>
<p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $L('The current picture will be deleted when you save the product') }}</p> <p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $L('The current picture will be deleted when you save the product') }}</p>
@else @else
<p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $L('No picture available') }}</p> <p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $L('No picture available') }}</p>