diff --git a/.autotest b/.autotest
index a0d2a937..d4b0ff1b 100644
--- a/.autotest
+++ b/.autotest
@@ -2,6 +2,7 @@ require 'autotest/bundler'
Autotest.add_hook :initialize do |at|
at.add_exception '.git'
+ at.add_exception '.redcar'
at.add_exception 'spec-results/index.html'
at.add_exception 'features_report.html'
at.add_exception /capybara-\d+\.html$/
diff --git a/.gitignore b/.gitignore
index 5ace98b2..3d43a748 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@ features_report.html
config/config.yml
config/deploy.rb
config/database.yml
+.redcar
*~
diff --git a/.rspec.travis b/.rspec.travis
new file mode 100644
index 00000000..89da67b6
--- /dev/null
+++ b/.rspec.travis
@@ -0,0 +1,2 @@
+--colour
+--format progress
diff --git a/.ruby-gemset b/.ruby-gemset
new file mode 100644
index 00000000..5780bbe7
--- /dev/null
+++ b/.ruby-gemset
@@ -0,0 +1 @@
+quorum2
\ No newline at end of file
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 00000000..068e6289
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+ruby-1.9.2-p320
\ No newline at end of file
diff --git a/.rvmrc b/.rvmrc
deleted file mode 100644
index d5c28e3d..00000000
--- a/.rvmrc
+++ /dev/null
@@ -1 +0,0 @@
-rvm use 1.9.3@quorum2
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..7f7c48e6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: ruby
+rvm:
+ - 1.9.3
+bundler_args: --without development
+services: postgresql
+before_install:
+ - gem update --system 1.8.25 # since ZenTest needs ~> 1.8
+ - gem --version
+before_script: ./ci/before_script.sh
+script: rake ci
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6b18371..7ee4aee7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,4 +36,45 @@ Quorum is distributed under the [BSD 3-Clause License](http://www.opensource.org
## v0.3.8, 23 September 2011
* Replace many controller specs with Cucumber stories [#54]
-* Deal better with HTML escaping
\ No newline at end of file
+* Deal better with HTML escaping
+
+## v0.3.9, 28 December 2011
+* Fix iCal export issues: we were exporting an invalid iCalendar file [#65]
+
+## v0.4.0, 29 December 2011
+* Remove make_resourceful now that Rails offers `respond_with` [#64]
+
+## v0.5.0, 12 March 2012
+* Add the ability to make comments on one's commitment status [#69]
+
+## v0.5.1, 12 March 2012
+* Fix invalid HTML on event list [#71]
+
+## v0.5.2, 12 March 2012
+* Autolink URLs in event descriptions [#75]
+
+## v0.5.3, 13 March 2012
+* Show commitment status icons on comments [#74]
+
+## v0.5.4, 13 March 2012
+* Bugfix.
+
+## v0.5.5, 2 July 2012
+* Put event comments on separate lines [#77]
+* Add application information in footer [#79]
+
+## v0.5.6, v0.5.7, 2 July 2012
+* Bugfix.
+
+## v0.5.8, 2 July 2012
+* Put attendance icons in "attending" and "not attending" columns [#81]
+
+## v0.5.9, 30 January 2013
+* Update to Rails 3.0.20 to patch security hole
+
+## v0.5.10, 5 August 2013
+* Make some configuration changes for Passenger
+* Remove YM4R and Spatial Adapter, since neither is currently supported [#82, #86]
+
+## v0.5.11, 23 October 2013
+* Add patch for ActionMailer vulnerability at http://seclists.org/oss-sec/2013/q4/118 [#88]
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 72336540..ab73be3d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
source 'http://rubygems.org'
-gem 'rails', '3.0.10'
+gem 'rails', '3.0.20'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
@@ -26,8 +26,10 @@ gem 'sass'
gem 'pg'
gem 'gettext_i18n_rails'
gem 'prawn'
-gem 'GeoRuby'
+gem 'geocoder', '~> 1.1.8'
gem 'rdiscount'
+gem 'rgeo-activerecord', github: 'marnen/rgeo-activerecord', branch: 'fix-proc-error-in-default-factory' # TODO: waiting for https://github.com/dazuma/rgeo-activerecord/pull/10
+gem 'activerecord-postgis-adapter'
gem 'authlogic', '~> 3.0.3'
gem 'dynamic_form'
gem 'exception_notification'
@@ -40,14 +42,14 @@ gem 'exception_notification'
# end
group :development do
- gem 'capistrano'
+ gem 'rvm-capistrano'
gem 'gettext', '>= 1.9.3', :require => false
+ gem 'ruby-debug19'
end
group :test, :development do
- gem 'ruby-debug19'
gem 'autotest-rails', :require => false
- gem 'rspec-rails', '~> 2.6.1', :require => false
+ gem 'rspec-rails', '~> 2.11.0', :require => false
gem 'test-unit', '1.2.3', :require => false # amazingly, RSpec needs this
gem 'cucumber-rails', :require => false
gem 'launchy'
diff --git a/Gemfile.lock b/Gemfile.lock
index 6af0d102..5b600715 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,35 +1,46 @@
+GIT
+ remote: git://github.com/marnen/rgeo-activerecord.git
+ revision: 26bd2daa1650b35643a3f0aef9ae82186bf1556b
+ branch: fix-proc-error-in-default-factory
+ specs:
+ rgeo-activerecord (0.5.0)
+ activerecord (>= 3.0.3)
+ arel (>= 2.0.6)
+ rgeo (>= 0.3.20)
+
GEM
remote: http://rubygems.org/
specs:
- GeoRuby (1.3.3)
ZenTest (4.6.2)
abstract (1.0.0)
- actionmailer (3.0.10)
- actionpack (= 3.0.10)
+ actionmailer (3.0.20)
+ actionpack (= 3.0.20)
mail (~> 2.2.19)
- actionpack (3.0.10)
- activemodel (= 3.0.10)
- activesupport (= 3.0.10)
+ actionpack (3.0.20)
+ activemodel (= 3.0.20)
+ activesupport (= 3.0.20)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.5.0)
- rack (~> 1.2.1)
+ rack (~> 1.2.5)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
- activemodel (3.0.10)
- activesupport (= 3.0.10)
+ activemodel (3.0.20)
+ activesupport (= 3.0.20)
builder (~> 2.1.2)
i18n (~> 0.5.0)
- activerecord (3.0.10)
- activemodel (= 3.0.10)
- activesupport (= 3.0.10)
+ activerecord (3.0.20)
+ activemodel (= 3.0.20)
+ activesupport (= 3.0.20)
arel (~> 2.0.10)
tzinfo (~> 0.3.23)
- activeresource (3.0.10)
- activemodel (= 3.0.10)
- activesupport (= 3.0.10)
- activesupport (3.0.10)
+ activerecord-postgis-adapter (0.6.5)
+ rgeo-activerecord (~> 0.5.0)
+ activeresource (3.0.20)
+ activemodel (= 3.0.20)
+ activesupport (= 3.0.20)
+ activesupport (3.0.20)
addressable (2.2.6)
archive-tar-minitar (0.5.2)
arel (2.0.10)
@@ -39,7 +50,7 @@ GEM
autotest-rails (4.1.0)
ZenTest
builder (2.1.2)
- capistrano (2.8.0)
+ capistrano (2.15.5)
highline
net-scp (>= 1.0.0)
net-sftp (>= 2.0.0)
@@ -65,31 +76,33 @@ GEM
capybara (>= 1.0.0)
cucumber (~> 1.0.0)
nokogiri (>= 1.4.6)
- database_cleaner (0.6.7)
+ database_cleaner (1.1.1)
diff-lcs (1.1.3)
dynamic_form (1.1.4)
erubis (2.6.6)
abstract (>= 1.0.0)
exception_notification (2.5.2)
actionmailer (>= 3.0.4)
- factory_girl (2.1.0)
- factory_girl_rails (1.2.0)
- factory_girl (~> 2.1.0)
+ factory_girl (4.2.0)
+ activesupport (>= 3.0.0)
+ factory_girl_rails (4.2.1)
+ factory_girl (~> 4.2.0)
railties (>= 3.0.0)
fast_gettext (0.5.13)
ffaker (1.8.1)
ffi (1.0.9)
+ geocoder (1.1.8)
gettext (2.0.0)
gettext_i18n_rails (0.2.20)
fast_gettext
gherkin (2.4.16)
json (>= 1.4.6)
haml (3.0.25)
- highline (1.6.2)
- hoe (2.12.3)
- rake (~> 0.8)
+ highline (1.6.19)
+ hoe (3.5.0)
+ rake (>= 0.8, < 11.0)
i18n (0.5.0)
- json (1.5.4)
+ json (1.8.0)
json_pure (1.5.4)
spruz (~> 0.2.8)
launchy (2.0.5)
@@ -101,58 +114,60 @@ GEM
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- mime-types (1.16)
- net-scp (1.0.4)
- net-ssh (>= 1.99.1)
- net-sftp (2.0.5)
- net-ssh (>= 2.0.9)
- net-ssh (2.2.1)
- net-ssh-gateway (1.1.0)
- net-ssh (>= 1.99.1)
+ mime-types (1.19)
+ net-scp (1.1.2)
+ net-ssh (>= 2.6.5)
+ net-sftp (2.1.2)
+ net-ssh (>= 2.6.5)
+ net-ssh (2.6.8)
+ net-ssh-gateway (1.2.0)
+ net-ssh (>= 2.6.5)
nokogiri (1.5.0)
pg (0.11.0)
pickle (0.4.8)
cucumber (>= 0.8)
rake
- polyglot (0.3.2)
+ polyglot (0.3.3)
prawn (0.4.0)
prawn-layout
prawn-layout (0.1.0)
- rack (1.2.3)
+ rack (1.2.8)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
- rails (3.0.10)
- actionmailer (= 3.0.10)
- actionpack (= 3.0.10)
- activerecord (= 3.0.10)
- activeresource (= 3.0.10)
- activesupport (= 3.0.10)
+ rails (3.0.20)
+ actionmailer (= 3.0.20)
+ actionpack (= 3.0.20)
+ activerecord (= 3.0.20)
+ activeresource (= 3.0.20)
+ activesupport (= 3.0.20)
bundler (~> 1.0)
- railties (= 3.0.10)
- railties (3.0.10)
- actionpack (= 3.0.10)
- activesupport (= 3.0.10)
+ railties (= 3.0.20)
+ railties (3.0.20)
+ actionpack (= 3.0.20)
+ activesupport (= 3.0.20)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.4)
- rake (0.9.2)
+ rake (10.1.0)
rdiscount (1.6.8)
- rdoc (3.9.4)
- rspec (2.6.0)
- rspec-core (~> 2.6.0)
- rspec-expectations (~> 2.6.0)
- rspec-mocks (~> 2.6.0)
- rspec-core (2.6.4)
- rspec-expectations (2.6.0)
- diff-lcs (~> 1.1.2)
- rspec-mocks (2.6.0)
- rspec-rails (2.6.1)
- actionpack (~> 3.0)
- activesupport (~> 3.0)
- railties (~> 3.0)
- rspec (~> 2.6.0)
+ rdoc (3.12.2)
+ json (~> 1.4)
+ rgeo (0.3.20)
+ rspec (2.11.0)
+ rspec-core (~> 2.11.0)
+ rspec-expectations (~> 2.11.0)
+ rspec-mocks (~> 2.11.0)
+ rspec-core (2.11.1)
+ rspec-expectations (2.11.3)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.11.3)
+ rspec-rails (2.11.4)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec (~> 2.11.0)
ruby-debug-base19 (0.11.25)
columnize (>= 0.3.1)
linecache19 (>= 0.5.11)
@@ -164,7 +179,9 @@ GEM
ruby_core_source (0.1.5)
archive-tar-minitar (>= 0.5.2)
rubyzip (0.9.4)
- sass (3.1.7)
+ rvm-capistrano (1.3.0)
+ capistrano (>= 2.0.0)
+ sass (3.1.15)
selenium-webdriver (2.5.0)
childprocess (>= 0.2.1)
ffi (>= 1.0.7)
@@ -175,10 +192,10 @@ GEM
test-unit (1.2.3)
hoe (>= 1.5.1)
thor (0.14.6)
- treetop (1.4.10)
+ treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
- tzinfo (0.3.29)
+ tzinfo (0.3.37)
xpath (0.1.4)
nokogiri (~> 1.3)
@@ -186,16 +203,16 @@ PLATFORMS
ruby
DEPENDENCIES
- GeoRuby
+ activerecord-postgis-adapter
authlogic (~> 3.0.3)
autotest-rails
- capistrano
cucumber-rails
database_cleaner
dynamic_form
exception_notification
factory_girl_rails
ffaker
+ geocoder (~> 1.1.8)
gettext (>= 1.9.3)
gettext_i18n_rails
haml
@@ -203,9 +220,11 @@ DEPENDENCIES
pg
pickle
prawn
- rails (= 3.0.10)
+ rails (= 3.0.20)
rdiscount
- rspec-rails (~> 2.6.1)
+ rgeo-activerecord!
+ rspec-rails (~> 2.11.0)
ruby-debug19
+ rvm-capistrano
sass
test-unit (= 1.2.3)
diff --git a/README b/README
deleted file mode 100644
index fe7013d5..00000000
--- a/README
+++ /dev/null
@@ -1,256 +0,0 @@
-== Welcome to Rails
-
-Rails is a web-application framework that includes everything needed to create
-database-backed web applications according to the Model-View-Control pattern.
-
-This pattern splits the view (also called the presentation) into "dumb"
-templates that are primarily responsible for inserting pre-built data in between
-HTML tags. The model contains the "smart" domain objects (such as Account,
-Product, Person, Post) that holds all the business logic and knows how to
-persist themselves to a database. The controller handles the incoming requests
-(such as Save New Account, Update Product, Show Post) by manipulating the model
-and directing data to the view.
-
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
-
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
-
-
-== Getting Started
-
-1. At the command prompt, create a new Rails application:
- rails new myapp (where myapp is the application name)
-
-2. Change directory to myapp and start the web server:
- cd myapp; rails server (run with --help for options)
-
-3. Go to http://localhost:3000/ and you'll see:
- "Welcome aboard: You're riding Ruby on Rails!"
-
-4. Follow the guidelines to start developing your application. You can find
-the following resources handy:
-
-* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
-* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
-
-
-== Debugging Rails
-
-Sometimes your application goes wrong. Fortunately there are a lot of tools that
-will help you debug it and get it back on the rails.
-
-First area to check is the application log files. Have "tail -f" commands
-running on the server.log and development.log. Rails will automatically display
-debugging and runtime information to these files. Debugging info will also be
-shown in the browser on requests from 127.0.0.1.
-
-You can also log your own messages directly into the log file from your code
-using the Ruby logger class from inside your controllers. Example:
-
- class WeblogController < ActionController::Base
- def destroy
- @weblog = Weblog.find(params[:id])
- @weblog.destroy
- logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
- end
- end
-
-The result will be a message in your log file along the lines of:
-
- Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
-
-More information on how to use the logger is at http://www.ruby-doc.org/core/
-
-Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
-several books available online as well:
-
-* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
-* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
-
-These two books will bring you up to speed on the Ruby language and also on
-programming in general.
-
-
-== Debugger
-
-Debugger support is available through the debugger command when you start your
-Mongrel or WEBrick server with --debugger. This means that you can break out of
-execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install ruby-debug to run the server in debugging
-mode. With gems, use sudo gem install ruby-debug. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find(:all)
- debugger
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the server window. Here you can do things like:
-
- >> @posts.inspect
- => "[#nil, "body"=>nil, "id"=>"1"}>,
- #"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
- >> @posts.first.title = "hello from a debugger"
- => "hello from a debugger"
-
-...and even better, you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you can enter "cont".
-
-
-== Console
-
-The console is a Ruby shell, which allows you to interact with your
-application's domain model. Here you'll have all parts of the application
-configured, just like it is when the application is running. You can inspect
-domain models, change values, and save to the database. Starting the script
-without arguments will launch it in the development environment.
-
-To start the console, run rails console from the application
-directory.
-
-Options:
-
-* Passing the -s, --sandbox argument will rollback any modifications
- made to the database.
-* Passing an environment name as an argument will load the corresponding
- environment. Example: rails console production.
-
-To reload your controllers and models after launching the console run
-reload!
-
-More information about irb can be found at:
-link:http://www.rubycentral.com/pickaxe/irb.html
-
-
-== dbconsole
-
-You can go to the command line of your database directly through rails
-dbconsole. You would be connected to the database with the credentials
-defined in database.yml. Starting the script without arguments will connect you
-to the development database. Passing an argument will connect you to a different
-database, like rails dbconsole production. Currently works for MySQL,
-PostgreSQL and SQLite 3.
-
-== Description of Contents
-
-The default directory structure of a generated Ruby on Rails application:
-
- |-- app
- | |-- controllers
- | |-- helpers
- | |-- mailers
- | |-- models
- | `-- views
- | `-- layouts
- |-- config
- | |-- environments
- | |-- initializers
- | `-- locales
- |-- db
- |-- doc
- |-- lib
- | `-- tasks
- |-- log
- |-- public
- | |-- images
- | |-- javascripts
- | `-- stylesheets
- |-- script
- |-- test
- | |-- fixtures
- | |-- functional
- | |-- integration
- | |-- performance
- | `-- unit
- |-- tmp
- | |-- cache
- | |-- pids
- | |-- sessions
- | `-- sockets
- `-- vendor
- `-- plugins
-
-app
- Holds all the code that's specific to this particular application.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from
- ApplicationController which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb. Models descend from
- ActiveRecord::Base by default.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.html.erb for the WeblogsController#index action. All views use
- eRuby syntax by default.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the
- common header/footer method of wrapping views. In your views, define a layout
- using the layout :default and create a file named default.html.erb.
- Inside default.html.erb, call <% yield %> to render the view using this
- layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are
- generated for you automatically when using generators for controllers.
- Helpers can be used to wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database,
- and other dependencies.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all the
- sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when
- generated using rake doc:app
-
-lib
- Application specific libraries. Basically, any kind of custom code that
- doesn't belong under controllers, models, or helpers. This directory is in
- the load path.
-
-public
- The directory available for the web server. Contains subdirectories for
- images, stylesheets, and javascripts. Also contains the dispatchers and the
- default HTML files. This should be set as the DOCUMENT_ROOT of your web
- server.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures. When using the rails generate
- command, template test files will be generated for you and placed in this
- directory.
-
-vendor
- External libraries that the application depends on. Also includes the plugins
- subdirectory. If the app has frozen rails, those gems also go here, under
- vendor/rails/. This directory is in the load path.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..749b0525
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+[![Build Status](https://travis-ci.org/marnen/quorum2.png?branch=master)](https://travis-ci.org/marnen/quorum2)
+
+# Quorum Calendar System
+
+This is the Quorum Calendar System, an easy-to-use solution for collaborative scheduling. For more information, please see http://quorum2.sf.net.
\ No newline at end of file
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index a3027e71..f21b177e 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -3,54 +3,60 @@
class CalendarsController < ApplicationController
@nonadmin ||= [:new, :create]
before_filter :check_admin, :except => @nonadmin
+ before_filter :load_calendar, :except => @nonadmin
before_filter :require_user, :only => @nonadmin
layout 'standard'
-
- make_resourceful do
- actions :new, :create, :edit, :update
-
- response_for :new do
- @page_title = _('Create calendar')
- render :action => 'edit'
- end
-
- response_for :edit do
- @page_title = _('Edit calendar')
- end
-
- # TODO: Shouldn't this be replaced by Calendar#set_admin ?
- after :create do
- p = User.current_user.permissions
- @admin ||= Role.find_or_create_by_name('admin')
- if !p.find_by_calendar_id_and_role_id(current_object.id, @admin.id)
- p << Permission.create!(:user => User.current_user, :calendar => current_object, :role => @admin)
- end
- end
-
- response_for :create do
- flash[:notice] = _('Your calendar was successfully created.')
- redirect_to '/admin'
- end
- response_for :create_fails do
- flash[:error] = _('Couldn\'t create your calendar!')
- redirect_to :back
- end
+ respond_to :html
- response_for :update do
- flash[:notice] = _('Your calendar was successfully saved.')
- redirect_to '/admin'
+ def new
+ @page_title = _('Create calendar')
+ @calendar = Calendar.new
+ respond_with @calendar
+ end
+
+ def create
+ @calendar = Calendar.new params[:calendar]
+ if @calendar.save
+ make_admin_permission_for @calendar
+ redirect_to '/admin', notice: _('Your calendar was successfully created.')
+ else
+ flash[:error] = _("Couldn't create your calendar!")
+ respond_with @calendar
end
+ end
- response_for :update_fails do
+ def edit
+ @page_title = _('Edit calendar')
+ respond_with @calendar
+ end
+
+ def update
+ if @calendar.update_attributes params[:calendar]
+ redirect_to '/admin', notice: _('Your calendar was successfully saved.')
+ else
flash[:error] = _('Couldn\'t save your calendar!')
- redirect_to :back
+ respond_with @calendar
end
end
-
+
# Lists all the users for the current #Calendar.
def users
- @page_title = _('Users for calendar %{calendar_name}') % {:calendar_name => current_object}
- @users = current_object.users.find(:all, :order => 'lastname, firstname')
+ @page_title = _('Users for calendar %{calendar_name}') % {:calendar_name => @calendar}
+ @users = @calendar.users.find(:all, order: 'lastname, firstname')
+ end
+
+ private
+
+ def load_calendar
+ @calendar = Calendar.find params[:id]
+ end
+
+ def make_admin_permission_for(calendar)
+ p = User.current_user.permissions
+ @admin ||= Role.find_or_create_by_name('admin')
+ if !p.find_by_calendar_id_and_role_id(calendar.id, @admin.id)
+ p << Permission.create!(:user => User.current_user, :calendar => calendar, :role => @admin)
+ end
end
end
diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb
index 72dbcf73..40f94920 100644
--- a/app/controllers/events_controller.rb
+++ b/app/controllers/events_controller.rb
@@ -2,84 +2,79 @@
# This is the controller for #Event instances. It supports the following make_resourceful[http://mr.hamptoncatlin.com] actions: :index, :create, :new, :edit, :update, :show.
class EventsController < ApplicationController
- layout "standard", :except => [:export, :feed] # no layout needed on export, since it generates an iCal file
- before_filter :require_user, :except => :feed
- before_filter :login_from_key, :only => :feed
- after_filter :ical_header, :only => :export # assign the correct MIME type so that it gets recognized as an iCal event
-
- rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
-
- make_resourceful do
- actions :index, :create, :new, :edit, :update, :show
-
- before :index do
- params[:order] ||= 'date' # isn't it enough to define this in routes.rb?
- params[:direction] ||= 'asc' # and this?
- @page_title = _("Upcoming events")
- @order = params[:order]
- @direction = params[:direction]
- @search = params[:search].extend(Search) if params[:search]
- end
-
- response_for :index do |format|
- format.html
+ layout "standard", except: [:export, :feed] # no layout needed on export, since it generates an iCal file
+ before_filter :require_user, except: :feed
+ before_filter :login_from_key, only: :feed
+ before_filter :load_event, only: [:edit, :update, :show, :delete]
+ after_filter :ical_header, only: :export # assign the correct MIME type so that it gets recognized as an iCal event
+
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
+
+ respond_to :html, except: :feed
+ respond_to :pdf, only: :index
+ respond_to :rss, only: :feed
+
+ def index
+ set_table_headers
+ @events = current_objects
+ respond_with @events do |format|
format.pdf do
@users = current_objects.blank? ? [] : current_objects[0].calendar.permissions.find_all_by_show_in_report(true, :include => :user).collect{|x| x.user}.sort # TODO: fix for multiple calendars
- prawnto :prawn => {:page_layout => :landscape}
- render :layout => false
+ prawnto prawn: {page_layout: :landscape}
+ render layout: false
end
end
-
- before :new do
- @page_title = _("Add event")
- end
-
- response_for :edit do
- if !current_object.allow?(:edit)
- flash[:error] = _("You are not authorized to edit that event.")
- redirect_to :action => :index
- else
- @page_title = _("Edit event")
- render :action => 'new'
- end
- end
-
- response_for :update, :create do
- flash[:notice] = _("Your event has been saved.")
- redirect_to :action => :index
- end
-
- response_for :update_fails, :create_fails do
- flash[:error] = _("We couldn't process that request. Please try again.")
- render :new
+ end
+
+ def new
+ @page_title = _("Add event")
+ @event = Event.new
+ respond_with @event
+ end
+
+ def create
+ @event = Event.new params[:event]
+ respond_with_flash { @event.save }
+ end
+
+ def edit
+ if @event.allow? :edit
+ @page_title = _("Edit event")
+ respond_with @event
+ else
+ redirect_to({action: :index}, flash: {error: _("You are not authorized to edit that event.")})
end
-
- response_for :show do
- if !current_object.allow?(:show)
- flash[:error] = _("You are not authorized to view that event.")
- redirect_to :action => :index
- else
- @page_title = current_object.name
- end
+ end
+
+ def update
+ respond_with_flash { @event.update_attributes params[:event] }
+ end
+
+ def show
+ if @event.allow? :show
+ @page_title = @event.name
+ respond_with @event
+ else
+ redirect_to({action: :index}, flash: {error: _("You are not authorized to view that event.")})
end
end
-
+
# Generate an RSS feed of events.
def feed
respond_to do |format|
format.rss do
- @key = params[:key]
params[:from_date] = Date.civil(1, 1, 1)
+ @key = params[:key]
+ @events = current_objects
end
end
end
# Delete an #Event, subject to #Event#allow?.
def delete
- event = Event.find(params[:id].to_i)
begin
- if event.allow?(:delete)
- event.hide
+ if @event.allow?(:delete)
+ @event.hide
flash[:notice] = _("The selected event was deleted.")
else
flash[:error] = _("You are not authorized to delete that event.")
@@ -107,7 +102,7 @@ def change_status
status_map = {'yes' => true, 'no' => false, 'maybe' => nil}
if !id.nil? then
event = Event.find_by_id(id)
- event.change_status!(current_user, status_map[params[:status].to_s])
+ event.change_status! current_user, status_map[params[:status].to_s], params[:comment]
end
if request.xhr?
render :partial => 'event', :locals => {:event => event}
@@ -115,11 +110,11 @@ def change_status
redirect_to :action => :index
end
end
-
+
# Display a map page for the current #Event.
def map
begin
- @event = Event.find(params[:id])
+ load_event
@host = request.host_with_port
rescue
flash[:error] = _("Couldn't find that event!")
@@ -127,13 +122,15 @@ def map
end
@page_title = _("Map for %{event}") % {:event => @event.name}
end
-
- # Return non-deleted events between params[:from_date] and params[:to_date], optionally ordered as specified by params[:order] and [:direction]. Provided for use with make_resourceful[http://mr.hamptoncatlin.com].
+
+ private
+
+ # Return non-deleted events between params[:from_date] and params[:to_date], optionally ordered as specified by params[:order] and [:direction].
def current_objects
user = params[:feed_user] || User.current_user
# Process parameters from the search form, if it was submitted.
- if !params[:search].nil?
+ if params[:search].present?
search = params[:search]
search.extend(Search)
['to', 'from'].each do |s|
@@ -143,36 +140,60 @@ def current_objects
calendars = search[:calendar_id].blank? ? nil : search[:calendar_id]
end
-
+
order = params[:order] || 'date'
from_date = params[:from_date] || Time.zone.today
to_date = params[:to_date]
direction = params[:direction] || 'asc'
- calendars ||= user.calendars.collect{|c| c.id}
-
- if to_date == nil
- date_query = 'date >= :from_date'
- else
+ calendars ||= (user ? user.calendars.collect{|c| c.id} : [])
+
+ if to_date.present?
date_query = 'date BETWEEN :from_date AND :to_date'
+ else
+ date_query = 'date >= :from_date'
end
-
- @current_objects || current_model.find(:all, :conditions => ['calendar_id IN (:calendars) AND ' + date_query, {:calendars => calendars, :from_date => from_date, :to_date => to_date}], :order => "#{order} #{direction}")
+
+ # TODO: can we use more Arel and less literal SQL?
+ @current_objects ||= Event.includes(:commitments => :user).where(["calendar_id IN (:calendars) AND #{date_query}", {:calendars => calendars, :from_date => from_date, :to_date => to_date}]).order("#{order} #{direction}")
end
-
- protected
+
# Return an HTTP header with proper MIME type for iCal.
def ical_header
headers['Content-Type'] = 'text/calendar'
end
-
+
+ def load_event
+ @event = Event.find params[:id]
+ end
+
# Log user in based on single_access_token.
def login_from_key
params[:feed_user] = User.find_by_single_access_token(params[:key])
end
-
+
# Handler for #RecordNotFound.
def record_not_found
flash[:error] = _("Couldn't find any event to edit!")
redirect_to(:action => :index) and return
end
+
+ def set_table_headers
+ params[:order] ||= 'date' # isn't it enough to define this in routes.rb?
+ params[:direction] ||= 'asc' # and this?
+ @page_title = _("Upcoming events")
+ @order = params[:order]
+ @direction = params[:direction]
+ @search = params[:search].extend(Search) if params[:search]
+ end
+
+ def respond_with_flash
+ raise ArgumentError, 'no block specified' unless block_given?
+
+ if yield
+ redirect_to({action: :index}, notice: _("Your event has been saved."))
+ else
+ flash[:error] = _("We couldn't process that request. Please try again.")
+ respond_with @event
+ end
+ end
end
diff --git a/app/controllers/permissions_controller.rb b/app/controllers/permissions_controller.rb
index 096533b7..98197e2c 100644
--- a/app/controllers/permissions_controller.rb
+++ b/app/controllers/permissions_controller.rb
@@ -1,65 +1,52 @@
# coding: UTF-8
class PermissionsController < ApplicationController
- @@nonadmin = :index, :subscribe, :destroy
- before_filter :check_admin, :except => @@nonadmin
- before_filter :require_user, :only => @@nonadmin
+ @nonadmin = :index, :subscribe, :destroy
+ before_filter :check_admin, except: @nonadmin
+ before_filter :require_user, only: @nonadmin
layout 'standard'
-
+
+ respond_to :html
+
class NotDeletableError < RuntimeError
end
-
+
rescue_from NotDeletableError do |e|
flash[:error] = _("Couldn't delete that subscription!")
go_back
end
-
- make_resourceful do
- actions :index, :edit, :update, :destroy
-
- before :destroy do
- if current_object.user_id != User.current_user.id # TODO: should maybe create Permission#allow? and use it here, as we did on Events
- raise NotDeletableError.new
- end
- end
-
- response_for :index do
- @page_title = _('Subscriptions')
- @permissions = User.current_user.permissions.find(:all, :include => [:calendar, :role])
- if @permissions.empty?
- @unsubscribed = Calendar.find(:all)
- else
- @unsubscribed = Calendar.find(:all, :conditions => ['id NOT IN (:permissions)', {:permissions => @permissions.collect{|p| p.calendar.id}}])
- end
- end
-
- response_for :update do
- flash[:notice] = _("Your changes have been saved.")
- go_back
+
+ def index
+ @page_title = _('Subscriptions')
+ @permissions = User.current_user.permissions.includes(:calendar, :role)
+ if @permissions.empty?
+ @unsubscribed = Calendar.find(:all)
+ else
+ @unsubscribed = Calendar.find(:all, :conditions => ['id NOT IN (:permissions)', {:permissions => @permissions.collect{|p| p.calendar.id}}])
end
-
- response_for :update_fails do
- flash[:error] = _("Couldn't save changes!")
- go_back
+ respond_with @permissions
+ end
+
+ def destroy
+ @permission = Permission.find params[:id]
+ if @permission.user_id != User.current_user.id # TODO: should maybe create Permission#allow? and use it here, as we did on Events
+ raise NotDeletableError.new
end
-
- response_for :destroy do
+
+ if @permission.destroy
flash[:notice] = _('You were successfully unsubscribed.')
- go_back
- end
-
- response_for :destroy_fails do
+ else
flash[:error] = _("Something went wrong. Please try again.")
- go_back
end
+ go_back
end
-
+
def subscribe
begin
- current_model.create! do |p|
+ Permission.create! do |p|
p.calendar_id = params[:calendar_id]
p.user = User.current_user
- p.role = Role.find_by_name('user')
+ p.role = Role.find_or_create_by_name('user')
end
rescue
flash[:error] = _("Something went wrong. Please try again.")
@@ -68,7 +55,7 @@ def subscribe
flash[:notice] = _("Your subscription has been saved.")
go_back and return
end
-
+
protected
def go_back
begin
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 347a167b..b3269331 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -1,6 +1,6 @@
# coding: UTF-8
-module EventsHelper
+module EventsHelper
# Returns #User's commitment status for #Event as a symbol -- :yes, :no, :or maybe.
def attendance_status(event, user)
if event.find_committed(:yes).include? user then
@@ -11,7 +11,12 @@ def attendance_status(event, user)
status = :maybe
end
end
-
+
+ # Returns #User's commitment comment for #Event.
+ def attendance_comment(event, user)
+ event.comments.find {|c| c.user == user }.try :comment
+ end
+
# Generates an HTML date element for #Event, including hCalendar[http://microformats.org/wiki/hcalendar] annotation.
#
# Usage:
@@ -25,47 +30,30 @@ def date_element(event)
full_date = h event.date.to_formatted_s(:rfc822)
content_tag :abbr, full_date, :class => :dtstart, :title => ical_date
end
-
+
# Generates a delete link for #Event.
def delete_link(event)
link_to h(_("delete")), url_for(:controller => 'events', :action => 'delete', :id => event.id), :class => :delete
end
-
+
# Returns the distance from #Event to #User's address, in a #String of the form "35.2 miles".
# If something goes wrong, returns "0.0 miles".
def distance_string(event, user)
begin
- meters = event.coords.ellipsoidal_distance(user.coords)
+ meters = event.coords.distance(user.coords)
miles = meters / 1609.344
-
+
content_tag(:span, h(_("%.1f miles" % miles)), :class => :distance)
rescue
content_tag(:span, h(_("%.1f miles" % 0)), :class => :distance)
end
end
-
+
# Generates an edit link for #Event.
def edit_link(event)
link_to h(_("edit")), url_for(:controller => 'events', :action => 'edit', :id => event.id), :class => :edit
end
-
- # Generates a
element with a map for #Event, using the Google API key for host.
- # TODO: figure out how to make this html_safe!
- def event_map(event, hostname)
- return nil if event.nil?
-
- @extra_headers ||= ''.html_safe
- @extra_headers << GMap.header(:host => hostname).to_s.html_safe << javascript_include_tag('events/map').html_safe
- map = GMap.new(:map)
- result = ''.html_safe
- result << info(event)
- result << content_tag(:div, event.coords.lat, :id => :lat, :class => :hidden)
- result << content_tag(:div, event.coords.lng, :id => :lng, :class => :hidden)
- result << map.div(:width => 500, :height => 400).html_safe
- result
- end
-
# Escapes characters in string that would be illegal in iCalendar format.
def ical_escape(string)
string.gsub(%r{[\\,;]}) {|c| '\\' + c}.gsub("\n", '\\n')
@@ -80,7 +68,7 @@ def ical_link(event)
def ical_uid(event)
"event-" << event.id.to_s << "@" << DOMAIN
end
-
+
# Generates text for the info window on Google map of #Event.
#
# TODO: this should probably become a partial.
@@ -91,7 +79,7 @@ def info(event)
result << content_tag(:h3, (event.site || event.name))
city = [event.city, event.state.code, event.state.country.code].compact.join(', ')
result << content_tag(:p, [h(event.street), h(event.street2), h(city)].compact.join(tag :br).html_safe)
-
+
gmaps = 'http://maps.google.com'
from = "saddr=#{u User.current_user.address.to_s(:geo)}"
to = "daddr=#{u event.address.to_s(:geo)}"
@@ -99,7 +87,7 @@ def info(event)
result << content_tag(:p, link_to(_('Get directions'), h("#{gmaps}?#{params}")))
content_tag(:div, result, :id => :info)
end
-
+
# Given an #Array (or similar) of #User objects, returns an #Array of their full names as #Strings.
def list_names(users)
return '' if users.nil? or users.size == 0
@@ -110,13 +98,13 @@ def list_names(users)
def map_link(event)
link_to h(_("map")), url_for(:controller => 'events', :action => 'map', :id => event.id), :class => 'map', :target => 'map'
end
-
+
# Generates a hint to use Markdown for formatting.
# TODO: make this work as html_safe properly.
def markdown_hint
content_tag(:span, (h(_ '(use %{Markdown} for formatting)').to_str % {:Markdown => link_to(_('Markdown'), 'http://daringfireball.net/projects/markdown/basics', :target => 'markdown')}).html_safe, :class => :hint)
end
-
+
# Generates an RSS URL for the current user's events feed.
def rss_url
if User.current_user
@@ -137,4 +125,8 @@ def sort_link(title, field, direction = :asc, options = {})
my_class ||= 'sort'
link_to h(_(title)), url_for(params.merge :order => field, :direction => direction), :class => my_class
end
+
+ def status_strings
+ @status_strings ||= {yes: _('attending'), no: _('not attending'), maybe: _('uncommitted')}
+ end
end
diff --git a/app/helpers/gmaps_helper.rb b/app/helpers/gmaps_helper.rb
new file mode 100644
index 00000000..17e7c95f
--- /dev/null
+++ b/app/helpers/gmaps_helper.rb
@@ -0,0 +1,5 @@
+module GmapsHelper
+ def gmaps_api_key
+ GMAPS_API_KEY.kind_of?(Hash) ? GMAPS_API_KEY[controller.request.host_with_port] : GMAPS_API_KEY
+ end
+end
\ No newline at end of file
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
index 9e965f29..7aa2f46e 100644
--- a/app/models/calendar.rb
+++ b/app/models/calendar.rb
@@ -4,18 +4,18 @@ class Calendar < ActiveRecord::Base
has_many :events
has_many :permissions
has_many :users, :through => :permissions
-
+
validates_presence_of :name
-
+
after_create :set_admin
-
+
def to_s
self.name
end
-
-
+
+
protected
- def set_admin
+ def set_admin # TODO: is this being used anywhere?
if User.current_user and User.current_user != :false # TODO: can't we refactor this condition elsewhere?
self.permissions.create(:user => User.current_user, :role => Role.find_by_name('admin'))
end
diff --git a/app/models/commitment.rb b/app/models/commitment.rb
index f058ea29..5267b441 100644
--- a/app/models/commitment.rb
+++ b/app/models/commitment.rb
@@ -5,7 +5,4 @@ class Commitment < ActiveRecord::Base
belongs_to :user
validates_presence_of :event_id
validates_presence_of :user_id
-
- scope :attending, :conditions => {:status => true}
- scope :not_attending, :conditions => {:status => false}
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 7719e7ee..6b4d8625 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,11 +1,9 @@
# coding: UTF-8
-
-require 'geocoding_utilities'
+require 'acts/geocoded'
class Event < ActiveRecord::Base
- acts_as_addressed
- include GeocodingUtilities
-
+ acts_as_geocoded
+
belongs_to :created_by, :class_name => "User"
belongs_to :calendar
has_many :commitments
@@ -14,10 +12,11 @@ class Event < ActiveRecord::Base
validates_presence_of :calendar_id
validates_presence_of :name
validates_presence_of :state_id
+
before_create :set_created_by_id
-
+
default_scope :conditions => 'deleted is distinct from true'
-
+
# Returns true if #User.current_user is allowed to perform operation on the current #Event, false otherwise.
# Operation may be :edit, :delete, or :show.
def allow?(operation)
@@ -32,38 +31,50 @@ def allow?(operation)
end
end
end
-
+
# Sets the #User's attendance status on the Event, where status is one of true (attending), false (not attending), or nil (uncommitted).
- def change_status!(user, status)
+ def change_status!(user, status, comment = nil)
commitment = commitments.find_or_create_by_user_id(user.id)
- commitment.status = status
- commitment.save!
+ commitment.update_attributes! :status => status, :comment => comment
+ end
+
+ # Returns an array of nonblank comments for the #Event, ordered by #User's name.
+ def comments
+ commitments.reject {|c| c.comment.blank? }.sort_by &:user
end
-
+
# Returns an #Array of #User objects with commitment status (for the current #Event) of status,
# where status may be :yes or :no.
def find_committed(status)
if ![:yes, :no].include? status
- raise "Invalid status: " << status
+ raise "Invalid status (not :yes or :no): " << status
end
- scope = {:yes => :attending, :no => :not_attending}[status]
- c = commitments.send(scope)
- c.collect{|e| e.user }.sort{|x, y| (x.lastname || x.email) <=> (y.lastname || y.email)}
+ status_to_find = {yes: true, no: false}[status]
+ found_commitments = commitments.select {|c| c.status == status_to_find }
+ found_commitments.collect {|e| e.user }.sort
end
-
+
# Hides the current #Event. This has the effect of deleting it, since hidden Events will not show up in the main list.
def hide
self.deleted = true
self.save
end
-
- protected
+
+ def latitude
+ coords.try :y
+ end
+
+ def longitude
+ coords.try :x
+ end
+
+ protected
# TODO: allow_* methods should probably be public. Keeping them protected mainly so as not to change the class interface just yet.
def allow_delete?(user)
role = role_of user
!role.nil? and role.name == 'admin'
end
-
+
def allow_edit?(user)
if created_by == user
return true
@@ -72,11 +83,11 @@ def allow_edit?(user)
return role.name == 'admin'
end
end
-
+
def allow_show?(user)
!(role_of user).nil?
end
-
+
# TODO: should this method be public?
# Returns the #Role of the #User for the #Event.
def role_of(user)
@@ -84,7 +95,7 @@ def role_of(user)
p = user.permissions.find_by_calendar_id(self.calendar_id)
p.nil? ? nil : p.role
end
-
+
def set_created_by_id
if User.current_user and User.current_user != :false
self.created_by = User.current_user
diff --git a/app/models/user.rb b/app/models/user.rb
index 52c602d9..f353bffb 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,15 +1,14 @@
# coding: UTF-8
-
+require 'acts/geocoded'
require 'digest/sha1'
-require 'geocoding_utilities'
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.transition_from_restful_authentication = true
end
-
- acts_as_addressed
- include GeocodingUtilities
+
+ acts_as_geocoded
+
cattr_accessor :current_user
has_many :commitments
@@ -17,7 +16,7 @@ class User < ActiveRecord::Base
has_many :permissions
has_many :calendars, :through => :permissions
# validates_presence_of :permissions
-
+
validates_presence_of :email
validates_presence_of :password, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
@@ -27,28 +26,29 @@ class User < ActiveRecord::Base
validates_uniqueness_of :email, :case_sensitive => false
before_save :make_single_access_token
after_create :set_calendar
+
# prevents a user from submitting a crafted form that bypasses activation
# anything else you want your user to change should be added here.
attr_accessible :email, :password, :password_confirmation, :firstname, :lastname, :street, :street2, :city, :state, :state_id, :zip, :show_contact
-
+
# Sets the User's active status to true.
# TODO: Rename to activate! , since it's destructive.
def activate
update_attribute(:active, true)
end
-
+
def admin?
@admin ||= Role.find_by_name('admin')
!(self.permissions.find_by_role_id(@admin).nil?)
end
-
+
# Compares users by last name, first name, and e-mail address in that order.
# ['Smith', 'John', 'jsmith1@aol.com'] < ['Smith', 'John', 'jsmith2@aol.com']
def <=>(other)
attrs = [:lastname, :firstname, :email]
attrs.collect{|a| self[a].downcase rescue nil}.compact <=> attrs.collect{|a| other[a].downcase rescue nil}.compact
end
-
+
# Resets the user's password and password_confirmation to a random string. Designed to be used for password resets.
def reset_password!
new_password = Digest::MD5.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join)[0, 10]
@@ -71,19 +71,20 @@ def to_s(format = :first_last)
end
protected
- def password_required?
- crypted_password.blank? || !password.blank? || !password_confirmation.blank?
- end
-
- def make_single_access_token
- if single_access_token.blank?
- reset_single_access_token!
- end
+
+ def password_required?
+ crypted_password.blank? || !password.blank? || !password_confirmation.blank?
+ end
+
+ def make_single_access_token
+ if single_access_token.blank?
+ reset_single_access_token!
end
-
- def set_calendar
- if Calendar.count == 1
- permissions.create(:user => self, :calendar => Calendar.find(:first), :role => Role.find_or_create_by_name('user'))
- end
+ end
+
+ def set_calendar
+ if Calendar.count == 1
+ permissions.create(:user => self, :calendar => Calendar.find(:first), :role => Role.find_or_create_by_name('user'))
end
+ end
end
diff --git a/app/views/calendars/_form.html.haml b/app/views/calendars/_form.html.haml
new file mode 100644
index 00000000..e51aafea
--- /dev/null
+++ b/app/views/calendars/_form.html.haml
@@ -0,0 +1,6 @@
+= form_for calendar do |f|
+ %table.edit
+ %tr
+ %th= f.label :name, _('Name')
+ %td= f.text_field :name
+ = submit_tag(h(_('Save')))
\ No newline at end of file
diff --git a/app/views/calendars/edit.html.haml b/app/views/calendars/edit.html.haml
index 8bf1b141..6b9d793f 100644
--- a/app/views/calendars/edit.html.haml
+++ b/app/views/calendars/edit.html.haml
@@ -1,6 +1 @@
-= form_for @current_object do |f|
- %table.edit
- %tr
- %th= f.label :name, _('Name')
- %td= f.text_field :name
- = submit_tag(h(_('Save')))
\ No newline at end of file
+= render partial: 'form', locals: {calendar: @calendar}
\ No newline at end of file
diff --git a/app/views/calendars/new.html.haml b/app/views/calendars/new.html.haml
new file mode 100644
index 00000000..6b9d793f
--- /dev/null
+++ b/app/views/calendars/new.html.haml
@@ -0,0 +1 @@
+= render partial: 'form', locals: {calendar: @calendar}
\ No newline at end of file
diff --git a/app/views/calendars/users.html.haml b/app/views/calendars/users.html.haml
index 1778c3b0..85d67294 100644
--- a/app/views/calendars/users.html.haml
+++ b/app/views/calendars/users.html.haml
@@ -8,8 +8,8 @@
%th
/ blank
- - for user in @users do
- - perm = user.permissions.find_by_calendar_id(@current_object)
+ - @users.each do |user|
+ - perm = user.permissions.find_by_calendar_id(@calendar) # TODO: get query out of view!
= form_for perm do |f|
%tr[user]
%td._name= [user.lastname, user.firstname].compact.join(', ')
diff --git a/app/views/events/_attendance.html.haml b/app/views/events/_attendance.html.haml
index b97e6c8d..5ac51c3f 100644
--- a/app/views/events/_attendance.html.haml
+++ b/app/views/events/_attendance.html.haml
@@ -1,9 +1,12 @@
-# TODO: stop defining variables in views!
-- @status_strings ||= { :yes => _('attending'), :no => _('not attending'), :maybe => _('uncommitted') }
- status = attendance_status(event, User.current_user)
-%td{:class => status}
- = form_for event, :as => :commitment, :url => {:action => :change_status, :id => event.id}, :html => {:class => :attendance} do |f|
- %p!= _("You are currently %{status}.") % {:status => content_tag(:span, h(@status_strings[status]), :class => status)}
- = select_tag(:status, options_for_select(@status_strings.invert, status), :class => :commit, :id => nil)
+%td.attendance{:class => status}
+ -# TODO: can we use accepts_nested here?
+ = form_tag({action: :change_status, id: event.id}, {class: :attendance}) do
+ %p!= h(_("You are currently %{status}.")) % {:status => content_tag(:span, h(status_strings[status]), :class => status)}
+ = select_tag(:status, options_for_select(status_strings.invert, status), :class => :commit, :id => nil)
%span.progress
+ %br
+ = text_area_tag :comment, attendance_comment(event, User.current_user), placeholder: _('comment'), id: nil
+ %br
= submit_tag(h(_("Change status")))
\ No newline at end of file
diff --git a/app/views/events/_comment.html.haml b/app/views/events/_comment.html.haml
new file mode 100644
index 00000000..cfe0cc7f
--- /dev/null
+++ b/app/views/events/_comment.html.haml
@@ -0,0 +1,3 @@
+.comment
+ %span.user{class: attendance_status(event, comment.user)}== #{comment.user}:
+ = comment.comment
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 773f43ee..48b89351 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -31,9 +31,10 @@
%div.description
:markdown
#{h event.description}
+ - if event.comments.present?
+ .comments= render partial: 'comment', collection: event.comments, locals: {event: event}
= render :partial => 'attendance', :locals => {:event => event}
- yes = event.find_committed(:yes)
- no = event.find_committed(:no)
- %td= render :partial => 'names', :object => yes
- %td= render :partial => 'names', :object => no
-
+ %td.yes= render :partial => 'names', :object => yes
+ %td.no= render :partial => 'names', :object => no
diff --git a/app/views/events/_form.html.haml b/app/views/events/_form.html.haml
new file mode 100644
index 00000000..8b03b9b2
--- /dev/null
+++ b/app/views/events/_form.html.haml
@@ -0,0 +1,45 @@
+= form_for event do |f|
+ %table.edit
+ - if current_user.calendars.size > 1
+ %tr
+ %th= _("Calendar")
+ %td= f.collection_select :calendar_id, User.current_user.calendars.sort{|x, y| x.name <=> y.name}, :id, :name
+ - else
+ = f.hidden_field :calendar_id, :value => User.current_user.calendars[0].id
+ %tr
+ %th= _("Event name")
+ %td
+ = f.text_field :name
+ = _(error_message_on(:event, :name)) # TODO: Do we need the :event now?
+ %tr
+ %th
+ = _("Description")
+ %br
+ = markdown_hint
+ %td= f.text_area :description, :cols => 40, :rows => 6
+ %tr
+ %th= _("Date")
+ %td
+ = f.date_select :date, :order => [:day, :month, :year], :start_year => 2006
+ = _(error_message_on(:event, :date))
+ %tr
+ %th= _("Site")
+ %td= f.text_field :site
+ %tr
+ %th= _("Street address")
+ %td
+ = f.text_field :street
+ %br
+ = f.text_field :street2
+ %tr
+ %th= _("City")
+ %td= f.text_field :city
+ %tr
+ %th= _("State")
+ %td
+ = f.collection_select :state_id, Acts::Addressed::State.find(:all), :id, :name
+ = _(error_message_on :event, :state)
+ %tr
+ %th= _("Zip")
+ %td= f.text_field :zip
+ = submit_tag _("Save changes")
\ No newline at end of file
diff --git a/app/views/events/_names.html.haml b/app/views/events/_names.html.haml
index 4ff62a0f..b8e054be 100644
--- a/app/views/events/_names.html.haml
+++ b/app/views/events/_names.html.haml
@@ -1,4 +1,5 @@
-- if !names.blank?
- %strong&= n_('1 person:', '%{num} people:', names.size) % {:num => names.size}
- &= list_names(names)
+- if names.present?
+ %p
+ %span.count= n_('1 person:', '%{num} people:', names.size) % {:num => names.size}
+ = list_names(names)
diff --git a/app/views/events/edit.html.haml b/app/views/events/edit.html.haml
new file mode 100644
index 00000000..b092fd17
--- /dev/null
+++ b/app/views/events/edit.html.haml
@@ -0,0 +1 @@
+= render partial: 'form', locals: {event: @event}
\ No newline at end of file
diff --git a/app/views/events/feed.rss.haml b/app/views/events/feed.rss.haml
index e1487e04..b8f969aa 100644
--- a/app/views/events/feed.rss.haml
+++ b/app/views/events/feed.rss.haml
@@ -5,10 +5,10 @@
%title= _("%{Quorum} Events") % {:Quorum => SITE_TITLE}
%link= events_url
%description
- - if !params[:feed_user].blank?
+ - if params[:feed_user].present?
= _("The latest events from %{Quorum}, generated for %{user}.") % {:Quorum => SITE_TITLE, :user => params[:feed_user]}
- - if !params[:feed_user].blank?
- - current_objects.each do |e|
+ - if params[:feed_user].present?
+ - @events.each do |e|
%item
%title= e.name
%description
diff --git a/app/views/events/ical.ics.erb b/app/views/events/ical.ics.erb
index 15a9d736..605de1d7 100644
--- a/app/views/events/ical.ics.erb
+++ b/app/views/events/ical.ics.erb
@@ -1,10 +1,12 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-BEGIN:VEVENT
-UID:<%= ical_uid @event %>
-SUMMARY:<%= ical_escape @event.name %>
-LOCATION:<%= [@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ') %>
-DESCRIPTION:<%= ical_escape @event.description %>
-DTSTART;VALUE=DATE:<%= @event.date.to_s :ical %>
-END:VEVENT
-END:VCALENDAR
\ No newline at end of file
+<% cr = 13.chr %>
+BEGIN:VCALENDAR<%= cr %>
+VERSION:2.0<%= cr %>
+PRODID:-//quorum2.sf.net//<%= SITE_TITLE %> <%= APP_VERSION %>//EN<%= cr %>
+BEGIN:VEVENT<%= cr %>
+UID:<%= ical_uid @event %><%= cr %>
+SUMMARY:<%= ical_escape @event.name %><%= cr %>
+LOCATION:<%= ical_escape [@event.street, @event.street2, @event.city, @event.state.code, @event.country.code].compact.join(', ') %><%= cr %>
+DESCRIPTION:<%= ical_escape @event.description %><%= cr %>
+DTSTART;VALUE=DATE:<%= @event.date.to_s :ical %><%= cr %>
+END:VEVENT<%= cr %>
+END:VCALENDAR<%= cr %>
diff --git a/app/views/events/index.html.haml b/app/views/events/index.html.haml
index 06790523..00e712fa 100644
--- a/app/views/events/index.html.haml
+++ b/app/views/events/index.html.haml
@@ -2,7 +2,7 @@
= auto_discovery_link_tag :rss, rss_url
- content_for :javascript do
= javascript_include_tag :defaults, 'lowpro/lowpro', 'events/index'
-
+
%p.rss
= h(_("Your personal %{RSS_feed} for this page is available at %{URL}")) % {:RSS_feed => link_to(_('RSS feed'), rss_url), :URL => content_tag(:span, h(rss_url), :class => :url)}
%br
@@ -24,7 +24,7 @@
%label
= s.radio_button :from_date_preset, 'other'
= _('Other date:')
- = s.date_select(:from_date, :order => [:day, :month, :year])
+ = s.date_select(:from_date, :order => [:day, :month, :year])
%tr
%th= _('To:')
%td
@@ -42,7 +42,7 @@
- if User.current_user.calendars.size > 1
%tr
%th= _('Calendar:')
- %td= s.select(:calendar_id, User.current_user.calendars.collect{|c| [c, c.id.to_s]}, {:include_blank => _('[All calendars]')})
+ %td{colspan: 3}= s.select(:calendar_id, User.current_user.calendars.collect{|c| [c, c.id.to_s]}, {:include_blank => _('[All calendars]')})
= submit_tag(_('Search'), :name => nil)
- if @events.collect{|e| e.calendar}.uniq.size == 1
%p.pdf!= _("Generate attendance report for these events (%{PDF})") % {:PDF => link_to(_('PDF'), "#{url_for(params.merge :format => :pdf)}")}
diff --git a/app/views/events/map.html.haml b/app/views/events/map.html.haml
index e3ee3b10..44229373 100644
--- a/app/views/events/map.html.haml
+++ b/app/views/events/map.html.haml
@@ -1 +1,8 @@
-= event_map @event, @host
+- content_for(:javascript) do
+ = javascript_include_tag "http://maps.google.com/maps?file=api&v=2&sensor=false&key=#{gmaps_api_key}"
+ = javascript_include_tag('events/map')
+
+= info(@event)
+#lat.hidden= @event.latitude
+#lng.hidden= @event.longitude
+#map
\ No newline at end of file
diff --git a/app/views/events/new.html.haml b/app/views/events/new.html.haml
index 679d652a..b092fd17 100644
--- a/app/views/events/new.html.haml
+++ b/app/views/events/new.html.haml
@@ -1,45 +1 @@
-= form_for @current_object do |f|
- %table.edit
- - if current_user.calendars.size > 1
- %tr
- %th= _("Calendar")
- %td= f.collection_select :calendar_id, User.current_user.calendars.sort{|x, y| x.name <=> y.name}, :id, :name
- - else
- = f.hidden_field :calendar_id, :value => User.current_user.calendars[0].id
- %tr
- %th= _("Event name")
- %td
- = f.text_field :name
- = _(error_message_on(:event, :name))
- %tr
- %th
- = _("Description")
- %br
- = markdown_hint
- %td= f.text_area :description, :cols => 40, :rows => 6
- %tr
- %th= _("Date")
- %td
- = f.date_select :date, :order => [:day, :month, :year], :start_year => 2006
- = _(error_message_on(:event, :date))
- %tr
- %th= _("Site")
- %td= f.text_field :site
- %tr
- %th= _("Street address")
- %td
- = f.text_field :street
- %br
- = f.text_field :street2
- %tr
- %th= _("City")
- %td= f.text_field :city
- %tr
- %th= _("State")
- %td
- = f.collection_select :state_id, Acts::Addressed::State.find(:all), :id, :name
- = _(error_message_on :event, :state)
- %tr
- %th= _("Zip")
- %td= f.text_field :zip
- = submit_tag _("Save changes")
\ No newline at end of file
+= render partial: 'form', locals: {event: @event}
\ No newline at end of file
diff --git a/app/views/events/show.html.haml b/app/views/events/show.html.haml
index a6667423..c965fb30 100644
--- a/app/views/events/show.html.haml
+++ b/app/views/events/show.html.haml
@@ -2,4 +2,4 @@
= javascript_include_tag :defaults, 'lowpro/lowpro', 'events/index'
%table.events
= render :partial => 'table_header', :locals => {:sortlinks => false}
- = render :partial => 'event', :locals => {:event => current_object}
+ = render :partial => 'event', :locals => {:event => @event}
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
new file mode 100644
index 00000000..3b2edb28
--- /dev/null
+++ b/app/views/layouts/_flash.html.haml
@@ -0,0 +1,9 @@
+- if !flash[:notice].nil?
+ #flash.notice
+ %p= flash[:notice]
+- if !flash[:warning].nil?
+ #flash.warning
+ %p= flash[:warning]
+- if !flash[:error].nil?
+ #flash.error
+ %p= flash[:error]
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
new file mode 100644
index 00000000..3a83fafa
--- /dev/null
+++ b/app/views/layouts/_head.html.haml
@@ -0,0 +1,6 @@
+%head
+ %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"}
+ %title= h(SITE_TITLE) + ' | ' + h(page_title)
+ = stylesheet_link_tag "quorum"
+ = yield :head
+ = yield :javascript
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
new file mode 100644
index 00000000..98ded7db
--- /dev/null
+++ b/app/views/layouts/application.html.haml
@@ -0,0 +1,11 @@
+!!! 5
+%html
+ = render partial: 'layouts/head', locals: {page_title: @page_title}
+
+ %body
+ = content_for?(:content) ? yield(:content) : yield
+
+ #footer
+ .version
+ = link_to _(SITE_TITLE), APP_HOME_PAGE
+ = _('version %{version}') % {version: APP_VERSION}
\ No newline at end of file
diff --git a/app/views/layouts/standard.html.haml b/app/views/layouts/standard.html.haml
index e6be1565..0dd31fd1 100644
--- a/app/views/layouts/standard.html.haml
+++ b/app/views/layouts/standard.html.haml
@@ -1,30 +1,10 @@
-!!! 5
-%html
- %head
- %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"}
- %title= h(SITE_TITLE) + ' | ' + h(@page_title)
- = stylesheet_link_tag "quorum"
- = yield :head
- = yield :javascript
- = @extra_headers
+- content_for :content do
+ %p#navbar
+ != ([content_tag(:span, h(SITE_TITLE), :class => :siteName)] + [link_to(h(_("List events")), events_path), link_to(h(_("Add event")), new_event_path), link_to(h(_("User profile")), profile_path), link_to(h(_("Create calendar")), new_calendar_path), link_to(h(_("Subscriptions")), subscriptions_path), current_user.admin? ? link_to(h(_("Admin tools")), url_for(:controller => :admin, :action => :index)) : nil, link_to(h(_("Log out")), logout_path)].compact).join(' | ')
+ #content
+ %p!= _("Welcome, %{user}!") % {:user => content_tag(:span, h(current_user), :class => :name)}
+ = render partial: 'layouts/flash', locals: {flash: flash}
+ %h1= @page_title
+ = yield
- %body
- %p#navbar
- != ([content_tag(:span, h(SITE_TITLE), :class => :siteName)] + [link_to(h(_("List events")), events_path), link_to(h(_("Add event")), new_event_path), link_to(h(_("User profile")), profile_path), link_to(h(_("Create calendar")), new_calendar_path), link_to(h(_("Subscriptions")), subscriptions_path), current_user.admin? ? link_to(h(_("Admin tools")), url_for(:controller => :admin, :action => :index)) : nil, link_to(h(_("Log out")), logout_path)].compact).join(' | ')
- #content
- %p!= _("Welcome, %{user}!") % {:user => content_tag(:span, h(current_user), :class => :name)}
- - if !flash[:notice].nil?
- #flash.notice
- %p= flash[:notice]
- - if !flash[:warning].nil?
- #flash.warning
- %p= flash[:warning]
- - if !flash[:error].nil?
- #flash.error
- %p= flash[:error]
- %h1= @page_title
- = yield
-
- %p.footer
- %a{:href => "http://validator.w3.org/check?uri=referer"}
- %img{:src => "http://www.w3.org/Icons/valid-html401", :alt => "Valid XHTML 4.01 Transitional", :height => "31", :width => "88"}
\ No newline at end of file
+= render template: 'layouts/application'
\ No newline at end of file
diff --git a/app/views/layouts/unauthenticated.html.haml b/app/views/layouts/unauthenticated.html.haml
index 7cbd5458..92711abe 100644
--- a/app/views/layouts/unauthenticated.html.haml
+++ b/app/views/layouts/unauthenticated.html.haml
@@ -1,20 +1,5 @@
-!!! 5
-%html
- %head
- %meta{:'http-equiv' => "content-type", :content => "text/html;charset=utf-8"}
- %title= h(SITE_TITLE) + ' | ' + h(@page_title)
- = stylesheet_link_tag "quorum"
- = @extra_headers
+- content_for :content do
+ = render partial: 'layouts/flash', locals: {flash: flash}
+ = yield
- %body
- - if !flash[:notice].nil?
- .flash_notice= flash[:notice]
- - if !flash[:warning].nil?
- .flash_warning= flash[:warning]
- - if !flash[:error].nil?
- .flash_error= flash[:error]
- = yield
-
- %p.footer
- %a{:href => "http://validator.w3.org/check?uri=referer"}
- %img{:src => "http://www.w3.org/Icons/valid-xhtml10", :alt => "Valid XHTML 1.0 Transitional", :height => "31", :width => "88"}
\ No newline at end of file
+= render template: 'layouts/application'
diff --git a/ci/before_script.sh b/ci/before_script.sh
new file mode 100755
index 00000000..4d981aea
--- /dev/null
+++ b/ci/before_script.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+./ci/install_postgis.sh
+./ci/create_config_files.sh
diff --git a/ci/create_config_files.sh b/ci/create_config_files.sh
new file mode 100755
index 00000000..756d160a
--- /dev/null
+++ b/ci/create_config_files.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env sh
+warn () {
+ printf >&2 "$*"
+}
+
+warn 'Setting up config files... '
+cat >config/database.yml <config/initializers/secret_token.rb 2>/dev/null <&2 'Installing PostGIS...'
+sudo apt-add-repository -y ppa:sharpie/for-science
+sudo apt-add-repository -y ppa:sharpie/postgis-nightly
+sudo apt-get update
+sudo apt-get install postgresql-9.1-postgis -q
+echo >&2 'PostGIS installed!'
diff --git a/config.ru b/config.ru
new file mode 100644
index 00000000..1ef9ccea
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Quorum2::Application
diff --git a/config/database-orig.yml b/config/database-orig.yml
index a618c890..5f35ddcb 100644
--- a/config/database-orig.yml
+++ b/config/database-orig.yml
@@ -4,7 +4,7 @@
# CONFIG: replace PASSWORD with an appropriate password.
development:
- adapter: postgresql
+ adapter: postgis
database: quorum2_dev
username: quorum2_dev
password: PASSWORD
@@ -15,7 +15,7 @@ development:
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test: &test
- adapter: postgresql
+ adapter: postgis
database: quorum2_test
username: quorum2_test
password: PASSWORD
@@ -23,11 +23,11 @@ test: &test
template: template_postgis
production:
- adapter: postgresql
+ adapter: postgis
database: quorum2
username: quorum2
password: PASSWORD
encoding: UTF8
template: template_postgis
-
+
cucumber: *test
diff --git a/config/deploy-orig.rb b/config/deploy-orig.rb
index fc68f491..e047e31f 100644
--- a/config/deploy-orig.rb
+++ b/config/deploy-orig.rb
@@ -21,9 +21,9 @@
set :remote, 'origin'
=begin
-set :scm_password, Proc.new { Capistrano::CLI.password_prompt("SVN
-password for #{scm_user}, please: ") }
-set :repository, Proc.new { "--username #{scm_user} --password
+set :scm_password, Proc.new { Capistrano::CLI.password_prompt("SVN
+password for #{scm_user}, please: ") }
+set :repository, Proc.new { "--username #{scm_user} --password
#{scm_password} --no-auth-cache #{repository}" }
=end
@@ -36,17 +36,19 @@
set :runner, "capistrano" # might want to change this
set :use_sudo, false
+set :migrate_target, :current
+
after 'deploy:update_code', 'deploy:remove_unnecessary_files', 'deploy:tag'
namespace :deploy do
-
+
# CONFIG: Comment out task :restart block unless you're using Phusion Passenger -- it won't work with other servers
desc 'Restart the application server.'
task :restart, :roles => :app do
run "if test ! -d #{current_path}/tmp; then mkdir #{current_path}/tmp; fi"
run "/usr/bin/touch #{current_path}/tmp/restart.txt"
end
-
+
desc 'Remove shared files and image sources.'
task :remove_unnecessary_files, :roles => :app do
# Remove some unversioned YAML config files and link to shared directory.
@@ -58,11 +60,11 @@
# Remove image source files.
run "rm -rf #{rpath}/public/images/sources"
-
+
#run "chown www-data #{current_path}/config/environment.rb"
end
-
+
# From http://stackoverflow.com/questions/5735656/tagging-release-before-deploying-with-capistrano
desc 'Tags deployed release with a unique Git tag.'
task :tag do
diff --git a/config/initializers/action_mailer_patch.rb b/config/initializers/action_mailer_patch.rb
new file mode 100644
index 00000000..1789b23f
--- /dev/null
+++ b/config/initializers/action_mailer_patch.rb
@@ -0,0 +1,14 @@
+# Security patch from http://seclists.org/oss-sec/2013/q4/118
+# TODO: remove when upgrading to Rails 3.2 or higher.
+
+require 'action_mailer'
+
+module ActionMailer
+ class LogSubscriber < ActiveSupport::LogSubscriber
+ def deliver(event)
+ recipients = Array.wrap(event.payload[:to]).join(', ')
+ info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
+ debug(event.payload[:mail])
+ end
+ end
+end
\ No newline at end of file
diff --git a/config/initializers/app_globals.rb b/config/initializers/app_globals.rb
index c4a4d17b..4837bfad 100644
--- a/config/initializers/app_globals.rb
+++ b/config/initializers/app_globals.rb
@@ -5,14 +5,18 @@
require 'yaml'
APP_CONFIG = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]
+APP_VERSION = '0.5.11'
+
SITE_TITLE = "Quorum" # Name of site as it appears in element
+APP_HOME_PAGE = 'http://quorum2.sourceforge.net'
+
DOMAIN = APP_CONFIG['domain'] # Domain on which the site is hosted
EMAIL = APP_CONFIG['email'] # Address that application-generated e-mail will come from.
-GeoRuby::SimpleFeatures::DEFAULT_SRID = 4326
-
-Time::DATE_FORMATS.merge :ical => "%Y%m%d" # yyyymmdd, for iCal conversion
+Date::DATE_FORMATS[:ical] = "%Y%m%d" # yyyymmdd, for iCal conversion
FONT_ROOT = "#{RAILS_ROOT}/fonts/dejavu-fonts-ttf-2.26/ttf"
+
+GMAPS_API_KEY = YAML.load_file(File.join Rails.root, 'config', 'gmaps_api_key.yml')[Rails.env]
diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb
index 8b3f3fd3..22477257 100644
--- a/config/initializers/haml.rb
+++ b/config/initializers/haml.rb
@@ -1,4 +1,10 @@
# coding: UTF-8
Haml::Template.options[:format] = :xhtml
-# We're still using HTML 5 for all actual HTML content, but since we need to generate XML as well, this is the easiest way.
\ No newline at end of file
+# We're still using HTML 5 for all actual HTML content, but since we need to generate XML as well, this is the easiest way.
+
+Haml::Filters::Markdown.module_eval do
+ def render(text)
+ RDiscount.new(text, :autolink).to_html
+ end
+end
diff --git a/db/migrate/20090508200432_restore_srid.rb b/db/migrate/20090508200432_restore_srid.rb
index acd8f8f3..13caaf3d 100644
--- a/db/migrate/20090508200432_restore_srid.rb
+++ b/db/migrate/20090508200432_restore_srid.rb
@@ -2,7 +2,7 @@
class RestoreSrid < ActiveRecord::Migration
def self.up
- srid = GeoRuby::SimpleFeatures::DEFAULT_SRID.to_i
+ srid = 4326
['events', 'users'].each do |table|
change_srid(table, srid)
end
@@ -16,7 +16,7 @@ def self.down
end
private
-
+
def self.change_srid(table, srid = -1)
execute "select updategeometrysrid('#{table}', 'coords', #{srid})"
execute "select setsrid(coords, #{srid}) from #{table}"
diff --git a/db/migrate/20120308160523_add_comment_to_commitments.rb b/db/migrate/20120308160523_add_comment_to_commitments.rb
new file mode 100644
index 00000000..20c027d9
--- /dev/null
+++ b/db/migrate/20120308160523_add_comment_to_commitments.rb
@@ -0,0 +1,9 @@
+class AddCommentToCommitments < ActiveRecord::Migration
+ def self.up
+ add_column :commitments, :comment, :text
+ end
+
+ def self.down
+ remove_column :commitments, :comment
+ end
+end
diff --git a/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb b/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb
new file mode 100644
index 00000000..611edca7
--- /dev/null
+++ b/db/migrate/20130803025446_change_geometry_columns_to_new_format.rb
@@ -0,0 +1,24 @@
+class ChangeGeometryColumnsToNewFormat < ActiveRecord::Migration
+ def self.up
+ classes.collect(&:table_name).each do |table|
+ remove_column table, :coords
+ add_column table, :coords, :point, geographic: true, srid: 4326
+ end
+ classes.each do |klass|
+ klass.all.each &:save!
+ end
+ end
+
+ def self.down
+ classes.collect(&:table_name).each do |table|
+ remove_column table, :coords
+ add_column table, :coords, :point, srid: 4326
+ end
+ end
+
+ private
+
+ def self.classes
+ [User, Event]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 692a734a..11788470 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1,101 +1,102 @@
-# coding: UTF-8
-
-# This file is auto-generated from the current state of the database. Instead of editing this file,
-# please use the migrations feature of Active Record to incrementally modify your database, and
-# then regenerate this schema definition.
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
#
-# Note that this schema.rb definition is the authoritative source for your database schema. If you need
-# to create the application database on another system, you should be using db:schema:load, not running
-# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20091020154940) do
+ActiveRecord::Schema.define(:version => 20130803025446) do
create_table "calendars", :force => true do |t|
- t.column "name", :string, :null => false
- t.column "created_at", :timestamp
- t.column "updated_at", :timestamp
+ t.string "name", :null => false
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
create_table "commitments", :force => true do |t|
- t.column "event_id", :integer, :null => false
- t.column "user_id", :integer, :null => false
- t.column "status", :boolean
+ t.integer "event_id", :null => false
+ t.integer "user_id", :null => false
+ t.boolean "status"
+ t.text "comment"
end
create_table "countries", :force => true do |t|
- t.column "code", :string, :limit => 2, :null => false
- t.column "name", :string, :null => false
+ t.string "code", :limit => 2, :null => false
+ t.string "name", :null => false
end
create_table "events", :force => true do |t|
- t.column "name", :string
- t.column "date", :date
- t.column "site", :string
- t.column "street", :string
- t.column "street2", :string
- t.column "city", :string
- t.column "state_id", :integer
- t.column "zip", :string
- t.column "created_at", :timestamp
- t.column "updated_at", :timestamp
- t.column "created_by_id", :integer
- t.column "deleted", :boolean
- t.column "description", :text
- t.column "calendar_id", :integer, :null => false
- t.column "coords", :point, :srid => 4326
+ t.string "name"
+ t.date "date"
+ t.string "site"
+ t.string "street"
+ t.string "street2"
+ t.string "city"
+ t.integer "state_id"
+ t.string "zip"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "created_by_id"
+ t.boolean "deleted"
+ t.text "description"
+ t.integer "calendar_id", :null => false
+ t.spatial "coords", :limit => {:srid=>4326, :type=>"point", :geographic=>true}
end
create_table "permissions", :force => true do |t|
- t.column "user_id", :integer, :null => false
- t.column "calendar_id", :integer, :null => false
- t.column "role_id", :integer, :null => false
- t.column "show_in_report", :boolean, :default => true, :null => false
+ t.integer "user_id", :null => false
+ t.integer "calendar_id", :null => false
+ t.integer "role_id", :null => false
+ t.boolean "show_in_report", :default => true, :null => false
end
add_index "permissions", ["user_id", "calendar_id", "role_id"], :name => "index_permissions_on_user_id_and_calendar_id_and_role_id", :unique => true
create_table "roles", :force => true do |t|
- t.column "name", :string
- t.column "created_at", :timestamp
- t.column "updated_at", :timestamp
+ t.string "name"
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
create_table "states", :force => true do |t|
- t.column "country_id", :integer
- t.column "code", :string, :limit => 10, :null => false
- t.column "name", :string, :null => false
+ t.integer "country_id"
+ t.string "code", :limit => 10, :null => false
+ t.string "name", :null => false
end
create_table "users", :force => true do |t|
- t.column "email", :string
- t.column "crypted_password", :string, :limit => 128, :default => "", :null => false
- t.column "password_salt", :string, :limit => 128, :default => "", :null => false
- t.column "firstname", :string
- t.column "lastname", :string
- t.column "street", :string
- t.column "street2", :string
- t.column "city", :string
- t.column "state_id", :integer
- t.column "zip", :string
- t.column "activated_at", :timestamp
- t.column "created_at", :timestamp
- t.column "updated_at", :timestamp
- t.column "show_contact", :boolean, :default => true
- t.column "single_access_token", :string, :limit => 32, :null => false
- t.column "coords", :point, :srid => 4326
- t.column "login_count", :integer, :default => 0, :null => false
- t.column "failed_login_count", :integer, :default => 0, :null => false
- t.column "last_request_at", :timestamp
- t.column "current_login_at", :timestamp
- t.column "last_login_at", :timestamp
- t.column "current_login_ip", :string
- t.column "last_login_ip", :string
- t.column "persistence_token", :string, :default => "", :null => false
- t.column "perishable_token", :string, :default => "", :null => false
- t.column "active", :boolean, :default => false, :null => false
+ t.string "email"
+ t.string "crypted_password", :limit => 128, :default => "", :null => false
+ t.string "password_salt", :limit => 128, :default => "", :null => false
+ t.string "firstname"
+ t.string "lastname"
+ t.string "street"
+ t.string "street2"
+ t.string "city"
+ t.integer "state_id"
+ t.string "zip"
+ t.datetime "activated_at"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.boolean "show_contact", :default => true
+ t.string "single_access_token", :limit => 32, :null => false
+ t.integer "login_count", :default => 0, :null => false
+ t.integer "failed_login_count", :default => 0, :null => false
+ t.datetime "last_request_at"
+ t.datetime "current_login_at"
+ t.datetime "last_login_at"
+ t.string "current_login_ip"
+ t.string "last_login_ip"
+ t.string "persistence_token", :default => "", :null => false
+ t.string "perishable_token", :default => "", :null => false
+ t.boolean "active", :default => false, :null => false
+ t.spatial "coords", :limit => {:srid=>4326, :type=>"point", :geographic=>true}
end
add_index "users", ["perishable_token"], :name => "index_users_on_perishable_token"
diff --git a/features/calendars/administer_calendars.feature b/features/calendars/administer_calendars.feature
index 6424fd79..1403d12e 100644
--- a/features/calendars/administer_calendars.feature
+++ b/features/calendars/administer_calendars.feature
@@ -2,30 +2,30 @@ Feature: Administer calendars
In order to keep calendars properly organized
any administrator should be able to
administer the calendars ey controls.
-
+
Scenario: Non-admin users should not see "Admin tools" link
Given I am logged in
And I am subscribed to "Calendar 1"
And I am on the homepage
Then I should not see "Admin tools"
-
+
Scenario Outline: Non-admin users should not be able to get to calendar admin pages
Given I am logged in
And I am subscribed to ""
When I go to the for ""
Then I should not be on the for ""
-
+
Examples:
| calendar | page |
| Calendar 1 | user list |
| Calendar 2 | calendar edit page |
-
+
Scenario: Admin users should see "Admin tools" link
Given I am logged in
And I am an admin of "Calendar 1"
And I am on the homepage
Then I should see "Admin tools"
-
+
Scenario: Admin users should only be able to administer calendars they control
Given I am logged in
And I am an admin of "My calendar"
@@ -35,7 +35,7 @@ Feature: Administer calendars
Then I should be on the admin page
And I should see "My calendar"
And I should not see "Someone else's calendar"
-
+
Scenario: Admin users should be able to change the name of calendars they control
Given I am logged in
And I am an admin of "My calendar"
@@ -46,7 +46,7 @@ Feature: Administer calendars
Then I should be on the admin page
And I should not see /My calendar\s*\(properties \| users\)/
And I should see /New name\s*\(properties \| users\)/
-
+
Scenario: Admin users should be able to see user lists for calendars they control
Given I am logged in
And I am an admin of "My calendar"
diff --git a/features/commitments/add_comment_to_commitment.feature b/features/commitments/add_comment_to_commitment.feature
new file mode 100644
index 00000000..d38cf2aa
--- /dev/null
+++ b/features/commitments/add_comment_to_commitment.feature
@@ -0,0 +1,47 @@
+Feature: Add comment to commitment
+ As a user
+ I can add comments to events
+ So I can qualify my commitment status
+
+ Background:
+ Given someone else has an event called "Event" in "Calendar 1"
+
+ Scenario Outline: Add comment to commitment
+ Given a user named "" exists with email ""
+ And I am logged in as ""
+ And I am subscribed to "Calendar 1"
+ When I go to the event list
+ And I fill in "comment" with ""
+ And I press "Change status"
+ Then I should see ": "
+
+ Examples:
+ | email | name | comment |
+ | john@smith.com | John Smith | This is my comment text. |
+
+ Scenario Outline: View other users' comments
+ Given someone else has a comment for "Event" in "Calendar 1" with text ""
+ And I am logged in
+ And I am subscribed to "Calendar 1"
+ When I go to the event list
+ Then I should see ""
+
+ Examples:
+ | comment |
+ | This is someone else's comment. |
+
+ Scenario Outline: Show attendance status on comments
+ Given a user named "John Doe" exists with email "john@doe.org"
+ And I am logged in as "john@doe.org"
+ And I am subscribed to "Calendar 1"
+ When I go to the event list
+ And I select "" from "status"
+ And I fill in "comment" with "text"
+ And I press "Change status"
+ Then I should see "John Doe:" within a user comment
+
+ Examples:
+ | status |
+ | attending |
+ | not attending |
+ | uncommitted |
diff --git a/features/commitment.feature b/features/commitments/update_commitment_status.feature
similarity index 100%
rename from features/commitment.feature
rename to features/commitments/update_commitment_status.feature
diff --git a/features/events/event_map.feature b/features/events/event_map.feature
index 9385132d..2d1ef6d1 100644
--- a/features/events/event_map.feature
+++ b/features/events/event_map.feature
@@ -2,7 +2,7 @@ Feature: Event map
As a registered user
I can see a map of any event on calendars I subscribe to
So I can figure out how to get there
-
+
Scenario:
Given I am logged in
And an event exists
diff --git a/features/events/list_events.feature b/features/events/list_events.feature
index 71ca7296..34df7f3e 100644
--- a/features/events/list_events.feature
+++ b/features/events/list_events.feature
@@ -2,12 +2,12 @@ Feature: List events
As a registered user
I can see events on calendars I subscribe to
So I can keep track of what's going on
-
+
Scenario: Unregistered users can't get to event list
Given I am not logged in
When I go to the events page
Then I should be on the login page
-
+
Scenario Outline: It should sort events by date
Given I am logged in
And I am subscribed to ""
@@ -21,18 +21,31 @@ Feature: List events
| October |
| November |
| December |
-
+
Examples:
| calendar |
| My Amazing Calendar |
-
+
Scenario Outline: It should not show deleted events
Given I am logged in
And I am subscribed to ""
And a deleted event exists with name: "", calendar: the calendar
When I go to the events page
Then I should not see ""
-
+
Examples:
| calendar | deleted |
- | Deletion test | I'm invisible! |
\ No newline at end of file
+ | Deletion test | I'm invisible! |
+
+ Scenario Outline: It should linkify URLs in event descriptions
+ Given I am logged in
+ And I am subscribed to "My Calendar"
+ And the following events exist:
+ | calendar | description |
+ | the calendar | I have a link to here. |
+ When I go to the events page
+ Then I should see a link to ""
+
+ Examples:
+ | url |
+ | http://www.example.com |
\ No newline at end of file
diff --git a/features/manage_subscriptions.feature b/features/manage_subscriptions.feature
index ffbdc020..755fcebe 100644
--- a/features/manage_subscriptions.feature
+++ b/features/manage_subscriptions.feature
@@ -2,60 +2,59 @@ Feature: Manage subscriptions
In order to control what content ey sees
any registered user should be able to
subscribe to and unsubscribe from any calendar of which ey is not an admin.
-
- Scenario Outline: Anyone can subscribe to a calendar
+
+ Background:
Given I am logged in
- And someone else has a calendar called ""
+ And no calendars exist
+ # TODO: why do we need that?
+
+ Scenario Outline: Anyone can subscribe to a calendar
+ Given someone else has a calendar called ""
And I am on the homepage
When I follow "Subscriptions"
Then I should see ""
And I should see the word "subscribe"
When I follow "subscribe"
Then I should be subscribed to ""
-
+
Examples:
| calendar |
| Someone else's calendar |
-
+
Scenario: Don't show the list of unsubscribed calendars if it's empty
- Given I am logged in
- And I am on the subscriptions page
+ Given I am on the subscriptions page
Then I should not see "subscribe to these calendars"
-
+
Scenario Outline: Non-admin users can unsubscribe from their calendars
- Given I am logged in
- And I am subscribed to ""
+ Given I am subscribed to ""
And I am on the homepage
When I follow "Subscriptions"
Then I should see ""
And I should see the word "unsubscribe"
When I follow "unsubscribe"
Then I should not be subscribed to ""
-
+
Examples:
| calendar |
| My calendar |
Scenario: Admin users cannot unsubscribe from calendars they control
- Given I am logged in
- And I am an admin of "My calendar"
+ Given I am an admin of "My calendar"
And I am on the homepage
When I follow "Subscriptions"
Then I should not see the word "unsubscribe"
-
+
Scenario: Admin users can unsubscribe from calendars they do not control
- Given I am logged in
- And I am an admin of "My calendar"
+ Given I am an admin of "My calendar"
And I am subscribed to "Someone else's calendar"
And I am on the homepage
When I follow "Subscriptions"
Then I should see the following in order:
| unsubscribe |
| Someone else's calendar |
-
+
Scenario Outline: I should see the role for each subscribed calendar
- Given I am logged in
- And I am an admin of ""
+ Given I am an admin of ""
And I am subscribed to ""
And I am on the subscriptions page
Then I should see the following in order:
@@ -64,7 +63,7 @@ Feature: Manage subscriptions
And I should see the following in order:
| |
| user |
-
+
Examples:
| mine | other |
| My calendar | Someone Else's Calendar |
diff --git a/features/step_definitions/calendar_steps.rb b/features/step_definitions/calendar_steps.rb
index 4d816af0..52c2e1d1 100644
--- a/features/step_definitions/calendar_steps.rb
+++ b/features/step_definitions/calendar_steps.rb
@@ -5,22 +5,26 @@
user = User.current_user
else
names = user.gsub(/^"|"$/, '').split(' ', 2)
- user = Factory :user, :firstname => names.first, :lastname => names.last
+ user = FactoryGirl.create :user, :firstname => names.first, :lastname => names.last
end
- cal = create_model(:calendar, :name => calendar)
+ cal = fetch_calendar calendar
Permission.destroy(cal.permissions.find_all_by_user_id(user.id).collect(&:id)) # make sure we don't have any superfluous admin permissions hanging around
FactoryGirl.create :permission, :user => user, :calendar => cal
end
Given /^I am an admin(?:istrator)? of "([^\"]*)"$/ do |calendar|
- cal = Calendar.find_by_name(calendar) || FactoryGirl.create(:calendar, :name => calendar)
+ cal = fetch_calendar calendar
FactoryGirl.create :permission, :user => UserSession.find.record, :calendar => cal, :role => FactoryGirl.create(:admin_role)
end
Given /^someone else has a calendar called "([^\"]*)"$/ do |calendar|
- cal = Calendar.find_by_name(calendar) || FactoryGirl.create(:calendar, :name => calendar)
+ cal = fetch_calendar calendar
Permission.destroy(cal.permissions.find_all_by_user_id(User.current_user.id).collect(&:id)) # make sure we don't have any superfluous admin permissions hanging around
- Factory :admin_permission, :calendar => cal
+ FactoryGirl.create :admin_permission, :calendar => cal
+end
+
+Given /^no calendars exist$/ do
+ Calendar.destroy_all
end
Then /^I should have a calendar called "([^\"]*)"$/ do |calendar|
@@ -29,17 +33,13 @@
Then /^I should be an admin(?:istrator)? of "([^\"]*)"$/ do |calendar|
admin = Role.find_or_create_by_name('admin')
- cal = Calendar.find_by_name(calendar)
+ cal = fetch_calendar calendar
User.current_user.permissions.find_by_calendar_id_and_role_id(cal.id, admin.id).should_not be_nil
end
Then /^I should (not )?be subscribed to "([^"]*)"$/ do |negation, calendar|
- calendar = Calendar.find_by_name(calendar)
- user = User.current_user
- permission = user.permissions.find_by_calendar_id(calendar)
- if negation
- permission.should be_nil
- else
- permission.should_not be_nil
+ visit subscriptions_path
+ within '.subscriptions' do
+ page.has_content?(calendar).should == !negation
end
end
\ No newline at end of file
diff --git a/features/step_definitions/comment_steps.rb b/features/step_definitions/comment_steps.rb
new file mode 100644
index 00000000..01dd32dc
--- /dev/null
+++ b/features/step_definitions/comment_steps.rb
@@ -0,0 +1,5 @@
+Given /^someone else has a comment for "([^"]*)" in "([^"]*)" with text "([^"]*)"$/ do |event, calendar, comment|
+ calendar = fetch_calendar calendar
+ event = fetch_event name: event, calendar: calendar
+ FactoryGirl.create :commitment, event: event, comment: comment
+end
\ No newline at end of file
diff --git a/features/step_definitions/event_steps.rb b/features/step_definitions/event_steps.rb
index fd92f6c4..970a0f7b 100644
--- a/features/step_definitions/event_steps.rb
+++ b/features/step_definitions/event_steps.rb
@@ -25,5 +25,8 @@
end
Then /^I should see a map of the event$/ do
- Then 'I should see an element matching "#map"'
+ event = model! :event
+ page.should have_selector '#map'
+ page.should have_selector '#lat', text: event.latitude.to_s
+ page.should have_selector '#lng', text: event.longitude.to_s
end
\ No newline at end of file
diff --git a/features/step_definitions/extra_web_steps.rb b/features/step_definitions/extra_web_steps.rb
index 2a9777a4..c8737a40 100644
--- a/features/step_definitions/extra_web_steps.rb
+++ b/features/step_definitions/extra_web_steps.rb
@@ -16,10 +16,14 @@
page.should_not have_selector(selector) and response.should_not have_xpath(selector)
end
+Then /^I should see a link to "([^\"]*)"$/ do |url|
+ page.should have_selector("a[href='#{url}']")
+end
+
Then /^I should see the following in order:$/ do |table|
# table is a Cucumber::Ast::Table
regexp = %r{#{table.raw.flatten.collect {|x| Regexp.escape x }.join '.*'}}m
-
+
page.should have_xpath('//*', :text => regexp)
end
diff --git a/features/step_definitions/user_steps.rb b/features/step_definitions/user_steps.rb
index e2675083..3b284fd2 100644
--- a/features/step_definitions/user_steps.rb
+++ b/features/step_definitions/user_steps.rb
@@ -1,12 +1,18 @@
# coding: UTF-8
+Given /^a user named "([^"]*)" exists with email "([^"]*)"$/ do |name, email|
+ first, last = name.split ' ', 2
+ FactoryGirl.create :user, firstname: first, lastname: last, email: email
+end
+
+Given /^I am logged in as "([^"]*)"$/ do |email|
+ user = User.find_by_email email
+ login_as user
+end
+
Given /^I am logged in$/ do
user = FactoryGirl.create :user, :password => 'passw0rd'
- visit login_path
- fill_in('user_session[email]', :with => user.email)
- fill_in('user_session[password]', :with => 'passw0rd')
- click_button 'Log in'
- UserSession.find.record.should == user
+ login_as user
end
Given /^I am not logged in$/ do
@@ -27,4 +33,14 @@
else
user.should_not be_nil
end
-end
\ No newline at end of file
+end
+
+private
+
+def login_as(user)
+ visit login_path
+ fill_in('user_session[email]', :with => user.email)
+ fill_in('user_session[password]', :with => 'passw0rd')
+ click_button 'Log in'
+ UserSession.find.record.should == user
+end
diff --git a/features/support/additional_cucumber_setup.rb b/features/support/additional_cucumber_setup.rb
index fc3b5f35..9a6bcef1 100644
--- a/features/support/additional_cucumber_setup.rb
+++ b/features/support/additional_cucumber_setup.rb
@@ -1,5 +1,11 @@
# coding: UTF-8
+begin
+ require 'ruby-debug'
+rescue LoadError
+ warn 'Failed to load ruby-debug; continuing without it...'
+end
+
# Set Gettext stuff so we can load Web pages.
FastGettext.text_domain ||= SITE_TITLE
FastGettext.available_locales ||= ['en']
\ No newline at end of file
diff --git a/features/support/fetch_helpers.rb b/features/support/fetch_helpers.rb
new file mode 100644
index 00000000..9ed43ea3
--- /dev/null
+++ b/features/support/fetch_helpers.rb
@@ -0,0 +1,14 @@
+module FetchHelpers
+ def fetch_calendar(name)
+ fields = {name: name}
+ args = [:calendar, fields]
+ find_model(*args) || create_model(*args)
+ end
+
+ def fetch_event(options)
+ calendar = options.delete :calendar
+ calendar.events.find_by_name(options[:name]) || FactoryGirl.create(:event, calendar: calendar)
+ end
+end
+
+World FetchHelpers
diff --git a/features/support/geocoder.rb b/features/support/geocoder.rb
new file mode 100644
index 00000000..50d07dc9
--- /dev/null
+++ b/features/support/geocoder.rb
@@ -0,0 +1,8 @@
+Geocoder.configure lookup: :test
+
+Geocoder::Lookup::Test.set_default_stub [
+ {
+ 'latitude' => 1.0,
+ 'longitude' => 2.0
+ }
+]
\ No newline at end of file
diff --git a/features/support/selectors.rb b/features/support/selectors.rb
index 52a7c707..d4587afd 100644
--- a/features/support/selectors.rb
+++ b/features/support/selectors.rb
@@ -25,6 +25,10 @@ def selector_for(locator)
# when /the header/
# [:xpath, "//header"]
+ when /^an? (.+) user comment$/
+ status = {'attending' => :yes, 'not attending' => :no, 'uncommitted' => :maybe}[$1]
+ ".comments .user.#{status}"
+
# This allows you to provide a quoted selector as the scope
# for "within" steps as was previously the default for the
# web steps:
diff --git a/lib/acts/geocoded.rb b/lib/acts/geocoded.rb
new file mode 100644
index 00000000..17e378b8
--- /dev/null
+++ b/lib/acts/geocoded.rb
@@ -0,0 +1,29 @@
+module Acts
+ module Geocoded
+ extend ActiveSupport::Concern
+
+ included do
+ acts_as_addressed
+ geocoded_by :address_for_geocoding do |model, results|
+ if result = results.first
+ model.coords = model.class.rgeo_factory_for_column(:coords).point result.longitude, result.latitude
+ end
+ end
+ after_validation :geocode
+ end
+
+ module InstanceMethods
+ private
+
+ def address_for_geocoding
+ address.to_s :geo
+ end
+ end
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ def self.acts_as_geocoded
+ include Acts::Geocoded
+ end
+end
\ No newline at end of file
diff --git a/lib/geocoding_utilities.rb b/lib/geocoding_utilities.rb
deleted file mode 100644
index 49351a8c..00000000
--- a/lib/geocoding_utilities.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# coding: UTF-8
-
-module GeocodingUtilities
- def self.included(klass)
- klass.before_update :clear_coords
- end
-
- # Returns a #Point with the coordinates of the model's address, or with (0, 0) if all else fails, and caches the coordinates so we don't hit the geocoder every time.
- def coords
- c = self[:coords]
- if c.nil?
- begin
- c = coords_from_string(address.to_s(:geo))
- self[:coords] = c
- self.save
- rescue
- c = Point.from_x_y(0, 0)
- end
- end
- c
- end
-
- # Sends the address contained in _string_ to a geocoder, and returns a #Point object with the resulting coordinates.
- #
- # _String_ is assumed to be in the format output by #Address#to_s(:geo)
- # (currently "#{street}, #{city}, #{state.code}, #{zip}, #{country.code}"),
- # but depending on the geocoder, other string formats are likely to work as well.
- def coords_from_string(string)
- host = @request.nil? ? nil : @request.host_with_port
- host ||= DOMAIN
- geo = Geocoding::get(string, :host => host)
- if geo.status == Geocoding::GEO_SUCCESS
- return Point.from_coordinates(geo[0].lonlat)
- else
- raise "Geocoding failed with code #{geo.status} for #{string}"
- end
- end
-
- # Clears the coordinates of the model so they will refresh themselves next time Model.coords is called
- def clear_coords
- self.coords = nil
- end
-end
\ No newline at end of file
diff --git a/lib/tasks/ci.rake b/lib/tasks/ci.rake
new file mode 100644
index 00000000..6d3b82df
--- /dev/null
+++ b/lib/tasks/ci.rake
@@ -0,0 +1,15 @@
+namespace :db do
+ desc 'Install PostGIS in the database'
+ task install_postgis: :environment do
+ ActiveRecord::Base.connection.execute 'CREATE EXTENSION postgis;'
+ end
+
+ desc 'Create database and load PostGIS and schema'
+ task setup_with_postgis: %w(db:create db:install_postgis db:schema:load)
+end
+
+desc 'Run CI tests (intended for Travis)'
+task ci: 'db:setup_with_postgis' do
+ sh 'bundle exec rspec -O .rspec.travis'
+ sh 'bundle exec cucumber'
+end
\ No newline at end of file
diff --git a/lib/tasks/databases.rake b/lib/tasks/databases.rake
deleted file mode 100644
index dc8988d0..00000000
--- a/lib/tasks/databases.rake
+++ /dev/null
@@ -1,41 +0,0 @@
-# coding: UTF-8
-
-namespace :db do
- namespace :test do
- desc "Empty the test database"
- task :purge => :environment do
- abcs = ActiveRecord::Base.configurations
- case abcs["test"]["adapter"]
- when "mysql"
- ActiveRecord::Base.establish_connection(:test)
- ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"])
- when "postgresql"
- ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"]
- ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"]
- ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
- enc_option = "-E #{abcs["test"]["encoding"]}" if abcs["test"]["encoding"]
-
- ActiveRecord::Base.clear_active_connections!
- `dropdb -U "#{abcs["test"]["username"]}" #{abcs["test"]["database"]}`
- `createdb #{enc_option} -U "#{abcs["test"]["username"]}" -T "#{abcs["test"]["template"]}" #{abcs["test"]["database"]}`
- when "sqlite","sqlite3"
- dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
- File.delete(dbfile) if File.exist?(dbfile)
- when "sqlserver"
- dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-')
- `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}`
- `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql`
- when "oci", "oracle"
- ActiveRecord::Base.establish_connection(:test)
- ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
- ActiveRecord::Base.connection.execute(ddl)
- end
- when "firebird"
- ActiveRecord::Base.establish_connection(:test)
- ActiveRecord::Base.connection.recreate_database!
- else
- raise "Task not supported by '#{abcs["test"]["adapter"]}'"
- end
- end
- end
-end
\ No newline at end of file
diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake
deleted file mode 100644
index 0e01578a..00000000
--- a/lib/tasks/rspec.rake
+++ /dev/null
@@ -1,146 +0,0 @@
-# coding: UTF-8
-
-gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9
-rspec_gem_dir = nil
-Dir["#{RAILS_ROOT}/vendor/gems/*"].each do |subdir|
- rspec_gem_dir = subdir if subdir.gsub("#{RAILS_ROOT}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb")
-end
-rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec')
-
-if rspec_gem_dir && (test ?d, rspec_plugin_dir)
- raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n"
-end
-
-if rspec_gem_dir
- $LOAD_PATH.unshift("#{rspec_gem_dir}/lib")
-elsif File.exist?(rspec_plugin_dir)
- $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib")
-end
-
-# Don't load rspec if running "rake gems:*"
-unless ARGV.any? {|a| a =~ /^gems/}
-
-begin
- require 'spec/rake/spectask'
-rescue MissingSourceFile
- module Spec
- module Rake
- class SpecTask
- def initialize(name)
- task name do
- # if rspec-rails is a configured gem, this will output helpful material and exit ...
- require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment"))
-
- # ... otherwise, do this:
- raise <<-MSG
-
-#{"*" * 80}
-* You are trying to run an rspec rake task defined in
-* #{__FILE__},
-* but rspec can not be found in vendor/gems, vendor/plugins or system gems.
-#{"*" * 80}
-MSG
- end
- end
- end
- end
- end
-end
-
-Rake.application.instance_variable_get('@tasks').delete('default')
-
-spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
-task :noop do
-end
-
-task :default => :spec
-task :stats => "spec:statsetup"
-
-desc "Run all specs in spec directory (excluding plugin specs)"
-Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
- t.spec_files = FileList['spec/**/*_spec.rb']
-end
-
-namespace :spec do
- desc "Run all specs in spec directory with RCov (excluding plugin specs)"
- Spec::Rake::SpecTask.new(:rcov) do |t|
- t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
- t.spec_files = FileList['spec/**/*_spec.rb']
- t.rcov = true
- t.rcov_opts = lambda do
- IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
- end
- end
-
- desc "Print Specdoc for all specs (excluding plugin specs)"
- Spec::Rake::SpecTask.new(:doc) do |t|
- t.spec_opts = ["--format", "specdoc", "--dry-run"]
- t.spec_files = FileList['spec/**/*_spec.rb']
- end
-
- desc "Print Specdoc for all plugin examples"
- Spec::Rake::SpecTask.new(:plugin_doc) do |t|
- t.spec_opts = ["--format", "specdoc", "--dry-run"]
- t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*')
- end
-
- [:models, :controllers, :views, :helpers, :lib, :integration].each do |sub|
- desc "Run the code examples in spec/#{sub}"
- Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
- t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
- end
- end
-
- desc "Run the code examples in vendor/plugins (except RSpec's own)"
- Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t|
- t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
- t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*")
- end
-
- namespace :plugins do
- desc "Runs the examples for rspec_on_rails"
- Spec::Rake::SpecTask.new(:rspec_on_rails) do |t|
- t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
- t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb']
- end
- end
-
- # Setup specs for stats
- task :statsetup do
- require 'code_statistics'
- ::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models')
- ::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views')
- ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers')
- ::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers')
- ::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib')
- ::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing')
- ::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration')
- ::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models')
- ::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views')
- ::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers')
- ::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers')
- ::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib')
- ::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing')
- ::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration')
- end
-
- namespace :db do
- namespace :fixtures do
- desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z."
- task :load => :environment do
- ActiveRecord::Base.establish_connection(Rails.env)
- base_dir = File.join(Rails.root, 'spec', 'fixtures')
- fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
-
- require 'active_record/fixtures'
- (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
- Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*'))
- end
- end
- end
- end
-end
-
-end
diff --git a/public/javascripts/events/index.js b/public/javascripts/events/index.js
index ca11f7a2..06c929d9 100644
--- a/public/javascripts/events/index.js
+++ b/public/javascripts/events/index.js
@@ -13,6 +13,7 @@ function ajaxify_page() {
'form.attendance input[type=submit]': function () {
this.hide();
},
+ 'form.attendance textarea:blur': ajaxify_form,
'form.attendance select.commit:change': ajaxify_form
});
};
@@ -29,10 +30,11 @@ function ajaxify_form(event) {
var id = row.id;
row.replace(transport.responseText);
var newForm = $(id).down('form.attendance');
-
- // these two statements repeat ajaxify_page; can we do better?
+
+ // TODO: these statements repeat ajaxify_page; can we do better?
newForm.down('input[type=submit]').hide();
newForm.down('select.commit').observe('change', ajaxify_form);
+ newForm.down('textarea').observe('blur', ajaxify_form);
f = prepare_spinner.bind(newForm.down('.progress'));
f();
},
diff --git a/public/javascripts/events/map.js b/public/javascripts/events/map.js
index 6b665202..60306b93 100644
--- a/public/javascripts/events/map.js
+++ b/public/javascripts/events/map.js
@@ -1,4 +1,8 @@
-window.onload = addCodeToFunction(window.onload,function() {
+var oldOnload = window.onload;
+window.onload = function() {
+ if(oldOnload) {
+ oldOnload();
+ }
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
var lat = parseFloat(document.getElementById('lat').innerHTML);
@@ -6,12 +10,12 @@ window.onload = addCodeToFunction(window.onload,function() {
var latlng = new GLatLng(lat,lng);
var info = document.getElementById('info');
var marker = new GMarker(latlng);
-
+
map.setCenter(latlng,14);
marker.bindInfoWindow(info);
map.addOverlay(marker);
marker.openInfoWindow(info);map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
}
-});
+};
diff --git a/public/stylesheets/sass/_icon.scss b/public/stylesheets/sass/_icon.scss
new file mode 100644
index 00000000..50a27e8b
--- /dev/null
+++ b/public/stylesheets/sass/_icon.scss
@@ -0,0 +1,22 @@
+// size of icons from Silk set
+$silk_icon_size: 16px;
+
+// margin around icons
+$default_icon_margin: 3px;
+
+@mixin icon($icon_url, $icon_size: $silk_icon_size, $icon_margin: $default_icon_margin) {
+ background: {
+ image: $icon_url;
+ repeat: no-repeat; };
+ min-height: $icon_size + $icon_margin;
+ padding-left: $icon_size + $icon_margin; }
+
+@mixin attendance_icon($status) { // $status is 'yes', 'no', or 'maybe'
+ $attendance_icon_size: 12px;
+ $extension: png;
+ @if $status == maybe {
+ $extension: gif;
+ }
+ $icon_url: url("../images/attend_#{$status}.#{$extension}");
+ @include icon($icon_url, $attendance_icon_size);
+}
\ No newline at end of file
diff --git a/public/stylesheets/sass/quorum.scss b/public/stylesheets/sass/quorum.scss
index faab77e0..ddb9132f 100644
--- a/public/stylesheets/sass/quorum.scss
+++ b/public/stylesheets/sass/quorum.scss
@@ -1,10 +1,7 @@
-$title_blue: #3333ff;
-
-// size of icons from Silk set
-$silk_icon_size: 16px;
+@import "_icon";
-// margin around icons
-$default_icon_margin: 3px;
+$light_gray: #999999;
+$title_blue: #3333ff;
body {
color: black;
@@ -28,10 +25,6 @@ body {
weight: bolder;
size: 116%; }; }
-.footer {
- border-top: 1px solid black;
- padding-top: 2px; }
-
#navbar {
color: white;
background-color: $title_blue;
@@ -65,38 +58,34 @@ th {
size: 90%;
weight: lighter; }; }
-span.yes {
- color: lime;
- font-weight: bolder; }
-
-span.no {
- font-weight: bolder;
- color: red; }
-
-span.maybe {
- color: yellow;
- font-weight: bolder; }
-
-$circle_size: 12px;
-$attend_yes_url: url("../images/attend_yes.png");
-$attend_no_url: url("../images/attend_no.png");
-$attend_maybe_url: url("../images/attend_maybe.gif");
-
td.yes p {
- background-image: $attend_yes_url; }
+ @include attendance_icon(yes);
+}
td.no p {
- background-image: $attend_no_url; }
+ @include attendance_icon(no);
+}
td.maybe p {
- background-image: $attend_maybe_url; }
+ @include attendance_icon(maybe);
+}
-td.yes, td.no, td.maybe {
+td.attendance {
background-color: black;
color: white;
- p {
- padding-left: $circle_size + $default_icon_margin;
- background-repeat: no-repeat; } }
+
+ span.yes {
+ color: lime;
+ font-weight: bolder; }
+
+ span.no {
+ font-weight: bolder;
+ color: red; }
+
+ span.maybe {
+ color: yellow;
+ font-weight: bolder; }
+}
td.actions {
background-color: transparent;
@@ -105,14 +94,27 @@ td.actions {
a {
display: inline-block; } }
-// Toolbar links with icons
+.comments .user {
+ font-weight: bolder;
-@mixin icon($icon_url, $icon_size: $silk_icon_size, $icon_margin: $default_icon_margin) {
- background: {
- image: $icon_url;
- repeat: no-repeat; };
- min-height: $icon_size + $icon_margin;
- padding-left: $icon_size + $icon_margin; }
+ &.yes {
+ @include attendance_icon(yes);
+ }
+
+ &.no {
+ @include attendance_icon(no);
+ }
+
+ &.maybe {
+ @include attendance_icon(maybe);
+ }
+}
+
+td .count {
+ font-weight: bolder;
+}
+
+// Toolbar links with icons
$edit_icon_url: url("../images/pencil.png");
@@ -196,6 +198,20 @@ $error_icon_url: url("../images/exclamation.png");
p {
padding: 0.25em; } }
+#footer {
+ margin-top: 2em;
+ border-top: 1px solid $light_gray;
+ .version {
+ font-size: 90%;
+ color: $light_gray;
+ }
+}
+
+#map {
+ height: 400px;
+ width: 500px;
+}
+
/* Some hCalendar properties... */
abbr {
text-decoration: inherit; }
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 8e8f7007..40fb4102 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -9,17 +9,17 @@
UserSession.create false
controller.admin?.should be_nil
end
-
+
it "should return true if current user is an admin" do
- admin_role = Factory :admin_role
- Role.should_receive(:find_by_name).with('admin').and_return admin_role
- @admin = Factory(:user).tap {|u| u.permissions << Factory(:admin_permission, :user => u, :role => admin_role) }
+ admin_role = FactoryGirl.create :admin_role
+ Role.should_receive(:find_by_name).with('admin').at_least(:once).and_return admin_role
+ @admin = FactoryGirl.create(:user).tap {|u| u.permissions << FactoryGirl.create(:admin_permission, :user => u, :role => admin_role) }
UserSession.create @admin
controller.admin?.should be_true
end
-
+
it "should return false if current user is not an admin" do
- @user = Factory(:user).tap {|u| u.permissions << Factory(:permission, :user => u) }
+ @user = FactoryGirl.create(:user).tap {|u| u.permissions << FactoryGirl.create(:permission, :user => u) }
UserSession.create @user
controller.admin?.should be_false
end
diff --git a/spec/controllers/events_controller_spec.rb b/spec/controllers/events_controller_spec.rb
index 8cba4d22..2f962336 100644
--- a/spec/controllers/events_controller_spec.rb
+++ b/spec/controllers/events_controller_spec.rb
@@ -6,19 +6,17 @@
before(:each) do
UserSession.create FactoryGirl.create(:user)
end
-
+
it "should pass sorting parameters from the URL" do
order = 'name'
direction = 'desc'
params = {:order => order, :direction => direction}
{:get => "/events/index/#{order}/#{direction}"}.should route_to params.merge(:controller => 'events', :action => 'index')
- Event.should_receive(:find) do |arg1, arg2|
- arg1.should == :all
- arg2.should be_an_instance_of(Hash)
- arg2.should have_key(:order)
- arg2[:order].should == "#{order} #{direction}"
- arg2.should have_key(:conditions)
- conditions = arg2[:conditions]
+ mock_conditions = mock 'conditions'
+ mock_conditions.should_receive(:order).with "#{order} #{direction}"
+ mock_includes = mock 'includes'
+ Event.should_receive(:includes).and_return(mock_includes)
+ mock_includes.should_receive(:where) do |conditions|
conditions.should be_an_instance_of(Array)
conditions[0].should =~ /date >= :from_date/i
conditions[1].should be_an_instance_of(Hash)
@@ -27,24 +25,25 @@
conditions[1][:from_date].should == Time.zone.today # default value if not set in params
conditions[1].should have_key(:to_date)
conditions[1][:to_date].should be_nil
+ mock_conditions
end
get :index, params
=begin
TODO: when we have a search form, I suppose :)
-
+
If to_date is not nil, then we need the following specs for the monstrosity above:
conditions[0].should =~ /between :from_date and :to_date/i
conditions[1][:to_date].should be_an_instance_of(Date)
conditions[1][:to_date].should > Time.zone.today + 99.years
=end
end
-
+
it "should have date/asc as default order and direction in URL" do
pending "route_for doesn't actually seem to work this way" do
route_for(:controller => 'events', :action => 'index', :order => 'date', :direction => 'asc').should == 'foo' # '/events/index'
end
end
-
+
it "should pass sorting parameters on to the view" do
get :index
assigns[:order].should_not be_nil
@@ -54,42 +53,42 @@
describe EventsController, "feed.rss" do
render_views
-
+
before(:each) do
user = FactoryGirl.create :user
User.stub!(:current_user).and_return(user) # we need this for some of the callbacks on Calendar and Event
@calendar = FactoryGirl.create :calendar
+ FactoryGirl.create :permission, user: user, calendar: @calendar, role: FactoryGirl.create(:role)
@one = FactoryGirl.create :event, :name => 'Event 1', :calendar => @calendar, :date => Date.civil(2008, 7, 4), :description => 'The first event.', :created_at => 1.week.ago
@two = FactoryGirl.create :event, :name => 'Event 2', :calendar => @calendar, :date => Date.civil(2008, 10, 10), :description => 'The second event.', :created_at => 2.days.ago
@events = [@one, @two]
- controller.stub!(:current_objects).and_return(@events)
get :feed, :format => 'rss', :key => user.single_access_token
end
-
+
it "should be successful" do
response.should be_success
end
-
+
it "should set a MIME type of application/rss+xml" do
response.content_type.should =~ (%r{^application/rss\+xml})
end
-
+
it "should set RSS version 2.0 and declare the Atom namespace" do
m = response.body[%r{<\s*rss(\s*[^>]*)?>}]
m.should_not be_blank
m.should =~ /version=(["'])2.0\1/
m.should =~ %r{xmlns:\w+=(["'])http://www.w3.org/2005/Atom\1}
end
-
+
it "should set @key to params[:key]" do
controller.params[:key].should_not be_nil
assigns[:key].should == controller.params[:key]
end
-
+
it "should set params[:feed_user] to the user whom the key belongs to" do
controller.params[:feed_user].should == User.find_by_single_access_token(controller.params[:key])
end
-
+
it "should have an tag" do
css_select('rss').each do |rss|
@m = css_select(rss, 'channel')[0].to_s[%r{<\s*atom:link(\s*[^>]*)?>}]
@@ -98,19 +97,19 @@
@m.should =~ /href=(["'])#{feed_events_url(:rss, controller.params[:key])}\1/
@m.should =~ /rel=(["'])self\1/
end
-
+
it "should have an appropriate tag" do
response.body.should have_selector('channel > title', :content => %r{#{SITE_TITLE}})
end
-
+
it "should link to the event list" do
response.body.should have_selector('channel > link', :content => events_url)
end
-
+
it "should contain a element, including (among other things) the name of the user whose feed it is" do
response.body.should have_selector('channel > description', :content => %r{#{ERB::Util::html_escape controller.params[:feed_user]}})
end
-
+
it "should contain an entry for every event, with , (with address and description), , , and elements" do
@events.each do |e|
response.body.should have_selector('item title', :content => ERB::Util::html_escape(e.name)) # actually, this is XML escape, but close enough
@@ -126,21 +125,25 @@
describe EventsController, "feed.rss (login)" do
render_views
-
+
it "should not list any events if given an invalid single_access_token" do
User.stub!(:find_by_single_access_token).and_return(nil)
get :feed, :format => 'rss', :key => 'fake key'
Event.should_not_receive(:find)
response.should_not have_selector('item')
end
-
+
it "should list events if given a valid single_access_token" do
@user = FactoryGirl.create :user
UserSession.create @user
- calendar = FactoryGirl.create :calendar # @user will be subscribed to
+ calendar = FactoryGirl.create :calendar
+ FactoryGirl.create :permission, user: @user, calendar: calendar
@events = (1..5).map { FactoryGirl.create :event, :calendar => calendar }
User.stub!(:find_by_single_access_token).and_return(@user)
- Event.should_receive(:find).and_return(@events)
+ @events.stub!(:order).and_return @events
+ mock_includes = mock 'includes'
+ mock_includes.should_receive(:where).and_return(@events)
+ Event.should_receive(:includes).and_return mock_includes
get :feed, :format => 'rss', :key => @user.single_access_token
response.body.should have_selector('item')
end
@@ -148,33 +151,33 @@
describe EventsController, 'index.pdf' do
before(:each) do
- @user = Factory(:user)
+ @user = FactoryGirl.create :user
UserSession.create @user
User.stub(:current_user).and_return @user
request.env["SERVER_PROTOCOL"] = "http" # see http://iain.nl/prawn-and-controller-tests
end
-
+
context 'generic events' do
before :each do
- event = Factory :event
- Factory(:permission, :calendar => event.calendar, :user => @user)
+ event = FactoryGirl.create :event
+ FactoryGirl.create :permission, :calendar => event.calendar, :user => @user
controller.stub!(:current_objects).and_return([event])
end
-
+
it "should be successful" do
get :index, :format => 'pdf'
response.should be_success
end
-
+
it "should return the appropriate MIME type for a PDF file" do
get :index, :format => 'pdf'
response.content_type.should =~ %r{^application/pdf}
end
end
-
+
it "should set assigns[:users]" do
- @perms = [Factory(:permission, :user => @user)]
- @event = Factory :event, :calendar => Factory(:calendar, :permissions => @perms)
+ @perms = [FactoryGirl.create(:permission, :user => @user)]
+ @event = FactoryGirl.create :event, :calendar => FactoryGirl.create(:calendar, :permissions => @perms)
controller.stub(:current_objects).and_return([@event])
get :index, :format => 'pdf'
assigns[:users].should_not be_nil
@@ -186,7 +189,7 @@
@user = FactoryGirl.create :user
UserSession.create @user
end
-
+
it "should change attendance status for current user if called with a non-nil event id" do
event = FactoryGirl.create :event
commitment = FactoryGirl.create :commitment, :user => @user, :event => event, :status => true
@@ -198,12 +201,12 @@
commitment.should_receive(:save!).once.and_return(true)
get "change_status", :id => id, :status => status
end
-
+
it "should redirect to index on a standard request" do
get 'change_status'
response.should redirect_to(:action => :index)
end
-
+
it "should render an event row on an Ajax request" do
event = FactoryGirl.create :event
xhr :get, "change_status", :id => event.id, :status => 'yes' # status could also be :no or :maybe
@@ -215,7 +218,7 @@
before(:each) do
@session = UserSession.create FactoryGirl.create(:user)
end
-
+
it "should require login" do
get :new
response.should be_success
@@ -223,55 +226,51 @@
get :new
response.body.should be_blank # not sure why this works and nothing else does...
end
-
+
it "should be successful if logged in" do
get :new
response.should be_success
end
-
+
it "should set the page_title" do
get 'new'
assigns[:page_title].should_not be_nil
end
-
+
it "should create an Event object" do
e = Event.new
Event.should_receive(:new).and_return(e)
get 'new'
assigns[:event].should_not be_nil
end
-
+
it "should redirect to event list with flash after post with successful save, but not otherwise" do
get 'new'
response.should_not redirect_to(:action => :list)
- my_event = Event.new #invalid
+ my_event = FactoryGirl.build :event, name: nil, calendar: nil, state: nil # invalid
post :create, :event => my_event.attributes
response.should_not redirect_to(:action => :list)
-
- my_event = Event.new(:name => 'name', :state_id => 23, :calendar_id => 'foo') # minimal valid set of attributes
+
+ my_event = FactoryGirl.build :event
post :create, :event => my_event.attributes
response.should redirect_to(:action => :index)
flash[:notice].should_not be_nil
end
-
+
end
describe EventsController, "create" do
+ let(:user) { FactoryGirl.create :user }
+
before(:each) do
- UserSession.create FactoryGirl.create(:user)
+ UserSession.create user
end
-
+
it "should save an Event object" do
- my_event = Event.new(:name => 'name', :state_id => 23)
- my_event.should_not be_nil
- my_event.created_by_id.should be_nil
- Event.stub!(:new).and_return(my_event)
- my_event.should_receive(:save)
- post :create, :event => my_event.attributes
- # assigns[:event].name.should == my_event.name
- # assigns[:event].id.should_not be_nil
- # assigns[:event].created_by_id.should == User.current_user.id
+ event = FactoryGirl.build :event, created_by: nil
+ post :create, event: event.attributes
+ Event.find_by_name(event.name).created_by.should == user
end
end
@@ -281,7 +280,7 @@
@admin = admin_user(@event.calendar)
UserSession.create @admin
end
-
+
it "should redirect to list with an error if the user does not own the event and is not an admin" do
@event.should_receive(:allow?).with(:edit).and_return(false)
Event.should_receive(:find).and_return(@event)
@@ -289,7 +288,7 @@
flash[:error].should_not be_nil
response.should redirect_to(:action => :index)
end
-
+
it 'should allow editing if the user is authorized to edit the event' do
@event.should_receive(:allow?).with(:edit).and_return(true)
Event.should_receive(:find).and_return(@event)
@@ -297,28 +296,23 @@
flash[:error].should be_nil
response.should_not redirect_to(:action => :index)
end
-
+
it "should redirect to list with an error if the event does not exist" do
get 'edit', :id => 0 # nonexistent
flash[:error].should_not be_nil
response.should redirect_to(:action => :index)
end
-
- it "should reuse the new-event form" do
- get 'edit', :id => @event.id
- response.should render_template(:new)
- end
-
+
it "should set the page title" do
get 'edit', :id => @event.id
assigns[:page_title].should_not be_nil
end
-
+
it "should set the event" do
get 'edit', :id => @event.id
assigns[:event].should == @event
end
-
+
=begin
it "should redirect to list with a flash error if no event id is supplied or if id is invalid" do
get 'edit', :id => 'a' # invalid
@@ -337,35 +331,20 @@
event.should be_valid
post 'update', :event => event.attributes, :id => id # valid
request.should be_post
- assigns[:current_object].should_receive(:update_attributes)
response.should redirect_to(:action => :index)
flash[:notice].should_not be_nil
-
+
event.name = nil # now it's invalid
post 'update', :event => event.attributes, :id => id
response.should_not redirect_to(:action => :index)
end
-
- it "should reset coords to nil when saving" do
- event = Event.find(:first)
- id = event.id.to_s
- event.coords.should_not be_nil
-=begin
- Event.should_receive(:find).with(id).twice.and_return(event, Event.find_by_id(id))
- event.should_receive(:update_attributes).with(an_instance_of(Hash)).once
-=end
- post 'edit', :event => event.attributes, :id => id
- event = Event.find(id)
- event.should_receive(:coords_from_string).once # calling event.coords should trigger recoding
- event.coords
- end
end
describe EventsController, "show" do
before(:each) do
UserSession.create FactoryGirl.create(:user)
end
-
+
it "should set the page title" do
@event = FactoryGirl.create :event
@event.should_receive(:allow?).with(:show).at_least(:once).and_return(true)
@@ -374,7 +353,7 @@
assigns[:page_title].should_not be_nil
assigns[:page_title].should =~ Regexp.new(@event.name)
end
-
+
it "should not show an event on a calendar for which the current user doesn't have access" do
@event = mock_model(Event, :id => 4, :calendar_id => 57, :name => 'Event on another calendar')
@event.should_receive(:allow?).with(:show).at_least(:once).and_return(false)
@@ -391,14 +370,14 @@
@event = FactoryGirl.create :event, :calendar => @calendar
@id = @event.id
end
-
+
it "should not work from non-admin account" do
UserSession.create FactoryGirl.create(:user)
@event.should_not_receive(:hide)
post 'delete', :id => @id
flash[:error].should_not be_nil
end
-
+
it "should work from admin account" do
UserSession.create admin_user(@event.calendar)
Event.should_receive(:find).with(@id.to_i).and_return(@event)
@@ -414,29 +393,29 @@
UserSession.create FactoryGirl.create(:user)
@one = FactoryGirl.create :event
end
-
+
it "should use the map view" do
get :map, :id => @one.id
response.should render_template(:map)
end
-
+
it "should get an event" do
id = @one.id
get :map, :id => id
assigns[:event].should == @one
end
-
+
it "should set the page title" do
get :map, :id => @one.id
assigns[:page_title].should_not be_nil
end
-
+
it "should set the hostname" do
get :map, :id => @one.id
assigns[:host].should_not be_nil
end
-
-=begin
+
+=begin
it "should center the map on the event and add a marker and basic and scale controls" do
@mock = GMap.new(:map)
GMap.should_receive(:new).with(:map).and_return(@mock)
@@ -460,17 +439,17 @@
@my_event = FactoryGirl.create :event
Event.should_receive(:find).with(@my_event.id.to_i).and_return(@my_event)
end
-
+
it "should use the ical view" do
get :export, :id => @my_event.id
response.should render_template('events/ical')
end
-
+
it "should get an event" do
get :export, :id => @my_event.id
assigns[:event].should == @my_event
end
-
+
it "should set a MIME type of text/calendar" do
get :export, :id => @my_event.id
response.content_type.should =~ (%r{^text/calendar})
@@ -480,9 +459,9 @@
# Returns a User with admin permissions on the specified Calendar.
def admin_user(calendar)
admin = Role.find_or_create_by_name('admin')
- Factory(:user).tap do |u|
+ FactoryGirl.create(:user).tap do |u|
u.permissions.destroy_all
- u.permissions << Factory(:permission, :calendar => calendar, :user => u, :role => admin)
+ u.permissions << FactoryGirl.create(:permission, :calendar => calendar, :user => u, :role => admin)
end
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 55811ef2..032b6e86 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -34,65 +34,70 @@
date { Date.civil(rand(10) + 2100, rand(12) + 1, rand(28) + 1) } # way in the future so it shows up on the event list
calendar
association :created_by, :factory => :user
-
+
factory :deleted_event do
deleted { true }
end
end
-
+
factory :state, :class => Acts::Addressed::State do
country
name { Faker::Name.last_name } # generic_name
code { LETTERS.sample + LETTERS.sample }
end
-
+
factory :country, :class => Acts::Addressed::Country do
name { Faker::Name.last_name } # generic_name
code { LETTERS.sample + LETTERS.sample }
end
-
+
factory :calendar do
name {Faker::Name.name + "'s calendar"}
end
-
+
factory :user do
firstname { Faker::Name.first_name }
lastname { Faker::Name.last_name }
email { Faker::Internet.email }
- password { (1..(rand(15) + 4)).map{(32..127).to_a.sample.chr}.join }
+ password {'passw0rd'}
password_confirmation { password }
street { Faker::Address.street_address }
street2 { Faker::Address.secondary_address }
city {Faker::Address.city}
- association :state_raw, :factory => :state
+ association :state_raw, :factory => :state
zip { Faker::Address.zip_code }
active {true}
-
+
factory :inactive_user do
active {false}
end
+
+ factory :user_with_random_password do
+ password { (1..(rand(15) + 4)).map{(32..127).to_a.sample.chr}.join }
+ end
end
-
+
factory :commitment do
- event
- user
+ event
+ user
status {true}
+ comment { Faker::Lorem.sentence }
end
-
+
factory :permission do
user
role
calendar
show_in_report {true}
-
+
factory :admin_permission do
association :role, :factory => :admin_role
end
end
-
+
factory :role do
name {'user'}
-
+
factory :admin_role do
name {'admin'}
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index 26682d01..412c3cb7 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -4,31 +4,32 @@
describe EventsHelper do
before(:each) do
+ User.delete_all # TODO: why is this necessary?
@event = FactoryGirl.create :event
end
-
+
# refactor from list.html.erb_spec into here?
-
+
it "should generate an iCal unique id as a String" do
helper.ical_uid(@event).should be_a_kind_of(String)
end
-
+
it "should generate a delete link as a String" do
helper.delete_link(@event).should be_a_kind_of(String)
end
-
+
it "should generate an edit link as a String" do
helper.edit_link(@event).should be_a_kind_of(String)
end
-
+
it "should generate an iCal export link as a String" do
helper.ical_link(@event).should be_a_kind_of(String)
end
-
+
it "should generate a map link as a String" do
helper.map_link(@event).should be_a_kind_of(String)
end
-
+
it "should generate a microformat HTML date element as a String" do
@event.date = Time.now # arbitrary value
helper.date_element(@event).should be_a_kind_of(String)
@@ -47,94 +48,56 @@
names.should include(user.to_s)
end
end
-
+
it "should get an attendance status for an event and a user" do
helper.attendance_status(@event, FactoryGirl.create(:user)).should == :maybe
end
-
+
+ describe '#attendance_comment' do
+ let(:user) { FactoryGirl.create :user }
+
+ it 'should retrieve the comment string for the event and user' do
+ commitment = FactoryGirl.create :commitment, event: @event, user: user
+ helper.attendance_comment(@event, user).should == commitment.comment
+ end
+
+ it "should return nil if there's no commitment for the event and user" do
+ helper.attendance_comment(@event, user).should be_nil
+ end
+ end
+
it "should generate a distance string from an event to a user's coords," do
- marnen = FactoryGirl.create :user, :coords => Point.from_x_y(5, 10) # TODO: use Faker instead of arbitrary coordinates
+ marnen = FactoryGirl.create :user, :coords => User.rgeo_factory_for_column(:coords).point(5, 10) # TODO: use Faker instead of arbitrary coordinates
@event.coords = marnen.coords
helper.distance_string(@event, marnen).should =~ /\D\d(\.\d+)? miles/
user = User.new
# distance_string(@event, user).should == "" # user.coords is nil -- this spec is not working right now
- @event = Event.new do |e| e.coords = Point.from_x_y(0, 2) end
- user.coords = Point.from_x_y(0, 1)
- helper.distance_string(@event, user).should =~ /\D6(8.7)|9.*miles.*#{h('•')}$/ # 1 degree of latitude
+ @event = Event.new do |e| e.coords = Event.rgeo_factory_for_column(:coords).point(0, 2) end
+ user.coords = User.rgeo_factory_for_column(:coords).point(0, 1)
+ helper.distance_string(@event, user).should =~ /\D6(8\.7)|9\D.*miles/ # 1 degree of latitude
end
-
+
it "should generate a sort link for a table header (asc unless desc is specified)" do
@request.stub!(:path_parameters).and_return(:controller => 'events', :action => 'index')
link = helper.sort_link("Date", :date)
link.should be_a_kind_of(String)
link.should have_selector("a.sort[href='#{url_for :controller => 'events', :action => 'index', :order => :date, :direction => :asc}']", :content => "Date")
-
+
#link = sort_link("Date", :date, :desc)
#link.should match(/\A]*href="#{url_for :controller => 'events', :action => 'index', :order => :date, :direction => :desc}".*<\/a>\Z/i)
end
end
-describe EventsHelper, "event_map" do
- before(:each) do
- User.stub!(:current_user).and_return(FactoryGirl.create :user)
- end
-
- it "should return a safe string" do
- helper.event_map(Factory(:event), DOMAIN).should be_html_safe
- end
-
- it "should set up a GMap with all the options" do
- event = FactoryGirl.create :event
-
- # TODO: since this code is now in events/map.js , translate these specs into JavaScript!
-=begin
- marker = GMarker.new([1.0, 2.0])
- gmap_header = "[Stubbed header for #{DOMAIN}]"
- GMap.should_receive(:header).with(:host => DOMAIN).at_least(:once).and_return(gmap_header)
- GMarker.stub!(:new).and_return(marker)
- gmap.should_receive(:center_zoom_init)
- gmap.should_receive(:overlay_init).with(marker)
- marker.should_receive(:open_info_window).with(EventsHelper::ElementVar.new(helper.info(event)))
- gmap.should_receive(:control_init) do |opts|
- opts.should be_a_kind_of(Hash)
- opts.should have_key(:large_map)
- opts[:large_map].should == true
- opts.should have_key(:map_type)
- opts[:map_type].should == true
- end
- gmap.should_receive(:to_html).at_least(:once)
-=end
-
- gmap_header = "[Stubbed header for #{DOMAIN}]"
- GMap.should_receive(:header).with(:host => DOMAIN).at_least(:once).and_return(gmap_header)
- gmap = GMap.new(:gmap)
- gmap_div = '
';
- var n = 0;
- for ( var i = 0 , len = cluster.markers.length; i < len; ++i )
- {
- var marker = cluster.markers[i];
- if ( marker != null )
- {
- ++n;
- html += '
';
- if ( marker.getIcon().smallImage != null )
- html += '';
- else
- html += '';
- html += '
' + marker.description + '
';
- if ( n == clusterer.maxLinesPerInfoBox - 1 && cluster.markerCount > clusterer.maxLinesPerInfoBox )
- {
- html += '
...and ' + ( cluster.markerCount - n ) + ' more
';
- break;
- }
- }
- }
- html += '
';
- clusterer.map.closeInfoWindow();
- cluster.marker.openInfoWindowHtml( html );
- clusterer.poppedUpCluster = cluster;
-};
-
-Clusterer.rePop = function ( clusterer ){
- if ( clusterer.poppedUpCluster != null )
- Clusterer.popUp( clusterer.poppedUpCluster );
-};
-
-Clusterer.popDown = function ( clusterer ){
- clusterer.poppedUpCluster = null;
-};
-
-Clusterer.prototype.clearCluster = function ( cluster ){
- var i, marker;
-
- for ( i = 0; i < cluster.markers.length; ++i ){
- if ( cluster.markers[i] != null ){
- cluster.markers[i].inCluster = false;
- cluster.markers[i] = null;
- }
- }
-
- cluster.markers.length = 0;
- cluster.markerCount = 0;
-
- if ( cluster == this.poppedUpCluster )
- this.map.closeInfoWindow();
-
- if ( cluster.onMap )
- {
- this.map.removeOverlay( cluster.marker );
- cluster.onMap = false;
- }
-};
-
-// This returns a function closure that calls the given routine with the
-// specified arg.
-Clusterer.makeCaller = function ( func, arg ){
- return function () { func( arg ); };
-};
-
-
-// Augment GMarker so it handles markers that have been created but
-// not yet addOverlayed.
-GMarker.prototype.setMap = function ( map ){
- this.map = map;
-};
-
-GMarker.prototype.getMap = function (){
- return this.map;
-}
-
-GMarker.prototype.addedToMap = function (){
- this.map = null;
-};
-
-
-GMarker.prototype.origOpenInfoWindow = GMarker.prototype.openInfoWindow;
-GMarker.prototype.openInfoWindow = function ( node, opts ){
- if ( this.map != null )
- return this.map.openInfoWindow( this.getPoint(), node, opts );
- else
- return this.origOpenInfoWindow( node, opts );
-};
-
-GMarker.prototype.origOpenInfoWindowHtml = GMarker.prototype.openInfoWindowHtml;
-GMarker.prototype.openInfoWindowHtml = function ( html, opts ){
- if ( this.map != null )
- return this.map.openInfoWindowHtml( this.getPoint(), html, opts );
- else
- return this.origOpenInfoWindowHtml( html, opts );
-};
-
-GMarker.prototype.origOpenInfoWindowTabs = GMarker.prototype.openInfoWindowTabs;
-GMarker.prototype.openInfoWindowTabs = function ( tabNodes, opts ){
- if ( this.map != null )
- return this.map.openInfoWindowTabs( this.getPoint(), tabNodes, opts );
- else
- return this.origOpenInfoWindowTabs( tabNodes, opts );
-};
-
-GMarker.prototype.origOpenInfoWindowTabsHtml = GMarker.prototype.openInfoWindowTabsHtml;
-GMarker.prototype.openInfoWindowTabsHtml = function ( tabHtmls, opts ){
- if ( this.map != null )
- return this.map.openInfoWindowTabsHtml( this.getPoint(), tabHtmls, opts );
- else
- return this.origOpenInfoWindowTabsHtml( tabHtmls, opts );
-};
-
-GMarker.prototype.origShowMapBlowup = GMarker.prototype.showMapBlowup;
-GMarker.prototype.showMapBlowup = function ( opts ){
- if ( this.map != null )
- return this.map.showMapBlowup( this.getPoint(), opts );
- else
- return this.origShowMapBlowup( opts );
-};
-
-
-function addDescriptionToMarker(marker, description){
- marker.description = description;
- return marker;
-}
diff --git a/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js b/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js
deleted file mode 100644
index 315c26d0..00000000
--- a/vendor/plugins/ym4r_gm/javascript/geoRssOverlay.js
+++ /dev/null
@@ -1,194 +0,0 @@
-// GeoRssOverlay: GMaps API extension to display a group of markers from
-// a RSS feed
-//
-// Copyright 2006 Mikel Maron (email: mikel_maron yahoo com)
-//
-// The original version of this code is called MGeoRSS and can be found
-// at the following address:
-// http://brainoff.com/gmaps/mgeorss.html
-//
-// Modified by Andrew Turner to add support for the GeoRss Simple vocabulary
-//
-// Modified and bundled with YM4R in accordance with the following
-// license:
-//
-// This work is public domain
-
-function GeoRssOverlay(rssurl,icon,proxyurl,options){
- this.rssurl = rssurl;
- this.icon = icon;
- this.proxyurl = proxyurl;
- if(options['visible'] == undefined)
- this.visible = true;
- else
- this.visible = options['visible'];
- this.listDiv = options['listDiv']; //ID of the item list DIV
- this.contentDiv = options['contentDiv']; //ID of the content DIV
- this.listItemClass = options['listItemClass']; //Class of the list item DIV
- this.limitItems = options['limit']; //Maximum number of displayed entries
- this.request = false;
- this.markers = [];
-}
-
-GeoRssOverlay.prototype = new GOverlay();
-
-GeoRssOverlay.prototype.initialize=function(map) {
- this.map = map;
- this.load();
-}
-
-GeoRssOverlay.prototype.redraw = function(force){
- //nothing to do : the markers are already taken care of
-}
-
-GeoRssOverlay.prototype.remove = function(){
- for(var i= 0, len = this.markers.length ; i< len; i++){
- this.map.removeOverlay(this.markers[i]);
- }
-}
-
-GeoRssOverlay.prototype.showHide=function() {
- if (this.visible) {
- for (var i=0;i" + title + "
" + description;
-
- if(this.contentDiv == undefined){
- GEvent.addListener(marker, "click", function() {
- marker.openInfoWindowHtml(html);
- });
- }else{
- var contentDiv = this.contentDiv;
- GEvent.addListener(marker, "click", function() {
- document.getElementById(contentDiv).innerHTML = html;
- });
- }
-
- if(this.listDiv != undefined){
- var a = document.createElement('a');
- a.innerHTML = title;
- a.setAttribute("href","#");
- var georss = this;
- a.onclick = function(){
- georss.showMarker(index);
- return false;
- };
- var div = document.createElement('div');
- if(this.listItemClass != undefined){
- div.setAttribute("class",this.listItemClass);
- }
- div.appendChild(a);
- document.getElementById(this.listDiv).appendChild(div);
- }
-
- return marker;
-}
diff --git a/vendor/plugins/ym4r_gm/javascript/markerGroup.js b/vendor/plugins/ym4r_gm/javascript/markerGroup.js
deleted file mode 100644
index 02fe6249..00000000
--- a/vendor/plugins/ym4r_gm/javascript/markerGroup.js
+++ /dev/null
@@ -1,114 +0,0 @@
-function GMarkerGroup(active, markers, markersById) {
- this.active = active;
- this.markers = markers || new Array();
- this.markersById = markersById || new Object();
-}
-
-GMarkerGroup.prototype = new GOverlay();
-
-GMarkerGroup.prototype.initialize = function(map) {
- this.map = map;
-
- if(this.active){
- for(var i = 0 , len = this.markers.length; i < len; i++) {
- this.map.addOverlay(this.markers[i]);
- }
- for(var id in this.markersById){
- this.map.addOverlay(this.markersById[id]);
- }
- }
-}
-
-//If not already done (ie if not inactive) remove all the markers from the map
-GMarkerGroup.prototype.remove = function() {
- this.deactivate();
-}
-
-GMarkerGroup.prototype.redraw = function(force){
- //Nothing to do : markers are already taken care of
-}
-
-//Copy the data to a new Marker Group
-GMarkerGroup.prototype.copy = function() {
- var overlay = new GMarkerGroup(this.active);
- overlay.markers = this.markers; //Need to do deep copy
- overlay.markersById = this.markersById; //Need to do deep copy
- return overlay;
-}
-
-//Inactivate the Marker group and clear the internal content
-GMarkerGroup.prototype.clear = function(){
- //deactivate the map first (which removes the markers from the map)
- this.deactivate();
- //Clear the internal content
- this.markers = new Array();
- this.markersById = new Object();
-}
-
-//Add a marker to the GMarkerGroup ; Adds it now to the map if the GMarkerGroup is active
-GMarkerGroup.prototype.addMarker = function(marker,id){
- if(id == undefined){
- this.markers.push(marker);
- }else{
- this.markersById[id] = marker;
- }
- if(this.active && this.map != undefined ){
- this.map.addOverlay(marker);
- }
-}
-
-//Open the info window (or info window tabs) of a marker
-GMarkerGroup.prototype.showMarker = function(id){
- var marker = this.markersById[id];
- if(marker != undefined){
- GEvent.trigger(marker,"click");
- }
-}
-
-//Activate (or deactivate depending on the argument) the GMarkerGroup
-GMarkerGroup.prototype.activate = function(active){
- active = (active == undefined) ? true : active;
- if(!active){
- if(this.active){
- if(this.map != undefined){
- for(var i = 0 , len = this.markers.length; i < len; i++){
- this.map.removeOverlay(this.markers[i])
- }
- for(var id in this.markersById){
- this.map.removeOverlay(this.markersById[id]);
- }
- }
- this.active = false;
- }
- }else{
- if(!this.active){
- if(this.map != undefined){
- for(var i = 0 , len = this.markers.length; i < len; i++){
- this.map.addOverlay(this.markers[i]);
- }
- for(var id in this.markersById){
- this.map.addOverlay(this.markersById[id]);
- }
- }
- this.active = true;
- }
- }
-}
-
-GMarkerGroup.prototype.centerAndZoomOnMarkers = function() {
- if(this.map != undefined){
- //merge markers and markersById
- var tmpMarkers = this.markers.slice();
- for (var id in this.markersById){
- tmpMarkers.push(this.markersById[id]);
- }
- if(tmpMarkers.length > 0){
- this.map.centerAndZoomOnMarkers(tmpMarkers);
- }
- }
-}
-
-//Deactivate the Group Overlay (convenience method)
-GMarkerGroup.prototype.deactivate = function(){
- this.activate(false);
-}
diff --git a/vendor/plugins/ym4r_gm/javascript/wms-gs.js b/vendor/plugins/ym4r_gm/javascript/wms-gs.js
deleted file mode 100644
index c67146b8..00000000
--- a/vendor/plugins/ym4r_gm/javascript/wms-gs.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Call generic wms service for GoogleMaps v2
- * John Deck, UC Berkeley
- * Inspiration & Code from:
- * Mike Williams http://www.econym.demon.co.uk/googlemaps2/ V2 Reference & custommap code
- * Brian Flood http://www.spatialdatalogic.com/cs/blogs/brian_flood/archive/2005/07/11/39.aspx V1 WMS code
- * Kyle Mulka http://blog.kylemulka.com/?p=287 V1 WMS code modifications
- * http://search.cpan.org/src/RRWO/GPS-Lowrance-0.31/lib/Geo/Coordinates/MercatorMeters.pm
- *
- * Modified by Chris Holmes, TOPP to work by default with GeoServer.
- *
- * Bundled with YM4R with John Deck's permission.
- * Slightly modified to fit YM4R.
- * See johndeck.blogspot.com for the original version and for examples and instructions of how to use it.
- */
-
-var WGS84_SEMI_MAJOR_AXIS = 6378137.0; //equatorial radius
-var WGS84_ECCENTRICITY = 0.0818191913108718138;
-var DEG2RAD=0.0174532922519943;
-var PI=3.14159267;
-
-function dd2MercMetersLng(p_lng) {
- return WGS84_SEMI_MAJOR_AXIS * (p_lng*DEG2RAD);
-}
-
-function dd2MercMetersLat(p_lat) {
- var lat_rad = p_lat * DEG2RAD;
- return WGS84_SEMI_MAJOR_AXIS * Math.log(Math.tan((lat_rad + PI / 2) / 2) * Math.pow( ((1 - WGS84_ECCENTRICITY * Math.sin(lat_rad)) / (1 + WGS84_ECCENTRICITY * Math.sin(lat_rad))), (WGS84_ECCENTRICITY/2)));
-}
-
-function addWMSPropertiesToLayer(tile_layer,base_url,layers,styles,format,merc_proj,use_geo){
- tile_layer.format = format;
- tile_layer.baseURL = base_url;
- tile_layer.styles = styles;
- tile_layer.layers = layers;
- tile_layer.mercatorEpsg = merc_proj;
- tile_layer.useGeographic = use_geo;
- return tile_layer;
-}
-
-getTileUrlForWMS=function(a,b,c) {
- var lULP = new GPoint(a.x*256,(a.y+1)*256);
- var lLRP = new GPoint((a.x+1)*256,a.y*256);
- var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP,b,c);
- var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP,b,c);
-
- if (this.useGeographic){
- var lBbox=lUL.x+","+lUL.y+","+lLR.x+","+lLR.y;
- var lSRS="EPSG:4326";
- }else{
- var lBbox=dd2MercMetersLng(lUL.x)+","+dd2MercMetersLat(lUL.y)+","+dd2MercMetersLng(lLR.x)+","+dd2MercMetersLat(lLR.y);
- var lSRS="EPSG:" + this.mercatorEpsg;
- }
- var lURL=this.baseURL;
- lURL+="?REQUEST=GetMap";
- lURL+="&SERVICE=WMS";
- lURL+="&VERSION=1.1.1";
- lURL+="&LAYERS="+this.layers;
- lURL+="&STYLES="+this.styles;
- lURL+="&FORMAT=image/"+this.format;
- lURL+="&BGCOLOR=0xFFFFFF";
- lURL+="&TRANSPARENT=TRUE";
- lURL+="&SRS="+lSRS;
- lURL+="&BBOX="+lBbox;
- lURL+="&WIDTH=256";
- lURL+="&HEIGHT=256";
- lURL+="&reaspect=false";
- return lURL;
-}
diff --git a/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js b/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js
deleted file mode 100644
index 1c768dfb..00000000
--- a/vendor/plugins/ym4r_gm/javascript/ym4r-gm.js
+++ /dev/null
@@ -1,117 +0,0 @@
-// JS helper functions for YM4R
-
-function addInfoWindowToMarker(marker,info,options){
- GEvent.addListener(marker, "click", function() {marker.openInfoWindowHtml(info,options);});
- return marker;
-}
-
-function addInfoWindowTabsToMarker(marker,info,options){
- GEvent.addListener(marker, "click", function() {marker.openInfoWindowTabsHtml(info,options);});
- return marker;
-}
-
-function addPropertiesToLayer(layer,getTile,copyright,opacity,isPng){
- layer.getTileUrl = getTile;
- layer.getCopyright = copyright;
- layer.getOpacity = opacity;
- layer.isPng = isPng;
- return layer;
-}
-
-function addOptionsToIcon(icon,options){
- for(var k in options){
- icon[k] = options[k];
- }
- return icon;
-}
-
-function addCodeToFunction(func,code){
- if(func == undefined)
- return code;
- else{
- return function(){
- func();
- code();
- }
- }
-}
-
-function addGeocodingToMarker(marker,address){
- marker.orig_initialize = marker.initialize;
- orig_redraw = marker.redraw;
- marker.redraw = function(force){}; //empty the redraw method so no error when called by addOverlay.
- marker.initialize = function(map){
- new GClientGeocoder().getLatLng(address,
- function(latlng){
- if(latlng){
- marker.redraw = orig_redraw;
- marker.orig_initialize(map); //init before setting point
- marker.setPoint(latlng);
- }//do nothing
- });
- };
- return marker;
-}
-
-
-
-GMap2.prototype.centerAndZoomOnMarkers = function(markers) {
- var bounds = new GLatLngBounds(markers[0].getPoint(),
- markers[0].getPoint());
- for (var i=1, len = markers.length ; i:key) or a host, (:host).
- def self.get(request,options = {})
- api_key = ApiKey.get(options)
- output = options[:output] || "kml"
- url = "http://maps.google.com/maps/geo?q=#{URI.encode(request)}&key=#{api_key}&output=#{output}"
-
- res = open(url).read
-
- case output.to_sym
- when :json
- res = eval(res.gsub(":","=>")) #!!!EVAL EVAL EVAL EVAL!!! hopefully we can trust google...
- placemarks = Placemarks.new(res['name'],res['Status']['code'])
- if res['Placemark']
- placemark = res['Placemark']
-
- placemark.each do |data|
-
- data_country = data['Country']['CountryNameCode'] rescue ""
- data_administrative = data['Country']['AdministrativeArea']['AdministrativeAreaName'] rescue ""
- data_sub_administrative = data['Country']['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName'] rescue ""
- data_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] rescue ""
- data_dependent_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['DependentLocalityName'] rescue ""
- data_thoroughfare = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['ThoroughfareName'] rescue ""
- data_postal_code = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['PostalCode']['PostalCodeNumber'] rescue ""
- lon, lat = data['Point']['coordinates'][0,2]
- data_accuracy = data['Accuracy']
- unless data_accuracy.nil?
- data_accuracy = data_accuracy.to_i
- end
-
- placemarks << Geocoding::Placemark.new(data['address'],
- data_country,
- data_administrative,
- data_sub_administrative,
- data_locality,
- data_dependent_locality,
- data_thoroughfare,
- data_postal_code,
- lon, lat, data_accuracy)
-
- end
- end
- when :kml, :xml
-
- doc = REXML::Document.new(res)
-
- response = doc.elements['//Response']
- placemarks = Placemarks.new(response.elements['name'].text,response.elements['Status/code'].text.to_i)
- response.elements.each(".//Placemark") do |placemark|
- data = placemark.elements
- data_country = data['.//CountryNameCode']
- data_administrative = data['.//AdministrativeAreaName']
- data_sub_administrative = data['.//SubAdministrativeAreaName']
- data_locality = data['.//LocalityName']
- data_dependent_locality = data['.//DependentLocalityName']
- data_thoroughfare = data['.//ThoroughfareName']
- data_postal_code = data['.//PostalCodeNumber']
- lon, lat = data['.//coordinates'].text.split(",")[0..1].collect {|l| l.to_f }
- data_accuracy = data['.//*[local-name()="AddressDetails"]'].attributes['Accuracy']
- unless data_accuracy.nil?
- data_accuracy = data_accuracy.to_i
- end
- placemarks << Geocoding::Placemark.new(data['address'].text,
- data_country.nil? ? "" : data_country.text,
- data_administrative.nil? ? "" : data_administrative.text,
- data_sub_administrative.nil? ? "" : data_sub_administrative.text,
- data_locality.nil? ? "" : data_locality.text,
- data_dependent_locality.nil? ? "" : data_dependent_locality.text,
- data_thoroughfare.nil? ? "" : data_thoroughfare.text,
- data_postal_code.nil? ? "" : data_postal_code.text,
- lon, lat, data_accuracy )
- end
- end
-
- placemarks
- end
-
- #Group of placemarks returned by the Geocoding service. If the result is valid the +status+ attribute should be equal to Geocoding::GE0_SUCCESS
- class Placemarks < Array
- attr_accessor :name,:status
-
- def initialize(name,status)
- super(0)
- @name = name
- @status = status
- end
- end
-
- #A result from the Geocoding service.
- class Placemark < Struct.new(:address,:country_code,:administrative_area,:sub_administrative_area,:locality,:dependent_locality,:thoroughfare,:postal_code,:longitude,:latitude,:accuracy)
- def lonlat
- [longitude,latitude]
- end
-
- def latlon
- [latitude,longitude]
- end
- end
- end
- end
-end
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb
deleted file mode 100644
index ddaf8bbc..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/helper.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-
-Ym4r::GmPlugin::GPolyline.class_eval do
- #Creates a GPolyline object from a georuby line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order.
- def self.from_georuby(line_string,color = nil,weight = nil,opacity = nil)
- GPolyline.new(line_string.points.collect { |point| GLatLng.new([point.y,point.x])},color,weight,opacity)
- end
-end
-
-Ym4r::GmPlugin::GMarker.class_eval do
- #Creates a GMarker object from a georuby point. Accepts the same options as the GMarker constructor. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order.
- def self.from_georuby(point,options = {})
- GMarker.new([point.y,point.x],options)
- end
-end
-
-Ym4r::GmPlugin::GLatLng.class_eval do
- #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order.
- def self.from_georuby(point,unbounded = nil)
- GLatLng.new([point.y,point.x],unbounded)
- end
-end
-
-Ym4r::GmPlugin::GLatLngBounds.class_eval do
- #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order.
- def self.from_georuby(envelope)
- GLatLngBounds.new(GLatLng.from_georuby(envelope.lower_corner),
- GLatLng.from_georuby(envelope.upper_corner))
- end
-end
-
-Ym4r::GmPlugin::GPolygon.class_eval do
- #Creates a GPolygon object from a georuby polygon or line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order.
- def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0)
- if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString)
- GPolygon.new(ls_or_p.collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity)
- else
- GPolygon.new(ls_or_p[0].collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity)
- end
- end
-end
-
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb
deleted file mode 100644
index 0de9c188..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/key.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-module Ym4r
- module GmPlugin
- class GMapsAPIKeyConfigFileNotFoundException < StandardError
- end
-
- class AmbiguousGMapsAPIKeyException < StandardError
- end
-
- #Class fo the manipulation of the API key
- class ApiKey
- #Read the API key config for the current ENV
- unless File.exist?(RAILS_ROOT + '/config/gmaps_api_key.yml')
- raise GMapsAPIKeyConfigFileNotFoundException.new("File RAILS_ROOT/config/gmaps_api_key.yml not found")
- else
- env = ENV['RAILS_ENV'] || RAILS_ENV
- GMAPS_API_KEY = YAML.load_file(RAILS_ROOT + '/config/gmaps_api_key.yml')[env]
- end
-
- def self.get(options = {})
- if options.has_key?(:key)
- options[:key]
- elsif GMAPS_API_KEY.is_a?(Hash)
- #For this environment, multiple hosts are possible.
- #:host must have been passed as option
- if options.has_key?(:host)
- GMAPS_API_KEY[options[:host]]
- else
- raise AmbiguousGMapsAPIKeyException.new(GMAPS_API_KEY.keys.join(","))
- end
- else
- #Only one possible key: take it and ignore the :host option if it is there
- GMAPS_API_KEY
- end
- end
- end
- end
-end
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb
deleted file mode 100644
index d7fc151f..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/layer.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-module Ym4r
- module GmPlugin
- #Map types of the map
- class GMapType
- include MappingObject
-
- G_NORMAL_MAP = Variable.new("G_NORMAL_MAP")
- G_SATELLITE_MAP = Variable.new("G_SATELLITE_MAP")
- G_HYBRID_MAP = Variable.new("G_HYBRID_MAP")
- G_PHYSICAL_MAP = Variable.new("G_PHYSICAL_MAP")
-
- attr_accessor :layers, :name, :projection, :options
-
- #The options can be any of the GMapType options detailed in the documentation + a :projection.
- def initialize(layers, name, options = {})
- @layers = layers
- @name = name
- @projection = options.delete(:projection) || GMercatorProjection.new
- @options = options
- end
-
- def create
- "new GMapType(#{MappingObject.javascriptify_variable(Array(layers))}, #{MappingObject.javascriptify_variable(projection)}, #{MappingObject.javascriptify_variable(name)}, #{MappingObject.javascriptify_variable(options)})"
- end
- end
-
- #Represents a mercator projection for zoom levels 0 to 17 (more than that by passing an argument to the constructor)
- class GMercatorProjection
- include MappingObject
-
- attr_accessor :n
-
- def initialize(n = nil)
- @n = n
- end
-
- def create
- if n.nil?
- return "G_NORMAL_MAP.getProjection()"
- else
- "new GMercatorProjection(#{@n})"
- end
- end
- end
-
- #Abstract Tile layer. Subclasses must implement a get_tile_url method.
- class GTileLayer
- include MappingObject
-
- attr_accessor :opacity, :zoom_range, :copyright, :format
-
- #Options are the following, with default values:
- #:zoom_range (0..17), :copyright ({'prefix' => '', 'copyright_texts' => [""]}), :opacity (1.0), :format ("png")
- def initialize(options = {})
- @opacity = options[:opacity] || 1.0
- @zoom_range = options[:zoom_range] || (0..17)
- @copyright = options[:copyright] || {'prefix' => '', 'copyright_texts' => [""]}
- @format = (options[:format] || "png").to_s
- end
-
- def create
- "addPropertiesToLayer(new GTileLayer(new GCopyrightCollection(\"\"),#{zoom_range.begin},#{zoom_range.end}),#{get_tile_url},function(a,b) {return #{MappingObject.javascriptify_variable(@copyright)};}\n,function() {return #{@opacity};},function(){return #{@format == "png"};})"
- end
-
- #for subclasses to implement
- def get_tile_url
- end
- end
-
- #Represents a pre tiled layer, taking images directly from a server, without using a server script.
- class PreTiledLayer < GTileLayer
- attr_accessor :base_url
-
- #Possible options are the same as for the GTileLayer constructor
- def initialize(base_url,options = {})
- super(options)
- @base_url = base_url
- end
-
- #Returns the code to determine the url to fetch the tile. Follows the convention adopted by the tiler: {base_url}/tile_{b}_{a.x}_{a.y}.{format}
- def get_tile_url
- "function(a,b) { return '#{@base_url}/tile_' + b + '_' + a.x + '_' + a.y + '.#{format}';}"
- end
- end
-
- #Represents a pretiled layer (it actually does not really matter where the tiles come from). Calls an action on the server to get back the tiles. It passes the action arguments x, y (coordinates of the tile) and z (zoom level). It can be used, for example, to return default tiles when the requested tile is not present.
- class PreTiledLayerFromAction < PreTiledLayer
- def get_tile_url
- "function(a,b) { return '#{base_url}?x=' + a.x + '&y=' + a.y + '&z=' + b ;}"
- end
- end
-
- #Represents a TileLayer where the tiles are generated dynamically from a WMS server (MapServer, GeoServer,...)
- #You need to include the JavaScript file wms-gs.js for this to work
- #see http://docs.codehaus.org/display/GEOSDOC/Google+Maps
- class WMSLayer < GTileLayer
- attr_accessor :base_url, :layers, :styles, :merc_proj, :use_geographic
-
- #Options are the same as with GTileLayer + :styles (""), :merc_proj (:mapserver), :use_geographic (false)
- def initialize(base_url, layers, options = {})
- super(options)
- @base_url = base_url.gsub(/\?$/,"") #standardize the url
- @layers = layers
- @styles = options[:styles] || ""
- merc_proj = options[:merc_proj] || :mapserver
- @merc_proj = if merc_proj == :mapserver
- "54004"
- elsif merc_proj == :geoserver
- "41001"
- else
- merc_proj.to_s
- end
- @use_geographic = options.has_key?(:use_geographic)? options[:use_geographic] : false
- puts format
- end
-
- def get_tile_url
- "getTileUrlForWMS"
- end
-
- def create
- "addWMSPropertiesToLayer(#{super},#{MappingObject.javascriptify_variable(@base_url)},#{MappingObject.javascriptify_variable(@layers)},#{MappingObject.javascriptify_variable(@styles)},#{MappingObject.javascriptify_variable(format)},#{MappingObject.javascriptify_variable(@merc_proj)},#{MappingObject.javascriptify_variable(@use_geographic)})"
- end
- end
- end
-end
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb
deleted file mode 100644
index 22ddf4c1..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/map.rb
+++ /dev/null
@@ -1,282 +0,0 @@
-module Ym4r
- module GmPlugin
- #Representing the Google Maps API class GMap2.
- class GMap
- include MappingObject
-
- #A constant containing the declaration of the VML namespace, necessary to display polylines under IE.
- VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\""
-
- #The id of the DIV that will contain the map in the HTML page.
- attr_reader :container
-
- #By default the map in the HTML page will be globally accessible with the name +map+.
- def initialize(container, variable = "map")
- @container = container
- @variable = variable
- @init = []
- @init_end = [] #for stuff that must be initialized at the end (controls)
- @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom)
- @global_init = []
- end
-
- #Deprecated. Use the static version instead.
- def header(with_vml = true)
- GMap.header(:with_vml => with_vml)
- end
-
- #Outputs the header necessary to use the Google Maps API, by including the JS files of the API, as well as a file containing YM4R/GM helper functions. By default, it also outputs a style declaration for VML elements. This default can be overriddent by passing :with_vml => false as option to the method. You can also pass a :host option in order to select the correct API key for the location where your app is currently running, in case the current environment has multiple possible keys. Usually, in this case, you should pass it @request.host. If you have defined only one API key for the current environment, the :host option is ignored. Finally you can override all the key settings in the configuration by passing a value to the :key key. You can pass a language for the map type buttons with the :hl option (possible values are: Japanese (ja), French (fr), German (de), Italian (it), Spanish (es), Catalan (ca), Basque (eu) and Galician (gl): no values means english). Finally, you can pass :local_search => true to get the header css and js information needed for the local search control. If you do want local search you must also add :local_search => true to the @map.control_init method.
- def self.header(options = {})
- options[:with_vml] = true unless options.has_key?(:with_vml)
- options[:hl] ||= ''
- options[:local_search] = false unless options.has_key?(:local_search)
- api_key = ApiKey.get(options)
- a = "\n"
- a << "\n" unless options[:without_js]
- a << "" if options[:with_vml]
- a << "" if options[:local_search]
- a << "\n" if options[:local_search]
- a << "" if options[:local_search]
- a
- end
-
- #Outputs the which has been configured to contain the map. You can pass :width and :height as options to output this in the style attribute of the DIV element (you could also achieve the same effect by putting the dimension info into a CSS or using the instance method GMap#header_width_height). You can aslo pass :class to set the classname of the div.
- def div(options = {})
- attributes = "id=\"#{@container}\" "
- if options.has_key?(:height) && options.has_key?(:width)
- width = options.delete(:width)
- if width.is_a?(Integer) or width =~ /^[0-9]+$/
- width = width.to_s + "px"
- end
- height = options.delete(:height)
- if height.is_a?(Integer) or height =~ /^[0-9]+$/
- height = height.to_s + "px"
- end
- attributes += "style=\"width:#{width};height:#{height}\" "
- end
- if options.has_key?(:class)
- attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ")
- end
- ""
- end
-
- #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS.
- def header_width_height(width,height)
- ""
- end
-
- #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function.
- def record_init(code)
- @init << code
- end
-
- #Initializes the controls: you can pass a hash with keys :small_map, :large_map, :small_zoom, :scale, :map_type, :overview_map and a boolean value as the value (usually true, since the control is not displayed by default), :local_search and :local_search_options
- def control_init(controls = {})
- @init_end << add_control(GSmallMapControl.new) if controls[:small_map]
- @init_end << add_control(GLargeMapControl.new) if controls[:large_map]
- @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom]
- @init_end << add_control(GScaleControl.new) if controls[:scale]
- @init_end << add_control(GMapTypeControl.new) if controls[:map_type]
- @init_end << add_control(GHierarchicalMapTypeControl.new) if controls[:hierarchical_map_type]
- @init_end << add_control(GOverviewMapControl.new) if controls[:overview_map]
- @init_end << add_control(GLocalSearchControl.new(controls[:anchor], controls[:offset_width], controls[:offset_height], controls[:local_search_options])) if controls[:local_search]
- end
-
- #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys :dragging, :info_window, :double_click_zoom, :continuous_zoom and :scroll_wheel_zoom. The values should be true or false. Check the google maps API doc to know what the default values are.
- def interface_init(interface = {})
- if !interface[:dragging].nil?
- if interface[:dragging]
- @init << enableDragging()
- else
- @init << disableDragging()
- end
- end
- if !interface[:info_window].nil?
- if interface[:info_window]
- @init << enableInfoWindow()
- else
- @init << disableInfoWindow()
- end
- end
- if !interface[:double_click_zoom].nil?
- if interface[:double_click_zoom]
- @init << enableDoubleClickZoom()
- else
- @init << disableDoubleClickZoom()
- end
- end
- if !interface[:continuous_zoom].nil?
- if interface[:continuous_zoom]
- @init << enableContinuousZoom()
- else
- @init << disableContinuousZoom()
- end
- end
- if !interface[:scroll_wheel_zoom].nil?
- if interface[:scroll_wheel_zoom]
- @init << enableScrollWheelZoom()
- else
- @init << disableScrollWheelZoom()
- end
- end
- end
-
- #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array.
- def center_zoom_init(center, zoom)
- if center.is_a?(GLatLng)
- @init_begin << set_center(center,zoom)
- else
- @init_begin << set_center(GLatLng.new(center),zoom)
- end
- end
-
- #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects)
- def center_zoom_on_points_init(*points)
- if(points.length > 0)
- if(points[0].is_a?(Array))
- points = points.collect { |point| GLatLng.new(point) }
- end
- @init_begin << center_and_zoom_on_points(points)
- end
- end
-
- #Center and zoom based on the bbox corners. Pass a GLatLngBounds object, an array of 2D coordinates (sw and ne) or an array of GLatLng objects (sw and ne).
- def center_zoom_on_bounds_init(latlngbounds)
- if(latlngbounds.is_a?(Array))
- if latlngbounds[0].is_a?(Array)
- latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1]))
- elsif latlngbounds[0].is_a?(GLatLng)
- latlngbounds = GLatLngBounds.new(*latlngbounds)
- end
- end
- #else it is already a latlngbounds object
-
- @init_begin << center_and_zoom_on_bounds(latlngbounds)
- end
-
- #Initializes the map by adding an overlay (marker or polyline).
- def overlay_init(overlay)
- @init << add_overlay(overlay)
- end
-
- #Sets up a new map type. If +add+ is false, all the other map types of the map are wiped out. If you want to access the map type in other methods, you should declare the map type first (with +declare_init+).
- def add_map_type_init(map_type, add = true)
- unless add
- @init << get_map_types.set_property(:length,0)
- end
- @init << add_map_type(map_type)
- end
- #for legacy purpose
- alias :map_type_init :add_map_type_init
-
- #Sets the map type displayed by default after the map is loaded. It should be known from the map (ie either the default map types or a user-defined map type added with add_map_type_init). Use set_map_type_init(GMapType::G_SATELLITE_MAP) or set_map_type_init(GMapType::G_HYBRID_MAP) to initialize the map with repsecitvely the Satellite view and the hybrid view.
- def set_map_type_init(map_type)
- @init << set_map_type(map_type)
- end
-
- #Locally declare a MappingObject with variable name "name"
- def declare_init(variable, name)
- @init << variable.declare(name)
- end
-
- #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally).
- def record_global_init(code)
- @global_init << code
- end
-
- #Deprecated. Use icon_global_init instead.
- def icon_init(icon , name)
- icon_global_init(icon , name)
- end
-
- #Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+.
- def icon_global_init(icon , name, options = {})
- declare_global_init(icon,name,options)
- end
-
- #Registers an event
- def event_init(object,event,callback)
- @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"
- end
-
- #Registers an event globally
- def event_global_init(object,event,callback)
- @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"
- end
-
- #Declares the overlay globally with name +name+
- def overlay_global_init(overlay,name, options = {})
- declare_global_init(overlay,name, options)
- @init << add_overlay(overlay)
- end
-
- #Globally declare a MappingObject with variable name "name". Option :local_construction should be passed if the construction has to be done inside the onload callback method (for exsample if it depends on the GMap to be initialized)
- def declare_global_init(variable,name, options = {})
- unless options[:local_construction]
- @global_init << "var #{variable.assign_to(name)}"
- else
- @global_init << "var #{name};"
- @init << variable.assign_to(name)
- end
- end
-
- #Outputs the initialization code for the map. By default, it outputs the script tags, performs the initialization in response to the onload event of the window and makes the map globally available. If you pass +true+ to the option key :full, the map will be setup in full screen, in which case it is not necessary (but not harmful) to set a size for the map div.
- def to_html(options = {})
- no_load = options[:no_load]
- no_script_tag = options[:no_script_tag]
- no_declare = options[:no_declare]
- no_global = options[:no_global]
- fullscreen = options[:full]
- load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used
-
- html = ""
- html << "" if !no_script_tag
-
- if fullscreen
- #setting up the style in case of full screen
- html << ""
- end
-
- html
- end
-
- #Outputs in JavaScript the creation of a GMap2 object
- def create
- "new GMap2(document.getElementById(\"#{@container}\"))"
- end
- end
- end
-end
-
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb
deleted file mode 100644
index 66d825eb..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/mapping.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-module Ym4r
- module GmPlugin
- #The module where all the Ruby-to-JavaScript conversion takes place. It is included by all the classes in the YM4R library.
- module MappingObject
- #The name of the variable in JavaScript space.
- attr_reader :variable
-
- #Creates javascript code for missing methods + takes care of listeners
- def method_missing(name,*args)
- str_name = name.to_s
- if str_name =~ /^on_(.*)/
- if args.length != 1
- raise ArgumentError("Only 1 argument is allowed on on_ methods");
- else
- Variable.new("GEvent.addListener(#{to_javascript},\"#{MappingObject.javascriptify_method($1)}\",#{args[0]})")
- end
- else
- args.collect! do |arg|
- MappingObject.javascriptify_variable(arg)
- end
- Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(str_name)}(#{args.join(",")})")
- end
- end
-
- #Creates javascript code for array or hash indexing
- def [](index) #index could be an integer or string
- return Variable.new("#{to_javascript}[#{MappingObject.javascriptify_variable(index)}]")
- end
-
- #Transforms a Ruby object into a JavaScript string : MAppingObject, String, Array, Hash and general case (using to_s)
- def self.javascriptify_variable(arg)
- if arg.is_a?(MappingObject)
- arg.to_javascript
- elsif arg.is_a?(String)
- "\"#{MappingObject.escape_javascript(arg)}\""
- elsif arg.is_a?(Array)
- "[" + arg.collect{ |a| MappingObject.javascriptify_variable(a)}.join(",") + "]"
- elsif arg.is_a?(Hash)
- "{" + arg.to_a.collect do |v|
- "#{MappingObject.javascriptify_method(v[0].to_s)} : #{MappingObject.javascriptify_variable(v[1])}"
- end.join(",") + "}"
- elsif arg.nil?
- "undefined"
- else
- arg.to_s
- end
- end
-
- #Escape string to be used in JavaScript. Lifted from rails.
- def self.escape_javascript(javascript)
- javascript.gsub(/\r\n|\n|\r/, "\\n").gsub("\"") { |m| "\\#{m}" }
- end
-
- #Transform a ruby-type method name (like add_overlay) to a JavaScript-style one (like addOverlay).
- def self.javascriptify_method(method_name)
- method_name.gsub(/_(\w)/){|s| $1.upcase}
- end
-
- #Declares a Mapping Object bound to a JavaScript variable of name +variable+.
- def declare(variable)
- @variable = variable
- "var #{@variable} = #{create};"
- end
-
- #declare with a random variable name
- def declare_random(init,size = 8)
- s = init.clone
- 6.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
- declare(s)
- end
-
- #Checks if the MappinObject has been declared
- def declared?
- !@variable.nil?
- end
-
- #Binds a Mapping object to a previously declared JavaScript variable of name +variable+.
- def assign_to(variable)
- @variable = variable
- "#{@variable} = #{create};"
- end
-
- #Assign the +value+ to the +property+ of the MappingObject
- def set_property(property, value)
- "#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)} = #{MappingObject.javascriptify_variable(value)}"
- end
-
- #Returns the code to get a +property+ from the MappingObject
- def get_property(property)
- Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)}")
- end
-
- #Returns a Javascript code representing the object
- def to_javascript
- unless @variable.nil?
- @variable
- else
- create
- end
- end
-
- #Creates a Mapping Object in JavaScript.
- #To be implemented by subclasses if needed
- def create
- end
- end
-
- #Used to bind a ruby variable to an already existing JavaScript one. It doesn't have to be a variable in the sense "var variable" but it can be any valid JavaScript expression that has a value.
- class Variable
- include MappingObject
-
- def initialize(variable)
- @variable = variable
- end
- #Returns the javascript expression contained in the object.
- def create
- @variable
- end
- #Returns the expression inside the Variable followed by a ";"
- def to_s
- @variable + ";"
- end
-
- UNDEFINED = Variable.new("undefined")
- end
- end
-end
-
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb
deleted file mode 100644
index 5e0953af..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/overlay.rb
+++ /dev/null
@@ -1,386 +0,0 @@
-module Ym4r
- module GmPlugin
- #A graphical marker positionned through geographic coordinates (in the WGS84 datum). An HTML info window can be set to be displayed when the marker is clicked on.
- class GMarker
- include MappingObject
- attr_accessor :point, :options, :info_window, :info_window_tabs, :address
- #The +points+ argument can be either a GLatLng object or an array of 2 floats. The +options+ keys can be: :icon, :clickable, :title, :info_window and :info_window_tabs, as well as :max_width. The value of the +info_window+ key is a string of HTML code that will be displayed when the markers is clicked on. The value of the +info_window_tabs+ key is an array of GInfoWindowTab objects or a hash directly, in which case it will be transformed to an array of GInfoWindowTabs, with the keys as the tab headers and the values as the content.
- def initialize(position, options = {})
- if position.is_a?(Array)
- @point = GLatLng.new(position)
- elsif position.is_a?(String)
- @point = Variable.new("INVISIBLE") #default coordinates: won't appear anyway
- @address = position
- else
- @point = position
- end
- @info_window = options.delete(:info_window)
- @info_window_tabs = options.delete(:info_window_tabs)
- if options.has_key?(:max_url)
- @info_window_options = {:max_url => options.delete(:max_url) }
- else
- @info_window_options = {}
- end
- @options = options
- end
- #Creates a marker: If an info_window or info_window_tabs is present, the response to the click action from the user is setup here.
- def create
- if @options.empty?
- creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)})"
- else
- creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)},#{MappingObject.javascriptify_variable(@options)})"
- end
- if @info_window && @info_window.is_a?(String)
- creation = "addInfoWindowToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window)},#{MappingObject.javascriptify_variable(@info_window_options)})"
- elsif @info_window_tabs && @info_window_tabs.is_a?(Hash)
- creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window_tabs.to_a.collect{|kv| GInfoWindowTab.new(kv[0],kv[1] ) })},#{MappingObject.javascriptify_variable(@info_window_options)})"
- elsif @info_window_tabs
- creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(Array(@info_window_tabs))},#{MappingObject.javascriptify_variable(@info_window_options)})"
- end
- if @address.nil?
- creation
- else
- "addGeocodingToMarker(#{creation},#{MappingObject.javascriptify_variable(@address)})"
- end
- end
- end
-
- #Represents a tab to be displayed in a bubble when a marker is clicked on.
- class GInfoWindowTab < Struct.new(:tab,:content)
- include MappingObject
- def create
- "new GInfoWindowTab(#{MappingObject.javascriptify_variable(tab)},#{MappingObject.javascriptify_variable(content)})"
- end
- end
-
- #Represents a definition of an icon. You can pass rubyfied versions of the attributes detailed in the Google Maps API documentation. You can initialize global icons to be used in the application by passing a icon object, along with a variable name, to GMap#icon_init. If you want to declare an icon outside this, you will need to declare it first, since the JavaScript constructor does not accept any argument.
- class GIcon
- include MappingObject
- DEFAULT = Variable.new("G_DEFAULT_ICON")
- attr_accessor :options, :copy_base
-
- #Options can contain all the attributes (in rubyfied format) of a GIcon object (see Google's doc), as well as :copy_base, which indicates if the icon is copied from another one.
- def initialize(options = {})
- @copy_base = options.delete(:copy_base)
- @options = options
- end
- #Creates a GIcon.
- def create
- if @copy_base
- c = "new GIcon(#{MappingObject.javascriptify_variable(@copy_base)})"
- else
- c = "new GIcon()"
- end
- if !options.empty?
- "addOptionsToIcon(#{c},#{MappingObject.javascriptify_variable(@options)})"
- else
- c
- end
- end
- end
-
- #A polyline.
- class GPolyline
- include MappingObject
- attr_accessor :points,:color,:weight,:opacity
- #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polyline from a GeoRuby linestring is provided in the helper.rb file.
- def initialize(points,color = nil,weight = nil,opacity = nil)
- if !points.empty? and points[0].is_a?(Array)
- @points = points.collect { |pt| GLatLng.new(pt) }
- else
- @points = points
- end
- @color = color
- @weight = weight
- @opacity = opacity
- end
- #Creates a new polyline.
- def create
- a = "new GPolyline(#{MappingObject.javascriptify_variable(points)}"
- a << ",#{MappingObject.javascriptify_variable(@color)}" if @color
- a << ",#{MappingObject.javascriptify_variable(@weight)}" if @weight
- a << ",#{MappingObject.javascriptify_variable(@opacity)}" if @opacity
- a << ")"
- end
- end
-
- #Encoded GPolyline class
- class GPolylineEncoded
- include MappingObject
- attr_accessor :points,:color,:weight,:opacity,:levels,:zoom_factor,:num_levels
-
- def initialize(options={})
- #points = options[:points]
- #if !points.empty? and points[0].is_a?(Array)
- # @points = points.collect { |pt| GLatLng.new(pt) }
- #else
- #@points = points
- #end
- @points = options[:points]
- @color = options[:color]
- @weight = options[:weight]
- @opacity = options[:opacity]
- @levels = options[:levels] || "BBBBBBBBBBBB"
- @zoom_factor = options[:zoom_factor] || 32
- @num_levels = options[:num_levels] || 4
- end
- def create
- a = "new GPolyline.fromEncoded({points: #{MappingObject.javascriptify_variable(points)},\n"
- a << "levels: #{MappingObject.javascriptify_variable(@levels)},"
- a << "zoomFactor: #{MappingObject.javascriptify_variable(@zoom_factor)},"
- a << "numLevels: #{MappingObject.javascriptify_variable(@num_levels)}"
- a << ",color: #{MappingObject.javascriptify_variable(@color)}" if @color
- a << ",weight: #{MappingObject.javascriptify_variable(@weight)}" if @weight
- a << ",opacity: #{MappingObject.javascriptify_variable(@opacity)}" if @opacity
- a << "})"
- end
- end
-
- #A basic Latitude/longitude point.
- class GLatLng
- include MappingObject
- attr_accessor :lat,:lng,:unbounded
-
- def initialize(latlng,unbounded = nil)
- @lat = latlng[0]
- @lng = latlng[1]
- @unbounded = unbounded
- end
- def create
- unless @unbounded
- "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)})"
- else
- "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)},#{MappingObject.javascriptify_variable(@unbounded)})"
- end
- end
- end
-
- #A rectangular bounding box, defined by its south-western and north-eastern corners.
- class GLatLngBounds < Struct.new(:sw,:ne)
- include MappingObject
- def create
- "new GLatLngBounds(#{MappingObject.javascriptify_variable(sw)},#{MappingObject.javascriptify_variable(ne)})"
- end
- end
-
- #Polygon. Not documented yet in the Google Maps API
- class GPolygon
- include MappingObject
-
- attr_accessor :points,:stroke_color,:stroke_weight,:stroke_opacity,:color,:opacity
-
- #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polygon from a GeoRuby polygon is provided in the helper.rb file.
- def initialize(points,stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0,encoded=false)
- if !points.empty? and points[0].is_a?(Array)
- @points = points.collect { |pt| GLatLng.new(pt) }
- else
- @points = points
- end
- @stroke_color = stroke_color
- @stroke_weight = stroke_weight
- @stroke_opacity = stroke_opacity
- @color = color
- @opacity = opacity
- end
-
- #Creates a new polygon
- def create
- a = "new GPolygon(#{MappingObject.javascriptify_variable(points)}"
- a << ",#{MappingObject.javascriptify_variable(@stroke_color)}"
- a << ",#{MappingObject.javascriptify_variable(@stroke_weight)}"
- a << ",#{MappingObject.javascriptify_variable(@stroke_opacity)}"
- a << ",#{MappingObject.javascriptify_variable(@color)}"
- a << ",#{MappingObject.javascriptify_variable(@opacity)}"
- a << ")"
- end
- end
-
- class GPolygonEncoded
- include MappingObject
-
- attr_accessor :polyline, :color, :opacity, :outline, :fill
-
- def initialize(polylines,fill=true,color="#000000",opacity=0.5,outline=false)
- #force polylines to be an array
- if polylines.is_a? Array
- @polylines = polylines
- else
- @polylines = [polylines]
- end
- @color = color
- @fill = fill
- @opacity = opacity
- @outline = outline
- end
-
- #Creates a new polygon.
- def create
- polylines_for_polygon= []
- @polylines.each do |p|
- x = "{points: #{MappingObject.javascriptify_variable(p.points)},"
- x << "levels: #{MappingObject.javascriptify_variable(p.levels)},"
- x << "zoomFactor: #{MappingObject.javascriptify_variable(p.zoom_factor)},"
- x << "numLevels: #{MappingObject.javascriptify_variable(p.num_levels)} "
- x << "}"
- polylines_for_polygon << x
- end
-
- polylines_for_polygon = "[" + polylines_for_polygon.join(",") + "]"
-
- a = "new GPolygon.fromEncoded({polylines: #{polylines_for_polygon},"
- a << "fill: #{MappingObject.javascriptify_variable(@fill)},"
- a << "color: #{MappingObject.javascriptify_variable(@color)},"
- a << "opacity: #{MappingObject.javascriptify_variable(@opacity)},"
- a << "outline: #{MappingObject.javascriptify_variable(@outline)}"
- a << "})"
- end
- end
-
- class ELabel
- attr_accessor :point, :text, :style
- include MappingObject
-
- def initialize(point, text=nil, style=nil)
- @point = point
- @text = text
- @style = style
- end
-
- def create
- a = "new ELabel(#{MappingObject.javascriptify_variable(@point)}"
- a << ",#{MappingObject.javascriptify_variable(@text)}" if @text
- a << ",#{MappingObject.javascriptify_variable(@style)}" if @style
- a << ")"
- end
- end
-
-
- #A GGeoXml object gets data from a GeoRSS or KML feed and displays it. Use overlay_init to add it to a map at initialization time.
- class GGeoXml
- include MappingObject
-
- attr_accessor :url
-
- def initialize(url)
- @url = url
- end
-
- def create
- "new GGeoXml(#{MappingObject.javascriptify_variable(@url)})"
- end
-
- end
-
- #A GOverlay representing a group of GMarkers. The GMarkers can be identified with an id, which can be used to show the info window of a specific marker, in reponse, for example, to a click on a link. The whole group can be shown on and off at once. It should be declared global at initialization time to be useful.
- class GMarkerGroup
- include MappingObject
- attr_accessor :active, :markers, :markers_by_id
-
- def initialize(active = true , markers = nil)
- @active = active
- @markers = []
- @markers_by_id = {}
- if markers.is_a?(Array)
- @markers = markers
- elsif markers.is_a?(Hash)
- @markers_by_id = markers
- end
- end
-
- def create
- "new GMarkerGroup(#{MappingObject.javascriptify_variable(@active)},#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@markers_by_id)})"
- end
- end
-
- #Can be used to implement a clusterer, similar to the clusterer below, except that there is more stuff to manage explicitly byt the programmer (but this is also more flexible). See the README for usage esamples.
- class GMarkerManager
- include MappingObject
-
- attr_accessor :map,:options,:managed_markers
-
- #options can be :border_padding, :max_zoom, :track_markers and :managed_markers: managed_markers must be an array of ManagedMarker objects
- def initialize(map, options = {})
- @map = map
- @managed_markers = Array(options.delete(:managed_markers)) #[] if nil
- @options = options
- end
-
- def create
- puts @options.inspect
- "addMarkersToManager(new GMarkerManager(#{MappingObject.javascriptify_variable(@map)},#{MappingObject.javascriptify_variable(@options)}),#{MappingObject.javascriptify_variable(@managed_markers)})"
- end
-
- end
-
- #A set of similarly managed markers: They share the same minZoom and maxZoom.
- class ManagedMarker
- include MappingObject
-
- attr_accessor :markers,:min_zoom, :max_zoom
-
- def initialize(markers,min_zoom,max_zoom = nil)
- @markers = markers
- @min_zoom = min_zoom
- @max_zoom = max_zoom
- end
-
- def create
- "new ManagedMarker(#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@min_zoom)},#{MappingObject.javascriptify_variable(@max_zoom)})"
- end
-
- end
-
- #Makes the link with the Clusterer2 library by Jef Poskanzer (slightly modified though). Is a GOverlay making clusters out of its GMarkers, so that GMarkers very close to each other appear as one when the zoom is low. When the zoom gets higher, the individual markers are drawn.
- class Clusterer
- include MappingObject
- attr_accessor :markers,:icon, :max_visible_markers, :grid_size, :min_markers_per_cluster , :max_lines_per_info_box
-
- def initialize(markers = [], options = {})
- @markers = markers
- @icon = options[:icon] || GIcon::DEFAULT
- @max_visible_markers = options[:max_visible_markers] || 150
- @grid_size = options[:grid_size] || 5
- @min_markers_per_cluster = options[:min_markers_per_cluster] || 5
- @max_lines_per_info_box = options[:max_lines_per_info_box] || 10
- end
-
- def create
- js_marker = '[' + @markers.collect do |marker|
- add_description(marker)
- end.join(",") + ']'
-
- "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@max_visible_markers)},#{MappingObject.javascriptify_variable(@grid_size)},#{MappingObject.javascriptify_variable(@min_markers_per_cluster)},#{MappingObject.javascriptify_variable(@max_lines_per_info_box)})"
- end
-
- private
- def add_description(marker)
- "addDescriptionToMarker(#{MappingObject.javascriptify_variable(marker)},#{MappingObject.javascriptify_variable(marker.options[:description] || marker.options[:title] || '')})"
- end
- end
-
- #Makes the link with the MGeoRSS extension by Mikel Maron (a bit modified though). It lets you overlay on top of Google Maps the items present in a RSS feed that has GeoRss data. This data can be either in W3C Geo vocabulary or in the GeoRss Simple format. See http://georss.org to know more about GeoRss.
- class GeoRssOverlay
- include MappingObject
- attr_accessor :url, :proxy, :icon, :options
-
- #You can pass the following options:
- #- :icon: An icon for the items of the feed. Defaults to the classic red balloon icon.
- #- :proxy: An URL on your server where fetching the RSS feed will be taken care of.
- #- :list_div: In case you want a list of all the markers, with a link on which you can click in order to display the info on the marker, use this option to indicate the ID of the div (that you must place yourself).
- #- :list_item_class: class of the DIV containing each item of the list. Ignored if option :list_div is not set.
- #- :limit: Maximum number of items to display on the map.
- #- :content_div: Instead of having an info window appear, indicates the ID of the DIV where this info should be displayed.
- def initialize(url, options = {})
- @url = url
- @icon = options.delete(:icon) || GIcon::DEFAULT
- @proxy = options.delete(:proxy) || Variable::UNDEFINED
- @options = options
- end
-
- def create
- "new GeoRssOverlay(#{MappingObject.javascriptify_variable(@url)},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@proxy)},#{MappingObject.javascriptify_variable(@options)})"
- end
- end
-
- end
-end
diff --git a/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb b/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb
deleted file mode 100644
index bcde4e28..00000000
--- a/vendor/plugins/ym4r_gm/lib/gm_plugin/point.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module Ym4r
- module GmPlugin
- #A point in pixel coordinates
- class GPoint < Struct.new(:x,:y)
- include MappingObject
- def create
- "new GPoint(#{x},#{y})"
- end
- end
- #A rectangular that contains all the pixel points passed as arguments
- class GBounds
- include MappingObject
- attr_accessor :points
- #Accepts both an array of GPoint and an array of 2-element arrays
- def initialize(points)
- if !points.empty? and points[0].is_a?(Array)
- @points = points.collect { |pt| GPoint.new(pt[0],pt[1]) }
- else
- @points = points
- end
- end
- def create
- "new GBounds([#{@points.map { |pt| pt.to_javascript}.join(",")}])"
- end
- end
- #A size object, in pixel space
- class GSize < Struct.new(:width,:height)
- include MappingObject
- def create
- "new GSize(#{width},#{height})"
- end
- end
- end
-end
diff --git a/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb b/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb
deleted file mode 100644
index 24c9068f..00000000
--- a/vendor/plugins/ym4r_gm/lib/ym4r_gm.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'gm_plugin/key'
-require 'gm_plugin/mapping'
-require 'gm_plugin/map'
-require 'gm_plugin/control'
-require 'gm_plugin/point'
-require 'gm_plugin/overlay'
-require 'gm_plugin/layer'
-require 'gm_plugin/helper'
-require 'gm_plugin/geocoding'
-
-include Ym4r::GmPlugin
diff --git a/vendor/plugins/ym4r_gm/rakefile.rb b/vendor/plugins/ym4r_gm/rakefile.rb
deleted file mode 100644
index e2d46bfd..00000000
--- a/vendor/plugins/ym4r_gm/rakefile.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-
-desc 'Default: run unit tests.'
-task :default => :test
-
-desc 'Test the gm plugin.'
-Rake::TestTask.new(:test) do |t|
- t.libs << 'lib'
- t.pattern = 'test/**/*_test.rb'
- t.verbose = true
-end
-
-desc 'Generate documentation for the gm plugin.'
-Rake::RDocTask.new(:rdoc) do |rdoc|
- rdoc.rdoc_dir = 'ym4r_gm-doc'
- rdoc.title = 'GM'
- rdoc.options << '--line-numbers' << '--inline-source'
- rdoc.rdoc_files.include('README')
- rdoc.rdoc_files.include('lib/**/*.rb')
-end
diff --git a/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake b/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake
deleted file mode 100644
index 40745d0e..00000000
--- a/vendor/plugins/ym4r_gm/tasks/gm_tasks.rake
+++ /dev/null
@@ -1,4 +0,0 @@
-# desc "Explaining what the task does"
-# task :gm do
-# # Task goes here
-# end
\ No newline at end of file
diff --git a/vendor/plugins/ym4r_gm/test/gm_test.rb b/vendor/plugins/ym4r_gm/test/gm_test.rb
deleted file mode 100644
index cea8447a..00000000
--- a/vendor/plugins/ym4r_gm/test/gm_test.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-$:.unshift(File.dirname(__FILE__) + '/../lib')
-
-require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
-
-
-require 'ym4r_gm'
-require 'test/unit'
-
-include Ym4r::GmPlugin
-
-class TestGoogleMaps< Test::Unit::TestCase
- def test_javascriptify_method
- assert_equal("addOverlayToHello",MappingObject::javascriptify_method("add_overlay_to_hello"))
- end
-
- def test_javascriptify_variable_mapping_object
- map = GMap.new("div")
- assert_equal(map.to_javascript,MappingObject::javascriptify_variable(map))
- end
-
- def test_javascriptify_variable_numeric
- assert_equal("123.4",MappingObject::javascriptify_variable(123.4))
- end
-
- def test_javascriptify_variable_array
- map = GMap.new("div")
- assert_equal("[123.4,#{map.to_javascript},[123.4,#{map.to_javascript}]]",MappingObject::javascriptify_variable([123.4,map,[123.4,map]]))
- end
-
- def test_javascriptify_variable_hash
- map = GMap.new("div")
- test_str = MappingObject::javascriptify_variable("hello" => map, "chopotopoto" => [123.55,map])
- assert("{hello : #{map.to_javascript},chopotopoto : [123.55,#{map.to_javascript}]}" == test_str || "{chopotopoto : [123.55,#{map.to_javascript}],hello : #{map.to_javascript}}" == test_str)
- end
-
- def test_method_call_on_mapping_object
- map = GMap.new("div","map")
- assert_equal("map.addHello(123.4);",map.add_hello(123.4).to_s)
- end
-
- def test_nested_calls_on_mapping_object
- gmap = GMap.new("div","map")
- assert_equal("map.addHello(map.hoYoYo(123.4),map);",gmap.add_hello(gmap.ho_yo_yo(123.4),gmap).to_s)
- end
-
- def test_declare_variable_latlng
- point = GLatLng.new([123.4,123.6])
- assert_equal("var point = new GLatLng(123.4,123.6);",point.declare("point"))
- assert_equal("point",point.variable)
- end
-
- def test_array_indexing
- obj = Variable.new("obj")
- assert_equal("obj[0]",obj[0].variable)
- end
-
- def test_google_maps_geocoding
-
-
- placemarks = Geocoding.get("Rue Clovis Paris")
- assert_equal(Geocoding::GEO_SUCCESS,placemarks.status)
- assert_equal(1,placemarks.length)
- placemark = placemarks[0]
- assert_equal("FR",placemark.country_code)
- assert_equal("Paris",placemark.locality)
- assert_equal("75005",placemark.postal_code)
-
- #test iwht multiple placemarks
- placemarks = Geocoding.get('hoogstraat, nl')
- assert_equal(Geocoding::GEO_SUCCESS,placemarks.status)
- assert(placemarks.length > 1)
- assert(placemarks[0].latitude != placemarks[1].latitude )
-
-
- end
-
-
-end
-