Merge branch 'master' into openstreetbugs

Conflicts:
	Gemfile.lock
	app/views/browse/_map.html.erb
	app/views/user/view.html.erb
	config/locales/en.yml
	config/openlayers.cfg
	db/structure.sql
	vendor/assets/openlayers/OpenLayers.js
This commit is contained in:
Tom Hughes 2012-08-22 20:52:08 +01:00
commit 0d3a9ed9cb
762 changed files with 18663 additions and 13000 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ public/assets
public/attachments
tmp
.DS_Store
*~

24
Gemfile
View file

@ -2,7 +2,10 @@
source 'http://rubygems.org'
# Require rails
gem 'rails', '3.2.2'
gem 'rails', '3.2.8'
# Require things which have moved to gems in ruby 1.9
gem 'bigdecimal', :platforms => :ruby_19
# Require the postgres database driver
gem 'pg'
@ -11,26 +14,36 @@ gem 'pg'
gem 'jquery-rails'
# Load rails plugins
gem 'rails-i18n', ">= 0.5.1"
gem 'rails-i18n', ">= 0.6.3"
gem 'dynamic_form'
gem 'rinku', '>= 1.2.2', :require => 'rails_rinku'
gem 'oauth-plugin', '>= 0.4.0.pre7'
gem 'oauth-plugin', '>= 0.4.1', :require => 'oauth-plugin'
gem 'open_id_authentication', '>= 1.1.0'
gem 'validates_email_format_of', '>= 1.5.1'
gem 'composite_primary_keys', '>= 5.0.0'
gem 'composite_primary_keys', '>= 5.0.8'
gem 'http_accept_language', '>= 1.0.2'
gem 'paperclip', '~> 2.0'
gem 'deadlock_retry', '>= 1.2.0'
gem 'jsonify-rails'
# We need ruby-openid 2.2.0 or later for ruby 1.9 support
gem 'ruby-openid', '>= 2.2.0'
# Browser detection support
gem 'browser'
# Markdown formatting support
gem 'redcarpet'
# Character conversion support for ruby 1.8
gem 'iconv', :platforms => :ruby_18
# Load libxml support for XML parsing and generation
gem 'libxml-ruby', '>= 2.0.5', :require => 'libxml'
# Load HTML sanitizer
# Use for HTML sanitisation
gem 'sanitize'
gem 'htmlentities'
# Load SystemTimer for implementing request timeouts
gem 'SystemTimer', '>= 1.1.3', :require => 'system_timer', :platforms => :ruby_18
@ -44,6 +57,7 @@ gem 'memcached', '>= 1.4.1'
# Gems needed for running tests
group :test do
gem 'timecop'
gem 'minitest', :platforms => :ruby_19
end
# Gems needed for compiling assets

View file

@ -2,35 +2,36 @@ GEM
remote: http://rubygems.org/
specs:
SystemTimer (1.2.3)
actionmailer (3.2.2)
actionpack (= 3.2.2)
mail (~> 2.4.0)
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
actionmailer (3.2.8)
actionpack (= 3.2.8)
mail (~> 2.4.4)
actionpack (3.2.8)
activemodel (= 3.2.8)
activesupport (= 3.2.8)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
journey (~> 1.0.4)
rack (~> 1.4.0)
rack-cache (~> 1.1)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.2)
activesupport (= 3.2.2)
sprockets (~> 2.1.3)
activemodel (3.2.8)
activesupport (= 3.2.8)
builder (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
activerecord (3.2.8)
activemodel (= 3.2.8)
activesupport (= 3.2.8)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
activesupport (3.2.2)
activeresource (3.2.8)
activemodel (= 3.2.8)
activesupport (= 3.2.8)
activesupport (3.2.8)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.7)
arel (3.0.2)
bigdecimal (1.1.0)
browser (0.1.4)
builder (3.0.0)
cocaine (0.2.1)
coffee-rails (3.2.2)
@ -39,53 +40,59 @@ GEM
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.2.0)
composite_primary_keys (5.0.1)
coffee-script-source (1.3.3)
composite_primary_keys (5.0.8)
activerecord (~> 3.2.0)
deadlock_retry (1.2.0)
dynamic_form (1.1.4)
erubis (2.7.0)
execjs (1.3.0)
execjs (1.4.0)
multi_json (~> 1.0)
faraday (0.7.6)
addressable (~> 2.2)
faraday (0.8.4)
multipart-post (~> 1.1)
rack (~> 1.1)
hike (1.2.1)
htmlentities (4.3.1)
http_accept_language (1.0.2)
httpclient (2.2.4)
httpauth (0.1)
httpclient (2.2.7)
i18n (0.6.0)
iconv (0.1)
journey (1.0.3)
jquery-rails (2.0.1)
railties (>= 3.2.0, < 5.0)
journey (1.0.4)
jquery-rails (2.1.1)
railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
json (1.6.5)
json (1.7.5)
jsonify (0.3.1)
multi_json (~> 1.0)
jsonify-rails (0.3.1)
jsonify-rails (0.3.2)
actionpack
jsonify (>= 0.3.1)
jsonify (< 0.4.0)
jwt (0.1.5)
multi_json (>= 1.0)
libv8 (3.3.10.4)
libxml-ruby (2.2.2)
mail (2.4.3)
libxml-ruby (2.3.3)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
memcached (1.4.1)
mime-types (1.17.2)
multi_json (1.1.0)
memcached (1.4.3)
mime-types (1.19)
minitest (3.3.0)
multi_json (1.3.6)
multipart-post (1.1.5)
nokogiri (1.5.0)
oauth (0.4.5)
oauth-plugin (0.4.0.rc2)
nokogiri (1.5.5)
oauth (0.4.6)
oauth-plugin (0.4.1)
multi_json
oauth (~> 0.4.4)
oauth2
oauth2 (>= 0.5.0)
rack
oauth2 (0.5.2)
faraday (~> 0.7)
oauth2 (0.8.0)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
open_id_authentication (1.1.0)
rack-openid (~> 1.3)
paperclip (2.7.0)
@ -93,7 +100,7 @@ GEM
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
pg (0.13.2)
pg (0.14.0)
polyglot (0.3.3)
rack (1.4.1)
rack-cache (1.2)
@ -105,51 +112,52 @@ GEM
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.2.2)
actionmailer (= 3.2.2)
actionpack (= 3.2.2)
activerecord (= 3.2.2)
activeresource (= 3.2.2)
activesupport (= 3.2.2)
rails (3.2.8)
actionmailer (= 3.2.8)
actionpack (= 3.2.8)
activerecord (= 3.2.8)
activeresource (= 3.2.8)
activesupport (= 3.2.8)
bundler (~> 1.0)
railties (= 3.2.2)
rails-i18n (0.5.1)
railties (= 3.2.8)
rails-i18n (0.6.5)
i18n (~> 0.5)
railties (3.2.2)
actionpack (= 3.2.2)
activesupport (= 3.2.2)
railties (3.2.8)
actionpack (= 3.2.8)
activesupport (= 3.2.8)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
thor (>= 0.14.6, < 2.0)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rinku (1.5.1)
ruby-openid (2.1.8)
redcarpet (2.1.1)
rinku (1.7.0)
ruby-openid (2.2.0)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.1.15)
sass-rails (3.2.4)
sass (3.2.1)
sass-rails (3.2.5)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
sprockets (2.1.2)
sprockets (2.1.3)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
therubyracer (0.9.10)
therubyracer (0.10.2)
libv8 (~> 3.3.10)
thor (0.14.6)
thor (0.16.0)
tilt (1.3.3)
timecop (0.3.5)
timecop (0.4.5)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.32)
uglifier (1.2.3)
tzinfo (0.3.33)
uglifier (1.2.7)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
multi_json (~> 1.3)
validates_email_format_of (1.5.3)
PLATFORMS
@ -157,10 +165,13 @@ PLATFORMS
DEPENDENCIES
SystemTimer (>= 1.1.3)
bigdecimal
browser
coffee-rails (~> 3.2.1)
composite_primary_keys (>= 5.0.0)
composite_primary_keys (>= 5.0.8)
deadlock_retry (>= 1.2.0)
dynamic_form
htmlentities
http_accept_language (>= 1.0.2)
httpclient
iconv
@ -168,13 +179,16 @@ DEPENDENCIES
jsonify-rails
libxml-ruby (>= 2.0.5)
memcached (>= 1.4.1)
oauth-plugin (>= 0.4.0.pre7)
minitest
oauth-plugin (>= 0.4.1)
open_id_authentication (>= 1.1.0)
paperclip (~> 2.0)
pg
rails (= 3.2.2)
rails-i18n (>= 0.5.1)
rails (= 3.2.8)
rails-i18n (>= 0.6.3)
redcarpet
rinku (>= 1.2.2)
ruby-openid (>= 2.2.0)
sanitize
sass-rails (~> 3.2.3)
therubyracer

View file

