diff --git a/api/http/handler/extensions/extension_inspect.go b/api/http/handler/extensions/extension_inspect.go index ea5fa0e90..712e4403e 100644 --- a/api/http/handler/extensions/extension_inspect.go +++ b/api/http/handler/extensions/extension_inspect.go @@ -35,6 +35,10 @@ func (handler *Handler) extensionInspect(w http.ResponseWriter, r *http.Request) for _, p := range extensions { if p.ID == extensionID { extension = p + if extension.DescriptionURL != "" { + description, _ := client.Get(extension.DescriptionURL, 10) + extension.Description = string(description) + } break } } diff --git a/api/http/handler/extensions/extension_list.go b/api/http/handler/extensions/extension_list.go index 8f6cacb77..392822528 100644 --- a/api/http/handler/extensions/extension_list.go +++ b/api/http/handler/extensions/extension_list.go @@ -22,7 +22,7 @@ func (handler *Handler) extensionList(w http.ResponseWriter, r *http.Request) *h if storeDetails { definitions, err := handler.ExtensionManager.FetchExtensionDefinitions() if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extension definitions", err} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions", err} } for idx := range definitions { diff --git a/api/portainer.go b/api/portainer.go index 2eb2a99c5..2e9ef97d2 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -485,6 +485,7 @@ type ( Name string `json:"Name,omitempty"` ShortDescription string `json:"ShortDescription,omitempty"` Description string `json:"Description,omitempty"` + DescriptionURL string `json:"DescriptionURL,omitempty"` Price string `json:"Price,omitempty"` PriceDescription string `json:"PriceDescription,omitempty"` Deal bool `json:"Deal,omitempty"` @@ -492,7 +493,7 @@ type ( License LicenseInformation `json:"License,omitempty"` Version string `json:"Version"` UpdateAvailable bool `json:"UpdateAvailable"` - ProductID int `json:"ProductId,omitempty"` + ShopURL string `json:"ShopURL,omitempty"` Images []string `json:"Images,omitempty"` Logo string `json:"Logo,omitempty"` } diff --git a/app/extensions/registry-management/views/configure/configureregistry.html b/app/extensions/registry-management/views/configure/configureregistry.html index 48574dc41..3325230b3 100644 --- a/app/extensions/registry-management/views/configure/configureregistry.html +++ b/app/extensions/registry-management/views/configure/configureregistry.html @@ -149,8 +149,8 @@ Test in progress... diff --git a/app/portainer/components/datatables/registries-datatable/registriesDatatable.html b/app/portainer/components/datatables/registries-datatable/registriesDatatable.html index 900e3432a..9bc262c40 100644 --- a/app/portainer/components/datatables/registries-datatable/registriesDatatable.html +++ b/app/portainer/components/datatables/registries-datatable/registriesDatatable.html @@ -64,8 +64,8 @@ Browse - - Browse ( ) + + Browse (extension) diff --git a/app/portainer/components/extension-list/extension-item/extensionItem.html b/app/portainer/components/extension-list/extension-item/extensionItem.html index d9d870a08..95acdb9cb 100644 --- a/app/portainer/components/extension-list/extension-item/extensionItem.html +++ b/app/portainer/components/extension-list/extension-item/extensionItem.html @@ -1,9 +1,10 @@ -
+
- - + + +
-
+
+ + + +

+ + Portainer must be connected to the Internet to fetch the list of available extensions. +

+
+
diff --git a/app/portainer/views/extensions/extensionsController.js b/app/portainer/views/extensions/extensionsController.js index fb6b87244..cb1a19d8a 100644 --- a/app/portainer/views/extensions/extensionsController.js +++ b/app/portainer/views/extensions/extensionsController.js @@ -1,58 +1,59 @@ angular.module('portainer.app') -.controller('ExtensionsController', ['$scope', '$state', 'ExtensionService', 'Notifications', -function ($scope, $state, ExtensionService, Notifications) { + .controller('ExtensionsController', ['$scope', '$state', 'ExtensionService', 'Notifications', + function($scope, $state, ExtensionService, Notifications) { - $scope.state = { - actionInProgress: false, - currentDate: moment().format('YYYY-MM-dd') - }; + $scope.state = { + actionInProgress: false, + currentDate: moment().format('YYYY-MM-dd') + }; - $scope.formValues = { - License: '' - }; + $scope.formValues = { + License: '' + }; - function initView() { - ExtensionService.extensions(true) - .then(function onSuccess(data) { - $scope.extensions = data; - }) - .catch(function onError(err) { - Notifications.error('Failure', err, 'Unable to access extension store'); - }); - } + function initView() { + ExtensionService.extensions(true) + .then(function onSuccess(data) { + $scope.extensions = data; + }) + .catch(function onError(err) { + $scope.extensions = []; + Notifications.error('Failure', err, 'Unable to access extension store'); + }); + } - $scope.enableExtension = function() { - var license = $scope.formValues.License; + $scope.enableExtension = function() { + var license = $scope.formValues.License; - $scope.state.actionInProgress = true; - ExtensionService.enable(license) - .then(function onSuccess() { - Notifications.success('Extension successfully enabled'); - $state.reload(); - }) - .catch(function onError(err) { - Notifications.error('Failure', err, 'Unable to enable extension'); - }) - .finally(function final() { - $scope.state.actionInProgress = false; - }); - }; + $scope.state.actionInProgress = true; + ExtensionService.enable(license) + .then(function onSuccess() { + Notifications.success('Extension successfully enabled'); + $state.reload(); + }) + .catch(function onError(err) { + Notifications.error('Failure', err, 'Unable to enable extension'); + }) + .finally(function final() { + $scope.state.actionInProgress = false; + }); + }; - $scope.isValidLicenseFormat = function(form) { - var valid = true; + $scope.isValidLicenseFormat = function(form) { + var valid = true; - if (!$scope.formValues.License) { - return; - } + if (!$scope.formValues.License) { + return; + } - if (isNaN($scope.formValues.License[0])) { - valid = false; - } + if (isNaN($scope.formValues.License[0])) { + valid = false; + } - form.extension_license.$setValidity('invalidLicense', valid); - }; + form.extension_license.$setValidity('invalidLicense', valid); + }; - initView(); -}]); + initView(); + }]); diff --git a/app/portainer/views/extensions/inspect/extension.html b/app/portainer/views/extensions/inspect/extension.html index 1e5e3d3c4..0a83740d9 100644 --- a/app/portainer/views/extensions/inspect/extension.html +++ b/app/portainer/views/extensions/inspect/extension.html @@ -51,11 +51,17 @@
- + Buy
+
+ + Add license key + +
+
Coming soon
@@ -92,10 +98,16 @@ Description
-
- - {{ extension.Description }} - +
+
+
+
+
+

+ + Description for this extension unavailable at the moment. +

+
@@ -113,7 +125,7 @@
- +
diff --git a/app/portainer/views/support/product/product.html b/app/portainer/views/support/product/product.html index 8af370d74..554fd3fb7 100644 --- a/app/portainer/views/support/product/product.html +++ b/app/portainer/views/support/product/product.html @@ -52,7 +52,7 @@
- + Buy
diff --git a/assets/css/app.css b/assets/css/app.css index 103582a1f..4532ce454 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -174,6 +174,11 @@ a[ng-click]{ box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5); } +.blocklist-item--disabled { + cursor: auto; + background-color: #ececec; +} + .blocklist-item--selected { border: 2px solid #bbbbbb; background-color: #ececec; diff --git a/assets/images/extensions_overview_diagram.png b/assets/images/extensions_overview_diagram.png new file mode 100644 index 000000000..91f7ee164 Binary files /dev/null and b/assets/images/extensions_overview_diagram.png differ