~netlandish/links-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH links] Adding filtering by domain to short links and link listings.

Details
Message ID
<20250731142951.31235-1-peter@netlandish.com>
Sender timestamp
1753950589
DKIM signature
missing
Download raw message
Patch: +460 -226
Implements: https://todo.code.netlandish.com/~netlandish/links/87
Changelog-added: Filtering by domain for shorts and link listings.
---
 api/graph/generated.go                        |  18 +-
 api/graph/model/models_gen.go                 |   2 +
 api/graph/schema.graphqls                     |   2 +
 api/graph/schema.resolvers.go                 |  22 +-
 internal/translations/catalog.go              | 416 +++++++++---------
 .../translations/locales/en/out.gotext.json   |   7 +
 .../locales/es/messages.gotext.json           |   5 +
 .../translations/locales/es/out.gotext.json   |   5 +
 list/routes.go                                |  46 +-
 list/routes_test.go                           |  39 ++
 short/routes.go                               |  48 +-
 short/routes_test.go                          |  39 ++
 static/js/advancedsearch.js                   |   9 +
 templates/link_short_list.html                |  14 +-
 templates/listing_list.html                   |  14 +-
 15 files changed, 460 insertions(+), 226 deletions(-)

diff --git a/api/graph/generated.go b/api/graph/generated.go
index c0901b0..c44ada2 100644
--- a/api/graph/generated.go
+++ b/api/graph/generated.go
@@ -25851,7 +25851,7 @@ func (ec *executionContext) unmarshalInputGetLinkShortInput(ctx context.Context,
		asMap[k] = v
	}

	fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag"}
	fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag", "domainId"}
	for _, k := range fieldsInOrder {
		v, ok := asMap[k]
		if !ok {
@@ -25900,6 +25900,13 @@ func (ec *executionContext) unmarshalInputGetLinkShortInput(ctx context.Context,
				return it, err
			}
			it.ExcludeTag = data
		case "domainId":
			ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("domainId"))
			data, err := ec.unmarshalOInt2ᚖint(ctx, v)
			if err != nil {
				return it, err
			}
			it.DomainID = data
		}
	}

@@ -25968,7 +25975,7 @@ func (ec *executionContext) unmarshalInputGetListingInput(ctx context.Context, o
		asMap[k] = v
	}

	fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag"}
	fieldsInOrder := [...]string{"orgSlug", "limit", "after", "before", "tag", "excludeTag", "domainId"}
	for _, k := range fieldsInOrder {
		v, ok := asMap[k]
		if !ok {
@@ -26017,6 +26024,13 @@ func (ec *executionContext) unmarshalInputGetListingInput(ctx context.Context, o
				return it, err
			}
			it.ExcludeTag = data
		case "domainId":
			ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("domainId"))
			data, err := ec.unmarshalOInt2ᚖint(ctx, v)
			if err != nil {
				return it, err
			}
			it.DomainID = data
		}
	}

diff --git a/api/graph/model/models_gen.go b/api/graph/model/models_gen.go
index 065ae14..1f100d8 100644
--- a/api/graph/model/models_gen.go
+++ b/api/graph/model/models_gen.go
@@ -217,6 +217,7 @@ type GetLinkShortInput struct {
	Before     *Cursor `json:"before,omitempty"`
	Tag        *string `json:"tag,omitempty"`
	ExcludeTag *string `json:"excludeTag,omitempty"`
	DomainID   *int    `json:"domainId,omitempty"`
}