@ -41,7 +41,10 @@ will be a better place to start.
Anybody hacking on the code is welcome to join the
[rails-dev](http://lists.openstreetmap.org/listinfo/rails-dev) mailing
list where other people hacking on the code hang out and will be happy
to help with any problems you may encounter.
to help with any problems you may encounter. If you are looking for a
project to help out with, please take a look at the list of
[Top Ten Tasks](http://wiki.openstreetmap.org/wiki/Top_Ten_Tasks) that
EWG maintains on the wiki.
There are also weekly IRC meetings, at 1800 GMT on Mondays in #osm-ewg on
the OFTC network where questions can be asked and ideas discussed. For more

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300"
height="200"
id="svg2"
version="1.1"
inkscape:version="0.48.2 r9819"
inkscape:export-filename="/Users/tmcw/src/openstreetmap-website/app/assets/images/sprite.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docname="New document 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="28.421709"
inkscape:cx="7.0037552"
inkscape:cy="187.29226"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-852.36218)">
<path
style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.55720866px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 8.4000012,852.36218 c -3.3137081,0 -5.9999994,2.68629 -5.9999994,6 0,3.31371 2.6862913,6 5.9999994,6 3.3137078,0 5.9999988,-2.68629 5.9999988,-6 0,-3.31371 -2.686291,-6 -5.9999988,-6 z m 0,2.4 c 1.9882248,0 3.5999988,1.61178 3.5999988,3.6 0,1.98822 -1.611774,3.6 -3.5999988,3.6 -1.9882249,0 -3.5999997,-1.61178 -3.5999997,-3.6 0,-1.98822 1.6117748,-3.6 3.5999997,-3.6 z"
id="path2987"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.55720866px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect3033"
width="7"
height="3"
x="679.2984"
y="-530.39673"
transform="matrix(-0.60876143,0.79335334,-0.79335334,-0.60876143,0,0)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -11,17 +11,15 @@ function createMap(divName, options) {
controls: options.controls || [
new OpenLayers.Control.ArgParser(),
new OpenLayers.Control.Attribution(),
new OpenLayers.Control.LayerSwitcher(),
new SimpleLayerSwitcher(),
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.PanZoom(),
new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.Zoom(),
new OpenLayers.Control.SimplePanZoom(),
new OpenLayers.Control.ScaleLine({geodesic: true})
],
units: "m",
maxResolution: 156543.0339,
numZoomLevels: 20,
displayProjection: new OpenLayers.Projection("EPSG:4326"),
theme: "<%= asset_path 'theme/default/style.css' %>"
theme: "<%= asset_path 'theme/openstreetmap/style.css' %>"
});
var mapnik = new OpenLayers.Layer.OSM.Mapnik(i18n("javascripts.map.base.standard"), {
@ -69,9 +67,6 @@ function createMap(divName, options) {
markers = new OpenLayers.Layer.Markers("Markers", {
displayInLayerSwitcher: false,
numZoomLevels: 20,
maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
maxResolution: 156543,
units: "m",
projection: "EPSG:900913"
});
map.addLayer(markers);
@ -81,27 +76,35 @@ function createMap(divName, options) {
function getArrowIcon() {
var size = new OpenLayers.Size(25, 22);
var offset = new OpenLayers.Pixel(-30, -27);
var offset = new OpenLayers.Pixel(-22, -20);
var icon = new OpenLayers.Icon("<%= asset_path 'arrow.png' %>", size, offset);
return icon;
}
function addMarkerToMap(position, icon, description) {
var marker = new OpenLayers.Marker(position.clone().transform(epsg4326, map.getProjectionObject()), icon);
var marker = new OpenLayers.Marker(proj(position), icon);
markers.addMarker(marker);
if (description) {
marker.events.register("mouseover", marker, function() { openMapPopup(marker, description) });
marker.events.register("mouseover", marker, function() {
openMapPopup(marker, description);
});
}
return marker;
}
function addObjectToMap(url, zoom, callback) {
var layer = new OpenLayers.Layer.GML("Objects", url, {
format: OpenLayers.Format.OSM,
var layer = new OpenLayers.Layer.Vector("Objects", {
strategies: [
new OpenLayers.Strategy.Fixed()
],
protocol: new OpenLayers.Protocol.HTTP({
url: url,
format: new OpenLayers.Format.OSM()
}),
style: {
strokeColor: "blue",
strokeWidth: 3,
@ -139,8 +142,6 @@ function addObjectToMap(url, zoom, callback) {
});
map.addLayer(layer);
layer.loadGML();
}
function addBoxToMap(boxbounds, id, outline) {
@ -155,9 +156,9 @@ function addBoxToMap(boxbounds, id, outline) {
if (outline) {
vertices = boxbounds.toGeometry().getVertices();
vertices.push(new OpenLayers.Geometry.Point(vertices[0].x, vertices[0].y));
geometry = new OpenLayers.Geometry.LineString(vertices).transform(epsg4326, map.getProjectionObject());
geometry = proj(new OpenLayers.Geometry.LineString(vertices));
} else {
geometry = boxbounds.toGeometry().transform(epsg4326, map.getProjectionObject());
geometry = proj(boxbounds.toGeometry());
}
var box = new OpenLayers.Feature.Vector(geometry, {}, {
strokeWidth: 2,
@ -197,27 +198,19 @@ function removeBoxFromMap(box){
vectors.removeFeature(box);
}
function getMapCenter() {
return map.getCenter().clone().transform(map.getProjectionObject(), epsg4326);
function proj(x) {
return x.clone().transform(epsg4326, map.getProjectionObject());
}
function unproj(x) {
return x.clone().transform(map.getProjectionObject(), epsg4326);
}
function setMapCenter(center, zoom) {
zoom = parseInt(zoom);
zoom = parseInt(zoom, 10);
var numzoom = map.getNumZoomLevels();
if (zoom >= numzoom) zoom = numzoom - 1;
map.setCenter(center.clone().transform(epsg4326, map.getProjectionObject()), zoom);
}
function setMapExtent(extent) {
map.zoomToExtent(extent.clone().transform(epsg4326, map.getProjectionObject()));
}
function getMapExtent() {
return map.getExtent().clone().transform(map.getProjectionObject(), epsg4326);
}
function getMapZoom() {
return map.getZoom();
map.setCenter(proj(center), zoom);
}
function getEventPosition(event) {
@ -265,7 +258,3 @@ function setMapLayers(layerConfig) {
}
}
}
function scaleToZoom(scale) {
return Math.log(360.0/(scale * 512.0)) / Math.log(2.0);
}

View file

@ -19,65 +19,31 @@ function openMenu(anchor, menu, align) {
});
}
/*
* Close a menu.
*/
function closeMenu(menu) {
clearTimeout(menu.timer);
menu.hide();
}
/*
* Callback called when the mouse enters a menu anchor.
*/
function enterMenuAnchor(event, anchor, menu, delay, align) {
if (!anchor.hasClass("disabled")) {
clearTimeout(menu.timer);
if (delay > 0) {
menu.timer = setTimeout(function () { openMenu(anchor, menu, align); }, delay);
} else {
openMenu(event, menu, align);
}
}
}
/*
* Callback called when the mouse leaves a menu anchor.
*/
function leaveMenuAnchor(event, anchor, menu) {
var to = event.relatedTarget;
if (!menu.is(to) && menu.has(to).length === 0) {
menu.hide();
}
clearTimeout(menu.timer);
}
/*
* Callback called when the mouse leaves a menu.
*/
function leaveMenu(event, anchor, menu) {
var to = event.relatedTarget;
if (!anchor.is(to) && menu.has(to).length === 0) {
menu.hide();
}
clearTimeout(menu.timer);
}
/*
* Setup a menu, triggered by hovering over an anchor for a given time.
*/
function createMenu(anchorid, menuid, delay, align) {
var anchor = $("#" + anchorid);
var menu = $("#" + menuid);
function createMenu(anchorid, menuid, align) {
var $anchor = $("#" + anchorid);
var $arrow = $("#" + anchorid + " .menuicon");
var $menu = $("#" + menuid);
var $page = $(":not(#" + menuid + ", #" + anchorid + ")");
anchor.mouseup(function (event) { closeMenu(menu); });
anchor.mouseover(function (event) { enterMenuAnchor(anchor, anchor, menu, delay, align); });
anchor.mouseout(function (event) { leaveMenuAnchor(event, anchor, menu); });
menu.mouseup(function (event) { closeMenu(menu); });
menu.mouseout(function (event) { leaveMenu(event, anchor, menu); });
function hide() {
$menu.hide();
$page.off("click", hide);
}
$arrow.click(function(e) {
if ($anchor.is(":not(.disabled)")) {
e.stopPropagation();
e.preventDefault();
if ($menu.is(":visible")) {
$menu.hide();
$page.off("click", hide);
} else {
openMenu($anchor, $menu.show(), align);
$page.on("click", hide);
}
}
});
}

View file

@ -1,5 +1,7 @@
//= require OpenLayers
//= require OpenStreetMap
//= require SimpleLayerSwitcher
//= require SimplePanZoom
OpenLayers.Util.imageURLs = {
"404.png": "<%= asset_path 'img/404.png' %>",
@ -28,8 +30,6 @@ OpenLayers.Util.imageURLs = {
"zoom-world-mini.png": "<%= asset_path 'img/zoom-world-mini.png' %>"
};
OpenLayers.Util.OSM.MISSING_TILE_URL = "<%= asset_path 'img/404.png' %>";
OpenLayers.Util.origGetImageLocation = OpenLayers.Util.getImageLocation;
OpenLayers.Util.getImageLocation = function(image) {

View file

@ -1,5 +1,7 @@
//= require jquery
//= require jquery_ujs
//= require jquery.autogrowtextarea
//= require jquery.timers
/*
* Called as the user scrolls/zooms around to aniplate hrefs of the
@ -142,23 +144,6 @@ function setArgs(url, args) {
return url.replace(/\?.*$/, "") + "?" + queryitems.join("&");
}
/*
* Called to get a CSS property for an element.
*/
function getStyle(el, property) {
var style;
if (el.currentStyle) {
style = el.currentStyle[property];
} else if( window.getComputedStyle ) {
style = document.defaultView.getComputedStyle(el,null).getPropertyValue(property);
} else {
style = el.style[property];
}
return style;
}
/*
* Called to interpolate JavaScript variables in strings using a
* similar syntax to rails I18n string interpolation - the only
@ -221,3 +206,64 @@ function makeShortCode(lat, lon, zoom) {
}
return str;
}
/*
* Click handler to switch a rich text control to preview mode
*/
function previewRichtext(event) {
var editor = $(this).parents(".richtext_container").find("textarea");
var preview = $(this).parents(".richtext_container").find(".richtext_preview");
var width = editor.outerWidth() - preview.outerWidth() + preview.innerWidth();
var minHeight = editor.outerHeight() - preview.outerHeight() + preview.innerHeight();
if (preview.contents().length == 0) {
preview.oneTime(500, "loading", function () {
preview.addClass("loading");
});
preview.load(editor.attr("data-preview-url"), { text: editor.val() }, function () {
preview.stopTime("loading");
preview.removeClass("loading");
});
}
editor.hide();
preview.width(width);
preview.css("min-height", minHeight + "px");
preview.show();
$(this).siblings(".richtext_doedit").prop("disabled", false);
$(this).prop("disabled", true);
event.preventDefault();
}
/*
* Click handler to switch a rich text control to edit mode
*/
function editRichtext(event) {
var editor = $(this).parents(".richtext_container").find("textarea");
var preview = $(this).parents(".richtext_container").find(".richtext_preview");
preview.hide();
editor.show();
$(this).siblings(".richtext_dopreview").prop("disabled", false);
$(this).prop("disabled", true);
event.preventDefault();
}
/*
* Setup any rich text controls
*/
$(document).ready(function () {
$(".richtext_preview").hide();
$(".richtext_content textarea").change(function () {
$(this).parents(".richtext_container").find(".richtext_preview").empty();
});
$(".richtext_doedit").prop("disabled", true);
$(".richtext_dopreview").prop("disabled", false);
$(".richtext_doedit").click(editRichtext);
$(".richtext_dopreview").click(previewRichtext);
});

View file

@ -0,0 +1,175 @@
var SimpleLayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
layerStates: null,
layersDiv: null,
ascending: true,
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
this.layerStates = [];
},
destroy: function() {
OpenLayers.Event.stopObservingElement(this.div);
//clear out layers info and unregister their events
this.map.events.un({
"addlayer": this.redraw,
"changelayer": this.redraw,
"removelayer": this.redraw,
"changebaselayer": this.redraw,
scope: this
});
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
this.map.events.on({
"addlayer": this.redraw,
"changelayer": this.redraw,
"removelayer": this.redraw,
"changebaselayer": this.redraw,
scope: this
});
},
draw: function() {
OpenLayers.Control.prototype.draw.apply(this);
this.loadContents();
this.redraw();
return this.div;
},
checkRedraw: function() {
var redraw = false;
if ( !this.layerStates.length ||
(this.map.layers.length != this.layerStates.length) ) {
redraw = true;
} else {
for (var i=0, len=this.layerStates.length; i<len; i++) {
var layerState = this.layerStates[i];
var layer = this.map.layers[i];
if ( (layerState.name != layer.name) ||
(layerState.inRange != layer.inRange) ||
(layerState.id != layer.id) ||
(layerState.visibility != layer.visibility) ) {
redraw = true;
break;
}
}
}
return redraw;
},
redraw: function() {
if (!this.checkRedraw()) {
return this.div;
}
this.div.innerHTML = '';
var len = this.map.layers.length;
this.layerStates = [];
for (var i = 0; i < this.map.layers.length; i++) {
var layer = this.map.layers[i];
this.layerStates[i] = {
'name': layer.name,
'visibility': layer.visibility,
'inRange': layer.inRange,
'id': layer.id
};
}
var layers = this.map.layers.slice();
if (!this.ascending) { layers.reverse(); }
for (var i = 0; i < layers.length; i++) {
var layer = layers[i];
var baseLayer = layer.isBaseLayer;
if (layer.displayInLayerSwitcher && baseLayer) {
var on = (baseLayer) ? (layer == this.map.baseLayer)
: layer.getVisibility();
var layerElem = document.createElement('a');
layerElem.id = this.id + '_input_' + layer.name;
layerElem.innerHTML = layer.name;
layerElem.href = '#';
OpenLayers.Element.addClass(layerElem, 'basey');
OpenLayers.Element.addClass(layerElem,
'basey-' + (on ? 'on' : 'off'));
if (!baseLayer && !layer.inRange) {
layerElem.disabled = true;
}
var context = {
'layer': layer
};
OpenLayers.Event.observe(layerElem, 'mouseup',
OpenLayers.Function.bindAsEventListener(
this.onInputClick,
context)
);
this.div.appendChild(layerElem);
}
}
return this.div;
},
onInputClick: function(e) {
if (this.layer.isBaseLayer) {
this.layer.map.setBaseLayer(this.layer);
} else {
this.layer.setVisibility(!this.layer.getVisibility());
}
OpenLayers.Event.stop(e);
},
updateMap: function() {
// set the newly selected base layer
for(var i=0, len=this.baseLayers.length; i<len; i++) {
var layerEntry = this.baseLayers[i];
if (layerEntry.inputElem.checked) {
this.map.setBaseLayer(layerEntry.layer, false);
}
}
// set the correct visibilities for the overlays
for(var i=0, len=this.dataLayers.length; i<len; i++) {
var layerEntry = this.dataLayers[i];
layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
}
},
loadContents: function() {
//configure main div
OpenLayers.Event.observe(this.div, 'mouseup',
OpenLayers.Function.bindAsEventListener(this.mouseUp, this));
OpenLayers.Event.observe(this.div, 'click',
this.ignoreEvent);
OpenLayers.Event.observe(this.div, 'mousedown',
OpenLayers.Function.bindAsEventListener(this.mouseDown, this));
OpenLayers.Event.observe(this.div, 'dblclick', this.ignoreEvent);
},
ignoreEvent: function(evt) {
OpenLayers.Event.stop(evt);
},
mouseDown: function(evt) {
this.isMouseDown = true;
this.ignoreEvent(evt);
},
mouseUp: function(evt) {
if (this.isMouseDown) {
this.isMouseDown = false;
this.ignoreEvent(evt);
}
},
CLASS_NAME: "SimpleLayerSwitcher"
});

View file

@ -0,0 +1,356 @@
/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/Control/PanZoom.js
*/
/**
* Class: OpenLayers.Control.PanZoomBar
* The PanZoomBar is a visible control composed of a
* <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>.
* By default it is displayed in the upper left corner of the map as 4
* directional arrows above a vertical slider.
*
* Inherits from:
* - <OpenLayers.Control.PanZoom>
*/
OpenLayers.Control.SimplePanZoom = OpenLayers.Class(OpenLayers.Control.PanZoom, {
/**
* APIProperty: zoomStopWidth
*/
zoomStopWidth: 18,
/**
* APIProperty: zoomStopHeight
*/
zoomStopHeight: 7,
/**
* Property: slider
*/
slider: null,
/**
* Property: sliderEvents
* {<OpenLayers.Events>}
*/
sliderEvents: null,
/**
* Property: zoombarDiv
* {DOMElement}
*/
zoombarDiv: null,
/**
* APIProperty: zoomWorldIcon
* {Boolean}
*/
zoomWorldIcon: false,
/**
* APIProperty: panIcons
* {Boolean} Set this property to false not to display the pan icons. If
* false the zoom world icon is placed under the zoom bar. Defaults to
* true.
*/
panIcons: true,
/**
* APIProperty: forceFixedZoomLevel
* {Boolean} Force a fixed zoom level even though the map has
* fractionalZoom
*/
forceFixedZoomLevel: false,
/**
* Property: mouseDragStart
* {<OpenLayers.Pixel>}
*/
mouseDragStart: null,
/**
* Property: deltaY
* {Number} The cumulative vertical pixel offset during a zoom bar drag.
*/
deltaY: null,
/**
* Property: zoomStart
* {<OpenLayers.Pixel>}
*/
zoomStart: null,
/**
* Constructor: OpenLayers.Control.PanZoomBar
*/
buttons: null,
/**
* APIMethod: destroy
*/
destroy: function() {
this._removeZoomBar();
this.map.events.un({
"changebaselayer": this.redraw,
"updatesize": this.redraw,
scope: this
});
OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
delete this.mouseDragStart;
delete this.zoomStart;
},
/**
* Method: setMap
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
this.map.events.on({
"changebaselayer": this.redraw,
"updatesize": this.redraw,
scope: this
});
},
/**
* Method: redraw
* clear the div and start over.
*/
redraw: function() {
if (this.div !== null) {
this.removeButtons();
this._removeZoomBar();
}
this.draw();
this.moveZoomBar();
},
/**
* Method: draw
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*/
draw: function(px) {
// initialize our internal div
OpenLayers.Control.prototype.draw.apply(this, arguments);
px = this.position.clone();
// place the controls
this.buttons = [];
var ids = ['panup', 'panleft', 'panright', 'pandown', 'zoomout', 'zoomin'];
for (var i = 0; i < ids.length; i++) {
var b = document.createElement('div');
b.id = ids[i];
b.action = ids[i];
b.className = 'button olButton';
this.div.appendChild(b);
this.buttons.push(b);
}
this._addZoomBar();
return this.div;
},
/**
* Method: _addZoomBar
*
* Parameters:
* centered - {<OpenLayers.Pixel>} where zoombar drawing is to start.
*/
_addZoomBar:function() {
var id = this.id + "_" + this.map.id;
var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
var slider = document.createElement('div');
slider.id = 'slider';
slider.className = 'button';
slider.style.cursor = 'move';
this.slider = slider;
this.sliderEvents = new OpenLayers.Events(this, slider, null, true,
{ includeXY: true });
this.sliderEvents.on({
"touchstart": this.zoomBarDown,
"touchmove": this.zoomBarDrag,
"touchend": this.zoomBarUp,
"mousedown": this.zoomBarDown,
"mousemove": this.zoomBarDrag,
"mouseup": this.zoomBarUp
});
var height = this.zoomStopHeight * (this.map.getNumZoomLevels());
// this is the background image
var div = document.createElement('div');
div.className = 'button olButton';
div.id = 'zoombar';
this.zoombarDiv = div;
this.div.appendChild(div);
this.startTop = 75;
this.div.appendChild(slider);
this.map.events.register("zoomend", this, this.moveZoomBar);
},
/**
* Method: _removeZoomBar
*/
_removeZoomBar: function() {
this.sliderEvents.un({
"touchstart": this.zoomBarDown,
"touchmove": this.zoomBarDrag,
"touchend": this.zoomBarUp,
"mousedown": this.zoomBarDown,
"mousemove": this.zoomBarDrag,
"mouseup": this.zoomBarUp
});
this.sliderEvents.destroy();
this.div.removeChild(this.zoombarDiv);
this.zoombarDiv = null;
this.div.removeChild(this.slider);
this.slider = null;
this.map.events.unregister("zoomend", this, this.moveZoomBar);
},
/**
* Method: onButtonClick
*
* Parameters:
* evt - {Event}
*/
onButtonClick: function(evt) {
OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments);
if (evt.buttonElement === this.zoombarDiv) {
var levels = evt.buttonXY.y / this.zoomStopHeight;
if (this.forceFixedZoomLevel || !this.map.fractionalZoom) {
levels = Math.floor(levels);
}
var zoom = (this.map.getNumZoomLevels() - 1) - levels;
zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
this.map.zoomTo(zoom);
}
},
/**
* Method: passEventToSlider
* This function is used to pass events that happen on the div, or the map,
* through to the slider, which then does its moving thing.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
passEventToSlider:function(evt) {
this.sliderEvents.handleBrowserEvent(evt);
},
/*
* Method: zoomBarDown
* event listener for clicks on the slider
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarDown:function(evt) {
if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {
return;
}
this.map.events.on({
"touchmove": this.passEventToSlider,
"mousemove": this.passEventToSlider,
"mouseup": this.passEventToSlider,
scope: this
});
this.mouseDragStart = evt.xy.clone();
this.zoomStart = evt.xy.clone();
this.div.style.cursor = "move";
// reset the div offsets just in case the div moved
this.zoombarDiv.offsets = null;
OpenLayers.Event.stop(evt);
},
/*
* Method: zoomBarDrag
* This is what happens when a click has occurred, and the client is
* dragging. Here we must ensure that the slider doesn't go beyond the
* bottom/top of the zoombar div, as well as moving the slider to its new
* visual location
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarDrag: function(evt) {
if (this.mouseDragStart !== null) {
var deltaY = this.mouseDragStart.y - evt.xy.y;
var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
if ((evt.clientY - offsets[1]) > 0 &&
(evt.clientY - offsets[1]) < 140) {
var newTop = parseInt(this.slider.style.top, 10) - deltaY;
this.slider.style.top = newTop + "px";
this.mouseDragStart = evt.xy.clone();
}
// set cumulative displacement
this.deltaY = this.zoomStart.y - evt.xy.y;
OpenLayers.Event.stop(evt);
}
},
/*
* Method: zoomBarUp
* Perform cleanup when a mouseup event is received -- discover new zoom
* level and switch to it.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
zoomBarUp: function(evt) {
if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") {
return;
}
if (this.mouseDragStart) {
this.div.style.cursor = "";
this.map.events.un({
"touchmove": this.passEventToSlider,
"mouseup": this.passEventToSlider,
"mousemove": this.passEventToSlider,
scope: this
});
var zoomLevel = this.map.zoom;
zoomLevel += this.deltaY/this.zoomStopHeight;
zoomLevel = Math.max(Math.round(zoomLevel), 0);
this.map.zoomTo(zoomLevel);
this.mouseDragStart = null;
this.zoomStart = null;
this.deltaY = 0;
OpenLayers.Event.stop(evt);
}
},
/*
* Method: moveZoomBar
* Change the location of the slider to match the current zoom level.
*/
moveZoomBar:function() {
var newTop =
((this.map.getNumZoomLevels()-1) - this.map.getZoom()) *
this.zoomStopHeight + this.startTop;
this.slider.style.top = newTop + "px";
},
CLASS_NAME: "OpenLayers.Control.SimplePanZoom"
});

View file

@ -1,433 +0,0 @@
div.olMap {
z-index: 0;
padding: 0 !important;
margin: 0 !important;
cursor: default;
}
div.olMapViewport {
text-align: left;
}
div.olLayerDiv {
-moz-user-select: none;
-khtml-user-select: none;
}
.olLayerGoogleCopyright {
left: 2px;
bottom: 2px;
}
.olLayerGoogleV3.olLayerGoogleCopyright {
right: auto !important;
}
.olLayerGooglePoweredBy {
left: 2px;
bottom: 15px;
}
.olLayerGoogleV3.olLayerGooglePoweredBy {
bottom: 15px !important;
}
.olControlAttribution {
font-size: smaller;
right: 3px;
bottom: 4.5em;
position: absolute;
display: block;
}
.olControlScale {
right: 3px;
bottom: 3em;
display: block;
position: absolute;
font-size: smaller;
}
.olControlScaleLine {
display: block;
position: absolute;
left: 10px;
bottom: 15px;
font-size: xx-small;
}
.olControlScaleLineBottom {
border: solid 2px black;
border-bottom: none;
margin-top:-2px;
text-align: center;
}
.olControlScaleLineTop {
border: solid 2px black;
border-top: none;
text-align: center;
}
.olControlPermalink {
right: 3px;
bottom: 1.5em;
display: block;
position: absolute;
font-size: smaller;
}
div.olControlMousePosition {
bottom: 0em;
right: 3px;
display: block;
position: absolute;
font-family: Arial;
font-size: smaller;
}
.olControlOverviewMapContainer {
position: absolute;
bottom: 0;
right: 0;
}
.olControlOverviewMapElement {
padding: 10px 18px 10px 10px;
background-color: #00008B;
-moz-border-radius: 1em 0 0 0;
}
.olControlOverviewMapMinimizeButton {
right: 0;
bottom: 80px;
cursor: pointer;
}
.olControlOverviewMapMaximizeButton {
right: 0;
bottom: 80px;
cursor: pointer;
}
.olControlOverviewMapExtentRectangle {
overflow: hidden;
background-image: image-url("theme/default/img/blank.gif");
cursor: move;
border: 2px dotted red;
}
.olControlOverviewMapRectReplacement {
overflow: hidden;
cursor: move;
background-image: image-url("theme/default/img/overview_replacement.gif");
background-repeat: no-repeat;
background-position: center;
}
.olLayerGeoRSSDescription {
float:left;
width:100%;
overflow:auto;
font-size:1.0em;
}
.olLayerGeoRSSClose {
float:right;
color:gray;
font-size:1.2em;
margin-right:6px;
font-family:sans-serif;
}
.olLayerGeoRSSTitle {
float:left;font-size:1.2em;
}
.olPopupContent {
padding:5px;
overflow: auto;
}
.olControlNavigationHistory {
background-image: image-url("theme/default/img/navigation_history.png");
background-repeat: no-repeat;
width: 24px;
height: 24px;
}
.olControlNavigationHistoryPreviousItemActive {
background-position: 0 0;
}
.olControlNavigationHistoryPreviousItemInactive {
background-position: 0 -24px;
}
.olControlNavigationHistoryNextItemActive {
background-position: -24px 0;
}
.olControlNavigationHistoryNextItemInactive {
background-position: -24px -24px;
}
div.olControlSaveFeaturesItemActive {
background-image: image-url("theme/default/img/save_features_on.png");
background-repeat: no-repeat;
background-position: 0 1px;
}
div.olControlSaveFeaturesItemInactive {
background-image: image-url("theme/default/img/save_features_off.png");
background-repeat: no-repeat;
background-position: 0 1px;
}
.olHandlerBoxZoomBox {
border: 2px solid red;
position: absolute;
background-color: white;
opacity: 0.50;
font-size: 1px;
filter: alpha(opacity=50);
}
.olHandlerBoxSelectFeature {
border: 2px solid blue;
position: absolute;
background-color: white;
opacity: 0.50;
font-size: 1px;
filter: alpha(opacity=50);
}
.olControlPanPanel {
top: 10px;
left: 5px;
}
.olControlPanPanel div {
background-image: image-url("theme/default/img/pan-panel.png");
height: 18px;
width: 18px;
cursor: pointer;
position: absolute;
}
.olControlPanPanel .olControlPanNorthItemInactive {
top: 0;
left: 9px;
background-position: 0 0;
}
.olControlPanPanel .olControlPanSouthItemInactive {
top: 36px;
left: 9px;
background-position: 18px 0;
}
.olControlPanPanel .olControlPanWestItemInactive {
position: absolute;
top: 18px;
left: 0;
background-position: 0 18px;
}
.olControlPanPanel .olControlPanEastItemInactive {
top: 18px;
left: 18px;
background-position: 18px 18px;
}
.olControlZoomPanel {
top: 71px;
left: 14px;
}
.olControlZoomPanel div {
background-image: image-url("theme/default/img/zoom-panel.png");
position: absolute;
height: 18px;
width: 18px;
cursor: pointer;
}
.olControlZoomPanel .olControlZoomInItemInactive {
top: 0;
left: 0;
background-position: 0 0;
}
.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
top: 18px;
left: 0;
background-position: 0 -18px;
}
.olControlZoomPanel .olControlZoomOutItemInactive {
top: 36px;
left: 0;
background-position: 0 18px;
}
/*
* When a potential text is bigger than the image it move the image
* with some headers (closes #3154)
*/
.olControlPanZoomBar div {
font-size: 1px;
}
.olPopupCloseBox {
background: image-url("theme/default/img/close.gif") no-repeat;
cursor: pointer;
}
.olFramedCloudPopupContent {
padding: 5px;
overflow: auto;
}
.olControlNoSelect {
-moz-user-select: none;
-khtml-user-select: none;
}
.olImageLoadError {
background-color: pink;
opacity: 0.5;
filter: alpha(opacity=50); /* IE */
}
/**
* Cursor styles
*/
.olCursorWait {
cursor: wait;
}
.olDragDown {
cursor: move;
}
.olDrawBox {
cursor: crosshair;
}
.olControlDragFeatureOver {
cursor: move;
}
.olControlDragFeatureActive.olControlDragFeatureOver.olDragDown {
cursor: -moz-grabbing;
}
/**
* Layer switcher
*/
.olControlLayerSwitcher {
position: absolute;
top: 25px;
right: 0;
width: 20em;
font-family: sans-serif;
font-weight: bold;
margin-top: 3px;
margin-left: 3px;
margin-bottom: 3px;
font-size: smaller;
color: white;
background-color: transparent;
}
.olControlLayerSwitcher .layersDiv {
padding-top: 5px;
padding-left: 10px;
padding-bottom: 5px;
padding-right: 75px;
background-color: darkblue;
width: 100%;
height: 100%;
}
.olControlLayerSwitcher .layersDiv .baseLbl,
.olControlLayerSwitcher .layersDiv .dataLbl {
margin-top: 3px;
margin-left: 3px;
margin-bottom: 3px;
}
.olControlLayerSwitcher .layersDiv .baseLayersDiv,
.olControlLayerSwitcher .layersDiv .dataLayersDiv {
padding-left: 10px;
}
.olControlLayerSwitcher .maximizeDiv,
.olControlLayerSwitcher .minimizeDiv {
top: 5px;
right: 0;
cursor: pointer;
}
.olBingAttribution {
color: #DDD;
}
.olBingAttribution.road {
color: #333;
}
.olGoogleAttribution.hybrid, .olGoogleAttribution.satellite {
color: #EEE;
}
.olGoogleAttribution {
color: #333;
}
span.olGoogleAttribution a {
color: #77C;
}
span.olGoogleAttribution.hybrid a, span.olGoogleAttribution.satellite a {
color: #EEE;
}
/**
* Editing and navigation icons.
* (using the editing_tool_bar.png sprint image)
*/
.olControlNavToolbar ,
.olControlEditingToolbar {
margin: 5px 5px 0 0;
}
.olControlNavToolbar div,
.olControlEditingToolbar div {
background-image: image-url("theme/default/img/editing_tool_bar.png");
background-repeat: no-repeat;
margin: 0 0 5px 5px;
width: 24px;
height: 22px;
cursor: pointer
}
/* positions */
.olControlEditingToolbar {
right: 0;
top: 0;
}
.olControlNavToolbar {
top: 295px;
left: 9px;
}
/* layouts */
.olControlEditingToolbar div {
float: right;
}
/* individual controls */
.olControlNavToolbar .olControlNavigationItemInactive,
.olControlEditingToolbar .olControlNavigationItemInactive {
background-position: -103px -1px;
}
.olControlNavToolbar .olControlNavigationItemActive ,
.olControlEditingToolbar .olControlNavigationItemActive {
background-position: -103px -24px;
}
.olControlNavToolbar .olControlZoomBoxItemInactive {
background-position: -128px -1px;
}
.olControlNavToolbar .olControlZoomBoxItemActive {
background-position: -128px -24px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
background-position: -77px -1px;
}
.olControlEditingToolbar .olControlDrawFeaturePointItemActive {
background-position: -77px -24px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
background-position: -51px -1px;
}
.olControlEditingToolbar .olControlDrawFeaturePathItemActive {
background-position: -51px -24px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive{
background-position: -26px -1px;
}
.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
background-position: -26px -24px;
}

View file

@ -0,0 +1,44 @@
.SimpleLayerSwitcher {
position: absolute;
top: 10px;
right: 10px;
background: #fff;
border: 1px solid #ccc;
min-width: 150px;
background: #fff;
}
.SimpleLayerSwitcher a.basey {
display: block;
text-decoration: none;
color: #838383;
padding: 2px 5px 2px 20px;
}
.SimpleLayerSwitcher a.basey-on {
color: #000;
background-color: #fff;
background-image: image-url("theme/openstreetmap/img/carat.png");
background-repeat: no-repeat;
background-position: 7px 9px;
}
.SimpleLayerSwitcher a.basey-off {
display: none;
}
.SimpleLayerSwitcher:hover a {
border-top: 1px solid #eee;
}
.SimpleLayerSwitcher a:hover {
background-color: #f5f5f5;
}
.SimpleLayerSwitcher:hover a:first-child {
border-top: none;
}
.SimpleLayerSwitcher:hover a.basey-off {
display: block;
}

View file

@ -0,0 +1,79 @@
.olControlSimplePanZoom {
top: 10px;
right: 10px;
}
.olControlSimplePanZoom .button {
background-image: image-url("theme/openstreetmap/img/map_sprite.png");
position: absolute;
background-repeat: no-repeat;
cursor: hand;
cursor: pointer;
}
.olControlSimplePanZoom #panup {
left: 10px;
width: 25px;
height: 13px;
background-position: -15px -5px;
}
.olControlSimplePanZoom #pandown {
left: 10px;
top: 36px;
width: 25px;
height: 15px;
background-position: -15px -40px;
}
.olControlSimplePanZoom #panleft {
top: 13px;
width: 25px;
height: 24px;
background-position: -5px -17px;
}
.olControlSimplePanZoom #panright {
top: 13px;
width: 25px;
height: 24px;
left: 25px;
background-position: -30px -17px;
}
.olControlSimplePanZoom #zoomin {
top: 50px;
width: 26px;
height: 20px;
left: 10px;
background-position: -15px -61px;
}
.olControlSimplePanZoom #zoomout {
top: 210px;
width: 26px;
height: 20px;
left: 10px;
background-position: -15px -220px;
}
.olControlSimplePanZoom #slider {
top: 75px;
width: 25px;
height: 10px;
left: 10px;
-webkit-transition: top 100ms linear;
-moz-transition: top 100ms linear;
-o-transition: top 100ms linear;
background-position: -77px -58px;
pointer: move;
cursor: move;
}
.olControlSimplePanZoom #zoombar {
top: 70px;
width: 26px;
height: 140px;
left: 10px;
background-position: -15px -80px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -0,0 +1,61 @@
/*
*= require theme/default/style
*= require theme/openstreetmap/SimpleLayerSwitcher
*= require theme/openstreetmap/SimplePanZoom
*/
.olControlOverviewMapExtentRectangle {
background-image: image-url("theme/default/img/blank.gif");
}
.olControlOverviewMapRectReplacement {
background-image: image-url("theme/default/img/overview_replacement.gif");
}
.olControlNavigationHistory {
background-image: image-url("theme/default/img/navigation_history.png");
}
div.olControlSaveFeaturesItemActive {
background-image: image-url("theme/default/img/save_features_on.png");
}
div.olControlSaveFeaturesItemInactive {
background-image: image-url("theme/default/img/save_features_off.png");
}
.olControlPanPanel div {
background-image: image-url("theme/default/img/pan-panel.png");
}
.olControlZoomPanel div {
background-image: image-url("theme/default/img/zoom-panel.png");
}
.olPopupCloseBox {
background: image-url("theme/default/img/close.gif") no-repeat;
}
.olImageLoadError {
// remove opacity?
// remove filter?
background: image-url("theme/openstreetmap/img/missing-tile.png") no-repeat;
}
.olControlNavToolbar div,
.olControlEditingToolbar div {
background-image: image-url("theme/default/img/editing_tool_bar.png");
}
div.olControlZoom a {
color: black;
background: #ffffff;
border: 1px solid #cccccc;
// remove filter
}
div.olControlZoom a:hover {
background: #f5f5f5;
}
// remove max-width hover?

View file

@ -85,12 +85,17 @@ h2 {
/* Rules for the introductory text displayed in the left sidebar to new users */
#intro {
border-top: 1px solid #ccc;
.sidebar-copy {
padding: 0px 10px;
}
#intro p {
margin: 5px;
.sidebar-copy p {
margin: 5px 0;
}
.sidebar-copy.intro {
margin-top: -1px;
border-top: 1px solid #ccc;
}
/*
@ -99,16 +104,19 @@ h2 {
* undergoing maintenance.
*/
#alert {
width: 170px;
margin: 5px;
padding: 5px;
border: 1px solid #ccc;
background: #d00;
line-height: 1.2em;
font-size: 14px;
border-radius: 5px;
-moz-border-radius: 5px;
.sidebar-alert {
padding: 4px 5px 4px 5px;
border-top: 1px solid #ccc;
margin-top: 4px;
margin-bottom: -4px;
background: #e00;
font-size: 13px;
font-weight: bold;
line-height: 17px;
p {
margin: 5px;
}
}
/*
@ -117,25 +125,26 @@ h2 {
* donation drives.
*/
.notice {
width: 170px;
margin: 5px;
padding: 5px;
border: 1px solid #ccc;
.sidebar-notice {
padding: 4px 5px 4px 5px;
border-top: 1px solid #ccc;
margin-top: 4px;
margin-bottom: -4px;
background: #ea0;
line-height: 20px;
font-size: 14px;
border-radius: 5px;
-moz-border-radius: 5px;
font-size: 13px;
line-height: 17px;
p {
margin: 5px;
}
}
/* Rules for the menu displayed in the left sidebar */
.left_menu {
padding: 5px;
padding: 5px 10px;
margin: 4px 0;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
line-height: 20px;
font-size: 14px;
font-weight: bold;
@ -156,6 +165,13 @@ h2 {
padding: 0px;
}
/* submenus */
.left_menu ul li ul {
font-weight: normal;
line-height: 15px;
font-size: 12px;
}
.left_menu a {
color: #000;
}
@ -163,14 +179,17 @@ h2 {
/* Rules for SOTM advert */
#sotm {
width: 180px;
min-width: 180px;
margin: 5px;
width: 165px;
margin: 10px;
padding: 0px;
border: 0px;
background: #fff;
}
#sotm img {
width: 165px;
}
/*
* Rules for "optional boxes" which appear in the left sidebar on
* certain pages. Current users are the seach box on the main page
@ -178,9 +197,8 @@ h2 {
*/
.optionalbox {
padding: 5px;
padding: 5px 10px;
margin: 4px 0;
border-top: 1px solid #ccc;
}
.optionalbox h1 {
@ -194,13 +212,6 @@ h2 {
/* Rules for the search box */
.whereami {
line-height: 20px;
vertical-align: bottom;
}
.search_container {
height: 15px;
padding-bottom: 5px;
}
#search_field form {
@ -209,34 +220,71 @@ h2 {
padding: 0px;
}
#search_field {
position:relative;
}
#search_field input[type="text"] {
width: 136px;
width: 165px;
padding: 5px;
font-size: 14px;
line-height: 15px;
height: 25px;
box-shadow: inset #DDD 0px 1px 3px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
}
#search_field input[type="text"]:focus {
outline: none;
border: 1px solid #000;
}
#search_field input[type="submit"] {
width: 26px;
width: 15px;
height: 15px;
border:0;
text-indent:-1000px;
padding-left: 0px;
padding-right: 0px;
background: url(sprite.png);
position:absolute;
right:2px;
top:5px;
cursor:pointer;
}
.search_help {
margin-top: 2px;
margin-bottom: 0px;
margin:0;
}
.deemphasize {
color: #999;
}
.deemphasize a {
color: #7092FF;
}
/* Rules for donation request box */
.donate {
width: 155px;
margin: 10px 5px;
width: 153px;
margin: 10px 10px;
padding: 5px;
border: 1px solid #ccc;
border: 1px solid #AED1A0;
background: #cbeea7;
line-height: 20px;
text-align: center;
font-size: 14px;
border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 2px;
-moz-border-radius: 2px;
}
.donate a {
color:#222;
}
/* Rules for Creative Commons logo button */
@ -290,7 +338,24 @@ h2 {
#tabnav a:visited.disabled,
#tabnav a:link:hover.disabled,
#tabnav a:visited:hover.disabled {
color: #888;
color: #ccc;
cursor: default;
}
#tabnav a:link.disabled:hover,
#tabnav a:visited.disabled:hover,
#tabnav a:link:hover.disabled:hover,
#tabnav a:visited:hover.disabled:hover {
text-decoration: none;
}
.count-number {
padding: 2px 5px;
border-radius: 3px;
background: #eee;
margin: 0 2px;
font-size: 11px;
color: #333;
}
/* Rules for greeting bar in the top right corner */
@ -345,23 +410,34 @@ h2 {
/* Rules for edit menu */
.menuicon {
padding: 5px;
&:hover {
background: #eee;
text-decoration: none !important;
}
}
.menu {
display: none;
z-index: 10000;
position: absolute;
background-color: #ffffff;
border: 1px solid black;
border: 1px solid #cccccc;
border-top: 0px;
}
.menu ul {
margin-top: 10px;
margin-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
margin: 0px;
padding: 0px;
}
.menu li {
padding: 2px 5px;
margin: 0px;
list-style-type: none;
border-top: 1px solid #eee;
white-space: nowrap;
}
@ -828,6 +904,10 @@ p#contributorGuidance {
/* Rules for the account settings page */
#accountForm {
margin-top: 20px;
}
#accountForm td {
padding-bottom: 10px;
}
@ -879,11 +959,11 @@ p#contributorGuidance {
/* Rules for the user map */
.user_map .olControlPanZoomBar {
.user_map .olControlSimplePanZoom {
display: none;
}
.user_map .olControlPanZoom {
.user_map .olControlZoom {
display: block;
}
@ -912,6 +992,14 @@ p#contributorGuidance {
font-weight: bold;
}
.inbox-row .inbox-mark-read {
display: none;
}
.inbox-row-unread .inbox-mark-unread {
display: none;
}
/* Rules for "flash" notice boxes shown at the top of the content area */
#error {
@ -997,7 +1085,7 @@ input[type="email"],
input[type="url"],
input[type="password"],
textarea {
border: 1px solid #888;
border: 1px solid #ccc;
}
/* Rules for user images */
@ -1049,3 +1137,63 @@ abbr.geo {
vertical-align: text-bottom;
border: 0;
}
/* Rules for rich text editors */
.richtext_container {
white-space: nowrap;
.richtext_content {
display: inline-block;
vertical-align: top;
.richtext_preview {
display: inline-block;
margin-top: 1px;
margin-bottom: 1px;
border: 4px solid #eee;
background-color: #eee;
white-space: normal;
&.loading {
background-image: image-url("loading.gif");
background-repeat: no-repeat;
background-position: center;
}
> :first-child {
margin-top: 0px;
}
}
}
.richtext_help {
display: inline-block;
vertical-align: top;
background-color: #ddd;
padding: 5px 10px 10px 10px;
font-size: 12px;
p {
margin: 0px;
}
th {
vertical-align: top;
}
td {
font-family: fixed;
line-height: 16px;
padding: 0px !important;
}
input.richtext_doedit {
margin-top: 5px !important;
}
input.richtext_dopreview {
margin-top: 5px !important;
}
}
}

View file

@ -14,7 +14,7 @@
/* Rules for OpenLayers maps */
.olControlPanZoom {
.olControlZoom {
display: none;
}

View file

@ -6,26 +6,6 @@ html body {
text-align: left;
}
/*
* Rules for alert boxes shown in the left sidebar when important
* information needs to be conveyed such as when the site is
* undergoing maintenance.
*/
#alert {
text-align: left;
}
/*
* Rules for notice boxes shown in the left sidebar when important, but
* non-critical information needs to be conveyed such as notices about
* donation drives.
*/
.notice {
text-align: left;
}
/* Rules for the menu displayed in the left sidebar */
.left_menu {
@ -60,12 +40,6 @@ html body {
text-align: left;
}
/* Rules for the search box */
.whereami {
float: right;
}
/* Rules for tabbed navigation bar */
#top-bar {
@ -235,3 +209,28 @@ input.openid_url {
background: image-url('openid_input.png') repeat-y left white;
padding-left: 16px;
}
/* Rules for rich text editors */
.richtext_container {
.richtext_help {
margin-left: 15px;
th {
text-align: left;
padding: 0px 15px 0px 0px !important;
}
td {
text-align: left;
}
input.richtext_doedit {
margin-right: 10px !important;
}
input.richtext_dopreview {
margin-left: 10px !important;
}
}
}

View file

@ -26,15 +26,15 @@
display: none;
}
.olControlPanZoom {
.olControlZoom {
display: none;
}
.olControlPanZoomBar {
.olControlSimplePanZoom {
display: none;
}
.olControlLayerSwitcher {
.SimpleLayerSwitcher {
display: none;
}

View file

@ -6,26 +6,6 @@ html body {
text-align: right;
}
/*
* Rules for alert boxes shown in the left sidebar when important
* information needs to be conveyed such as when the site is
* undergoing maintenance.
*/
#alert {
text-align: right;
}
/*
* Rules for notice boxes shown in the left sidebar when important, but
* non-critical information needs to be conveyed such as notices about
* donation drives.
*/
.notice {
text-align: right;
}
/* Rules for the menu displayed in the left sidebar */
.left_menu {
@ -60,12 +40,6 @@ html body {
text-align: right;
}
/* Rules for the search box */
.whereami {
float: left;
}
/* Rules for tabbed navigation bar */
#top-bar {
@ -235,3 +209,28 @@ input.openid_url {
background: image-url('openid_input.png') repeat-y right white;
padding-right: 16px;
}
/* Rules for rich text editors */
.richtext_container {
.richtext_help {
margin-right: 15px;
th {
text-align: right;
padding: 0px 0px 0px 15px !important;
}
td {
text-align: right;
}
input.richtext_doedit {
margin-left: 10px !important;
}
input.richtext_dopreview {
margin-right: 10px !important;
}
}
}

View file

@ -18,7 +18,8 @@ h1 {
/* Rules for tabbed navigation bar */
#top-bar {
margin: 0;
margin: 0px;
height: 19px;
}
#tabnav {
@ -90,18 +91,20 @@ h1 {
border: 0;
}
.olControlPanZoomBar {
.olControlSimplePanZoom {
display: none;
}
/* Rules for the main content area */
#content {
left: 10px;
right: 10px;
top: 47px;
left: 0px;
right: 0px;
top: 38px;
margin-left: 0px;
margin-right: 0px;
border-left: 0px;
border-right: 0px;
}
#content.site_index {

View file

@ -133,7 +133,7 @@ class AmfController < ApplicationController
amf_handle_error("'startchangeset'",nil,nil) do
user = getuser(usertoken)
if !user then return -1,"You are not logged in, so Potlatch can't write any changes to the database." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
if cstags
@ -359,13 +359,13 @@ class AmfController < ApplicationController
amf_handle_error_with_timeout("'getway_old' #{id}, #{timestamp}", 'way',id) do
if timestamp == ''
# undelete
old_way = OldWay.where(:visible => true, :way_id => id).order("version DESC").first
old_way = OldWay.where(:visible => true, :way_id => id).unredacted.order("version DESC").first
points = old_way.get_nodes_undelete unless old_way.nil?
else
begin
# revert
timestamp = DateTime.strptime(timestamp.to_s, "%d %b %Y, %H:%M:%S")
old_way = OldWay.where("way_id = ? AND timestamp <= ?", id, timestamp).order("timestamp DESC").first
old_way = OldWay.where("way_id = ? AND timestamp <= ?", id, timestamp).unredacted.order("timestamp DESC").first
unless old_way.nil?
points = old_way.get_nodes_revert(timestamp)
if !old_way.visible
@ -403,11 +403,11 @@ class AmfController < ApplicationController
# Find list of revision dates for way and all constituent nodes
revdates=[]
revusers={}
Way.find(wayid).old_ways.collect do |a|
Way.find(wayid).old_ways.unredacted.collect do |a|
revdates.push(a.timestamp)
unless revusers.has_key?(a.timestamp.to_i) then revusers[a.timestamp.to_i]=change_user(a) end
a.nds.each do |n|
Node.find(n).old_nodes.collect do |o|
Node.find(n).old_nodes.unredacted.collect do |o|
revdates.push(o.timestamp)
unless revusers.has_key?(o.timestamp.to_i) then revusers[o.timestamp.to_i]=change_user(o) end
end
@ -423,7 +423,7 @@ class AmfController < ApplicationController
# Remove any elements where 2 seconds doesn't elapse before next one
revdates.delete_if { |d| revdates.include?(d+1) or revdates.include?(d+2) }
# Collect all in one nested array
revdates.collect! {|d| [d.succ.strftime("%d %b %Y, %H:%M:%S")] + revusers[d.to_i] }
revdates.collect! {|d| [(d + 1).strftime("%d %b %Y, %H:%M:%S")] + revusers[d.to_i] }
revdates.uniq!
return ['way', wayid, revdates]
@ -436,8 +436,8 @@ class AmfController < ApplicationController
def getnode_history(nodeid) #:doc:
begin
history = Node.find(nodeid).old_nodes.reverse.collect do |old_node|
[old_node.timestamp.succ.strftime("%d %b %Y, %H:%M:%S")] + change_user(old_node)
history = Node.find(nodeid).old_nodes.unredacted.reverse.collect do |old_node|
[(old_node.timestamp + 1).strftime("%d %b %Y, %H:%M:%S")] + change_user(old_node)
end
return ['node', nodeid, history]
rescue ActiveRecord::RecordNotFound
@ -459,7 +459,7 @@ class AmfController < ApplicationController
amf_handle_error_with_timeout("'findgpx'" ,nil,nil) do
user = getuser(usertoken)
if !user then return -1,"You must be logged in to search for GPX traces." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
query = Trace.visible_to(user)
if searchterm.to_i > 0 then
@ -523,7 +523,7 @@ class AmfController < ApplicationController
amf_handle_error("'putrelation' #{relid}" ,'relation',relid) do
user = getuser(usertoken)
if !user then return -1,"You are not logged in, so the relation could not be saved." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
@ -613,7 +613,7 @@ class AmfController < ApplicationController
user = getuser(usertoken)
if !user then return -1,"You are not logged in, so the way could not be saved." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
@ -722,7 +722,7 @@ class AmfController < ApplicationController
amf_handle_error("'putpoi' #{id}", 'node',id) do
user = getuser(usertoken)
if !user then return -1,"You are not logged in, so the point could not be saved." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
@ -782,7 +782,7 @@ class AmfController < ApplicationController
n = Node.find(id)
v = n.version
unless timestamp == ''
n = OldNode.where("id = ? AND timestamp <= ?", id, timestamp).order("timestamp DESC").first
n = OldNode.where("node_id = ? AND timestamp <= ?", id, timestamp).unredacted.order("timestamp DESC").first
end
if n
@ -807,7 +807,7 @@ class AmfController < ApplicationController
amf_handle_error("'deleteway' #{way_id}" ,'way', way_id) do
user = getuser(usertoken)
unless user then return -1,"You are not logged in, so the way could not be deleted." end
unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
if user.blocks.active.exists? then return -1,t('application.setup_user_auth.blocked') end
if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
way_id = way_id.to_i

View file

@ -2,6 +2,7 @@ class ApiController < ApplicationController
skip_before_filter :verify_authenticity_token
before_filter :check_api_readable, :except => [:capabilities]
before_filter :setup_user_auth, :only => [:permissions]
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
@ -289,4 +290,20 @@ class ApiController < ApplicationController
render :text => doc.to_s, :content_type => "text/xml"
end
# External apps that use the api are able to query which permissions
# they have. This currently returns a list of permissions granted to the current user:
# * if authenticated via OAuth, this list will contain all permissions granted by the user to the access_token.
# * if authenticated via basic auth all permissions are granted, so the list will contain all permissions.
# * unauthenticated users have no permissions, so the list will be empty.
def permissions
@permissions = case
when current_token.present?
ClientApplication.all_permissions.select { |p| current_token.read_attribute(p) }
when @user
ClientApplication.all_permissions
else
[]
end
end
end

View file

@ -4,13 +4,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery
if STATUS == :database_readonly or STATUS == :database_offline
after_filter :clear_session
wrap_parameters false
def clear_session
session.clear
end
def self.cache_sweeper(*sweepers)
end
end
@ -51,7 +44,13 @@ class ApplicationController < ActionController::Base
end
def require_user
redirect_to :controller => 'user', :action => 'login', :referer => request.fullpath unless @user
unless @user
if request.get?
redirect_to :controller => 'user', :action => 'login', :referer => request.fullpath
else
render :nothing => true, :status => :forbidden
end
end
end
##
@ -112,6 +111,20 @@ class ApplicationController < ActionController::Base
require_capability(:allow_write_gpx)
end
##
# require that the user is a moderator, or fill out a helpful error message
# and return them to the index for the controller this is wrapped from.
def require_moderator
unless @user.moderator?
if request.get?
flash[:error] = t('application.require_moderator.not_a_moderator')
redirect_to :action => 'index'
else
render :nothing => true, :status => :forbidden
end
end
end
##
# sets up the @user object for use by other methods. this is mostly called
# from the authorize method, but can be called elsewhere if authorisation
@ -133,7 +146,7 @@ class ApplicationController < ActionController::Base
# have we identified the user?
if @user
# check if the user has been banned
if not @user.active_blocks.empty?
if @user.blocks.active.exists?
# NOTE: need slightly more helpful message than this.
report_error t('application.setup_user_auth.blocked'), :forbidden
end
@ -161,6 +174,22 @@ class ApplicationController < ActionController::Base
end
end
##
# to be used as a before_filter *after* authorize. this checks that
# the user is a moderator and, if not, returns a forbidden error.
#
# NOTE: this isn't a very good way of doing it - it duplicates logic
# from require_moderator - but what we really need to do is a fairly
# drastic refactoring based on :format and respond_to? but not a
# good idea to do that in this branch.
def authorize_moderator(errormessage="Access restricted to moderators")
# check user is a moderator
unless @user.moderator?
render :text => errormessage, :status => :forbidden
return false
end
end
def check_database_readable(need_api = false)
if STATUS == :database_offline or (need_api and STATUS == :api_offline)
redirect_to :controller => 'site', :action => 'offline'
@ -356,6 +385,26 @@ class ApplicationController < ActionController::Base
!@user.nil?
end
##
# ensure that there is a "this_user" instance variable
def lookup_this_user
unless @this_user = User.active.find_by_display_name(params[:display_name])
render_unknown_user params[:display_name]
end
end
##
# render a "no such user" page
def render_unknown_user(name)
@title = t "user.no_such_user.title"
@not_found_user = name
respond_to do |format|
format.html { render :template => "user/no_such_user", :status => :not_found }
format.all { render :nothing => true, :status => :not_found }
end
end
private
# extract authorisation credentials from headers, returns user = nil if none

View file

@ -7,6 +7,11 @@ class BrowseController < ApplicationController
around_filter :web_timeout, :except => [:start]
def start
@max_features = case
when browser.ie? && browser.version.to_i < 8 then 100
when browser.ie? && browser.version.to_i < 9 then 500
else 2000
end
end
def relation

View file

@ -143,10 +143,11 @@ class ChangesetController < ApplicationController
def download
changeset = Changeset.find(params[:id])
# get all the elements in the changeset and stick them in a big array.
elements = [changeset.old_nodes,
changeset.old_ways,
changeset.old_relations].flatten
# get all the elements in the changeset which haven't been redacted
# and stick them in a big array.
elements = [changeset.old_nodes.unredacted,
changeset.old_ways.unredacted,
changeset.old_relations.unredacted].flatten
# sort the elements by timestamp and version number, as this is the
# almost sensible ordering available. this would be much nicer if
@ -259,10 +260,8 @@ class ChangesetController < ApplicationController
else
changesets = changesets.where("false")
end
elsif request.format == :html
@title = t 'user.no_such_user.title'
@not_found_user = params[:display_name]
render :template => 'user/no_such_user', :status => :not_found
else
render_unknown_user params[:display_name]
return
end
end

View file

@ -4,6 +4,7 @@ class DiaryEntryController < ApplicationController
before_filter :authorize_web
before_filter :set_locale
before_filter :require_user, :only => [:new, :edit, :comment, :hide, :hidecomment]
before_filter :lookup_this_user, :only => [:view, :comments]
before_filter :check_database_readable
before_filter :check_database_writable, :only => [:new, :edit]
before_filter :require_administrator, :only => [:hide, :hidecomment]
@ -84,10 +85,7 @@ class DiaryEntryController < ApplicationController
:order => 'created_at DESC',
:per_page => 20)
else
@title = t'diary_entry.no_such_user.title'
@not_found_user = params[:display_name]
render :action => 'no_such_user', :status => :not_found
render_unknown_user params[:display_name]
end
elsif params[:language]
@title = t 'diary_entry.list.in_language_title', :language => Language.find(params[:language]).english_name
@ -167,49 +165,36 @@ class DiaryEntryController < ApplicationController
end
def view
user = User.active.find_by_display_name(params[:display_name])
if user
@entry = user.diary_entries.visible.where(:id => params[:id]).first
if @entry
@title = t 'diary_entry.view.title', :user => params[:display_name], :title => @entry.title
else
@title = t 'diary_entry.no_such_entry.title', :id => params[:id]
render :action => 'no_such_entry', :status => :not_found
end
@entry = @this_user.diary_entries.visible.where(:id => params[:id]).first
if @entry
@title = t 'diary_entry.view.title', :user => params[:display_name], :title => @entry.title
else
@not_found_user = params[:display_name]
render :action => 'no_such_user', :status => :not_found
@title = t 'diary_entry.no_such_entry.title', :id => params[:id]
render :action => 'no_such_entry', :status => :not_found
end
end
def hide
entry = DiaryEntry.find(params[:id])
entry.update_attributes(:visible => false)
entry.update_attributes({:visible => false}, :without_protection => true)
redirect_to :action => "list", :display_name => entry.user.display_name
end
def hidecomment
comment = DiaryComment.find(params[:comment])
comment.update_attributes(:visible => false)
comment.update_attributes({:visible => false}, :without_protection => true)
redirect_to :action => "view", :display_name => comment.diary_entry.user.display_name, :id => comment.diary_entry.id
end
def comments
@this_user = User.active.find_by_display_name(params[:display_name])
if @this_user
@comment_pages, @comments = paginate(:diary_comments,
:conditions => { :user_id => @this_user },
:order => 'created_at DESC',
:per_page => 20)
@page = (params[:page] || 1).to_i
else
@title = t'diary_entry.no_such_user.title'
@not_found_user = params[:display_name]
render :action => 'no_such_user', :status => :not_found
end
@comment_pages, @comments = paginate(:diary_comments,
:conditions => {
:user_id => @this_user,
:visible => true
},
:order => 'created_at DESC',
:per_page => 20)
@page = (params[:page] || 1).to_i
end
private
##

View file

@ -21,15 +21,6 @@ class ExportController < ApplicationController
scale = params[:mapnik_scale]
redirect_to "http://parent.tile.openstreetmap.org/cgi-bin/export?bbox=#{bbox}&scale=#{scale}&format=#{format}"
elsif format == "osmarender"
#redirect to the t@h 'MapOf' service
format = params[:osmarender_format]
zoom = params[:osmarender_zoom].to_i
width = bbox.slippy_width(zoom).to_i
height = bbox.slippy_height(zoom).to_i
redirect_to "http://tah.openstreetmap.org/MapOf/index.php?long=#{bbox.centre_lon}&lat=#{bbox.centre_lat}&z=#{zoom}&w=#{width}&h=#{height}&format=#{format}"
end
end
end

View file

@ -26,7 +26,7 @@ class GeocoderController < ApplicationController
@sources.push "osm_nominatim"
else
@sources.push "osm_nominatim"
@sources.push "geonames"
@sources.push "geonames" if defined?(GEONAMES_USERNAME)
end
end
@ -272,7 +272,7 @@ class GeocoderController < ApplicationController
@results = Array.new
# ask geonames.org
response = fetch_xml("http://ws.geonames.org/search?q=#{escape_query(query)}&maxRows=20")
response = fetch_xml("http://api.geonames.org/search?q=#{escape_query(query)}&maxRows=20&username=#{GEONAMES_USERNAME}")
# parse the response
response.elements.each("geonames/geoname") do |geoname|

View file

@ -4,44 +4,32 @@ class MessageController < ApplicationController
before_filter :authorize_web
before_filter :set_locale
before_filter :require_user
before_filter :lookup_this_user, :only => [:new]
before_filter :check_database_readable
before_filter :check_database_writable, :only => [:new, :reply, :mark]
# Allow the user to write a new message to another user. This action also
# Allow the user to write a new message to another user. This action also
# deals with the sending of that message to the other user when the user
# clicks send.
# The display_name param is the display name of the user that the message is being sent to.
def new
@to_user = User.find_by_display_name(params[:display_name])
if @to_user
if params[:message]
if @user.sent_messages.where("sent_on >= ?", Time.now.getutc - 1.hour).count >= MAX_MESSAGES_PER_HOUR
flash[:error] = t 'message.new.limit_exceeded'
else
@message = Message.new(params[:message])
@message.to_user_id = @to_user.id
@message.from_user_id = @user.id
@message.sent_on = Time.now.getutc
if @message.save
flash[:notice] = t 'message.new.message_sent'
Notifier.message_notification(@message).deliver
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
end
if params[:message]
if @user.sent_messages.where("sent_on >= ?", Time.now.getutc - 1.hour).count >= MAX_MESSAGES_PER_HOUR
flash[:error] = t 'message.new.limit_exceeded'
else
if params[:title]
# ?title= is set when someone reponds to this user's diary
# entry. Then we pre-fill out the subject and the <title>
@title = @subject = params[:title]
else
# The default /message/new/$user view
@title = t 'message.new.title'
@message = Message.new(params[:message])
@message.to_user_id = @this_user.id
@message.from_user_id = @user.id
@message.sent_on = Time.now.getutc
if @message.save
flash[:notice] = t 'message.new.message_sent'
Notifier.message_notification(@message).deliver
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
end
else
@title = t'message.no_such_user.title'
render :action => 'no_such_user', :status => :not_found
@title = t 'message.new.title'
end
end
@ -50,9 +38,9 @@ class MessageController < ApplicationController
message = Message.find(params[:message_id])
if message.to_user_id == @user.id then
@body = "On #{message.sent_on} #{message.sender.display_name} wrote:\n\n#{message.body.gsub(/^/, '> ')}"
@body = "On #{message.sent_on} #{message.sender.display_name} wrote:\n\n#{message.body.gsub(/^/, '> ')}"
@title = @subject = "Re: #{message.title.sub(/^Re:\s*/, '')}"
@to_user = User.find(message.from_user_id)
@this_user = User.find(message.from_user_id)
render :action => 'new'
else
@ -101,22 +89,19 @@ class MessageController < ApplicationController
# Set the message as being read or unread.
def mark
if params[:message_id]
id = params[:message_id]
@message = Message.where(:id => id).where("to_user_id = ? OR from_user_id = ?", @user.id, @user.id).first
if params[:mark] == 'unread'
message_read = false
notice = t 'message.mark.as_unread'
else
message_read = true
notice = t 'message.mark.as_read'
end
@message.message_read = message_read
if @message.save
if not request.xhr?
flash[:notice] = notice
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
@message = Message.where("to_user_id = ? OR from_user_id = ?", @user.id, @user.id).find(params[:message_id])
if params[:mark] == 'unread'
message_read = false
notice = t 'message.mark.as_unread'
else
message_read = true
notice = t 'message.mark.as_read'
end
@message.message_read = message_read
if @message.save
if not request.xhr?
flash[:notice] = notice
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
end
rescue ActiveRecord::RecordNotFound
@ -126,19 +111,16 @@ class MessageController < ApplicationController
# Delete the message.
def delete
if params[:message_id]
id = params[:message_id]
message = Message.where(:id => id).where("to_user_id = ? OR from_user_id = ?", @user.id, @user.id).first
message.from_user_visible = false if message.sender == @user
message.to_user_visible = false if message.recipient == @user
if message.save
flash[:notice] = t 'message.delete.deleted'
message = Message.where("to_user_id = ? OR from_user_id = ?", @user.id, @user.id).find(params[:message_id])
message.from_user_visible = false if message.sender == @user
message.to_user_visible = false if message.recipient == @user
if message.save
flash[:notice] = t 'message.delete.deleted'
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
end
rescue ActiveRecord::RecordNotFound

View file

@ -32,7 +32,7 @@ class OauthController < ApplicationController
@token.invalidate!
flash[:notice] = t('oauth.revoke.flash', :application => @token.client_application.name)
end
redirect_to :controller => 'oauth_clients', :action => 'index'
redirect_to oauth_clients_url(:display_name => @token.user.display_name)
end
protected

View file

@ -0,0 +1,77 @@
# this class pulls together the logic for all the old_* controllers
# into one place. as it turns out, the API methods for historical
# nodes, ways and relations are basically identical.
class OldController < ApplicationController
require 'xml/libxml'
skip_before_filter :verify_authenticity_token
before_filter :setup_user_auth, :only => [ :history, :version ]
before_filter :authorize, :only => [ :redact ]
before_filter :authorize_moderator, :only => [ :redact ]
before_filter :require_allow_write_api, :only => [ :redact ]
before_filter :check_api_readable
before_filter :check_api_writable, :only => [ :redact ]
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
before_filter :lookup_old_element, :except => [ :history ]
before_filter :lookup_old_element_versions, :only => [ :history ]
def history
# the .where() method used in the lookup_old_element_versions
# call won't throw an error if no records are found, so we have
# to do that ourselves.
raise OSM::APINotFoundError.new if @elements.empty?
doc = OSM::API.new.get_xml_doc
visible_elements = if show_redactions?
@elements
else
@elements.unredacted
end
visible_elements.each do |element|
doc.root << element.to_xml_node
end
render :text => doc.to_s, :content_type => "text/xml"
end
def version
if @old_element.redacted? and not show_redactions?
render :nothing => true, :status => :forbidden
else
response.last_modified = @old_element.timestamp
doc = OSM::API.new.get_xml_doc
doc.root << @old_element.to_xml_node
render :text => doc.to_s, :content_type => "text/xml"
end
end
def redact
redaction_id = params['redaction']
unless redaction_id.nil?
# if a redaction ID was specified, then set this element to
# be redacted in that redaction.
redaction = Redaction.find(redaction_id.to_i)
@old_element.redact!(redaction)
else
# if no redaction ID was provided, then this is an unredact
# operation.
@old_element.redact!(nil)
end
# just return an empty 200 OK for success
render :nothing => true
end
private
def show_redactions?
@user and @user.moderator? and params[:show_redactions] == "true"
end
end

View file

@ -1,33 +1,12 @@
class OldNodeController < ApplicationController
require 'xml/libxml'
class OldNodeController < OldController
skip_before_filter :verify_authenticity_token
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
def history
node = Node.find(params[:id])
doc = OSM::API.new.get_xml_doc
node.old_nodes.each do |old_node|
doc.root << old_node.to_xml_node
end
render :text => doc.to_s, :content_type => "text/xml"
end
private
def version
if old_node = OldNode.where(:node_id => params[:id], :version => params[:version]).first
response.last_modified = old_node.timestamp
def lookup_old_element
@old_element = OldNode.find([params[:id], params[:version]])
end
doc = OSM::API.new.get_xml_doc
doc.root << old_node.to_xml_node
render :text => doc.to_s, :content_type => "text/xml"
else
render :nothing => true, :status => :not_found
end
def lookup_old_element_versions
@elements = OldNode.where(:node_id => params[:id]).order(:version)
end
end

View file

@ -1,32 +1,12 @@
class OldRelationController < ApplicationController
require 'xml/libxml'
class OldRelationController < OldController
skip_before_filter :verify_authenticity_token
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
def history
relation = Relation.find(params[:id])
doc = OSM::API.new.get_xml_doc
relation.old_relations.each do |old_relation|
doc.root << old_relation.to_xml_node
end
render :text => doc.to_s, :content_type => "text/xml"
end
private
def version
if old_relation = OldRelation.where(:relation_id => params[:id], :version => params[:version]).first
response.last_modified = old_relation.timestamp
def lookup_old_element
@old_element = OldRelation.find([params[:id], params[:version]])
end
doc = OSM::API.new.get_xml_doc
doc.root << old_relation.to_xml_node
render :text => doc.to_s, :content_type => "text/xml"
else
render :nothing => true, :status => :not_found
end
def lookup_old_element_versions
@elements = OldRelation.where(:relation_id => params[:id]).order(:version)
end
end

View file

@ -1,33 +1,12 @@
class OldWayController < ApplicationController
require 'xml/libxml'
class OldWayController < OldController
skip_before_filter :verify_authenticity_token
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
def history
way = Way.find(params[:id])
doc = OSM::API.new.get_xml_doc
way.old_ways.each do |old_way|
doc.root << old_way.to_xml_node
end
render :text => doc.to_s, :content_type => "text/xml"
end
private
def version
if old_way = OldWay.where(:way_id => params[:id], :version => params[:version]).first
response.last_modified = old_way.timestamp
def lookup_old_element
@old_element = OldWay.find([params[:id], params[:version]])
end
doc = OSM::API.new.get_xml_doc
doc.root << old_way.to_xml_node
render :text => doc.to_s, :content_type => "text/xml"
else
render :nothing => true, :status => :not_found
end
def lookup_old_element_versions
@elements = OldWay.where(:way_id => params[:id]).order(:version)
end
end

View file

@ -0,0 +1,76 @@
class RedactionsController < ApplicationController
layout 'site'
before_filter :authorize_web
before_filter :set_locale
before_filter :require_user, :only => [:new, :create, :edit, :update, :destroy]
before_filter :require_moderator, :only => [:new, :create, :edit, :update, :destroy]
before_filter :lookup_redaction, :only => [:show, :edit, :update, :destroy]
before_filter :check_database_readable
before_filter :check_database_writable, :only => [:create, :update, :destroy]
def index
@redactions_pages, @redactions = paginate(:redactions, :order => :id, :per_page => 10)
end
def new
@redaction = Redaction.new
end
def create
@redaction = Redaction.new
@redaction.user = @user
@redaction.title = params[:redaction][:title]
@redaction.description = params[:redaction][:description]
# note that the description format will default to 'markdown'
if @redaction.save
flash[:notice] = t('redaction.create.flash')
redirect_to @redaction
else
render :action => 'new'
end
end
def show
end
def edit
end
def update
# note - don't update the user ID
@redaction.title = params[:redaction][:title]
@redaction.description = params[:redaction][:description]
if @redaction.save
flash[:notice] = t('redaction.update.flash')
redirect_to @redaction
else
render :action => 'edit'
end
end
def destroy
unless @redaction.old_nodes.empty? and
@redaction.old_ways.empty? and
@redaction.old_relations.empty?
flash[:error] = t('redaction.destroy.not_empty')
redirect_to @redaction
else
if @redaction.destroy
flash[:notice] = t('redaction.destroy.flash')
redirect_to :redactions
else
flash[:error] = t('redaction.destroy.error')
redirect_to @redaction
end
end
end
private
def lookup_redaction
@redaction = Redaction.find(params[:id])
end
end

View file

@ -85,4 +85,8 @@ class SiteController < ApplicationController
def copyright
@locale = params[:copyright_locale] || I18n.locale
end
def preview
render :text => RichText.new(params[:format], params[:text]).to_html
end
end

View file

@ -30,9 +30,7 @@ class TraceController < ApplicationController
if !display_name.blank?
target_user = User.active.where(:display_name => display_name).first
if target_user.nil?
@title = t'trace.no_such_user.title'
@not_found_user = display_name
render :action => 'no_such_user', :status => :not_found
render_unknown_user display_name
return
end
end

View file

@ -4,7 +4,7 @@ class UserBlocksController < ApplicationController
before_filter :authorize_web
before_filter :set_locale
before_filter :require_user, :only => [:new, :create, :edit, :update, :revoke]
before_filter :require_moderator, :only => [:create, :update, :revoke]
before_filter :require_moderator, :only => [:new, :create, :edit, :update, :revoke]
before_filter :lookup_this_user, :only => [:new, :create, :blocks_on, :blocks_by]
before_filter :lookup_user_block, :only => [:show, :edit, :update, :revoke]
before_filter :require_valid_params, :only => [:create, :update]
@ -34,46 +34,43 @@ class UserBlocksController < ApplicationController
end
def create
unless @valid_params
redirect_to :action => "new"
return
end
@user_block = UserBlock.new({
:user_id => @this_user.id,
:creator_id => @user.id,
:reason => params[:user_block][:reason],
:ends_at => Time.now.getutc() + @block_period.hours,
:needs_view => params[:user_block][:needs_view]
}, :without_protection => true)
if @valid_params
@user_block = UserBlock.new({
:user_id => @this_user.id,
:creator_id => @user.id,
:reason => params[:user_block][:reason],
:ends_at => Time.now.getutc() + @block_period.hours,
:needs_view => params[:user_block][:needs_view]
}, :without_protection => true)
if @user_block.save
flash[:notice] = t('user_block.create.flash', :name => @this_user.display_name)
redirect_to @user_block
if @user_block.save
flash[:notice] = t('user_block.create.flash', :name => @this_user.display_name)
redirect_to @user_block
else
render :action => "new"
end
else
render :action => "new"
redirect_to new_user_block_path(:display_name => params[:display_name])
end
end
def update
unless @valid_params
redirect_to :action => "edit"
return
end
if @user_block.creator_id != @user.id
flash[:error] = t('user_block.update.only_creator_can_edit')
redirect_to :action => "edit"
return
end
if @user_block.update_attributes({ :ends_at => Time.now.getutc() + @block_period.hours,
:reason => params[:user_block][:reason],
:needs_view => params[:user_block][:needs_view] }, :without_protection => true)
flash[:notice] = t('user_block.update.success')
redirect_to(@user_block)
if @valid_params
if @user_block.creator_id != @user.id
flash[:error] = t('user_block.update.only_creator_can_edit')
redirect_to :action => "edit"
elsif @user_block.update_attributes({
:ends_at => Time.now.getutc() + @block_period.hours,
:reason => params[:user_block][:reason],
:needs_view => params[:user_block][:needs_view]
}, :without_protection => true)
flash[:notice] = t('user_block.update.success')
redirect_to(@user_block)
else
render :action => "edit"
end
else
render :action => "edit"
redirect_to edit_user_block_path(:id => params[:id])
end
end
@ -109,24 +106,6 @@ class UserBlocksController < ApplicationController
end
private
##
# require that the user is a moderator, or fill out a helpful error message
# and return them to the blocks index.
def require_moderator
unless @user.moderator?
flash[:error] = t('user_block.filter.not_a_moderator')
redirect_to :action => 'index'
end
end
##
# ensure that there is a "this_user" instance variable
def lookup_this_user
@this_user = User.find_by_display_name(params[:display_name])
rescue ActiveRecord::RecordNotFound
redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user
end
##
# ensure that there is a "user_block" instance variable
def lookup_user_block

View file

@ -7,8 +7,8 @@ class UserController < ApplicationController
before_filter :authorize_web, :except => [:api_details, :api_gpx_files]
before_filter :set_locale, :except => [:api_details, :api_gpx_files]
before_filter :require_user, :only => [:account, :go_public, :make_friend, :remove_friend]
before_filter :check_database_readable, :except => [:api_details, :api_gpx_files]
before_filter :check_database_writable, :only => [:login, :new, :account, :go_public, :make_friend, :remove_friend]
before_filter :check_database_readable, :except => [:login, :api_details, :api_gpx_files]
before_filter :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend]
before_filter :check_api_readable, :only => [:api_details, :api_gpx_files]
before_filter :require_allow_read_prefs, :only => [:api_details]
before_filter :require_allow_read_gpx, :only => [:api_gpx_files]
@ -143,51 +143,24 @@ class UserController < ApplicationController
@tokens = @user.oauth_tokens.authorized
if params[:user] and params[:user][:display_name] and params[:user][:description]
@user.display_name = params[:user][:display_name]
@user.new_email = params[:user][:new_email]
if params[:user][:pass_crypt].length > 0 or params[:user][:pass_crypt_confirmation].length > 0
@user.pass_crypt = params[:user][:pass_crypt]
@user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
end
@user.description = params[:user][:description]
@user.languages = params[:user][:languages].split(",")
case params[:image_action]
when "new" then @user.image = params[:user][:image]
when "delete" then @user.image = nil
end
@user.home_lat = params[:user][:home_lat]
@user.home_lon = params[:user][:home_lon]
if params[:user][:preferred_editor] == "default"
@user.preferred_editor = nil
else
@user.preferred_editor = params[:user][:preferred_editor]
end
@user.openid_url = nil if params[:user][:openid_url].blank?
if params[:user][:openid_url] and
params[:user][:openid_url].length > 0 and
params[:user][:openid_url] != @user.openid_url
# If the OpenID has changed, we want to check that it is a
# valid OpenID and one the user has control over before saving
# it as a password equivalent for the user.
session[:new_user] = @user
session[:new_user_settings] = params
openid_verify(params[:user][:openid_url], @user)
else
update_user(@user)
update_user(@user, params)
end
elsif using_open_id?
# The redirect from the OpenID provider reenters here
# again and we need to pass the parameters through to
# the open_id_authentication function
@user = session.delete(:new_user)
settings = session.delete(:new_user_settings)
openid_verify(nil, @user) do |user|
update_user(user)
update_user(user, settings)
end
end
end
@ -419,54 +392,60 @@ class UserController < ApplicationController
(@this_user.visible? or (@user and @user.administrator?))
@title = @this_user.display_name
else
@title = t 'user.no_such_user.title'
@not_found_user = params[:display_name]
render :action => 'no_such_user', :status => :not_found
render_unknown_user params[:display_name]
end
end
def make_friend
if params[:display_name]
name = params[:display_name]
new_friend = User.active.where(:display_name => name).first
friend = Friend.new
friend.user_id = @user.id
friend.friend_user_id = new_friend.id
unless @user.is_friends_with?(new_friend)
if friend.save
flash[:notice] = t 'user.make_friend.success', :name => name
Notifier.friend_notification(friend).deliver
else
friend.add_error(t('user.make_friend.failed', :name => name))
end
else
flash[:warning] = t 'user.make_friend.already_a_friend', :name => name
end
@new_friend = User.find_by_display_name(params[:display_name])
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'user', :action => 'view'
if @new_friend
if request.post?
friend = Friend.new
friend.user_id = @user.id
friend.friend_user_id = @new_friend.id
unless @user.is_friends_with?(@new_friend)
if friend.save
flash[:notice] = t 'user.make_friend.success', :name => @new_friend.display_name
Notifier.friend_notification(friend).deliver
else
friend.add_error(t('user.make_friend.failed', :name => @new_friend.display_name))
end
else
flash[:warning] = t 'user.make_friend.already_a_friend', :name => @new_friend.display_name
end
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'user', :action => 'view'
end
end
else
render_unknown_user params[:display_name]
end
end
def remove_friend
if params[:display_name]
name = params[:display_name]
friend = User.active.where(:display_name => name).first
if @user.is_friends_with?(friend)
Friend.delete_all "user_id = #{@user.id} AND friend_user_id = #{friend.id}"
flash[:notice] = t 'user.remove_friend.success', :name => friend.display_name
else
flash[:error] = t 'user.remove_friend.not_a_friend', :name => friend.display_name
end
@friend = User.find_by_display_name(params[:display_name])
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'user', :action => 'view'
if @friend
if request.post?
if @user.is_friends_with?(@friend)
Friend.delete_all "user_id = #{@user.id} AND friend_user_id = #{@friend.id}"
flash[:notice] = t 'user.remove_friend.success', :name => @friend.display_name
else
flash[:error] = t 'user.remove_friend.not_a_friend', :name => @friend.display_name
end
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'user', :action => 'view'
end
end
else
render_unknown_user params[:display_name]
end
end
@ -546,7 +525,7 @@ private
if user = User.find_by_openid_url(identity_url)
case user.status
when "pending" then
failed_login t('user.login.account not active')
failed_login t('user.login.account not active', :reconfirm => url_for(:action => 'confirm_resend', :display_name => user.display_name))
when "active", "confirmed" then
successful_login(user)
when "suspended" then
@ -563,8 +542,8 @@ private
# to the create account page with username and email filled
# in if they have been given by the OpenID provider through
# the simple registration protocol.
nickname = sreg["nickname"] || ax["http://axschema.org/namePerson/friendly"]
email = sreg["email"] || ax["http://axschema.org/contact/email"]
nickname = sreg["nickname"] || ax["http://axschema.org/namePerson/friendly"].first
email = sreg["email"] || ax["http://axschema.org/contact/email"].first
redirect_to :controller => 'user', :action => 'new', :nickname => nickname, :email => email, :openid => identity_url
end
elsif result.missing?
@ -660,7 +639,38 @@ private
##
# update a user's details
def update_user(user)
def update_user(user, params)
user.display_name = params[:user][:display_name]
user.new_email = params[:user][:new_email]
if params[:user][:pass_crypt].length > 0 or params[:user][:pass_crypt_confirmation].length > 0
user.pass_crypt = params[:user][:pass_crypt]
user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
end
if params[:user][:description] != user.description
user.description = params[:user][:description]
user.description_format = "markdown"
end
user.languages = params[:user][:languages].split(",")
case params[:image_action]
when "new" then user.image = params[:user][:image]
when "delete" then user.image = nil
end
user.home_lat = params[:user][:home_lat]
user.home_lon = params[:user][:home_lon]
if params[:user][:preferred_editor] == "default"
user.preferred_editor = nil
else
user.preferred_editor = params[:user][:preferred_editor]
end
user.openid_url = nil if params[:user][:openid_url].blank?
if user.save
set_locale

View file

@ -8,7 +8,6 @@ class UserRolesController < ApplicationController
before_filter :require_valid_role
before_filter :not_in_role, :only => [:grant]
before_filter :in_role, :only => [:revoke]
around_filter :setup_nonce
def grant
@this_user.roles.create({
@ -23,6 +22,9 @@ class UserRolesController < ApplicationController
end
private
##
# require that the user is an administrator, or fill out a helpful error message
# and return them to theuser page.
def require_administrator
unless @user.administrator?
flash[:error] = t'user_role.filter.not_an_administrator'
@ -30,30 +32,6 @@ class UserRolesController < ApplicationController
end
end
##
# ensure that there is a "this_user" instance variable
def lookup_this_user
@this_user = User.find_by_display_name(params[:display_name])
rescue ActiveRecord::RecordNotFound
redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user
end
##
# the random nonce here which isn't predictable, making an CSRF
# procedure much, much more difficult. setup the nonce. if the given
# nonce matches the session nonce then yield into the actual method.
# otherwise, just sets up the nonce for the form.
def setup_nonce
if params[:nonce] and params[:nonce] == session[:nonce]
@nonce = params[:nonce]
yield
else
@nonce = OAuth::Helper.generate_nonce
session[:nonce] = @nonce
render
end
end
##
# require that the given role is valid. the role is a URL
# parameter, so should always be present.

View file

@ -1,2 +0,0 @@
module AmfHelper
end

View file

@ -1,2 +0,0 @@
module ApiHelper
end

View file

@ -1,14 +1,6 @@
module ApplicationHelper
require 'rexml/document'
def sanitize(text)
Sanitize.clean(text, Sanitize::Config::OSM).html_safe
end
def htmlize(text)
return linkify(sanitize(simple_format(text)))
end
def linkify(text)
if text.html_safe?
Rinku.auto_link(text, :urls, tag_options(:rel => "nofollow")).html_safe
@ -83,38 +75,6 @@ module ApplicationHelper
content_tag(tag, capture(&block), :class => "hide_unless_administrator")
end
def describe_location(lat, lon, zoom = nil, language = nil)
zoom = zoom || 14
language = language || request.user_preferred_languages.join(',')
url = "http://nominatim.openstreetmap.org/reverse?lat=#{lat}&lon=#{lon}&zoom=#{zoom}&accept-language=#{language}"
begin
response = OSM::Timer.timeout(4) do
REXML::Document.new(Net::HTTP.get(URI.parse(url)))
end
rescue Exception
response = nil
end
if response and result = response.get_text("reversegeocode/result")
result.to_s
else
"#{number_with_precision(lat, :precision => 3)}, #{number_with_precision(lon, :precision => 3)}"
end
end
def user_image(user, options = {})
options[:class] ||= "user_image"
image_tag user.image.url(:large), options
end
def user_thumbnail(user, options = {})
options[:class] ||= "user_thumbnail"
image_tag user.image.url(:small), options
end
def preferred_editor
if params[:editor]
params[:editor]
@ -125,6 +85,28 @@ module ApplicationHelper
end
end
def scale_to_zoom(scale)
Math.log(360.0 / (scale.to_f * 512.0)) / Math.log(2.0)
end
def richtext_area(object_name, method, options = {})
id = "#{object_name.to_s}_#{method.to_s}"
format = options.delete(:format) || "markdown"
content_tag(:div, :id => "#{id}_container", :class => "richtext_container") do
output_buffer << content_tag(:div, :id => "#{id}_content", :class => "richtext_content") do
output_buffer << text_area(object_name, method, options.merge("data-preview-url" => preview_url(:format => format)))
output_buffer << content_tag(:div, "", :id => "#{id}_preview", :class => "richtext_preview")
end
output_buffer << content_tag(:div, :id => "#{id}_help", :class => "richtext_help") do
output_buffer << render("site/#{format}_help")
output_buffer << submit_tag(I18n.t("site.richtext_area.edit"), :id => "#{id}_doedit", :class => "richtext_doedit", :disabled => true)
output_buffer << submit_tag(I18n.t("site.richtext_area.preview"), :id => "#{id}_dopreview", :class => "richtext_dopreview")
end
end
end
def friendly_date(date)
content_tag(:span, time_ago_in_words(date), :title => l(date, :format => :friendly))
end

View file

@ -13,20 +13,34 @@ module BrowseHelper
if version
name = t 'printable_name.with_version', :id => name, :version => object.version.to_s
end
if object.tags.include? "name:#{I18n.locale}"
name = t 'printable_name.with_name', :name => object.tags["name:#{I18n.locale}"].to_s, :id => name
elsif object.tags.include? 'name'
name = t 'printable_name.with_name', :name => object.tags['name'].to_s, :id => name
# don't look at object tags if redacted, so as to avoid giving
# away redacted version tag information.
unless object.redacted?
if object.tags.include? "name:#{I18n.locale}"
name = t 'printable_name.with_name', :name => object.tags["name:#{I18n.locale}"].to_s, :id => name
elsif object.tags.include? 'name'
name = t 'printable_name.with_name', :name => object.tags['name'].to_s, :id => name
end
end
return name
end
def link_class(type, object)
return type + " " + h(icon_tags(object).join(' ')) + (object.visible == false ? ' deleted' : '')
if object.redacted?
type + " deleted"
else
type + " " + h(icon_tags(object).join(' ')) + (object.visible == false ? ' deleted' : '')
end
end
def link_title(object)
return h(icon_tags(object).map { |k,v| k + '=' + v }.to_sentence)
if object.redacted?
""
else
h(icon_tags(object).map { |k,v| k + '=' + v }.to_sentence)
end
end
def format_key(key)

View file

@ -1,2 +0,0 @@
module DiaryEntryHelper
end

View file

@ -1,2 +0,0 @@
module ExportHelper
end

View file

@ -1,25 +1,43 @@
module GeocoderHelper
def result_to_html(result)
html_options = {}
#html_options[:title] = strip_tags(result[:description]) if result[:description]
html_options = { :class => "set_position", :data => {} }
if result[:min_lon] and result[:min_lat] and result[:max_lon] and result[:max_lat]
html_options[:href] = raw("?minlon=#{result[:min_lon]}&minlat=#{result[:min_lat]}&maxlon=#{result[:max_lon]}&maxlat=#{result[:max_lat]}")
url = "?minlon=#{result[:min_lon]}&minlat=#{result[:min_lat]}&maxlon=#{result[:max_lon]}&maxlat=#{result[:max_lat]}"
else
html_options[:href] = raw("?mlat=#{result[:lat]}&mlon=#{result[:lon]}&zoom=#{result[:zoom]}")
url = "?mlat=#{result[:lat]}&mlon=#{result[:lon]}&zoom=#{result[:zoom]}"
end
result.each do |key,value|
html_options[:data][key.to_s.tr('_', '-')] = value
end
html = ""
html << result[:prefix] if result[:prefix]
html << " " if result[:prefix] and result[:name]
if result[:min_lon] and result[:min_lat] and result[:max_lon] and result[:max_lat]
html << link_to_function(result[:name],"setPosition(#{result[:lat]}, #{result[:lon]}, null, #{result[:min_lon]}, #{result[:min_lat]}, #{result[:max_lon]}, #{result[:max_lat]})", html_options) if result[:name]
else
html << link_to_function(result[:name],"setPosition(#{result[:lat]}, #{result[:lon]}, #{result[:zoom]})", html_options) if result[:name]
end
html << link_to(result[:name], url, html_options) if result[:name]
html << result[:suffix] if result[:suffix]
return raw(html)
end
def describe_location(lat, lon, zoom = nil, language = nil)
zoom = zoom || 14
language = language || request.user_preferred_languages.join(',')
url = "http://nominatim.openstreetmap.org/reverse?lat=#{lat}&lon=#{lon}&zoom=#{zoom}&accept-language=#{language}"
begin
response = OSM::Timer.timeout(4) do
REXML::Document.new(Net::HTTP.get(URI.parse(url)))
end
rescue Exception
response = nil
end
if response and result = response.get_text("reversegeocode/result")
result.to_s
else
"#{number_with_precision(lat, :precision => 3)}, #{number_with_precision(lon, :precision => 3)}"
end
end
end

View file

@ -1,2 +0,0 @@
module NodeHelper
end

View file

@ -1,2 +0,0 @@
module OauthClientsHelper
end

View file

@ -1,2 +0,0 @@
module OauthHelper
end

View file

@ -1,2 +0,0 @@
module OldNodeHelper
end

View file

@ -1,2 +0,0 @@
module OldWayHelper
end

View file

@ -1,2 +0,0 @@
module OldWayNodeHelper
end

View file

@ -1,2 +0,0 @@
module OldWayTagHelper
end

View file

@ -1,2 +0,0 @@
module SiteHelper
end

View file

@ -1,2 +0,0 @@
module SwfHelper
end

View file

@ -1,2 +0,0 @@
module TracePointsHelper
end

View file

@ -1,2 +0,0 @@
module TracepointHelper
end

View file

@ -1,2 +0,0 @@
module TracetagHelper
end

View file

@ -1,12 +1,25 @@
module UserHelper
def user_image(user, options = {})
options[:class] ||= "user_image"
image_tag user.image.url(:large), options
end
def user_thumbnail(user, options = {})
options[:class] ||= "user_thumbnail"
image_tag user.image.url(:small), options
end
def openid_logo
image_tag "openid_small.png", :alt => t('user.login.openid_logo_alt'), :class => "openid_logo"
end
def openid_button(name, url)
link_to_function(
link_to(
image_tag("#{name}.png", :alt => t("user.login.openid_providers.#{name}.alt")),
"submitOpenidUrl('#{url}')",
"#",
:class => "openid_button", :data => { :url => url },
:title => t("user.login.openid_providers.#{name}.title")
)
end

View file

@ -1,2 +0,0 @@
module UserPreferenceHelper
end

View file

@ -0,0 +1,37 @@
module UserRolesHelper
def role_icons(user)
UserRole::ALL_ROLES.reduce("".html_safe) { |s,r| s + " " + role_icon(user, r) }
end
def role_icon(user, role)
if @user and @user.administrator?
if user.has_role?(role)
image = "roles/#{role}.png"
alt = t("user.view.role.revoke.#{role}")
title = t("user.view.role.revoke.#{role}")
url = revoke_role_path(:display_name => user.display_name, :role => role)
confirm = t("user_role.revoke.are_you_sure", :name => user.display_name, :role => role)
else
image = "roles/blank_#{role}.png"
alt = t("user.view.role.grant.#{role}")
title = t("user.view.role.grant.#{role}")
url = grant_role_path(:display_name => user.display_name, :role => role)
confirm = t("user_role.grant.are_you_sure", :name => user.display_name, :role => role)
end
elsif user.has_role?(role)
image = "roles/#{role}.png"
alt = t("user.view.role.#{role}")
title = t("user.view.role.#{role}")
end
if image
icon = image_tag(image, :size => "20x20", :border => 0, :alt => alt, :title => title)
if url
icon = link_to(icon, url, :method => :post, :confirm => confirm)
end
end
icon
end
end

View file

@ -1,2 +0,0 @@
module WayHelper
end

View file

@ -1,2 +0,0 @@
module WayNodeHelper
end

View file

@ -1,2 +0,0 @@
module WayTagHelper
end

View file

@ -1,7 +1,7 @@
class Changeset < ActiveRecord::Base
require 'xml/libxml'
belongs_to :user
belongs_to :user, :counter_cache => true
has_many :changeset_tags

View file

@ -2,7 +2,7 @@ require 'oauth'
class ClientApplication < ActiveRecord::Base
belongs_to :user
has_many :tokens, :class_name => "OauthToken"
has_many :tokens, :class_name => "OauthToken", :dependent => :delete_all
has_many :access_tokens
has_many :oauth2_verifiers
has_many :oauth_tokens

View file

@ -7,6 +7,12 @@ class DiaryComment < ActiveRecord::Base
attr_accessible :body
after_initialize :set_defaults
def body
RichText.new(read_attribute(:body_format), read_attribute(:body))
end
def digest
md5 = Digest::MD5.new
md5 << diary_entry_id.to_s
@ -15,4 +21,10 @@ class DiaryComment < ActiveRecord::Base
md5 << body
md5.hexdigest
end
private
def set_defaults
self.body_format = "markdown" unless self.attribute_present?(:body_format)
end
end

View file

@ -25,4 +25,16 @@ class DiaryEntry < ActiveRecord::Base
validates_associated :language
attr_accessible :title, :body, :language_code, :latitude, :longitude
after_initialize :set_defaults
def body
RichText.new(read_attribute(:body_format), read_attribute(:body))
end
private
def set_defaults
self.body_format = "markdown" unless self.attribute_present?(:body_format)
end
end

View file

@ -11,6 +11,35 @@ class Message < ActiveRecord::Base
attr_accessible :title, :body
after_initialize :set_defaults
def self.from_mail(mail, from, to)
if mail.multipart?
if mail.text_part
body = mail.text_part.decoded
elsif mail.html_part
body = HTMLEntities.new.decode(Sanitize.clean(mail.html_part.decoded))
end
elsif mail.text? and mail.sub_type == "html"
body = HTMLEntities.new.decode(Sanitize.clean(mail.decoded))
else
body = mail.decoded
end
message = Message.new({
:sender => from,
:recipient => to,
:sent_on => mail.date.new_offset(0),
:title => mail.subject.sub(/\[OpenStreetMap\] */, ""),
:body => body,
:body_format => "text"
}, :without_protection => true)
end
def body
RichText.new(read_attribute(:body_format), read_attribute(:body))
end
def digest
md5 = Digest::MD5.new
md5 << from_user_id.to_s
@ -20,4 +49,10 @@ class Message < ActiveRecord::Base
md5 << body
md5.hexdigest
end
private
def set_defaults
self.body_format = "markdown" unless self.attribute_present?(:body_format)
end
end

View file

@ -3,6 +3,7 @@ class Node < ActiveRecord::Base
include GeoRecord
include ConsistencyValidations
include NotRedactable
self.table_name = "current_nodes"
@ -200,11 +201,14 @@ class Node < ActiveRecord::Base
def to_xml_node(changeset_cache = {}, user_display_name_cache = {})
el1 = XML::Node.new 'node'
el1['id'] = self.id.to_s
el1['lat'] = self.lat.to_s
el1['lon'] = self.lon.to_s
el1['version'] = self.version.to_s
el1['changeset'] = self.changeset_id.to_s
if self.visible?
el1['lat'] = self.lat.to_s
el1['lon'] = self.lon.to_s
end
if changeset_cache.key?(self.changeset_id)
# use the cache if available
else

View file

@ -14,7 +14,9 @@ class OauthToken < ActiveRecord::Base
end
def invalidate!
update_attribute(:invalidated_at, Time.now)
update_attributes({
:invalidated_at => Time.now
}, :without_protection => true)
end
def authorized?

View file

@ -5,6 +5,10 @@ class OldNode < ActiveRecord::Base
self.table_name = "nodes"
self.primary_keys = "node_id", "version"
# note this needs to be included after the table name changes, or
# the queries generated by Redactable will use the wrong table name.
include Redactable
validates_presence_of :changeset_id, :timestamp
validates_inclusion_of :visible, :in => [ true, false ]
validates_numericality_of :latitude, :longitude
@ -12,7 +16,9 @@ class OldNode < ActiveRecord::Base
validates_associated :changeset
belongs_to :changeset
belongs_to :redaction
belongs_to :current_node, :class_name => "Node", :foreign_key => "node_id"
def validate_position
errors.add(:base, "Node is not in the world") unless in_world?
end
@ -39,13 +45,6 @@ class OldNode < ActiveRecord::Base
def to_xml_node
el1 = XML::Node.new 'node'
el1['id'] = self.node_id.to_s
el1['lat'] = self.lat.to_s
el1['lon'] = self.lon.to_s
el1['changeset'] = self.changeset.id.to_s
if self.changeset.user.data_public?
el1['user'] = self.changeset.user.display_name
el1['uid'] = self.changeset.user.id.to_s
end
self.tags.each do |k,v|
el2 = XML::Node.new('tag')
@ -54,9 +53,23 @@ class OldNode < ActiveRecord::Base
el1 << el2
end
if self.visible?
el1['lat'] = self.lat.to_s
el1['lon'] = self.lon.to_s
end
el1['changeset'] = self.changeset.id.to_s
if self.changeset.user.data_public?
el1['user'] = self.changeset.user.display_name
el1['uid'] = self.changeset.user.id.to_s
end
el1['visible'] = self.visible.to_s
el1['timestamp'] = self.timestamp.xmlschema
el1['version'] = self.version.to_s
el1['redacted'] = self.redaction.id.to_s if self.redacted?
return el1
end
@ -106,4 +119,10 @@ class OldNode < ActiveRecord::Base
def containing_relation_members
return []
end
# check whether this element is the latest version - that is,
# has the same version as its "current" counterpart.
def is_latest_version?
current_node.version == self.version
end
end

View file

@ -4,7 +4,13 @@ class OldRelation < ActiveRecord::Base
self.table_name = "relations"
self.primary_keys = "relation_id", "version"
# note this needs to be included after the table name changes, or
# the queries generated by Redactable will use the wrong table name.
include Redactable
belongs_to :changeset
belongs_to :redaction
belongs_to :current_relation, :class_name => "Relation", :foreign_key => "relation_id"
has_many :old_members, :class_name => 'OldRelationMember', :foreign_key => [:relation_id, :version], :order => :sequence_id
has_many :old_tags, :class_name => 'OldRelationTag', :foreign_key => [:relation_id, :version]
@ -99,6 +105,8 @@ class OldRelation < ActiveRecord::Base
el1['version'] = self.version.to_s
el1['changeset'] = self.changeset_id.to_s
el1['redacted'] = self.redaction.id.to_s if self.redacted?
self.old_members.each do |member|
e = XML::Node.new 'member'
e['type'] = member.member_type.to_s.downcase
@ -106,13 +114,14 @@ class OldRelation < ActiveRecord::Base
e['role'] = member.member_role.to_s
el1 << e
end
self.old_tags.each do |tag|
e = XML::Node.new 'tag'
e['k'] = tag.k
e['v'] = tag.v
el1 << e
end
return el1
end
@ -130,4 +139,10 @@ class OldRelation < ActiveRecord::Base
def containing_relation_members
return []
end
# check whether this element is the latest version - that is,
# has the same version as its "current" counterpart.
def is_latest_version?
current_relation.version == self.version
end
end

View file

@ -1,10 +1,16 @@
class OldWay < ActiveRecord::Base
include ConsistencyValidations
self.table_name = "ways"
self.primary_keys = "way_id", "version"
# note this needs to be included after the table name changes, or
# the queries generated by Redactable will use the wrong table name.
include Redactable
belongs_to :changeset
belongs_to :redaction
belongs_to :current_way, :class_name => "Way", :foreign_key => "way_id"
has_many :old_nodes, :class_name => 'OldWayNode', :foreign_key => [:way_id, :version]
has_many :old_tags, :class_name => 'OldWayTag', :foreign_key => [:way_id, :version]
@ -97,19 +103,22 @@ class OldWay < ActiveRecord::Base
end
el1['version'] = self.version.to_s
el1['changeset'] = self.changeset.id.to_s
el1['redacted'] = self.redaction.id.to_s if self.redacted?
self.old_nodes.each do |nd| # FIXME need to make sure they come back in the right order
e = XML::Node.new 'nd'
e['ref'] = nd.node_id.to_s
el1 << e
end
self.old_tags.each do |tag|
e = XML::Node.new 'tag'
e['k'] = tag.k
e['v'] = tag.v
el1 << e
end
return el1
end
@ -132,7 +141,7 @@ class OldWay < ActiveRecord::Base
def get_nodes_revert(timestamp)
points=[]
self.nds.each do |n|
oldnode = OldNode.where('node_id = ? AND timestamp <= ?', n, timestamp).order("timestamp DESC").first
oldnode = OldNode.where('node_id = ? AND timestamp <= ?', n, timestamp).unredacted.order("timestamp DESC").first
curnode = Node.find(n)
id = n; reuse = curnode.visible
if oldnode.lat != curnode.lat or oldnode.lon != curnode.lon or oldnode.tags != curnode.tags then
@ -158,4 +167,10 @@ class OldWay < ActiveRecord::Base
def containing_relation_members
return []
end
# check whether this element is the latest version - that is,
# has the same version as its "current" counterpart.
def is_latest_version?
current_way.version == self.version
end
end

32
app/models/redaction.rb Normal file
View file

@ -0,0 +1,32 @@
##
# Redaction represents a record associated with a particular
# action on the database to hide revisions from the history
# which are not appropriate to redistribute any more.
#
# The circumstances of the redaction can be recorded in the
# record's title and description fields, which can be
# displayed linked from the redacted records.
#
class Redaction < ActiveRecord::Base
belongs_to :user
has_many :old_nodes
has_many :old_ways
has_many :old_relations
after_initialize :set_defaults
# this method overrides the AR default to provide the rich
# text object for the description field.
def description
RichText.new(read_attribute(:description_format), read_attribute(:description))
end
private
# set the default format to be markdown, in the absence of
# any other setting.
def set_defaults
self.description_format = "markdown" unless self.attribute_present?(:description_format)
end
end

View file

@ -2,7 +2,8 @@ class Relation < ActiveRecord::Base
require 'xml/libxml'
include ConsistencyValidations
include NotRedactable
self.table_name = "current_relations"
belongs_to :changeset

View file

@ -1,7 +1,7 @@
class Trace < ActiveRecord::Base
self.table_name = "gpx_files"
belongs_to :user
belongs_to :user, :counter_cache => true
has_many :tags, :class_name => 'Tracetag', :foreign_key => 'gpx_id', :dependent => :delete_all
has_many :points, :class_name => 'Tracepoint', :foreign_key => 'gpx_id', :dependent => :delete_all

View file

@ -18,7 +18,10 @@ class User < ActiveRecord::Base
has_many :client_applications
has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application]
has_many :active_blocks, :class_name => "UserBlock", :conditions => proc { [ "user_blocks.ends_at > :ends_at or user_blocks.needs_view", { :ends_at => Time.now.getutc } ] }
has_many :blocks, :class_name => "UserBlock"
has_many :blocks_created, :class_name => "UserBlock", :foreign_key => :creator_id
has_many :blocks_revoked, :class_name => "UserBlock", :foreign_key => :revoker_id
has_many :roles, :class_name => "UserRole"
scope :visible, where(:status => ["pending", "active", "confirmed"])
@ -35,7 +38,7 @@ class User < ActiveRecord::Base
validates_length_of :display_name, :within => 3..255, :allow_nil => true
validates_email_format_of :email, :if => Proc.new { |u| u.email_changed? }
validates_email_format_of :new_email, :allow_blank => true, :if => Proc.new { |u| u.new_email_changed? }
validates_format_of :display_name, :with => /^[^\/;.,?]*$/, :if => Proc.new { |u| u.display_name_changed? }
validates_format_of :display_name, :with => /^[^\/;.,?%#]*$/, :if => Proc.new { |u| u.display_name_changed? }
validates_format_of :display_name, :with => /^\S/, :message => "has leading whitespace", :if => Proc.new { |u| u.display_name_changed? }
validates_format_of :display_name, :with => /\S$/, :message => "has trailing whitespace", :if => Proc.new { |u| u.display_name_changed? }
validates_numericality_of :home_lat, :allow_nil => true
@ -46,7 +49,7 @@ class User < ActiveRecord::Base
attr_accessible :display_name, :email, :email_confirmation, :openid_url,
:pass_crypt, :pass_crypt_confirmation, :consider_pd
after_initialize :set_creation_time
after_initialize :set_defaults
before_save :encrypt_password
has_attached_file :image,
@ -78,7 +81,7 @@ class User < ActiveRecord::Base
user = nil
end
token.update_attribute(:expiry, 1.week.from_now) if token and user
token.update_column(:expiry, 1.week.from_now) if token and user
return user
end
@ -103,6 +106,10 @@ class User < ActiveRecord::Base
return el1
end
def description
RichText.new(read_attribute(:description_format), read_attribute(:description))
end
def languages
attribute_present?(:languages) ? read_attribute(:languages).split(/ *, */) : []
end
@ -180,7 +187,7 @@ class User < ActiveRecord::Base
# returns the first active block which would require users to view
# a message, or nil if there are none.
def blocked_on_view
active_blocks.detect { |b| b.needs_view? }
blocks.active.detect { |b| b.needs_view? }
end
##
@ -202,10 +209,10 @@ class User < ActiveRecord::Base
def spam_score
changeset_score = self.changesets.limit(10).length * 50
trace_score = self.traces.limit(10).length * 50
diary_entry_score = self.diary_entries.inject(0) { |s,e| s += OSM.spam_score(e.body) }
diary_comment_score = self.diary_comments.inject(0) { |s,e| s += OSM.spam_score(e.body) }
diary_entry_score = self.diary_entries.inject(0) { |s,e| s += e.body.spam_score }
diary_comment_score = self.diary_comments.inject(0) { |s,c| s += c.body.spam_score }
score = OSM.spam_score(self.description)
score = self.description.spam_score / 4.0
score += diary_entry_score / self.diary_entries.length if self.diary_entries.length > 0
score += diary_comment_score / self.diary_comments.length if self.diary_comments.length > 0
score -= changeset_score
@ -222,14 +229,16 @@ class User < ActiveRecord::Base
private
def set_creation_time
def set_defaults
self.creation_time = Time.now.getutc unless self.attribute_present?(:creation_time)
self.description_format = "markdown" unless self.attribute_present?(:description_format)
end
def encrypt_password
if pass_crypt_confirmation
self.pass_salt = OSM::make_token(8)
self.pass_crypt = OSM::encrypt_password(pass_crypt, pass_salt)
self.pass_crypt_confirmation = nil
end
end
end

View file

@ -5,8 +5,22 @@ class UserBlock < ActiveRecord::Base
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
belongs_to :revoker, :class_name => "User", :foreign_key => :revoker_id
after_initialize :set_defaults
PERIODS = USER_BLOCK_PERIODS
##
# scope to match active blocks
def self.active
self.where("needs_view or ends_at > ?", Time.now.getutc)
end
##
# return a renderable version of the reason text.
def reason
RichText.new(read_attribute(:reason_format), read_attribute(:reason))
end
##
# returns true if the block is currently active (i.e: the user can't
# use the API).
@ -25,7 +39,14 @@ class UserBlock < ActiveRecord::Base
}, :without_protection => true)
end
private
private
##
# set default values for new records.
def set_defaults
self.reason_format = "markdown" unless self.attribute_present?(:reason_format)
end
##
# validate that only moderators are allowed to change the
# block. this should be caught and dealt with in the controller,

View file

@ -2,6 +2,7 @@ class Way < ActiveRecord::Base
require 'xml/libxml'
include ConsistencyValidations
include NotRedactable
self.table_name = "current_ways"

View file

@ -0,0 +1,9 @@
# create list of permissions
xml.instruct! :xml, :version=>"1.0"
xml.osm("version" => "#{API_VERSION}", "generator" => "OpenStreetMap Server") do
xml.permissions do
@permissions.each do |permission|
xml.permission :name => permission
end
end
end

View file

@ -15,12 +15,12 @@
<%= link_to(t("browse.map.larger.area"), { :controller => :site, :action => :index, :box => "yes" }, { :id => "area_larger_map", :class => "geolink bbox" }) %>
<% end -%>
<br />
<%= link_to(t("browse.map.edit.area"), { :controller => :site, :action => :edit }, { :id => "area_edit", :class => "geolink bbox" }) %>
<%= link_to(h(t("browse.map.edit.area")) + content_tag(:span, "▾", :class => "menuicon"), { :controller => :site, :action => :edit }, { :id => "area_edit", :class => "geolink bbox" }) %>
<% unless map.instance_of? Changeset or map.instance_of? Note %>
<br />
<%= link_to("", { :controller => :site, :action => :index }, { :id => "object_larger_map", :class => "geolink object" }) %>
<%= link_to(t("browse.map.larger." + map.class.to_s.downcase), { :controller => :site, :action => :index }, { :id => "object_larger_map", :class => "geolink object" }) %>
<br />
<%= link_to("", { :controller => :site, :action => :edit }, { :id => "object_edit", :class => "geolink object" }) %>
<%= link_to(h(t("browse.map.edit." + map.class.to_s.downcase)) + content_tag(:span, "▾", :class => "menuicon"), { :controller => :site, :action => :edit }, { :id => "object_edit", :class => "geolink object" }) %>
<% end %>
<% else %>
<%= t 'browse.map.deleted' %>
@ -83,7 +83,7 @@
var bbox = new OpenLayers.Bounds(minlon, minlat, maxlon, maxlat);
var centre = bbox.getCenterLonLat();
setMapExtent(bbox);
map.zoomToExtent(proj(bbox));
addBoxToMap(bbox);
$("#loading").hide();
@ -136,6 +136,9 @@
url += "/" + previous_version;
}
$("#object_larger_map").hide();
$("#object_edit").hide();
addObjectToMap(url, true, function(extent) {
$("#loading").hide();
$("#browse_map .geolink").show();
@ -155,7 +158,6 @@
});
<% end -%>
<% unless map.instance_of? Changeset -%>
$("#remote_object_edit").click(function (event) {
return remoteEditHandler(event, extent, "<%= map.class.to_s.downcase + map.id.to_s %>");
});
@ -166,9 +168,8 @@
});
<% end -%>
$("#object_larger_map").html("<%=j t('browse.map.larger.' + map.class.to_s.downcase) %>");
$("#object_edit").html("<%=j t('browse.map.edit.' + map.class.to_s.downcase) %>");
<% end -%>
$("#object_larger_map").show();
$("#object_edit").show();
updatelinks(centre.lon, centre.lat, 16, null, extent.left, extent.bottom, extent.right, extent.top, "<%= map.class.to_s.downcase %>", <%= map.id %>);
} else {
@ -177,8 +178,8 @@
});
<% end -%>
createMenu("area_edit", "area_edit_menu", 1000, "right");
createMenu("object_edit", "object_edit_menu", 1000, "right");
createMenu("area_edit", "area_edit_menu", "right");
createMenu("object_edit", "object_edit_menu", "right");
}
window.onload = init;

