From 292c09cc1b2d73bcaab2d903ca073db12ff61795 Mon Sep 17 00:00:00 2001
From: kalpitk <kkothari1999@gmail.com>
Date: Wed, 17 Jul 2019 18:33:29 +0530
Subject: [PATCH] Add language selection option

Related T1909
---
 .gitignore                                    |  1 +
 package.json                                  |  1 +
 .../config/webpack.config.development.js      |  9 ++++++
 swh/web/assets/src/bundles/browse/content.css | 29 +++++++++++++++++++
 .../src/bundles/browse/origin-search.js       |  2 +-
 swh/web/assets/src/bundles/vendors/index.js   |  6 ++++
 swh/web/browse/views/content.py               | 14 ++++++++-
 swh/web/browse/views/origin.py                |  3 +-
 swh/web/browse/views/snapshot.py              |  4 ++-
 .../browse/views/utils/snapshot_context.py    | 15 ++++++++--
 .../templates/includes/content-display.html   | 18 ++++++++++++
 .../templates/includes/top-navigation.html    | 14 +++++++++
 yarn.lock                                     |  5 ++++
 13 files changed, 115 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index ac787cc33..92077e169 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@ swh/web/static/js/
 swh/web/static/css/
 swh/web/static/fonts/
 swh/web/static/jssources/
+swh/web/static/img/thirdParty/
 .cache-loader/
 build/
 dist/
diff --git a/package.json b/package.json
index 979dbc465..ab4c73132 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
     "ansi_up": "^4.0.3",
     "bootstrap": "^4.3.1",
     "bootstrap-year-calendar-bs4": "^1.0.0",
+    "chosen-js": "^1.8.7",
     "clipboard": "^2.0.4",
     "core-js": "^3.1.4",
     "d3": "^5.9.7",
diff --git a/swh/web/assets/config/webpack.config.development.js b/swh/web/assets/config/webpack.config.development.js
index 569a14d77..e6c49f54d 100644
--- a/swh/web/assets/config/webpack.config.development.js
+++ b/swh/web/assets/config/webpack.config.development.js
@@ -302,6 +302,15 @@ module.exports = {
             outputPath: 'fonts/'
           }
         }]
+      }, {
+        test: /\.png$/,
+        use: [{
+          loader: 'file-loader',
+          options: {
+            name: '[name].[ext]',
+            outputPath: 'img/thirdParty/'
+          }
+        }]
       }
     ],
     // tell webpack to not parse minified pdfjs file to speedup build process
diff --git a/swh/web/assets/src/bundles/browse/content.css b/swh/web/assets/src/bundles/browse/content.css
index adcaf49a0..58f29e81e 100644
--- a/swh/web/assets/src/bundles/browse/content.css
+++ b/swh/web/assets/src/bundles/browse/content.css
@@ -21,3 +21,32 @@
 .swh-content svg {
     max-width: 100%;
 }
