Class: Decidim::ShortLink

Inherits:
ApplicationRecord show all
Defined in:
decidim-core/app/models/decidim/short_link.rb

Overview

Short links are a way to reference specific locations within Decidim with shorted URLs, similar to the popular link shortening services. The original reason for creating the feature was to reference long calendar URLs in a more compact way for the URLs to be compatible with the calendar programs. When the URL is long with lots of filtering parameters included in it, it may be too long for specific 3rd party programs.

This feature can be used to link to any URLs or resources in Decidim with a short reference.

Defined Under Namespace

Classes: OutOfCandidatesError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.to(target, mounted_engine, route_name: nil, params: {}) ⇒ Decidim::ShortLink

Finds a matching short link to the same target with exactly the same parameters if it already exists or creates a new one if it does not exist.

Parameters:

  • target (ActiveRecord::Base)

    The target where this short link should link to. Most of the times [Decidim::Organization], [Decidim::Component] or some other top-level record.

  • mounted_engine (String)

    The mounted engine helper name that will be used to generate the link.

  • route_name (String, nil) (defaults to: nil)

    The route name to be linked to. If not defined, the short link will be generated for the root URL for the mounted engine.

  • params (Hash) (defaults to: {})

    The query parameters that should be included in the URL where this short link will redirect to.

Returns:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'decidim-core/app/models/decidim/short_link.rb', line 35

def self.to(target, mounted_engine, route_name: nil, params: {})
  organization =
    if target.is_a?(Decidim::Organization)
      target
    else
      target.try(:organization)
    end

  values = {
    organization:,
    target:,
    mounted_engine_name: mounted_engine,
    route_name:
  }
  existing =
    if params
      where(values).find_by("params = ?::jsonb", params.to_json)
    else
      find_by(values.merge(params: nil))
    end

  existing || create!(values.merge(params:))
end

.unique_identifier_within(organization) ⇒ String

Creates a random unique identifier for any new links. Raises an OutOfCandidatesError if a free candidate cannot be found with 20 tries. In this situation the older records should be removed from the database.

Returns:

  • (String)

    A new unique identifier.



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'decidim-core/app/models/decidim/short_link.rb', line 64

def self.unique_identifier_within(organization)
  1.step do |n|
    raise OutOfCandidatesError if n > 20

    # A-Z, a-z and 0-9
    # 26 + 26 + 10 = 62 possibilities per character
    # 62^10 ≈ 8×10¹⁷ total possibilities
    candidate = SecureRandom.alphanumeric(10)
    next if where(organization:, identifier: candidate).any?

    return candidate
  end
end

Instance Method Details

#route_nameString

Overrides the route_name method to add a default route name for the “root” path in case the route name is not defined for the record.

Returns:

  • (String)

    The route name to link to.



82
83
84
# File 'decidim-core/app/models/decidim/short_link.rb', line 82

def route_name
  super || "root"
end

#short_urlString

Generates the short URL referencing this link.

Returns:

  • (String)

    The short URL that can be used to link to the target.



89
90
91
# File 'decidim-core/app/models/decidim/short_link.rb', line 89

def short_url
  EngineRouter.new("decidim", default_url_options).short_link_url(id: identifier)
end

#target_urlString

Generates the full long URL to the resource matching this short link.

Returns:

  • (String)

    The target full URL that the short link should link to.



96
97
98
# File 'decidim-core/app/models/decidim/short_link.rb', line 96

def target_url
  url_helpers.send("#{route_name}_url", **params)
end