Use absolute URLs everywhere, this should fix #3

This commit is contained in:
Bernd Bestel 2018-04-18 19:03:39 +02:00
parent 3d1c6fc5f0
commit 607a90cccc
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
36 changed files with 207 additions and 159 deletions

View File

@ -4,11 +4,11 @@ use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use \Grocy\Middleware\SessionAuthMiddleware;
use \Grocy\Helpers\UrlManager;
use \Grocy\Services\ApplicationService;
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/data/config.php';
require_once __DIR__ . '/extensions.php';
// Setup base application
if (PHP_SAPI !== 'cli')
@ -21,6 +21,10 @@ if (PHP_SAPI !== 'cli')
'view' => function($container)
{
return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache');
},
'UrlManager' => function($container)
{
return new UrlManager(BASE_URL);
}
]);

View File

@ -9,7 +9,11 @@
"psr-4": {
"Grocy\\Services\\": "services/",
"Grocy\\Controllers\\": "controllers/",
"Grocy\\Middleware\\": "middleware/"
}
"Grocy\\Middleware\\": "middleware/",
"Grocy\\Helpers\\": "helpers/"
},
"files": [
"helpers/extensions.php"
]
}
}

View File

@ -1,7 +1,17 @@
<?php
define('MODE', 'production'); # Either "production" or "dev"
define('CULTURE', 'en');
# Login credentials
define('HTTP_USER', 'admin');
define('HTTP_PASSWORD', 'admin');
# Either "production" or "dev"
define('MODE', 'production');
# Either "en" or "de" or the filename (without extension) of
# one of the other available localization files in the "/localization" directory
define('CULTURE', 'en');
# The base url of your installation,
# should be just "/" when running directly under the root of a (sub)domain
# or for example "https:/example.com/grocy" when using a subdirectory
define('BASE_URL', '/');

View File

@ -9,8 +9,6 @@ use \Grocy\Services\LocalizationService;
class BaseController
{
public function __construct(\Slim\Container $container) {
$this->AppContainer = $container;
$databaseService = new DatabaseService();
$this->Database = $databaseService->GetDbConnection();
@ -23,6 +21,12 @@ class BaseController
{
return $localizationService->Localize($text, ...$placeholderValues);
});
$container->view->set('U', function($relativePath) use($container)
{
return $container->UrlManager->ConstructUrl($relativePath);
});
$this->AppContainer = $container;
}
protected $AppContainer;

View File

@ -27,16 +27,16 @@ class LoginController extends BaseController
$sessionKey = $this->SessionService->CreateSession();
setcookie('grocy_session', $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
return $response->withRedirect('/');
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
}
else
{
return $response->withRedirect('/login?invalid=true');
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login?invalid=true'));
}
}
else
{
return $response->withRedirect('/login?invalid=true');
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login?invalid=true'));
}
}
@ -48,7 +48,7 @@ class LoginController extends BaseController
public function Logout(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
$this->SessionService->RemoveSession($_COOKIE['grocy_session']);
return $response->withRedirect('/');
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
}
public function Root(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
@ -64,6 +64,6 @@ class LoginController extends BaseController
$demoDataGeneratorService->PopulateDemoData();
}
return $response->withRedirect('/stockoverview');
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/stockoverview'));
}
}

17
helpers/UrlManager.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace Grocy\Helpers;
class UrlManager
{
public function __construct(string $basePath) {
$this->BasePath = $basePath;
}
protected $BasePath;
public function ConstructUrl($relativePath)
{
return rtrim($this->BasePath, '/') . $relativePath;
}
}

View File

@ -5,8 +5,8 @@ namespace Grocy\Middleware;
class BaseMiddleware
{
public function __construct(\Slim\Container $container) {
$this->container = $container;
$this->AppContainer = $container;
}
protected $container;
protected $AppContainer;
}

View File

