Change comment:
Install extension [com.xwiki.store:application-store-ui-1.7.1]
Summary
-
Page properties (3 modified, 0 added, 0 removed)
-
Attachments (0 modified, 0 added, 3 removed)
-
Objects (0 modified, 2 added, 0 removed)
Details
- Page properties
-
- Title
-
... ... @@ -1,1 +1,0 @@ 1 -Pricing tab - Hidden
-
... ... @@ -1,1 +1,1 @@ 1 - false1 +true - Content
-
... ... @@ -1,46 +1,307 @@ 1 -{{velocity}} 2 -= Extension price = 3 -#if("$!price" == '' && $hasAdmin) 1 +{{include reference="Store.Code.Macros" /}} 4 4 5 - {{error}}Admin error: The price field is empty. Please set a price for this extension in the **Store.PayingApps.Code.PayingAppsClass**.{{/error}} 3 +{{velocity output="false"}} 4 +#macro (getPriceMatrix $skus) 5 + #set ($priceMatrix = { 6 + 'columns': $collectionstool.sortedSet, 7 + 'rows': $collectionstool.orderedMap 8 + }) 9 + #foreach ($sku in $skus) 10 + #set ($support = "$!sku.support") 11 + #set ($users = $mathtool.toNumber($sku.users)) 12 + #set ($price = "$!sku.price") 13 + #if ($support && $users && $price) 14 + #set ($pricePerUsers = $priceMatrix.rows.get($support)) 15 + #if (!$pricePerUsers) 16 + #set ($pricePerUsers = {}) 17 + #set ($discard = $priceMatrix.rows.put($support, $pricePerUsers)) 18 + #end 19 + #set ($discard = $pricePerUsers.put($users, $price)) 20 + #set ($discard = $priceMatrix.columns.add($users)) 21 + #end 22 + #end 23 +#end 6 6 7 - #set($price = "{{{--}}}") 25 +#macro (findSKUMatchingCurrentLicense $priceMatrix $minUserCount $currentLicenseDetails) 26 + #set ($skuMatchingCurrentLicense = $NULL) 27 + #if ($priceMatrix.rows.containsKey($currentLicenseDetails.support)) 28 + ## Select the best match for the specified support level. 29 + #foreach ($column in $priceMatrix.columns) 30 + #set ($price = $priceMatrix.rows.get($currentLicenseDetails.support).get($column)) 31 + #if ($price && $column >= $mathtool.max($minUserCount, $currentLicenseDetails.maxUserCount)) 32 + #set ($skuMatchingCurrentLicense = "$currentLicenseDetails.support,$column") 33 + #break 34 + #end 35 + #end 36 + #end 37 + #if (!$skuMatchingCurrentLicense) 38 + ## Select the best match for the specified user limit. 39 + #foreach ($entry in $priceMatrix.rows.entrySet()) 40 + #foreach ($column in $priceMatrix.columns) 41 + #set ($price = $entry.value.get($column)) 42 + #if ($price && $column >= $mathtool.max($minUserCount, $currentLicenseDetails.maxUserCount)) 43 + #set ($skuMatchingCurrentLicense = "$entry.key,$column") 44 + #break 45 + #end 46 + #end 47 + #if ($skuMatchingCurrentLicense) 48 + #break 49 + #end 50 + #end 51 + #end 8 8 #end 9 9 10 -This application costs **$price euros/year** and you get the following: 54 +#macro (displayPriceMatrix $priceMatrix $minUserCount $currentLicenseDetails $readOnly) 55 + <table class="prices"> 56 + <tr> 57 + <th class="legend">$escapetool.xml($services.localization.render('store.buyLicense.support')) 58 + / $escapetool.xml($services.localization.render('store.buyLicense.users'))</th> 59 + #foreach ($column in $priceMatrix.columns) 60 + <th class="users#if ($column < $minUserCount) disabled#end" 61 + title="$escapetool.xml($services.localization.render('store.buyLicense.users.hint', [$column]))" 62 + >$escapetool.xml($column)<span class="icon">$services.icon.renderHTML('user')</span></th> 63 + #end 64 + </tr> 65 + #set ($selectedSKU = $NULL) 66 + #if (!$readOnly && $currentLicenseDetails) 67 + #findSKUMatchingCurrentLicense($priceMatrix $minUserCount $currentLicenseDetails) 68 + #set ($selectedSKU = $skuMatchingCurrentLicense) 69 + #end 70 + #foreach ($entry in $priceMatrix.rows.entrySet()) 71 + <tr class="support-$escapetool.xml($entry.key)"> 72 + <th><a tabindex="0" role="button" data-toggle="popover" data-trigger="focus" data-html="true" 73 + title="$escapetool.xml($services.localization.render("store.buyLicense.support.${entry.key}.title"))" 74 + data-content="$escapetool.xml($services.localization.render( 75 + "store.buyLicense.support.${entry.key}.description"))" 76 + >$escapetool.xml($services.localization.render("store.buyLicense.support.$entry.key"))<span 77 + class="icon">$services.icon.renderHTML('info')</span></a></th> 78 + #foreach ($column in $priceMatrix.columns) 79 + #set ($price = $entry.value.get($column)) 80 + #if ($price) 81 + #set ($sku = "$entry.key,$column") 82 + #set ($disabled = $column < $minUserCount) 83 + #if (!$disabled && !$selectedSKU && !$readOnly) 84 + #set ($selectedSKU = $sku) 85 + #end 86 + <td class="price price-euro#if ($disabled) disabled#elseif ($selectedSKU == $sku) active#end" 87 + data-support="$escapetool.xml($entry.key)" data-users="$escapetool.xml($column)"> 88 + <label>#if (!$readOnly)<input type="radio" name="sku" value="$escapetool.xml($sku)" required="required" 89 + #if ($disabled)disabled="disabled"#elseif ($selectedSKU == $sku)checked="checked"#end 90 + />#end$escapetool.xml($price)</label> 91 + </td> 92 + #else 93 + <td/> 94 + #end 95 + #end 96 + </tr> 97 + #end 98 + </table> 99 +#end 11 11 12 - (% class="col-xs-12 col-lg-12" id="benefits" %) ((( 13 - (% class="col-xs-12 col-s-4 col-lg-4" %) ((( 14 - image:key.png 15 - == 1 year license == 16 - By purchasing an XWiki Application License, you'll benefit from it during one year. 17 - ))) 18 - (% class="col-xs-12 col-s-4 col-lg-4" %) ((( 19 - image:recycle.png 20 - == Free updates == 21 - You benefit from all the application updates during one year. You are always up to date. 22 - ))) 23 - (% class="col-xs-12 col-s-4 col-lg-4" %) ((( 24 - image:tool.png 25 - == Support included == 26 - If you are facing an issue, you can reach the XWiki support. Our team is always available to help. 27 - ))) 28 - ))) 101 +#macro (getLicenseDetails) 102 + #set ($licenseDetails = {}) 103 + #foreach ($name in ['firstName', 'lastName', 'email', 'instanceId']) 104 + #set ($discard = $licenseDetails.put($name, $request.session.getAttribute("com.xwiki.store.buyer.$name"))) 105 + #end 106 + #set ($licenseDetails.featureId = $doc.getValue('id')) 107 + #set ($licenseDetails.extensionVersion = $request.version) 108 + #if ("$!licenseDetails.extensionVersion" == '') 109 + ## Use the latest stable version. 110 + #set ($versions = $services.extension.resolveVersions($licenseDetails.featureId, 0, -1)) 111 + ## The versions are returned in ascending order and we want the latest. 112 + ## Note that we can't access the last item directly. We're forced to iterate. 113 + #foreach ($version in $versions) 114 + #if ($version.type == 'STABLE') 115 + #set ($licenseDetails.extensionVersion = $version.value) 116 + #end 117 + #end 118 + #end 119 + #set ($licenseDetails.licenseType = 'paid') 120 +#end 29 29 30 -{{comment}} 31 -* __1 year license__: By purchasing an XWiki Application License, you'll benefit from it during one year. 32 -* __Free updates__: You benefit from all the application updates during one year. You are always up to date. 33 -* __Support included__: If you are facing an issue, you can reach the [[XWiki support>>mailto:support@xwiki.com]]. Our team is always available to help. 34 -{{/comment}} 122 +#macro (benefits) 123 + <p>What do you get when you purchase an XWiki extension?</p> 124 + <div class="row" id="benefits"> 125 + <div class="col-xs-12 col-sm-4"> 126 + <div class="icon">$services.icon.renderHTML('key')</div> 127 + <h2>1 year license</h2> 128 + <p>By purchasing an XWiki extension license, you'll benefit from it during one year.</p> 129 + </div> 130 + <div class="col-xs-12 col-sm-4"> 131 + <div class="icon">$services.icon.renderHTML('refresh')</div> 132 + <h2>Free updates</h2> 133 + <p>You benefit from all the extension updates during one year. You are always up to date.</p> 134 + </div> 135 + <div class="col-xs-12 col-sm-4"> 136 + <div class="icon">$services.icon.renderHTML('wrench')</div> 137 + <h2>Support included</h2> 138 + <p>If you are facing an issue, you can reach the XWiki support. Our team is always available to help.</p> 139 + </div> 140 + </div> 141 +#end 35 35 36 -== VAT information == 143 +#macro (getCurrentLicense $licenseDetails) 144 + ## Get the best license for the specified instance and feature. 145 + #set ($currentLicenseDetails = $NULL) 146 + #getLicenseQuery($licenseDetails.instanceId $licenseDetails.featureId $query) 147 + #set ($results = $query.setLimit(1).execute()) 148 + #if ($results && $results.size() > 0) 149 + #set ($currentLicenseDetailsDocument = $xwiki.getDocument($results[0])) 150 + #set ($oldLicenseDetails = $licenseDetails) 151 + #readLicenseDetailsFromDocument($currentLicenseDetailsDocument) 152 + ## Check if the best license is still active. 153 + #if ($licenseDetails.expirationDate && $licenseDetails.expirationDate.time > $datetool.date.time) 154 + #set ($currentLicenseDetails = $licenseDetails) 155 + #end 156 + #set ($licenseDetails = $oldLicenseDetails) 157 + #end 158 +#end 37 37 38 -* If you are in the EU and have a VAT number there is no VAT to add to the price 39 -* If you are in the EU and don't have a VAT number there is the VAT of your country to add to the price 40 -* If you are outside the EU there is no VAT to add to the price 160 +#macro (displayCurrentLicense $currentLicenseDetails) 161 + #if ($currentLicenseDetails) 162 + <dt> 163 + <label>$escapetool.xml($services.localization.render('store.buyLicense.currentLicense'))</label> 164 + </dt> 165 + <dd> 166 + ## 167 + ## Extension name 168 + ## 169 + #if ("$!currentLicenseDetails.extensionName" == '') 170 + #getLicensedExtension($currentLicenseDetails) 171 + #if ($extension) 172 + #set ($currentLicenseDetails.extensionName = $extension.name) 173 + #else 174 + #set ($currentLicenseDetails.extensionName = $currentLicenseDetails.featureId) 175 + #end 176 + #end 177 + ## 178 + ## User limit 179 + ## 180 + #set ($LONG_MAX_VALUE = $mathtool.pow(2,63)) 181 + #if (!$currentLicenseDetails.maxUserCount || $currentLicenseDetails.maxUserCount == $LONG_MAX_VALUE) 182 + ## Unlimited user count. 183 + #set ($currentLicenseDetails.maxUserCount = -1) 184 + #end 185 + ## 186 + ## Days left 187 + ## 188 + #set ($remainingTime = $currentLicenseDetails.expirationDate.time - $datetool.date.time) 189 + #set ($daysLeft = $xwiki.jodatime.getDuration($remainingTime).standardDays) 190 + ## 191 + ## Support 192 + ## 193 + #if ("$!currentLicenseDetails.support" != '') 194 + #set ($support = $services.localization.render("store.buyLicense.support.$currentLicenseDetails.support")) 195 + $escapetool.xml($services.localization.render('store.buyLicense.currentLicense.summary', 196 + [$currentLicenseDetails.extensionName, $support, $currentLicenseDetails.maxUserCount, $daysLeft])) 197 + #else 198 + <div class="text-info"> 199 + $services.icon.renderHTML('info') 200 + $escapetool.xml($services.localization.render('store.buyLicense.currentLicense.summaryWithoutSupport', 201 + [$currentLicenseDetails.extensionName, $currentLicenseDetails.maxUserCount, $daysLeft])) 202 + </div> 203 + <p class="xHint">$escapetool.xml($services.localization.render('store.buyLicense.currentLicense.hint'))</p> 204 + #end 205 + </dd> 206 + #end 207 +#end 41 41 42 -== How to buy == 209 +#macro (buyLicenseForm $licenseDetails $priceMatrix $minUserCount) 210 + <form id="buyLicense" class="xform" action="" method="post"> 211 + <p>Fill in the form below to get your license.</p> 212 + <div class="hidden"> 213 + <input type="hidden" name="instanceId" value="$!escapetool.xml($licenseDetails.instanceId)"/> 214 + <input type="hidden" name="featureId" value="$!escapetool.xml($licenseDetails.featureId)"/> 215 + <input type="hidden" name="extensionVersion" value="$!escapetool.xml($licenseDetails.extensionVersion)"/> 216 + </div> 217 + <dl> 218 + <dt> 219 + <label>$escapetool.xml($services.localization.render('store.buyLicense.buyer'))</label> 220 + </dt> 221 + <dd> 222 + $!licenseDetails.firstName $!licenseDetails.lastName, $licenseDetails.email 223 + <a href="#editBuyer" class="editBuyer" 224 + title="$escapetool.xml($services.localization.render('store.buyLicense.buyer.edit'))" 225 + >$services.icon.renderHTML('pencil')</a> 226 + </dd> 227 + <dt class="hidden"> 228 + <label for="firstName"> 229 + $escapetool.xml($services.localization.render('store.buyLicense.firstName')) 230 + </label> 231 + </dt> 232 + <dd class="hidden"> 233 + <input type="text" name="firstName" id="firstName" value="$!escapetool.xml($licenseDetails.firstName)" 234 + required="required"/> 235 + </dd> 236 + <dt class="hidden"> 237 + <label for="lastName"> 238 + $escapetool.xml($services.localization.render('store.buyLicense.lastName')) 239 + </label> 240 + </dt> 241 + <dd class="hidden"> 242 + <input type="text" name="lastName" id="lastName" value="$!escapetool.xml($licenseDetails.lastName)" 243 + required="required"/> 244 + </dd> 245 + <dt class="hidden"> 246 + <label for="email"> 247 + $escapetool.xml($services.localization.render('store.buyLicense.email')) 248 + </label> 249 + <span class="xHint"> 250 + $escapetool.xml($services.localization.render('store.buyLicense.email.hint')) 251 + </span> 252 + </dt> 253 + <dd class="hidden"> 254 + <input type="email" name="email" id="email" value="$!escapetool.xml($licenseDetails.email)" 255 + class="form-control" required="required"/> 256 + </dd> 257 + #getCurrentLicense($licenseDetails) 258 + #displayCurrentLicense($currentLicenseDetails) 259 + <dt> 260 + <label>$escapetool.xml($services.localization.render('store.buyLicense.options'))</label> 261 + <span class="xHint">$escapetool.xml($services.localization.render('store.buyLicense.options.hint'))</span> 262 + </dt> 263 + <dd> 264 + #displayPriceMatrix($priceMatrix $minUserCount $currentLicenseDetails) 265 + </dd> 266 + </dl> 267 + #benefits 268 + <p> 269 + <input type="submit" class="button btn-success"#if (!$selectedSKU) disabled="disabled" #end 270 + value="$escapetool.xml($services.localization.render('store.buyLicense.submit'))"/> 271 + </p> 272 + </form> 273 +#end 43 43 44 -## Note: this is temporary until we implement the buy button 45 -To buy, (% id='install' %)[[install this extension from inside your XWiki instance and follow the instructions>>path:#installation]](%%). 275 +#macro (accessPurchaseOrderForm $licenseDetails) 276 + <form id="accessPurchaseOrder" class="hidden" action="$services.extension.store.activePurchaseOrderURL" method="post"> 277 + <input type="hidden" name="instanceId" value="$!escapetool.xml($licenseDetails.instanceId)"/> 278 + <input type="hidden" name="email" value="$!escapetool.xml($licenseDetails.email)"/> 279 + <input type="submit"/> 280 + </form> 281 +#end 46 46 {{/velocity}} 283 + 284 +{{velocity}} 285 +{{html clean="false"}} 286 +#set ($discard = $xwiki.ssx.use('Store.Content.Pricing')) 287 +#set ($discard = $xwiki.jsx.use('Store.Content.Pricing')) 288 +#getLicenseDetails 289 +#getPriceMatrix($prices.skus) 290 +#set ($minUserCount = $request.session.getAttribute('com.xwiki.store.buyer.userCount')) 291 +#if (!$minUserCount) 292 + #set ($minUserCount = 1) 293 +#end 294 +#if ("$!licenseDetails.instanceId" != '') 295 + #buyLicenseForm($licenseDetails $priceMatrix $minUserCount) 296 + #accessPurchaseOrderForm($licenseDetails) 297 +#else 298 + <h2>Options</h2> 299 + <p>The price varies depending on the support level and the number of users.</p> 300 + #displayPriceMatrix($priceMatrix $minUserCount $NULL true) 301 + <h2>Benefits</h2> 302 + #benefits 303 + <h2>How to Buy</h2> 304 + <p>To buy, install this extension from inside your XWiki instance and <a href="#installation">follow the instructions</a>.</p> 305 +#end 306 +{{/html}} 307 +{{/velocity}}
- key.png
-
- Author
-
... ... @@ -1,1 +1,0 @@ 1 -XWiki.Admin - Size
-
... ... @@ -1,1 +1,0 @@ 1 -1.9 KB - Content
- recycle.png
-
- Author
-
... ... @@ -1,1 +1,0 @@ 1 -XWiki.Admin - Size
-
... ... @@ -1,1 +1,0 @@ 1 -1.9 KB - Content
- tool.png
-
- Author
-
... ... @@ -1,1 +1,0 @@ 1 -XWiki.Admin - Size
-
... ... @@ -1,1 +1,0 @@ 1 -1.9 KB - Content
- XWiki.JavaScriptExtension[0]
-
- Caching policy
-
... ... @@ -1,0 +1,1 @@ 1 +long - Code
-
... ... @@ -1,0 +1,57 @@ 1 +require(['jquery', 'bootstrap'], function($) { 2 + // Activate the support information. 3 + $('table.prices [data-toggle="popover"]').popover(); 4 + 5 + // Manage the selected SKU from the price matrix. 6 + var updateSelectedSKU = function() { 7 + $(this).closest('table.prices').find('td.price.active').removeClass('active'); 8 + $(this).closest('td.price').toggleClass('active'); 9 + } 10 + $('form table.prices td.price input:checked').each(updateSelectedSKU); 11 + $('form table.prices').on('change', 'td.price input[type="radio"]', updateSelectedSKU); 12 + 13 + var showBuyerFields = function(editBuyerLink) { 14 + editBuyerLink.closest('dd').hide().prev('dt').hide() 15 + .nextAll('.hidden').removeClass('hidden') 16 + .first().css('margin-top', 0); 17 + }; 18 + 19 + // Edit buyer details link. 20 + $('a.editBuyer').click(function (event) { 21 + event.preventDefault(); 22 + showBuyerFields($(this)); 23 + }); 24 + 25 + // We need to register the event directly on the input field because it doesn't bubble up. 26 + $('form#buyLicense input').on('invalid', function() { 27 + if ($(this).is(':hidden')) { 28 + showBuyerFields($(this).closest('form').find('a.editBuyer')); 29 + } 30 + }); 31 + 32 + $('form#buyLicense').submit(function(event) { 33 + event.preventDefault(); 34 + var buyLicenseForm = $(this); 35 + var submitButton = buyLicenseForm.find('input[type="submit"]').prop('disabled', true); 36 + var progressNotification = new XWiki.widgets.Notification($jsontool.serialize($services.localization.render( 37 + 'store.buyLicense.submit.inProgress')), 'inprogress'); 38 + var url = new XWiki.Document('BuyLicense', 'Store').getURL('get', 'outputSyntax=plain'); 39 + $.post(url, buyLicenseForm.serialize()).done(function(result) { 40 + if (result && result.order) { 41 + progressNotification.replace(new XWiki.widgets.Notification($jsontool.serialize( 42 + $services.localization.render('store.buyLicense.submit.done')), 'done')); 43 + var accessPurchaseOrderForm = $('form#accessPurchaseOrder'); 44 + accessPurchaseOrderForm.find('input[name="email"]').val(buyLicenseForm.find('input[name="email"]').val()); 45 + accessPurchaseOrderForm.submit(); 46 + } else { 47 + progressNotification.replace(new XWiki.widgets.Notification($jsontool.serialize( 48 + $services.localization.render('store.buyLicense.submit.failed')), 'error')); 49 + } 50 + }).fail(function() { 51 + progressNotification.replace(new XWiki.widgets.Notification($jsontool.serialize( 52 + $services.localization.render('store.buyLicense.submit.failed')), 'error')); 53 + }).always(function() { 54 + submitButton.prop('disabled', false); 55 + }); 56 + }); 57 +}); - Parse content
-
... ... @@ -1,0 +1,1 @@ 1 +Yes - Use this extension
-
... ... @@ -1,0 +1,1 @@ 1 +onDemand
- XWiki.StyleSheetExtension[0]
-
- Caching policy
-
... ... @@ -1,0 +1,1 @@ 1 +long - Code
-
... ... @@ -1,0 +1,21 @@ 1 +table.prices a[data-toggle="popover"] { 2 + color: inherit; 3 + text-decoration: none; 4 +} 5 + 6 +table.prices th span.icon { 7 + margin-left: .3em; 8 +} 9 + 10 +table.prices th.users, 11 +table.prices td.price { 12 + text-align: right; 13 +} 14 + 15 +table.prices td.price-euro label { 16 + display: block; 17 +} 18 + 19 +table.prices td.price-euro label:after { 20 + content: ' \20ac'; 21 +} - Content Type
-
... ... @@ -1,0 +1,1 @@ 1 +CSS - Parse content
-
... ... @@ -1,0 +1,1 @@ 1 +No - Use this extension
-
... ... @@ -1,0 +1,1 @@ 1 +onDemand