Skip to content

Commit

Permalink
Create CommunityLinks.
Browse files Browse the repository at this point in the history
Links must be https.  Add validate_url ruby gem to validate the urls.

Add not null on link site and url.

Add relevant FK.
  • Loading branch information
openbrian committed Aug 9, 2024
1 parent 1efd91a commit 9dd48e9
Show file tree
Hide file tree
Showing 22 changed files with 616 additions and 80 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ gem "rails_param"
gem "rinku", ">= 2.0.6", :require => "rails_rinku"
gem "strong_migrations", "< 2.0.0"
gem "validates_email_format_of", ">= 1.5.1"
gem "validate_url"

# Native OSM extensions
gem "quad_tile", "~> 1.0.1"
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,9 @@ GEM
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
uri (0.13.0)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
validates_email_format_of (1.8.2)
i18n (>= 0.8.0)
simpleidn
Expand Down Expand Up @@ -703,6 +706,7 @@ DEPENDENCIES
terser
turbo-rails
unicode-display_width
validate_url
validates_email_format_of (>= 1.5.1)
vendorer
webmock
Expand Down
2 changes: 2 additions & 0 deletions app/abilities/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def initialize(user)
can [:index, :feed, :show], Changeset
can :index, ChangesetComment
can [:index, :show], Community
can [:index], CommunityLink
can [:confirm, :confirm_resend, :confirm_email], :confirmation
can [:index, :rss, :show], DiaryEntry
can :index, DiaryComment
Expand Down Expand Up @@ -50,6 +51,7 @@ def initialize(user)
can [:new, :create, :reply, :show, :inbox, :outbox, :muted, :mark, :unmute, :destroy], Message
can [:create, :new], Community
can [:edit, :update], Community, { :organizer_id => user.id }
can [:edit, :create, :destroy, :new, :update], CommunityLink, { :community => { :organizer_id => user.id } }
can [:close, :reopen], Note
can [:show, :edit, :update], :preference
can [:edit, :update], :profile
Expand Down
162 changes: 107 additions & 55 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//= require leaflet.osm
//= require leaflet.map
//= require leaflet.zoom
//= require leaflet.locatecontrol/src/L.Control.Locate
//= require leaflet.locationfilter
//= require i18n
//= require oauth
Expand Down Expand Up @@ -148,70 +149,121 @@ $(document).ready(function () {
.attr("title", I18n.t("javascripts.site.edit_disabled_tooltip"));
});

window.addLocateControl = function (map, position) {
var locate = L.control.locate({
position: position,
icon: "icon geolocate",
iconLoading: "icon geolocate",
strings: {
title: I18n.t("javascripts.map.locate.title"),
popup: function (options) {
return I18n.t("javascripts.map.locate." + options.unit + "Popup", { count: options.distance });
}
}
}).addTo(map);
$(locate.getContainer())
.removeClass("leaflet-control-locate leaflet-bar")
.addClass("control-locate")
.children("a")
.attr("href", "#")
.removeClass("leaflet-bar-part leaflet-bar-part-single")
.addClass("control-button");
};