View file

@ -1,11 +1,16 @@
<% if node_details.redacted? %>
<p><%= t 'browse.redacted.message_html', :type => t('browse.redacted.type.node'), :redaction_link => link_to(t('browse.redacted.redaction', :id => node_details.redaction.id), node_details.redaction), :version => node_details.version %></p>
<% else %>
<table class="browse_details" id="<%= node_details.version %>">
<%= render :partial => "common_details", :object => node_details %>
<% if node_details.visible -%>
<tr>
<th><%= t 'browse.node_details.coordinates' %></th>
<td><div class="geo"><%= link_to(content_tag(:span, number_with_delimiter(node_details.lat), :class => "latitude") + ", " + content_tag(:span, number_with_delimiter(node_details.lon), :class => "longitude"), {:controller => 'site', :action => 'index', :lat => h(node_details.lat), :lon => h(node_details.lon), :zoom => "18"}) %></div></td>
</tr>
<% end -%>
<% unless node_details.ways.empty? and node_details.containing_relation_members.empty? %>
<tr valign="top">
@ -22,3 +27,4 @@
<% end %>
</table>
<% end %>

View file

@ -1,3 +1,6 @@
<% if relation_details.redacted? %>
<p><%= t 'browse.redacted.message_html', :type => t('browse.redacted.type.relation'), :redaction_link => link_to(t('browse.redacted.redaction', :id => relation_details.redaction.id), relation_details.redaction), :version => relation_details.version %></p>
<% else %>
<table class="browse_details" id="<%= relation_details.version %>">
<%= render :partial => "common_details", :object => relation_details %>
@ -25,3 +28,4 @@
<% end %>
</table>
<% end %>