type GetListingDetailInput struct {
@@ -234,6 +235,7 @@ type GetListingInput struct {
	Before     *Cursor `json:"before,omitempty"`
	Tag        *string `json:"tag,omitempty"`
	ExcludeTag *string `json:"excludeTag,omitempty"`
	DomainID   *int    `json:"domainId,omitempty"`
}

type GetOrganizationsInput struct {
diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls
index ea8c48a..02f9db8 100644
--- a/api/graph/schema.graphqls
+++ b/api/graph/schema.graphqls
@@ -624,6 +624,7 @@ input GetLinkShortInput {
    before: Cursor
    tag: String
    excludeTag: String
    domainId: Int
}

input GetListingInput {
@@ -633,6 +634,7 @@ input GetListingInput {
    before: Cursor
    tag: String
    excludeTag: String
    domainId: Int
}

input GetListingDetailInput {
diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go
index e056216..231ea5b 100644
--- a/api/graph/schema.resolvers.go
+++ b/api/graph/schema.resolvers.go
@@ -4810,8 +4810,8 @@ func (r *qRCodeResolver) CodeType(ctx context.Context, obj *models.QRCode) (mode
func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) {
	return &model.Version{
		Major:           0,
		Minor:           5,
		Patch:           1,
		Minor:           6,
		Patch:           0,
		DeprecationDate: nil,
	}, nil
}
@@ -5641,9 +5641,7 @@ func (r *queryResolver) GetLinkShorts(ctx context.Context, input *model.GetLinkS
	}

	linkOpts := &database.FilterOptions{
		Filter: sq.And{
			sq.Expr("l.org_id = ?", org.ID),
		},
		Filter:  sq.Expr("l.org_id = ?", org.ID),
		OrderBy: "l.id DESC",
	}
	if input.After != nil && input.Before != nil {
@@ -5652,6 +5650,13 @@ func (r *queryResolver) GetLinkShorts(ctx context.Context, input *model.GetLinkS
		return nil, nil
	}

	if input.DomainID != nil && *input.DomainID != 0 {
		linkOpts.Filter = sq.And{
			linkOpts.Filter,
			sq.Eq{"l.domain_id": *input.DomainID},
		}
	}

	if (input.Tag != nil && *input.Tag != "") || (input.ExcludeTag != nil && *input.ExcludeTag != "") {
		f := links.NewTagQuery("tag_link_shorts", "link_short_id")
		subQText, subQVal, err := f.GetSubQuery(input.Tag, input.ExcludeTag)
@@ -5776,6 +5781,13 @@ func (r *queryResolver) GetListings(ctx context.Context, input *model.GetListing
		OrderBy: "l.id DESC",
	}

	if input.DomainID != nil && *input.DomainID != 0 {
		listingOpts.Filter = sq.And{
			listingOpts.Filter,
			sq.Eq{"l.domain_id": input.DomainID},
		}
	}

	if (input.Tag != nil && *input.Tag != "") || (input.ExcludeTag != nil && *input.ExcludeTag != "") {
		f := links.NewTagQuery("tag_listings", "listing_id")
		subQText, subQVal, err := f.GetSubQuery(input.Tag, input.ExcludeTag)
diff --git a/internal/translations/catalog.go b/internal/translations/catalog.go
index bd38577..345ab3e 100644
--- a/internal/translations/catalog.go
+++ b/internal/translations/catalog.go
@@ -42,15 +42,15 @@ var messageKeyToIndex = map[string]int{
	" you can host your own instance if you'd like full control over your own platform.": 412,
	"%s":                                       437,
	"%s Links":                                 527,
	"%s: domain not found":                     680,
	"%s: domain not found":                     681,
	"- LinkTaco Team":                          337,
	"100%% open source project":                411,
	"A link was successfully created.":         487,
	"A list was successfully created.":         621,
	"A new link was created succesfully":       661,
	"A new link was created succesfully":       662,
	"A new member invitation was sent to %s":   166,
	"A new register invitation was sent to %s": 164,
	"A new short was created succesfully":      659,
	"A new short was created succesfully":      660,
	"A new window called Library will open":    560,
	"A valid Organizaiton Slug is required.":   252,
	"API Powered":                              392,
@@ -60,7 +60,7 @@ var messageKeyToIndex = map[string]int{
	"Action":                                   11,
	"Actions":                                  40,
	"Add":                                      305,
	"Add Link":                                 686,
	"Add Link":                                 687,
	"Add Member":                               463,
	"Add OAuth2 Client":                        307,
	"Add Personal Access Token":                294,
@@ -74,7 +74,7 @@ var messageKeyToIndex = map[string]int{
	"Already have an account? Click here to login": 58,
	"An invitation was sent to this user":          469,
	"An note was successfully created.":            582,
	"An short link was successfully created.":      673,
	"An short link was successfully created.":      674,
	"Analytics":   106,
	"Apply":       112,
	"Approve":     332,
@@ -131,29 +131,29 @@ var messageKeyToIndex = map[string]int{
	"Confirmation User Not Found":                                                               191,
	"Confirmation key is required.":                                                             171,
	"Connect":                                                                                   555,
	"Connect Mattermost":                                                                        639,
	"Connect User":                                                                              645,
	"Connect to Slack Workspace":                                                                693,
	"Connect Mattermost":                                                                        640,
	"Connect User":                                                                              646,
	"Connect to Slack Workspace":                                                                694,
	"Connected":                                                                                 556,
	"Continue to Upgrade":                                                                       2,
	"Country":                                                                                   108,
	"Country analytics":                                                                         378,
	"Create":                                                                                    629,
	"Create":                                                                                    630,
	"Create Domain":                                                                             432,
	"Create Link":                                                                               479,
	"Create Links":                                                                              609,
	"Create List":                                                                               620,
	"Create Note":                                                                               581,
	"Create Organization":                                                                       453,
	"Create QR Code":                                                                            628,
	"Create Short Link":                                                                         670,
	"Create QR Code":                                                                            629,
	"Create Short Link":                                                                         671,
	"Create link listings (ie, social media bios, etc.)": 363,
	"Creation Date":                       624,
	"Current Organization":                37,
	"Current Password":                    65,
	"Current password given is incorrect": 700,
	"Current password given is incorrect": 701,
	"CurrentSlug is required":             129,
	"Custom background image":             631,
	"Custom background image":             632,
	"Custom domain + SSL":                 361,
	"Days":                                114,
	"Default Bookmark Visibility":         455,
@@ -167,7 +167,7 @@ var messageKeyToIndex = map[string]int{
	"Delete Org Member":                   471,
	"Delete Picture":                      615,
	"Delete QR Code":                      546,
	"Delete Short Link":                   676,
	"Delete Short Link":                   677,
	"Delete member %s (%s) from Organization %s?": 474,
	"Description":              321,
	"Description is required.": 154,
@@ -176,19 +176,19 @@ var messageKeyToIndex = map[string]int{
	"Devices":                  111,
	"Disabled":                 446,
	"Disconnect":               554,
	"Disconnect Mattermost":    636,
	"Disconnect Slack":         687,
	"Do you really want to disconnect this organization from mattermost":        638,
	"Do you really want to disconnect this organization from slack":             689,
	"Disconnect Mattermost":    637,
	"Disconnect Slack":         688,
	"Do you really want to disconnect this organization from mattermost":        639,
	"Do you really want to disconnect this organization from slack":             690,
	"Do you really whant to delete this bookmark?":                              538,
	"Do you really whant to delete this domain":                                 440,
	"Do you really whant to delete this link":                                   608,
	"Do you really whant to delete this list":                                   627,
	"Do you really whant to delete this list":                                   628,
	"Do you really whant to delete this qr code":                                549,
	"Do you want to connect this organization to mattermost?":                   641,
	"Do you want to connect with slack?":                                        695,
	"Do you want to connect this organization to mattermost?":                   642,
	"Do you want to connect with slack?":                                        696,
	"Do you want to delete":                                                     540,
	"Do you want to proceed?":                                                   647,
	"Do you want to proceed?":                                                   648,
	"Do you want to revoke this personal access token? This can not be undone.": 297,
	"Documentation":                          398,
	"Domain":                                 613,
@@ -196,11 +196,11 @@ var messageKeyToIndex = map[string]int{
	"Domain Not Found":                       221,
	"Domain created successfully":            435,
	"Domain in use. Can not change service.": 255,
	"Domain not found":                       658,
	"Domain not found":                       659,
	"Domain successfully deleted":            438,
	"DomainID is required":                   214,
	"Domains":                                448,
	"Download":                               630,
	"Download":                               631,
	"Download Image":                         545,
	"Drag and drop this button to your web browser toolbar or your bookmarks.": 50,
	"Duplicated short code":               222,
@@ -231,7 +231,7 @@ var messageKeyToIndex = map[string]int{
	"Export":                              450,
	"Export Data":                         550,
	"Export in JSON or HTML":              391,
	"Failed the '%s' tag.":                704,
	"Failed the '%s' tag.":                705,
	"Feature":                             343,
	"Feed":                                499,
	"File format":                         551,
@@ -280,15 +280,15 @@ var messageKeyToIndex = map[string]int{
	"Import from Firefox":  388,
	"Import from Pinboard": 387,
	"Import from Safari":   390,
	"In order to interact with Link Taco you have to connect you slack account with your link user":          691,
	"In order to interact with the mattermost you have to connect your account with your link user":          646,
	"In order to interact with Link Taco you have to connect you slack account with your link user":          692,
	"In order to interact with the mattermost you have to connect your account with your link user":          647,
	"In the left sidebar select the folder that you want to export":                                          566,
	"In the left sidebar select the folder you want to export. To export all bookmarks select All Bookmarks": 561,
	"In the top bookmark bar click on the three points at the top right":                                     567,
	"Inactive Domain":                                  338,
	"Include Tags":                                     504,
	"Informative URL":                                  322,
	"Installed successfully":                           663,
	"Installed successfully":                           664,
	"Instructions":                                     572,
	"Integrations":                                     419,
	"Invalid Confirmation Target":                      174,
@@ -304,17 +304,18 @@ var messageKeyToIndex = map[string]int{
	"Invalid default permission value":                 139,
	"Invalid domain ID.":                               209,
	"Invalid domain name.":                             197,
	"Invalid email and/or password":                    697,
	"Invalid domain value given":                       626,
	"Invalid email and/or password":                    698,
	"Invalid email format":                             160,
	"Invalid level value.":                             250,
	"Invalid listing ID provided":                      275,
	"Invalid listing for specified organization":       276,
	"Invalid orgType":                                  248,
	"Invalid organization given":                       666,
	"Invalid organization given":                       667,
	"Invalid origin source for importer.":              280,
	"Invalid permissions for Organization ID":          203,
	"Invalid service value.":                           198,
	"Invalid slack response":                           694,
	"Invalid slack response":                           695,
	"Invalid status value.":                            251,
	"Invalid tag cloud type without organization slug": 265,
	"Invalid visibility":                               249,
@@ -330,7 +331,7 @@ var messageKeyToIndex = map[string]int{
	"Link Listings":        362,
	"Link Lists":           417,
	"Link Not Found":       152,
	"Link Search":          683,
	"Link Search":          684,
	"Link Short Not Found": 269,
	"Link Shortening":      369,
	"Link Shortner":        430,
@@ -348,7 +349,7 @@ var messageKeyToIndex = map[string]int{
	"Links - Reset your password":          74,
	"Linktaco: Invitation to Register":     259,
	"List Not Found":                       240,
	"List successfully deleted":            626,
	"List successfully deleted":            627,
	"Listing":                              368,
	"Listing Link Not Found":               236,
	"Listing Not Found":                    232,
@@ -368,7 +369,7 @@ var messageKeyToIndex = map[string]int{
	"Mark as read":                         516,
	"Mark as unread":                       517,
	"MatterMost Integration":               384,
	"Mattermost successfully disconnected": 637,
	"Mattermost successfully disconnected": 638,
	"Member List":                          477,
	"Member added succesxfully":            475,
	"Member successfully deleted":          473,
@@ -384,26 +385,26 @@ var messageKeyToIndex = map[string]int{
	"Name may not exceed 255 characters":               199,
	"Name may not exceed 500 characters":               200,
	"New Password":                                     66,
	"New passwords do not match":                       698,
	"New passwords do not match":                       699,
	"Next":                                             15,
	"No Clients":                                       306,
	"No Data":                                          121,
	"No Domain":                                        672,
	"No Domain":                                        673,
	"No Domains":                                       431,
	"No Links":                                         611,
	"No Lists":                                         623,
	"No QR Codes":                                      635,
	"No Short Links":                                   669,
	"No URL argument was given":                        679,
	"No QR Codes":                                      636,
	"No Short Links":                                   670,
	"No URL argument was given":                        680,
	"No accounts? Click here to create one":            87,
	"No audit logs to display":                         14,
	"No default organization found":                    667,
	"No links were found for %s":                       652,
	"No default organization found":                    668,
	"No links were found for %s":                       653,
	"No members":                                       478,
	"No organization found":                            653,
	"No organization found":                            654,
	"No organizations":                                 498,
	"No personal organization found for this user":     196,
	"No slack connection found":                        649,
	"No slack connection found":                        650,
	"No, nevermind":                                    299,
	"Not Found":                                        263,
	"Note":                                             578,
@@ -435,8 +436,8 @@ var messageKeyToIndex = map[string]int{
	"Organization Not Found":                               132,
	"Organization Slug is required for user level domains": 253,
	"Organization created successfully":                    459,
	"Organization linked successfully with mattermost":     643,
	"Organization linked successfully with slack":          696,
	"Organization linked successfully with mattermost":     644,
	"Organization linked successfully with slack":          697,
	"Organization not found.":                              274,
	"Organization updated successfully":                    462,
	"Organizations":                                        36,
@@ -459,12 +460,12 @@ var messageKeyToIndex = map[string]int{
	"Personal":               345,
	"Personal Access Tokens": 284,
	"Personal Tokens":        595,
	"Please click in the following link to tie a org %s":                               662,
	"Please click in the following link to tie a org %s":                               663,
	"Please click the link below:":                                                     261,
	"Please confirm your account":                                                      100,
	"Please enter a valid email address.":                                              702,
	"Please enter a valid phone number.":                                               703,
	"Please link your slack user with link: %s":                                        684,
	"Please enter a valid email address.":                                              703,
	"Please enter a valid phone number.":                                               704,
	"Please link your slack user with link: %s":                                        685,
	"Please login to view multiple tag combos (sorry, this is to help stop bot abuse)": 6,
	"Please upgrade to a Business organization to add members":                         465,
	"Please upgrade your account to reactivate it":                                     340,
@@ -483,10 +484,10 @@ var messageKeyToIndex = map[string]int{
	"Public Post":                    531,
	"Public only":                    352,
	"QR Code Details":                544,
	"QR Code Listing":                633,
	"QR Code Listing":                634,
	"QR Code Not Found":              244,
	"QR Code specific analytics":     375,
	"QR Code succesfully created":    632,
	"QR Code succesfully created":    633,
	"QR Code successfully deleted":   548,
	"QR Codes":                       625,
	"QR Codes powered by Link Taco!": 542,
@@ -530,16 +531,16 @@ var messageKeyToIndex = map[string]int{
	"Service":                                      428,
	"Set up your account, so you can save links.":  60,
	"Settings":                                     30,
	"Short Code":                                   671,
	"Short Code":                                   672,
	"Short Code may not exceed 20 characters":      216,
	"Short Link":                                   668,
	"Short Link successfully deleted":              677,
	"Short Link":                                   669,
	"Short Link successfully deleted":              678,
	"Short Links":                                  598,
	"Short Not Found":                              242,
	"Short link successfully updated.":             675,
	"Short link successfully updated.":             676,
	"Since we're a":                                410,
	"Slack Integration":                            383,
	"Slack successfully disconnected":              688,
	"Slack successfully disconnected":              689,
	"Slug":                                         31,
	"Slug is required":                             130,
	"Slug is required.":                            224,
@@ -555,11 +556,11 @@ var messageKeyToIndex = map[string]int{
	"Something went wrong. Impossible to get the member data":                                                                    476,
	"Something went wrong. The QR Code could not be deleted.":                                                                    547,
	"Something went wrong. The link could not be deleted.":                                                                       606,
	"Something went wrong. The user could not be linked.":                                                                        692,
	"Something went wrong. The user could not be linked.":                                                                        693,
	"Something went wrong. This bookmark could not be deleted.":                                                                  536,
	"Something went wrong. This member could not be deleted: %s":                                                                 472,
	"Sorry, free accounts do not support Mattermost Integration. Please upgrade to continue":                                     644,
	"Sorry, free accounts do not support Slack Integration. Please upgrade to continue":                                          690,
	"Sorry, free accounts do not support Mattermost Integration. Please upgrade to continue":                                     645,
	"Sorry, free accounts do not support Slack Integration. Please upgrade to continue":                                          691,
	"Sorry, you have exceeded the amount of free accounts available. Please update your current free account to create one more": 456,
	"Source":                             558,
	"Spanish":                            27,
@@ -580,17 +581,17 @@ var messageKeyToIndex = map[string]int{
	"The %s domain is currently inactive":                            339,
	"The User is not verified":                                       177,
	"The User you try to invite is not verified":                     165,
	"The code is required":                                           656,
	"The code is required":                                           657,
	"The domain is already registered in the system":                 205,
	"The domain is required":                                         657,
	"The domain is required":                                         658,
	"The file is required":                                           281,
	"The file submitted for this source should be html":              282,
	"The file submitted for this source should be json":              283,
	"The member was removed successfully":                            170,
	"The passwords you entered do not match.":                        17,
	"The text to be searched is required":                            651,
	"The title is required":                                          654,
	"The url is required":                                            655,
	"The text to be searched is required":                            652,
	"The title is required":                                          655,
	"The url is required":                                            656,
	"The user for given email is not a member of given organization": 169,
	"The user was added successfully":                                178,
	"There is already a default listing for this domain":             229,
@@ -601,9 +602,9 @@ var messageKeyToIndex = map[string]int{
	"This email domain is not allowed":                                                                       162,
	"This email is already registered":                                                                       187,
	"This feature is not allowed for free user. Please upgrade.":                                             552,
	"This feature is restricted to free accounts. Please upgrade.":                                           642,
	"This feature is restricted to free accounts. Please upgrade.":                                           643,
	"This feed has no links. Booo!":                                                                          509,
	"This field is required.":                                                                                701,
	"This field is required.":                                                                                702,
	"This function is only allowed for business users.":                                                      163,
	"This function is only allowed for paid users.":                                                          204,
	"This function is only allowed for paid users. Please upgrade":                                           1,
@@ -616,7 +617,7 @@ var messageKeyToIndex = map[string]int{
	"This shortCode can not be used. Please chose another one":                                               217,
	"This slug can not be used. Please chose another one":                                                    134,
	"This special link allows you to save to LinkTaco directly by using a bookmark in your web browser.":     49,
	"This team is already tied to an organization":                                                           640,
	"This team is already tied to an organization":                                                           641,
	"This user does not follow this org":                                                                     246,
	"This user is not allowed to perform this action":                                                        153,
	"This username can not be used. Please chose another one":                                                188,
@@ -637,7 +638,7 @@ var messageKeyToIndex = map[string]int{
	"Tour":                                 585,
	"Try It FREE":                          353,
	"URL":                                  603,
	"URL Shortening powered by Link Taco!": 678,
	"URL Shortening powered by Link Taco!": 679,
	"URL is required.":                     213,
	"URL may not exceed 2048 characters":   142,
	"Unable to delete. Domain has active short links or link listings": 212,
@@ -660,31 +661,31 @@ var messageKeyToIndex = map[string]int{
	"Update List":         612,
	"Update Note":         577,
	"Update Organization": 460,
	"Update Short Link":   674,
	"Update Short Link":   675,
	"Update email":        42,
	"Upgrade Org":         45,
	"Upgrade the organization to activate them.":                 529,
	"Upgrade your account to support private bookmarks.":         571,
	"Upgrade your organization to paid to view the detail stats": 118,
	"Url is required": 660,
	"Url is required": 661,
	"Use commas to separate your tags. Example: tag 1, tag 2, tag 3": 485,
	"User Not Found":                         176,
	"User connected successfully":            648,
	"User connected successfully":            649,
	"User email is required":                 158,
	"User follows %s":                        245,
	"User is not authenticated":              699,
	"User is not authenticated":              700,
	"User not found for given email":         168,
	"User unfollows %s":                      247,
	"Username":                               23,
	"Username is required":                   182,
	"Username may not exceed 150 characters": 183,
	"View":                                   634,
	"View":                                   635,
	"View Audit Logs":                        46,
	"Visibility":                             481,
	"We sent you a confirmation email. Please click the link in that email to confirm your account.":     62,
	"We sent you a direct message with instructions":                                                     685,
	"We sent you a direct message with instructions":                                                     686,
	"We sent you a new confirmation email. Please click the link in that email to confirm your account.": 105,
	"We sent you a private msg": 650,
	"We sent you a private msg": 651,
	"Weeks":                     113,
	"Welcome to LinkTaco!":      401,
	"Welcome to LinkTaco! Here you can mix all your link saving and sharing needs in one tight little bundle. Much like a taco. A link taco if you will.": 4,
@@ -718,12 +719,12 @@ var messageKeyToIndex = map[string]int{
	"Your bookmark import is being processed. We will notify you once it's complete.": 576,
	"Your feed is empty :( Go follow some people. Try the Popular or Recent feeds to find some interesting people to follow.": 502,
	"Your importing into a free account / organization, any private pinboard bookmarks will be marked restricted.":            570,
	"Your link was successfully saved. Details here: %s":                                                                      682,
	"Your short link was successfully created: %s":                                                                            681,
	"Your link was successfully saved. Details here: %s":                                                                      683,
	"Your short link was successfully created: %s":                                                                            682,
	"and login": 73,
	"bookmark":  490,
	"bookmark, note, detail, popular, links, linktaco": 534,
	"done":              665,
	"done":              666,
	"for":               429,
	"invalid domain ID": 210,
	"is a third-party application operated by": 329,
@@ -742,11 +743,11 @@ var messageKeyToIndex = map[string]int{
	"saved":                                  512,
	"short-service-domain is not configured": 220,
	"social bookmarks, bookmarking, links, link sharing, link shortening, link listings, bookmarks, link saving, qr codes, analytics": 5,
	"something went wrong: %s":                        664,
	"something went wrong: %s":                        665,
	"would like to access to your Link Taco account.": 328,
}

var enIndex = []uint32{ // 706 elements
var enIndex = []uint32{ // 707 elements
	// Entry 0 - 1F
	0x00000000, 0x00000010, 0x0000004d, 0x00000061,
	0x000000af, 0x00000143, 0x000001c3, 0x00000214,
@@ -923,33 +924,33 @@ var enIndex = []uint32{ // 706 elements
	0x000047cd, 0x000047d9, 0x000047e0, 0x000047eb,
	0x000047fa, 0x00004800, 0x0000480b, 0x00004818,
	0x00004836, 0x00004842, 0x00004863, 0x00004870,
	0x00004879, 0x00004887, 0x00004890, 0x000048aa,
	0x000048d2, 0x000048e1, 0x000048e8, 0x000048f1,
	0x00004909, 0x00004925, 0x00004935, 0x0000493a,
	0x00004946, 0x0000495c, 0x00004981, 0x000049c4,
	0x00004879, 0x00004887, 0x00004890, 0x000048ab,
	0x000048c5, 0x000048ed, 0x000048fc, 0x00004903,
	0x0000490c, 0x00004924, 0x00004940, 0x00004950,
	0x00004955, 0x00004961, 0x00004977, 0x0000499c,
	// Entry 280 - 29F
	0x000049d7, 0x00004a04, 0x00004a3c, 0x00004a79,
	0x00004aaa, 0x00004b01, 0x00004b0e, 0x00004b6c,
	0x00004b84, 0x00004ba0, 0x00004bba, 0x00004bd4,
	0x00004bf8, 0x00004c16, 0x00004c2c, 0x00004c42,
	0x00004c56, 0x00004c6b, 0x00004c82, 0x00004c93,
	0x00004cb7, 0x00004cc7, 0x00004cea, 0x00004d20,
	0x00004d37, 0x00004d53, 0x00004d58, 0x00004d73,
	0x00004d91, 0x00004d9c, 0x00004dab, 0x00004dbd,
	0x000049df, 0x000049f2, 0x00004a1f, 0x00004a57,
	0x00004a94, 0x00004ac5, 0x00004b1c, 0x00004b29,
	0x00004b87, 0x00004b9f, 0x00004bbb, 0x00004bd5,
	0x00004bef, 0x00004c13, 0x00004c31, 0x00004c47,
	0x00004c5d, 0x00004c71, 0x00004c86, 0x00004c9d,
	0x00004cae, 0x00004cd2, 0x00004ce2, 0x00004d05,
	0x00004d3b, 0x00004d52, 0x00004d6e, 0x00004d73,
	0x00004d8e, 0x00004dac, 0x00004db7, 0x00004dc6,
	// Entry 2A0 - 2BF
	0x00004dc8, 0x00004dd2, 0x00004dfa, 0x00004e0c,
	0x00004e2d, 0x00004e3f, 0x00004e5f, 0x00004e84,
	0x00004e9e, 0x00004eb6, 0x00004ee6, 0x00004f1c,
	0x00004f28, 0x00004f55, 0x00004f84, 0x00004f8d,
	0x00004f9e, 0x00004fbe, 0x00004ffc, 0x0000504e,
	0x000050ac, 0x000050e0, 0x000050fb, 0x00005112,
	0x00005135, 0x00005161, 0x0000517f, 0x0000519a,
	0x000051b4, 0x000051d8, 0x000051f0, 0x00005214,
	0x00004dd8, 0x00004de3, 0x00004ded, 0x00004e15,
	0x00004e27, 0x00004e48, 0x00004e5a, 0x00004e7a,
	0x00004e9f, 0x00004eb9, 0x00004ed1, 0x00004f01,
	0x00004f37, 0x00004f43, 0x00004f70, 0x00004f9f,
	0x00004fa8, 0x00004fb9, 0x00004fd9, 0x00005017,
	0x00005069, 0x000050c7, 0x000050fb, 0x00005116,
	0x0000512d, 0x00005150, 0x0000517c, 0x0000519a,
	0x000051b5, 0x000051cf, 0x000051f3, 0x0000520b,
	// Entry 2C0 - 2DF
	0x00005237, 0x0000524f,
} // Size: 2848 bytes
	0x0000522f, 0x00005252, 0x0000526a,
} // Size: 2852 bytes

const enData string = "" + // Size: 21071 bytes
const enData string = "" + // Size: 21098 bytes
	"\x02Restricted Page\x02This function is only allowed for paid users. Ple" +
	"ase upgrade\x02Continue to Upgrade\x02Social bookmarking plus link shari" +
	"ng, shortening and listings all in one app.\x02Welcome to LinkTaco! Here" +
@@ -1235,47 +1236,47 @@ const enData string = "" + // Size: 21071 bytes
	"o Links\x02Update List\x02Domain\x02Is Default\x02Delete Picture\x02Othe" +
	"r\x02Other Name\x02Social Links\x02Listing successfully updated.\x02Crea" +
	"te List\x02A list was successfully created.\x02Manage Links\x02No Lists" +
	"\x02Creation Date\x02QR Codes\x02List successfully deleted\x02Do you rea" +
	"lly whant to delete this list\x02Create QR Code\x02Create\x02Download" +
	"\x02Custom background image\x02QR Code succesfully created\x02QR Code Li" +
	"sting\x02View\x02No QR Codes\x02Disconnect Mattermost\x02Mattermost succ" +
	"essfully disconnected\x02Do you really want to disconnect this organizat" +
	"ion from mattermost\x02Connect Mattermost\x02This team is already tied t" +
	"o an organization\x02Do you want to connect this organization to matterm" +
	"ost?\x02This feature is restricted to free accounts. Please upgrade.\x02" +
	"Organization linked successfully with mattermost\x02Sorry, free accounts" +
	" do not support Mattermost Integration. Please upgrade to continue\x02Co" +
	"nnect User\x02In order to interact with the mattermost you have to conne" +
	"ct your account with your link user\x02Do you want to proceed?\x02User c" +
	"onnected successfully\x02No slack connection found\x02We sent you a priv" +
	"ate msg\x02The text to be searched is required\x02No links were found fo" +
	"r %[1]s\x02No organization found\x02The title is required\x02The url is " +
	"required\x02The code is required\x02The domain is required\x02Domain not" +
	" found\x02A new short was created succesfully\x02Url is required\x02A ne" +
	"w link was created succesfully\x02Please click in the following link to " +
	"tie a org %[1]s\x02Installed successfully\x02something went wrong: %[1]s" +
	"\x02done\x02Invalid organization given\x02No default organization found" +
	"\x02Short Link\x02No Short Links\x02Create Short Link\x02Short Code\x02N" +
	"o Domain\x02An short link was successfully created.\x02Update Short Link" +
	"\x02Short link successfully updated.\x02Delete Short Link\x02Short Link " +
	"successfully deleted\x02URL Shortening powered by Link Taco!\x02No URL a" +
	"rgument was given\x02%[1]s: domain not found\x02Your short link was succ" +
	"essfully created: %[1]s\x02Your link was successfully saved. Details her" +
	"e: %[1]s\x02Link Search\x02Please link your slack user with link: %[1]s" +
	"\x02We sent you a direct message with instructions\x02Add Link\x02Discon" +
	"nect Slack\x02Slack successfully disconnected\x02Do you really want to d" +
	"isconnect this organization from slack\x02Sorry, free accounts do not su" +
	"pport Slack Integration. Please upgrade to continue\x02In order to inter" +
	"act with Link Taco you have to connect you slack account with your link " +
	"user\x02Something went wrong. The user could not be linked.\x02Connect t" +
	"o Slack Workspace\x02Invalid slack response\x02Do you want to connect wi" +
	"th slack?\x02Organization linked successfully with slack\x02Invalid emai" +
	"l and/or password\x02New passwords do not match\x02User is not authentic" +
	"ated\x02Current password given is incorrect\x02This field is required." +
	"\x02Please enter a valid email address.\x02Please enter a valid phone nu" +
	"mber.\x02Failed the '%[1]s' tag."
	"\x02Creation Date\x02QR Codes\x02Invalid domain value given\x02List succ" +
	"essfully deleted\x02Do you really whant to delete this list\x02Create QR" +
	" Code\x02Create\x02Download\x02Custom background image\x02QR Code succes" +
	"fully created\x02QR Code Listing\x02View\x02No QR Codes\x02Disconnect Ma" +
	"ttermost\x02Mattermost successfully disconnected\x02Do you really want t" +
	"o disconnect this organization from mattermost\x02Connect Mattermost\x02" +
	"This team is already tied to an organization\x02Do you want to connect t" +
	"his organization to mattermost?\x02This feature is restricted to free ac" +
	"counts. Please upgrade.\x02Organization linked successfully with matterm" +
	"ost\x02Sorry, free accounts do not support Mattermost Integration. Pleas" +
	"e upgrade to continue\x02Connect User\x02In order to interact with the m" +
	"attermost you have to connect your account with your link user\x02Do you" +
	" want to proceed?\x02User connected successfully\x02No slack connection " +
	"found\x02We sent you a private msg\x02The text to be searched is require" +
	"d\x02No links were found for %[1]s\x02No organization found\x02The title" +
	" is required\x02The url is required\x02The code is required\x02The domai" +
	"n is required\x02Domain not found\x02A new short was created succesfully" +
	"\x02Url is required\x02A new link was created succesfully\x02Please clic" +
	"k in the following link to tie a org %[1]s\x02Installed successfully\x02" +
	"something went wrong: %[1]s\x02done\x02Invalid organization given\x02No " +
	"default organization found\x02Short Link\x02No Short Links\x02Create Sho" +
	"rt Link\x02Short Code\x02No Domain\x02An short link was successfully cre" +
	"ated.\x02Update Short Link\x02Short link successfully updated.\x02Delete" +
	" Short Link\x02Short Link successfully deleted\x02URL Shortening powered" +
	" by Link Taco!\x02No URL argument was given\x02%[1]s: domain not found" +
	"\x02Your short link was successfully created: %[1]s\x02Your link was suc" +
	"cessfully saved. Details here: %[1]s\x02Link Search\x02Please link your " +
	"slack user with link: %[1]s\x02We sent you a direct message with instruc" +
	"tions\x02Add Link\x02Disconnect Slack\x02Slack successfully disconnected" +
	"\x02Do you really want to disconnect this organization from slack\x02Sor" +
	"ry, free accounts do not support Slack Integration. Please upgrade to co" +
	"ntinue\x02In order to interact with Link Taco you have to connect you sl" +
	"ack account with your link user\x02Something went wrong. The user could " +
	"not be linked.\x02Connect to Slack Workspace\x02Invalid slack response" +
	"\x02Do you want to connect with slack?\x02Organization linked successful" +
	"ly with slack\x02Invalid email and/or password\x02New passwords do not m" +
	"atch\x02User is not authenticated\x02Current password given is incorrect" +
	"\x02This field is required.\x02Please enter a valid email address.\x02Pl" +
	"ease enter a valid phone number.\x02Failed the '%[1]s' tag."

var esIndex = []uint32{ // 706 elements
var esIndex = []uint32{ // 707 elements
	// Entry 0 - 1F
	0x00000000, 0x00000014, 0x0000006a, 0x00000089,
	0x000000e0, 0x00000199, 0x00000233, 0x000002a4,
@@ -1453,32 +1454,32 @@ var esIndex = []uint32{ // 706 elements
	0x00005578, 0x0000557d, 0x00005589, 0x00005598,
	0x000055b0, 0x000055bc, 0x000055dc, 0x000055ea,
	0x000055f5, 0x00005608, 0x00005614, 0x0000562f,
	0x00005658, 0x00005669, 0x0000566f, 0x00005679,
	0x00005699, 0x000056b6, 0x000056ca, 0x000056ce,
	0x000056e1, 0x000056f8, 0x0000571b, 0x00005758,
	0x0000564a, 0x00005673, 0x00005684, 0x0000568a,
	0x00005694, 0x000056b4, 0x000056d1, 0x000056e5,
	0x000056e9, 0x000056fc, 0x00005713, 0x00005736,
	// Entry 280 - 29F
	0x00005771, 0x000057ab, 0x000057e0, 0x00005829,
	0x00005853, 0x000058b6, 0x000058c7, 0x00005921,
	0x00005933, 0x00005950, 0x00005973, 0x00005992,
	0x000059b6, 0x000059da, 0x000059f6, 0x00005a0e,
	0x00005a22, 0x00005a3a, 0x00005a52, 0x00005a68,
	0x00005a8c, 0x00005a9d, 0x00005ac3, 0x00005b12,
	0x00005b28, 0x00005b3f, 0x00005b45, 0x00005b6b,
	0x00005b98, 0x00005ba3, 0x00005bb4, 0x00005bc5,
	0x00005773, 0x0000578c, 0x000057c6, 0x000057fb,
	0x00005844, 0x0000586e, 0x000058d1, 0x000058e2,
	0x0000593c, 0x0000594e, 0x0000596b, 0x0000598e,
	0x000059ad, 0x000059d1, 0x000059f5, 0x00005a11,
	0x00005a29, 0x00005a3d, 0x00005a55, 0x00005a6d,
	0x00005a83, 0x00005aa7, 0x00005ab8, 0x00005ade,
	0x00005b2d, 0x00005b43, 0x00005b5a, 0x00005b60,
	0x00005b86, 0x00005bb3, 0x00005bbe, 0x00005bcf,
	// Entry 2A0 - 2BF
	0x00005bd3, 0x00005bdf, 0x00005c03, 0x00005c19,
	0x00005c3b, 0x00005c4f, 0x00005c6f, 0x00005c8b,
	0x00005cb2, 0x00005ccf, 0x00005cfc, 0x00005d35,
	0x00005d41, 0x00005d7a, 0x00005dab, 0x00005db8,
	0x00005dcd, 0x00005def, 0x00005e1d, 0x00005e94,
	0x00005ef6, 0x00005f2a, 0x00005f45, 0x00005f62,
	0x00005f7f, 0x00005fac, 0x00005fc9, 0x00005fee,
	0x0000600e, 0x00006032, 0x0000604a, 0x00006071,
	0x00005be0, 0x00005bee, 0x00005bfa, 0x00005c1e,
	0x00005c34, 0x00005c56, 0x00005c6a, 0x00005c8a,
	0x00005ca6, 0x00005ccd, 0x00005cea, 0x00005d17,
	0x00005d50, 0x00005d5c, 0x00005d95, 0x00005dc6,
	0x00005dd3, 0x00005de8, 0x00005e0a, 0x00005e38,
	0x00005eaf, 0x00005f11, 0x00005f45, 0x00005f60,
	0x00005f7d, 0x00005f9a, 0x00005fc7, 0x00005fe4,
	0x00006009, 0x00006029, 0x0000604d, 0x00006065,
	// Entry 2C0 - 2DF
	0x00006099, 0x000060ac,
} // Size: 2848 bytes
	0x0000608c, 0x000060b4, 0x000060c7,
} // Size: 2852 bytes

const esData string = "" + // Size: 24748 bytes
const esData string = "" + // Size: 24775 bytes
	"\x02Página Restringida\x02Esta función solo está permitida para usuarios" +
	" de pago. Por favor actualice su plan\x02Continuar para actualizar plan" +
	"\x02Marcadores sociales más compartir enlaces, acortar y listar, todo en" +
@@ -1806,46 +1807,47 @@ const esData string = "" + // Size: 24748 bytes
	"ks\x02Actualizar Lista\x02Dominio\x02Es por defecto\x02Eliminar Foto\x02" +
	"Otro\x02Otro Nombre\x02Links Sociales\x02Lista creada con éxito\x02Crear" +
	" Lista\x02Una lista fue creada con éxito\x02Manejar Links\x02Sin Listas" +
	"\x02Fecha de Creación\x02Códigos QR\x02Lista eliminada con éxito\x02¿Rea" +
	"lmente quieres eliminar esta lista?\x02Crear Código QR\x02Crear\x02Desca" +
	"rgar\x02Fondo de pantalla personalizado\x02Código QR creado con éxito" +
	"\x02Código QR de Lista\x02Ver\x02No hay Códigos QR\x02Desconectar Matter" +
	"most\x02Mattermost desconectado con éxito\x02Desea realmente desconectar" +
	" esta organización de mattermost\x02Connectar con Mattermost\x02Este equ" +
	"ipo ya se encuentra vinculado a una organización\x02¿Deseas conectar est" +
	"a organización con mattermost?\x02Esta función está restringida para cue" +
	"ntas gratis. Por favor actualiza\x02La organización fue vinculada con éx" +
	"ito\x02Lo sentimos, las cuentas gratuitas no soportan la integración con" +
	" Mattermost. Por favor actualice\x02Conectar Usuario\x02Para interactuar" +
	" con mattermost tienes que conectar tu cuenta con tu usuario de Link Tac" +
	"o\x02¿Desea proceder?\x02Usuario conectado con éxito\x02Connexión con sl" +
	"ack no encontrada\x02Te enviamos un mensaje privado\x02El texto a ser bu" +
	"scado es requerido\x02No se encuentraron links para %[1]s\x02Organizació" +
	"n no encontrada\x02El título es requerido\x02La url es requerida\x02El c" +
	"ódigo es requerido\x02El dominio es requerido\x02Dominio no encontrado" +
	"\x02A nuevo short fue creado con éxito\x02Url es requerida\x02Un nuevo e" +
	"nlace fue creado con éxito\x02Por favor haga click en el próximo link pa" +
	"ra vincular una organización %[1]s\x02Instalacción exitosa\x02algo salió" +
	" mal: %[1]s\x02hecho\x02Organización inválida proporcionada\x02No se enc" +
	"ontró organización predeterminada\x02Link Corto\x02Sin Links Cortos\x02C" +
	"rear Link Corto\x02Código Corto\x02Sin Dominio\x02Un link corto fue crea" +
	"do con éxito\x02Actualizar Link Corto\x02Link Corto actualizado con éxit" +
	"o\x02Eliminar Link Corto\x02Link Corto eliminado con éxito\x02URL acorta" +
	"da por Link Taco!\x02No se proporcionó un argumento de URL\x02%[1]s: dom" +
	"inio no encontrado\x02Tu enlace corto fue creado con éxito: %[1]s\x02Tu " +
	"enlace fue guardado con éxito. Detalles aquí: %[1]s\x02Buscar Link\x02Po" +
	"r favor vincule tu usuario de slack con el link: %[1]s\x02Te enviamos un" +
	" mensaje directo con instrucciones\x02Agregar Link\x02Desconectar de Sla" +
	"ck\x02Slack fue desconectado con éxito\x02Desea desconectar esta organiz" +
	"ación de slack\x02Lo sentimos, las cuentas gratis no soportan integració" +
	"n con Slack. Por favor actualice su subscripcón para continuar\x02Para i" +
	"nteractuar con Link Taco tienes que conectar tu cuenta de slack con tu u" +
	"suario de Link Taco\x02Algo salió mal. El usuario no puede ser vinculado" +
	".\x02Conectar a Slack Workspace\x02Respuesta de slack inválida\x02¿Desea" +
	"s conectar con Slack?\x02Organización vinculada con éxito con slack\x02E" +
	"mail y/o password inválido\x02Las nuevas contraseñas no coinciden\x02El " +
	"usuario no está autenticado\x02La contraseña actual es incorrecta\x02Est" +
	"e campo es requerido\x02Por favor introduzca un correo válido\x02Por fav" +
	"or introduzca un número válido\x02El '%[1]s' falló."
	"\x02Fecha de Creación\x02Códigos QR\x02Valor de dominio inválido\x02List" +
	"a eliminada con éxito\x02¿Realmente quieres eliminar esta lista?\x02Crea" +
	"r Código QR\x02Crear\x02Descargar\x02Fondo de pantalla personalizado\x02" +
	"Código QR creado con éxito\x02Código QR de Lista\x02Ver\x02No hay Código" +
	"s QR\x02Desconectar Mattermost\x02Mattermost desconectado con éxito\x02D" +
	"esea realmente desconectar esta organización de mattermost\x02Connectar " +
	"con Mattermost\x02Este equipo ya se encuentra vinculado a una organizaci" +
	"ón\x02¿Deseas conectar esta organización con mattermost?\x02Esta funció" +
	"n está restringida para cuentas gratis. Por favor actualiza\x02La organi" +
	"zación fue vinculada con éxito\x02Lo sentimos, las cuentas gratuitas no " +
	"soportan la integración con Mattermost. Por favor actualice\x02Conectar " +
	"Usuario\x02Para interactuar con mattermost tienes que conectar tu cuenta" +
	" con tu usuario de Link Taco\x02¿Desea proceder?\x02Usuario conectado co" +
	"n éxito\x02Connexión con slack no encontrada\x02Te enviamos un mensaje p" +
	"rivado\x02El texto a ser buscado es requerido\x02No se encuentraron link" +
	"s para %[1]s\x02Organización no encontrada\x02El título es requerido\x02" +
	"La url es requerida\x02El código es requerido\x02El dominio es requerido" +
	"\x02Dominio no encontrado\x02A nuevo short fue creado con éxito\x02Url e" +
	"s requerida\x02Un nuevo enlace fue creado con éxito\x02Por favor haga cl" +
	"ick en el próximo link para vincular una organización %[1]s\x02Instalacc" +
	"ión exitosa\x02algo salió mal: %[1]s\x02hecho\x02Organización inválida p" +
	"roporcionada\x02No se encontró organización predeterminada\x02Link Corto" +
	"\x02Sin Links Cortos\x02Crear Link Corto\x02Código Corto\x02Sin Dominio" +
	"\x02Un link corto fue creado con éxito\x02Actualizar Link Corto\x02Link " +
	"Corto actualizado con éxito\x02Eliminar Link Corto\x02Link Corto elimina" +
	"do con éxito\x02URL acortada por Link Taco!\x02No se proporcionó un argu" +
	"mento de URL\x02%[1]s: dominio no encontrado\x02Tu enlace corto fue crea" +
	"do con éxito: %[1]s\x02Tu enlace fue guardado con éxito. Detalles aquí: " +
	"%[1]s\x02Buscar Link\x02Por favor vincule tu usuario de slack con el lin" +
	"k: %[1]s\x02Te enviamos un mensaje directo con instrucciones\x02Agregar " +
	"Link\x02Desconectar de Slack\x02Slack fue desconectado con éxito\x02Dese" +
	"a desconectar esta organización de slack\x02Lo sentimos, las cuentas gra" +
	"tis no soportan integración con Slack. Por favor actualice su subscripcó" +
	"n para continuar\x02Para interactuar con Link Taco tienes que conectar t" +
	"u cuenta de slack con tu usuario de Link Taco\x02Algo salió mal. El usua" +
	"rio no puede ser vinculado.\x02Conectar a Slack Workspace\x02Respuesta d" +
	"e slack inválida\x02¿Deseas conectar con Slack?\x02Organización vinculad" +
	"a con éxito con slack\x02Email y/o password inválido\x02Las nuevas contr" +
	"aseñas no coinciden\x02El usuario no está autenticado\x02La contraseña a" +
	"ctual es incorrecta\x02Este campo es requerido\x02Por favor introduzca u" +
	"n correo válido\x02Por favor introduzca un número válido\x02El '%[1]s' f" +
	"alló."

	// Total table size 51515 bytes (50KiB); checksum: B449DFD5
	// Total table size 51577 bytes (50KiB); checksum: 811A805E
diff --git a/internal/translations/locales/en/out.gotext.json b/internal/translations/locales/en/out.gotext.json
index 82f811b..45e5b4a 100644
--- a/internal/translations/locales/en/out.gotext.json
+++ b/internal/translations/locales/en/out.gotext.json
@@ -4579,6 +4579,13 @@
            "translatorComment": "Copied from source.",
            "fuzzy": true
        },
        {
            "id": "Invalid domain value given",
            "message": "Invalid domain value given",
            "translation": "Invalid domain value given",
            "translatorComment": "Copied from source.",
            "fuzzy": true
        },
        {
            "id": "List successfully deleted",
            "message": "List successfully deleted",
diff --git a/internal/translations/locales/es/messages.gotext.json b/internal/translations/locales/es/messages.gotext.json
index cc891da..ed7f836 100644
--- a/internal/translations/locales/es/messages.gotext.json
+++ b/internal/translations/locales/es/messages.gotext.json
@@ -3327,6 +3327,11 @@
            "message": "QR Codes",
            "translation": "Códigos QR"
        },
        {
            "id": "Invalid domain value given",
            "message": "Invalid domain value given",
            "translation": "Valor de dominio inválido"
        },
        {
            "id": "List successfully deleted",
            "message": "List successfully deleted",
diff --git a/internal/translations/locales/es/out.gotext.json b/internal/translations/locales/es/out.gotext.json
index cc891da..ed7f836 100644
--- a/internal/translations/locales/es/out.gotext.json
+++ b/internal/translations/locales/es/out.gotext.json
@@ -3327,6 +3327,11 @@
            "message": "QR Codes",
            "translation": "Códigos QR"
        },
        {
            "id": "Invalid domain value given",
            "message": "Invalid domain value given",
            "translation": "Valor de dominio inválido"
        },
        {
            "id": "List successfully deleted",
            "message": "List successfully deleted",
diff --git a/list/routes.go b/list/routes.go
index 2f82358..2fe20c2 100644
--- a/list/routes.go
+++ b/list/routes.go
@@ -1043,6 +1043,7 @@ func (s *Service) ListingList(c echo.Context) error {
	pd.Data["clear"] = lt.Translate("Clear")
	pd.Data["is_default"] = lt.Translate("Is Default")
	pd.Data["logs"] = lt.Translate("View Audit Logs")
	pd.Data["domain"] = lt.Translate("Domain")

	type GraphQLResponse struct {
		Listings struct {
@@ -1057,8 +1058,8 @@ func (s *Service) ListingList(c echo.Context) error {

	var result GraphQLResponse
	op := gqlclient.NewOperation(
		`query GetListings($orgSlug: String!, $after: Cursor, $before: Cursor, $tag: String, $excludeTag: String) {
			getListings(input: {orgSlug: $orgSlug, after: $after, before: $before, tag: $tag, excludeTag: $excludeTag}) {
		`query GetListings($orgSlug: String!, $after: Cursor, $before: Cursor, $tag: String, $excludeTag: String, $domain: Int) {
			getListings(input: {orgSlug: $orgSlug, after: $after, before: $before, tag: $tag, excludeTag: $excludeTag, domainId: $domain}) {
				result {
					id
					title
@@ -1104,6 +1105,16 @@ func (s *Service) ListingList(c echo.Context) error {
		op.Var("before", c.QueryParam("prev"))
	}

	var domID int
	if c.QueryParam("domain") != "" {
		domID, err = strconv.Atoi(c.QueryParam("domain"))
		if err != nil {
			messages.Warning(c, lt.Translate("Invalid domain value given"))
		} else {
			op.Var("domain", domID)
		}
	}

	err = links.Execute(c.Request().Context(), op, &result)
	if err != nil {
		if graphError, ok := err.(*gqlclient.Error); ok {
@@ -1114,16 +1125,43 @@ func (s *Service) ListingList(c echo.Context) error {
	if c.QueryParam("prev") != "" {
		slices.Reverse(result.Listings.Result)
	}

	// Get domain list.
	opts = &database.FilterOptions{
		Filter: sq.And{
			sq.Eq{"d.service": models.DomainServiceList},
			sq.Eq{"d.status": models.DomainStatusApproved},
			sq.Eq{"d.is_active": true},
			sq.Or{
				sq.And{
					sq.Eq{"d.level": models.DomainLevelUser},
					sq.Eq{"d.org_id": org.ID},
				},
				sq.And{
					sq.Eq{"d.level": models.DomainLevelSystem},
					sq.Eq{"d.org_id": nil},
				},
			},
		},
		OrderBy: "d.lookup_name",
	}
	domains, err := models.GetDomains(c.Request().Context(), opts)
	if err != nil {
		return err
	}

	gmap := gobwebs.Map{
		"pd":                pd,
		"org":               org,
		"navFlag":           "listing",
		"lists":             result.Listings.Result,
		"tagFilter":         strings.Replace(tag, ",", ", ", -1),
		"excludeTagFilter":  strings.Replace(excludeTag, ",", ", ", -1),
		"tagFilter":         strings.ReplaceAll(tag, ",", ", "),
		"excludeTagFilter":  strings.ReplaceAll(excludeTag, ",", ", "),
		"advancedSearch":    true,
		"queries":           template.URL(queries.Encode()),
		"autoCompleteOrgID": org.ID,
		"domains":           domains,
		"cur_domain":        domID,
	}
	if result.Listings.PageInfo.HasPrevPage {
		gmap["prevURL"] = links.GetPaginationParams(c, "prev", result.Listings.PageInfo.Cursor, "next")
diff --git a/list/routes_test.go b/list/routes_test.go
index 72f30ea..38637a1 100644
--- a/list/routes_test.go
+++ b/list/routes_test.go
@@ -11,6 +11,7 @@ import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"strings"
	"testing"

@@ -138,6 +139,44 @@ func TestHandlers(t *testing.T) {
		c.True(strings.Contains(htmlBody, "Title 2"))
	})

	t.Run("listing list with domain filter", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		
		// Create a domain in the test database
		dbCtx := test.NewDBContext(srv.DB, "America/Managua")
		domain := &models.Domain{
			Name:       "Test List Domain",
			LookupName: "list.example.com",
			Level:      models.DomainLevelSystem,
			Service:    models.DomainServiceList,
			Status:     models.DomainStatusApproved,
			IsActive:   true,
		}
		err := domain.Store(dbCtx)
		c.NoError(err)

		jsonResponse, err := httpmock.NewJsonResponder(http.StatusOK, httpmock.File("samples/listing_list.json"))
		c.NoError(err)
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query", jsonResponse)

		request := httptest.NewRequest(http.MethodGet, "/:slug/list?domain="+strconv.Itoa(domain.ID), nil)
		recorder := httptest.NewRecorder()
		ctx := &server.Context{
			Server:  srv,
			Context: e.NewContext(request, recorder),
			User:    user,
		}
		ctx.SetPath("/:slug/list")
		ctx.SetParamNames("slug")
		ctx.SetParamValues("personal-org")
		err = test.MakeRequest(srv, listService.ListingList, ctx)
		c.NoError(err)
		c.Equal(http.StatusOK, recorder.Code)
		htmlBody := recorder.Body.String()
		c.True(strings.Contains(htmlBody, "list.example.com"))
	})

	t.Run("listing link manage", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
diff --git a/short/routes.go b/short/routes.go
index ebbac75..4c92a39 100644
--- a/short/routes.go
+++ b/short/routes.go
@@ -73,6 +73,8 @@ func (s *Service) LinkShortList(c echo.Context) error {
		return echo.NotFoundHandler(c)
	}

	lt := localizer.GetSessionLocalizer(c)

	type GraphQLResponse struct {
		LinkShorts struct {
			Result   []models.LinkShort `json:"result"`
@@ -86,13 +88,14 @@ func (s *Service) LinkShortList(c echo.Context) error {

	var result GraphQLResponse
	op := gqlclient.NewOperation(
		`query GetLinkShorts($slug: String!, $after: Cursor, $before: Cursor, $tag: String, $excludeTag: String) {
		`query GetLinkShorts($slug: String!, $after: Cursor, $before: Cursor, $tag: String, $excludeTag: String, $domain: Int) {
				getLinkShorts(input: {
						orgSlug: $slug,
						after: $after,
						before: $before,
						tag: $tag,
						excludeTag: $excludeTag,
						domainId: $domain,
					}) {
					result {
						id
@@ -139,6 +142,16 @@ func (s *Service) LinkShortList(c echo.Context) error {
		op.Var("before", c.QueryParam("prev"))
	}

	var domID int
	if c.QueryParam("domain") != "" {
		domID, err = strconv.Atoi(c.QueryParam("domain"))
		if err != nil {
			messages.Warning(c, lt.Translate("Invalid domain value given"))
		} else {
			op.Var("domain", domID)
		}
	}

	err = links.Execute(c.Request().Context(), op, &result)
	if err != nil {
		if graphError, ok := err.(*gqlclient.Error); ok {
@@ -147,7 +160,6 @@ func (s *Service) LinkShortList(c echo.Context) error {
		return err
	}

	lt := localizer.GetSessionLocalizer(c)
	pd := localizer.NewPageData(lt.Translate("Short Links"))
	pd.Data["edit"] = lt.Translate("Edit")
	pd.Data["next"] = lt.Translate("Next")
@@ -166,21 +178,49 @@ func (s *Service) LinkShortList(c echo.Context) error {
	pd.Data["exclude_tags"] = lt.Translate("Exclude Tags")
	pd.Data["apply"] = lt.Translate("Apply")
	pd.Data["clear"] = lt.Translate("Clear")
	pd.Data["domain"] = lt.Translate("Domain")

	linkShorts := result.LinkShorts.Result
	if c.QueryParam("prev") != "" {
		slices.Reverse(linkShorts)
	}

	// Get domain list.
	opts = &database.FilterOptions{
		Filter: sq.And{
			sq.Eq{"d.service": models.DomainServiceShort},
			sq.Eq{"d.status": models.DomainStatusApproved},
			sq.Eq{"d.is_active": true},
			sq.Or{
				sq.And{
					sq.Eq{"d.level": models.DomainLevelUser},
					sq.Eq{"d.org_id": org.ID},
				},
				sq.And{
					sq.Eq{"d.level": models.DomainLevelSystem},
					sq.Eq{"d.org_id": nil},
				},
			},
		},
		OrderBy: "d.lookup_name",
	}
	domains, err := models.GetDomains(c.Request().Context(), opts)
	if err != nil {
		return err
	}

	gmap := gobwebs.Map{
		"pd":                pd,
		"links":             linkShorts,
		"org":               org,
		"navFlag":           "short",
		"tagFilter":         strings.Replace(tag, ",", ", ", -1),
		"excludeTagFilter":  strings.Replace(excludeTag, ",", ", ", -1),
		"tagFilter":         strings.ReplaceAll(tag, ",", ", "),
		"excludeTagFilter":  strings.ReplaceAll(excludeTag, ",", ", "),
		"advancedSearch":    true,
		"queries":           template.URL(queries.Encode()),
		"autoCompleteOrgID": org.ID,
		"domains":           domains,
		"cur_domain":        domID,
	}

	if result.LinkShorts.PageInfo.HasPrevPage {
diff --git a/short/routes_test.go b/short/routes_test.go
index 91b1c91..ec708fa 100644
--- a/short/routes_test.go
+++ b/short/routes_test.go
@@ -11,6 +11,7 @@ import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"strings"
	"testing"

@@ -112,6 +113,44 @@ func TestHandlers(t *testing.T) {
		c.Equal(http.StatusOK, recorder.Code)
	})

	t.Run("short list with domain filter", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		
		// Create a domain in the test database
		dbCtx := test.NewDBContext(srv.DB, "America/Managua")
		domain := &models.Domain{
			Name:       "Test Domain",
			LookupName: "test.example.com",
			Level:      models.DomainLevelSystem,
			Service:    models.DomainServiceShort,
			Status:     models.DomainStatusApproved,
			IsActive:   true,
		}
		err := domain.Store(dbCtx)
		c.NoError(err)

		jsonResponse, err := httpmock.NewJsonResponder(http.StatusOK, httpmock.File("samples/list_short_list.json"))
		c.NoError(err)
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query", jsonResponse)

		request := httptest.NewRequest(http.MethodGet, "/:slug/short?domain="+strconv.Itoa(domain.ID), nil)
		recorder := httptest.NewRecorder()
		ctx := &server.Context{
			Server:  srv,
			Context: e.NewContext(request, recorder),
			User:    loggedInUser,
		}
		ctx.SetPath("/:slug/short")
		ctx.SetParamNames("slug")
		ctx.SetParamValues("personal-org")
		err = test.MakeRequest(srv, shortService.LinkShortList, ctx)
		c.NoError(err)
		c.Equal(http.StatusOK, recorder.Code)
		htmlBody := recorder.Body.String()
		c.True(strings.Contains(htmlBody, "test.example.com"))
	})

	t.Run("short delete", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
diff --git a/static/js/advancedsearch.js b/static/js/advancedsearch.js
index 4b228a4..f800ce2 100644
--- a/static/js/advancedsearch.js
+++ b/static/js/advancedsearch.js
@@ -275,6 +275,15 @@ form.addEventListener("submit", function(e) {
        qURL.set("exclude", excludeValue);
    }

    // Handle domain field
    var domainValue = "";
    if (form.elements["domain"] !== undefined) {
        domainValue = form.elements.domain.value;
        if (domainValue !== "") {
            qURL.set("domain", domainValue);
        }
    }

    if (hasQ) {
        if (qValue === "") {
            form.elements.q.disabled = true;
diff --git a/templates/link_short_list.html b/templates/link_short_list.html
index 0a5c81b..9d0f30e 100644
--- a/templates/link_short_list.html
+++ b/templates/link_short_list.html
@@ -9,16 +9,26 @@
    <section id="advanced-search-div" {{if and (not .tagFilter) (not .excludeTagFilter)}}class="d-none"{{end}}>
        <form method="GET" id="advanced-search-form" action="{{reverse "short:link_short_list" .org.Slug}}">
            <div class="row">
                <div class="col-5">
                <div class="col-4">
                    <label>{{.pd.Data.include_tags}}</label>
                    <input type="text" name="tag" value="{{.tagFilter}}" class="tag-selector" autocomplete="off"/>
                    <div class="d-none autocomplete-tags"></div>
                </div>
                <div class="col-5">
                <div class="col-3">
                    <label>{{.pd.Data.exclude_tags}}</label>
                    <input type="text" name="exclude" value="{{.excludeTagFilter}}" class="tag-selector" autocomplete="off"/>
                    <div class="d-none autocomplete-tags"></div>
                </div>
		<div class="col-3">
                    <label>{{.pd.Data.domain}}</label>
                    <select name="domain">
                      <option value="">---</option>
		      {{ range .domains }}
		      <option value="{{ .ID }}"{{ if eq .ID $.cur_domain }} selected{{ end }}>{{ .LookupName }}</option>
		      {{ end }}
		    </select>
                </div>

                <div class="col-2 is-right">
                    <button type="submit" class="button primary is-small advanced-search-btn">{{.pd.Data.apply}}</button>
                    <a href="{{reverse "short:link_short_list" .org.Slug}}" class="button primary is-small advanced-search-btn">{{.pd.Data.clear}}</a>
diff --git a/templates/listing_list.html b/templates/listing_list.html
index 838e598..5605125 100644
--- a/templates/listing_list.html
+++ b/templates/listing_list.html
@@ -9,16 +9,26 @@
    <section id="advanced-search-div" {{if and (not .tagFilter) (not .excludeTagFilter)}}class="d-none"{{end}}>
        <form method="GET" id="advanced-search-form" action="{{reverse "list:listing_list" .org.Slug}}">
            <div class="row">
                <div class="col-5">
                <div class="col-4">
                    <label>{{.pd.Data.include_tags}}</label>
                    <input type="text" name="tag" value="{{.tagFilter}}" class="tag-selector" autocomplete="off"/>
                    <div class="d-none autocomplete-tags"></div>
                </div>
                <div class="col-5">
                <div class="col-3">
                    <label>{{.pd.Data.exclude_tags}}</label>
                    <input type="text" name="exclude" value="{{.excludeTagFilter}}" class="tag-selector" autocomplete="off"/>
                    <div class="d-none autocomplete-tags"></div>
                </div>
		<div class="col-3">
                    <label>{{.pd.Data.domain}}</label>
                    <select name="domain">
                      <option value="">---</option>
		      {{ range .domains }}
		      <option value="{{ .ID }}"{{ if eq .ID $.cur_domain }} selected{{ end }}>{{ .LookupName }}</option>
		      {{ end }}
		    </select>
                </div>

                <div class="col-2 is-right">
                    <button type="submit" class="button primary is-small advanced-search-btn">{{.pd.Data.apply}}</button>
                    <a href="{{reverse "list:listing_list" .org.Slug}}" class="button primary is-small advanced-search-btn">{{.pd.Data.clear}}</a>
-- 
2.49.1
Details
Message ID
<DBQBA7QUPQDM.3554NNIPEYN7X@netlandish.com>
In-Reply-To
<20250731142951.31235-1-peter@netlandish.com> (view parent)
Sender timestamp
1753950684
DKIM signature
missing
Download raw message
Applied.

To git@git.code.netlandish.com:~netlandish/links
   8bec807..6111dea  master -> master
Reply to thread Export thread (mbox)