/*
* Create a map on the page for the given `id`. Adhere to rtl pages. Parameters to
* the map are provided as data attributes.
* * zoom
* * latitude, longitude
* * min-lat, max-lat, min-lon, max-lon
* If the map div has a class `has_marker` there will be a marker added to the map.
*/
window.showMap = function (id) {
var params = $("#" + id).data();
var map = L.map(id, {
attributionControl: false,
zoomControl: false
});
map.addLayer(new L.OSM.Mapnik());
var show_marker = true;
if (!params.lon || !params.lat) {
params.lon = 0;
params.lat = 0;
params.zoom = 1;
show_marker = false;
const div = $("#" + id);
// Defaults
const position = $("html").attr("dir") === "rtl" ? "topleft" : "topright";
let zoom = 2;
let [latitude, longitude] = [0, 0];
let bounds = null;
let marker = null;

// Extract params
const params = div.data();
if (params.zoom) {
zoom = params.zoom;
}
map.setView([params.lat, params.lon], params.zoom);
if (show_marker) {
L.marker([params.lat, params.lon], { icon: OSM.getUserIcon() }).addTo(map);
if (params.latitude && params.longitude) {
[latitude, longitude] = [params.latitude, params.longitude];
}
if (params.minLat && params.maxLat && params.minLon && params.maxLon) {
bounds = [
[params.minLat, params.minLon],
[params.maxLat, params.maxLon]
];
}
};

window.formMapInput = function (id, type) {
var map = L.map(id, {
attributionControl: false
// Create map.
const map = L.map(id, {
attributionControl: false,
center: [latitude, longitude],
zoom: zoom,
zoomControl: false
});
window.addLocateControl(map, position);
L.OSM.zoom({ position: position }).addTo(map);
map.addLayer(new L.OSM.Mapnik());
if (bounds) {
map.fitBounds(bounds);
}

var lat_field = document.getElementById(type + "_latitude");
var lon_field = document.getElementById(type + "_longitude");

if (lat_field.value) {
map.setView([lat_field.value, lon_field.value], 12);
} else {
map.setView([0, 0], 0);
if (div.hasClass("has_marker")) {
marker = L.marker([latitude, longitude], {
icon: OSM.getUserIcon(),
keyboard: false,
interactive: false
}).addTo(map);
}

L.Control.Watermark = L.Control.extend({
onAdd: function () {
var container = map.getContainer();
var img = L.DomUtil.create("img");
img.src = "/assets/marker-blue.png"; // 25x41 px
// img.style.width = '200px';
img.style["margin-left"] = ((container.offsetWidth / 2) - 12) + "px";
img.style["margin-bottom"] = ((container.offsetHeight / 2) - 20) + "px";
return img;
},
onRemove: function () {
// Nothing to do here
return { map, marker };
};

/*
* Create a basic map using showMap above, and connect it to the form that
* contains it. If the map div has the set_location class, the map will
* set values in appropriate fields if they exist as the user clicks and
* moves the map. These fields are:
* * field_latitude
* * field_longitude
* * field_min_lat
* * field_max_lat
* * field_min_lon
* * field_max_lon
*/
window.formMapInit = function (id) {
const formDiv = $("#" + id);
const mapDiv = $("#" + id + "_map");
const { map, marker } = window.showMap(id + "_map");

if (mapDiv.hasClass("set_location")) {
if ($(".field_latitude", formDiv) && $(".field_longitude", formDiv)) {
map.on("click", function (e) {
const location = e.latlng.wrap();
marker.setLatLng(location);
// If the page has these elements, populate them.
$(".field_latitude", formDiv).val(location.lat);
$(".field_longitude", formDiv).val(location.lng);
});
}
});
L.control.watermark = function (opts) {
return new L.Control.Watermark(opts);
};
L.control.watermark({ position: "bottomleft" }).addTo(map);

map.on("move", function () {
var center = map.getCenter();
$("#" + type + "_latitude").val(center.lat);
$("#" + type + "_longitude").val(center.lng);
if ($("#" + type + "_min_lat")) {
var bounds = map.getBounds();
$("#" + type + "_min_lat").val(bounds._southWest.lat);
$("#" + type + "_max_lat").val(bounds._northEast.lat);
$("#" + type + "_min_lon").val(bounds._southWest.lng);
$("#" + type + "_max_lon").val(bounds._northEast.lng);

if ($(".field_min_lat", formDiv) && $(".field_max_lat", formDiv) &&
$(".field_min_lon", formDiv) && $(".field_max_lon", formDiv)) {
map.on("move", function () {
var bounds = map.getBounds();
$(".field_min_lat").val(bounds._southWest.lat);
$(".field_max_lat").val(bounds._northEast.lat);
$(".field_min_lon").val(bounds._southWest.lng);
$(".field_max_lon").val(bounds._northEast.lng);
});
}
});
}
};
6 changes: 3 additions & 3 deletions app/assets/javascripts/communities.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/*global showMap,formMapInput*/
/*global showMap,formMapInit*/


$(document).ready(function () {
if ($("#community_map_form").length) {
formMapInput("community_map_form", "community");
if ($("#community_form").length) {
formMapInit("community_form");
} else if ($("#community_map").length) {
showMap("community_map");
}
Expand Down
6 changes: 0 additions & 6 deletions app/assets/stylesheets/communities.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,4 @@
label {
font-weight: bold;
}
ul {
display: inline-block;
}
ul > li {
display: inline-block;
}
}
61 changes: 61 additions & 0 deletions app/controllers/community_links_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class CommunityLinksController < ApplicationController
layout "site"
before_action :authorize_web

before_action :set_link, :only => [:destroy, :edit, :update]

load_and_authorize_resource :except => [:create, :new]
authorize_resource

def index
@community = Community.friendly.find(params[:community_id])
@links = @community.community_links
end

def new
return "missing parameter community_id" unless params.key?(:community_id)

@community = Community.friendly.find(params[:community_id])
@title = t ".title"
@link = CommunityLink.new
@link.community_id = params[:community_id]
end

def edit; end

def create
@community = Community.friendly.find(params[:community_id])
@link = @community.community_links.build(link_params)
if @link.save
response.set_header("link_id", @link.id) # for testing
redirect_to @link.community, :notice => t(".success")
else
render "new"
end
end

def update
if @link.update(link_params)
redirect_to @link.community, :notice => t(".success")
else
flash.now[:alert] = t(".failure")
render :edit
end
end

def destroy
community_id = @link.community_id
@link.delete
redirect_to community_path(community_id)
end

private

def set_link
@link = CommunityLink.find(params[:id])
end

def link_params
params.require(:community_link).permit(:community_id, :text, :url)
end
end
1 change: 1 addition & 0 deletions app/models/community.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Community < ApplicationRecord
friendly_id :name, :use => :slugged

belongs_to :organizer, :class_name => "User"
has_many :community_links

validates :name, :presence => true, :length => 1..255, :characters => true
validates :description, :presence => true, :length => 1..1023, :characters => true
Expand Down
25 changes: 25 additions & 0 deletions app/models/community_link.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# == Schema Information
#
# Table name: community_links
#
# id :bigint(8) not null, primary key
# community_id :bigint(8) not null
# text :string not null
# url :string not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_community_links_on_community_id (community_id)
#
# Foreign Keys
#
# fk_rails_... (community_id => communities.id)
#

class CommunityLink < ApplicationRecord
belongs_to :community
validates :text, :presence => true, :length => 1..255, :characters => true
validates :url, :presence => true, :length => 1..255, :url => { :schemes => ["https"] }
end
Loading

0 comments on commit 9dd48e9

Please sign in to comment.