@ -20,7 +20,7 @@ class SessionAuthMiddleware extends BaseMiddleware
$sessionService = new SessionService();
if ((!isset($_COOKIE['grocy_session']) || !$sessionService->IsValidSession($_COOKIE['grocy_session'])) && $routeName !== 'login')
{
$response = $response->withRedirect('/login');
$response = $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login'));
}
else
{

View File

@ -14,6 +14,11 @@
return localizedText;
}
U = function(relativePath)
{
return Grocy.BaseUrl.replace(/\/$/, '') + relativePath;
}
if (!Grocy.ActiveNav.isEmpty())
{
var menuItem = $('.nav').find("[data-nav-for-page='" + Grocy.ActiveNav + "']");
@ -23,9 +28,11 @@ if (!Grocy.ActiveNav.isEmpty())
$.timeago.settings.allowFuture = true;
$('time.timeago').timeago();
Grocy.FetchJson = function(url, success, error)
Grocy.Api = { };
Grocy.Api.Get = function(apiFunction, success, error)
{
var xhr = new XMLHttpRequest();
var url = U('/api/' + apiFunction);
xhr.onreadystatechange = function()
{
@ -52,9 +59,10 @@ Grocy.FetchJson = function(url, success, error)
xhr.send();
};
Grocy.PostJson = function(url, jsonData, success, error)
Grocy.Api.Post = function(apiFunction, jsonData, success, error)
{
var xhr = new XMLHttpRequest();
var url = U('/api/' + apiFunction);
xhr.onreadystatechange = function()
{

View File

@ -1,7 +1,7 @@
$(document).on('click', '.battery-delete-button', function(e)
{
bootbox.confirm({
message: 'Delete battery <strong>' + $(e.target).attr('data-battery-name') + '</strong>?',
message: 'Delete battery <strong>' + $(e.currentTarget).attr('data-battery-name') + '</strong>?',
buttons: {
confirm: {
label: 'Yes',
@ -16,10 +16,10 @@
{
if (result === true)
{
Grocy.FetchJson('/api/delete-object/batteries/' + $(e.target).attr('data-battery-id'),
Grocy.Api.Get('delete-object/batteries/' + $(e.currentTarget).attr('data-battery-id'),
function(result)
{
window.location.href = '/batteries';
window.location.href = U('/batteries');
},
function(xhr)
{

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/batteries', $('#battery-form').serializeJSON(),
Grocy.Api.Post('add-object/batteries', $('#battery-form').serializeJSON(),
function(result)
{
window.location.href = '/batteries';
window.location.href = U('/batteries');
},
function(xhr)
{
@ -17,10 +17,10 @@
}
else
{
Grocy.PostJson('/api/edit-object/batteries/' + Grocy.EditObjectId, $('#battery-form').serializeJSON(),
Grocy.Api.Post('edit-object/batteries/' + Grocy.EditObjectId, $('#battery-form').serializeJSON(),
function(result)
{
window.location.href = '/batteries';
window.location.href = U('/batteries');
},
function(xhr)
{

View File

@ -4,10 +4,10 @@
var jsonForm = $('#batterytracking-form').serializeJSON();
Grocy.FetchJson('/api/batteries/get-battery-details/' + jsonForm.battery_id,
Grocy.Api.Get('batteries/get-battery-details/' + jsonForm.battery_id,
function (batteryDetails)
{
Grocy.FetchJson('/api/batteries/track-charge-cycle/' + jsonForm.battery_id + '?tracked_time=' + $('#tracked_time').val(),
Grocy.Api.Get('batteries/track-charge-cycle/' + jsonForm.battery_id + '?tracked_time=' + $('#tracked_time').val(),
function(result)
{
toastr.success('Tracked charge cylce of battery ' + batteryDetails.battery.name + ' on ' + $('#tracked_time').val());

View File

@ -2,7 +2,7 @@ Grocy.Components.BatteryCard = { };
Grocy.Components.BatteryCard.Refresh = function(batteryId)
{
Grocy.FetchJson('/api/batteries/get-battery-details/' + batteryId,
Grocy.Api.Get('batteries/get-battery-details/' + batteryId,
function(batteryDetails)
{
$('#batterycard-battery-name').text(batteryDetails.battery.name);

View File

@ -2,7 +2,7 @@ Grocy.Components.HabitCard = { };
Grocy.Components.HabitCard.Refresh = function (habitId)
{
Grocy.FetchJson('/api/habits/get-habit-details/' + habitId,
Grocy.Api.Get('habits/get-habit-details/' + habitId,
function(habitDetails)
{
$('#habitcard-habit-name').text(habitDetails.habit.name);

View File

@ -2,7 +2,7 @@ Grocy.Components.ProductCard = { };
Grocy.Components.ProductCard.Refresh = function(productId)
{
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails)
{
$('#productcard-product-name').text(productDetails.product.name);

View File

@ -10,10 +10,10 @@
spoiled = 1;
}
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails)
{
Grocy.FetchJson('/api/stock/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled,
Grocy.Api.Get('stock/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled,
function(result)
{
toastr.success('Removed ' + jsonForm.amount + ' ' + productDetails.quantity_unit_stock.name + ' of ' + productDetails.product.name + ' from stock');
@ -46,7 +46,7 @@ $('#product_id').on('change', function(e)
{
Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function (productDetails)
{
$('#amount').attr('max', productDetails.stock_amount);

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/habits', $('#habit-form').serializeJSON(),
Grocy.Api.Post('add-object/habits', $('#habit-form').serializeJSON(),
function(result)
{
window.location.href = '/habits';
window.location.href = U('/habits');
},
function(xhr)
{
@ -17,10 +17,10 @@
}
else
{
Grocy.PostJson('/api/edit-object/habits/' + Grocy.EditObjectId, $('#habit-form').serializeJSON(),
Grocy.Api.Post('edit-object/habits/' + Grocy.EditObjectId, $('#habit-form').serializeJSON(),
function(result)
{
window.location.href = '/habits';
window.location.href = U('/habits');
},
function(xhr)
{

View File

@ -1,7 +1,7 @@
$(document).on('click', '.habit-delete-button', function(e)
{
bootbox.confirm({
message: 'Delete habit <strong>' + $(e.target).attr('data-habit-name') + '</strong>?',
message: 'Delete habit <strong>' + $(e.currentTarget).attr('data-habit-name') + '</strong>?',
buttons: {
confirm: {
label: 'Yes',
@ -16,10 +16,10 @@
{
if (result === true)
{
Grocy.FetchJson('/api/delete-object/habits/' + $(e.target).attr('data-habit-id'),
Grocy.Api.Get('delete-object/habits/' + $(e.currentTarget).attr('data-habit-id'),
function(result)
{
window.location.href = '/habits';
window.location.href = U('/habits');
},
function(xhr)
{

View File

@ -4,10 +4,10 @@
var jsonForm = $('#habittracking-form').serializeJSON();
Grocy.FetchJson('/api/habits/get-habit-details/' + jsonForm.habit_id,
Grocy.Api.Get('habits/get-habit-details/' + jsonForm.habit_id,
function (habitDetails)
{
Grocy.FetchJson('/api/habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + $('#tracked_time').val(),
Grocy.Api.Get('habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + $('#tracked_time').val(),
function(result)
{
toastr.success('Tracked execution of habit ' + habitDetails.habit.name + ' on ' + $('#tracked_time').val());

View File

@ -4,10 +4,10 @@
var jsonForm = $('#inventory-form').serializeJSON();
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails)
{
Grocy.FetchJson('/api/stock/inventory-product/' + jsonForm.product_id + '/' + jsonForm.new_amount + '?bestbeforedate=' + $('#best_before_date').val(),
Grocy.Api.Get('stock/inventory-product/' + jsonForm.product_id + '/' + jsonForm.new_amount + '?bestbeforedate=' + $('#best_before_date').val(),
function(result)
{
var addBarcode = GetUriParam('addbarcodetoselection');
@ -23,7 +23,7 @@
productDetails.product.barcode += ',' + addBarcode;
}
Grocy.PostJson('/api/edit-object/products/' + productDetails.product.id, productDetails.product,
Grocy.Api.Get('edit-object/products/' + productDetails.product.id, productDetails.product,
function (result) { },
function(xhr)
{
@ -36,7 +36,7 @@
if (addBarcode !== undefined)
{
window.location.href = '/inventory';
window.location.href = U('/inventory');
}
else
{
@ -71,7 +71,7 @@ $('#product_id').on('change', function(e)
{
Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails)
{
$('#new_amount').attr('not-equal', productDetails.stock_amount);
@ -124,7 +124,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-success add-new-product-dialog-button',
callback: function()
{
window.location.href = '/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname);
window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
}
},
addbarcode: {
@ -132,7 +132,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-info add-new-barcode-dialog-button',
callback: function()
{
window.location.href = '/inventory?addbarcodetoselection=' + encodeURIComponent(input);
window.location.href = U('/inventory?addbarcodetoselection=' + encodeURIComponent(input));
}
},
addnewproductwithbarcode: {
@ -140,7 +140,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-warning add-new-product-with-barcode-dialog-button',
callback: function()
{
window.location.href = '/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname);
window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
}
}
}
@ -283,7 +283,7 @@ $('#new_amount').on('change', function(e)
if (productId)
{
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails)
{
var productStockAmount = productDetails.stock_amount || '0';

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/locations', $('#location-form').serializeJSON(),
Grocy.Api.Post('add-object/locations', $('#location-form').serializeJSON(),
function(result)
{
window.location.href = '/locations';
window.location.href = U('/locations');
},
function(xhr)
{
@ -17,10 +17,10 @@
}
else
{
Grocy.PostJson('/api/edit-object/locations/' + Grocy.EditObjectId, $('#location-form').serializeJSON(),
Grocy.Api.Post('edit-object/locations/' + Grocy.EditObjectId, $('#location-form').serializeJSON(),
function(result)
{
window.location.href = '/locations';
window.location.href = U('/locations');
},
function(xhr)
{

View File

@ -1,7 +1,7 @@
$(document).on('click', '.location-delete-button', function(e)
{
bootbox.confirm({
message: 'Delete location <strong>' + $(e.target).attr('data-location-name') + '</strong>?',
message: 'Delete location <strong>' + $(e.currentTarget).attr('data-location-name') + '</strong>?',
buttons: {
confirm: {
label: 'Yes',
@ -16,10 +16,10 @@
{
if (result === true)
{
Grocy.FetchJson('/api/delete-object/locations/' + $(e.target).attr('data-location-id'),
Grocy.Api.Get('delete-object/locations/' + $(e.currentTarget).attr('data-location-id'),
function(result)
{
window.location.href = '/locations';
window.location.href = U('/locations');
},
function(xhr)
{

View File

@ -2,7 +2,7 @@
{
e.preventDefault();
var redirectDestination = '/products';
var redirectDestination = U('/products');
var returnTo = GetUriParam('returnto');
if (returnTo !== undefined)
{
@ -11,7 +11,7 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/products', $('#product-form').serializeJSON(),
Grocy.Api.Post('add-object/products', $('#product-form').serializeJSON(),
function(result)
{
window.location.href = redirectDestination;
@ -24,7 +24,7 @@
}
else
{
Grocy.PostJson('/api/edit-object/products/' + Grocy.EditObjectId, $('#product-form').serializeJSON(),
Grocy.Api.Post('edit-object/products/' + Grocy.EditObjectId, $('#product-form').serializeJSON(),
function(result)
{
window.location.href = redirectDestination;
@ -44,7 +44,7 @@ $('#barcode-taginput').tagsManager({
if (Grocy.EditMode === 'edit')
{
Grocy.FetchJson('/api/get-object/products/' + Grocy.EditObjectId,
Grocy.Api.Get('get-object/products/' + Grocy.EditObjectId,
function (product)
{
if (product.barcode !== null && product.barcode.length > 0)

View File

@ -1,7 +1,7 @@
$(document).on('click', '.product-delete-button', function(e)
{
bootbox.confirm({
message: 'Delete product <strong>' + $(e.target).attr('data-product-name') + '</strong>?',
message: 'Delete product <strong>' + $(e.currentTarget).attr('data-product-name') + '</strong>?',
buttons: {
confirm: {
label: 'Yes',
@ -16,10 +16,10 @@
{
if (result === true)
{
Grocy.FetchJson('/api/delete-object/products/' + $(e.target).attr('data-product-id'),
Grocy.Api.Get('delete-object/products/' + $(e.currentTarget).attr('data-product-id'),
function(result)
{
window.location.href = '/products';
window.location.href = U('/products');
},
function(xhr)
{

View File

@ -4,12 +4,12 @@
var jsonForm = $('#purchase-form').serializeJSON();
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails)
{
var amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock;
Grocy.FetchJson('/api/stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + $('#best_before_date').val(),
Grocy.Api.Get('stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + $('#best_before_date').val(),
function(result)
{
var addBarcode = GetUriParam('addbarcodetoselection');
@ -25,7 +25,7 @@
productDetails.product.barcode += ',' + addBarcode;
}
Grocy.PostJson('/api/edit-object/products/' + productDetails.product.id, productDetails.product,
Grocy.Api.Post('edit-object/products/' + productDetails.product.id, productDetails.product,
function (result) { },
function(xhr)
{
@ -38,7 +38,7 @@
if (addBarcode !== undefined)
{
window.location.href = '/purchase';
window.location.href = U('/purchase');
}
else
{
@ -72,7 +72,7 @@ $('#product_id').on('change', function(e)
{
Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails)
{
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
@ -133,7 +133,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-success add-new-product-dialog-button',
callback: function()
{
window.location.href = '/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname);
window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
}
},
addbarcode: {
@ -141,7 +141,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-info add-new-barcode-dialog-button',
callback: function()
{
window.location.href = '/purchase?addbarcodetoselection=' + encodeURIComponent(input);
window.location.href = U('/purchase?addbarcodetoselection=' + encodeURIComponent(input));
}
},
addnewproductwithbarcode: {
@ -149,7 +149,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-warning add-new-product-with-barcode-dialog-button',
callback: function()
{
window.location.href = '/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname);
window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
}
}
}

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/quantity_units', $('#quantityunit-form').serializeJSON(),
Grocy.Api.Post('add-object/quantity_units', $('#quantityunit-form').serializeJSON(),
function(result)
{
window.location.href = '/quantityunits';
window.location.href = U('/quantityunits');
},
function(xhr)
{
@ -17,10 +17,10 @@
}
else
{
Grocy.PostJson('/api/edit-object/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(),
Grocy.Api.Post('edit-object/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(),
function(result)
{
window.location.href = '/quantityunits';
window.location.href = U('/quantityunits');
},
function(xhr)
{

View File

@ -1,7 +1,7 @@
$(document).on('click', '.quantityunit-delete-button', function(e)
{
bootbox.confirm({
message: 'Delete quantity unit <strong>' + $(e.target).attr('data-quantityunit-name') + '</strong>?',
message: 'Delete quantity unit <strong>' + $(e.currentTarget).attr('data-quantityunit-name') + '</strong>?',
buttons: {
confirm: {
label: 'Yes',
@ -16,10 +16,10 @@
{
if (result === true)
{
Grocy.FetchJson('/api/delete-object/quantity_units/' + $(e.target).attr('data-quantityunit-id'),
Grocy.Api.Get('delete-object/quantity_units/' + $(e.currentTarget).attr('data-quantityunit-id'),
function(result)
{
window.location.href = '/quantityunits';
window.location.href = U('/quantityunits');
},
function(xhr)
{

View File

@ -1,9 +1,9 @@
$(document).on('click', '.shoppinglist-delete-button', function(e)
{
Grocy.FetchJson('/api/delete-object/shopping_list/' + $(e.target).attr('data-shoppinglist-id'),
Grocy.Api.Get('delete-object/shopping_list/' + $(e.currentTarget).attr('data-shoppinglist-id'),
function(result)
{
window.location.href = '/shoppinglist';
window.location.href = U('/shoppinglist');
},
function(xhr)
{
@ -14,10 +14,10 @@
$(document).on('click', '#add-products-below-min-stock-amount', function(e)
{
Grocy.FetchJson('/api/stock/add-missing-products-to-shoppinglist',
Grocy.Api.Get('stock/add-missing-products-to-shoppinglist',
function(result)
{
window.location.href = '/shoppinglist';
window.location.href = U('/shoppinglist');
},
function(xhr)
{

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create')
{
Grocy.PostJson('/api/add-object/shopping_list', $('#shoppinglist-form').serializeJSON(),
Grocy.Api.Post('add-object/shopping_list', $('#shoppinglist-form').serializeJSON(),
function(result)
{
window.location.href = '/shoppinglist';
window.location.href = U('/shoppinglist');
},
function(xhr)
{
@ -17,10 +17,10 @@
}
else
{
Grocy.PostJson('/api/edit-object/shopping_list/' + Grocy.EditObjectId, $('#shoppinglist-form').serializeJSON(),
Grocy.Api.Post('edit-object/shopping_list/' + Grocy.EditObjectId, $('#shoppinglist-form').serializeJSON(),
function(result)
{
window.location.href = '/shoppinglist';
window.location.href = U('/shoppinglist');
},
function(xhr)
{
@ -38,7 +38,7 @@ $('#product_id').on('change', function(e)
{
Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
Grocy.Api.Get('stock/get-product-details/' + productId,
function (productDetails)
{
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
@ -49,7 +49,7 @@ $('#product_id').on('change', function(e)
}
else
{
Grocy.FetchJson('/api/get-objects/shopping_list',
Grocy.Api.Get('get-objects/shopping_list',
function (currentShoppingListItems)
{
if (currentShoppingListItems.filter(function (e) { return e.product_id === productId; }).length > 0)

View File

@ -1,5 +1,5 @@
@push('componentScripts')
<script src="/viewjs/components/batterycard.js"></script>
<script src="{{ $U('/viewjs/components/batterycard.js') }}?v={{ $version }}"></script>
@endpush
<div class="main well">

View File

@ -1,5 +1,5 @@
@push('componentScripts')
<script src="/viewjs/components/datepicker.js"></script>
<script src="{{ $U('/viewjs/components/datepicker.js') }}?v={{ $version }}"></script>
@endpush
<div class="form-group">

View File

@ -1,5 +1,5 @@
@push('componentScripts')
<script src="/viewjs/components/datetimepicker.js"></script>
<script src="{{ $U('/viewjs/components/datetimepicker.js') }}?v={{ $version }}"></script>
@endpush
<div class="form-group">

View File

@ -1,5 +1,5 @@
@push('componentScripts')
<script src="/viewjs/components/habitcard.js"></script>
<script src="{{ $U('/viewjs/components/habitcard.js') }}?v={{ $version }}"></script>
@endpush
<div class="main well">

View File

@ -1,5 +1,5 @@
@push('componentScripts')
<script src="/viewjs/components/productcard.js"></script>
<script src="{{ $U('/viewjs/components/productcard.js') }}?v={{ $version }}"></script>
@endpush
<div class="main well">

View File

@ -13,21 +13,22 @@
<title>@yield('title') | grocy</title>
<link href="/bower_components/bootstrap/dist/css/bootstrap.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/font-awesome/css/font-awesome.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/bootstrap-combobox/css/bootstrap-combobox.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/datatables.net-responsive-bs/css/responsive.bootstrap.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/toastr/toastr.min.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/tagmanager/tagmanager.css?v={{ $version }}" rel="stylesheet">
<link href="/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css?v={{ $version }}" rel="stylesheet">
<link href="/components_unmanaged/noto-sans-v6-latin/noto-sans-v6-latin.css?v={{ $version }}" rel="stylesheet">
<link href="/css/grocy.css?v={{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/bootstrap/dist/css/bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/font-awesome/css/font-awesome.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/bootstrap-combobox/css/bootstrap-combobox.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/datatables.net-responsive-bs/css/responsive.bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/toastr/toastr.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/tagmanager/tagmanager.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/components_unmanaged/noto-sans-v6-latin/noto-sans-v6-latin.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="{{ $U('/css/grocy.css?v=') }}{{ $version }}" rel="stylesheet">
<script>
var Grocy = { };
Grocy.Components = { };
Grocy.BaseUrl = '{{ $U('/') }}';
Grocy.LocalizationStrings = {!! json_encode($localizationStrings) !!};
Grocy.ActiveNav = '@yield('activeNav', '')';
</script>
@ -42,13 +43,13 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">grocy</a>
<a class="navbar-brand" href="{{ $U('/') }}">grocy</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
</li>
</ul>
</div>
@ -57,60 +58,60 @@
<ul class="nav navbar-nav navbar-right">
<li data-nav-for-page="stockoverview">
<a class="discrete-link" href="/stockoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
<a class="discrete-link" href="{{ $U('/stockoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
</li>
<li data-nav-for-page="habitsoverview">
<a class="discrete-link" href="/habitsoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
<a class="discrete-link" href="{{ $U('/habitsoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
</li>
<li data-nav-for-page="batteriesoverview">
<a class="discrete-link" href="/batteriesoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
<a class="discrete-link" href="{{ $U('/batteriesoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li>
<li data-nav-for-page="purchase">
<a class="discrete-link" href="/purchase"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
<a class="discrete-link" href="{{ $U('/purchase') }}"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
</li>
<li data-nav-for-page="consume">
<a class="discrete-link" href="/consume"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
<a class="discrete-link" href="{{ $U('/consume') }}"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
</li>
<li data-nav-for-page="shoppinglist">
<a class="discrete-link" href="/shoppinglist"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
<a class="discrete-link" href="{{ $U('/shoppinglist') }}"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
</li>
<li data-nav-for-page="inventory">
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
<a class="discrete-link" href="{{ $U('/inventory') }}"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
</li>
<li data-nav-for-page="habittracking">
<a class="discrete-link" href="/habittracking"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
<a class="discrete-link" href="{{ $U('/habittracking') }}"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
</li>
<li data-nav-for-page="batterytracking">
<a class="discrete-link" href="/batterytracking"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
<a class="discrete-link" href="{{ $U('/batterytracking') }}"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li>
<li data-nav-for-page="products">
<a class="discrete-link" href="/products"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
<a class="discrete-link" href="{{ $U('/products') }}"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
</li>
<li data-nav-for-page="locations">
<a class="discrete-link" href="/locations"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
<a class="discrete-link" href="{{ $U('/locations') }}"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
</li>
<li data-nav-for-page="quantityunits">
<a class="discrete-link" href="/quantityunits"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
<a class="discrete-link" href="{{ $U('/quantityunits') }}"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
</li>
<li data-nav-for-page="habits">
<a class="discrete-link" href="/habits"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
<a class="discrete-link" href="{{ $U('/habits') }}"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
</li>
<li data-nav-for-page="batteries">
<a class="discrete-link" href="/batteries"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
<a class="discrete-link" href="{{ $U('/batteries') }}"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
</li>
</ul>
@ -140,54 +141,54 @@
<ul class="nav nav-sidebar">
<li data-nav-for-page="stockoverview">
<a class="discrete-link" href="/stockoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
<a class="discrete-link" href="{{ $U('/stockoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
</li>
<li data-nav-for-page="habitsoverview">
<a class="discrete-link" href="/habitsoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
<a class="discrete-link" href="{{ $U('/habitsoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
</li>
<li data-nav-for-page="batteriesoverview">
<a class="discrete-link" href="/batteriesoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
<a class="discrete-link" href="{{ $U('/batteriesoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
</li>
</ul>
<ul class="nav nav-sidebar">
<li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li>
<li data-nav-for-page="purchase">
<a class="discrete-link" href="/purchase"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
<a class="discrete-link" href="{{ $U('/purchase') }}"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
</li>
<li data-nav-for-page="consume">
<a class="discrete-link" href="/consume"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
<a class="discrete-link" href="{{ $U('/consume') }}"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
</li>
<li data-nav-for-page="shoppinglist">
<a class="discrete-link" href="/shoppinglist"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
<a class="discrete-link" href="{{ $U('/shoppinglist') }}"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
</li>
<li data-nav-for-page="inventory">
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
<a class="discrete-link" href="{{ $U('/inventory') }}"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
</li>
<li data-nav-for-page="habittracking">
<a class="discrete-link" href="/habittracking"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
<a class="discrete-link" href="{{ $U('/habittracking') }}"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
</li>
<li data-nav-for-page="batterytracking">
<a class="discrete-link" href="/batterytracking"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
<a class="discrete-link" href="{{ $U('/batterytracking') }}"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
</li>
</ul>
<ul class="nav nav-sidebar">
<li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li>
<li data-nav-for-page="products">
<a class="discrete-link" href="/products"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
<a class="discrete-link" href="{{ $U('/products') }}"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
</li>
<li data-nav-for-page="locations">
<a class="discrete-link" href="/locations"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
<a class="discrete-link" href="{{ $U('/locations') }}"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
</li>
<li data-nav-for-page="quantityunits">
<a class="discrete-link" href="/quantityunits"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
<a class="discrete-link" href="{{ $U('/quantityunits') }}"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
</li>
<li data-nav-for-page="habits">
<a class="discrete-link" href="/habits"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
<a class="discrete-link" href="{{ $U('/habits') }}"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
</li>
<li data-nav-for-page="batteries">
<a class="discrete-link" href="/batteries"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
<a class="discrete-link" href="{{ $U('/batteries') }}"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
</li>
</ul>
@ -213,29 +214,29 @@
</div>
</div>
<script src="/bower_components/jquery/dist/jquery.min.js?v={{ $version }}"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js?v={{ $version }}"></script>
<script src="/bower_components/bootbox/bootbox.js?v={{ $version }}"></script>
<script src="/bower_components/jquery.serializeJSON/jquery.serializejson.min.js?v={{ $version }}"></script>
<script src="/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js?v={{ $version }}"></script>
@if(!empty($L('bootstrap_datepicker_locale')))<script src="/bower_components/bootstrap-datepicker/dist/locales/bootstrap-datepicker.{{ $L('bootstrap_datepicker_locale') }}.min.js?v={{ $version }}"></script>@endif
<script src="/bower_components/moment/min/moment.min.js?v={{ $version }}"></script>
@if(!empty($L('moment_locale')))<script src="/bower_components/moment/locale/{{ $L('moment_locale') }}.js?v={{ $version }}"></script>@endif
<script src="/bower_components/bootstrap-validator/dist/validator.min.js?v={{ $version }}"></script>
<script src="/bower_components/bootstrap-combobox/js/bootstrap-combobox.js?v={{ $version }}"></script>
<script src="/bower_components/datatables.net/js/jquery.dataTables.min.js?v={{ $version }}"></script>
<script src="/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js?v={{ $version }}"></script>
<script src="/bower_components/datatables.net-responsive/js/dataTables.responsive.min.js?v={{ $version }}"></script>
<script src="/bower_components/datatables.net-responsive-bs/js/responsive.bootstrap.min.js?v={{ $version }}"></script>
<script src="/bower_components/jquery-timeago/jquery.timeago.js?v={{ $version }}"></script>
<script src="/bower_components/jquery-timeago/locales/jquery.timeago.{{ $L('timeago_locale') }}.js?v={{ $version }}"></script>
<script src="/bower_components/toastr/toastr.min.js?v={{ $version }}"></script>
<script src="/bower_components/tagmanager/tagmanager.js?v={{ $version }}"></script>
<script src="/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js?v={{ $version }}"></script>
<script src="{{ $U('/bower_components/jquery/dist/jquery.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/bootstrap/dist/js/bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/bootbox/bootbox.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/jquery.serializeJSON/jquery.serializejson.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js?v=') }}{{ $version }}"></script>
@if(!empty($L('bootstrap_datepicker_locale')))<script src="{{ $U('/bower_components') }}/bootstrap-datepicker/dist/locales/bootstrap-datepicker.{{ $L('bootstrap_datepicker_locale') }}.min.js?v={{ $version }}"></script>@endif
<script src="{{ $U('/bower_components/moment/min/moment.min.js?v=') }}{{ $version }}"></script>
@if(!empty($L('moment_locale')))<script src="{{ $U('/bower_components') }}/moment/locale/{{ $L('moment_locale') }}.js?v={{ $version }}"></script>@endif
<script src="{{ $U('/bower_components/bootstrap-validator/dist/validator.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/bootstrap-combobox/js/bootstrap-combobox.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/datatables.net/js/jquery.dataTables.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/datatables.net-responsive/js/dataTables.responsive.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/datatables.net-responsive-bs/js/responsive.bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/jquery-timeago/jquery.timeago.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components') }}/jquery-timeago/locales/jquery.timeago.{{ $L('timeago_locale') }}.js?v={{ $version }}"></script>
<script src="{{ $U('/bower_components/toastr/toastr.min.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/tagmanager/tagmanager.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js?v=') }}{{ $version }}"></script>
<script src="/js/extensions.js?v={{ $version }}"></script>
<script src="/js/grocy.js?v={{ $version }}"></script>
<script src="/viewjs/@yield('viewJsName').js"></script>
<script src="{{ $U('/js/extensions.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/js/grocy.js?v=') }}{{ $version }}"></script>
<script src="{{ $U('/viewjs') }}/@yield('viewJsName').js?v={{ $version }}"></script>
@stack('componentScripts')
@if(file_exists(__DIR__ . '/../../data/add_before_end_body.html'))