+
+.chosen-container {
+    color: #444;
+}
+
+.chosen-container .chosen-single div b {
+    margin-top: 2px;
+    filter: brightness(0%);
+}
+
+.chosen-container .chosen-single {
+    height: 31px;
+    line-height: 1.5;
+    border-radius: 0px;
+    background-color: #f4f4f4;
+    border-color: #ddd;
+    background-image:none;
+    -webkit-box-shadow: none;
+	-moz-box-shadow: none;
+    box-shadow: none;
+    font-weight: 400;
+    font-size: 14px;
+    padding-top: 4px;
+    padding-bottom: 4px;
+}
+
+.chosen-container .chosen-single:hover {
+    background-color: #e7e7e7;
+}
diff --git a/swh/web/assets/src/bundles/browse/origin-search.js b/swh/web/assets/src/bundles/browse/origin-search.js
index 78a9bafa3..c5e68325f 100644
--- a/swh/web/assets/src/bundles/browse/origin-search.js
+++ b/swh/web/assets/src/bundles/browse/origin-search.js
@@ -43,7 +43,7 @@ function populateOriginSearchResultsTable(origins, offset) {
       table.append(tableRow);
       // get async latest visit snapshot and update visit status icon
       let latestSnapshotUrl = Urls.api_1_origin_visit_latest(origin.url);
-      latestSnapshotUrl += "?require_snapshot=true";
+      latestSnapshotUrl += '?require_snapshot=true';
       fetch(latestSnapshotUrl)
         .then(response => response.json())
         .then(data => {
diff --git a/swh/web/assets/src/bundles/vendors/index.js b/swh/web/assets/src/bundles/vendors/index.js
index 11a3e44a1..91fb058ce 100644
--- a/swh/web/assets/src/bundles/vendors/index.js
+++ b/swh/web/assets/src/bundles/vendors/index.js
@@ -31,6 +31,12 @@ import 'datatables.net-bs4/css/dataTables.bootstrap4.css';
 import 'datatables.net-responsive-bs4/css/responsive.bootstrap4.css';
 import './datatables.css';
 
+// chosen-js
+import 'chosen-js';
+import 'chosen-js/chosen.min.css';
+import 'chosen-js/chosen-sprite.png';
+import 'chosen-js/chosen-sprite@2x.png';
+
 // iframe-resizer
 import 'iframe-resizer';
 
diff --git a/swh/web/browse/views/content.py b/swh/web/browse/views/content.py
index d91f9d584..4fd9361be 100644
--- a/swh/web/browse/views/content.py
+++ b/swh/web/browse/views/content.py
@@ -14,7 +14,7 @@ from django.template.defaultfilters import filesizeformat
 
 from swh.model.hashutil import hash_to_hex
 
-from swh.web.common import query, service
+from swh.web.common import query, service, highlightjs
 from swh.web.common.utils import (
     reverse, gen_path_info, swh_object_icons
 )
@@ -181,6 +181,8 @@ def content_display(request, query_string):
                                        raise_if_unavailable=False)
         origin_type = request.GET.get('origin_type', None)
         origin_url = request.GET.get('origin_url', None)
+        selected_language = request.GET.get('language', None)
+
         if not origin_url:
             origin_url = request.GET.get('origin', None)
         snapshot_context = None
@@ -218,6 +220,15 @@ def content_display(request, query_string):
         language = content_display_data['language']
         mimetype = content_display_data['mimetype']
 
+    # Override language with user-selected language
+    if selected_language is not None:
+        language = selected_language
+
+    available_languages = None
+
+    if mimetype and 'text/' in mimetype:
+        available_languages = highlightjs._hljs_languages
+
     root_dir = None
     filename = None
     path_info = None
@@ -303,6 +314,7 @@ def content_display(request, query_string):
                    'max_content_size': content_display_max_size,
                    'mimetype': mimetype,
                    'language': language,
+                   'available_languages': available_languages,
                    'breadcrumbs': breadcrumbs,
                    'top_right_link': {
                         'url': content_raw_url,
diff --git a/swh/web/browse/views/origin.py b/swh/web/browse/views/origin.py
index 7a55a06ab..b03cb0e28 100644
--- a/swh/web/browse/views/origin.py
+++ b/swh/web/browse/views/origin.py
@@ -79,9 +79,10 @@ def origin_content_browse(request, origin_url, origin_type=None, path=None,
         * :http:get:`/browse/origin/[(origin_type)/url/](origin_url)/visit/(timestamp)/content/(path)/`
 
     """ # noqa
+    language = request.GET.get('language', None)
     return browse_snapshot_content(request, origin_type=origin_type,
                                    origin_url=origin_url, timestamp=timestamp,
-                                   path=path)
+                                   path=path, selected_language=language)
 
 
 PER_PAGE = 20
diff --git a/swh/web/browse/views/snapshot.py b/swh/web/browse/views/snapshot.py
index 25be99327..e40a50963 100644
--- a/swh/web/browse/views/snapshot.py
+++ b/swh/web/browse/views/snapshot.py
@@ -60,7 +60,9 @@ def snapshot_content_browse(request, snapshot_id, path):
     The url that points to it is
         :http:get:`/browse/snapshot/(snapshot_id)/content/(path)/`
     """
-    return browse_snapshot_content(request, snapshot_id=snapshot_id, path=path)
+    language = request.GET.get('language', None)
+    return browse_snapshot_content(request, snapshot_id=snapshot_id, path=path,
+                                   selected_language=language)
 
 
 @browse_route(r'snapshot/(?P<snapshot_id>[0-9a-f]+)/log/',
diff --git a/swh/web/browse/views/utils/snapshot_context.py b/swh/web/browse/views/utils/snapshot_context.py
index 60e8180af..3a22f8037 100644
--- a/swh/web/browse/views/utils/snapshot_context.py
+++ b/swh/web/browse/views/utils/snapshot_context.py
@@ -23,7 +23,7 @@ from swh.web.browse.utils import (
     gen_snapshot_link, process_snapshot_branches
 )
 
-from swh.web.common import service
+from swh.web.common import service, highlightjs
 from swh.web.common.exc import (
     handle_view_exception, NotFoundExc
 )
@@ -399,7 +399,8 @@ def browse_snapshot_directory(request, snapshot_id=None, origin_type=None,
 
 
 def browse_snapshot_content(request, snapshot_id=None, origin_type=None,
-                            origin_url=None, timestamp=None, path=None):
+                            origin_url=None, timestamp=None, path=None,
+                            selected_language=None):
     """
     Django view implementation for browsing a content in a snapshot context.
     """
@@ -453,6 +454,15 @@ def browse_snapshot_content(request, snapshot_id=None, origin_type=None,
         language = content_display_data['language']
         mimetype = content_display_data['mimetype']
 
+    # Override language with user-selected language
+    if selected_language is not None:
+        language = selected_language
+
+    available_languages = None
+
+    if mimetype and 'text/' in mimetype:
+        available_languages = highlightjs._hljs_languages
+
     browse_view_name = 'browse-' + swh_type + '-directory'
 
     breadcrumbs = []
@@ -562,6 +572,7 @@ def browse_snapshot_content(request, snapshot_id=None, origin_type=None,
                    'max_content_size': content_display_max_size,
                    'mimetype': mimetype,
                    'language': language,
+                   'available_languages': available_languages,
                    'breadcrumbs': breadcrumbs if root_sha1_git else [],
                    'top_right_link': {
                         'url': content_raw_url,
diff --git a/swh/web/templates/includes/content-display.html b/swh/web/templates/includes/content-display.html
index cad9f23b6..4a6255215 100644
--- a/swh/web/templates/includes/content-display.html
+++ b/swh/web/templates/includes/content-display.html
@@ -53,7 +53,25 @@ See top-level LICENSE file for more information
     {% elif swh_object_metadata.filename and swh_object_metadata.filename|default:""|slice:"-5:" == "ipynb" %}
       swh.webapp.renderNotebook({{ top_right_link.url|jsonify }}, '.swh-ipynb');
     {% elif content %}
+      let codeContainer = $('code');
+      let content = codeContainer.text();
+
       swh.webapp.highlightCode();
+
+      function updateLanguage(language) {
+        codeContainer.text(content);
+        codeContainer.removeClass();
+        codeContainer.addClass(language);
+
+        let urlParams = new URLSearchParams(window.location.search);
+        urlParams.set('language', language);
+
+        const newUrl = window.location.pathname + '?' + urlParams.toString() + window.location.hash;
+        window.history.replaceState('', document.title, newUrl);
+
+        swh.webapp.highlightCode();
+      }
+
     {% endif %}
   </script>
 {% endif %}
diff --git a/swh/web/templates/includes/top-navigation.html b/swh/web/templates/includes/top-navigation.html
index 94a2a3bb3..21c2bf9a0 100644
--- a/swh/web/templates/includes/top-navigation.html
+++ b/swh/web/templates/includes/top-navigation.html
@@ -94,6 +94,14 @@ See top-level LICENSE file for more information
         {{ top_right_link.text }}
       </a>
     {% endif %}
+    {% if available_languages %}
+      <select data-placeholder="Select Language" class="language-select chosen-select">
+        <option value=""></option>
+        {% for lang in available_languages %}
+          <option value="{{ lang }}">{{ lang }}</option>
+        {% endfor %}
+      </select>
+    {% endif %}
     {% if show_actions_menu %}
       <button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown">
         <i class="fa fa-bars fa-fw" aria-hidden="true"></i>Actions
@@ -122,5 +130,11 @@ See top-level LICENSE file for more information
     snapshotContext = true;
     branch = "{{ snapshot_context.branch|escape }}";
   {% endif %}
+  {% if available_languages %}
+    $(".chosen-select").val("{{ language }}");
+    $(".chosen-select").chosen().change(function(event, params) {
+      updateLanguage(params.selected);
+    });
+  {% endif %}
   swh.browse.initSnapshotNavigation(snapshotContext, branch !== "None");
 </script>
diff --git a/yarn.lock b/yarn.lock
index c234062f1..78b94a756 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2005,6 +2005,11 @@ chokidar@^2.0.2, chokidar@^2.1.5, chokidar@^2.1.6:
   optionalDependencies:
     fsevents "^1.2.7"
 
+chosen-js@^1.8.7:
+  version "1.8.7"
+  resolved "https://registry.yarnpkg.com/chosen-js/-/chosen-js-1.8.7.tgz#9bfa5597f5081d602ff4ae904af9aef33265bb1d"
+  integrity sha512-eVdrZJ2U5ISdObkgsi0od5vIJdLwq1P1Xa/Vj/mgxkMZf14DlgobfB6nrlFi3kW4kkvKLsKk4NDqZj1MU1DCpw==
+
 chownr@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
-- 
GitLab