From 08ec2614457a7364442e8647d1ec594bf9b2bb41 Mon Sep 17 00:00:00 2001
From: bnjmnm <bnjmnm@2369194.no-reply.drupal.org>
Date: Fri, 15 Jul 2022 20:15:18 +0000
Subject: [PATCH] Issue #3294608 by bnjmnm, fjgarlin, drumm: Consider replacing
 "actively maintained" icon

---
 images/blue-security-shield-icon.svg          |   2 +-
 images/green-maintained-wrench-icon.svg       |  13 ---
 images/triangle-alert.svg                     |   5 +
 .../ProjectBrowserSource/MockDrupalDotOrg.php |  93 +++++++++++++++---
 src/ProjectBrowser/Project.php                |  10 ++
 src/ProjectBrowser/ProjectInterface.php       |   8 ++
 sveltejs/public/build/bundle.css              | Bin 9710 -> 9997 bytes
 sveltejs/public/build/bundle.js               | Bin 68810 -> 69412 bytes
 sveltejs/public/build/bundle.js.map           | Bin 201236 -> 202009 bytes
 sveltejs/src/Project/Project.svelte           |  75 ++++++++++++--
 sveltejs/src/Search.svelte                    |   9 --
 .../ProjectBrowserUiTest.php                  |   8 +-
 12 files changed, 175 insertions(+), 48 deletions(-)
 delete mode 100644 images/green-maintained-wrench-icon.svg
 create mode 100644 images/triangle-alert.svg

diff --git a/images/blue-security-shield-icon.svg b/images/blue-security-shield-icon.svg
index 1814c71ab..baec22229 100644
--- a/images/blue-security-shield-icon.svg
+++ b/images/blue-security-shield-icon.svg
@@ -1,4 +1,4 @@
-<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
+<svg width="31" height="31" viewBox="3 4 25 23" fill="none" xmlns="http://www.w3.org/2000/svg">
     <path d="M24.2438 7.82616L16.2878 4.41643C15.7847 4.20081 15.2152 4.20081 14.7121 4.41643L6.75603 7.82616C5.9368 8.17726 5.44876 9.0281 5.55932 9.91252L6.46397 17.1497C6.68412 18.9109 7.56509 20.5228 8.92862 21.659L14.2196 26.0681C14.9612 26.6862 16.0386 26.6862 16.7803 26.0681L22.0712 21.659C23.4347 20.5228 24.3157 18.9109 24.5359 17.1497L25.4405 9.91252C25.5511 9.0281 25.063 8.17726 24.2438 7.82616Z" stroke="#003CC5" stroke-width="2" stroke-linecap="round"/>
     <path d="M11.6251 15.5L15.0688 18.9437C15.2919 19.1668 15.6634 19.1301 15.8384 18.8675L20.6667 11.625" stroke="#003CC5" stroke-width="2" stroke-linecap="round"/>
 </svg>