View file

@ -1,3 +1,6 @@
<% if way_details.redacted? %>
<p><%= t 'browse.redacted.message_html', :type => t('browse.redacted.type.way'), :redaction_link => link_to(t('browse.redacted.redaction', :id => way_details.redaction.id), way_details.redaction), :version => way_details.version %></p>
<% else %>
<table class="browse_details" id="<%= way_details.version %>">
<%= render :partial => "common_details", :object => way_details %>
@ -33,3 +36,4 @@
<% end %>
</table>
<% end %>

View file

@ -8,5 +8,5 @@
<% end %>
<%= render :partial => "changeset_details", :object => @changeset %>
<hr />
<%= raw t 'browse.changeset.download', :changeset_xml_link => link_to(t('browse.changeset.changesetxml'), :controller => "changeset", :action => "read"),
:osmchange_xml_link => link_to(t('browse.changeset.osmchangexml'), :controller => "changeset", :action => "download") %>
<%= link_to(t('browse.changeset.changesetxml'), :controller => "changeset", :action => "read") %>
| <%= link_to(t('browse.changeset.osmchangexml'), :controller => "changeset", :action => "download") %>

View file

@ -7,10 +7,13 @@
<% end %>
<%= render :partial => "navigation" %>
<h2><%= t'browse.node.node_title', :node_name => @name %></h2>
<% if @node.visible -%>
<%= render :partial => "map", :object => @node %>
<% end -%>
<%= render :partial => "node_details", :object => @node %>
<hr />
<%= raw t'browse.node.download', :download_xml_link => link_to(t('browse.node.download_xml'), :controller => "old_node", :action => "version", :version => @node.version),
:view_history_link => link_to(t('browse.node.view_history'), :action => "node_history"),
:edit_link => link_to(t('browse.node.edit'), :controller => "site", :action => "edit", :lat => @node.lat, :lon => @node.lon, :zoom => 18, :node => @node.id)
%>
<%= link_to(t('browse.node.download_xml'), :controller => "node", :action => "read") %>
| <%= link_to(t('browse.node.view_history'), :action => "node_history") %>
<% if @node.visible -%>
| <%= link_to(t('browse.node.edit'), :controller => "site", :action => "edit", :lat => @node.lat, :lon => @node.lon, :zoom => 18, :node => @node.id) %>
<% end -%>

