Change comment:
There is no comment for this version
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,308 @@ 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 paid and still active. 153 + #if ($licenseDetails.type == 'paid' && $licenseDetails.expirationDate 154 + && $licenseDetails.expirationDate.time > $datetool.date.time) 155 + #set ($currentLicenseDetails = $licenseDetails) 156 + #end 157 + #set ($licenseDetails = $oldLicenseDetails) 158 + #end 159 +#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 161 +#macro (displayCurrentLicense $currentLicenseDetails) 162 + #if ($currentLicenseDetails) 163 + <dt> 164 + <label>$escapetool.xml($services.localization.render('store.buyLicense.currentLicense'))</label> 165 + </dt> 166 + <dd> 167 + ## 168 + ## Extension name 169 + ## 170 + #if ("$!currentLicenseDetails.extensionName" == '') 171 + #getLicensedExtension($currentLicenseDetails) 172 + #if ($extension) 173 + #set ($currentLicenseDetails.extensionName = $extension.name) 174 + #else 175 + #set ($currentLicenseDetails.extensionName = $currentLicenseDetails.featureId) 176 + #end 177 + #end 178 + ## 179 + ## User limit 180 + ## 181 + #set ($LONG_MAX_VALUE = $mathtool.pow(2,63)) 182 + #if (!$currentLicenseDetails.maxUserCount || $currentLicenseDetails.maxUserCount == $LONG_MAX_VALUE) 183 + ## Unlimited user count. 184 + #set ($currentLicenseDetails.maxUserCount = -1) 185 + #end 186 + ## 187 + ## Days left 188 + ## 189 + #set ($remainingTime = $currentLicenseDetails.expirationDate.time - $datetool.date.time) 190 + #set ($daysLeft = $xwiki.jodatime.getDuration($remainingTime).standardDays) 191 + ## 192 + ## Support 193 + ## 194 + #if ("$!currentLicenseDetails.support" != '') 195 + #set ($support = $services.localization.render("store.buyLicense.support.$currentLicenseDetails.support")) 196 + $escapetool.xml($services.localization.render('store.buyLicense.currentLicense.summary', 197 + [$currentLicenseDetails.extensionName, $support, $currentLicenseDetails.maxUserCount, $daysLeft])) 198 + #else 199 + <div class="text-info"> 200 + $services.icon.renderHTML('info') 201 + $escapetool.xml($services.localization.render('store.buyLicense.currentLicense.summaryWithoutSupport', 202 + [$currentLicenseDetails.extensionName, $currentLicenseDetails.maxUserCount, $daysLeft])) 203 + </div> 204 + <p class="xHint">$escapetool.xml($services.localization.render('store.buyLicense.currentLicense.hint'))</p> 205 + #end 206 + </dd> 207 + #end 208 +#end 41 41 42 -== How to buy == 210 +#macro (buyLicenseForm $licenseDetails $priceMatrix $minUserCount) 211 + <form id="buyLicense" class="xform" action="" method="post"> 212 + <p>Fill in the form below to get your license.</p> 213 + <div class="hidden"> 214 + <input type="hidden" name="instanceId" value="$!escapetool.xml($licenseDetails.instanceId)"/> 215 + <input type="hidden" name="featureId" value="$!escapetool.xml($licenseDetails.featureId)"/> 216 + <input type="hidden" name="extensionVersion" value="$!escapetool.xml($licenseDetails.extensionVersion)"/> 217 + </div> 218 + <dl> 219 + <dt> 220 + <label>$escapetool.xml($services.localization.render('store.buyLicense.buyer'))</label> 221 + </dt> 222 + <dd> 223 + $!licenseDetails.firstName $!licenseDetails.lastName, $licenseDetails.email 224 + <a href="#editBuyer" class="editBuyer" 225 + title="$escapetool.xml($services.localization.render('store.buyLicense.buyer.edit'))" 226 + >$services.icon.renderHTML('pencil')</a> 227 + </dd> 228 + <dt class="hidden"> 229 + <label for="firstName"> 230 + $escapetool.xml($services.localization.render('store.buyLicense.firstName')) 231 + </label> 232 + </dt> 233 + <dd class="hidden"> 234 + <input type="text" name="firstName" id="firstName" value="$!escapetool.xml($licenseDetails.firstName)" 235 + required="required"/> 236 + </dd> 237 + <dt class="hidden"> 238 + <label for="lastName"> 239 + $escapetool.xml($services.localization.render('store.buyLicense.lastName')) 240 + </label> 241 + </dt> 242 + <dd class="hidden"> 243 + <input type="text" name="lastName" id="lastName" value="$!escapetool.xml($licenseDetails.lastName)" 244 + required="required"/> 245 + </dd> 246 + <dt class="hidden"> 247 + <label for="email"> 248 + $escapetool.xml($services.localization.render('store.buyLicense.email')) 249 + </label> 250 + <span class="xHint"> 251 + $escapetool.xml($services.localization.render('store.buyLicense.email.hint')) 252 + </span> 253 + </dt> 254 + <dd class="hidden"> 255 + <input type="email" name="email" id="email" value="$!escapetool.xml($licenseDetails.email)" 256 + class="form-control" required="required"/> 257 + </dd> 258 + #getCurrentLicense($licenseDetails) 259 + #displayCurrentLicense($currentLicenseDetails) 260 + <dt> 261 + <label>$escapetool.xml($services.localization.render('store.buyLicense.options'))</label> 262 + <span class="xHint">$escapetool.xml($services.localization.render('store.buyLicense.options.hint'))</span> 263 + </dt> 264 + <dd> 265 + #displayPriceMatrix($priceMatrix $minUserCount $currentLicenseDetails) 266 + </dd> 267 + </dl> 268 + #benefits 269 + <p> 270 + <input type="submit" class="button btn-success"#if (!$selectedSKU) disabled="disabled" #end 271 + value="$escapetool.xml($services.localization.render('store.buyLicense.submit'))"/> 272 + </p> 273 + </form> 274 +#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]](%%). 276 +#macro (accessPurchaseOrderForm $licenseDetails) 277 + <form id="accessPurchaseOrder" class="hidden" action="$services.extension.store.activePurchaseOrderURL" method="post"> 278 + <input type="hidden" name="instanceId" value="$!escapetool.xml($licenseDetails.instanceId)"/> 279 + <input type="hidden" name="email" value="$!escapetool.xml($licenseDetails.email)"/> 280 + <input type="submit"/> 281 + </form> 282 +#end 46 46 {{/velocity}} 284 + 285 +{{velocity}} 286 +{{html clean="false"}} 287 +#set ($discard = $xwiki.ssx.use('Store.Content.Pricing')) 288 +#set ($discard = $xwiki.jsx.use('Store.Content.Pricing')) 289 +#getLicenseDetails 290 +#getPriceMatrix($prices.skus) 291 +#set ($minUserCount = $request.session.getAttribute('com.xwiki.store.buyer.userCount')) 292 +#if (!$minUserCount) 293 + #set ($minUserCount = 1) 294 +#end 295 +#if ("$!licenseDetails.instanceId" != '') 296 + #buyLicenseForm($licenseDetails $priceMatrix $minUserCount) 297 + #accessPurchaseOrderForm($licenseDetails) 298 +#else 299 + <h2>Options</h2> 300 + <p>The price is per year and varies depending on the support level and the number of users.</p> 301 + #displayPriceMatrix($priceMatrix $minUserCount $NULL true) 302 + <h2>Benefits</h2> 303 + #benefits 304 + <h2>How to Buy</h2> 305 + <p>To buy, install this extension from inside your XWiki instance and <a href="#installation">follow the instructions</a>.</p> 306 +#end 307 +{{/html}} 308 +{{/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,22 @@ 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 / year'; 21 + font-weight: 100; 22 +} - 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