\ No newline at end of file
diff --git a/images/green-maintained-wrench-icon.svg b/images/green-maintained-wrench-icon.svg
deleted file mode 100644
index d584928f1..000000000
--- a/images/green-maintained-wrench-icon.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<svg width="31" height="31" viewBox="-5 -3 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M19.7856 4.65941L11.8295 1.24968C11.3264 1.03406 10.7569 1.03406 10.2538 1.24968L2.29778 4.65941C1.47855 5.01051 0.990512 5.86135 1.10106 6.74577L2.00572 13.983C2.22587 15.7442 3.10684 17.356 4.47037 18.4923L9.7613 22.9014C10.503 23.5195 11.5803 23.5195 12.322 22.9014L17.613 18.4923C18.9765 17.356 19.8575 15.7442 20.0776 13.983L20.9823 6.74577C21.0928 5.86135 20.6048 5.01051 19.7856 4.65941Z"
-          stroke="#71B43D" stroke-width="2" stroke-linecap="round"/>
-    <path d="M8.11154 10.9899L7.34928 11.6372L7.88422 12.2672L8.60366 11.8605L8.11154 10.9899ZM11.6207 9.00614L12.1129 9.87667L12.8539 9.45775L12.5587 8.65935L11.6207 9.00614ZM13.2092 18.0066C13.4792 18.4883 14.0887 18.6599 14.5704 18.3899C15.0522 18.1198 15.2238 17.5104 14.9537 17.0286L13.2092 18.0066ZM5.57218 7.16219C5.40773 7.64804 5.34215 8.11967 5.39734 8.58696C5.45262 9.05488 5.62146 9.45431 5.8345 9.80386C6.04064 10.1421 6.3001 10.4518 6.54986 10.7351C6.81997 11.0414 7.06271 11.2997 7.34928 11.6372L8.87379 10.3427C8.57775 9.99404 8.26679 9.65818 8.04992 9.41225C7.81271 9.14325 7.6519 8.94281 7.54229 8.76297C7.43959 8.59446 7.3972 8.468 7.38353 8.35234C7.3698 8.23605 7.37758 8.06639 7.4666 7.80341L5.57218 7.16219ZM12.5587 8.65935C12.4279 8.3056 12.3295 7.96118 12.2183 7.58989C12.1136 7.24024 11.9914 6.84446 11.8225 6.48856C11.65 6.12512 11.3978 5.73392 10.9911 5.43968C10.5709 5.13569 10.0753 4.99929 9.53106 4.99669L9.52149 6.99666C9.72727 6.99765 9.79608 7.04363 9.81883 7.06008C9.85505 7.08629 9.92429 7.15361 10.0156 7.34604C10.1105 7.54602 10.1945 7.80345 10.3024 8.16366C10.4038 8.50222 10.5243 8.92429 10.6828 9.35293L12.5587 8.65935ZM8.60366 11.8605L10.3583 10.8686L9.37402 9.12752L7.61942 10.1194L8.60366 11.8605ZM10.3583 10.8686L12.1129 9.87667L11.1286 8.13561L9.37402 9.12752L10.3583 10.8686ZM8.99385 10.487L13.2092 18.0066L14.9537 17.0286L10.7384 9.50906L8.99385 10.487Z"
-          fill="#71B43D"/>
-    <mask id="path-3-inside-1_137_4246" fill="white">
-        <path d="M14 16.5C14 16.7761 13.7761 17 13.5 17C13.2239 17 13 16.7761 13 16.5C13 16.2239 13.2239 16 13.5 16C13.7761 16 14 16.2239 14 16.5Z"/>
-    </mask>
-    <path d="M14 16.5C14 16.7761 13.7761 17 13.5 17C13.2239 17 13 16.7761 13 16.5C13 16.2239 13.2239 16 13.5 16C13.7761 16 14 16.2239 14 16.5Z"
-          fill="white"/>
-    <path d="M13 16.5C13 16.2239 13.2239 16 13.5 16V18C14.3284 18 15 17.3284 15 16.5H13ZM13.5 16C13.7761 16 14 16.2239 14 16.5H12C12 17.3284 12.6716 18 13.5 18V16ZM14 16.5C14 16.7761 13.7761 17 13.5 17V15C12.6716 15 12 15.6716 12 16.5H14ZM13.5 17C13.2239 17 13 16.7761 13 16.5H15C15 15.6716 14.3284 15 13.5 15V17Z"
-          fill="#71B43D" mask="url(#path-3-inside-1_137_4246)"/>
-</svg>
\ No newline at end of file
diff --git a/images/triangle-alert.svg b/images/triangle-alert.svg
new file mode 100644
index 000000000..a8c0d3a0b
--- /dev/null
+++ b/images/triangle-alert.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg width="22.610355mm" height="25.777401mm" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <g><path d="M 40 8 L 5 70 L 75 70 L 40 8 Z" style="stroke-width: 10; stroke-linejoin: round; stroke: rgb(255, 124, 58); fill: rgb(255, 124, 58);"/></g>
+  <g><path d="m 43.957714,33.658331 q 0,4.98 -0.36,9.12 -0.36,4.14 -0.9,8.34 l -5.16,0 q -0.6,-4.2 -0.96,-8.34 -0.36,-4.2 -0.36,-9.12 l 0,-11.16 7.74,0 0,11.16 z m 0.9,26.46 q 0,2.1 -1.38,3.42 -1.38,1.32 -3.42,1.32 -1.98,0 -3.42,-1.32 -1.38,-1.32 -1.38,-3.42 0,-2.1 1.38,-3.42 1.44,-1.38 3.42,-1.38 2.04,0 3.42,1.38 1.38,1.32 1.38,3.42 z" style="fill: rgb(255, 255, 255);"/></g>
+</svg>
diff --git a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
index f0bb4187d..416831f83 100644
--- a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
+++ b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
@@ -4,6 +4,7 @@ namespace Drupal\project_browser\Plugin\ProjectBrowserSource;
 
 use Composer\Semver\Semver;
 use Drupal\Component\Serialization\Json;
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\State\StateInterface;
@@ -79,6 +80,13 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
    */
   protected $state;
 