View file

@ -3,10 +3,12 @@
@title = t('browse.node_history.node_history') + ' | ' + @name
%>
<h2><%= raw t'browse.node_history.node_history_title', :node_name => link_to(h(@name), :action => "node", :id => @node.id) %></h2>
<% if @node.visible -%>
<%= render :partial => "map", :object => @node %>
<% end -%>
<% @node.old_nodes.reverse.each do |node| %>
<%= render :partial => "node_details", :object => node %>
<hr />
<% end %>
<%= raw t 'browse.node_history.download', :download_xml_link => link_to(t('browse.node_history.download_xml'), :controller => "old_node", :action => "history"),
:view_details_link => link_to(t('browse.node_history.view_details'), :action => "node") %>
<%= link_to(t('browse.node_history.download_xml'), :controller => "old_node", :action => "history") %>
| <%= link_to(t('browse.node_history.view_details'), :action => "node") %>

View file

@ -10,5 +10,5 @@
<%= render :partial => "map", :object => @relation %>
<%= render :partial => "relation_details", :object => @relation %>
<hr />
<%= raw t'browse.relation.download', :download_xml_link => link_to(t('browse.relation.download_xml'), :controller => "relation", :action => "read"),
:view_history_link => link_to(t('browse.relation.view_history'), :action => "relation_history") %>
<%= link_to(t('browse.relation.download_xml'), :controller => "relation", :action => "read") %>
| <%= link_to(t('browse.relation.view_history'), :action => "relation_history") %>

