Commit e0d52181 authored by Simon Gilli's avatar Simon Gilli
Browse files

Merge branch '175-display-other-extension-of-same-user' into 'develop'

Resolve "Display Other extension of same user"

Closes #175

See merge request !338
parents bf7a6857 52d2c996
Pipeline #11542 passed with stages
in 7 minutes and 41 seconds
......@@ -792,12 +792,27 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implement
public function getAdditionalSupportVersions(): array
{
$supportVersions = [];
$supportedVersions = $this->getAllSupportedVersions();
$supportVersionsOfLastVersion = $this->getLastVersion()->getMatrixOfSupportedTypo3Versions();
foreach ($supportedVersions as $key => $version) {
$test = $supportVersionsOfLastVersion[$key];
if (!empty($test)) {
unset($supportedVersions[$key]);
}
}
return $supportedVersions;
}
public function getAllSupportedVersions(): array
{
$supportedVersions = [];
$ltsVersionService = GeneralUtility::makeInstance(\T3o\TerFe2\Service\LTSVersionService::class);
foreach ($ltsVersionService->getOldLTSVersions() as $versionNumber) {
if (VersionUtility::doesExtensionSupportTypo3Version($this, $versionNumber)) {
$supportVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = [
$supportedVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = [
'label' => $ltsVersionService->getVersionLabelOfRelease($versionNumber) . ' LTS',
'badgeClass' => 'danger'
];
......@@ -805,29 +820,21 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity implement
}
foreach ($ltsVersionService->getELTSVersions() as $versionNumber) {
if (VersionUtility::doesExtensionSupportTypo3Version($this, $versionNumber)) {
$supportVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = ['label' => $ltsVersionService->getVersionLabelOfRelease($versionNumber) . ' LTS', 'badgeClass' => 'warning'];
$supportedVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = ['label' => $ltsVersionService->getVersionLabelOfRelease($versionNumber) . ' LTS', 'badgeClass' => 'warning'];
}
}
foreach ($ltsVersionService->getActiveVersions() as $versionNumber) {
if (VersionUtility::doesExtensionSupportTypo3Version($this, $versionNumber)) {
$supportVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = ['label' => $ltsVersionService->getVersionLabelOfRelease($versionNumber) . ' LTS', 'badgeClass' => 'success'];
$supportedVersions[$ltsVersionService->getVersionLabelOfRelease($versionNumber)] = ['label' => $ltsVersionService->getVersionLabelOfRelease($versionNumber) . ' LTS', 'badgeClass' => 'success'];
}
}
if ($ltsVersionService->getLatestDevelopmentVersion(true) > VersionNumberUtility::convertVersionNumberToInteger($ltsVersionService->coreData['latest_lts'])
&& VersionUtility::doesExtensionSupportTypo3Version($this, $ltsVersionService->getLatestDevelopmentVersion(true))) {
$supportVersions[$ltsVersionService->getLatestDevelopmentVersion()] = ['label' => $ltsVersionService->getLatestDevelopmentVersion() . '-dev', 'badgeClass' => 'primary'];
}
array_unique($supportVersions);
$supportVersionsOfLastVersion = $this->getLastVersion()->getMatrixOfSupportedTypo3Versions();
foreach ($supportVersions as $key => $version) {
$test = $supportVersionsOfLastVersion[$key];
if (!empty($test)) {
unset($supportVersions[$key]);
}
$supportedVersions[$ltsVersionService->getLatestDevelopmentVersion()] = ['label' => $ltsVersionService->getLatestDevelopmentVersion() . '-dev', 'badgeClass' => 'primary'];
}
array_unique($supportedVersions);
return $supportVersions;
return $supportedVersions;
}
/**
......
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<div class="text-center my-3">
<div class="row mx-auto my-auto">
<div id="extensionCarousel" class="carousel slide w-100" data-ride="carousel">
<div class="carousel-inner w-100" role="listbox">
<f:for each="{extensionsByUser}" iteration="it" as="extension">
<div class="carousel-item{f:if(condition:it.isFirst,then:' active')}">
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<terfe2:extensionIcon version="{extension.lastVersion}" alt="{extension.lastVersion.title}" class="ter-ext-icon" />
<f:link.action action="show" arguments="{extension: extension}">
<span title="Extension title: {extension.lastVersion.title}" itemprop="alternateName">
{extension.lastVersion.title}
</span>
</f:link.action>
<br>
<small>({extension.extKey})</small>
</h5>
</div>
<div class="card-body">
<p>{extension.lastVersion.description}</p>
<small>
Supported TYPO3 versions:<br>
<f:for each="{extension.allSupportedVersions}" as="version">
<button class="btn btn-{version.badgeClass} btn-sm mr-2" disabled title="{f:translate(key:'title_{version.badgeClass}', arguments: {0: version.label})}">{version.label}</button>
</f:for>
</small>
</div>
<div class="card-footer">
<small>
Last upload:
<f:format.date format="%d. %b %Y">@{extension.lastVersion.uploadDate}</f:format.date>
</small>
</div>
</div>
</div>
</div>
</f:for>
</div>
<a class="carousel-control-prev w-auto" href="#extensionCarousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon bg-primary border border-primary rounded-circle" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next w-auto" href="#extensionCarousel" role="button" data-slide="next">
<span class="carousel-control-next-icon bg-primary border border-primary rounded-circle" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
</div>
</html>
......@@ -4,241 +4,250 @@
<f:section name="main">
<f:if condition="{extension}">
<f:then>
<f:security.ifHasRole role="{settings.reviewerGroupUid}">
<div class="alert alert-info mb-3" role="alert">
<div class="media">
<f:format.raw>
<tl:gravatar emailAddress="{owner.email}" size="125" />
</f:format.raw>
<div class="media-body ml-5">
<h4>Owner of this extension</h4>
<f:if condition="{owner}">
<f:then>
<p>
<strong>Name:</strong> {owner.name}<br>
<strong>Email:</strong>
<f:link.email email="{owner.email}">{owner.email}</f:link.email>
<br>
<strong>Username:</strong> {owner.username}
</p>
</f:then>
<f:else>
<p>
<strong>Username:</strong> {extension.frontendUser}
<small>User did not log in on typo3.org at least once.</small>
</p>
</f:else>
</f:if>
</div>
</div>
</div>
</f:security.ifHasRole>
<f:if condition="{extension.lastVersion.reviewState} == -2">
<div class="alert alert-warning mt-3 mb-3" role="alert">
<h4 class="alert-header">
<span class="ter-ext-state-beta"><i class="fa fa-exclamation-triangle"></i></span>
Outdated extension<br>
<small class="text-muted">This extension supports none of the currently supported TYPO3 core version</small>
</h4>
</div>
</f:if>
<f:if condition="{owner.username} == 'abandoned_extensions'">
<div class="alert alert-danger mt-3 mb-3" role="alert">
<h4 class="alert-header">
<span class="ter-ext-state-beta"><i class="fa fa-exclamation-triangle"></i></span>
Abandoned extension<br>
<small class="text-muted">This extension has no maintainer at the moment. If you like to become the maintainer of this extension, fill out our <f:link.page pageUid="{settings.pages.abandonedExtensionFormPid}">registration form</f:link.page>.</small>
</h4>
</div>
</f:if>
<f:if condition="{extension.securityTeamNotice}">
<div class="alert alert-warning mt-3 mb-3" role="alert">
{extension.securityTeamNotice}
</div>
</f:if>
<div itemscope itemtype="http://schema.org/SoftwareApplication">
<div hidden itemprop="applicationCategory">TYPO3 Extension</div>
<div hidden itemprop="applicationSuite">TYPO3</div>
<div hidden itemprop="downloadUrl">
<f:uri.action action="download" absolute="1" controller="Extension" arguments="{extension : extension, versionString : extension.lastVersion.versionString, format : 'zip'}" />
</div>
<div hidden itemprop="fileFormat">application/zip</div>
<div hidden itemprop="operatingSystem">Windows,Linux,MacOS</div>
<div class="row mt-3 align-items-center">
<div class="col-md-9">
<header>
<h1>
<terfe2:extensionIcon version="{extension.lastVersion}" alt="{extension.lastVersion.title}" class="ter-ext-icon" />
<span title="Extension title: {extension.lastVersion.title}" itemprop="alternateName">{extension.lastVersion.title}</span>
</h1>
<h2>
<small class="text-muted">
<span itemprop="name" title="Extension key: {extension.extKey}">{extension.extKey}</span> /
<span title="Stability: {extension.lastVersion.state}" class="ter-ext-state-{extension.lastVersion.state}">{extension.lastVersion.state}</span>
</small>
<f:render partial="Like" arguments="{hasLiked: hasLiked, extension: extension}" />
</h2>
</header>
</div>
<div class="col-md-3">
</div>
</div>
<div class="row mb-3">
<div class="col-md-8">
<p itemprop="description">{extension.lastVersion.description}</p>
<f:render partial="VersionSupportForTypo3" arguments="{version: extension.lastVersion, additionalVersions: extension.additionalSupportVersions}" />
<f:if condition="{extension.composerName}">
<p class="mb-3">
<h4>Composer support</h4>
<f:render partial="CopyToClipboard" arguments="{content: 'composer req {extension.composerName}', message: 'Composer require command is in your clipboard now'}" />
</p>
</f:if>
<f:if condition="{extension.tags}">
<h4>Tags</h4>
<p class="tags">
<f:for each="{extension.tags}" as="tag">
<f:link.page class="btn btn-outline-info mb-1" pageUid="{settings.pages.searchResultsPid}" additionalParams="{tx_solr: {filter: {0: 'tags:{tag.title}'}}}">
<strong>#{tag.title}</strong>
</f:link.page>
</f:for>
</p>
<div hidden itemprop="keywords">
<f:for each="{extension.tags}" as="tag" iteration="it">{tag.title}
<f:if condition="{it.isLast}">
<f:else>,</f:else>
</f:if>
</f:for>
</div>
</f:if>
<h4>
<f:translate key="last_upload_comment" />
</h4>
<p itemprop="releaseNotes">
<f:format.nl2br>{extension.lastVersion.uploadComment}</f:format.nl2br>
</p>
<f:if condition="{versionHistory->f:count()} >= 1}">
<h4>
<f:translate key="downloads_by_month" />
<i class="fas fa-info-circle" data-placement="right" data-toggle="tooltip" title="Packagist downloads are fetched during the day and are not up-to-date."></i>
</h4>
<p>
Total downloads (TER and Extension Manager) incl. November 2020: <f:format.number decimals="0" thousandsSeparator=",">{extension.oldDownloads}</f:format.number>
</p>
<script type="text/javascript">
var versionChartData = {extension.downloadsByTimeIntervalAsJson -> f:format.raw()};
var versionChartLastSync = "<f:format.date format='%B %d, %I:%M %P %Z'>{extension.lastDownloadSync}</f:format.date>";
</script>
<div id="versionChart">
</div>
</f:if>
</div>
<div class="col-md-4">
<f:if condition="{extension.lastVersion.reviewState} != -1">
<f:link.action rel="nofollow" class="btn btn-primary btn-block btn-lg" controller="Extension" action="download" arguments="{extension : extension, versionString : extension.lastVersion.versionString, format : 'zip'}">
<strong>Download
<span itemprop="softwareVersion">{extension.lastVersion.versionString}</span></strong>
</f:link.action>
</f:if>
<f:if condition="{extension.externalManual}">
<f:then>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.externalManual, icon: 'fa-book', label: 'External Manual', btnStyle: 'btn-info'}"/>
</f:then>
<f:else>
<f:render partial="ExternalButtonLink" arguments="{externalLink: documentationLink, icon: 'fa-book', label: 'Extension Manual', btnStyle: 'btn-info'}"/>
</f:else>
</f:if>
<f:security.ifAuthenticated>
<f:if condition="{notification}">
<f:then>
<button class="btn btn-success btn-block" data-rating-type="deactivateNotification" data-rating-extension="{extension.uid}" title="Do not notify me anymore of new extension releases">
<strong><i class="fa fa-bell"></i> Notify me via email on new releases</strong>
</button>
</f:then>
<f:else>
<button class="btn btn-danger btn-block" data-rating-type="activateNotification" data-rating-extension="{extension.uid}" title="Notify me on new extension releases">
<strong><i class="fa fa-bell-slash"></i> Notify me via email on new releases</strong>
</button>
</f:else>
</f:if>
</f:security.ifAuthenticated>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.forgeLink, icon: 'fa-hand-o-right', label: 'Found an Issue?'}"/>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.repositoryUrl, icon: 'fa-code-fork', label: 'Code Insights'}"/>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.packagistPage, icon: 'fa-cube', label: 'Packagist.org'}"/>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.paypalUrl, icon: 'fa-thumbs-o-up', label: 'Donate and Give Kudos'}"/>
<f:if condition="{extension.localizationStatus}">
<f:render partial="ExternalButtonLink" arguments="{externalLink: 'https://crowdin.com/project/{extension.crowdinKey}/invite', icon: 'fa-globe', label: 'Crowdin translations'}"/>
</f:if>
<div class="alert alert-secondary">
<f:render partial="ExtensionSingleInfo" arguments="{extension:extension, settings:settings, owner:owner, flattrUrl:flattrUrl, documentationLink:documentationLink, qualityLinkNotBroken:qualityLinkNotBroken, urlToQualityServer:urlToQualityServer}" />
</div>
</div>
</div>
<f:if condition="{extension.localizationStatus}">
<div class="mb-3" id="accordion" role="tablist" aria-multiselectable="true">
<div class="card bgWhite">
<div class="card-header" role="tab" id="headingLocalization">
<h4 class="mb-0">
<a data-toggle="collapse" data-parent="#accordion" href="#localization" aria-expanded="false" aria-controls="collapseOne">
Show localization Status
</a>
</h4>
</div>
<div id="localization" class="collapse" role="tabpanel" aria-labelledby="headingLocalization">
<div class="card-body">
<div class="container-fluid">
<div class="row">
<f:for each="{extension.localizationStatus}" as="localization" iteration="it">
<div class="col">
<h6 class="mb-0">
<a class="text-dark" href="https://crowdin.com/project/{extension.crowdinKey}/{localization.iso}" target="_blank" rel="noopener noreferrer">
{localization.name}: {localization.percentage}%
</a>
</h6>
<div class="progress mb-3">
<div class="progress-bar" role="progressbar" style="width: {localization.percentage}%;" aria-valuenow="{localization.percentage}" aria-valuemin="0" aria-valuemax="100">{localization.percentage}%</div>
</div>
</div>
<f:if condition="{it.cycle % 4} == 0">
</div>
<div class="row">
</f:if>
</f:for>
</div>
</div>
</div>
</div>
</div>
</div>
</f:if>
<f:render partial="InstallationHowTo" arguments="{_all}" />
<h3 id="version-history">
<f:translate key="version_history" />
</h3>
<div class="ter-ext-single-versionhistory ter-toggle-hide">
<f:render partial="ExtensionUploadHistoryList" arguments="{versionHistory: extension.reverseVersionsByVersionNumber, extension: extension}" />
</div>
<div class="socialshareprivacy"></div>
</div>
</f:then>
<f:else>
<div class="alert alert-warning">
No public version of this extension available.
</div>
</f:else>
</f:if>
<f:if condition="{extension}">
<f:then>
<f:security.ifHasRole role="{settings.reviewerGroupUid}">
<div class="alert alert-info mb-3" role="alert">
<div class="media">
<f:format.raw>
<tl:gravatar emailAddress="{owner.email}" size="125" />
</f:format.raw>
<div class="media-body ml-5">
<h4>Owner of this extension</h4>
<f:if condition="{owner}">
<f:then>
<p>
<strong>Name:</strong> {owner.name}<br>
<strong>Email:</strong>
<f:link.email email="{owner.email}">{owner.email}</f:link.email>
<br>
<strong>Username:</strong> {owner.username}
</p>
</f:then>
<f:else>
<p>
<strong>Username:</strong> {extension.frontendUser}
<small>User did not log in on typo3.org at least once.</small>
</p>
</f:else>
</f:if>
</div>
</div>
</div>
</f:security.ifHasRole>
<f:if condition="{extension.lastVersion.reviewState} == -2">
<div class="alert alert-warning mt-3 mb-3" role="alert">
<h4 class="alert-header">
<span class="ter-ext-state-beta"><i class="fa fa-exclamation-triangle"></i></span>
Outdated extension<br>
<small class="text-muted">This extension supports none of the currently supported TYPO3 core version</small>
</h4>
</div>
</f:if>
<f:if condition="{owner.username} == 'abandoned_extensions'">
<div class="alert alert-danger mt-3 mb-3" role="alert">
<h4 class="alert-header">
<span class="ter-ext-state-beta"><i class="fa fa-exclamation-triangle"></i></span>
Abandoned extension<br>
<small class="text-muted">This extension has no maintainer at the moment. If you like to become the maintainer of this extension, fill out our
<f:link.page pageUid="{settings.pages.abandonedExtensionFormPid}">registration form</f:link.page>
.</small>
</h4>
</div>
</f:if>
<f:if condition="{extension.securityTeamNotice}">
<div class="alert alert-warning mt-3 mb-3" role="alert">
{extension.securityTeamNotice}
</div>
</f:if>
<div itemscope itemtype="http://schema.org/SoftwareApplication">
<div hidden itemprop="applicationCategory">TYPO3 Extension</div>
<div hidden itemprop="applicationSuite">TYPO3</div>
<div hidden itemprop="downloadUrl">
<f:uri.action action="download" absolute="1" controller="Extension" arguments="{extension : extension, versionString : extension.lastVersion.versionString, format : 'zip'}" />
</div>
<div hidden itemprop="fileFormat">application/zip</div>
<div hidden itemprop="operatingSystem">Windows,Linux,MacOS</div>
<div class="row mt-3 align-items-center">
<div class="col-md-9">
<header>
<h1>
<terfe2:extensionIcon version="{extension.lastVersion}" alt="{extension.lastVersion.title}" class="ter-ext-icon" />
<span title="Extension title: {extension.lastVersion.title}" itemprop="alternateName">{extension.lastVersion.title}</span>
</h1>
<h2>
<small class="text-muted">
<span itemprop="name" title="Extension key: {extension.extKey}">{extension.extKey}</span> /
<span title="Stability: {extension.lastVersion.state}" class="ter-ext-state-{extension.lastVersion.state}">{extension.lastVersion.state}</span>
</small>
<f:render partial="Like" arguments="{hasLiked: hasLiked, extension: extension}" />
</h2>
</header>
</div>
<div class="col-md-3">
</div>
</div>
<div class="row mb-3">
<div class="col-md-8">
<p itemprop="description">{extension.lastVersion.description}</p>
<f:render partial="VersionSupportForTypo3" arguments="{version: extension.lastVersion, additionalVersions: extension.additionalSupportVersions}" />
<f:if condition="{extension.composerName}">
<p class="mb-3">
<h4>Composer support</h4>
<f:render partial="CopyToClipboard" arguments="{content: 'composer req {extension.composerName}', message: 'Composer require command is in your clipboard now'}" />
</p>
</f:if>
<f:if condition="{extension.tags}">
<h4>Tags</h4>
<p class="tags">
<f:for each="{extension.tags}" as="tag">
<f:link.page class="btn btn-outline-info mb-1" pageUid="{settings.pages.searchResultsPid}" additionalParams="{tx_solr: {filter: {0: 'tags:{tag.title}'}}}">
<strong>#{tag.title}</strong>
</f:link.page>
</f:for>
</p>
<div hidden itemprop="keywords">
<f:for each="{extension.tags}" as="tag" iteration="it">{tag.title}
<f:if condition="{it.isLast}">
<f:else>,</f:else>
</f:if>
</f:for>
</div>
</f:if>
<h4>
<f:translate key="last_upload_comment" />
</h4>
<p itemprop="releaseNotes">
<f:format.nl2br>{extension.lastVersion.uploadComment}</f:format.nl2br>
</p>
<f:if condition="{versionHistory->f:count()} >= 1}">
<h4>
<f:translate key="downloads_by_month" />
<i class="fas fa-info-circle" data-placement="right" data-toggle="tooltip" title="Packagist downloads are fetched during the day and are not up-to-date."></i>
</h4>
<p>
Total downloads (TER and Extension Manager) incl. November 2020:
<f:format.number decimals="0" thousandsSeparator=",">{extension.oldDownloads}</f:format.number>
</p>
<script type="text/javascript">
var versionChartData = {extension.downloadsByTimeIntervalAsJson -> f:format.raw()};
var versionChartLastSync = "<f:format.date format='%B %d, %I:%M %P %Z'>{extension.lastDownloadSync}</f:format.date>";
</script>
<div id="versionChart">
</div>
</f:if>
</div>
<div class="col-md-4">
<f:if condition="{extension.lastVersion.reviewState} != -1">
<f:link.action rel="nofollow" class="btn btn-primary btn-block btn-lg" controller="Extension" action="download" arguments="{extension : extension, versionString : extension.lastVersion.versionString, format : 'zip'}">
<strong>Download
<span itemprop="softwareVersion">{extension.lastVersion.versionString}</span></strong>
</f:link.action>
</f:if>
<f:if condition="{extension.externalManual}">
<f:then>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.externalManual, icon: 'fa-book', label: 'External Manual', btnStyle: 'btn-info'}" />
</f:then>
<f:else>
<f:render partial="ExternalButtonLink" arguments="{externalLink: documentationLink, icon: 'fa-book', label: 'Extension Manual', btnStyle: 'btn-info'}" />
</f:else>
</f:if>
<f:security.ifAuthenticated>
<f:if condition="{notification}">
<f:then>
<button class="btn btn-success btn-block" data-rating-type="deactivateNotification" data-rating-extension="{extension.uid}" title="Do not notify me anymore of new extension releases">
<strong><i class="fa fa-bell"></i> Notify me via email on new releases</strong>
</button>
</f:then>
<f:else>
<button class="btn btn-danger btn-block" data-rating-type="activateNotification" data-rating-extension="{extension.uid}" title="Notify me on new extension releases">
<strong><i class="fa fa-bell-slash"></i> Notify me via email on new releases</strong>
</button>
</f:else>
</f:if>
</f:security.ifAuthenticated>
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.forgeLink, icon: 'fa-hand-o-right', label: 'Found an Issue?'}" />
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.repositoryUrl, icon: 'fa-code-fork', label: 'Code Insights'}" />
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.packagistPage, icon: 'fa-cube', label: 'Packagist.org'}" />
<f:render partial="ExternalButtonLink" arguments="{externalLink: extension.paypalUrl, icon: 'fa-thumbs-o-up', label: 'Donate and Give Kudos'}" />
<f:if condition="{extension.localizationStatus}">
<f:render partial="ExternalButtonLink" arguments="{externalLink: 'https://crowdin.com/project/{extension.crowdinKey}/invite', icon: 'fa-globe', label: 'Crowdin translations'}" />
</f:if>
<div class="alert alert-secondary">
<f:render partial="ExtensionSingleInfo" arguments="{extension:extension, settings:settings, owner:owner, flattrUrl:flattrUrl, documentationLink:documentationLink, qualityLinkNotBroken:qualityLinkNotBroken, urlToQualityServer:urlToQualityServer}" />
</div>
</div>
</div>
<f:if condition="{extension.localizationStatus}">
<div class="mb-3" id="accordion" role="tablist" aria-multiselectable="true">
<div class="card bgWhite">
<div class="card-header" role="tab" id="headingLocalization">
<h4 class="mb-0">
<a data-toggle="collapse" data-parent="#accordion" href="#localization" aria-expanded="false" aria-controls="collapseOne">
Show localization Status
</a>
</h4>
</div>
<div id="localization" class="collapse" role="tabpanel" aria-labelledby="headingLocalization">
<div class="card-body">
<div class="container-fluid">
<div class="row">
<f:for each="{extension.localizationStatus}" as="localization" iteration="it">
<div class="col">
<h6 class="mb-0">
<a class="text-dark" href="https://crowdin.com/project/{extension.crowdinKey}/{localization.iso}" target="_blank" rel="noopener noreferrer">
{localization.name}: {localization.percentage}%
</a>
</h6>
<div class="progress mb-3">
<div class="progress-bar" role="progressbar" style="width: {localization.percentage}%;" aria-valuenow="{localization.percentage}" aria-valuemin="0" aria-valuemax="100">{localization.percentage}%</div>
</div>
</div>
<f:if condition="{it.cycle % 4} == 0">
</div>
<div class="row">
</f:if>
</f:for>
</div>
</div>
</div>