+  /**
+   * ProjectBrowser cache bin.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBin;
+
   /**
    * Constructs a MockDrupalDotOrg object.
    *
@@ -96,13 +104,16 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
    *   A Guzzle client object.
    * @param \Drupal\Core\State\StateInterface $state
    *   The state object.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_bin
+   *   The cache bin.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, Connection $database, ClientInterface $http_client, StateInterface $state) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, Connection $database, ClientInterface $http_client, StateInterface $state, CacheBackendInterface $cache_bin) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->logger = $logger;
     $this->database = $database;
     $this->httpClient = $http_client;
     $this->state = $state;
+    $this->cacheBin = $cache_bin;
   }
 
   /**
@@ -117,32 +128,55 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
       $container->get('database'),
       $container->get('http_client'),
       $container->get('state'),
+      $container->get('cache.project_browser'),
     );
   }
 
+  /**
+   * Gets status vocabulary info from the Drupal.org json endpoint.
+   *
+   * @param int $taxonomy_id
+   *   The id of the taxonomy being retrieved.
+   *
+   * @return array|array[]
+   *   An array with the term id, name and description.
+   *
+   * @throws \GuzzleHttp\Exception\GuzzleException
+   *   Thrown if request is unsuccessful.
+   */
+  protected function getStatuses(int $taxonomy_id) {
+    $cached_statuses = $this->cacheBin->get("MockDrupalDotOrg:taxonomy_$taxonomy_id");
+    if ($cached_statuses) {
+      return $cached_statuses->data;
+    }
+    $url = "https://www.drupal.org/api-d7/taxonomy_term.json?vocabulary=$taxonomy_id";
+    $response = \Drupal::httpClient()->request('GET', $url);
+    if ($response->getStatusCode() !== 200) {
+      throw new \RuntimeException("Request to $url failed, returned {$response->getStatusCode()} with reason: {$response->getReasonPhrase()}");
+    }
+    $body = Json::decode($response->getBody()->getContents());
+    $list = $body['list'];
+    $list = array_map(function ($item) {
+      $item['id'] = $item['tid'];
+      return array_intersect_key($item, array_flip(['id', 'name', 'description']));
+    }, $list);
+    $this->cacheBin->set("MockDrupalDotOrg:taxonomy_$taxonomy_id", $list);
+
+    return $list;
+  }
+
   /**
    * {@inheritdoc}
    */
   public function getDevelopmentStatuses(): array {
-    return [
-      ['id' => 9988, 'name' => 'Active'],
-      ['id' => 13030, 'name' => 'Maintenance Only'],
-      ['id' => 16538, 'name' => 'No Further Development'],
-      ['id' => 9994, 'name' => 'Obsolete'],
-    ];
+    return $this->getStatuses(46);
   }
 
   /**
    * {@inheritdoc}
    */
   public function getMaintenanceStatuses(): array {
-    return [
-      ['id' => 13028, 'name' => 'Actively maintained'],
-      ['id' => 19370, 'name' => 'Minimal'],
-      ['id' => 9990, 'name' => 'Seeking co-maintainer'],
-      ['id' => 9992, 'name' => 'Seeking new maintainer'],
-      ['id' => 13032, 'name' => 'Unsupported'],
-    ];
+    return $this->getStatuses(44);
   }
 
   /**
@@ -394,6 +428,7 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
         $project['is_covered'] = $this->projectIsCovered($project);
         $project['is_active'] = $this->projectIsActive($project);
         $project['is_maintained'] = $this->projectIsMaintained($project);
+        $project['warnings'] = $this->getWarnings($project);
 
         $returned_list[] = new Project($project);
       }
@@ -792,4 +827,34 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
     return FALSE;
   }
 
+  /**
+   * Determines warning messages based on development and maintenance status.
+   *
+   * @param $project
+   *   A project array.
+   *
+   * @return string[]
+   *   An array of warning messages.
+   */
+  protected function getWarnings($project) {
+    // This is based on logic from Drupal.org.
+    // @see https://git.drupalcode.org/project/drupalorg/-/blob/e31465608d1380345834/drupalorg_project/drupalorg_project.module
+    $warnings = [];
+    $merged_vocabularies = array_merge($this->getDevelopmentStatuses(), $this->getMaintenanceStatuses());
+    $statuses = array_column($merged_vocabularies, 'description', 'id');
+    foreach (['taxonomy_vocabulary_44', 'taxonomy_vocabulary_46'] as $field) {
+      // Maintenance status is not Actively maintained and Development status is
+      // not Under active development.
+      $id = $project[$field]['id'] ?? FALSE;
+      if ($id && !in_array($id, [13028, 9988])) {
+        // Maintenance status is Abandoned, or Development status is No further
+        // development or Obsolete.
+        if (in_array($id, [13032, 16538, 9994])) {
+          $warnings[] = $statuses[$id];
+        }
+      }
+    }
+    return $warnings;
+  }
+
 }