View file

@ -8,5 +8,5 @@
<%= render :partial => "relation_details", :object => relation %>
<hr />
<% end %>
<%= raw t'browse.relation_history.download', :download_xml_link => link_to(t('browse.relation_history.download_xml'), :controller => "old_relation", :action => "history"),
:view_details_link => link_to(t('browse.relation_history.view_details'), :action => "relation") %>
<%= link_to(t('browse.relation_history.download_xml'), :controller => "old_relation", :action => "history") %>
| <%= link_to(t('browse.relation_history.view_details'), :action => "relation") %>

View file

@ -32,9 +32,11 @@ function startBrowse() {
browseBoxControl.handler.callbacks.done = endDrag;
map.addControl(browseBoxControl);
map.events.register("moveend", map, showData);
map.events.register("moveend", map, updateData);
map.events.triggerEvent("moveend");
$("#browse_select_view").click(useMap);
$("#browse_select_box").click(startDrag);
$("#browse_hide_areas_box").html("<%=j t 'browse.start_rjs.hide_areas' %>");
@ -42,7 +44,7 @@ function startBrowse() {
$("#browse_hide_areas_box").click(hideAreas);
}
function showData() {
function updateData() {
if (browseMode == "auto") {
if (map.getZoom() >= 15) {
useMap(false);
@ -77,7 +79,7 @@ function stopBrowse() {
}
map.dataLayer.setVisibility(false);
map.events.unregister("moveend", map, showData);
map.events.unregister("moveend", map, updateData);
}
}
@ -133,8 +135,6 @@ function showAreas() {
useMap(true);
}
$("#browse_select_view").click(useMap);
function endDrag(bbox) {
var bounds = bbox.getBounds();
var projected = bounds.clone().transform(map.getProjectionObject(), epsg4326);
@ -149,59 +149,42 @@ function endDrag(bbox) {
$("#browse_select_view").show();
}
function displayFeatureWarning() {
function displayFeatureWarning(count, limit, callback) {
clearStatus();
var div = document.createElement("div");
var p = document.createElement("p");
p.appendChild(document.createTextNode(i18n("<%=j t 'browse.start_rjs.loaded_an_area_with_num_features' %>", { num_features: browseFeatureList.length })));
p.appendChild(document.createTextNode(i18n("<%=j t 'browse.start_rjs.loaded_an_area_with_num_features' %>", { num_features: count, max_features: limit })));
div.appendChild(p);
var input = document.createElement("input");
input.type = "submit";
input.value = "<%=j t 'browse.start_rjs.load_data' %>";
input.onclick = loadFeatureList;
input.onclick = callback;
div.appendChild(input);
$("#browse_content").html("");
$("#browse_content").append(div);
}
function loadFeatureList() {
browseDataLayer.addFeatures(browseFeatureList);
browseDataLayer.events.triggerEvent("loadend");
browseFeatureList = [];
return false;
}
function customDataLoader(request) {
if (this.map.dataLayer.active) {
function customDataLoader(resp, options) {
if (map.dataLayer.active) {
var request = resp.priv;
var doc = request.responseXML;
if (!doc || !doc.documentElement) {
doc = request.responseText;
}
var options = {};
resp.features = this.format.read(doc);
OpenLayers.Util.extend(options, this.formatOptions);
if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
options.externalProjection = this.projection;
options.internalProjection = this.map.getProjectionObject();
}
var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
browseFeatureList = gml.read(doc);
if (!this.maxFeatures || browseFeatureList.length <= this.maxFeatures) {
loadFeatureList();
if (!this.maxFeatures || resp.features.length <= this.maxFeatures) {
options.callback.call(options.scope, resp);
} else {
displayFeatureWarning();
displayFeatureWarning(resp.features.length, this.maxFeatures, function () {
options.callback.call(options.scope, resp);
});
}
}
}
@ -213,11 +196,11 @@ function getData(bounds, reload) {
if (size > <%= MAX_REQUEST_AREA %>) {
setStatus(i18n("<%=j t 'browse.start_rjs.unable_to_load_size', :max_bbox_size => MAX_REQUEST_AREA %>", { bbox_size: size }));
} else {
loadGML("/api/<%= API_VERSION %>/map?bbox=" + projected.toBBOX(), reload);
loadData("/api/<%= API_VERSION %>/map?bbox=" + projected.toBBOX(), reload);
}
}
function loadGML(url, reload) {
function loadData(url, reload) {
setStatus("<%=j t 'browse.start_rjs.loading' %>");
$("#browse_content").empty();
@ -242,11 +225,17 @@ function loadGML(url, reload) {
if (browseDataLayer) browseDataLayer.destroyFeatures();
browseDataLayer = new OpenLayers.Layer.GML("Data", url, {
format: OpenLayers.Format.OSM,
formatOptions: formatOptions,
maxFeatures: 100,
requestSuccess: customDataLoader,
browseDataLayer = new OpenLayers.Layer.Vector("Data", {
strategies: [
new OpenLayers.Strategy.Fixed()
],
protocol: new OpenLayers.Protocol.HTTP({
url: url,
format: new OpenLayers.Format.OSM(formatOptions),
maxFeatures: <%= @max_features %>,
handleRead: customDataLoader
}),
projection: new OpenLayers.Projection("EPSG:4326"),
displayInLayerSwitcher: false,
styleMap: new OpenLayers.StyleMap({
'default': style,
@ -263,8 +252,7 @@ function loadGML(url, reload) {
browseSelectControl.activate();
} else {
browseDataLayer.destroyFeatures();
browseDataLayer.format(formatOptions);
browseDataLayer.setUrl(url);
browseDataLayer.refresh({ url: url });
}
browseActiveFeature = null;
@ -307,7 +295,7 @@ function dataLoaded() {
browseObjectList.appendChild(list);
var link = document.createElement("a");
link.href = this.url;
link.href = this.protocol.url;
link.appendChild(document.createTextNode("<%=j t 'browse.start_rjs.object_list.api' %>"));
browseObjectList.appendChild(link);

View file

@ -12,6 +12,6 @@
<hr />
<%= link_to(t('browse.way.download_xml'), :controller => "way", :action => "read") %>
| <%= link_to(t('browse.way.view_history'), :action => "way_history") %>
<% unless @midnode.nil? %>
| <%= link_to(t('browse.way.edit'), :controller => "site", :action => "edit", :way => @way.id, :lat => @midnode.lat, :lon => @midnode.lon, :zoom => 16) %>
<% end %>
<% unless @midnode.nil? -%>
| <%= link_to(t('browse.way.edit'), :controller => "site", :action => "edit", :way => @way.id, :lat => @midnode.lat, :lon => @midnode.lon, :zoom => 16) %>
<% end -%>

Some files were not shown because too many files have changed in this diff Show more