Compare commits

...

20 Commits

Author SHA1 Message Date
Sascha Ißbrücker
1daad2c86c Bump version 2023-11-04 10:17:01 +01:00
dependabot[bot]
251def2583 Bump django from 4.1.10 to 4.1.13 (#567)
Bumps [django](https://github.com/django/django) from 4.1.10 to 4.1.13.
- [Commits](https://github.com/django/django/compare/4.1.10...4.1.13)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-04 09:57:08 +01:00
Vitor Marçal
560769f068 Fix RSS feed not handling None values (#569)
Previously, the 'sanitize' function would throw an error when 'text' was None. This commit fixes the issue by adding a check to handle the case where 'text' is None, returning an empty string instead.

Closes #568
2023-11-04 09:56:06 +01:00
Sascha Ißbrücker
dc9799cc53 Update CHANGELOG.md 2023-10-27 21:18:08 +02:00
Sascha Ißbrücker
41c1b9ab84 Bump version 2023-10-27 20:06:25 +02:00
dependabot[bot]
2396c8fe99 Bump urllib3 from 1.26.17 to 1.26.18 (#560)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.17 to 1.26.18.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.17...1.26.18)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-27 20:00:14 +02:00
Sascha Ißbrücker
de328c78e2 Sanitize RSS feed to remove control characters (#565) 2023-10-27 19:59:06 +02:00
Strubbl
314e4a9b74 Add feed2linkding to community section (#544)
* Update README.md

add feed2linkding

* Update README.md

sort feed2linkding in the list
2023-10-14 00:25:44 +02:00
Sascha Ißbrücker
ff400a79ec Disable editing of search preferences in user admin (#555) 2023-10-14 00:05:27 +02:00
Sascha Ißbrücker
f4fcb96b5e Update README.md 2023-10-11 18:16:47 +02:00
Sascha Ißbrücker
daab772971 update ios shortcut how-to 2023-10-07 18:02:07 +02:00
Sascha Ißbrücker
64c81ea565 rename ios shortcut 2023-10-07 17:58:01 +02:00
Sascha Ißbrücker
1dd19e8fa2 add ios shortcut 2023-10-07 17:36:54 +02:00
andrewdolphin
dd3699cdeb Add iOS shortcut to community section (#550)
* Update README.md

* Update README.md

* Update README.md

---------

Co-authored-by: Sascha Ißbrücker <sascha.issbruecker@googlemail.com>
2023-10-07 16:41:12 +02:00
dependabot[bot]
f9c9d17873 Bump urllib3 from 1.26.11 to 1.26.17 (#542)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.11 to 1.26.17.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.11...1.26.17)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-07 10:30:53 +02:00
Sascha Ißbrücker
5c9f03a715 Fix search options not opening on iOS (#549)
* Fix search options not opening on iOS

* cleanup
2023-10-07 10:24:09 +02:00
Sascha Ißbrücker
7600fe87f9 Bump version 2023-10-06 23:35:17 +02:00
Sascha Ißbrücker
f756e28daf Fix memory leak with SQLite (#548) 2023-10-06 23:29:29 +02:00
Sascha Ißbrücker
1e10d7eb4a Bump docker node version 2023-10-03 18:08:23 +02:00
Sascha Ißbrücker
ccf8e03571 Update CHANGELOG.md 2023-10-01 22:19:39 +02:00
19 changed files with 160 additions and 61 deletions

View File

@@ -1,5 +1,52 @@
# Changelog
## v1.22.2 (27/10/2023)
### What's Changed
* Fix search options not opening on iOS by @sissbruecker in https://github.com/sissbruecker/linkding/pull/549
* Bump urllib3 from 1.26.11 to 1.26.17 by @dependabot in https://github.com/sissbruecker/linkding/pull/542
* Add iOS shortcut to community section by @andrewdolphin in https://github.com/sissbruecker/linkding/pull/550
* Disable editing of search preferences in user admin by @sissbruecker in https://github.com/sissbruecker/linkding/pull/555
* Add feed2linkding to community section by @Strubbl in https://github.com/sissbruecker/linkding/pull/544
* Sanitize RSS feed to remove control characters by @sissbruecker in https://github.com/sissbruecker/linkding/pull/565
* Bump urllib3 from 1.26.17 to 1.26.18 by @dependabot in https://github.com/sissbruecker/linkding/pull/560
### New Contributors
* @andrewdolphin made their first contribution in https://github.com/sissbruecker/linkding/pull/550
* @Strubbl made their first contribution in https://github.com/sissbruecker/linkding/pull/544
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.22.1...v1.22.2
---
## v1.22.1 (06/10/2023)
### What's Changed
* Fix memory leak with SQLite by @sissbruecker in https://github.com/sissbruecker/linkding/pull/548
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.22.0...v1.22.1
---
## v1.22.0 (01/10/2023)
### What's Changed
* Fix case-insensitive search for unicode characters in SQLite by @sissbruecker in https://github.com/sissbruecker/linkding/pull/520
* Add sort option to bookmark list by @sissbruecker in https://github.com/sissbruecker/linkding/pull/522
* Add button to show tags on smaller screens by @sissbruecker in https://github.com/sissbruecker/linkding/pull/529
* Make code blocks in notes scrollable by @sissbruecker in https://github.com/sissbruecker/linkding/pull/530
* Add filter for shared state by @sissbruecker in https://github.com/sissbruecker/linkding/pull/531
* Add support for exporting/importing bookmark notes by @sissbruecker in https://github.com/sissbruecker/linkding/pull/532
* Add filter for unread state by @sissbruecker in https://github.com/sissbruecker/linkding/pull/535
* Allow saving search preferences by @sissbruecker in https://github.com/sissbruecker/linkding/pull/540
* Add user profile endpoint by @sissbruecker in https://github.com/sissbruecker/linkding/pull/541
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.21.0...v1.22.0
---
## v1.21.1 (26/09/2023)
### What's Changed

View File

@@ -1,4 +1,4 @@
FROM node:18.13.0-alpine AS node-build
FROM node:18.18.0-alpine AS node-build
WORKDIR /etc/linkding
# install build dependencies
COPY package.json package-lock.json ./

View File

@@ -17,7 +17,7 @@
- [Documentation](#documentation)
- [Browser Extension](#browser-extension)
- [Community](#community)
- [Acknowledgements](#acknowledgements)
- [Acknowledgements + Donations](#acknowledgements--donations)
- [Development](#development)
## Introduction
@@ -160,10 +160,10 @@ Instead of configuring header forwarding in your proxy, you can also configure t
### Managed Hosting Options
Self-hosting web applications still requires a lot of technical know-how, and commitment to maintenance, with regard to keeping everything up-to-date and secure. This can be a huge entry barrier for people who are interested in self-hosting, but lack the technical knowledge to do so. This section is intended to provide alternatives in form of managed hosting solutions. Note that these options are usually commercial offerings, that require paying a fee for the convenience of being managed by another party. The technical knowledge required to make use of individual options is going to vary, and no guarantees can be made that every option is accessible for everyone. That being said, I hope this section helps in making the application accessible to a wider audience.
Self-hosting web applications still requires a lot of technical know-how and commitment to maintenance, in order to keep everything up-to-date and secure. This section is intended to provide simple alternatives in form of managed hosting solutions.
- [linkding on fly.io](https://github.com/fspoettel/linkding-on-fly) - Guide for hosting a linkding installation on [fly.io](https://fly.io). By [fspoettel](https://github.com/fspoettel)
- [PikaPods.com](https://www.pikapods.com/) - Managed hosting for linkding, EU and US regions available. [1-click setup link](https://www.pikapods.com/pods?run=linkding)
- [PikaPods.com](https://www.pikapods.com/) - Managed hosting for linkding, EU and US regions available. [1-click setup link](https://www.pikapods.com/pods?run=linkding) ([Disclosure](#pikapods))
## Documentation
@@ -190,7 +190,9 @@ The extension is open-source as well, and can be found [here](https://github.com
This section lists community projects around using linkding, in alphabetical order. If you have a project that you want to share with the linkding community, feel free to submit a PR to add your project to this section.
- [aiolinkding](https://github.com/bachya/aiolinkding) A Python3, async library to interact with the linkding REST API. By [bachya](https://github.com/bachya)
- [feed2linkding](https://codeberg.org/strubbl/feed2linkding) A commandline utility to add all web feed item links to linkding via API call. By [Strubbl](https://github.com/Strubbl)
- [Helm Chart](https://charts.pascaliske.dev/charts/linkding/) Helm Chart for deploying linkding inside a Kubernetes cluster. By [pascaliske](https://github.com/pascaliske)
- [iOS Shortcut using API and Tagging](https://gist.github.com/andrewdolphin/a7dff49505e588d940bec55132fab8ad) An iOS shortcut using the Linkding API (no extra logins required) that pulls previously used tags and allows tagging at the time of link creation.
- [Linka!](https://github.com/cmsax/linka) Web app (also a PWA) for quickly searching & opening bookmarks in linkding, support multi keywords, exclude mode and other advance options. By [cmsax](https://github.com/cmsax)
- [linkding-cli](https://github.com/bachya/linkding-cli) A command-line interface (CLI) to interact with the linkding REST API. Powered by [aiolinkding](https://github.com/bachya/aiolinkding). By [bachya](https://github.com/bachya)
- [linkding-extension](https://github.com/jeroenpardon/linkding-extension) Chromium compatible extension that wraps the linkding bookmarklet. Tested with Chrome, Edge, Brave. By [jeroenpardon](https://github.com/jeroenpardon)
@@ -199,9 +201,21 @@ This section lists community projects around using linkding, in alphabetical ord
- [Open all links bookmarklet](https://gist.github.com/ukcuddlyguy/336dd7339e6d35fc64a75ccfc9323c66) A browser bookmarklet to open all links on the current Linkding page in new tabs. By [ukcuddlyguy](https://github.com/ukcuddlyguy)
- [Postman collection](https://gist.github.com/gingerbeardman/f0b42502f3bc9344e92ce63afd4360d3) a group of saved request templates for API testing. By [gingerbeardman](https://github.com/gingerbeardman)
## Acknowledgements
## Acknowledgements + Donations
JetBrains provides an open-source license of [IntelliJ IDEA](https://www.jetbrains.com/idea/) for the development of linkding.
### PikaPods
[PikaPods](https://www.pikapods.com/) has a revenue sharing agreement with this project, sharing some of their revenue from hosting linkding instances. I do not intend to profit from this project financially, so I am in turn donating that revenue. Big thanks to PikaPods for making this possible.
See the table below for a list of donations.
| Source | Description | Amount | Donated to |
|---------------------------------------|---------------------------------------------|---------|---------------------------------------------------------------------|
| [PikaPods](https://www.pikapods.com/) | Linkding hosting June 2022 - September 2023 | $163.50 | [Internet Archive](/docs/donations/2023-10-11-internet-archive.png) |
### JetBrains
JetBrains has previously provided an open-source license of [IntelliJ IDEA](https://www.jetbrains.com/idea/) for the development of linkding.
## Development

View File

@@ -122,7 +122,7 @@ class AdminUserProfileInline(admin.StackedInline):
can_delete = False
verbose_name_plural = 'Profile'
fk_name = 'user'
readonly_fields = ('search_preferences', )
class AdminCustomUser(UserAdmin):
inlines = (AdminUserProfileInline,)

View File

@@ -1,11 +1,12 @@
import unicodedata
from dataclasses import dataclass
from django.contrib.syndication.views import Feed
from django.db.models import QuerySet
from django.urls import reverse
from bookmarks.models import Bookmark, BookmarkSearch, FeedToken
from bookmarks import queries
from bookmarks.models import Bookmark, BookmarkSearch, FeedToken
@dataclass
@@ -14,6 +15,14 @@ class FeedContext:
query_set: QuerySet[Bookmark]
def sanitize(text: str):
if not text:
return ''
# remove control characters
valid_chars = ['\n', '\r', '\t']
return ''.join(ch for ch in text if ch in valid_chars or unicodedata.category(ch)[0] != 'C')
class BaseBookmarksFeed(Feed):
def get_object(self, request, feed_key: str):
feed_token = FeedToken.objects.get(key__exact=feed_key)
@@ -22,10 +31,10 @@ class BaseBookmarksFeed(Feed):
return FeedContext(feed_token, query_set)
def item_title(self, item: Bookmark):
return item.resolved_title
return sanitize(item.resolved_title)
def item_description(self, item: Bookmark):
return item.resolved_description
return sanitize(item.resolved_description)
def item_link(self, item: Bookmark):
return item.url

View File

@@ -0,0 +1,36 @@
import { registerBehavior } from "./index";
class DropdownBehavior {
constructor(element) {
this.element = element;
this.opened = false;
this.onOutsideClick = this.onOutsideClick.bind(this);
const toggle = element.querySelector(".dropdown-toggle");
toggle.addEventListener("click", () => {
if (this.opened) {
this.close();
} else {
this.open();
}
});
}
open() {
this.element.classList.add("active");
document.addEventListener("click", this.onOutsideClick);
}
close() {
this.element.classList.remove("active");
document.removeEventListener("click", this.onOutsideClick);
}
onOutsideClick(event) {
if (!this.element.contains(event.target)) {
this.close();
}
}
}
registerBehavior("ld-dropdown", DropdownBehavior);

View File

@@ -4,6 +4,7 @@ import { ApiClient } from "./api";
import "./behaviors/bookmark-page";
import "./behaviors/bulk-edit";
import "./behaviors/confirm-button";
import "./behaviors/dropdown";
import "./behaviors/modal";
import "./behaviors/global-shortcuts";
import "./behaviors/tag-autocomplete";

View File

@@ -74,12 +74,6 @@
min-width: 250px;
}
&:focus-within {
.menu {
display: block;
}
}
.menu .actions {
margin-top: $unit-4;
display: flex;

View File

@@ -44,8 +44,8 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
</a>
<div class="dropdown dropdown-right">
<a href="#" id="mobile-nav-menu-trigger" class="btn btn-link dropdown-toggle" tabindex="0">
<div ld-dropdown class="dropdown dropdown-right">
<a href="#" class="btn btn-link dropdown-toggle" tabindex="0">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
style="width: 24px; height: 24px">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
@@ -80,22 +80,3 @@
</div>
</div>
{% endhtmlmin %}
<script>
// Hide mobile menu on outside click
// The Spectre CSS component relies on focus changes to show/hide the dropdown, however mobile browsers like
// Safari will not trigger a blur event when clicking on a non-focusable element, so we have to simulate the
// behaviour through Javascript
const mobileNavMenuTrigger = document.getElementById('mobile-nav-menu-trigger');
function mobileNavMenuOutsideClickHandler(clickEvent) {
if (mobileNavMenuTrigger.parentElement.contains(clickEvent.target)) return
mobileNavMenuTrigger.blur();
}
mobileNavMenuTrigger.addEventListener('focus', function () {
document.addEventListener('click', mobileNavMenuOutsideClickHandler);
})
mobileNavMenuTrigger.addEventListener('blur', function () {
document.removeEventListener('click', mobileNavMenuOutsideClickHandler);
})
</script>

View File

@@ -9,7 +9,7 @@
{{ hidden_field }}
{% endfor %}
</form>
<div class="search-options dropdown dropdown-right">
<div ld-dropdown class="search-options dropdown dropdown-right">
<button type="button" class="btn dropdown-toggle{% if search.has_modified_preferences %} badge{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">

View File

@@ -7,6 +7,8 @@ from django.urls import reverse
from bookmarks.tests.helpers import BookmarkFactoryMixin
from bookmarks.models import FeedToken, User
from bookmarks.feeds import sanitize
def rfc2822_date(date):
@@ -104,6 +106,17 @@ class FeedsTestCase(TestCase, BookmarkFactoryMixin):
self.assertContains(response, '<item>', count=0)
def test_strip_control_characters(self):
self.setup_bookmark(title='test\n\r\t\0\x08title', description='test\n\r\t\0\x08description')
response = self.client.get(reverse('bookmarks:feeds.all', args=[self.token.key]))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<item>', count=1)
self.assertContains(response, f'<title>test\n\r\ttitle</title>', count=1)
self.assertContains(response, f'<description>test\n\r\tdescription</description>', count=1)
def test_sanitize_with_none_text(self):
self.assertEqual('', sanitize(None))
def test_unread_returns_404_for_unknown_feed_token(self):
response = self.client.get(reverse('bookmarks:feeds.unread', args=['foo']))

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

View File

@@ -40,23 +40,22 @@ Try using share button on an app, a new item `Send to...` should appear on the s
This how-to explains how to make use of the app shortcuts iOS app to create a share action that can be used in Safari for adding bookmarks to your linkding instance.
**In the shortcuts app:**
- create new shortcut
- go to shortcut details, enable to option to show the shortcut in share menu
- from the available share input types only select "URL"
- add Safari action "Show Web Page At"
- for URL enter your linkding instance URL and specifically point to the new bookmark form, then add the shortcut input variable from the list of suggested variables after the URL parameter. Visually it should look something like this: `https://linkding.mydomain.com/bookmarks/new?url=[Shortcut input]`, where `[Shortcut input]` is a visual block that was inserted after selecting the shortcut input variable suggestion. This is basically a placeholder that will get replaced with the actual URL that you want to bookmark. See screenshot at the end for an example on how this looks.
- save, give the shortcut a nice name + glyph
To install the shortcut:
- Download the [Shortcut](https://raw.githubusercontent.com/sissbruecker/linkding/master/docs/Add%20To%20Linkding.shortcut) on your iOS device
- Tap the downloaded file, which brings up the Shortcuts app
- Confirm that you want to add the shortcut
- In the shortcut, change `https://linkding.mydomain.com` to the URL of your linkding instance
- Confirm / close the shortcut
Example of how the shortcut configuration should look:
To use the shortcut:
- Open Safari and navigate to the page you want to bookmark
- Tap the share button
- Scroll down and tap "Add To Linkding"
- This opens linkding in a Safari overlay where you can configure the bookmark
- When you're done, tap "Save"
- After the bookmark is saved you can close the overlay
![Screenshot](/docs/ios-app-shortcut-example.png?raw=true "Screenshot demonstrating how to insert the input placeholder into the URL")
**Using the share action from Safari:**
- browse to the website that you want to share
- click the share button
- your new app shortcut should now be available as share action
- select the app shortcut
- this should open a new Safari overlay showing the add bookmark form with the URL field prefilled
- after saving the bookmark you can close the overlay and continue browsing
At the bottom of the share sheet there is a button for configuring share actions. You can use this to move the "Add To Linkding" action to the top of the share sheet if you like.
> [!NOTE]
> You can also check the [Community section](https://github.com/sissbruecker/linkding#community) for other pre-made shortcuts that you can use.

View File

@@ -1,6 +1,6 @@
{
"name": "linkding",
"version": "1.22.0",
"version": "1.22.3",
"description": "",
"main": "index.js",
"scripts": {

View File

@@ -6,7 +6,7 @@ certifi==2023.7.22
charset-normalizer==2.1.1
click==8.1.3
confusable-homoglyphs==3.2.0
Django==4.1.10
Django==4.1.13
django-generate-secret-key==1.0.2
django-registration==3.3
django-sass-processor==1.2.1
@@ -23,7 +23,7 @@ soupsieve==2.3.2.post1
sqlparse==0.4.4
supervisor==4.2.4
typing-extensions==3.10.0.0
urllib3==1.26.11
urllib3==1.26.18
uWSGI==2.0.22
waybackpy==3.0.6
webencodings==0.5.1

View File

@@ -7,7 +7,7 @@ charset-normalizer==2.1.1
click==8.1.3
confusable-homoglyphs==3.2.0
coverage==5.5
Django==4.1.10
Django==4.1.13
django-appconf==1.0.5
django-compressor==4.1
django-debug-toolbar==3.6.0
@@ -33,6 +33,6 @@ six==1.16.0
soupsieve==2.3.2.post1
sqlparse==0.4.4
typing-extensions==3.10.0.0
urllib3==1.26.11
urllib3==1.26.18
waybackpy==3.0.6
webencodings==0.5.1

View File

@@ -225,6 +225,11 @@ else:
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'data', 'db.sqlite3'),
'OPTIONS': LD_DB_OPTIONS,
# Creating a connection loads the ICU extension into the SQLite
# connection, and also loads an ICU collation. The latter causes a
# memory leak, so try to counter that by making connections indefinitely
# persistent.
'CONN_MAX_AGE': None
}
DATABASES = {

View File

@@ -1 +1 @@
1.22.0
1.22.3