koalagator 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.md +661 -0
- data/README.md +74 -21
- data/app/assets/config/calagator/manifest.js +5 -1
- data/app/assets/images/external_sites/mastodon.png +0 -0
- data/app/assets/images/nav_marker.png +0 -0
- data/app/assets/javascripts/calagator/forms.js +7 -0
- data/app/assets/stylesheets/calagator/custom/calendar.css +137 -0
- data/app/assets/stylesheets/calagator/errors.css +2 -4
- data/app/assets/stylesheets/calagator/forms.scss +5 -0
- data/app/assets/stylesheets/calagator/layout.scss +31 -9
- data/app/assets/stylesheets/calagator/typography.scss +39 -9
- data/app/assets/stylesheets/calagator/utils.scss +44 -0
- data/app/controllers/calagator/admin/curations_controller.rb +62 -0
- data/app/controllers/calagator/admin/users_controller.rb +79 -0
- data/app/controllers/calagator/application_controller.rb +29 -5
- data/app/controllers/calagator/curations_controller.rb +32 -0
- data/app/controllers/calagator/events_controller.rb +6 -0
- data/app/controllers/calagator/paper_trail_manager_controller.rb +5 -0
- data/app/controllers/calagator/passwords_controller.rb +4 -0
- data/app/controllers/calagator/registrations_controller.rb +5 -0
- data/app/controllers/calagator/sessions_controller.rb +4 -0
- data/app/controllers/calagator/site_controller.rb +10 -0
- data/app/controllers/calagator/sources_controller.rb +2 -0
- data/app/controllers/calagator/venues_controller.rb +5 -1
- data/app/controllers/calagator/versions_controller.rb +1 -1
- data/app/controllers/paper_trail_manager/changes_controller.rb +16 -16
- data/app/helpers/calagator/application_helper.rb +32 -3
- data/app/helpers/calagator/time_range_helper.rb +1 -1
- data/app/helpers/paper_trail_manager/changes_helper.rb +7 -7
- data/app/javascript/calagator/calendar/calendar.js +82 -0
- data/app/javascript/calagator/calendar/event.js +94 -0
- data/app/javascript/calagator/calendar/lib/components.js +120 -0
- data/app/javascript/calagator/calendar/lib/utils.js +67 -0
- data/app/models/calagator/curation.rb +32 -0
- data/app/models/calagator/event/browse.rb +3 -2
- data/app/models/calagator/event/cloner.rb +1 -1
- data/app/models/calagator/event/ical_renderer.rb +1 -1
- data/app/models/calagator/event/overview.rb +3 -1
- data/app/models/calagator/event/saver.rb +6 -1
- data/app/models/calagator/event/search.rb +1 -1
- data/app/models/calagator/event/search_engine.rb +1 -1
- data/app/models/calagator/event.rb +22 -5
- data/app/models/calagator/source/parser/hcal.rb +1 -1
- data/app/models/calagator/source/parser.rb +3 -3
- data/app/models/calagator/source.rb +23 -6
- data/app/models/calagator/user.rb +50 -0
- data/app/models/calagator/venue/geocoder.rb +1 -1
- data/app/models/calagator/venue/search.rb +1 -1
- data/app/models/calagator/venue/search_engine.rb +1 -1
- data/app/models/calagator/venue.rb +20 -1
- data/app/models/concerns/calagator/event_filterable.rb +22 -0
- data/app/views/calagator/admin/curations/_form.html.erb +56 -0
- data/app/views/calagator/admin/curations/_index.html.erb +12 -0
- data/app/views/calagator/admin/curations/edit.html.erb +2 -0
- data/app/views/calagator/admin/curations/index.html.erb +21 -0
- data/app/views/calagator/admin/curations/new.html.erb +2 -0
- data/app/views/calagator/admin/index.html.erb +6 -2
- data/app/views/calagator/admin/users/_form.html.erb +28 -0
- data/app/views/calagator/admin/users/edit.html.erb +7 -0
- data/app/views/calagator/admin/users/index.html.erb +38 -0
- data/app/views/calagator/admin/users/invite.html.erb +19 -0
- data/app/views/calagator/admin/users/new.html.erb +3 -0
- data/app/views/calagator/curations/show.html.erb +17 -0
- data/app/views/calagator/events/_index.html.erb +59 -0
- data/app/views/calagator/events/_item.html.erb +10 -3
- data/app/views/calagator/events/_subnav.html.erb +7 -2
- data/app/views/calagator/events/_subnav_custom.html.erb +0 -0
- data/app/views/calagator/events/index.atom.builder +1 -1
- data/app/views/calagator/events/index.html.erb +9 -60
- data/app/views/calagator/events/show.html.erb +5 -3
- data/app/views/calagator/shared/_calendar.html.erb +7 -0
- data/app/views/calagator/shared/_subnav_curations.html.erb +5 -0
- data/app/views/calagator/shared/_subnav_pinned_venues.html.erb +5 -0
- data/app/views/calagator/site/_contact.html.erb +1 -0
- data/app/views/calagator/site/_description.html.erb +2 -2
- data/app/views/calagator/site/about.html.erb +9 -0
- data/app/views/calagator/site/closed_registrations.html.erb +2 -0
- data/app/views/calagator/site/embed.html.erb +16 -0
- data/app/views/calagator/site/index.html.erb +1 -1
- data/app/views/calagator/sources/index.html.erb +1 -1
- data/app/views/calagator/sources/show.html.erb +1 -1
- data/app/views/calagator/venues/_form.html.erb +5 -1
- data/app/views/calagator/venues/_subnav.html.erb +9 -1
- data/app/views/calagator/venues/_subnav_custom.html.erb +0 -0
- data/app/views/calagator/venues/show.html.erb +1 -1
- data/app/views/layouts/calagator/_devise.html.erb +17 -0
- data/app/views/layouts/calagator/_footer.html.erb +3 -1
- data/app/views/layouts/calagator/_head.html.erb +0 -0
- data/app/views/layouts/calagator/_header.html.erb +3 -0
- data/app/views/layouts/calagator/application.html.erb +3 -0
- data/app/views/layouts/calagator/embed.html.erb +15 -0
- data/app/views/paper_trail_manager/changes/_version.html.erb +5 -5
- data/app/views/paper_trail_manager/changes/index.atom.builder +12 -12
- data/bin/{calagator → koalagator} +14 -9
- data/config/importmap.rb +11 -0
- data/config/initializers/admin_user.rb +15 -0
- data/config/initializers/observers.rb +1 -1
- data/config/initializers/paper_trail_manager.rb +1 -1
- data/config/locales/devise.en.yml +65 -0
- data/config/routes.rb +26 -1
- data/db/migrate/20240319042449_devise_create_calagator_users.rb +43 -0
- data/db/migrate/20240319061154_add_admin_flag_to_calagator_user.rb +5 -0
- data/db/migrate/20240320043535_add_name_to_calagator_user.rb +8 -0
- data/db/migrate/20240322035554_add_created_by_to_records.rb +12 -0
- data/db/migrate/20240510051940_create_calagator_curations.rb +15 -0
- data/db/migrate/20240628055300_add_pinned_to_venue.rb +5 -0
- data/db/seeds.rb +49 -0
- data/lib/calagator/decode_html_entities_hack.rb +1 -1
- data/lib/calagator/engine.rb +16 -1
- data/lib/calagator/machine_tag.rb +1 -1
- data/lib/calagator/strip_whitespace.rb +1 -1
- data/lib/calagator/vcalendar.rb +4 -4
- data/lib/calagator/version.rb +5 -2
- data/lib/generators/calagator/install_generator.rb +9 -1
- data/lib/generators/calagator/templates/app/views/devise/registrations/edit.html.erb +48 -0
- data/lib/generators/calagator/templates/app/views/devise/registrations/new.html.erb +29 -0
- data/lib/generators/calagator/templates/config/initializers/01_calagator.rb +34 -6
- data/lib/generators/calagator/templates/config/initializers/04_devise.rb +314 -0
- data/lib/{calagator.rb → koalagator.rb} +15 -3
- data/lib/paper_trail_manager.rb +11 -11
- data/lib/theme_reader.rb +1 -1
- data/rails_template.rb +6 -6
- data/vendor/javascript/@event-calendar--core.js +10 -0
- data/vendor/javascript/@event-calendar--day-grid.js +2 -0
- data/vendor/javascript/@event-calendar--list.js +2 -0
- data/vendor/javascript/ical.js.js +2 -0
- metadata +166 -105
- data/MIT-LICENSE.txt +0 -23
- data/app/models/calagator/event/search_engine/apache_sunspot.rb +0 -106
- data/app/models/calagator/venue/search_engine/apache_sunspot.rb +0 -85
- data/lib/tasks/sunspot_reindex_calagator.rake +0 -20
- data/lib/tasks/sunspot_solr_restart_enhancements.rake +0 -20
- data/lib/wait_for_solr.rb +0 -26
@@ -0,0 +1,120 @@
|
|
1
|
+
import { Optional, toTitleCase } from "calendar/lib/utils"
|
2
|
+
|
3
|
+
class BaseComponent extends HTMLElement {
|
4
|
+
static observe(...attr) {
|
5
|
+
this._attrSet = (this._attrSet || new Set)
|
6
|
+
attr.forEach(e => {this._attrSet.add(e)})
|
7
|
+
this.observedAttributes = Array.from(this._attrSet)
|
8
|
+
}
|
9
|
+
|
10
|
+
constructor() {
|
11
|
+
super()
|
12
|
+
}
|
13
|
+
|
14
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
15
|
+
const callback = `on${toTitleCase(name)}Changed`
|
16
|
+
if (typeof this[callback] == "function") {
|
17
|
+
this[callback](oldValue, newValue)
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
emit(name, data) {
|
22
|
+
this.dispatchEvent(new CustomEvent(name, {detail: data}))
|
23
|
+
}
|
24
|
+
|
25
|
+
on(name, callback) {
|
26
|
+
this.addEventListener(name, callback)
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
class SelectComponent extends BaseComponent {
|
31
|
+
constructor() {
|
32
|
+
super()
|
33
|
+
}
|
34
|
+
|
35
|
+
_children = new Set
|
36
|
+
selected = new Set
|
37
|
+
|
38
|
+
select(child) {
|
39
|
+
child.setAttribute("selected", true)
|
40
|
+
}
|
41
|
+
|
42
|
+
deselect(child) {
|
43
|
+
child.removeAttribute("selected")
|
44
|
+
}
|
45
|
+
|
46
|
+
_select(child) {
|
47
|
+
this.selected.add(child)
|
48
|
+
this.emit("child-selected", child)
|
49
|
+
}
|
50
|
+
|
51
|
+
_deselect(child) {
|
52
|
+
this.selected.delete(child)
|
53
|
+
this.emit("child-deselected", child)
|
54
|
+
}
|
55
|
+
|
56
|
+
_registerChild(child) {
|
57
|
+
this._children.add(child)
|
58
|
+
}
|
59
|
+
_removeChild(child) {
|
60
|
+
this._children.delete(child)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
class SingleSelectComponent extends SelectComponent {
|
65
|
+
constructor() {
|
66
|
+
super()
|
67
|
+
}
|
68
|
+
|
69
|
+
selected = Optional.empty()
|
70
|
+
|
71
|
+
_select(selected) {
|
72
|
+
this.selected = Optional.of(selected)
|
73
|
+
this._children.forEach(child => {
|
74
|
+
if (child == selected) { return }
|
75
|
+
this.deselect(child)
|
76
|
+
})
|
77
|
+
this.emit("child-selected", selected)
|
78
|
+
}
|
79
|
+
|
80
|
+
_deselect(child) {
|
81
|
+
if (child == this.selected.get()) {
|
82
|
+
this.selected = Optional.empty()
|
83
|
+
}
|
84
|
+
this.emit("child-deselected", child)
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
class OptionComponent extends BaseComponent {
|
89
|
+
static {
|
90
|
+
this.observe("selected")
|
91
|
+
}
|
92
|
+
constructor(parentType) {
|
93
|
+
super()
|
94
|
+
const parent = this.closest(parentType)
|
95
|
+
this.parent = Optional.try(parent instanceof SelectComponent, parent)
|
96
|
+
this.parent.ifPresent(p => { p._registerChild(this) })
|
97
|
+
}
|
98
|
+
selected = false
|
99
|
+
|
100
|
+
onSelectedChanged(_, value) {
|
101
|
+
this.selected = (value == null ? false : true)
|
102
|
+
if (value) {
|
103
|
+
this.parent.ifPresent(p => { p._select(this) })
|
104
|
+
this.emit("selected", {})
|
105
|
+
} else {
|
106
|
+
this.parent.ifPresent(p => { p._deselect(this) })
|
107
|
+
this.emit("deselected", {})
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
select() {
|
112
|
+
this.setAttribute("selected", true)
|
113
|
+
}
|
114
|
+
|
115
|
+
deselect() {
|
116
|
+
this.removeAttribute("selected")
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
export { BaseComponent, SelectComponent, SingleSelectComponent, OptionComponent }
|
@@ -0,0 +1,67 @@
|
|
1
|
+
function toTitleCase(str) {
|
2
|
+
return str.replace(
|
3
|
+
/\w\S*/g,
|
4
|
+
text => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase()
|
5
|
+
);
|
6
|
+
}
|
7
|
+
|
8
|
+
class Optional {
|
9
|
+
static empty() {
|
10
|
+
return new this(null)
|
11
|
+
}
|
12
|
+
|
13
|
+
static of(value) {
|
14
|
+
return new this(value)
|
15
|
+
}
|
16
|
+
|
17
|
+
static try(condition, value) {
|
18
|
+
if (condition) {
|
19
|
+
return this.of(value)
|
20
|
+
} else {
|
21
|
+
return this.empty()
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
constructor(value) {
|
26
|
+
if (value != undefined) {
|
27
|
+
this.#value = value
|
28
|
+
}
|
29
|
+
}
|
30
|
+
#value = null
|
31
|
+
|
32
|
+
isPresent() {
|
33
|
+
return !(this.#value === null)
|
34
|
+
}
|
35
|
+
|
36
|
+
get() {
|
37
|
+
return this.#value
|
38
|
+
}
|
39
|
+
|
40
|
+
ifPresent(callback) {
|
41
|
+
if (!this.isPresent()) { return }
|
42
|
+
callback(this.#value)
|
43
|
+
}
|
44
|
+
|
45
|
+
unlessPresent(callback) {
|
46
|
+
if (this.isPresent()) { return }
|
47
|
+
callback()
|
48
|
+
}
|
49
|
+
|
50
|
+
orElse(value) {
|
51
|
+
if (this.isPresent()) {
|
52
|
+
return this.#value
|
53
|
+
} else {
|
54
|
+
return value
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
orElseGet(callback) {
|
59
|
+
if (this.isPresent()) {
|
60
|
+
return this.#value
|
61
|
+
} else {
|
62
|
+
return callback()
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
export { Optional, toTitleCase }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: calagator_curations
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# description :string
|
7
|
+
# display_name :string
|
8
|
+
# name :string not null
|
9
|
+
# priority :integer default(0), not null
|
10
|
+
# unlisted :boolean default(FALSE), not null
|
11
|
+
# created_at :datetime not null
|
12
|
+
# updated_at :datetime not null
|
13
|
+
#
|
14
|
+
# Indexes
|
15
|
+
#
|
16
|
+
# index_calagator_curations_on_name (name) UNIQUE
|
17
|
+
#
|
18
|
+
module Calagator
|
19
|
+
class Curation < Calagator::ApplicationRecord
|
20
|
+
include EventFilterable
|
21
|
+
scope :listed, -> { where(unlisted: false) }
|
22
|
+
|
23
|
+
validates :name, :display_name, :priority, presence: true
|
24
|
+
validates :name,
|
25
|
+
format: {with: /\A[a-z0-9\-_]+\z/, message: "only allows ASCII letters, numbers, dashes and underscores"},
|
26
|
+
uniqueness: true,
|
27
|
+
length: {maximum: 64}
|
28
|
+
|
29
|
+
scope :order_by_priority, ->(direction = :desc) { order(priority: direction) }
|
30
|
+
scope :listed, ->(listed = true) { where(unlisted: !listed) }
|
31
|
+
end
|
32
|
+
end
|
@@ -3,10 +3,11 @@
|
|
3
3
|
module Calagator
|
4
4
|
class Event < Calagator::ApplicationRecord
|
5
5
|
class Browse < Struct.new(:order, :date, :time)
|
6
|
-
def initialize(attributes = {})
|
6
|
+
def initialize(attributes = {}, scope = nil)
|
7
7
|
members.each do |key|
|
8
|
-
send "#{key}=", attributes[key]
|
8
|
+
send :"#{key}=", attributes[key]
|
9
9
|
end
|
10
|
+
@scope = scope
|
10
11
|
end
|
11
12
|
|
12
13
|
def events
|
@@ -15,7 +15,7 @@ module Calagator
|
|
15
15
|
def clone
|
16
16
|
clone = Event.new
|
17
17
|
ATTRIBUTES.each do |attribute|
|
18
|
-
clone.send "#{attribute}=", event.send(attribute)
|
18
|
+
clone.send :"#{attribute}=", event.send(attribute)
|
19
19
|
end
|
20
20
|
if event.start_time
|
21
21
|
clone.start_time = clone_time_for_today(event.start_time)
|
@@ -20,7 +20,9 @@ module Calagator
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def tags
|
23
|
-
@tags ||= Event.tag_counts_on(
|
23
|
+
@tags ||= Event.tag_counts_on(
|
24
|
+
:tags, limit: 100, conditions: "tags_count >= #{Calagator.tag_cloud_min}"
|
25
|
+
).sort_by(&:name)
|
24
26
|
end
|
25
27
|
|
26
28
|
private
|
@@ -25,7 +25,12 @@ module Calagator
|
|
25
25
|
if params[:event] && params[:event][:venue_id].present?
|
26
26
|
Venue.find(params[:event][:venue_id]).originator
|
27
27
|
else
|
28
|
-
Venue.find_or_initialize_by(title: params[:venue_name]).originator
|
28
|
+
venue = Venue.find_or_initialize_by(title: params[:venue_name]).originator
|
29
|
+
# Set the created_by to match the event, if creating the venue.
|
30
|
+
if event.created_by_id? && !venue.id?
|
31
|
+
venue.created_by = event.created_by
|
32
|
+
end
|
33
|
+
venue
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# Table name: events
|
6
6
|
#
|
7
7
|
# id :integer not null, primary key
|
8
|
+
# created_by_name :string
|
8
9
|
# description :text
|
9
10
|
# end_time :datetime
|
10
11
|
# locked :boolean default(FALSE)
|
@@ -15,10 +16,19 @@
|
|
15
16
|
# venue_details :text
|
16
17
|
# created_at :datetime
|
17
18
|
# updated_at :datetime
|
19
|
+
# created_by_id :integer
|
18
20
|
# duplicate_of_id :integer
|
19
21
|
# source_id :integer
|
20
22
|
# venue_id :integer
|
21
23
|
#
|
24
|
+
# Indexes
|
25
|
+
#
|
26
|
+
# index_events_on_created_by_id (created_by_id)
|
27
|
+
#
|
28
|
+
# Foreign Keys
|
29
|
+
#
|
30
|
+
# created_by_id (created_by_id => calagator_users.id)
|
31
|
+
#
|
22
32
|
|
23
33
|
require "calagator/denylist_validator"
|
24
34
|
require "calagator/duplicate_checking"
|
@@ -49,6 +59,7 @@ module Calagator
|
|
49
59
|
include ActiveModel::Serializers::Xml
|
50
60
|
|
51
61
|
# Associations
|
62
|
+
belongs_to :created_by, class_name: "Calagator::User", optional: true
|
52
63
|
belongs_to :venue, counter_cache: true, optional: true
|
53
64
|
belongs_to :source, optional: true
|
54
65
|
|
@@ -60,6 +71,7 @@ module Calagator
|
|
60
71
|
format: {with: %r{\Ahttps?://(\w+:?\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@\-/]))?\Z},
|
61
72
|
allow_blank: true}
|
62
73
|
|
74
|
+
before_save :set_created_by_name, if: :created_by_id?
|
63
75
|
before_destroy :check_if_locked_before_destroy # prevent locked events from being destroyed
|
64
76
|
|
65
77
|
# Duplicates
|
@@ -105,25 +117,25 @@ module Calagator
|
|
105
117
|
#---[ Overrides ]-------------------------------------------------------
|
106
118
|
|
107
119
|
def url=(value)
|
108
|
-
super
|
120
|
+
super(UrlPrefixer.prefix(value))
|
109
121
|
end
|
110
122
|
|
111
123
|
# Set the start_time to the given +value+, which could be a Time, Date,
|
112
124
|
# DateTime, String, Array of Strings, or nil.
|
113
125
|
def start_time=(value)
|
114
|
-
super
|
126
|
+
super(time_for(value))
|
115
127
|
rescue ArgumentError
|
116
128
|
errors.add :start_time, "is invalid"
|
117
|
-
super
|
129
|
+
super(nil)
|
118
130
|
end
|
119
131
|
|
120
132
|
# Set the end_time to the given +value+, which could be a Time, Date,
|
121
133
|
# DateTime, String, Array of Strings, or nil.
|
122
134
|
def end_time=(value)
|
123
|
-
super
|
135
|
+
super(time_for(value))
|
124
136
|
rescue ArgumentError
|
125
137
|
errors.add :end_time, "is invalid"
|
126
|
-
super
|
138
|
+
super(nil)
|
127
139
|
end
|
128
140
|
|
129
141
|
def time_for(value)
|
@@ -182,5 +194,10 @@ module Calagator
|
|
182
194
|
|
183
195
|
(end_time - start_time)
|
184
196
|
end
|
197
|
+
|
198
|
+
def set_created_by_name
|
199
|
+
self.created_by_name = created_by.name_with_email
|
200
|
+
end
|
201
|
+
private :set_created_by_name
|
185
202
|
end
|
186
203
|
end
|
@@ -113,6 +113,6 @@ module Calagator
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
require_relative "parser/not_found"
|
117
|
+
require_relative "parser/ical"
|
118
|
+
require_relative "parser/hcal"
|
@@ -4,12 +4,22 @@
|
|
4
4
|
#
|
5
5
|
# Table name: sources
|
6
6
|
#
|
7
|
-
# id
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# created_by_name :string
|
9
|
+
# imported_at :datetime
|
10
|
+
# title :string
|
11
|
+
# url :string
|
12
|
+
# created_at :datetime
|
13
|
+
# updated_at :datetime
|
14
|
+
# created_by_id :integer
|
15
|
+
#
|
16
|
+
# Indexes
|
17
|
+
#
|
18
|
+
# index_sources_on_created_by_id (created_by_id)
|
19
|
+
#
|
20
|
+
# Foreign Keys
|
21
|
+
#
|
22
|
+
# created_by_id (created_by_id => calagator_users.id)
|
13
23
|
#
|
14
24
|
|
15
25
|
# == Source
|
@@ -26,11 +36,14 @@ module Calagator
|
|
26
36
|
|
27
37
|
validate :assert_url
|
28
38
|
|
39
|
+
belongs_to :created_by, class_name: "Calagator::User", optional: true
|
29
40
|
has_many :events, dependent: :destroy
|
30
41
|
has_many :venues, dependent: :destroy
|
31
42
|
|
32
43
|
scope :listing, -> { order("created_at DESC") }
|
33
44
|
|
45
|
+
before_save :set_created_by_name, if: :created_by_id?
|
46
|
+
|
34
47
|
has_paper_trail
|
35
48
|
|
36
49
|
xss_foliate
|
@@ -76,5 +89,9 @@ module Calagator
|
|
76
89
|
errors.add :url, "has invalid format"
|
77
90
|
false
|
78
91
|
end
|
92
|
+
|
93
|
+
def set_created_by_name
|
94
|
+
self.created_by_name = created_by.name_with_email
|
95
|
+
end
|
79
96
|
end
|
80
97
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: calagator_users
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# admin :boolean
|
7
|
+
# display_name :string
|
8
|
+
# email :string default(""), not null
|
9
|
+
# encrypted_password :string default(""), not null
|
10
|
+
# name :string not null
|
11
|
+
# remember_created_at :datetime
|
12
|
+
# reset_password_sent_at :datetime
|
13
|
+
# reset_password_token :string
|
14
|
+
# created_at :datetime not null
|
15
|
+
# updated_at :datetime not null
|
16
|
+
#
|
17
|
+
# Indexes
|
18
|
+
#
|
19
|
+
# index_calagator_users_on_email (email) UNIQUE
|
20
|
+
# index_calagator_users_on_name (name) UNIQUE
|
21
|
+
# index_calagator_users_on_reset_password_token (reset_password_token) UNIQUE
|
22
|
+
#
|
23
|
+
module Calagator
|
24
|
+
class User < ApplicationRecord
|
25
|
+
if Calagator.devise_enabled
|
26
|
+
# Include default devise modules. Others available are:
|
27
|
+
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
28
|
+
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable
|
29
|
+
end
|
30
|
+
|
31
|
+
scope :admin, -> { where(admin: true) }
|
32
|
+
validates :name, :email, presence: true
|
33
|
+
validates :name, format: {with: /\A[a-z0-9\-_]+\z/, message: "only allows ASCII letters, numbers, dashes and underscores"}
|
34
|
+
|
35
|
+
has_many :events, foreign_key: :created_by_id, dependent: :nullify
|
36
|
+
has_many :venues, foreign_key: :created_by_id, dependent: :nullify
|
37
|
+
has_many :sources, foreign_key: :created_by_id, dependent: :nullify
|
38
|
+
|
39
|
+
before_validation -> { name&.downcase! }, on: :create
|
40
|
+
|
41
|
+
def display_name
|
42
|
+
attribute = read_attribute(:display_name)
|
43
|
+
attribute.present? ? attribute : name
|
44
|
+
end
|
45
|
+
|
46
|
+
def name_with_email
|
47
|
+
"@#{name} <#{email}>"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -9,12 +9,14 @@
|
|
9
9
|
# address :string
|
10
10
|
# closed :boolean default(FALSE)
|
11
11
|
# country :string
|
12
|
+
# created_by_name :string
|
12
13
|
# description :text
|
13
14
|
# email :string
|
14
15
|
# events_count :integer
|
15
16
|
# latitude :decimal(7, 4)
|
16
17
|
# locality :string
|
17
18
|
# longitude :decimal(7, 4)
|
19
|
+
# pinned :boolean default(FALSE), not null
|
18
20
|
# postal_code :string
|
19
21
|
# region :string
|
20
22
|
# street_address :string
|
@@ -24,9 +26,18 @@
|
|
24
26
|
# wifi :boolean default(FALSE)
|
25
27
|
# created_at :datetime
|
26
28
|
# updated_at :datetime
|
29
|
+
# created_by_id :integer
|
27
30
|
# duplicate_of_id :integer
|
28
31
|
# source_id :integer
|
29
32
|
#
|
33
|
+
# Indexes
|
34
|
+
#
|
35
|
+
# index_venues_on_created_by_id (created_by_id)
|
36
|
+
#
|
37
|
+
# Foreign Keys
|
38
|
+
#
|
39
|
+
# created_by_id (created_by_id => calagator_users.id)
|
40
|
+
#
|
30
41
|
require "calagator/decode_html_entities_hack"
|
31
42
|
require "calagator/strip_whitespace"
|
32
43
|
require "calagator/url_prefixer"
|
@@ -51,11 +62,13 @@ module Calagator
|
|
51
62
|
|
52
63
|
# Associations
|
53
64
|
has_many :events, -> { non_duplicates }, dependent: :nullify
|
65
|
+
belongs_to :created_by, class_name: "Calagator::User", optional: true
|
54
66
|
belongs_to :source, optional: true
|
55
67
|
|
56
68
|
# Triggers
|
57
69
|
strip_whitespace! :title, :description, :address, :url, :street_address, :locality, :region, :postal_code, :country, :email, :telephone
|
58
70
|
before_save :geocode!
|
71
|
+
before_save :set_created_by_name, if: :created_by_id?
|
59
72
|
|
60
73
|
# Validations
|
61
74
|
validates :title, presence: true
|
@@ -75,13 +88,14 @@ module Calagator
|
|
75
88
|
scope :with_public_wifi, -> { where(wifi: true) }
|
76
89
|
scope :in_business, -> { where(closed: false) }
|
77
90
|
scope :out_of_business, -> { where(closed: true) }
|
91
|
+
scope :pinned, ->(pinned = true) { where(pinned: pinned) }
|
78
92
|
|
79
93
|
def self.search(query, opts = {})
|
80
94
|
SearchEngine.search(query, opts)
|
81
95
|
end
|
82
96
|
|
83
97
|
def url=(value)
|
84
|
-
super
|
98
|
+
super(UrlPrefixer.prefix(value))
|
85
99
|
end
|
86
100
|
|
87
101
|
# Display a single line address.
|
@@ -112,5 +126,10 @@ module Calagator
|
|
112
126
|
def update_events_count!
|
113
127
|
update_attribute(:events_count, events.non_duplicates.count)
|
114
128
|
end
|
129
|
+
|
130
|
+
def set_created_by_name
|
131
|
+
self.created_by_name = created_by.name_with_email
|
132
|
+
end
|
133
|
+
private :set_created_by_name
|
115
134
|
end
|
116
135
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Calagator
|
2
|
+
module EventFilterable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
acts_as_taggable_on :blocks, :requires, :denies, :allows
|
7
|
+
end
|
8
|
+
|
9
|
+
def events
|
10
|
+
event_list = Event.all
|
11
|
+
event_list.merge!(Event.tagged_with(block_list, exclude: true)) if block_list.any?
|
12
|
+
event_list.merge!(Event.tagged_with(require_list, any: true)) if require_list.any?
|
13
|
+
if deny_list.any?
|
14
|
+
deny = event_list.tagged_with(deny_list, exclude: true).or(
|
15
|
+
event_list.tagged_with(allow_list, any: true).except(:select, :order)
|
16
|
+
)
|
17
|
+
event_list.merge!(deny)
|
18
|
+
end
|
19
|
+
event_list
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|