diff --git a/src/ProjectBrowser/Project.php b/src/ProjectBrowser/Project.php
index f6c822526..215bcb44c 100644
--- a/src/ProjectBrowser/Project.php
+++ b/src/ProjectBrowser/Project.php
@@ -49,6 +49,9 @@ class Project implements ProjectInterface {
       $this->setIsCovered($project['is_covered']);
       $this->setIsMaintained($project['is_maintained']);
       $this->setIsCompatible($project['is_compatible']);
+      if (isset($project['warnings'])) {
+        $this->setWarnings($project['warnings']);
+      }
     }
   }
 
@@ -212,4 +215,11 @@ class Project implements ProjectInterface {
     $this->is_compatible = $compatible;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setWarnings(array $warnings) {
+    $this->warnings = $warnings;
+  }
+
 }
diff --git a/src/ProjectBrowser/ProjectInterface.php b/src/ProjectBrowser/ProjectInterface.php
index 9aef6c486..30c2a24ac 100644
--- a/src/ProjectBrowser/ProjectInterface.php
+++ b/src/ProjectBrowser/ProjectInterface.php
@@ -129,4 +129,12 @@ interface ProjectInterface {
    */
   public function setIsCompatible(bool $compatible);
 
+  /**
+   * Warnings related to installing a given module.
+   *
+   * @param string[] $warnings
+   *   Warnings about the module to present the the user.
+   */
+  public function setWarnings(array $warnings);
+
 }
