diff --git a/lib/Controller/Campaign.php b/lib/Controller/Campaign.php
index 46bd143e89..81219b85ce 100644
--- a/lib/Controller/Campaign.php
+++ b/lib/Controller/Campaign.php
@@ -527,7 +527,7 @@ public function addForm(Request $request, Response $response)
* @SWG\Parameter(
* name="targetType",
* in="formData",
- * description="For ad campaigns, how do we measure the target? plays|budget",
+ * description="For ad campaigns, how do we measure the target? plays|budget|imp",
* type="string",
* required=false
* ),
@@ -774,7 +774,7 @@ public function editForm(Request $request, Response $response, $id)
* @SWG\Parameter(
* name="targetType",
* in="formData",
- * description="For ad campaigns, how do we measure the target? plays|budget",
+ * description="For ad campaigns, how do we measure the target? plays|budget|imp",
* type="string",
* required=false
* ),
diff --git a/lib/Controller/Layout.php b/lib/Controller/Layout.php
index b55e07c54d..462ef51372 100644
--- a/lib/Controller/Layout.php
+++ b/lib/Controller/Layout.php
@@ -1941,7 +1941,7 @@ function editBackgroundForm(Request $request, Response $response, $id)
if (!$this->getUser()->checkEditable($layout)) {
throw new AccessDeniedException();
}
-
+
// Edits always happen on Drafts, get the draft Layout using the Parent Layout ID
if ($layout->schemaVersion < 2) {
$resolution = $this->resolutionFactory->getByDesignerDimensions($layout->width, $layout->height);
@@ -3055,9 +3055,16 @@ public function addThumbnail(Request $request, Response $response, $id): Respons
$image = Img::canvas($layout->width, $layout->height, $layout->backgroundColor);
}
+ $countRegions = count($layout->regions);
+
// Draw some regions on it.
foreach ($layout->regions as $region) {
try {
+ // We don't do this for the canvas region.
+ if ($countRegions > 1 && $region->type === 'canvas') {
+ continue;
+ }
+
// Get widgets in this region
$playlist = $region->getPlaylist()->setModuleFactory($this->moduleFactory);
$widgets = $playlist->expandWidgets();
diff --git a/lib/Entity/Campaign.php b/lib/Entity/Campaign.php
index b8c24e52da..34bfeda296 100644
--- a/lib/Entity/Campaign.php
+++ b/lib/Entity/Campaign.php
@@ -126,7 +126,7 @@ class Campaign implements \JsonSerializable
public $listPlayOrder;
/**
- * @SWG\Property(description="For an ad campaign, what's the target type, plays|budget")
+ * @SWG\Property(description="For an ad campaign, what's the target type, plays|budget|imp")
* @var string
*/
public $targetType;
@@ -397,7 +397,7 @@ public function getProgress(?Carbon $testDate = null): CampaignProgress
if ($this->targetType === 'budget') {
$progress->progressTarget = ($this->spend / $this->target) * 100;
- } else if ($this->targetType === 'impressions') {
+ } else if ($this->targetType === 'imp') {
$progress->progressTarget = ($this->impressions / $this->target) * 100;
} else {
$progress->progressTarget = ($this->plays / $this->target) * 100;
@@ -485,7 +485,7 @@ public function validate()
}
if ($this->type === 'ad') {
- if (!in_array($this->targetType, ['plays', 'budget', 'impressions'])) {
+ if (!in_array($this->targetType, ['plays', 'budget', 'imp'])) {
throw new InvalidArgumentException(__('Invalid target type'), 'targetType');
}
diff --git a/lib/Factory/UserGroupFactory.php b/lib/Factory/UserGroupFactory.php
index 70d3a20aad..173b2287dd 100644
--- a/lib/Factory/UserGroupFactory.php
+++ b/lib/Factory/UserGroupFactory.php
@@ -884,12 +884,16 @@ public function getFeatures()
}
/**
- * @param string $homepage The home page id
- * @return array|mixed
+ * @param string|null $homepage The home page id
+ * @return \Xibo\Entity\Homepage
* @throws \Xibo\Support\Exception\NotFoundException
*/
- public function getHomepageByName(string $homepage)
+ public function getHomepageByName(?string $homepage): Homepage
{
+ if (empty($homepage)) {
+ throw new NotFoundException(__('Homepage has not been set'));
+ }
+
$homepages = $this->getHomepages();
if (!array_key_exists($homepage, $homepages)) {
diff --git a/lib/Helper/Environment.php b/lib/Helper/Environment.php
index c4affe9eeb..5a86aabd80 100644
--- a/lib/Helper/Environment.php
+++ b/lib/Helper/Environment.php
@@ -30,7 +30,7 @@
*/
class Environment
{
- public static $WEBSITE_VERSION_NAME = '4.0.5';
+ public static $WEBSITE_VERSION_NAME = '4.0.6';
public static $XMDS_VERSION = '7';
public static $XLF_VERSION = 4;
public static $VERSION_REQUIRED = '8.1.0';
diff --git a/lib/XTR/CampaignSchedulerTask.php b/lib/XTR/CampaignSchedulerTask.php
index cbc962b541..4ae024943c 100644
--- a/lib/XTR/CampaignSchedulerTask.php
+++ b/lib/XTR/CampaignSchedulerTask.php
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
- * Xibo - Digital Signage - http://www.xibo.org.uk
+ * Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
@@ -232,7 +232,7 @@ public function run()
// cost/impressions/displays are sums.
if ($campaign->targetType === 'budget') {
$playsNeededPerLayout = $targetNeededPerLayout / $costPerPlay;
- } else if ($campaign->targetType === 'impressions') {
+ } else if ($campaign->targetType === 'imp') {
$playsNeededPerLayout = $targetNeededPerLayout / $impressionsPerPlay;
} else {
$playsNeededPerLayout = $targetNeededPerLayout / $countDisplays;
diff --git a/lib/Xmds/Soap6.php b/lib/Xmds/Soap6.php
index e68ca9d9d9..57425efc91 100644
--- a/lib/Xmds/Soap6.php
+++ b/lib/Xmds/Soap6.php
@@ -1,8 +1,8 @@
$serverKey,
'hardwareKey' => $hardwareKey
]);
-
+
// Sanitize
$serverKey = $sanitizer->getString('serverKey');
$hardwareKey = $sanitizer->getString('hardwareKey');
@@ -90,6 +90,11 @@ public function reportFaults(string $serverKey, string $hardwareKey, string $fau
$mediaId = $sanitizedFaultAlert->getInt('mediaId');
$widgetId = $sanitizedFaultAlert->getInt('widgetId');
+ // Trim the reason if it is too long
+ if (strlen($reason) >= 255) {
+ $reason = substr($reason, 0, 255);
+ }
+
try {
$dbh = $this->getStore()->getConnection();
diff --git a/modules/templates/product-elements.xml b/modules/templates/product-elements.xml
index 59692efd48..af4cef133f 100644
--- a/modules/templates/product-elements.xml
+++ b/modules/templates/product-elements.xml
@@ -97,6 +97,8 @@ var options = {};
if (properties.currencyCode && properties.currencyCode !== '') {
options.style = 'currency';
options.currency = properties.currencyCode;
+} else {
+ options.minimumFractionDigits = 2;
}
value = new Intl.NumberFormat(undefined, options).format(value);
diff --git a/tests/XMDS.http b/tests/XMDS.http
index 0a5f3bb37d..989f26080c 100644
--- a/tests/XMDS.http
+++ b/tests/XMDS.http
@@ -128,6 +128,25 @@ Content-Type: application/xml
###
+POST {{url}}/xmds.php?v=7
+Content-Type: application/xml
+
+
+
+
+ {{serverKey}}
+ {{hardwareKey}}
+ [{"code":5000, "reason": "Too long: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}]
+
+
+
+
+###
+
# Get the fileID from the Required Files response.
GET {{url}}/xmds.php?file=12.xlf&displayId=1&type=L&itemId=12&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230502T000000Z&X-Amz-Expires=1683048895&X-Amz-SignedHeaders=host&X-Amz-Signature=7c876be170afb29d194e7b035be6969198c22a32c22e163e2696754bb1163f5d
diff --git a/ui/src/layout-editor/layout.js b/ui/src/layout-editor/layout.js
index 5d586b5fd4..2117706b36 100644
--- a/ui/src/layout-editor/layout.js
+++ b/ui/src/layout-editor/layout.js
@@ -451,10 +451,13 @@ Layout.prototype.publish = function() {
toastr.success(res.message);
- // Redirect to the new published layout ( read only mode )
- window.location.href =
- urlsForApi.layout.designer.url.replace(
- ':id', res.data.layoutId) + '?vM=1';
+ // Update the thumbnail
+ lD.uploadThumbnail().finally(() => {
+ // Redirect to the new published layout ( read only mode )
+ window.location.href =
+ urlsForApi.layout.designer.url.replace(
+ ':id', res.data.layoutId) + '?vM=1';
+ });
} else {
lD.common.hideLoadingScreen();
diff --git a/ui/src/layout-editor/main.js b/ui/src/layout-editor/main.js
index 6136c8384c..b596ef4104 100644
--- a/ui/src/layout-editor/main.js
+++ b/ui/src/layout-editor/main.js
@@ -914,8 +914,7 @@ lD.showPublishScreen = function() {
"#publishNow",
"",
".publish-date-control"
- );
- lD.uploadThumbnail($("#layoutPublishForm #publishPreview"));`,
+ );`,
'lD.layout.publish();',
);
};
@@ -4169,6 +4168,7 @@ lD.importFromProvider = function(items) {
/**
* Take and upload a thumbnail
* @param {object} targetToAttach DOM object to attach the thumbnail to
+ * @return {Promise}
*/
lD.uploadThumbnail = function(targetToAttach) {
if ($(targetToAttach).length > 0) {
@@ -4179,19 +4179,27 @@ lD.uploadThumbnail = function(targetToAttach) {
);
$(targetToAttach).removeClass('d-none');
}
- const linkToAPI = urlsForApi.layout.addThumbnail;
- const requestPath = linkToAPI.url.replace(':id', lD.layout.layoutId);
- $.ajax({
- url: requestPath,
- type: 'POST',
- success: function() {
- // Attach to target
- if ($(targetToAttach).length > 0) {
- $(targetToAttach).find('.thumb-preview')
- .replaceWith($('')
- .attr('src', requestPath));
- }
- },
+
+ return new Promise(function(resolve, reject) {
+ const linkToAPI = urlsForApi.layout.addThumbnail;
+ const requestPath = linkToAPI.url.replace(':id', lD.layout.layoutId);
+ $.ajax({
+ url: requestPath,
+ type: 'POST',
+ success: function() {
+ // Attach to target
+ if ($(targetToAttach).length > 0) {
+ $(targetToAttach).find('.thumb-preview')
+ .replaceWith($('')
+ .attr('src', requestPath));
+ }
+
+ resolve();
+ },
+ fail: function() {
+ reject();
+ },
+ });
});
};
diff --git a/views/campaign-builder.twig b/views/campaign-builder.twig
index 6b23113e4d..9505b925e6 100644
--- a/views/campaign-builder.twig
+++ b/views/campaign-builder.twig
@@ -88,7 +88,7 @@
{% set options = [
{ id: "plays", name: "Plays"|trans },
{ id: "budget", name: "Budget"|trans },
- { id: "impressions", name: "Impressions"|trans },
+ { id: "imp", name: "Impressions"|trans },
] %}
{{ forms.dropdown("targetType", "single", title, campaign.targetType, options, "id", "name", helpText, "campaign-type-ad") }}
diff --git a/views/campaign-form-add.twig b/views/campaign-form-add.twig
index b3cb8dfb26..f2fd643a1d 100644
--- a/views/campaign-form-add.twig
+++ b/views/campaign-form-add.twig
@@ -109,7 +109,7 @@
{% set options = [
{ id: "plays", name: "Plays"|trans },
{ id: "budget", name: "Budget"|trans },
- { id: "impressions", name: "Impressions"|trans },
+ { id: "imp", name: "Impressions"|trans },
] %}
{{ forms.dropdown("targetType", "single", title, "both", options, "id", "name", helpText, "campaign-type-ad") }}
diff --git a/views/campaign-page.twig b/views/campaign-page.twig
index 060df20a63..3b5e7b3f16 100644
--- a/views/campaign-page.twig
+++ b/views/campaign-page.twig
@@ -260,6 +260,8 @@
return '{{ "Plays"|trans }}';
} else if (data === 'budget') {
return '{{ "Budget"|trans }}';
+ } else if (data === 'imp') {
+ return '{{ "Impressions"|trans }}';
}
return data;
},
diff --git a/views/menuboard-product-page.twig b/views/menuboard-product-page.twig
index cb1d57b383..be8c5ca6c1 100644
--- a/views/menuboard-product-page.twig
+++ b/views/menuboard-product-page.twig
@@ -111,8 +111,14 @@
responsivePriority: 2
},
{
- "data": "price",
- responsivePriority: 2
+ data: 'price',
+ responsivePriority: 2,
+ render: function (data, type) {
+ if (type !== 'display' || data === null || data === '') {
+ return data;
+ }
+ return new Intl.NumberFormat(undefined, {minimumFractionDigits: 2}).format(data);
+ },
},
{
responsivePriority: 3,