diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a097c7ad853d0fbd8356b58ea49bcb0e85299c..41f569a3647f8d428c5b95674a4ee9050a8e6317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ext_applicable` module for building fancy features which append functionality to entities (EXPERIMENTAL). - `ext_cosmetics` module which converts http(s) uris in property values into - clickable links. + clickable links (with tests) - Added a menu/toc for the tour - Added a previous and next buttons for pages in the tour - Added warnings to inform about minimum width when accessing tour and diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties index 1c505bccd77282551b4c898cac3984a5990eeb42..386eb8bd22bd3f32b94d9b9ea4714b80e355ea8e 100644 --- a/build.properties.d/00_default.properties +++ b/build.properties.d/00_default.properties @@ -50,6 +50,7 @@ BUILD_MODULE_EXT_BOTTOM_LINE_TABLE_PREVIEW=DISABLED BUILD_MODULE_EXT_BOTTOM_LINE_TIFF_PREVIEW=DISABLED BUILD_MODULE_EXT_BOOKMARKS=ENABLED BUILD_MODULE_EXT_ANNOTATION=ENABLED +BUILD_MODULE_EXT_COSMETICS_LINKIFY=DISABLED BUILD_MODULE_USER_MANAGEMENT=ENABLED BUILD_MODULE_USER_MANAGEMENT_CHANGE_OWN_PASSWORD_REALM=CaosDB @@ -150,4 +151,5 @@ MODULE_DEPENDENCIES=( ext_sss_markdown.js ext_trigger_crawler_form.js ext_bookmarks.js + ext_cosmetics.js ) diff --git a/misc/ext_cosmetics_test_data.py b/misc/ext_cosmetics_test_data.py new file mode 100755 index 0000000000000000000000000000000000000000..786f46c2d6bcd1d55488a15e2c4f50085f331950 --- /dev/null +++ b/misc/ext_cosmetics_test_data.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# ** header v3.0 +# This file is a part of the CaosDB Project. +# +# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> +# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# ** end header + +import caosdb as db + +# clean +old = db.execute_query("FIND Test*") +if len(old): + old.delete() + +# data model +datamodel = db.Container() +datamodel.extend([ + db.Property("TestProp", datatype=db.TEXT), + db.RecordType("TestRecordType"), +]) + +datamodel.insert() + + +# test data +testdata = db.Container() + +test_cases = [ + "no link", + "https://example.com", + "https://example.com and http://example.com", + "this is text https://example.com", + "this is text https://example.com and this as well", + "this is text https://example.com and another linke https://example.com", + "this is text https://example.com and another linke https://example.com and more text", + ("this is a lot of text with links in it Lorem ipsum dolor sit amet, " + "consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore " + "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " + "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " + "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + "proident, sunt in culpa qui officia deserunt mollit anim id est " + "laborum.https://example.com and another linke https://example.com and " + "more text and here comes a very long link: " + "https://example.com/this/has/certainly/more/than/40/characters/just/count/if/you/dont/believe/it.html"), +] +for test_case in test_cases: + testdata.append(db.Record().add_parent("TestRecordType").add_property("TestProp", + test_case)) +testdata.insert() diff --git a/src/core/js/ext_cosmetics.js b/src/core/js/ext_cosmetics.js index 4d935a2a5afecd699fee1a24416c06b30d1adc46..f4f281123b39a87b7ef6848db4e84a81b5e30d9c 100644 --- a/src/core/js/ext_cosmetics.js +++ b/src/core/js/ext_cosmetics.js @@ -28,22 +28,33 @@ */ var cosmetics = new function () { + /** + * Cut-off length of links. When linkify processes the links any href + * longer than this will be cut off at character 25 and "[...]" will be + * appended for the link text. + */ + var _link_cut_off_length = 40; + var _linkify = function () { $('.caosdb-f-property-text-value').each(function (index) { - // TODO also extract and convert links surrounded by other text - if (/^https?:\/\//.test(this.innerText)) { - var uri = this.innerText; - var text = uri + if (/https?:\/\//.test(this.innerText)) { + var result = this.innerText.replace(/https?:\/\/[^\s]*/g, function (href, index) { + var link_text = href; + if (_link_cut_off_length > 4 && link_text.length > _link_cut_off_length) { + link_text = link_text.substring(0, _link_cut_off_length - 5) + "[...]"; + } + + return `<a title="Open ${href} in a new tab." target="_blank" class="caosdb-v-property-href-value" href="${href}">${link_text} <i class="bi bi-box-arrow-up-right"></i></a>`; + }); - $(this).parent().css("overflow", "hidden"); - $(this).parent().css("text-overflow", "ellipsis"); - $(this).html(`<a class="caosdb-v-property-href-value" href="${uri}">${text} <i class="bi bi-box-arrow-up-right"></i></a>`); + $(this).html(result); } }); } /** - * Convert any text-value beginning with 'http(s)://' into a link. + * Convert any substring of a text-value beginning with 'http(s)://' into a + * link. * * A listener detects edit-mode changes and previews */ @@ -57,6 +68,7 @@ var cosmetics = new function () { } this.init = function () { + this.linkify = linkify; if ("${BUILD_MODULE_EXT_COSMETICS_LINKIFY}" == "ENABLED") { linkify(); } diff --git a/test/core/js/modules/ext_cosmetics.js.js b/test/core/js/modules/ext_cosmetics.js.js new file mode 100644 index 0000000000000000000000000000000000000000..d5d4df7f10a2859bcd7318680d4f6720aedc6127 --- /dev/null +++ b/test/core/js/modules/ext_cosmetics.js.js @@ -0,0 +1,87 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +'use strict'; + +QUnit.module("ext_cosmetics.js", { + before: function (assert) { + cosmetics.init(); + // setup before module + }, + beforeEach: function (assert) { + // setup before each test + }, + afterEach: function (assert) { + // teardown after each test + }, + after: function (assert) { + // teardown after module + } +}); + +QUnit.test("linkify - https", function (assert) { + assert.ok(cosmetics.linkify, "linkify available"); + var test_cases = [ + ["https://link", 1], + ["this is other text https://link", 1], + ["https://link this is other text", 1], + ["this is other text https://link and this as well", 1], + ["this is other text https://link", 1], + ["this is other text https://link and here comes another link https://link and more text", 2], + ]; + for (let test_case of test_cases) { + var text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`); + $(document.body).append(text_value); + assert.equal($(text_value).find("a[href='https://link']").length, 0, "no link present"); + cosmetics.linkify(); + assert.equal($(text_value).find("a[href='https://link']").length, test_case[1], "link is present"); + text_value.remove(); + } +}); + +QUnit.test("linkify - http", function (assert) { + var test_cases = [ + ["http://link", 1], + ["this is other text http://link", 1], + ["http://link this is other text", 1], + ["this is other text http://link and this as well", 1], + ["this is other text http://link", 1], + ["this is other text http://link and here comes another link http://link and more text", 2], + ]; + for (let test_case of test_cases) { + var text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`); + $(document.body).append(text_value); + assert.equal($(text_value).find("a[href='http://link']").length, 0, "no link present"); + cosmetics.linkify(); + assert.equal($(text_value).find("a[href='http://link']").length, test_case[1], "link is present"); + text_value.remove(); + } +}); + +QUnit.test("linkify cut-off (40)", function (assert) { + var test_case = "here is some text https://this.is.a.link/with/more/than/40/characters/ this is more text"; + var text_value = $(`<div class="caosdb-f-property-text-value">${test_case}</div>`); + $(document.body).append(text_value); + assert.equal($(text_value).find("a").length, 0, "no link present"); + cosmetics.linkify(); + assert.equal($(text_value).find("a").length, 1, "link is present"); + assert.equal($(text_value).find("a").text(), "https://this.is.a.link/with/more/th[...] ", "link text has been cut off"); + text_value.remove(); +}); \ No newline at end of file