diff --git a/sveltejs/public/build/bundle.css b/sveltejs/public/build/bundle.css
index 01dafd4fb5ddd44f5070fd8b71a403b6745f35f5..2f565a4f6c9d2675d44df3243c1ab2cf5cc16dca 100644
GIT binary patch
delta 989
zcmaJ=%}T>S5I%VDCT-I;NEHILAe1akw5U-;1VIo~^dw%~wlP`l?uKNGt)=(?;wfhj
z;tL4k&5LJWz>D|-;xm|<pWPbkDU<J;nQ!LH<ZI_<_u($%zs!~W_l#8N>b7>?RRE(7
za2jg(>Q81jGhrxz$JyyI>DSr4boys@d+m3R{sn3=+gMzw3_u%s3^*=5Gn>IPmgbSq
za!2W|AGz5yI3+zv(IWPO(BVz0<{}&lu$-U&3qH&*7o%)|AtFM>ef}h&8no3Ds8V4@
zj;m0U5>Mdv3riaz$HD$7B~10urD<q&ryKha@2>!ap=I)X|FuvmB?3cELD&-biDG#@
z=10el+}3Kf#O_39#3#F0PMdmL?2KKkDp#(4Evq>F7V?}n>=}r(5itiK%(#FqLBtY#
z;ln2-jW%tbkFQWX2MXU~Sa$FuH9R5}F%$n&J7QO&7!J7_9;60z*TnuyUcwE^7{!KK
z1A0Bc1|U6u6~HD#l=LxlEksloSvE5?6R$=<)FVI!AUFvY9Fyttt$E~Ad3h`T2~>|k
b*74M#Z9IiX(dX2`B3BBfYoPx4EbCL>E%aCp

delta 720
zcmeD6d*{7jp@2kYZn|DUlCGgqWkE_(esy_fN=b&5acb`51p?;chK5BY8AiqmdL{V<
zU{w(Q<TnCNLQpwmVLL%doLX`O{jq5|E$HEZX%f^KCYGwTdO4ZJC3*!#`B|ySKpN;$
zh#QcYll_Gvv6;O?NEVy$Q=zS*Fpp&zBb&2USX%@plZY(wN>~HCgo=oT1WbosZenI0
zvaTYL1cYQ#eo7^><O>lQNx06;<ovv1WLarZ^LnT(l5lleeqM=gab{Jjm7!@t1uSgf
zVWOaynVXoNimam`F(oB4FI_h$HLb+TBsCY89tBuBfP`*!ZsO$Aq6!G-qx+3fOcX~T
zYKkdi3-uVWQat{BA*L!141*G22&NVxxgUu+IZnbt6za;#jGS`QQdnZ$d|SeZ4*;OJ
B<VOGi

diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js
index dae643346047efcf48e660a8cbf6310321852382..1e46d70904e370ed15b990a15853a5328a004ca2 100644
GIT binary patch
delta 4154
zcmbVP4RBP|6`uQ8LitNjNP>c7cMZ??^ll~_3gU)G2r+~}U=u<TNL<p*?n`#V=I-*|
z+iXG-!i=3MMJkqqf?|bsKt~+~`#>Dg)|pO6+m0Yo6_xr^MFA~>wAQ~2J@38E^5W=}
zadu|+-gD1A_uTJ%=bXELz2C9-A;*En09g;UYXWdEu#2qFYu7j(AO$yqGwgzQf;IR%
z4W8+k0KMgtVX$^Aw@icbm)+1LFD8p&kNj)00@jE4(J77Rm+67_EJBvS6JZ}b)UlLX
zq(RQCJe@@LWtqx1=||bETzcqPH+^F6Vr@W5MOiqWDs4})kQR+I=MsI*hQEyE%C#M?
z@x<d;qR~aryh$dF@WQ4(vJi4M&w%D%9wb#bdKTx$?sCB=n?Jz$vAc}*rCYAUvAo4i
z8uU$DN;s}5ZK*+{jqtC#SCFN!VC!5`2M1cF7SNDX?2;t8#T(%HRhl4+G-=M}OS5@?
zH5}OLb;wLu3a7U&&efP8(k^q^+%pUJFn!@Ymk3z}bGFr!)v$S63)=r^n}^6S1v-cZ
z{ZL1keiWPpdbVBzm$zq=Civ}x1>m_iYK`=#?|qxV`R(K3{at%VGvw4?Z!YBNZ08gX
z=IxvaTOTNNG^2O$&bxWAX152vexRId)<D{IKOXVMuE#jW;KKGB%zc~FW|#iveL@uQ
z?9O-8UD?~n)mi)EdhMPR$HkZhbevHIzW!b5V30|&G+*db;wjZPmzM`bs@g?z%d7y(
zLUx&@mb8n~YGJ#~QcKuHX|;}wY*pse_KXZgX}vDH%rY0Ti_&U^cA2Ht1{3y9Cn*s3
zdPxFW_CAmuW}+`5VCYK%B2B=keRZT88utB>^g`~#`?*#IPqpMj&ZR8);9;J$>Ej+b
zL&ijyNb98@nDodDQVvre-A~H(mmZx-vfFW?T59QKw42cseDl~kl7z0uokZ2|e%#4r
z7urv2gXf-@o!w;@Mc^`uI_#o$Soq|T?2uiQgz@_y%nsT`DjeGXI_c539N068t4~L>
zhtXDOJ=jFn!r_CTbDah|@?iP#ad7N#m45Bhy9hjbD5~c@vzw6HVZqBcz{!KR!nkLX
zWF>5T_E)3=vXA)2b~4{uCW^k>q3+0sq*|Zz+>uG#I_n5$HKQvTRcT*BiFe8&%~w1i
z4h&@SxE8Jcp47mlm&?g=n0?gc@H1Z}G#@=gD$ON7{OQ=jYgaI;MP%k%#%L(0g(AN7
zkpD^n7KvM5*@fRHUI}r_hn+tMbB^ag)vMpa+*fm8X!}^a&~z9&F$OlAIE<CZIB*f1
z{k!C-VZK_p`L#czg$u83Bun&luM>h3^$izSo`z@%!^-mF8x=W=na%A+2FJ^8gn1_?
zV=bvYIp0ymghqYa$*%}jpe=7Xv9>(%)?@g+>g_|M0kThtqZcwk6JgR#`A~mqH}1N0
zssS^w`X9%+rRk%Z7;S)M@66%qGS<0O2E8UztR}^QqEt{}QP!dI{D^5$OlBQgWPUN0
zBCI{|hr>4%M7uiNB8^EkSc`%@A1n&tASAFNRS8i=g5uM;u;B1SC^}8yMY$B_y<0ci
z3Xn(Df?87bL_=6+6K$TJl8AS04;ou8Xw)5w1y$8e-I)a-wby6SwYV08Nga8&F|lui
zb3f#3S+Fcvln`W!Bo%_vY6ee+1!(Z+K!H5Ph4;|JzL=~zS*apxNrm;{1^Sq@PH?MT
z!B`B+nrFft?~ZpOEu5+xN-9yU*BK0VN7cB}>rBLB(NM2j9QYa3y~#r&G<I4u!?JCf
z)F|e?9Z$H&d(&uH5fJz1ym!u+&BrapSZRA)5x%S3P*Ok`Buff7S%LdXQpV3yExDu7
z=1&9QoizoMXM$)2AZ`XAmT?<l44R_v4@I`7M1!m&CVPT0S<&D<*?l|W&TP2<=pfaa
zL5ho3tdJW;QO0Ct@yca2l4=Tr;s7-wg2;P$Y}G2ZFF`f0b&K0A^1Lu&54d(t<22*$
z`Fi(<bI42^Q*0hbyMi6^6(Yfj)7~*@O}N-T9fXg5&M^fh)n7-}f#*yC_!^{Zd4{Jc
zMd_MhL<`DSMmgmw_2Q;iW>NmzM^h(R(bl3`Ova1QdT@Q3`;$^dVsgq^4pTl}fX%q!
zWB*{OLAXv!JF2#Z;@z?$hq2=u)S(sD!v9Y~lWADx-8E5Fb4$bT22C-nR7!$t1o`^=
zi%pf{KvgEe(;1DBGjo&wYL8Y7b$lS?LF1WP(gTm2nb+FM^POh;7n;ng;Q0!3+Gb9x
zdA{15rp#$2&sUn$ZgcAAdA~UwK|(E_um#kA)}3Vq6nf6CF0`>AafP>S@i1oPa5aJd
zo-G>QL(g4Tli9OKh*0-1!frYOs2{2%BP&xc+;OgO*!C~Z#YjE;``nEY6NE0b&XTd1
zkpmH0h#@tw7%6ST{%k-pQkufR8OYeB8toRT2~ZbSKg(__?73|Uq&}&-xwXIF%QK8%
zkC8<^BPodD^+JgDh*WU3!kJHQ&dI3P8|nr_XQJm-pROIWK)6E3vd*&3OyTU5EN)r>
z?lk=?#8!;g6*`N;f>)%SlI7Y}VOdsDR*^AYAt93o4d$B^u#jp)>j;}rVOg(8TOYyo
zvg)$xjFoE1BEH2k3)mDIL6n7R%SiS2yJ$YS2knJAofl;o3!z=cj#elOmDGzLK@g?+
zeU+x60TJKdxGMT8%Y@3bBNU^-^AS@YR|HhG^3l9oT25^d7OfqFKaP@Dhqk{=@PF4o
zKlK{^`6V-#X}y(y=%BGc{TSjj*+d_bF9QRvW?=A-gux(Le}9Q_|6YS#W{S9ma}K#^
z@9e$U1foW#Y=sRM^I_Ea{Be~cHI5hYtw_Ep0QG!+5Jd>RA(C9)0U9<o$UMIiyEa`b
z3yS&Oj^s0HPZ$GbW|}d;up3cN=aVroQwRqGw)a&G#(q`~JAK!{>+j`4$7d@^aEEg|
zDRRUV8i(_r37o2!FR;=J(@7LoU6?_Fdg?-$kPw`@*i@imY-tQT8C9jAz{Jvckt%C~
zN|hEZAVu{RpWiqtE2>b9s?hhJ10<=_FP3xg_QjlWG+xTmodLc~Ig)4zwmv=;o?kN+
z=6{vI=lcGy?i-a*1i0hE_0ab9KVkisIiB<@#c6GgF~NB6CRATv0=pmnEh!FAV^kkq
zFZ&Ahjo(OwYqy$NJ-qqtEfXV(Z5^%|noK(2g`r!Zc4!R#55bXxV%3G`hl<A2u-qO@
z#x!3`NPl5y3E{d-L-57i8BlR~8fn*CFV7@-5k)Gdg^J`Yx$>{MO@Y=8`G=k&`$mz%
z!%YtIQWi;>z%FQ0XqRHV(+A8_Mq4~y>hT8bZ}n~^^C+Xr$P1?w@IJhG$J7aAA?e=H
IGJ(YZ2cg`Ql>h($

delta 3563
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
B6&C;i

diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map
index 1165afb6ee55e6f3c8e4cc48586e706f9b1e83e7..cade11f98b3d0b3ba07c685efcbcf3d768419e94 100644
GIT binary patch
delta 11189
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
Ku?roz<G%s3su2eO

delta 9869
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

diff --git a/sveltejs/src/Project/Project.svelte b/sveltejs/src/Project/Project.svelte
index b06ae3636..11e389564 100644
--- a/sveltejs/src/Project/Project.svelte
+++ b/sveltejs/src/Project/Project.svelte
@@ -5,10 +5,8 @@
   import ActionButton from './ActionButton.svelte';
   import Image from './Image.svelte';
   import Categories from './Categories.svelte';
-  import SecurityCoverage from './SecurityCoverage.svelte';
-  import MaintainedIcon from './MaintainedIcon.svelte';
 
-  const { drupalSettings } = window;
+  const { drupalSettings, Drupal } = window;
 </script>
 
 <li class="project {toggleView.toLowerCase()}">
@@ -32,9 +30,39 @@
       />
     </div>
   </div>
-  <div class="icons">
-    <SecurityCoverage coverage={project.is_covered} />
-    <MaintainedIcon maintained={project.is_maintained} />
+  <div
+    class="icons"
+    class:warnings={project.warnings && project.warnings.length > 0}
+  >
+    {#if project.is_covered}
+      <span>
+        <img
+          src="{drupalSettings.project_browser.origin_url}/{drupalSettings
+            .project_browser.module_path}/images/blue-security-shield-icon.svg"
+          alt=""
+          title={Drupal.t('Covered by Drupal Security Team')}
+          class="project-status-icon"
+        />
+        <!-- Show the security policy description if it is accompanied by warnings,
+             since those also have descriptions.  -->
+        {#if project.warnings && project.warnings.length > 0}
+          <small>{Drupal.t('Covered by the security advisory policy')}</small>
+        {/if}
+      </span>
+    {/if}
+    {#if project.warnings && project.warnings.length > 0}
+      {#each project.warnings as warning}
+        <span>
+          <img
+            src="{drupalSettings.project_browser.origin_url}/{drupalSettings
+              .project_browser.module_path}/images/triangle-alert.svg"
+            alt=""
+            class="project-status-icon"
+          />
+          <small>{@html warning}</small>
+        </span>
+      {/each}
+    {/if}
     {#if toggleView === 'List'}
       <div class="container">
         <div class="image">
@@ -49,8 +77,17 @@
         </div>
       </div>
     {/if}
-    <ActionButton {project} />
+    <!--If there are no warnings, there is space to include the action button
+        in the icons container -->
+    {#if !project.warnings || project.warnings.length === 0}
+      <ActionButton {project} />
+    {/if}
   </div>
+  <!--If there are warnings, the action button needs to be moved out of the
+      icons container to provide space for the warning descriptions. -->
+  {#if project.warnings && project.warnings.length > 0}
+    <ActionButton {project} />
+  {/if}
 </li>
 
 <style>
@@ -163,9 +200,33 @@
     display: flex;
     padding: 1em 1em 1em 0;
   }
+  .icons :global(p) {
+    display: inline;
+  }
+  .icons.warnings {
+    display: block;
+  }
+  .icons.warnings span {
+    display: list-item;
+  }
+  .icons.warnings img {
+    display: inline;
+    width: 1.2rem;
+    position: relative;
+    bottom: -0.25rem;
+  }
+  .warnings + :global(.action) {
+    margin-right: 1em;
+    margin-bottom: 1em;
+  }
   .container {
     display: flex;
     align-items: center;
     justify-content: center;
   }
+  .project-status-icon {
+    width: 2.4em;
+    margin-right: 0.5em;
+    display: block;
+  }
 </style>
diff --git a/sveltejs/src/Search.svelte b/sveltejs/src/Search.svelte
index 9337e207c..2cf79d33d 100644
--- a/sveltejs/src/Search.svelte
+++ b/sveltejs/src/Search.svelte
@@ -328,15 +328,6 @@
             for={`maintenanceStatus${id}`}
           >
             {label}
-            {#if id === ACTIVELY_MAINTAINED_ID}
-              <img
-                class="small-icons"
-                id="actively-maintained"
-                src="/{drupalSettings.project_browser
-                  .module_path}/images/green-maintained-wrench-icon.svg"
-                alt=""
-              />
-            {/if}
           </label>
         </FilterGroup>
         <FilterGroup
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index 558c6ea82..96ab4f05e 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -51,10 +51,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $assert_session = $this->assertSession();
 
     $assert_session->waitForElementVisible('css', '#project-browser .project');
-    $titles = array_map(function ($element) {
-      return $element->getText();
-    }, $page->findAll('css', '#project-browser .project__title'));
-    $this->assertSame($project_titles, $titles);
+    foreach ($project_titles as $key => $title) {
+      $this->assertEquals($title, $page->findAll('css', '#project-browser .project__title')[$key]->getText());
+    }
   }
 
   /**
@@ -147,6 +146,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $page->find('css', '#67')->click();
     // Click 'Commerce/Advertising' checkbox.
     $page->find('css', '#55')->click();
+
     $module_category_media_filter_selector = 'p.filters-applied:nth-child(3)';
     $module_category_media_filter_element = $page->find('css', $module_category_media_filter_selector);
     // Make sure the 'Media' module category filter is applied.
-- 
GitLab