Grosse MàJ
This commit is contained in:
211
P5B/ruby/3dossmanno_annuaire/README
Normal file
211
P5B/ruby/3dossmanno_annuaire/README
Normal file
@ -0,0 +1,211 @@
|
||||
== Welcome to Rails
|
||||
|
||||
Rails is a web-application and persistence framework that includes everything
|
||||
needed to create database-backed web-applications according to the
|
||||
Model-View-Control pattern of separation. 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, start a new rails application using the rails command
|
||||
and your application name. Ex: rails myapp
|
||||
(If you've downloaded rails in a complete tgz or zip, this step is already done)
|
||||
2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
|
||||
3. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"
|
||||
4. Follow the guidelines to start developing your application
|
||||
|
||||
|
||||
== Web Servers
|
||||
|
||||
By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise
|
||||
Rails will use the WEBrick, the webserver that ships with Ruby. When you run script/server,
|
||||
Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures
|
||||
that you can always get up and running quickly.
|
||||
|
||||
Mongrel is a Ruby-based webserver with a C-component (which requires compilation) that is
|
||||
suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
|
||||
getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
|
||||
More info at: http://mongrel.rubyforge.org
|
||||
|
||||
If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than
|
||||
Mongrel and WEBrick and also suited for production use, but requires additional
|
||||
installation and currently only works well on OS X/Unix (Windows users are encouraged
|
||||
to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from
|
||||
http://www.lighttpd.net.
|
||||
|
||||
And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby
|
||||
web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not
|
||||
for production.
|
||||
|
||||
But of course its also possible to run Rails on any platform that supports FCGI.
|
||||
Apache, LiteSpeed, IIS are just a few. For more information on FCGI,
|
||||
please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI
|
||||
|
||||
|
||||
== 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/ including:
|
||||
|
||||
* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
|
||||
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
|
||||
|
||||
These two online (and free) books will bring you up to speed on the Ruby language
|
||||
and also on programming in general.
|
||||
|
||||
|
||||
== Breakpoints
|
||||
|
||||
Breakpoint support is available through the script/breakpointer client. This
|
||||
means that you can break out of execution at any point in the code, investigate
|
||||
and change the model, AND then resume execution! Example:
|
||||
|
||||
class WeblogController < ActionController::Base
|
||||
def index
|
||||
@posts = Post.find(:all)
|
||||
breakpoint "Breaking out from the list"
|
||||
end
|
||||
end
|
||||
|
||||
So the controller will accept the action, run the first line, then present you
|
||||
with a IRB prompt in the breakpointer window. Here you can do things like:
|
||||
|
||||
Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
|
||||
|
||||
>> @posts.inspect
|
||||
=> "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
|
||||
#<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
|
||||
>> @posts.first.title = "hello from a breakpoint"
|
||||
=> "hello from a breakpoint"
|
||||
|
||||
...and even better is that you can examine how your runtime objects actually work:
|
||||
|
||||
>> f = @posts.first
|
||||
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
|
||||
>> f.
|
||||
Display all 152 possibilities? (y or n)
|
||||
|
||||
Finally, when you're ready to resume execution, you press CTRL-D
|
||||
|
||||
|
||||
== Console
|
||||
|
||||
You can interact with the domain model by starting the console through <tt>script/console</tt>.
|
||||
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.
|
||||
Passing an argument will specify a different environment, like <tt>script/console production</tt>.
|
||||
|
||||
To reload your controllers and models after launching the console run <tt>reload!</tt>
|
||||
|
||||
To reload your controllers and models after launching the console run <tt>reload!</tt>
|
||||
|
||||
|
||||
|
||||
== Description of contents
|
||||
|
||||
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.
|
||||
Most models will descend from ActiveRecord::Base.
|
||||
|
||||
app/views
|
||||
Holds the template files for the view that should be named like
|
||||
weblogs/index.rhtml for the WeblogsController#index action. All views use eRuby
|
||||
syntax.
|
||||
|
||||
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
|
||||
<tt>layout :default</tt> and create a file named default.rhtml. Inside default.rhtml,
|
||||
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 script/generate 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.
|
||||
|
||||
components
|
||||
Self-contained mini-applications that can bundle together controllers, models, and views.
|
||||
|
||||
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 <tt>rake doc:app</tt>
|
||||
|
||||
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 script/generate scripts, 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.
|
||||
This directory is in the load path.
|
10
P5B/ruby/3dossmanno_annuaire/Rakefile
Normal file
10
P5B/ruby/3dossmanno_annuaire/Rakefile
Normal file
@ -0,0 +1,10 @@
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
||||
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
require 'tasks/rails'
|
@ -0,0 +1,7 @@
|
||||
# Filters added to this controller apply to all controllers in the application.
|
||||
# Likewise, all the methods added will be available for all controllers.
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
# Pick a unique cookie name to distinguish our session data from others'
|
||||
session :session_key => '_3dossmanno_annuaire_session_id'
|
||||
end
|
@ -0,0 +1,31 @@
|
||||
# This controller handles the login/logout function of the site.
|
||||
class SessionsController < ApplicationController
|
||||
# Be sure to include AuthenticationSystem in Application Controller instead
|
||||
include AuthenticatedSystem
|
||||
|
||||
# render new.rhtml
|
||||
def new
|
||||
end
|
||||
|
||||
def create
|
||||
self.current_user = User.authenticate(params[:login], params[:password])
|
||||
if logged_in?
|
||||
if params[:remember_me] == "1"
|
||||
self.current_user.remember_me
|
||||
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
|
||||
end
|
||||
redirect_back_or_default('/')
|
||||
flash[:notice] = "Logged in successfully"
|
||||
else
|
||||
render :action => 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
self.current_user.forget_me if logged_in?
|
||||
cookies.delete :auth_token
|
||||
reset_session
|
||||
flash[:notice] = "You have been logged out."
|
||||
redirect_back_or_default('/')
|
||||
end
|
||||
end
|
@ -0,0 +1,30 @@
|
||||
class UsersController < ApplicationController
|
||||
# Be sure to include AuthenticationSystem in Application Controller instead
|
||||
include AuthenticatedSystem
|
||||
|
||||
# render new.rhtml
|
||||
def new
|
||||
end
|
||||
|
||||
def create
|
||||
cookies.delete :auth_token
|
||||
reset_session
|
||||
@user = User.new(params[:user])
|
||||
@user.save!
|
||||
self.current_user = @user
|
||||
redirect_back_or_default('/')
|
||||
flash[:notice] = "Thanks for signing up!"
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
render :action => 'new'
|
||||
end
|
||||
|
||||
def activate
|
||||
self.current_user = params[:activation_code].blank? ? :false : User.find_by_activation_code(params[:activation_code])
|
||||
if logged_in? && !current_user.activated?
|
||||
current_user.activate
|
||||
flash[:notice] = "Signup complete!"
|
||||
end
|
||||
redirect_back_or_default('/')
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,79 @@
|
||||
class UtilisateursController < ApplicationController
|
||||
# GET /utilisateurs
|
||||
# GET /utilisateurs.xml
|
||||
def index
|
||||
@utilisateurs = Utilisateur.find(:all)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.rhtml
|
||||
format.xml { render :xml => @utilisateurs.to_xml }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /utilisateurs/1
|
||||
# GET /utilisateurs/1.xml
|
||||
def show
|
||||
@utilisateur = Utilisateur.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.rhtml
|
||||
format.xml { render :xml => @utilisateur.to_xml }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /utilisateurs/new
|
||||
def new
|
||||
@utilisateur = Utilisateur.new
|
||||
end
|
||||
|
||||
# GET /utilisateurs/1;edit
|
||||
def edit
|
||||
@utilisateur = Utilisateur.find(params[:id])
|
||||
end
|
||||
|
||||
# POST /utilisateurs
|
||||
# POST /utilisateurs.xml
|
||||
def create
|
||||
@utilisateur = Utilisateur.new(params[:utilisateur])
|
||||
|
||||
respond_to do |format|
|
||||
if @utilisateur.save
|
||||
flash[:notice] = 'Utilisateur was successfully created.'
|
||||
format.html { redirect_to utilisateur_url(@utilisateur) }
|
||||
format.xml { head :created, :location => utilisateur_url(@utilisateur) }
|
||||
else
|
||||
format.html { render :action => "new" }
|
||||
format.xml { render :xml => @utilisateur.errors.to_xml }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /utilisateurs/1
|
||||
# PUT /utilisateurs/1.xml
|
||||
def update
|
||||
@utilisateur = Utilisateur.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
if @utilisateur.update_attributes(params[:utilisateur])
|
||||
flash[:notice] = 'Utilisateur was successfully updated.'
|
||||
format.html { redirect_to utilisateur_url(@utilisateur) }
|
||||
format.xml { head :ok }
|
||||
else
|
||||
format.html { render :action => "edit" }
|
||||
format.xml { render :xml => @utilisateur.errors.to_xml }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /utilisateurs/1
|
||||
# DELETE /utilisateurs/1.xml
|
||||
def destroy
|
||||
@utilisateur = Utilisateur.find(params[:id])
|
||||
@utilisateur.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to utilisateurs_url }
|
||||
format.xml { head :ok }
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,3 @@
|
||||
# Methods added to this helper will be available to all templates in the application.
|
||||
module ApplicationHelper
|
||||
end
|
@ -0,0 +1,2 @@
|
||||
module SessionsHelper
|
||||
end
|
2
P5B/ruby/3dossmanno_annuaire/app/helpers/users_helper.rb
Normal file
2
P5B/ruby/3dossmanno_annuaire/app/helpers/users_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module UsersHelper
|
||||
end
|
@ -0,0 +1,2 @@
|
||||
module UtilisateursHelper
|
||||
end
|
98
P5B/ruby/3dossmanno_annuaire/app/models/user.rb
Normal file
98
P5B/ruby/3dossmanno_annuaire/app/models/user.rb
Normal file
@ -0,0 +1,98 @@
|
||||
require 'digest/sha1'
|
||||
class User < ActiveRecord::Base
|
||||
# Virtual attribute for the unencrypted password
|
||||
attr_accessor :password
|
||||
|
||||
validates_presence_of :login, :email
|
||||
validates_presence_of :password, :if => :password_required?
|
||||
validates_presence_of :password_confirmation, :if => :password_required?
|
||||
validates_length_of :password, :within => 4..40, :if => :password_required?
|
||||
validates_confirmation_of :password, :if => :password_required?
|
||||
validates_length_of :login, :within => 3..40
|
||||
validates_length_of :email, :within => 3..100
|
||||
validates_uniqueness_of :login, :email, :case_sensitive => false
|
||||
before_save :encrypt_password
|
||||
before_create :make_activation_code
|
||||
# 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 :login, :email, :password, :password_confirmation
|
||||
|
||||
# Activates the user in the database.
|
||||
def activate
|
||||
@activated = true
|
||||
self.activated_at = Time.now.utc
|
||||
self.activation_code = nil
|
||||
save(false)
|
||||
end
|
||||
|
||||
def activated?
|
||||
# the existence of an activation code means they have not activated yet
|
||||
activation_code.nil?
|
||||
end
|
||||
|
||||
# Returns true if the user has just been activated.
|
||||
def recently_activated?
|
||||
@activated
|
||||
end
|
||||
|
||||
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
|
||||
def self.authenticate(login, password)
|
||||
u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] # need to get the salt
|
||||
u && u.authenticated?(password) ? u : nil
|
||||
end
|
||||
|
||||
# Encrypts some data with the salt.
|
||||
def self.encrypt(password, salt)
|
||||
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
||||
end
|
||||
|
||||
# Encrypts the password with the user salt
|
||||
def encrypt(password)
|
||||
self.class.encrypt(password, salt)
|
||||
end
|
||||
|
||||
def authenticated?(password)
|
||||
crypted_password == encrypt(password)
|
||||
end
|
||||
|
||||
def remember_token?
|
||||
remember_token_expires_at && Time.now.utc < remember_token_expires_at
|
||||
end
|
||||
|
||||
# These create and unset the fields required for remembering users between browser closes
|
||||
def remember_me
|
||||
remember_me_for 2.weeks
|
||||
end
|
||||
|
||||
def remember_me_for(time)
|
||||
remember_me_until time.from_now.utc
|
||||
end
|
||||
|
||||
def remember_me_until(time)
|
||||
self.remember_token_expires_at = time
|
||||
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
|
||||
save(false)
|
||||
end
|
||||
|
||||
def forget_me
|
||||
self.remember_token_expires_at = nil
|
||||
self.remember_token = nil
|
||||
save(false)
|
||||
end
|
||||
|
||||
protected
|
||||
# before filter
|
||||
def encrypt_password
|
||||
return if password.blank?
|
||||
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
|
||||
self.crypted_password = encrypt(password)
|
||||
end
|
||||
|
||||
def password_required?
|
||||
crypted_password.blank? || !password.blank?
|
||||
end
|
||||
|
||||
def make_activation_code
|
||||
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
|
||||
end
|
||||
end
|
24
P5B/ruby/3dossmanno_annuaire/app/models/user_mailer.rb
Normal file
24
P5B/ruby/3dossmanno_annuaire/app/models/user_mailer.rb
Normal file
@ -0,0 +1,24 @@
|
||||
class UserMailer < ActionMailer::Base
|
||||
def signup_notification(user)
|
||||
setup_email(user)
|
||||
@subject += 'Please activate your new account'
|
||||
|
||||
@body[:url] = "http://YOURSITE/activate/#{user.activation_code}"
|
||||
|
||||
end
|
||||
|
||||
def activation(user)
|
||||
setup_email(user)
|
||||
@subject += 'Your account has been activated!'
|
||||
@body[:url] = "http://YOURSITE/"
|
||||
end
|
||||
|
||||
protected
|
||||
def setup_email(user)
|
||||
@recipients = "#{user.email}"
|
||||
@from = "ADMINEMAIL"
|
||||
@subject = "[YOURSITE] "
|
||||
@sent_on = Time.now
|
||||
@body[:user] = user
|
||||
end
|
||||
end
|
11
P5B/ruby/3dossmanno_annuaire/app/models/user_observer.rb
Normal file
11
P5B/ruby/3dossmanno_annuaire/app/models/user_observer.rb
Normal file
@ -0,0 +1,11 @@
|
||||
class UserObserver < ActiveRecord::Observer
|
||||
def after_create(user)
|
||||
UserMailer.deliver_signup_notification(user)
|
||||
end
|
||||
|
||||
def after_save(user)
|
||||
|
||||
UserMailer.deliver_activation(user) if user.recently_activated?
|
||||
|
||||
end
|
||||
end
|
2
P5B/ruby/3dossmanno_annuaire/app/models/utilisateur.rb
Normal file
2
P5B/ruby/3dossmanno_annuaire/app/models/utilisateur.rb
Normal file
@ -0,0 +1,2 @@
|
||||
class Utilisateur < ActiveRecord::Base
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
||||
<title>Utilisateurs: <%= controller.action_name %></title>
|
||||
<%= stylesheet_link_tag 'scaffold' %>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p style="color: green"><%= flash[:notice] %></p>
|
||||
|
||||
<%= yield %>
|
||||
|
||||
</body>
|
||||
</html>
|
14
P5B/ruby/3dossmanno_annuaire/app/views/sessions/new.rhtml
Normal file
14
P5B/ruby/3dossmanno_annuaire/app/views/sessions/new.rhtml
Normal file
@ -0,0 +1,14 @@
|
||||
<% form_tag session_path do -%>
|
||||
<p><label for="login">Login</label><br/>
|
||||
<%= text_field_tag 'login' %></p>
|
||||
|
||||
<p><label for="password">Password</label><br/>
|
||||
<%= password_field_tag 'password' %></p>
|
||||
|
||||
<!-- Uncomment this if you want this functionality
|
||||
<p><label for="remember_me">Remember me:</label>
|
||||
<%= check_box_tag 'remember_me' %></p>
|
||||
-->
|
||||
|
||||
<p><%= submit_tag 'Log in' %></p>
|
||||
<% end -%>
|
@ -0,0 +1,3 @@
|
||||
<%= @user.login %>, your account has been activated. You may now start adding your plugins:
|
||||
|
||||
<%= @url %>
|
@ -0,0 +1,8 @@
|
||||
Your account has been created.
|
||||
|
||||
Username: <%= @user.login %>
|
||||
Password: <%= @user.password %>
|
||||
|
||||
Visit this url to activate your account:
|
||||
|
||||
<%= @url %>
|
16
P5B/ruby/3dossmanno_annuaire/app/views/users/new.rhtml
Normal file
16
P5B/ruby/3dossmanno_annuaire/app/views/users/new.rhtml
Normal file
@ -0,0 +1,16 @@
|
||||
<%= error_messages_for :user %>
|
||||
<% form_for :user, :url => users_path do |f| -%>
|
||||
<p><label for="login">Login</label><br/>
|
||||
<%= f.text_field :login %></p>
|
||||
|
||||
<p><label for="email">Email</label><br/>
|
||||
<%= f.text_field :email %></p>
|
||||
|
||||
<p><label for="password">Password</label><br/>
|
||||
<%= f.password_field :password %></p>
|
||||
|
||||
<p><label for="password_confirmation">Confirm Password</label><br/>
|
||||
<%= f.password_field :password_confirmation %></p>
|
||||
|
||||
<p><%= submit_tag 'Sign up' %></p>
|
||||
<% end -%>
|
@ -0,0 +1,62 @@
|
||||
<h1>Edition utilisateur</h1>
|
||||
|
||||
<%= error_messages_for :utilisateur %>
|
||||
|
||||
<% form_for(:utilisateur, :url => utilisateur_path(@utilisateur), :html => { :method => :put }) do |f| %>
|
||||
<p>
|
||||
<b>Nom</b><br />
|
||||
<%= f.text_field :nom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Pr<50>nom</b><br />
|
||||
<%= f.text_field :prenom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Classe</b><br />
|
||||
<%= f.text_field :classe %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Adresse courriel</b><br />
|
||||
<%= f.text_field :email %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Age</b><br />
|
||||
<%= f.text_field :age %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Rue</b><br />
|
||||
<%= f.text_field :rue %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Code postal</b><br />
|
||||
<%= f.text_field :codePostal %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Ville</b><br />
|
||||
<%= f.text_field :ville %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Photo</b><br />
|
||||
<%= f.text_field :photo %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Type</b><br />
|
||||
<%= f.text_field :type %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= submit_tag "Mise <20> jour" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to 'Montrer', utilisateur_path(@utilisateur) %> |
|
||||
<%= link_to 'Retour', utilisateurs_path %>
|
@ -0,0 +1,38 @@
|
||||
<h1>Listing utilisateurs</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Pr<50>nom</th>
|
||||
<th>Classe</th>
|
||||
<th>Email</th>
|
||||
<th>Age</th>
|
||||
<th>Rue</th>
|
||||
<th>Code Postal</th>
|
||||
<th>Ville</th>
|
||||
<th>Photo</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
|
||||
<% for utilisateur in @utilisateurs %>
|
||||
<tr>
|
||||
<td><%=h utilisateur.nom %></td>
|
||||
<td><%=h utilisateur.prenom %></td>
|
||||
<td><%=h utilisateur.classe %></td>
|
||||
<td><%=h utilisateur.email %></td>
|
||||
<td><%=h utilisateur.age %></td>
|
||||
<td><%=h utilisateur.rue %></td>
|
||||
<td><%=h utilisateur.codePostal %></td>
|
||||
<td><%=h utilisateur.ville %></td>
|
||||
<td><%=h utilisateur.photo %></td>
|
||||
<td><%=h utilisateur.type %></td>
|
||||
<td><%= link_to 'Montrer', utilisateur_path(utilisateur) %></td>
|
||||
<td><%= link_to 'Editer', edit_utilisateur_path(utilisateur) %></td>
|
||||
<td><%= link_to 'Supprimer', utilisateur_path(utilisateur), :confirm => 'Etes vous s<>r ?', :method => :delete %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<%= link_to 'Nouvel utilisateur', new_utilisateur_path %>
|
@ -0,0 +1,61 @@
|
||||
<h1>New utilisateur</h1>
|
||||
|
||||
<%= error_messages_for :utilisateur %>
|
||||
|
||||
<% form_for(:utilisateur, :url => utilisateurs_path) do |f| %>
|
||||
<p>
|
||||
<b>Nom</b><br />
|
||||
<%= f.text_field :nom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Prenom</b><br />
|
||||
<%= f.text_field :prenom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Classe</b><br />
|
||||
<%= f.text_field :classe %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Email</b><br />
|
||||
<%= f.text_field :email %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Age</b><br />
|
||||
<%= f.text_field :age %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Rue</b><br />
|
||||
<%= f.text_field :rue %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Codepostal</b><br />
|
||||
<%= f.text_field :codePostal %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Ville</b><br />
|
||||
<%= f.text_field :ville %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Photo</b><br />
|
||||
<%= f.text_field :photo %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Type</b><br />
|
||||
<%= f.text_field :type %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= submit_tag "Create" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to 'Back', utilisateurs_path %>
|
@ -0,0 +1,53 @@
|
||||
<p>
|
||||
<b>Nom:</b>
|
||||
<%=h @utilisateur.nom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Prenom:</b>
|
||||
<%=h @utilisateur.prenom %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Classe:</b>
|
||||
<%=h @utilisateur.classe %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Email:</b>
|
||||
<%=h @utilisateur.email %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Age:</b>
|
||||
<%=h @utilisateur.age %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Rue:</b>
|
||||
<%=h @utilisateur.rue %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Codepostal:</b>
|
||||
<%=h @utilisateur.codePostal %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Ville:</b>
|
||||
<%=h @utilisateur.ville %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Photo:</b>
|
||||
<%=h @utilisateur.photo %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Type:</b>
|
||||
<%=h @utilisateur.type %>
|
||||
</p>
|
||||
|
||||
|
||||
<%= link_to 'Edit', edit_utilisateur_path(@utilisateur) %> |
|
||||
<%= link_to 'Back', utilisateurs_path %>
|
14
P5B/ruby/3dossmanno_annuaire/config/amazon_s3.yml
Normal file
14
P5B/ruby/3dossmanno_annuaire/config/amazon_s3.yml
Normal file
@ -0,0 +1,14 @@
|
||||
development:
|
||||
bucket_name: appname_development
|
||||
access_key_id:
|
||||
secret_access_key:
|
||||
|
||||
test:
|
||||
bucket_name: appname_test
|
||||
access_key_id:
|
||||
secret_access_key:
|
||||
|
||||
production:
|
||||
bucket_name: appname
|
||||
access_key_id:
|
||||
secret_access_key:
|
39
P5B/ruby/3dossmanno_annuaire/config/boot.rb
Normal file
39
P5B/ruby/3dossmanno_annuaire/config/boot.rb
Normal file
@ -0,0 +1,39 @@
|
||||
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
|
||||
|
||||
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
||||
|
||||
unless defined?(Rails::Initializer)
|
||||
if File.directory?("#{RAILS_ROOT}/vendor/rails")
|
||||
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
||||
else
|
||||
require 'rubygems'
|
||||
|
||||
rails_gem_version =
|
||||
if defined? RAILS_GEM_VERSION
|
||||
RAILS_GEM_VERSION
|
||||
else
|
||||
File.read("#{File.dirname(__FILE__)}/environment.rb") =~ /^[^#]*RAILS_GEM_VERSION\s+=\s+'([\d.]+)'/
|
||||
$1
|
||||
end
|
||||
|
||||
if rails_gem_version
|
||||
rails_gem = Gem.cache.search('rails', "=#{rails_gem_version}.0").sort_by { |g| g.version.version }.last
|
||||
|
||||
if rails_gem
|
||||
gem "rails", "=#{rails_gem.version.version}"
|
||||
require rails_gem.full_gem_path + '/lib/initializer'
|
||||
else
|
||||
STDERR.puts %(Cannot find gem for Rails =#{rails_gem_version}.0:
|
||||
Install the missing gem with 'gem install -v=#{rails_gem_version} rails', or
|
||||
change environment.rb to define RAILS_GEM_VERSION with your desired version.
|
||||
)
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
gem "rails"
|
||||
require 'initializer'
|
||||
end
|
||||
end
|
||||
|
||||
Rails::Initializer.run(:set_load_path)
|
||||
end
|
38
P5B/ruby/3dossmanno_annuaire/config/database.yml
Normal file
38
P5B/ruby/3dossmanno_annuaire/config/database.yml
Normal file
@ -0,0 +1,38 @@
|
||||
# MySQL (default setup). Versions 4.1 and 5.0 are recommended.
|
||||
#
|
||||
# Install the MySQL driver:
|
||||
# gem install mysql
|
||||
# On MacOS X:
|
||||
# gem install mysql -- --include=/usr/local/lib
|
||||
# On Windows:
|
||||
# gem install mysql
|
||||
# Choose the win32 build.
|
||||
# Install MySQL and put its /bin directory on your path.
|
||||
#
|
||||
# And be sure to use new-style password hashing:
|
||||
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
|
||||
development:
|
||||
adapter: mysql
|
||||
database: 073dossmanno_dev
|
||||
username: 3dossmanno
|
||||
password: 3dossmanno
|
||||
host: pipit
|
||||
encoding: utf8
|
||||
|
||||
# Warning: The database defined as 'test' will be erased and
|
||||
# re-generated from your development database when you run 'rake'.
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
adapter: mysql
|
||||
database: 073dossmanno_test
|
||||
username: 3dossmanno
|
||||
password: 3dossmanno
|
||||
host: pipit
|
||||
encoding: utf8
|
||||
|
||||
production:
|
||||
adapter: mysql
|
||||
database: 3dossmanno_annuaire_production
|
||||
username: root
|
||||
password:
|
||||
host: localhost
|
60
P5B/ruby/3dossmanno_annuaire/config/environment.rb
Normal file
60
P5B/ruby/3dossmanno_annuaire/config/environment.rb
Normal file
@ -0,0 +1,60 @@
|
||||
# Be sure to restart your web server when you modify this file.
|
||||
|
||||
# Uncomment below to force Rails into production mode when
|
||||
# you don't control web/app server and can't set it the proper way
|
||||
# ENV['RAILS_ENV'] ||= 'production'
|
||||
|
||||
# Specifies gem version of Rails to use when vendor/rails is not present
|
||||
RAILS_GEM_VERSION = '1.2.5' unless defined? RAILS_GEM_VERSION
|
||||
|
||||
# Bootstrap the Rails environment, frameworks, and default configuration
|
||||
require File.join(File.dirname(__FILE__), 'boot')
|
||||
|
||||
Rails::Initializer.run do |config|
|
||||
# Settings in config/environments/* take precedence over those specified here
|
||||
|
||||
# Skip frameworks you're not going to use (only works if using vendor/rails)
|
||||
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
||||
|
||||
# Only load the plugins named here, by default all plugins in vendor/plugins are loaded
|
||||
# config.plugins = %W( exception_notification ssl_requirement )
|
||||
|
||||
# Add additional load paths for your own custom dirs
|
||||
# config.load_paths += %W( #{RAILS_ROOT}/extras )
|
||||
|
||||
# Force all environments to use the same logger level
|
||||
# (by default production uses :info, the others :debug)
|
||||
# config.log_level = :debug
|
||||
|
||||
# Use the database for sessions instead of the file system
|
||||
# (create the session table with 'rake db:sessions:create')
|
||||
# config.action_controller.session_store = :active_record_store
|
||||
|
||||
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
||||
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
||||
# like if you have constraints or database-specific column types
|
||||
# config.active_record.schema_format = :sql
|
||||
|
||||
# Activate observers that should always be running
|
||||
# config.active_record.observers = :cacher, :garbage_collector
|
||||
|
||||
# Make Active Record use UTC-base instead of local time
|
||||
# config.active_record.default_timezone = :utc
|
||||
|
||||
# Add new inflection rules using the following format
|
||||
# (all these examples are active by default):
|
||||
# Inflector.inflections do |inflect|
|
||||
# inflect.plural /^(ox)$/i, '\1en'
|
||||
# inflect.singular /^(ox)en/i, '\1'
|
||||
# inflect.irregular 'person', 'people'
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
# See Rails::Configuration for more options
|
||||
end
|
||||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
# Mime::Type.register "application/x-mobile", :mobile
|
||||
|
||||
# Include your application configuration below
|
@ -0,0 +1,21 @@
|
||||
# Settings specified here will take precedence over those in config/environment.rb
|
||||
|
||||
# In the development environment your application's code is reloaded on
|
||||
# every request. This slows down response time but is perfect for development
|
||||
# since you don't have to restart the webserver when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# Log error messages when you accidentally call methods on nil.
|
||||
config.whiny_nils = true
|
||||
|
||||
# Enable the breakpoint server that script/breakpointer connects to
|
||||
config.breakpoint_server = true
|
||||
|
||||
# Show full error reports and disable caching
|
||||
config.action_controller.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
config.action_view.cache_template_extensions = false
|
||||
config.action_view.debug_rjs = true
|
||||
|
||||
# Don't care if the mailer can't send
|
||||
config.action_mailer.raise_delivery_errors = false
|
@ -0,0 +1,18 @@
|
||||
# Settings specified here will take precedence over those in config/environment.rb
|
||||
|
||||
# The production environment is meant for finished, "live" apps.
|
||||
# Code is not reloaded between requests
|
||||
config.cache_classes = true
|
||||
|
||||
# Use a different logger for distributed setups
|
||||
# config.logger = SyslogLogger.new
|
||||
|
||||
# Full error reports are disabled and caching is turned on
|
||||
config.action_controller.consider_all_requests_local = false
|
||||
config.action_controller.perform_caching = true
|
||||
|
||||
# Enable serving of images, stylesheets, and javascripts from an asset server
|
||||
# config.action_controller.asset_host = "http://assets.example.com"
|
||||
|
||||
# Disable delivery errors, bad email addresses will be ignored
|
||||
# config.action_mailer.raise_delivery_errors = false
|
19
P5B/ruby/3dossmanno_annuaire/config/environments/test.rb
Normal file
19
P5B/ruby/3dossmanno_annuaire/config/environments/test.rb
Normal file
@ -0,0 +1,19 @@
|
||||
# Settings specified here will take precedence over those in config/environment.rb
|
||||
|
||||
# The test environment is used exclusively to run your application's
|
||||
# test suite. You never need to work with it otherwise. Remember that
|
||||
# your test database is "scratch space" for the test suite and is wiped
|
||||
# and recreated between test runs. Don't rely on the data there!
|
||||
config.cache_classes = true
|
||||
|
||||
# Log error messages when you accidentally call methods on nil.
|
||||
config.whiny_nils = true
|
||||
|
||||
# Show full error reports and disable caching
|
||||
config.action_controller.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
|
||||
# Tell ActionMailer not to deliver emails to the real world.
|
||||
# The :test delivery method accumulates sent emails in the
|
||||
# ActionMailer::Base.deliveries array.
|
||||
config.action_mailer.delivery_method = :test
|
28
P5B/ruby/3dossmanno_annuaire/config/routes.rb
Normal file
28
P5B/ruby/3dossmanno_annuaire/config/routes.rb
Normal file
@ -0,0 +1,28 @@
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.resources :utilisateurs
|
||||
|
||||
map.ressources :users
|
||||
map.resource :session, :controller => 'sessions'
|
||||
|
||||
# The priority is based upon order of creation: first created -> highest priority.
|
||||
|
||||
# Sample of regular route:
|
||||
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
|
||||
# Keep in mind you can assign values other than :controller and :action
|
||||
|
||||
# Sample of named route:
|
||||
# map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
|
||||
# This route can be invoked with purchase_url(:id => product.id)
|
||||
|
||||
# You can have the root of your site routed by hooking up ''
|
||||
# -- just remember to delete public/index.html.
|
||||
# map.connect '', :controller => "welcome"
|
||||
|
||||
# Allow downloading Web Service WSDL as a file with an extension
|
||||
# instead of a file named 'wsdl'
|
||||
#map.connect ':controller/service.wsdl', :action => 'wsdl'
|
||||
|
||||
# Install the default route as the lowest priority.
|
||||
map.connect ':controller/:action/:id.:format'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
class CreateUtilisateurs < ActiveRecord::Migration
|
||||
db_name = ActiveRecord::Base::connection.current_database()
|
||||
execute "ALTER DATABASE #{db_name} CHARACTER SET utf8 COLLATE utf8_general_ci"
|
||||
|
||||
def self.up
|
||||
create_table :utilisateurs do |t|
|
||||
t.column :nom, :string
|
||||
t.column :prenom, :string
|
||||
t.column :classe, :string
|
||||
t.column :email, :string
|
||||
t.column :age, :int[3]
|
||||
t.column :rue, :string
|
||||
t.column :codePostal, :int[5]
|
||||
t.column :ville, :string
|
||||
t.column :photo, :image
|
||||
t.column :type, :string
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :utilisateurs
|
||||
end
|
||||
end
|
21
P5B/ruby/3dossmanno_annuaire/db/migrate/002_create_users.rb
Normal file
21
P5B/ruby/3dossmanno_annuaire/db/migrate/002_create_users.rb
Normal file
@ -0,0 +1,21 @@
|
||||
class CreateUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table "users", :force => true do |t|
|
||||
t.column :login, :string
|
||||
t.column :email, :string
|
||||
t.column :crypted_password, :string, :limit => 40
|
||||
t.column :salt, :string, :limit => 40
|
||||
t.column :created_at, :datetime
|
||||
t.column :updated_at, :datetime
|
||||
t.column :remember_token, :string
|
||||
t.column :remember_token_expires_at, :datetime
|
||||
|
||||
t.column :activation_code, :string, :limit => 40
|
||||
t.column :activated_at, :datetime
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "users"
|
||||
end
|
||||
end
|
37
P5B/ruby/3dossmanno_annuaire/db/schema.rb
Normal file
37
P5B/ruby/3dossmanno_annuaire/db/schema.rb
Normal file
@ -0,0 +1,37 @@
|
||||
# This file is autogenerated. Instead of editing this file, please use the
|
||||
# migrations feature of ActiveRecord to incrementally modify your database, and
|
||||
# then regenerate this schema definition.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 6) do
|
||||
|
||||
create_table "addresses", :force => true do |t|
|
||||
t.column "street", :text
|
||||
t.column "postal_code", :string
|
||||
t.column "city", :string
|
||||
t.column "country", :string
|
||||
end
|
||||
|
||||
create_table "customers", :force => true do |t|
|
||||
t.column "firstname", :string
|
||||
t.column "name", :string
|
||||
end
|
||||
|
||||
create_table "products", :force => true do |t|
|
||||
t.column "designation", :string
|
||||
t.column "description", :text
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
t.column "supplier_id", :integer
|
||||
t.column "customer_id", :integer
|
||||
t.column "type", :string
|
||||
end
|
||||
|
||||
create_table "suppliers", :force => true do |t|
|
||||
t.column "name", :string
|
||||
t.column "description", :text
|
||||
t.column "code", :string
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
end
|
2
P5B/ruby/3dossmanno_annuaire/doc/README_FOR_APP
Normal file
2
P5B/ruby/3dossmanno_annuaire/doc/README_FOR_APP
Normal file
@ -0,0 +1,2 @@
|
||||
Use this README file to introduce your application and point to useful places in the API for learning more.
|
||||
Run "rake appdoc" to generate API documentation for your models and controllers.
|
127
P5B/ruby/3dossmanno_annuaire/lib/authenticated_system.rb
Normal file
127
P5B/ruby/3dossmanno_annuaire/lib/authenticated_system.rb
Normal file
@ -0,0 +1,127 @@
|
||||
module AuthenticatedSystem
|
||||
protected
|
||||
# Returns true or false if the user is logged in.
|
||||
# Preloads @current_user with the user model if they're logged in.
|
||||
def logged_in?
|
||||
current_user != :false
|
||||
end
|
||||
|
||||
# Accesses the current user from the session. Set it to :false if login fails
|
||||
# so that future calls do not hit the database.
|
||||
def current_user
|
||||
@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
|
||||
end
|
||||
|
||||
# Store the given user in the session.
|
||||
def current_user=(new_user)
|
||||
session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
|
||||
@current_user = new_user
|
||||
end
|
||||
|
||||
# Check if the user is authorized
|
||||
#
|
||||
# Override this method in your controllers if you want to restrict access
|
||||
# to only a few actions or if you want to check if the user
|
||||
# has the correct rights.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # only allow nonbobs
|
||||
# def authorized?
|
||||
# current_user.login != "bob"
|
||||
# end
|
||||
def authorized?
|
||||
logged_in?
|
||||
end
|
||||
|
||||
# Filter method to enforce a login requirement.
|
||||
#
|
||||
# To require logins for all actions, use this in your controllers:
|
||||
#
|
||||
# before_filter :login_required
|
||||
#
|
||||
# To require logins for specific actions, use this in your controllers:
|
||||
#
|
||||
# before_filter :login_required, :only => [ :edit, :update ]
|
||||
#
|
||||
# To skip this in a subclassed controller:
|
||||
#
|
||||
# skip_before_filter :login_required
|
||||
#
|
||||
def login_required
|
||||
authorized? || access_denied
|
||||
end
|
||||
|
||||
# Redirect as appropriate when an access request fails.
|
||||
#
|
||||
# The default action is to redirect to the login screen.
|
||||
#
|
||||
# Override this method in your controllers if you want to have special
|
||||
# behavior in case the user is not authorized
|
||||
# to access the requested action. For example, a popup window might
|
||||
# simply close itself.
|
||||
def access_denied
|
||||
respond_to do |accepts|
|
||||
accepts.html do
|
||||
store_location
|
||||
redirect_to :controller => '/sessions', :action => 'new'
|
||||
end
|
||||
accepts.xml do
|
||||
headers["Status"] = "Unauthorized"
|
||||
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
||||
render :text => "Could't authenticate you", :status => '401 Unauthorized'
|
||||
end
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# Store the URI of the current request in the session.
|
||||
#
|
||||
# We can return to this location by calling #redirect_back_or_default.
|
||||
def store_location
|
||||
session[:return_to] = request.request_uri
|
||||
end
|
||||
|
||||
# Redirect to the URI stored by the most recent store_location call or
|
||||
# to the passed default.
|
||||
def redirect_back_or_default(default)
|
||||
session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
|
||||
session[:return_to] = nil
|
||||
end
|
||||
|
||||
# Inclusion hook to make #current_user and #logged_in?
|
||||
# available as ActionView helper methods.
|
||||
def self.included(base)
|
||||
base.send :helper_method, :current_user, :logged_in?
|
||||
end
|
||||
|
||||
# Called from #current_user. First attempt to login by the user id stored in the session.
|
||||
def login_from_session
|
||||
self.current_user = User.find_by_id(session[:user]) if session[:user]
|
||||
end
|
||||
|
||||
# Called from #current_user. Now, attempt to login by basic authentication information.
|
||||
def login_from_basic_auth
|
||||
username, passwd = get_auth_data
|
||||
self.current_user = User.authenticate(username, passwd) if username && passwd
|
||||
end
|
||||
|
||||
# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
|
||||
def login_from_cookie
|
||||
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
|
||||
if user && user.remember_token?
|
||||
user.remember_me
|
||||
cookies[:auth_token] = { :value => user.remember_token, :expires => user.remember_token_expires_at }
|
||||
self.current_user = user
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
|
||||
# gets BASIC auth info
|
||||
def get_auth_data
|
||||
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
|
||||
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
|
||||
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
|
||||
end
|
||||
end
|
@ -0,0 +1,26 @@
|
||||
module AuthenticatedTestHelper
|
||||
# Sets the current user in the session from the user fixtures.
|
||||
def login_as(user)
|
||||
@request.session[:user] = user ? users(user).id : nil
|
||||
end
|
||||
|
||||
def authorize_as(user)
|
||||
@request.env["HTTP_AUTHORIZATION"] = user ? "Basic #{Base64.encode64("#{users(user).login}:test")}" : nil
|
||||
end
|
||||
|
||||
# taken from edge rails / rails 2.0. Only needed on Rails 1.2.3
|
||||
def assert_difference(expressions, difference = 1, message = nil, &block)
|
||||
expression_evaluations = [expressions].flatten.collect{|expression| lambda { eval(expression, block.binding) } }
|
||||
|
||||
original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
|
||||
yield
|
||||
expression_evaluations.each_with_index do |expression, i|
|
||||
assert_equal original_values[i] + difference, expression.call, message
|
||||
end
|
||||
end
|
||||
|
||||
# taken from edge rails / rails 2.0. Only needed on Rails 1.2.3
|
||||
def assert_no_difference(expressions, message = nil, &block)
|
||||
assert_difference expressions, 0, message, &block
|
||||
end
|
||||
end
|
344
P5B/ruby/3dossmanno_annuaire/log/development.log
Normal file
344
P5B/ruby/3dossmanno_annuaire/log/development.log
Normal file
@ -0,0 +1,344 @@
|
||||
|
||||
|
||||
Processing UtilisateursController#index (for 127.0.0.1 at 2007-11-06 16:55:22) [GET]
|
||||
Session ID: ebdec02688b41592bff1775fab8da692
|
||||
Parameters: {"action"=>"index", "controller"=>"utilisateurs"}
|
||||
[4;36;1mSQL (0.000391)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000354)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mUtilisateur Load (0.002877)[0m [0;1mSELECT * FROM utilisateurs [0m
|
||||
Rendering layoutfalsecontent_typetext/htmlactionindex within layouts/utilisateurs
|
||||
Rendering utilisateurs/index
|
||||
|
||||
|
||||
ActionView::TemplateError (undefined method `nom' for #<Utilisateur:0xb72d2eec>) on line #19 of app/views/utilisateurs/index.rhtml:
|
||||
16:
|
||||
17: <% for utilisateur in @utilisateurs %>
|
||||
18: <tr>
|
||||
19: <td><%=h utilisateur.nom %></td>
|
||||
20: <td><%=h utilisateur.prenom %></td>
|
||||
21: <td><%=h utilisateur.classe %></td>
|
||||
22: <td><%=h utilisateur.email %></td>
|
||||
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/base.rb:1863:in `method_missing'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/views/utilisateurs/index.rhtml:19:in `_run_rhtml_47app47views47utilisateurs47index46rhtml'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/views/utilisateurs/index.rhtml:17:in `each'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/views/utilisateurs/index.rhtml:17:in `_run_rhtml_47app47views47utilisateurs47index46rhtml'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_view/base.rb:325:in `send'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_view/base.rb:325:in `compile_and_render_template'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_view/base.rb:301:in `render_template'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_view/base.rb:260:in `render_file'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:812:in `render_file'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:744:in `render_with_no_layout'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:869:in `render_without_layout'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:804:in `render_action'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:754:in `render_with_no_layout'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/deprecation.rb:44:in `silence'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:753:in `render_with_no_layout'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/layout.rb:244:in `render_without_benchmark'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:50:in `render'
|
||||
/usr/lib/ruby/1.8/benchmark.rb:293:in `measure'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:50:in `render'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/controllers/utilisateurs_controller.rb:7:in `index'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/mime_responds.rb:167:in `call'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/mime_responds.rb:167:in `respond'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/mime_responds.rb:161:in `each'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/mime_responds.rb:161:in `respond'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/mime_responds.rb:105:in `respond_to'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/controllers/utilisateurs_controller.rb:7:in `index'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:1101:in `send'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:1101:in `perform_action_without_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:696:in `call_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:688:in `perform_action_without_benchmark'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
|
||||
/usr/lib/ruby/1.8/benchmark.rb:293:in `measure'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/rescue.rb:83:in `perform_action'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:435:in `send'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:435:in `process_without_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:684:in `process_without_session_management_support'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/session_management.rb:114:in `process'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:334:in `process'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/dispatcher.rb:41:in `dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:113:in `handle_dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:79:in `service'
|
||||
/usr/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
|
||||
/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:95:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:92:in `each'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:92:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:23:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:82:in `start'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:63:in `dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/commands/servers/webrick.rb:59
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/commands/server.rb:39
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
|
||||
script/server:3
|
||||
|
||||
|
||||
Rendering /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/templates/rescues/layout.rhtml (500 Internal Error)
|
||||
[4;36;1mSQL (0.000346)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000330)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.022462)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000648)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001260)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.004494)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.002046)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002521)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.002087)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.003737)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.002598)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.003038)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001972)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;35;1mSQL (0.002987)[0m [0mSHOW FIELDS FROM utilisateurs0[0m
|
||||
[4;36;1mSQL (0.001915)[0m [0;1mSHOW KEYS FROM utilisateurs0[0m
|
||||
[4;36;1mSQL (0.000324)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000273)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000576)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.001038)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000964)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002343)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.002067)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002952)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.002650)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.002664)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.002058)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002585)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.002202)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;35;1mSQL (0.003190)[0m [0mSHOW FIELDS FROM utilisateurs0[0m
|
||||
[4;36;1mSQL (0.002052)[0m [0;1mSHOW KEYS FROM utilisateurs0[0m
|
||||
[4;36;1mSQL (0.000327)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000335)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000615)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000554)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001139)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002539)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.001794)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002117)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.002836)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.005304)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001824)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.003148)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001822)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000324)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000316)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000321)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000577)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000590)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000495)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001012)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002332)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.002280)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002149)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.001880)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.004202)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001842)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.003232)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.002026)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000344)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000322)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000619)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000462)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001309)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.007139)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.003189)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.003171)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.003555)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.003635)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001912)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002762)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001890)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000317)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000278)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000683)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000319)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000329)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000274)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000324)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000282)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000521)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.012236)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000509)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000509)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000950)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.003472)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.002259)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002701)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.002017)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.002611)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001984)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002393)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.002449)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000329)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000316)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000779)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.000577)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000632)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000588)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001240)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.003601)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.001946)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002402)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.001833)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.002700)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001880)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002815)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001944)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000322)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000276)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000446)[0m [0mSELECT version FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000403)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000623)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.000526)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000530)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
Migrating to CreateUtilisateurs (1)
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S02Unknown table 'utilisateurs': DROP TABLE utilisateurs[0m
|
||||
[4;36;1mSQL (0.000317)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000281)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000722)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.000765)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000464)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000457)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000916)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002459)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.001870)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002171)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.001915)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.002727)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.001986)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.004044)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.002829)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
|
||||
|
||||
Processing UtilisateursController#index (for 127.0.0.1 at 2007-11-06 17:19:40) [GET]
|
||||
Session ID: ebdec02688b41592bff1775fab8da692
|
||||
Parameters: {"action"=>"index", "controller"=>"utilisateurs"}
|
||||
[4;35;1mUtilisateur Load (0.000000)[0m [0mMysql::Error: #42S02Table '073dossmanno_dev.utilisateurs' doesn't exist: SELECT * FROM utilisateurs [0m
|
||||
|
||||
|
||||
ActiveRecord::StatementInvalid (Mysql::Error: #42S02Table '073dossmanno_dev.utilisateurs' doesn't exist: SELECT * FROM utilisateurs ):
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/connection_adapters/abstract_adapter.rb:128:in `log'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/connection_adapters/mysql_adapter.rb:243:in `execute'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/connection_adapters/mysql_adapter.rb:399:in `select'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/base.rb:427:in `find_by_sql'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/base.rb:997:in `find_every'
|
||||
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.5/lib/active_record/base.rb:418:in `find'
|
||||
/home/3dossmanno/P5b/ruby/3dossmanno_annuaire/app/controllers/utilisateurs_controller.rb:5:in `index'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:1101:in `send'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:1101:in `perform_action_without_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:696:in `call_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:688:in `perform_action_without_benchmark'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
|
||||
/usr/lib/ruby/1.8/benchmark.rb:293:in `measure'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/rescue.rb:83:in `perform_action'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:435:in `send'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:435:in `process_without_filters'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/filters.rb:684:in `process_without_session_management_support'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/session_management.rb:114:in `process'
|
||||
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/base.rb:334:in `process'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/dispatcher.rb:41:in `dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:113:in `handle_dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:79:in `service'
|
||||
/usr/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
|
||||
/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:95:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:92:in `each'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:92:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:23:in `start'
|
||||
/usr/lib/ruby/1.8/webrick/server.rb:82:in `start'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/webrick_server.rb:63:in `dispatch'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/commands/servers/webrick.rb:59
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:342:in `new_constants_in'
|
||||
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.4/lib/active_support/dependencies.rb:495:in `require'
|
||||
/usr/lib/ruby/gems/1.8/gems/rails-1.2.5/lib/commands/server.rb:39
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
|
||||
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
|
||||
./script/server:3
|
||||
|
||||
|
||||
Rendering /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.5/lib/action_controller/templates/rescues/layout.rhtml (500 Internal Error)
|
||||
[4;36;1mSQL (0.000319)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000279)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000713)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.000554)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000445)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000507)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.001107)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002537)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.001859)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002061)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.001905)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.003183)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.002289)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002503)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001893)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000364)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.001224)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000591)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.000540)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000439)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000649)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000913)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002435)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.002132)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002260)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.001886)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.003226)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.002654)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.003384)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.001831)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.000451)[0m [0;1mSET NAMES 'utf8'[0m
|
||||
[4;35;1mSQL (0.000278)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
|
||||
[4;36;1mSQL (0.000000)[0m [0;1mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;35;1mSQL (0.000000)[0m [0mMysql::Error: #42S01Table 'schema_info' already exists: CREATE TABLE schema_info (version int(11))[0m
|
||||
[4;36;1mSQL (0.000537)[0m [0;1mSELECT DATABASE() as db[0m
|
||||
[4;35;1mSQL (0.001057)[0m [0mALTER DATABASE 073dossmanno_dev CHARACTER SET utf8 COLLATE utf8_general_ci[0m
|
||||
[4;36;1mSQL (0.000505)[0m [0;1mSELECT version FROM schema_info[0m
|
||||
[4;35;1mSQL (0.000430)[0m [0mSELECT * FROM schema_info[0m
|
||||
[4;36;1mSQL (0.000894)[0m [0;1mSHOW TABLES[0m
|
||||
[4;35;1mSQL (0.002388)[0m [0mSHOW FIELDS FROM addresses[0m
|
||||
[4;36;1mSQL (0.001876)[0m [0;1mSHOW KEYS FROM addresses[0m
|
||||
[4;35;1mSQL (0.002298)[0m [0mSHOW FIELDS FROM customers[0m
|
||||
[4;36;1mSQL (0.002201)[0m [0;1mSHOW KEYS FROM customers[0m
|
||||
[4;35;1mSQL (0.004052)[0m [0mSHOW FIELDS FROM products[0m
|
||||
[4;36;1mSQL (0.002114)[0m [0;1mSHOW KEYS FROM products[0m
|
||||
[4;35;1mSQL (0.002437)[0m [0mSHOW FIELDS FROM suppliers[0m
|
||||
[4;36;1mSQL (0.002394)[0m [0;1mSHOW KEYS FROM suppliers[0m
|
0
P5B/ruby/3dossmanno_annuaire/log/production.log
Normal file
0
P5B/ruby/3dossmanno_annuaire/log/production.log
Normal file
0
P5B/ruby/3dossmanno_annuaire/log/server.log
Normal file
0
P5B/ruby/3dossmanno_annuaire/log/server.log
Normal file
0
P5B/ruby/3dossmanno_annuaire/log/test.log
Normal file
0
P5B/ruby/3dossmanno_annuaire/log/test.log
Normal file
40
P5B/ruby/3dossmanno_annuaire/public/.htaccess
Normal file
40
P5B/ruby/3dossmanno_annuaire/public/.htaccess
Normal file
@ -0,0 +1,40 @@
|
||||
# General Apache options
|
||||
AddHandler fastcgi-script .fcgi
|
||||
AddHandler cgi-script .cgi
|
||||
Options +FollowSymLinks +ExecCGI
|
||||
|
||||
# If you don't want Rails to look in certain directories,
|
||||
# use the following rewrite rules so that Apache won't rewrite certain requests
|
||||
#
|
||||
# Example:
|
||||
# RewriteCond %{REQUEST_URI} ^/notrails.*
|
||||
# RewriteRule .* - [L]
|
||||
|
||||
# Redirect all requests not available on the filesystem to Rails
|
||||
# By default the cgi dispatcher is used which is very slow
|
||||
#
|
||||
# For better performance replace the dispatcher with the fastcgi one
|
||||
#
|
||||
# Example:
|
||||
# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
|
||||
RewriteEngine On
|
||||
|
||||
# If your Rails application is accessed via an Alias directive,
|
||||
# then you MUST also set the RewriteBase in this htaccess file.
|
||||
#
|
||||
# Example:
|
||||
# Alias /myrailsapp /path/to/myrailsapp/public
|
||||
# RewriteBase /myrailsapp
|
||||
|
||||
RewriteRule ^$ index.html [QSA]
|
||||
RewriteRule ^([^.]+)$ $1.html [QSA]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
|
||||
|
||||
# In case Rails experiences terminal errors
|
||||
# Instead of displaying this message you can supply a file here which will be rendered instead
|
||||
#
|
||||
# Example:
|
||||
# ErrorDocument 500 /500.html
|
||||
|
||||
ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
|
30
P5B/ruby/3dossmanno_annuaire/public/404.html
Normal file
30
P5B/ruby/3dossmanno_annuaire/public/404.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<title>The page you were looking for doesn't exist (404)</title>
|
||||
<style type="text/css">
|
||||
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
||||
div.dialog {
|
||||
width: 25em;
|
||||
padding: 0 4em;
|
||||
margin: 4em auto 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
border-right-color: #999;
|
||||
border-bottom-color: #999;
|
||||
}
|
||||
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- This file lives in public/404.html -->
|
||||
<div class="dialog">
|
||||
<h1>The page you were looking for doesn't exist.</h1>
|
||||
<p>You may have mistyped the address or the page may have moved.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
30
P5B/ruby/3dossmanno_annuaire/public/500.html
Normal file
30
P5B/ruby/3dossmanno_annuaire/public/500.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<title>We're sorry, but something went wrong</title>
|
||||
<style type="text/css">
|
||||
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
||||
div.dialog {
|
||||
width: 25em;
|
||||
padding: 0 4em;
|
||||
margin: 4em auto 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
border-right-color: #999;
|
||||
border-bottom-color: #999;
|
||||
}
|
||||
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- This file lives in public/500.html -->
|
||||
<div class="dialog">
|
||||
<h1>We're sorry, but something went wrong.</h1>
|
||||
<p>We've been notified about this issue and we'll take a look at it shortly.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
10
P5B/ruby/3dossmanno_annuaire/public/dispatch.cgi
Normal file
10
P5B/ruby/3dossmanno_annuaire/public/dispatch.cgi
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/ruby1.8
|
||||
|
||||
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
|
||||
|
||||
# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
|
||||
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
|
||||
require "dispatcher"
|
||||
|
||||
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
|
||||
Dispatcher.dispatch
|
24
P5B/ruby/3dossmanno_annuaire/public/dispatch.fcgi
Normal file
24
P5B/ruby/3dossmanno_annuaire/public/dispatch.fcgi
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/ruby1.8
|
||||
#
|
||||
# You may specify the path to the FastCGI crash log (a log of unhandled
|
||||
# exceptions which forced the FastCGI instance to exit, great for debugging)
|
||||
# and the number of requests to process before running garbage collection.
|
||||
#
|
||||
# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
|
||||
# and the GC period is nil (turned off). A reasonable number of requests
|
||||
# could range from 10-100 depending on the memory footprint of your app.
|
||||
#
|
||||
# Example:
|
||||
# # Default log path, normal GC behavior.
|
||||
# RailsFCGIHandler.process!
|
||||
#
|
||||
# # Default log path, 50 requests between GC.
|
||||
# RailsFCGIHandler.process! nil, 50
|
||||
#
|
||||
# # Custom log path, normal GC behavior.
|
||||
# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
|
||||
#
|
||||
require File.dirname(__FILE__) + "/../config/environment"
|
||||
require 'fcgi_handler'
|
||||
|
||||
RailsFCGIHandler.process!
|
10
P5B/ruby/3dossmanno_annuaire/public/dispatch.rb
Normal file
10
P5B/ruby/3dossmanno_annuaire/public/dispatch.rb
Normal file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/ruby1.8
|
||||
|
||||
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
|
||||
|
||||
# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
|
||||
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
|
||||
require "dispatcher"
|
||||
|
||||
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
|
||||
Dispatcher.dispatch
|
0
P5B/ruby/3dossmanno_annuaire/public/favicon.ico
Normal file
0
P5B/ruby/3dossmanno_annuaire/public/favicon.ico
Normal file
BIN
P5B/ruby/3dossmanno_annuaire/public/images/rails.png
Normal file
BIN
P5B/ruby/3dossmanno_annuaire/public/images/rails.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
277
P5B/ruby/3dossmanno_annuaire/public/index.html
Normal file
277
P5B/ruby/3dossmanno_annuaire/public/index.html
Normal file
@ -0,0 +1,277 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Ruby on Rails: Welcome aboard</title>
|
||||
<style type="text/css" media="screen">
|
||||
body {
|
||||
margin: 0;
|
||||
margin-bottom: 25px;
|
||||
padding: 0;
|
||||
background-color: #f0f0f0;
|
||||
font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a {color: #03c}
|
||||
a:hover {
|
||||
background-color: #03c;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
#page {
|
||||
background-color: #f0f0f0;
|
||||
width: 750px;
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#content {
|
||||
float: left;
|
||||
background-color: white;
|
||||
border: 3px solid #aaa;
|
||||
border-top: none;
|
||||
padding: 25px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
float: right;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
#header, #about, #getting-started {
|
||||
padding-left: 75px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
|
||||
#header {
|
||||
background-image: url("images/rails.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: top left;
|
||||
height: 64px;
|
||||
}
|
||||
#header h1, #header h2 {margin: 0}
|
||||
#header h2 {
|
||||
color: #888;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
#about h3 {
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#about-content {
|
||||
background-color: #ffd;
|
||||
border: 1px solid #fc0;
|
||||
margin-left: -11px;
|
||||
}
|
||||
#about-content table {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 11px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#about-content td {
|
||||
padding: 10px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
#about-content td.name {color: #555}
|
||||
#about-content td.value {color: #000}
|
||||
|
||||
#about-content.failure {
|
||||
background-color: #fcc;
|
||||
border: 1px solid #f00;
|
||||
}
|
||||
#about-content.failure p {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
#getting-started {
|
||||
border-top: 1px solid #ccc;
|
||||
margin-top: 25px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
#getting-started h1 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
#getting-started h2 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: #333;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
#getting-started ol {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#getting-started li {
|
||||
font-size: 18px;
|
||||
color: #888;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
#getting-started li h2 {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
#getting-started li p {
|
||||
color: #555;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
#search {
|
||||
margin: 0;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
#search input {
|
||||
font-size: 11px;
|
||||
margin: 2px;
|
||||
}
|
||||
#search-text {width: 170px}
|
||||
|
||||
|
||||
#sidebar ul {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#sidebar ul h3 {
|
||||
margin-top: 25px;
|
||||
font-size: 16px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
#sidebar li {
|
||||
list-style-type: none;
|
||||
}
|
||||
#sidebar ul.links li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="text/javascript" src="javascripts/prototype.js"></script>
|
||||
<script type="text/javascript" src="javascripts/effects.js"></script>
|
||||
<script type="text/javascript">
|
||||
function about() {
|
||||
if (Element.empty('about-content')) {
|
||||
new Ajax.Updater('about-content', 'rails/info/properties', {
|
||||
method: 'get',
|
||||
onFailure: function() {Element.classNames('about-content').add('failure')},
|
||||
onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
|
||||
});
|
||||
} else {
|
||||
new Effect[Element.visible('about-content') ?
|
||||
'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
$('search-text').value = '';
|
||||
$('search').onsubmit = function() {
|
||||
$('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
<div id="sidebar">
|
||||
<ul id="sidebar-items">
|
||||
<li>
|
||||
<form id="search" action="http://www.google.com/search" method="get">
|
||||
<input type="hidden" name="hl" value="en" />
|
||||
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
|
||||
<input type="submit" value="Search" /> the Rails site
|
||||
</form>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h3>Join the community</h3>
|
||||
<ul class="links">
|
||||
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
|
||||
<li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
|
||||
<li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
|
||||
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
|
||||
<li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h3>Browse the documentation</h3>
|
||||
<ul class="links">
|
||||
<li><a href="http://api.rubyonrails.org/">Rails API</a></li>
|
||||
<li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
|
||||
<li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<div id="header">
|
||||
<h1>Welcome aboard</h1>
|
||||
<h2>You’re riding the Rails!</h2>
|
||||
</div>
|
||||
|
||||
<div id="about">
|
||||
<h3><a href="rails/info/properties" onclick="about(); return false">About your application’s environment</a></h3>
|
||||
<div id="about-content" style="display: none"></div>
|
||||
</div>
|
||||
|
||||
<div id="getting-started">
|
||||
<h1>Getting started</h1>
|
||||
<h2>Here’s how to get rolling:</h2>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<h2>Create your databases and edit <tt>config/database.yml</tt></h2>
|
||||
<p>Rails needs to know your login and password.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
|
||||
<p>To see all available options, run it without parameters.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>Set up a default route and remove or rename this file</h2>
|
||||
<p>Routes are setup in config/routes.rb.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer"> </div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,2 @@
|
||||
// Place your application-specific JavaScript functions and classes here
|
||||
// This file is automatically included by javascript_include_tag :defaults
|
833
P5B/ruby/3dossmanno_annuaire/public/javascripts/controls.js
vendored
Normal file
833
P5B/ruby/3dossmanno_annuaire/public/javascripts/controls.js
vendored
Normal file
@ -0,0 +1,833 @@
|
||||
// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||
// (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
|
||||
// Contributors:
|
||||
// Richard Livsey
|
||||
// Rahul Bhargava
|
||||
// Rob Wills
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
// Autocompleter.Base handles all the autocompletion functionality
|
||||
// that's independent of the data source for autocompletion. This
|
||||
// includes drawing the autocompletion menu, observing keyboard
|
||||
// and mouse events, and similar.
|
||||
//
|
||||
// Specific autocompleters need to provide, at the very least,
|
||||
// a getUpdatedChoices function that will be invoked every time
|
||||
// the text inside the monitored textbox changes. This method
|
||||
// should get the text for which to provide autocompletion by
|
||||
// invoking this.getToken(), NOT by directly accessing
|
||||
// this.element.value. This is to allow incremental tokenized
|
||||
// autocompletion. Specific auto-completion logic (AJAX, etc)
|
||||
// belongs in getUpdatedChoices.
|
||||
//
|
||||
// Tokenized incremental autocompletion is enabled automatically
|
||||
// when an autocompleter is instantiated with the 'tokens' option
|
||||
// in the options parameter, e.g.:
|
||||
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
|
||||
// will incrementally autocomplete with a comma as the token.
|
||||
// Additionally, ',' in the above example can be replaced with
|
||||
// a token array, e.g. { tokens: [',', '\n'] } which
|
||||
// enables autocompletion on multiple tokens. This is most
|
||||
// useful when one of the tokens is \n (a newline), as it
|
||||
// allows smart autocompletion after linebreaks.
|
||||
|
||||
if(typeof Effect == 'undefined')
|
||||
throw("controls.js requires including script.aculo.us' effects.js library");
|
||||
|
||||
var Autocompleter = {}
|
||||
Autocompleter.Base = function() {};
|
||||
Autocompleter.Base.prototype = {
|
||||
baseInitialize: function(element, update, options) {
|
||||
this.element = $(element);
|
||||
this.update = $(update);
|
||||
this.hasFocus = false;
|
||||
this.changed = false;
|
||||
this.active = false;
|
||||
this.index = 0;
|
||||
this.entryCount = 0;
|
||||
|
||||
if(this.setOptions)
|
||||
this.setOptions(options);
|
||||
else
|
||||
this.options = options || {};
|
||||
|
||||
this.options.paramName = this.options.paramName || this.element.name;
|
||||
this.options.tokens = this.options.tokens || [];
|
||||
this.options.frequency = this.options.frequency || 0.4;
|
||||
this.options.minChars = this.options.minChars || 1;
|
||||
this.options.onShow = this.options.onShow ||
|
||||
function(element, update){
|
||||
if(!update.style.position || update.style.position=='absolute') {
|
||||
update.style.position = 'absolute';
|
||||
Position.clone(element, update, {
|
||||
setHeight: false,
|
||||
offsetTop: element.offsetHeight
|
||||
});
|
||||
}
|
||||
Effect.Appear(update,{duration:0.15});
|
||||
};
|
||||
this.options.onHide = this.options.onHide ||
|
||||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
||||
|
||||
if(typeof(this.options.tokens) == 'string')
|
||||
this.options.tokens = new Array(this.options.tokens);
|
||||
|
||||
this.observer = null;
|
||||
|
||||
this.element.setAttribute('autocomplete','off');
|
||||
|
||||
Element.hide(this.update);
|
||||
|
||||
Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
|
||||
Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||
if(!this.iefix &&
|
||||
(navigator.appVersion.indexOf('MSIE')>0) &&
|
||||
(navigator.userAgent.indexOf('Opera')<0) &&
|
||||
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
new Insertion.After(this.update,
|
||||
'<iframe id="' + this.update.id + '_iefix" '+
|
||||
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
||||
this.iefix = $(this.update.id+'_iefix');
|
||||
}
|
||||
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
||||
},
|
||||
|
||||
fixIEOverlapping: function() {
|
||||
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
|
||||
this.iefix.style.zIndex = 1;
|
||||
this.update.style.zIndex = 2;
|
||||
Element.show(this.iefix);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.stopIndicator();
|
||||
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
|
||||
if(this.iefix) Element.hide(this.iefix);
|
||||
},
|
||||
|
||||
startIndicator: function() {
|
||||
if(this.options.indicator) Element.show(this.options.indicator);
|
||||
},
|
||||
|
||||
stopIndicator: function() {
|
||||
if(this.options.indicator) Element.hide(this.options.indicator);
|
||||
},
|
||||
|
||||
onKeyPress: function(event) {
|
||||
if(this.active)
|
||||
switch(event.keyCode) {
|
||||
case Event.KEY_TAB:
|
||||
case Event.KEY_RETURN:
|
||||
this.selectEntry();
|
||||
Event.stop(event);
|
||||
case Event.KEY_ESC:
|
||||
this.hide();
|
||||
this.active = false;
|
||||
Event.stop(event);
|
||||
return;
|
||||
case Event.KEY_LEFT:
|
||||
case Event.KEY_RIGHT:
|
||||
return;
|
||||
case Event.KEY_UP:
|
||||
this.markPrevious();
|
||||
this.render();
|
||||
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
|
||||
return;
|
||||
case Event.KEY_DOWN:
|
||||
this.markNext();
|
||||
this.render();
|
||||
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
|
||||
return;
|
||||
}
|
||||
else
|
||||
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
||||
(navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
|
||||
|
||||
this.changed = true;
|
||||
this.hasFocus = true;
|
||||
|
||||
if(this.observer) clearTimeout(this.observer);
|
||||
this.observer =
|
||||
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.changed = false;
|
||||
this.hasFocus = true;
|
||||
this.getUpdatedChoices();
|
||||
},
|
||||
|
||||
onHover: function(event) {
|
||||
var element = Event.findElement(event, 'LI');
|
||||
if(this.index != element.autocompleteIndex)
|
||||
{
|
||||
this.index = element.autocompleteIndex;
|
||||
this.render();
|
||||
}
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
onClick: function(event) {
|
||||
var element = Event.findElement(event, 'LI');
|
||||
this.index = element.autocompleteIndex;
|
||||
this.selectEntry();
|
||||
this.hide();
|
||||
},
|
||||
|
||||
onBlur: function(event) {
|
||||
// needed to make click events working
|
||||
setTimeout(this.hide.bind(this), 250);
|
||||
this.hasFocus = false;
|
||||
this.active = false;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if(this.entryCount > 0) {
|
||||
for (var i = 0; i < this.entryCount; i++)
|
||||
this.index==i ?
|
||||
Element.addClassName(this.getEntry(i),"selected") :
|
||||
Element.removeClassName(this.getEntry(i),"selected");
|
||||
|
||||
if(this.hasFocus) {
|
||||
this.show();
|
||||
this.active = true;
|
||||
}
|
||||
} else {
|
||||
this.active = false;
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
markPrevious: function() {
|
||||
if(this.index > 0) this.index--
|
||||
else this.index = this.entryCount-1;
|
||||
this.getEntry(this.index).scrollIntoView(true);
|
||||
},
|
||||
|
||||
markNext: function() {
|
||||
if(this.index < this.entryCount-1) this.index++
|
||||
else this.index = 0;
|
||||
this.getEntry(this.index).scrollIntoView(false);
|
||||
},
|
||||
|
||||
getEntry: function(index) {
|
||||
return this.update.firstChild.childNodes[index];
|
||||
},
|
||||
|
||||
getCurrentEntry: function() {
|
||||
return this.getEntry(this.index);
|
||||
},
|
||||
|
||||
selectEntry: function() {
|
||||
this.active = false;
|
||||
this.updateElement(this.getCurrentEntry());
|
||||
},
|
||||
|
||||
updateElement: function(selectedElement) {
|
||||
if (this.options.updateElement) {
|
||||
this.options.updateElement(selectedElement);
|
||||
return;
|
||||
}
|
||||
var value = '';
|
||||
if (this.options.select) {
|
||||
var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
|
||||
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
||||
} else
|
||||
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
||||
|
||||
var lastTokenPos = this.findLastToken();
|
||||
if (lastTokenPos != -1) {
|
||||
var newValue = this.element.value.substr(0, lastTokenPos + 1);
|
||||
var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
|
||||
if (whitespace)
|
||||
newValue += whitespace[0];
|
||||
this.element.value = newValue + value;
|
||||
} else {
|
||||
this.element.value = value;
|
||||
}
|
||||
this.element.focus();
|
||||
|
||||
if (this.options.afterUpdateElement)
|
||||
this.options.afterUpdateElement(this.element, selectedElement);
|
||||
},
|
||||
|
||||
updateChoices: function(choices) {
|
||||
if(!this.changed && this.hasFocus) {
|
||||
this.update.innerHTML = choices;
|
||||
Element.cleanWhitespace(this.update);
|
||||
Element.cleanWhitespace(this.update.down());
|
||||
|
||||
if(this.update.firstChild && this.update.down().childNodes) {
|
||||
this.entryCount =
|
||||
this.update.down().childNodes.length;
|
||||
for (var i = 0; i < this.entryCount; i++) {
|
||||
var entry = this.getEntry(i);
|
||||
entry.autocompleteIndex = i;
|
||||
this.addObservers(entry);
|
||||
}
|
||||
} else {
|
||||
this.entryCount = 0;
|
||||
}
|
||||
|
||||
this.stopIndicator();
|
||||
this.index = 0;
|
||||
|
||||
if(this.entryCount==1 && this.options.autoSelect) {
|
||||
this.selectEntry();
|
||||
this.hide();
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addObservers: function(element) {
|
||||
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
|
||||
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
|
||||
},
|
||||
|
||||
onObserverEvent: function() {
|
||||
this.changed = false;
|
||||
if(this.getToken().length>=this.options.minChars) {
|
||||
this.startIndicator();
|
||||
this.getUpdatedChoices();
|
||||
} else {
|
||||
this.active = false;
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
getToken: function() {
|
||||
var tokenPos = this.findLastToken();
|
||||
if (tokenPos != -1)
|
||||
var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
|
||||
else
|
||||
var ret = this.element.value;
|
||||
|
||||
return /\n/.test(ret) ? '' : ret;
|
||||
},
|
||||
|
||||
findLastToken: function() {
|
||||
var lastTokenPos = -1;
|
||||
|
||||
for (var i=0; i<this.options.tokens.length; i++) {
|
||||
var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
|
||||
if (thisTokenPos > lastTokenPos)
|
||||
lastTokenPos = thisTokenPos;
|
||||
}
|
||||
return lastTokenPos;
|
||||
}
|
||||
}
|
||||
|
||||
Ajax.Autocompleter = Class.create();
|
||||
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
|
||||
initialize: function(element, update, url, options) {
|
||||
this.baseInitialize(element, update, options);
|
||||
this.options.asynchronous = true;
|
||||
this.options.onComplete = this.onComplete.bind(this);
|
||||
this.options.defaultParams = this.options.parameters || null;
|
||||
this.url = url;
|
||||
},
|
||||
|
||||
getUpdatedChoices: function() {
|
||||
entry = encodeURIComponent(this.options.paramName) + '=' +
|
||||
encodeURIComponent(this.getToken());
|
||||
|
||||
this.options.parameters = this.options.callback ?
|
||||
this.options.callback(this.element, entry) : entry;
|
||||
|
||||
if(this.options.defaultParams)
|
||||
this.options.parameters += '&' + this.options.defaultParams;
|
||||
|
||||
new Ajax.Request(this.url, this.options);
|
||||
},
|
||||
|
||||
onComplete: function(request) {
|
||||
this.updateChoices(request.responseText);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// The local array autocompleter. Used when you'd prefer to
|
||||
// inject an array of autocompletion options into the page, rather
|
||||
// than sending out Ajax queries, which can be quite slow sometimes.
|
||||
//
|
||||
// The constructor takes four parameters. The first two are, as usual,
|
||||
// the id of the monitored textbox, and id of the autocompletion menu.
|
||||
// The third is the array you want to autocomplete from, and the fourth
|
||||
// is the options block.
|
||||
//
|
||||
// Extra local autocompletion options:
|
||||
// - choices - How many autocompletion choices to offer
|
||||
//
|
||||
// - partialSearch - If false, the autocompleter will match entered
|
||||
// text only at the beginning of strings in the
|
||||
// autocomplete array. Defaults to true, which will
|
||||
// match text at the beginning of any *word* in the
|
||||
// strings in the autocomplete array. If you want to
|
||||
// search anywhere in the string, additionally set
|
||||
// the option fullSearch to true (default: off).
|
||||
//
|
||||
// - fullSsearch - Search anywhere in autocomplete array strings.
|
||||
//
|
||||
// - partialChars - How many characters to enter before triggering
|
||||
// a partial match (unlike minChars, which defines
|
||||
// how many characters are required to do any match
|
||||
// at all). Defaults to 2.
|
||||
//
|
||||
// - ignoreCase - Whether to ignore case when autocompleting.
|
||||
// Defaults to true.
|
||||
//
|
||||
// It's possible to pass in a custom function as the 'selector'
|
||||
// option, if you prefer to write your own autocompletion logic.
|
||||
// In that case, the other options above will not apply unless
|
||||
// you support them.
|
||||
|
||||
Autocompleter.Local = Class.create();
|
||||
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
|
||||
initialize: function(element, update, array, options) {
|
||||
this.baseInitialize(element, update, options);
|
||||
this.options.array = array;
|
||||
},
|
||||
|
||||
getUpdatedChoices: function() {
|
||||
this.updateChoices(this.options.selector(this));
|
||||
},
|
||||
|
||||
setOptions: function(options) {
|
||||
this.options = Object.extend({
|
||||
choices: 10,
|
||||
partialSearch: true,
|
||||
partialChars: 2,
|
||||
ignoreCase: true,
|
||||
fullSearch: false,
|
||||
selector: function(instance) {
|
||||
var ret = []; // Beginning matches
|
||||
var partial = []; // Inside matches
|
||||
var entry = instance.getToken();
|
||||
var count = 0;
|
||||
|
||||
for (var i = 0; i < instance.options.array.length &&
|
||||
ret.length < instance.options.choices ; i++) {
|
||||
|
||||
var elem = instance.options.array[i];
|
||||
var foundPos = instance.options.ignoreCase ?
|
||||
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
||||
elem.indexOf(entry);
|
||||
|
||||
while (foundPos != -1) {
|
||||
if (foundPos == 0 && elem.length != entry.length) {
|
||||
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
||||
elem.substr(entry.length) + "</li>");
|
||||
break;
|
||||
} else if (entry.length >= instance.options.partialChars &&
|
||||
instance.options.partialSearch && foundPos != -1) {
|
||||
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
||||
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
||||
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
|
||||
foundPos + entry.length) + "</li>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foundPos = instance.options.ignoreCase ?
|
||||
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
||||
elem.indexOf(entry, foundPos + 1);
|
||||
|
||||
}
|
||||
}
|
||||
if (partial.length)
|
||||
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
|
||||
return "<ul>" + ret.join('') + "</ul>";
|
||||
}
|
||||
}, options || {});
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX in-place editor
|
||||
//
|
||||
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
|
||||
|
||||
// Use this if you notice weird scrolling problems on some browsers,
|
||||
// the DOM might be a bit confused when this gets called so do this
|
||||
// waits 1 ms (with setTimeout) until it does the activation
|
||||
Field.scrollFreeActivate = function(field) {
|
||||
setTimeout(function() {
|
||||
Field.activate(field);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
Ajax.InPlaceEditor = Class.create();
|
||||
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
|
||||
Ajax.InPlaceEditor.prototype = {
|
||||
initialize: function(element, url, options) {
|
||||
this.url = url;
|
||||
this.element = $(element);
|
||||
|
||||
this.options = Object.extend({
|
||||
paramName: "value",
|
||||
okButton: true,
|
||||
okText: "ok",
|
||||
cancelLink: true,
|
||||
cancelText: "cancel",
|
||||
savingText: "Saving...",
|
||||
clickToEditText: "Click to edit",
|
||||
okText: "ok",
|
||||
rows: 1,
|
||||
onComplete: function(transport, element) {
|
||||
new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
|
||||
},
|
||||
onFailure: function(transport) {
|
||||
alert("Error communicating with the server: " + transport.responseText.stripTags());
|
||||
},
|
||||
callback: function(form) {
|
||||
return Form.serialize(form);
|
||||
},
|
||||
handleLineBreaks: true,
|
||||
loadingText: 'Loading...',
|
||||
savingClassName: 'inplaceeditor-saving',
|
||||
loadingClassName: 'inplaceeditor-loading',
|
||||
formClassName: 'inplaceeditor-form',
|
||||
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
|
||||
highlightendcolor: "#FFFFFF",
|
||||
externalControl: null,
|
||||
submitOnBlur: false,
|
||||
ajaxOptions: {},
|
||||
evalScripts: false
|
||||
}, options || {});
|
||||
|
||||
if(!this.options.formId && this.element.id) {
|
||||
this.options.formId = this.element.id + "-inplaceeditor";
|
||||
if ($(this.options.formId)) {
|
||||
// there's already a form with that name, don't specify an id
|
||||
this.options.formId = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.externalControl) {
|
||||
this.options.externalControl = $(this.options.externalControl);
|
||||
}
|
||||
|
||||
this.originalBackground = Element.getStyle(this.element, 'background-color');
|
||||
if (!this.originalBackground) {
|
||||
this.originalBackground = "transparent";
|
||||
}
|
||||
|
||||
this.element.title = this.options.clickToEditText;
|
||||
|
||||
this.onclickListener = this.enterEditMode.bindAsEventListener(this);
|
||||
this.mouseoverListener = this.enterHover.bindAsEventListener(this);
|
||||
this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
|
||||
Event.observe(this.element, 'click', this.onclickListener);
|
||||
Event.observe(this.element, 'mouseover', this.mouseoverListener);
|
||||
Event.observe(this.element, 'mouseout', this.mouseoutListener);
|
||||
if (this.options.externalControl) {
|
||||
Event.observe(this.options.externalControl, 'click', this.onclickListener);
|
||||
Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
|
||||
Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||
}
|
||||
},
|
||||
enterEditMode: function(evt) {
|
||||
if (this.saving) return;
|
||||
if (this.editing) return;
|
||||
this.editing = true;
|
||||
this.onEnterEditMode();
|
||||
if (this.options.externalControl) {
|
||||
Element.hide(this.options.externalControl);
|
||||
}
|
||||
Element.hide(this.element);
|
||||
this.createForm();
|
||||
this.element.parentNode.insertBefore(this.form, this.element);
|
||||
if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
|
||||
// stop the event to avoid a page refresh in Safari
|
||||
if (evt) {
|
||||
Event.stop(evt);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
createForm: function() {
|
||||
this.form = document.createElement("form");
|
||||
this.form.id = this.options.formId;
|
||||
Element.addClassName(this.form, this.options.formClassName)
|
||||
this.form.onsubmit = this.onSubmit.bind(this);
|
||||
|
||||
this.createEditField();
|
||||
|
||||
if (this.options.textarea) {
|
||||
var br = document.createElement("br");
|
||||
this.form.appendChild(br);
|
||||
}
|
||||
|
||||
if (this.options.okButton) {
|
||||
okButton = document.createElement("input");
|
||||
okButton.type = "submit";
|
||||
okButton.value = this.options.okText;
|
||||
okButton.className = 'editor_ok_button';
|
||||
this.form.appendChild(okButton);
|
||||
}
|
||||
|
||||
if (this.options.cancelLink) {
|
||||
cancelLink = document.createElement("a");
|
||||
cancelLink.href = "#";
|
||||
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
|
||||
cancelLink.onclick = this.onclickCancel.bind(this);
|
||||
cancelLink.className = 'editor_cancel';
|
||||
this.form.appendChild(cancelLink);
|
||||
}
|
||||
},
|
||||
hasHTMLLineBreaks: function(string) {
|
||||
if (!this.options.handleLineBreaks) return false;
|
||||
return string.match(/<br/i) || string.match(/<p>/i);
|
||||
},
|
||||
convertHTMLLineBreaks: function(string) {
|
||||
return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
|
||||
},
|
||||
createEditField: function() {
|
||||
var text;
|
||||
if(this.options.loadTextURL) {
|
||||
text = this.options.loadingText;
|
||||
} else {
|
||||
text = this.getText();
|
||||
}
|
||||
|
||||
var obj = this;
|
||||
|
||||
if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
|
||||
this.options.textarea = false;
|
||||
var textField = document.createElement("input");
|
||||
textField.obj = this;
|
||||
textField.type = "text";
|
||||
textField.name = this.options.paramName;
|
||||
textField.value = text;
|
||||
textField.style.backgroundColor = this.options.highlightcolor;
|
||||
textField.className = 'editor_field';
|
||||
var size = this.options.size || this.options.cols || 0;
|
||||
if (size != 0) textField.size = size;
|
||||
if (this.options.submitOnBlur)
|
||||
textField.onblur = this.onSubmit.bind(this);
|
||||
this.editField = textField;
|
||||
} else {
|
||||
this.options.textarea = true;
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.obj = this;
|
||||
textArea.name = this.options.paramName;
|
||||
textArea.value = this.convertHTMLLineBreaks(text);
|
||||
textArea.rows = this.options.rows;
|
||||
textArea.cols = this.options.cols || 40;
|
||||
textArea.className = 'editor_field';
|
||||
if (this.options.submitOnBlur)
|
||||
textArea.onblur = this.onSubmit.bind(this);
|
||||
this.editField = textArea;
|
||||
}
|
||||
|
||||
if(this.options.loadTextURL) {
|
||||
this.loadExternalText();
|
||||
}
|
||||
this.form.appendChild(this.editField);
|
||||
},
|
||||
getText: function() {
|
||||
return this.element.innerHTML;
|
||||
},
|
||||
loadExternalText: function() {
|
||||
Element.addClassName(this.form, this.options.loadingClassName);
|
||||
this.editField.disabled = true;
|
||||
new Ajax.Request(
|
||||
this.options.loadTextURL,
|
||||
Object.extend({
|
||||
asynchronous: true,
|
||||
onComplete: this.onLoadedExternalText.bind(this)
|
||||
}, this.options.ajaxOptions)
|
||||
);
|
||||
},
|
||||
onLoadedExternalText: function(transport) {
|
||||
Element.removeClassName(this.form, this.options.loadingClassName);
|
||||
this.editField.disabled = false;
|
||||
this.editField.value = transport.responseText.stripTags();
|
||||
Field.scrollFreeActivate(this.editField);
|
||||
},
|
||||
onclickCancel: function() {
|
||||
this.onComplete();
|
||||
this.leaveEditMode();
|
||||
return false;
|
||||
},
|
||||
onFailure: function(transport) {
|
||||
this.options.onFailure(transport);
|
||||
if (this.oldInnerHTML) {
|
||||
this.element.innerHTML = this.oldInnerHTML;
|
||||
this.oldInnerHTML = null;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onSubmit: function() {
|
||||
// onLoading resets these so we need to save them away for the Ajax call
|
||||
var form = this.form;
|
||||
var value = this.editField.value;
|
||||
|
||||
// do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
|
||||
// which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
|
||||
// to be displayed indefinitely
|
||||
this.onLoading();
|
||||
|
||||
if (this.options.evalScripts) {
|
||||
new Ajax.Request(
|
||||
this.url, Object.extend({
|
||||
parameters: this.options.callback(form, value),
|
||||
onComplete: this.onComplete.bind(this),
|
||||
onFailure: this.onFailure.bind(this),
|
||||
asynchronous:true,
|
||||
evalScripts:true
|
||||
}, this.options.ajaxOptions));
|
||||
} else {
|
||||
new Ajax.Updater(
|
||||
{ success: this.element,
|
||||
// don't update on failure (this could be an option)
|
||||
failure: null },
|
||||
this.url, Object.extend({
|
||||
parameters: this.options.callback(form, value),
|
||||
onComplete: this.onComplete.bind(this),
|
||||
onFailure: this.onFailure.bind(this)
|
||||
}, this.options.ajaxOptions));
|
||||
}
|
||||
// stop the event to avoid a page refresh in Safari
|
||||
if (arguments.length > 1) {
|
||||
Event.stop(arguments[0]);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onLoading: function() {
|
||||
this.saving = true;
|
||||
this.removeForm();
|
||||
this.leaveHover();
|
||||
this.showSaving();
|
||||
},
|
||||
showSaving: function() {
|
||||
this.oldInnerHTML = this.element.innerHTML;
|
||||
this.element.innerHTML = this.options.savingText;
|
||||
Element.addClassName(this.element, this.options.savingClassName);
|
||||
this.element.style.backgroundColor = this.originalBackground;
|
||||
Element.show(this.element);
|
||||
},
|
||||
removeForm: function() {
|
||||
if(this.form) {
|
||||
if (this.form.parentNode) Element.remove(this.form);
|
||||
this.form = null;
|
||||
}
|
||||
},
|
||||
enterHover: function() {
|
||||
if (this.saving) return;
|
||||
this.element.style.backgroundColor = this.options.highlightcolor;
|
||||
if (this.effect) {
|
||||
this.effect.cancel();
|
||||
}
|
||||
Element.addClassName(this.element, this.options.hoverClassName)
|
||||
},
|
||||
leaveHover: function() {
|
||||
if (this.options.backgroundColor) {
|
||||
this.element.style.backgroundColor = this.oldBackground;
|
||||
}
|
||||
Element.removeClassName(this.element, this.options.hoverClassName)
|
||||
if (this.saving) return;
|
||||
this.effect = new Effect.Highlight(this.element, {
|
||||
startcolor: this.options.highlightcolor,
|
||||
endcolor: this.options.highlightendcolor,
|
||||
restorecolor: this.originalBackground
|
||||
});
|
||||
},
|
||||
leaveEditMode: function() {
|
||||
Element.removeClassName(this.element, this.options.savingClassName);
|
||||
this.removeForm();
|
||||
this.leaveHover();
|
||||
this.element.style.backgroundColor = this.originalBackground;
|
||||
Element.show(this.element);
|
||||
if (this.options.externalControl) {
|
||||
Element.show(this.options.externalControl);
|
||||
}
|
||||
this.editing = false;
|
||||
this.saving = false;
|
||||
this.oldInnerHTML = null;
|
||||
this.onLeaveEditMode();
|
||||
},
|
||||
onComplete: function(transport) {
|
||||
this.leaveEditMode();
|
||||
this.options.onComplete.bind(this)(transport, this.element);
|
||||
},
|
||||
onEnterEditMode: function() {},
|
||||
onLeaveEditMode: function() {},
|
||||
dispose: function() {
|
||||
if (this.oldInnerHTML) {
|
||||
this.element.innerHTML = this.oldInnerHTML;
|
||||
}
|
||||
this.leaveEditMode();
|
||||
Event.stopObserving(this.element, 'click', this.onclickListener);
|
||||
Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
|
||||
Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
|
||||
if (this.options.externalControl) {
|
||||
Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
|
||||
Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
|
||||
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.InPlaceCollectionEditor = Class.create();
|
||||
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
|
||||
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
|
||||
createEditField: function() {
|
||||
if (!this.cached_selectTag) {
|
||||
var selectTag = document.createElement("select");
|
||||
var collection = this.options.collection || [];
|
||||
var optionTag;
|
||||
collection.each(function(e,i) {
|
||||
optionTag = document.createElement("option");
|
||||
optionTag.value = (e instanceof Array) ? e[0] : e;
|
||||
if((typeof this.options.value == 'undefined') &&
|
||||
((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
|
||||
if(this.options.value==optionTag.value) optionTag.selected = true;
|
||||
optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
|
||||
selectTag.appendChild(optionTag);
|
||||
}.bind(this));
|
||||
this.cached_selectTag = selectTag;
|
||||
}
|
||||
|
||||
this.editField = this.cached_selectTag;
|
||||
if(this.options.loadTextURL) this.loadExternalText();
|
||||
this.form.appendChild(this.editField);
|
||||
this.options.callback = function(form, value) {
|
||||
return "value=" + encodeURIComponent(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delayed observer, like Form.Element.Observer,
|
||||
// but waits for delay after last key input
|
||||
// Ideal for live-search fields
|
||||
|
||||
Form.Element.DelayedObserver = Class.create();
|
||||
Form.Element.DelayedObserver.prototype = {
|
||||
initialize: function(element, delay, callback) {
|
||||
this.delay = delay || 0.5;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
this.timer = null;
|
||||
this.lastValue = $F(this.element);
|
||||
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||
},
|
||||
delayedListener: function(event) {
|
||||
if(this.lastValue == $F(this.element)) return;
|
||||
if(this.timer) clearTimeout(this.timer);
|
||||
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
||||
this.lastValue = $F(this.element);
|
||||
},
|
||||
onTimerEvent: function() {
|
||||
this.timer = null;
|
||||
this.callback(this.element, $F(this.element));
|
||||
}
|
||||
};
|
942
P5B/ruby/3dossmanno_annuaire/public/javascripts/dragdrop.js
vendored
Normal file
942
P5B/ruby/3dossmanno_annuaire/public/javascripts/dragdrop.js
vendored
Normal file
@ -0,0 +1,942 @@
|
||||
// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
if(typeof Effect == 'undefined')
|
||||
throw("dragdrop.js requires including script.aculo.us' effects.js library");
|
||||
|
||||
var Droppables = {
|
||||
drops: [],
|
||||
|
||||
remove: function(element) {
|
||||
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
|
||||
},
|
||||
|
||||
add: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend({
|
||||
greedy: true,
|
||||
hoverclass: null,
|
||||
tree: false
|
||||
}, arguments[1] || {});
|
||||
|
||||
// cache containers
|
||||
if(options.containment) {
|
||||
options._containers = [];
|
||||
var containment = options.containment;
|
||||
if((typeof containment == 'object') &&
|
||||
(containment.constructor == Array)) {
|
||||
containment.each( function(c) { options._containers.push($(c)) });
|
||||
} else {
|
||||
options._containers.push($(containment));
|
||||
}
|
||||
}
|
||||
|
||||
if(options.accept) options.accept = [options.accept].flatten();
|
||||
|
||||
Element.makePositioned(element); // fix IE
|
||||
options.element = element;
|
||||
|
||||
this.drops.push(options);
|
||||
},
|
||||
|
||||
findDeepestChild: function(drops) {
|
||||
deepest = drops[0];
|
||||
|
||||
for (i = 1; i < drops.length; ++i)
|
||||
if (Element.isParent(drops[i].element, deepest.element))
|
||||
deepest = drops[i];
|
||||
|
||||
return deepest;
|
||||
},
|
||||
|
||||
isContained: function(element, drop) {
|
||||
var containmentNode;
|
||||
if(drop.tree) {
|
||||
containmentNode = element.treeNode;
|
||||
} else {
|
||||
containmentNode = element.parentNode;
|
||||
}
|
||||
return drop._containers.detect(function(c) { return containmentNode == c });
|
||||
},
|
||||
|
||||
isAffected: function(point, element, drop) {
|
||||
return (
|
||||
(drop.element!=element) &&
|
||||
((!drop._containers) ||
|
||||
this.isContained(element, drop)) &&
|
||||
((!drop.accept) ||
|
||||
(Element.classNames(element).detect(
|
||||
function(v) { return drop.accept.include(v) } ) )) &&
|
||||
Position.within(drop.element, point[0], point[1]) );
|
||||
},
|
||||
|
||||
deactivate: function(drop) {
|
||||
if(drop.hoverclass)
|
||||
Element.removeClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = null;
|
||||
},
|
||||
|
||||
activate: function(drop) {
|
||||
if(drop.hoverclass)
|
||||
Element.addClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = drop;
|
||||
},
|
||||
|
||||
show: function(point, element) {
|
||||
if(!this.drops.length) return;
|
||||
var affected = [];
|
||||
|
||||
if(this.last_active) this.deactivate(this.last_active);
|
||||
this.drops.each( function(drop) {
|
||||
if(Droppables.isAffected(point, element, drop))
|
||||
affected.push(drop);
|
||||
});
|
||||
|
||||
if(affected.length>0) {
|
||||
drop = Droppables.findDeepestChild(affected);
|
||||
Position.within(drop.element, point[0], point[1]);
|
||||
if(drop.onHover)
|
||||
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||
|
||||
Droppables.activate(drop);
|
||||
}
|
||||
},
|
||||
|
||||
fire: function(event, element) {
|
||||
if(!this.last_active) return;
|
||||
Position.prepare();
|
||||
|
||||
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
||||
if (this.last_active.onDrop)
|
||||
this.last_active.onDrop(element, this.last_active.element, event);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
if(this.last_active)
|
||||
this.deactivate(this.last_active);
|
||||
}
|
||||
}
|
||||
|
||||
var Draggables = {
|
||||
drags: [],
|
||||
observers: [],
|
||||
|
||||
register: function(draggable) {
|
||||
if(this.drags.length == 0) {
|
||||
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
||||
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
||||
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
||||
|
||||
Event.observe(document, "mouseup", this.eventMouseUp);
|
||||
Event.observe(document, "mousemove", this.eventMouseMove);
|
||||
Event.observe(document, "keypress", this.eventKeypress);
|
||||
}
|
||||
this.drags.push(draggable);
|
||||
},
|
||||
|
||||
unregister: function(draggable) {
|
||||
this.drags = this.drags.reject(function(d) { return d==draggable });
|
||||
if(this.drags.length == 0) {
|
||||
Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
||||
Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
||||
Event.stopObserving(document, "keypress", this.eventKeypress);
|
||||
}
|
||||
},
|
||||
|
||||
activate: function(draggable) {
|
||||
if(draggable.options.delay) {
|
||||
this._timeout = setTimeout(function() {
|
||||
Draggables._timeout = null;
|
||||
window.focus();
|
||||
Draggables.activeDraggable = draggable;
|
||||
}.bind(this), draggable.options.delay);
|
||||
} else {
|
||||
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
||||
this.activeDraggable = draggable;
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
updateDrag: function(event) {
|
||||
if(!this.activeDraggable) return;
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
// Mozilla-based browsers fire successive mousemove events with
|
||||
// the same coordinates, prevent needless redrawing (moz bug?)
|
||||
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
||||
this._lastPointer = pointer;
|
||||
|
||||
this.activeDraggable.updateDrag(event, pointer);
|
||||
},
|
||||
|
||||
endDrag: function(event) {
|
||||
if(this._timeout) {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = null;
|
||||
}
|
||||
if(!this.activeDraggable) return;
|
||||
this._lastPointer = null;
|
||||
this.activeDraggable.endDrag(event);
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
keyPress: function(event) {
|
||||
if(this.activeDraggable)
|
||||
this.activeDraggable.keyPress(event);
|
||||
},
|
||||
|
||||
addObserver: function(observer) {
|
||||
this.observers.push(observer);
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
|
||||
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
||||
this.observers = this.observers.reject( function(o) { return o.element==element });
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
|
||||
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
||||
if(this[eventName+'Count'] > 0)
|
||||
this.observers.each( function(o) {
|
||||
if(o[eventName]) o[eventName](eventName, draggable, event);
|
||||
});
|
||||
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
|
||||
},
|
||||
|
||||
_cacheObserverCallbacks: function() {
|
||||
['onStart','onEnd','onDrag'].each( function(eventName) {
|
||||
Draggables[eventName+'Count'] = Draggables.observers.select(
|
||||
function(o) { return o[eventName]; }
|
||||
).length;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var Draggable = Class.create();
|
||||
Draggable._dragging = {};
|
||||
|
||||
Draggable.prototype = {
|
||||
initialize: function(element) {
|
||||
var defaults = {
|
||||
handle: false,
|
||||
reverteffect: function(element, top_offset, left_offset) {
|
||||
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
|
||||
new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
|
||||
queue: {scope:'_draggable', position:'end'}
|
||||
});
|
||||
},
|
||||
endeffect: function(element) {
|
||||
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
|
||||
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
|
||||
queue: {scope:'_draggable', position:'end'},
|
||||
afterFinish: function(){
|
||||
Draggable._dragging[element] = false
|
||||
}
|
||||
});
|
||||
},
|
||||
zindex: 1000,
|
||||
revert: false,
|
||||
scroll: false,
|
||||
scrollSensitivity: 20,
|
||||
scrollSpeed: 15,
|
||||
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
||||
delay: 0
|
||||
};
|
||||
|
||||
if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
|
||||
Object.extend(defaults, {
|
||||
starteffect: function(element) {
|
||||
element._opacity = Element.getOpacity(element);
|
||||
Draggable._dragging[element] = true;
|
||||
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
|
||||
}
|
||||
});
|
||||
|
||||
var options = Object.extend(defaults, arguments[1] || {});
|
||||
|
||||
this.element = $(element);
|
||||
|
||||
if(options.handle && (typeof options.handle == 'string'))
|
||||
this.handle = this.element.down('.'+options.handle, 0);
|
||||
|
||||
if(!this.handle) this.handle = $(options.handle);
|
||||
if(!this.handle) this.handle = this.element;
|
||||
|
||||
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
|
||||
options.scroll = $(options.scroll);
|
||||
this._isScrollChild = Element.childOf(this.element, options.scroll);
|
||||
}
|
||||
|
||||
Element.makePositioned(this.element); // fix IE
|
||||
|
||||
this.delta = this.currentDelta();
|
||||
this.options = options;
|
||||
this.dragging = false;
|
||||
|
||||
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
||||
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
||||
|
||||
Draggables.register(this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
||||
Draggables.unregister(this);
|
||||
},
|
||||
|
||||
currentDelta: function() {
|
||||
return([
|
||||
parseInt(Element.getStyle(this.element,'left') || '0'),
|
||||
parseInt(Element.getStyle(this.element,'top') || '0')]);
|
||||
},
|
||||
|
||||
initDrag: function(event) {
|
||||
if(typeof Draggable._dragging[this.element] != 'undefined' &&
|
||||
Draggable._dragging[this.element]) return;
|
||||
if(Event.isLeftClick(event)) {
|
||||
// abort on form elements, fixes a Firefox issue
|
||||
var src = Event.element(event);
|
||||
if(src.tagName && (
|
||||
src.tagName=='INPUT' ||
|
||||
src.tagName=='SELECT' ||
|
||||
src.tagName=='OPTION' ||
|
||||
src.tagName=='BUTTON' ||
|
||||
src.tagName=='TEXTAREA')) return;
|
||||
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
var pos = Position.cumulativeOffset(this.element);
|
||||
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
||||
|
||||
Draggables.activate(this);
|
||||
Event.stop(event);
|
||||
}
|
||||
},
|
||||
|
||||
startDrag: function(event) {
|
||||
this.dragging = true;
|
||||
|
||||
if(this.options.zindex) {
|
||||
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
||||
this.element.style.zIndex = this.options.zindex;
|
||||
}
|
||||
|
||||
if(this.options.ghosting) {
|
||||
this._clone = this.element.cloneNode(true);
|
||||
Position.absolutize(this.element);
|
||||
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||
}
|
||||
|
||||
if(this.options.scroll) {
|
||||
if (this.options.scroll == window) {
|
||||
var where = this._getWindowScroll(this.options.scroll);
|
||||
this.originalScrollLeft = where.left;
|
||||
this.originalScrollTop = where.top;
|
||||
} else {
|
||||
this.originalScrollLeft = this.options.scroll.scrollLeft;
|
||||
this.originalScrollTop = this.options.scroll.scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
Draggables.notify('onStart', this, event);
|
||||
|
||||
if(this.options.starteffect) this.options.starteffect(this.element);
|
||||
},
|
||||
|
||||
updateDrag: function(event, pointer) {
|
||||
if(!this.dragging) this.startDrag(event);
|
||||
Position.prepare();
|
||||
Droppables.show(pointer, this.element);
|
||||
Draggables.notify('onDrag', this, event);
|
||||
|
||||
this.draw(pointer);
|
||||
if(this.options.change) this.options.change(this);
|
||||
|
||||
if(this.options.scroll) {
|
||||
this.stopScrolling();
|
||||
|
||||
var p;
|
||||
if (this.options.scroll == window) {
|
||||
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
|
||||
} else {
|
||||
p = Position.page(this.options.scroll);
|
||||
p[0] += this.options.scroll.scrollLeft + Position.deltaX;
|
||||
p[1] += this.options.scroll.scrollTop + Position.deltaY;
|
||||
p.push(p[0]+this.options.scroll.offsetWidth);
|
||||
p.push(p[1]+this.options.scroll.offsetHeight);
|
||||
}
|
||||
var speed = [0,0];
|
||||
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
|
||||
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
|
||||
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
|
||||
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
|
||||
this.startScrolling(speed);
|
||||
}
|
||||
|
||||
// fix AppleWebKit rendering
|
||||
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
|
||||
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
finishDrag: function(event, success) {
|
||||
this.dragging = false;
|
||||
|
||||
if(this.options.ghosting) {
|
||||
Position.relativize(this.element);
|
||||
Element.remove(this._clone);
|
||||
this._clone = null;
|
||||
}
|
||||
|
||||
if(success) Droppables.fire(event, this.element);
|
||||
Draggables.notify('onEnd', this, event);
|
||||
|
||||
var revert = this.options.revert;
|
||||
if(revert && typeof revert == 'function') revert = revert(this.element);
|
||||
|
||||
var d = this.currentDelta();
|
||||
if(revert && this.options.reverteffect) {
|
||||
this.options.reverteffect(this.element,
|
||||
d[1]-this.delta[1], d[0]-this.delta[0]);
|
||||
} else {
|
||||
this.delta = d;
|
||||
}
|
||||
|
||||
if(this.options.zindex)
|
||||
this.element.style.zIndex = this.originalZ;
|
||||
|
||||
if(this.options.endeffect)
|
||||
this.options.endeffect(this.element);
|
||||
|
||||
Draggables.deactivate(this);
|
||||
Droppables.reset();
|
||||
},
|
||||
|
||||
keyPress: function(event) {
|
||||
if(event.keyCode!=Event.KEY_ESC) return;
|
||||
this.finishDrag(event, false);
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
endDrag: function(event) {
|
||||
if(!this.dragging) return;
|
||||
this.stopScrolling();
|
||||
this.finishDrag(event, true);
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
draw: function(point) {
|
||||
var pos = Position.cumulativeOffset(this.element);
|
||||
if(this.options.ghosting) {
|
||||
var r = Position.realOffset(this.element);
|
||||
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
|
||||
}
|
||||
|
||||
var d = this.currentDelta();
|
||||
pos[0] -= d[0]; pos[1] -= d[1];
|
||||
|
||||
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
|
||||
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
|
||||
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
|
||||
}
|
||||
|
||||
var p = [0,1].map(function(i){
|
||||
return (point[i]-pos[i]-this.offset[i])
|
||||
}.bind(this));
|
||||
|
||||
if(this.options.snap) {
|
||||
if(typeof this.options.snap == 'function') {
|
||||
p = this.options.snap(p[0],p[1],this);
|
||||
} else {
|
||||
if(this.options.snap instanceof Array) {
|
||||
p = p.map( function(v, i) {
|
||||
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
|
||||
} else {
|
||||
p = p.map( function(v) {
|
||||
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
|
||||
}
|
||||
}}
|
||||
|
||||
var style = this.element.style;
|
||||
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
||||
style.left = p[0] + "px";
|
||||
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
||||
style.top = p[1] + "px";
|
||||
|
||||
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
||||
},
|
||||
|
||||
stopScrolling: function() {
|
||||
if(this.scrollInterval) {
|
||||
clearInterval(this.scrollInterval);
|
||||
this.scrollInterval = null;
|
||||
Draggables._lastScrollPointer = null;
|
||||
}
|
||||
},
|
||||
|
||||
startScrolling: function(speed) {
|
||||
if(!(speed[0] || speed[1])) return;
|
||||
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
|
||||
this.lastScrolled = new Date();
|
||||
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
|
||||
},
|
||||
|
||||
scroll: function() {
|
||||
var current = new Date();
|
||||
var delta = current - this.lastScrolled;
|
||||
this.lastScrolled = current;
|
||||
if(this.options.scroll == window) {
|
||||
with (this._getWindowScroll(this.options.scroll)) {
|
||||
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
|
||||
var d = delta / 1000;
|
||||
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
||||
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
||||
}
|
||||
|
||||
Position.prepare();
|
||||
Droppables.show(Draggables._lastPointer, this.element);
|
||||
Draggables.notify('onDrag', this);
|
||||
if (this._isScrollChild) {
|
||||
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
|
||||
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
|
||||
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
|
||||
if (Draggables._lastScrollPointer[0] < 0)
|
||||
Draggables._lastScrollPointer[0] = 0;
|
||||
if (Draggables._lastScrollPointer[1] < 0)
|
||||
Draggables._lastScrollPointer[1] = 0;
|
||||
this.draw(Draggables._lastScrollPointer);
|
||||
}
|
||||
|
||||
if(this.options.change) this.options.change(this);
|
||||
},
|
||||
|
||||
_getWindowScroll: function(w) {
|
||||
var T, L, W, H;
|
||||
with (w.document) {
|
||||
if (w.document.documentElement && documentElement.scrollTop) {
|
||||
T = documentElement.scrollTop;
|
||||
L = documentElement.scrollLeft;
|
||||
} else if (w.document.body) {
|
||||
T = body.scrollTop;
|
||||
L = body.scrollLeft;
|
||||
}
|
||||
if (w.innerWidth) {
|
||||
W = w.innerWidth;
|
||||
H = w.innerHeight;
|
||||
} else if (w.document.documentElement && documentElement.clientWidth) {
|
||||
W = documentElement.clientWidth;
|
||||
H = documentElement.clientHeight;
|
||||
} else {
|
||||
W = body.offsetWidth;
|
||||
H = body.offsetHeight
|
||||
}
|
||||
}
|
||||
return { top: T, left: L, width: W, height: H };
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var SortableObserver = Class.create();
|
||||
SortableObserver.prototype = {
|
||||
initialize: function(element, observer) {
|
||||
this.element = $(element);
|
||||
this.observer = observer;
|
||||
this.lastValue = Sortable.serialize(this.element);
|
||||
},
|
||||
|
||||
onStart: function() {
|
||||
this.lastValue = Sortable.serialize(this.element);
|
||||
},
|
||||
|
||||
onEnd: function() {
|
||||
Sortable.unmark();
|
||||
if(this.lastValue != Sortable.serialize(this.element))
|
||||
this.observer(this.element)
|
||||
}
|
||||
}
|
||||
|
||||
var Sortable = {
|
||||
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
|
||||
|
||||
sortables: {},
|
||||
|
||||
_findRootElement: function(element) {
|
||||
while (element.tagName != "BODY") {
|
||||
if(element.id && Sortable.sortables[element.id]) return element;
|
||||
element = element.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
options: function(element) {
|
||||
element = Sortable._findRootElement($(element));
|
||||
if(!element) return;
|
||||
return Sortable.sortables[element.id];
|
||||
},
|
||||
|
||||
destroy: function(element){
|
||||
var s = Sortable.options(element);
|
||||
|
||||
if(s) {
|
||||
Draggables.removeObserver(s.element);
|
||||
s.droppables.each(function(d){ Droppables.remove(d) });
|
||||
s.draggables.invoke('destroy');
|
||||
|
||||
delete Sortable.sortables[s.element.id];
|
||||
}
|
||||
},
|
||||
|
||||
create: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend({
|
||||
element: element,
|
||||
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||
dropOnEmpty: false,
|
||||
tree: false,
|
||||
treeTag: 'ul',
|
||||
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
||||
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
||||
containment: element, // also takes array of elements (or id's); or false
|
||||
handle: false, // or a CSS class
|
||||
only: false,
|
||||
delay: 0,
|
||||
hoverclass: null,
|
||||
ghosting: false,
|
||||
scroll: false,
|
||||
scrollSensitivity: 20,
|
||||
scrollSpeed: 15,
|
||||
format: this.SERIALIZE_RULE,
|
||||
onChange: Prototype.emptyFunction,
|
||||
onUpdate: Prototype.emptyFunction
|
||||
}, arguments[1] || {});
|
||||
|
||||
// clear any old sortable with same element
|
||||
this.destroy(element);
|
||||
|
||||
// build options for the draggables
|
||||
var options_for_draggable = {
|
||||
revert: true,
|
||||
scroll: options.scroll,
|
||||
scrollSpeed: options.scrollSpeed,
|
||||
scrollSensitivity: options.scrollSensitivity,
|
||||
delay: options.delay,
|
||||
ghosting: options.ghosting,
|
||||
constraint: options.constraint,
|
||||
handle: options.handle };
|
||||
|
||||
if(options.starteffect)
|
||||
options_for_draggable.starteffect = options.starteffect;
|
||||
|
||||
if(options.reverteffect)
|
||||
options_for_draggable.reverteffect = options.reverteffect;
|
||||
else
|
||||
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
|
||||
element.style.top = 0;
|
||||
element.style.left = 0;
|
||||
};
|
||||
|
||||
if(options.endeffect)
|
||||
options_for_draggable.endeffect = options.endeffect;
|
||||
|
||||
if(options.zindex)
|
||||
options_for_draggable.zindex = options.zindex;
|
||||
|
||||
// build options for the droppables
|
||||
var options_for_droppable = {
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
tree: options.tree,
|
||||
hoverclass: options.hoverclass,
|
||||
onHover: Sortable.onHover
|
||||
}
|
||||
|
||||
var options_for_tree = {
|
||||
onHover: Sortable.onEmptyHover,
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass
|
||||
}
|
||||
|
||||
// fix for gecko engine
|
||||
Element.cleanWhitespace(element);
|
||||
|
||||
options.draggables = [];
|
||||
options.droppables = [];
|
||||
|
||||
// drop on empty handling
|
||||
if(options.dropOnEmpty || options.tree) {
|
||||
Droppables.add(element, options_for_tree);
|
||||
options.droppables.push(element);
|
||||
}
|
||||
|
||||
(this.findElements(element, options) || []).each( function(e) {
|
||||
// handles are per-draggable
|
||||
var handle = options.handle ?
|
||||
$(e).down('.'+options.handle,0) : e;
|
||||
options.draggables.push(
|
||||
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||
Droppables.add(e, options_for_droppable);
|
||||
if(options.tree) e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
|
||||
if(options.tree) {
|
||||
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
||||
Droppables.add(e, options_for_tree);
|
||||
e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
}
|
||||
|
||||
// keep reference
|
||||
this.sortables[element.id] = options;
|
||||
|
||||
// for onupdate
|
||||
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
|
||||
|
||||
},
|
||||
|
||||
// return all suitable-for-sortable elements in a guaranteed order
|
||||
findElements: function(element, options) {
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.tag);
|
||||
},
|
||||
|
||||
findTreeElements: function(element, options) {
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.treeTag);
|
||||
},
|
||||
|
||||
onHover: function(element, dropon, overlap) {
|
||||
if(Element.isParent(dropon, element)) return;
|
||||
|
||||
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
|
||||
return;
|
||||
} else if(overlap>0.5) {
|
||||
Sortable.mark(dropon, 'before');
|
||||
if(dropon.previousSibling != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = "hidden"; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, dropon);
|
||||
if(dropon.parentNode!=oldParentNode)
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
Sortable.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
} else {
|
||||
Sortable.mark(dropon, 'after');
|
||||
var nextElement = dropon.nextSibling || null;
|
||||
if(nextElement != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = "hidden"; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, nextElement);
|
||||
if(dropon.parentNode!=oldParentNode)
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
Sortable.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onEmptyHover: function(element, dropon, overlap) {
|
||||
var oldParentNode = element.parentNode;
|
||||
var droponOptions = Sortable.options(dropon);
|
||||
|
||||
if(!Element.isParent(dropon, element)) {
|
||||
var index;
|
||||
|
||||
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
|
||||
var child = null;
|
||||
|
||||
if(children) {
|
||||
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
||||
|
||||
for (index = 0; index < children.length; index += 1) {
|
||||
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
||||
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
||||
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
|
||||
child = index + 1 < children.length ? children[index + 1] : null;
|
||||
break;
|
||||
} else {
|
||||
child = children[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropon.insertBefore(element, child);
|
||||
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
droponOptions.onChange(element);
|
||||
}
|
||||
},
|
||||
|
||||
unmark: function() {
|
||||
if(Sortable._marker) Sortable._marker.hide();
|
||||
},
|
||||
|
||||
mark: function(dropon, position) {
|
||||
// mark on ghosting only
|
||||
var sortable = Sortable.options(dropon.parentNode);
|
||||
if(sortable && !sortable.ghosting) return;
|
||||
|
||||
if(!Sortable._marker) {
|
||||
Sortable._marker =
|
||||
($('dropmarker') || Element.extend(document.createElement('DIV'))).
|
||||
hide().addClassName('dropmarker').setStyle({position:'absolute'});
|
||||
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
||||
}
|
||||
var offsets = Position.cumulativeOffset(dropon);
|
||||
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
|
||||
|
||||
if(position=='after')
|
||||
if(sortable.overlap == 'horizontal')
|
||||
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
|
||||
else
|
||||
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
|
||||
|
||||
Sortable._marker.show();
|
||||
},
|
||||
|
||||
_tree: function(element, options, parent) {
|
||||
var children = Sortable.findElements(element, options) || [];
|
||||
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var match = children[i].id.match(options.format);
|
||||
|
||||
if (!match) continue;
|
||||
|
||||
var child = {
|
||||
id: encodeURIComponent(match ? match[1] : null),
|
||||
element: element,
|
||||
parent: parent,
|
||||
children: [],
|
||||
position: parent.children.length,
|
||||
container: $(children[i]).down(options.treeTag)
|
||||
}
|
||||
|
||||
/* Get the element containing the children and recurse over it */
|
||||
if (child.container)
|
||||
this._tree(child.container, options, child)
|
||||
|
||||
parent.children.push (child);
|
||||
}
|
||||
|
||||
return parent;
|
||||
},
|
||||
|
||||
tree: function(element) {
|
||||
element = $(element);
|
||||
var sortableOptions = this.options(element);
|
||||
var options = Object.extend({
|
||||
tag: sortableOptions.tag,
|
||||
treeTag: sortableOptions.treeTag,
|
||||
only: sortableOptions.only,
|
||||
name: element.id,
|
||||
format: sortableOptions.format
|
||||
}, arguments[1] || {});
|
||||
|
||||
var root = {
|
||||
id: null,
|
||||
parent: null,
|
||||
children: [],
|
||||
container: element,
|
||||
position: 0
|
||||
}
|
||||
|
||||
return Sortable._tree(element, options, root);
|
||||
},
|
||||
|
||||
/* Construct a [i] index for a particular node */
|
||||
_constructIndex: function(node) {
|
||||
var index = '';
|
||||
do {
|
||||
if (node.id) index = '[' + node.position + ']' + index;
|
||||
} while ((node = node.parent) != null);
|
||||
return index;
|
||||
},
|
||||
|
||||
sequence: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend(this.options(element), arguments[1] || {});
|
||||
|
||||
return $(this.findElements(element, options) || []).map( function(item) {
|
||||
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
|
||||
});
|
||||
},
|
||||
|
||||
setSequence: function(element, new_sequence) {
|
||||
element = $(element);
|
||||
var options = Object.extend(this.options(element), arguments[2] || {});
|
||||
|
||||
var nodeMap = {};
|
||||
this.findElements(element, options).each( function(n) {
|
||||
if (n.id.match(options.format))
|
||||
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
|
||||
n.parentNode.removeChild(n);
|
||||
});
|
||||
|
||||
new_sequence.each(function(ident) {
|
||||
var n = nodeMap[ident];
|
||||
if (n) {
|
||||
n[1].appendChild(n[0]);
|
||||
delete nodeMap[ident];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
serialize: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend(Sortable.options(element), arguments[1] || {});
|
||||
var name = encodeURIComponent(
|
||||
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
||||
|
||||
if (options.tree) {
|
||||
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
||||
return [name + Sortable._constructIndex(item) + "[id]=" +
|
||||
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
||||
}).flatten().join('&');
|
||||
} else {
|
||||
return Sortable.sequence(element, arguments[1]).map( function(item) {
|
||||
return name + "[]=" + encodeURIComponent(item);
|
||||
}).join('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if child is contained within element
|
||||
Element.isParent = function(child, element) {
|
||||
if (!child.parentNode || child == element) return false;
|
||||
if (child.parentNode == element) return true;
|
||||
return Element.isParent(child.parentNode, element);
|
||||
}
|
||||
|
||||
Element.findChildren = function(element, only, recursive, tagName) {
|
||||
if(!element.hasChildNodes()) return null;
|
||||
tagName = tagName.toUpperCase();
|
||||
if(only) only = [only].flatten();
|
||||
var elements = [];
|
||||
$A(element.childNodes).each( function(e) {
|
||||
if(e.tagName && e.tagName.toUpperCase()==tagName &&
|
||||
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
|
||||
elements.push(e);
|
||||
if(recursive) {
|
||||
var grandchildren = Element.findChildren(e, only, recursive, tagName);
|
||||
if(grandchildren) elements.push(grandchildren);
|
||||
}
|
||||
});
|
||||
|
||||
return (elements.length>0 ? elements.flatten() : []);
|
||||
}
|
||||
|
||||
Element.offsetSize = function (element, type) {
|
||||
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
|
||||
}
|
1088
P5B/ruby/3dossmanno_annuaire/public/javascripts/effects.js
vendored
Normal file
1088
P5B/ruby/3dossmanno_annuaire/public/javascripts/effects.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2515
P5B/ruby/3dossmanno_annuaire/public/javascripts/prototype.js
vendored
Normal file
2515
P5B/ruby/3dossmanno_annuaire/public/javascripts/prototype.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
P5B/ruby/3dossmanno_annuaire/public/robots.txt
Normal file
1
P5B/ruby/3dossmanno_annuaire/public/robots.txt
Normal file
@ -0,0 +1 @@
|
||||
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
74
P5B/ruby/3dossmanno_annuaire/public/stylesheets/scaffold.css
Normal file
74
P5B/ruby/3dossmanno_annuaire/public/stylesheets/scaffold.css
Normal file
@ -0,0 +1,74 @@
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
.fieldWithErrors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#errorExplanation {
|
||||
width: 400px;
|
||||
border: 2px solid red;
|
||||
padding: 7px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#errorExplanation h2 {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px 5px 15px;
|
||||
font-size: 12px;
|
||||
margin: -7px;
|
||||
background-color: #c00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#errorExplanation p {
|
||||
color: #333;
|
||||
margin-bottom: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#errorExplanation ul li {
|
||||
font-size: 12px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.uploadStatus {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div.progressBar {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div.progressBar div.border {
|
||||
background-color: #fff;
|
||||
border: 1px solid grey;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.progressBar div.background {
|
||||
background-color: #333;
|
||||
height: 18px;
|
||||
width: 0%;
|
||||
}
|
||||
|
3
P5B/ruby/3dossmanno_annuaire/script/about
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/about
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/about'
|
3
P5B/ruby/3dossmanno_annuaire/script/breakpointer
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/breakpointer
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/breakpointer'
|
3
P5B/ruby/3dossmanno_annuaire/script/console
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/console
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/console'
|
3
P5B/ruby/3dossmanno_annuaire/script/destroy
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/destroy
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/destroy'
|
3
P5B/ruby/3dossmanno_annuaire/script/generate
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/generate
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/generate'
|
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require 'commands/performance/benchmarker'
|
3
P5B/ruby/3dossmanno_annuaire/script/performance/profiler
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/performance/profiler
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require 'commands/performance/profiler'
|
3
P5B/ruby/3dossmanno_annuaire/script/plugin
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/plugin
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/plugin'
|
3
P5B/ruby/3dossmanno_annuaire/script/process/inspector
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/process/inspector
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require 'commands/process/inspector'
|
3
P5B/ruby/3dossmanno_annuaire/script/process/reaper
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/process/reaper
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require 'commands/process/reaper'
|
3
P5B/ruby/3dossmanno_annuaire/script/process/spawner
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/process/spawner
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require 'commands/process/spawner'
|
3
P5B/ruby/3dossmanno_annuaire/script/runner
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/runner
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/runner'
|
3
P5B/ruby/3dossmanno_annuaire/script/server
Normal file
3
P5B/ruby/3dossmanno_annuaire/script/server
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require 'commands/server'
|
17
P5B/ruby/3dossmanno_annuaire/test/fixtures/users.yml
vendored
Normal file
17
P5B/ruby/3dossmanno_annuaire/test/fixtures/users.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
quentin:
|
||||
id: 1
|
||||
login: quentin
|
||||
email: quentin@example.com
|
||||
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
|
||||
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
|
||||
created_at: <%= 5.days.ago.to_s :db %>
|
||||
activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b
|
||||
activated_at: <%= 5.days.ago.to_s :db %>
|
||||
aaron:
|
||||
id: 2
|
||||
login: aaron
|
||||
email: aaron@example.com
|
||||
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
|
||||
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
|
||||
created_at: <%= 1.days.ago.to_s :db %>
|
||||
activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9a
|
25
P5B/ruby/3dossmanno_annuaire/test/fixtures/utilisateurs.yml
vendored
Normal file
25
P5B/ruby/3dossmanno_annuaire/test/fixtures/utilisateurs.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||
one:
|
||||
id: 1
|
||||
nom: PFALZGRAF
|
||||
prenom: Christian
|
||||
classe: LPCDED
|
||||
email: christian@pfalzy.net
|
||||
age: 24
|
||||
rue:
|
||||
codePostal:
|
||||
ville:
|
||||
photo:
|
||||
type:
|
||||
two:
|
||||
id: 2
|
||||
nom: DOSSMANN
|
||||
prenom: Olivier
|
||||
classe: LPCDED
|
||||
email: f@f.f
|
||||
age: 23
|
||||
rue: 3, rue des platanes
|
||||
codePostal: 67550
|
||||
ville: VENDENHEIM
|
||||
photo:
|
||||
type:
|
@ -0,0 +1,85 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'sessions_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class SessionsController; def rescue_action(e) raise e end; end
|
||||
|
||||
class SessionsControllerTest < Test::Unit::TestCase
|
||||
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
|
||||
# Then, you can remove it from this and the units test.
|
||||
include AuthenticatedTestHelper
|
||||
|
||||
fixtures :users
|
||||
|
||||
def setup
|
||||
@controller = SessionsController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_should_login_and_redirect
|
||||
post :create, :login => 'quentin', :password => 'test'
|
||||
assert session[:user]
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
def test_should_fail_login_and_not_redirect
|
||||
post :create, :login => 'quentin', :password => 'bad password'
|
||||
assert_nil session[:user]
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_logout
|
||||
login_as :quentin
|
||||
get :destroy
|
||||
assert_nil session[:user]
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
def test_should_remember_me
|
||||
post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
|
||||
assert_not_nil @response.cookies["auth_token"]
|
||||
end
|
||||
|
||||
def test_should_not_remember_me
|
||||
post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
|
||||
assert_nil @response.cookies["auth_token"]
|
||||
end
|
||||
|
||||
def test_should_delete_token_on_logout
|
||||
login_as :quentin
|
||||
get :destroy
|
||||
assert_equal @response.cookies["auth_token"], []
|
||||
end
|
||||
|
||||
def test_should_login_with_cookie
|
||||
users(:quentin).remember_me
|
||||
@request.cookies["auth_token"] = cookie_for(:quentin)
|
||||
get :new
|
||||
assert @controller.send(:logged_in?)
|
||||
end
|
||||
|
||||
def test_should_fail_expired_cookie_login
|
||||
users(:quentin).remember_me
|
||||
users(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
|
||||
@request.cookies["auth_token"] = cookie_for(:quentin)
|
||||
get :new
|
||||
assert !@controller.send(:logged_in?)
|
||||
end
|
||||
|
||||
def test_should_fail_cookie_login
|
||||
users(:quentin).remember_me
|
||||
@request.cookies["auth_token"] = auth_token('invalid_auth_token')
|
||||
get :new
|
||||
assert !@controller.send(:logged_in?)
|
||||
end
|
||||
|
||||
protected
|
||||
def auth_token(token)
|
||||
CGI::Cookie.new('name' => 'auth_token', 'value' => token)
|
||||
end
|
||||
|
||||
def cookie_for(user)
|
||||
auth_token users(user).remember_token
|
||||
end
|
||||
end
|
@ -0,0 +1,86 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'users_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class UsersController; def rescue_action(e) raise e end; end
|
||||
|
||||
class UsersControllerTest < Test::Unit::TestCase
|
||||
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
|
||||
# Then, you can remove it from this and the units test.
|
||||
include AuthenticatedTestHelper
|
||||
|
||||
fixtures :users
|
||||
|
||||
def setup
|
||||
@controller = UsersController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_should_allow_signup
|
||||
assert_difference 'User.count' do
|
||||
create_user
|
||||
assert_response :redirect
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_login_on_signup
|
||||
assert_no_difference 'User.count' do
|
||||
create_user(:login => nil)
|
||||
assert assigns(:user).errors.on(:login)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_password_on_signup
|
||||
assert_no_difference 'User.count' do
|
||||
create_user(:password => nil)
|
||||
assert assigns(:user).errors.on(:password)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_password_confirmation_on_signup
|
||||
assert_no_difference 'User.count' do
|
||||
create_user(:password_confirmation => nil)
|
||||
assert assigns(:user).errors.on(:password_confirmation)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_email_on_signup
|
||||
assert_no_difference 'User.count' do
|
||||
create_user(:email => nil)
|
||||
assert assigns(:user).errors.on(:email)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_activate_user
|
||||
assert_nil User.authenticate('aaron', 'test')
|
||||
get :activate, :activation_code => users(:aaron).activation_code
|
||||
assert_redirected_to '/'
|
||||
assert_not_nil flash[:notice]
|
||||
assert_equal users(:aaron), User.authenticate('aaron', 'test')
|
||||
end
|
||||
|
||||
def test_should_not_activate_user_without_key
|
||||
get :activate
|
||||
assert_nil flash[:notice]
|
||||
rescue ActionController::RoutingError
|
||||
# in the event your routes deny this, we'll just bow out gracefully.
|
||||
end
|
||||
|
||||
def test_should_not_activate_user_with_blank_key
|
||||
get :activate, :activation_code => ''
|
||||
assert_nil flash[:notice]
|
||||
rescue ActionController::RoutingError
|
||||
# well played, sir
|
||||
end
|
||||
|
||||
protected
|
||||
def create_user(options = {})
|
||||
post :create, :user => { :login => 'quire', :email => 'quire@example.com',
|
||||
:password => 'quire', :password_confirmation => 'quire' }.merge(options)
|
||||
end
|
||||
end
|
@ -0,0 +1,57 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'utilisateurs_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class UtilisateursController; def rescue_action(e) raise e end; end
|
||||
|
||||
class UtilisateursControllerTest < Test::Unit::TestCase
|
||||
fixtures :utilisateurs
|
||||
|
||||
def setup
|
||||
@controller = UtilisateursController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_should_get_index
|
||||
get :index
|
||||
assert_response :success
|
||||
assert assigns(:utilisateurs)
|
||||
end
|
||||
|
||||
def test_should_get_new
|
||||
get :new
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_create_utilisateur
|
||||
old_count = Utilisateur.count
|
||||
post :create, :utilisateur => { }
|
||||
assert_equal old_count+1, Utilisateur.count
|
||||
|
||||
assert_redirected_to utilisateur_path(assigns(:utilisateur))
|
||||
end
|
||||
|
||||
def test_should_show_utilisateur
|
||||
get :show, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_get_edit
|
||||
get :edit, :id => 1
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_should_update_utilisateur
|
||||
put :update, :id => 1, :utilisateur => { }
|
||||
assert_redirected_to utilisateur_path(assigns(:utilisateur))
|
||||
end
|
||||
|
||||
def test_should_destroy_utilisateur
|
||||
old_count = Utilisateur.count
|
||||
delete :destroy, :id => 1
|
||||
assert_equal old_count-1, Utilisateur.count
|
||||
|
||||
assert_redirected_to utilisateurs_path
|
||||
end
|
||||
end
|
28
P5B/ruby/3dossmanno_annuaire/test/test_helper.rb
Normal file
28
P5B/ruby/3dossmanno_annuaire/test/test_helper.rb
Normal file
@ -0,0 +1,28 @@
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
||||
require 'test_help'
|
||||
|
||||
class Test::Unit::TestCase
|
||||
# Transactional fixtures accelerate your tests by wrapping each test method
|
||||
# in a transaction that's rolled back on completion. This ensures that the
|
||||
# test database remains unchanged so your fixtures don't have to be reloaded
|
||||
# between every test method. Fewer database queries means faster tests.
|
||||
#
|
||||
# Read Mike Clark's excellent walkthrough at
|
||||
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
||||
#
|
||||
# Every Active Record database supports transactions except MyISAM tables
|
||||
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
||||
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
||||
# is recommended.
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
# Instantiated fixtures are slow, but give you @david where otherwise you
|
||||
# would need people(:david). If you don't want to migrate your existing
|
||||
# test cases which use the @david style and don't mind the speed hit (each
|
||||
# instantiated fixtures translates to a database query per test method),
|
||||
# then set this back to true.
|
||||
self.use_instantiated_fixtures = false
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
end
|
31
P5B/ruby/3dossmanno_annuaire/test/unit/user_mailer_test.rb
Normal file
31
P5B/ruby/3dossmanno_annuaire/test/unit/user_mailer_test.rb
Normal file
@ -0,0 +1,31 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'user_mailer'
|
||||
|
||||
class UserMailerTest < Test::Unit::TestCase
|
||||
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
|
||||
CHARSET = "utf-8"
|
||||
|
||||
include ActionMailer::Quoting
|
||||
|
||||
def setup
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
|
||||
@expected = TMail::Mail.new
|
||||
@expected.set_content_type "text", "plain", { "charset" => CHARSET }
|
||||
end
|
||||
|
||||
def test_dummy_test
|
||||
#do nothing
|
||||
end
|
||||
|
||||
private
|
||||
def read_fixture(action)
|
||||
IO.readlines("#{FIXTURES_PATH}/user_mailer/#{action}")
|
||||
end
|
||||
|
||||
def encode(subject)
|
||||
quoted_printable(subject, CHARSET)
|
||||
end
|
||||
end
|
101
P5B/ruby/3dossmanno_annuaire/test/unit/user_test.rb
Normal file
101
P5B/ruby/3dossmanno_annuaire/test/unit/user_test.rb
Normal file
@ -0,0 +1,101 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class UserTest < Test::Unit::TestCase
|
||||
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
|
||||
# Then, you can remove it from this and the functional test.
|
||||
include AuthenticatedTestHelper
|
||||
fixtures :users
|
||||
|
||||
def test_should_create_user
|
||||
assert_difference 'User.count' do
|
||||
user = create_user
|
||||
assert !user.new_record?, "#{user.errors.full_messages.to_sentence}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_login
|
||||
assert_no_difference 'User.count' do
|
||||
u = create_user(:login => nil)
|
||||
assert u.errors.on(:login)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_password
|
||||
assert_no_difference 'User.count' do
|
||||
u = create_user(:password => nil)
|
||||
assert u.errors.on(:password)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_password_confirmation
|
||||
assert_no_difference 'User.count' do
|
||||
u = create_user(:password_confirmation => nil)
|
||||
assert u.errors.on(:password_confirmation)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_require_email
|
||||
assert_no_difference 'User.count' do
|
||||
u = create_user(:email => nil)
|
||||
assert u.errors.on(:email)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_reset_password
|
||||
users(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
|
||||
assert_equal users(:quentin), User.authenticate('quentin', 'new password')
|
||||
end
|
||||
|
||||
def test_should_not_rehash_password
|
||||
users(:quentin).update_attributes(:login => 'quentin2')
|
||||
assert_equal users(:quentin), User.authenticate('quentin2', 'test')
|
||||
end
|
||||
|
||||
def test_should_authenticate_user
|
||||
assert_equal users(:quentin), User.authenticate('quentin', 'test')
|
||||
end
|
||||
|
||||
def test_should_set_remember_token
|
||||
users(:quentin).remember_me
|
||||
assert_not_nil users(:quentin).remember_token
|
||||
assert_not_nil users(:quentin).remember_token_expires_at
|
||||
end
|
||||
|
||||
def test_should_unset_remember_token
|
||||
users(:quentin).remember_me
|
||||
assert_not_nil users(:quentin).remember_token
|
||||
users(:quentin).forget_me
|
||||
assert_nil users(:quentin).remember_token
|
||||
end
|
||||
|
||||
def test_should_remember_me_for_one_week
|
||||
before = 1.week.from_now.utc
|
||||
users(:quentin).remember_me_for 1.week
|
||||
after = 1.week.from_now.utc
|
||||
assert_not_nil users(:quentin).remember_token
|
||||
assert_not_nil users(:quentin).remember_token_expires_at
|
||||
assert users(:quentin).remember_token_expires_at.between?(before, after)
|
||||
end
|
||||
|
||||
def test_should_remember_me_until_one_week
|
||||
time = 1.week.from_now.utc
|
||||
users(:quentin).remember_me_until time
|
||||
assert_not_nil users(:quentin).remember_token
|
||||
assert_not_nil users(:quentin).remember_token_expires_at
|
||||
assert_equal users(:quentin).remember_token_expires_at, time
|
||||
end
|
||||
|
||||
def test_should_remember_me_default_two_weeks
|
||||
before = 2.weeks.from_now.utc
|
||||
users(:quentin).remember_me
|
||||
after = 2.weeks.from_now.utc
|
||||
assert_not_nil users(:quentin).remember_token
|
||||
assert_not_nil users(:quentin).remember_token_expires_at
|
||||
assert users(:quentin).remember_token_expires_at.between?(before, after)
|
||||
end
|
||||
|
||||
protected
|
||||
def create_user(options = {})
|
||||
User.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
|
||||
end
|
||||
end
|
10
P5B/ruby/3dossmanno_annuaire/test/unit/utilisateur_test.rb
Normal file
10
P5B/ruby/3dossmanno_annuaire/test/unit/utilisateur_test.rb
Normal file
@ -0,0 +1,10 @@
|
||||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class UtilisateurTest < Test::Unit::TestCase
|
||||
fixtures :utilisateurs
|
||||
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
||||
end
|
||||
end
|
Binary file not shown.
19
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/CHANGELOG
vendored
Normal file
19
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/CHANGELOG
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
* April 2, 2007 *
|
||||
|
||||
* don't copy the #full_filename to the default #temp_paths array if it doesn't exist
|
||||
* add default ID partitioning for attachments
|
||||
* add #binmode call to Tempfile (note: ruby should be doing this!) [Eric Beland]
|
||||
* Check for current type of :thumbnails option.
|
||||
* allow customization of the S3 configuration file path with the :s3_config_path option.
|
||||
* Don't try to remove thumbnails if there aren't any. Closes #3 [ben stiglitz]
|
||||
|
||||
* BC * (before changelog)
|
||||
|
||||
* add default #temp_paths entry [mattly]
|
||||
* add MiniMagick support to attachment_fu [Isacc]
|
||||
* update #destroy_file to clear out any empty directories too [carlivar]
|
||||
* fix references to S3Backend module [Hunter Hillegas]
|
||||
* make #current_data public with db_file and s3 backends [ebryn]
|
||||
* oops, actually svn add the files for s3 backend. [Jeffrey Hardy]
|
||||
* experimental s3 support, egad, no tests.... [Jeffrey Hardy]
|
||||
* doh, fix a few bad references to ActsAsAttachment [sixty4bit]
|
162
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/README
vendored
Normal file
162
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/README
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
attachment-fu
|
||||
=====================
|
||||
|
||||
attachment_fu is a plugin by Rick Olson (aka technoweenie <http://techno-weenie.net>) and is the successor to acts_as_attachment. To get a basic run-through of its capabilities, check out Mike Clark's tutorial <http://clarkware.com/cgi/blosxom/2007/02/24#FileUploadFu>.
|
||||
|
||||
|
||||
attachment_fu functionality
|
||||
===========================
|
||||
|
||||
attachment_fu facilitates file uploads in Ruby on Rails. There are a few storage options for the actual file data, but the plugin always at a minimum stores metadata for each file in the database.
|
||||
|
||||
There are three storage options for files uploaded through attachment_fu:
|
||||
File system
|
||||
Database file
|
||||
Amazon S3
|
||||
|
||||
Each method of storage many options associated with it that will be covered in the following section. Something to note, however, is that the Amazon S3 storage requires you to modify config/amazon_s3.yml and the Database file storage requires an extra table.
|
||||
|
||||
|
||||
attachment_fu models
|
||||
====================
|
||||
|
||||
For all three of these storage options a table of metadata is required. This table will contain information about the file (hence the 'meta') and its location. This table has no restrictions on naming, unlike the extra table required for database storage, which must have a table name of db_files (and by convention a model of DbFile).
|
||||
|
||||
In the model there are two methods made available by this plugins: has_attachment and validates_as_attachment.
|
||||
|
||||
has_attachment(options = {})
|
||||
This method accepts the options in a hash:
|
||||
:content_type # Allowed content types.
|
||||
# Allows all by default. Use :image to allow all standard image types.
|
||||
:min_size # Minimum size allowed.
|
||||
# 1 byte is the default.
|
||||
:max_size # Maximum size allowed.
|
||||
# 1.megabyte is the default.
|
||||
:size # Range of sizes allowed.
|
||||
# (1..1.megabyte) is the default. This overrides the :min_size and :max_size options.
|
||||
:resize_to # Used by RMagick to resize images.
|
||||
# Pass either an array of width/height, or a geometry string.
|
||||
:thumbnails # Specifies a set of thumbnails to generate.
|
||||
# This accepts a hash of filename suffixes and RMagick resizing options.
|
||||
# This option need only be included if you want thumbnailing.
|
||||
:thumbnail_class # Set which model class to use for thumbnails.
|
||||
# This current attachment class is used by default.
|
||||
:path_prefix # path to store the uploaded files.
|
||||
# Uses public/#{table_name} by default for the filesystem, and just #{table_name} for the S3 backend.
|
||||
# Setting this sets the :storage to :file_system.
|
||||
:storage # Specifies the storage system to use..
|
||||
# Defaults to :db_file. Options are :file_system, :db_file, and :s3.
|
||||
:processor # Sets the image processor to use for resizing of the attached image.
|
||||
# Options include ImageScience, Rmagick, and MiniMagick. Default is whatever is installed.
|
||||
|
||||
|
||||
Examples:
|
||||
has_attachment :max_size => 1.kilobyte
|
||||
has_attachment :size => 1.megabyte..2.megabytes
|
||||
has_attachment :content_type => 'application/pdf'
|
||||
has_attachment :content_type => ['application/pdf', 'application/msword', 'text/plain']
|
||||
has_attachment :content_type => :image, :resize_to => [50,50]
|
||||
has_attachment :content_type => ['application/pdf', :image], :resize_to => 'x50'
|
||||
has_attachment :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
|
||||
has_attachment :storage => :file_system, :path_prefix => 'public/files'
|
||||
has_attachment :storage => :file_system, :path_prefix => 'public/files',
|
||||
:content_type => :image, :resize_to => [50,50]
|
||||
has_attachment :storage => :file_system, :path_prefix => 'public/files',
|
||||
:thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
|
||||
has_attachment :storage => :s3
|
||||
|
||||
validates_as_attachment
|
||||
This method prevents files outside of the valid range (:min_size to :max_size, or the :size range) from being saved. It does not however, halt the upload of such files. They will be uploaded into memory regardless of size before validation.
|
||||
|
||||
Example:
|
||||
validates_as_attachment
|
||||
|
||||
|
||||
attachment_fu migrations
|
||||
========================
|
||||
|
||||
Fields for attachment_fu metadata tables...
|
||||
in general:
|
||||
size, :integer # file size in bytes
|
||||
content_type, :string # mime type, ex: application/mp3
|
||||
filename, :string # sanitized filename
|
||||
that reference images:
|
||||
height, :integer # in pixels
|
||||
width, :integer # in pixels
|
||||
that reference images that will be thumbnailed:
|
||||
parent_id, :integer # id of parent image (on the same table, a self-referencing foreign-key).
|
||||
# Only populated if the current object is a thumbnail.
|
||||
thumbnail, :string # the 'type' of thumbnail this attachment record describes.
|
||||
# Only populated if the current object is a thumbnail.
|
||||
# Usage:
|
||||
# [ In Model 'Avatar' ]
|
||||
# has_attachment :content_type => :image,
|
||||
# :storage => :file_system,
|
||||
# :max_size => 500.kilobytes,
|
||||
# :resize_to => '320x200>',
|
||||
# :thumbnails => { :small => '10x10>',
|
||||
# :thumb => '100x100>' }
|
||||
# [ Elsewhere ]
|
||||
# @user.avatar.thumbnails.first.thumbnail #=> 'small'
|
||||
that reference files stored in the database (:db_file):
|
||||
db_file_id, :integer # id of the file in the database (foreign key)
|
||||
|
||||
Field for attachment_fu db_files table:
|
||||
data, :binary # binary file data, for use in database file storage
|
||||
|
||||
|
||||
attachment_fu views
|
||||
===================
|
||||
|
||||
There are two main views tasks that will be directly affected by attachment_fu: upload forms and displaying uploaded images.
|
||||
|
||||
There are two parts of the upload form that differ from typical usage.
|
||||
1. Include ':multipart => true' in the html options of the form_for tag.
|
||||
Example:
|
||||
<% form_for(:attachment_metadata, :url => { :action => "create" }, :html => { :multipart => true }) do |form| %>
|
||||
|
||||
2. Use the file_field helper with :uploaded_data as the field name.
|
||||
Example:
|
||||
<%= form.file_field :uploaded_data %>
|
||||
|
||||
Displaying uploaded images is made easy by the public_filename method of the ActiveRecord attachment objects using file system and s3 storage.
|
||||
|
||||
public_filename(thumbnail = nil)
|
||||
Returns the public path to the file. If a thumbnail prefix is specified it will return the public file path to the corresponding thumbnail.
|
||||
Examples:
|
||||
attachment_obj.public_filename #=> /attachments/2/file.jpg
|
||||
attachment_obj.public_filename(:thumb) #=> /attachments/2/file_thumb.jpg
|
||||
attachment_obj.public_filename(:small) #=> /attachments/2/file_small.jpg
|
||||
|
||||
When serving files from database storage, doing more than simply downloading the file is beyond the scope of this document.
|
||||
|
||||
|
||||
attachment_fu controllers
|
||||
=========================
|
||||
|
||||
There are two considerations to take into account when using attachment_fu in controllers.
|
||||
|
||||
The first is when the files have no publicly accessible path and need to be downloaded through an action.
|
||||
|
||||
Example:
|
||||
def readme
|
||||
send_file '/path/to/readme.txt', :type => 'plain/text', :disposition => 'inline'
|
||||
end
|
||||
|
||||
See the possible values for send_file for reference.
|
||||
|
||||
|
||||
The second is when saving the file when submitted from a form.
|
||||
Example in view:
|
||||
<%= form.file_field :attachable, :uploaded_data %>
|
||||
|
||||
Example in controller:
|
||||
def create
|
||||
@attachable_file = AttachmentMetadataModel.new(params[:attachable])
|
||||
if @attachable_file.save
|
||||
flash[:notice] = 'Attachment was successfully created.'
|
||||
redirect_to attachable_url(@attachable_file)
|
||||
else
|
||||
render :action => :new
|
||||
end
|
||||
end
|
22
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/Rakefile
vendored
Normal file
22
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/Rakefile
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the attachment_fu plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the attachment_fu plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'ActsAsAttachment'
|
||||
rdoc.options << '--line-numbers --inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
14
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/amazon_s3.yml.tpl
vendored
Normal file
14
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/amazon_s3.yml.tpl
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
development:
|
||||
bucket_name: appname_development
|
||||
access_key_id:
|
||||
secret_access_key:
|
||||
|
||||
test:
|
||||
bucket_name: appname_test
|
||||
access_key_id:
|
||||
secret_access_key:
|
||||
|
||||
production:
|
||||
bucket_name: appname
|
||||
access_key_id:
|
||||
secret_access_key:
|
14
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/init.rb
vendored
Normal file
14
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/init.rb
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
require 'tempfile'
|
||||
|
||||
Tempfile.class_eval do
|
||||
# overwrite so tempfiles use the extension of the basename. important for rmagick and image science
|
||||
def make_tmpname(basename, n)
|
||||
ext = nil
|
||||
sprintf("%s%d-%d%s", basename.to_s.gsub(/\.\w+$/) { |s| ext = s; '' }, $$, n, ext)
|
||||
end
|
||||
end
|
||||
|
||||
require 'geometry'
|
||||
ActiveRecord::Base.send(:extend, Technoweenie::AttachmentFu::ActMethods)
|
||||
Technoweenie::AttachmentFu.tempfile_path = ATTACHMENT_FU_TEMPFILE_PATH if Object.const_defined?(:ATTACHMENT_FU_TEMPFILE_PATH)
|
||||
FileUtils.mkdir_p Technoweenie::AttachmentFu.tempfile_path
|
5
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/install.rb
vendored
Normal file
5
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/install.rb
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
require 'fileutils'
|
||||
|
||||
s3_config = File.dirname(__FILE__) + '/../../../config/amazon_s3.yml'
|
||||
FileUtils.cp File.dirname(__FILE__) + '/amazon_s3.yml.tpl', s3_config unless File.exist?(s3_config)
|
||||
puts IO.read(File.join(File.dirname(__FILE__), 'README'))
|
93
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/lib/geometry.rb
vendored
Normal file
93
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/lib/geometry.rb
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
# This Geometry class was yanked from RMagick. However, it lets ImageMagick handle the actual change_geometry.
|
||||
# Use #new_dimensions_for to get new dimensons
|
||||
# Used so I can use spiffy RMagick geometry strings with ImageScience
|
||||
class Geometry
|
||||
# ! and @ are removed until support for them is added
|
||||
FLAGS = ['', '%', '<', '>']#, '!', '@']
|
||||
RFLAGS = { '%' => :percent,
|
||||
'!' => :aspect,
|
||||
'<' => :>,
|
||||
'>' => :<,
|
||||
'@' => :area }
|
||||
|
||||
attr_accessor :width, :height, :x, :y, :flag
|
||||
|
||||
def initialize(width=nil, height=nil, x=nil, y=nil, flag=nil)
|
||||
# Support floating-point width and height arguments so Geometry
|
||||
# objects can be used to specify Image#density= arguments.
|
||||
raise ArgumentError, "width must be >= 0: #{width}" if width < 0
|
||||
raise ArgumentError, "height must be >= 0: #{height}" if height < 0
|
||||
@width = width.to_f
|
||||
@height = height.to_f
|
||||
@x = x.to_i
|
||||
@y = y.to_i
|
||||
@flag = flag
|
||||
end
|
||||
|
||||
# Construct an object from a geometry string
|
||||
RE = /\A(\d*)(?:x(\d+))?([-+]\d+)?([-+]\d+)?([%!<>@]?)\Z/
|
||||
|
||||
def self.from_s(str)
|
||||
raise(ArgumentError, "no geometry string specified") unless str
|
||||
|
||||
if m = RE.match(str)
|
||||
new(m[1].to_i, m[2].to_i, m[3].to_i, m[4].to_i, RFLAGS[m[5]])
|
||||
else
|
||||
raise ArgumentError, "invalid geometry format"
|
||||
end
|
||||
end
|
||||
|
||||
# Convert object to a geometry string
|
||||
def to_s
|
||||
str = ''
|
||||
str << "%g" % @width if @width > 0
|
||||
str << 'x' if (@width > 0 || @height > 0)
|
||||
str << "%g" % @height if @height > 0
|
||||
str << "%+d%+d" % [@x, @y] if (@x != 0 || @y != 0)
|
||||
str << FLAGS[@flag.to_i]
|
||||
end
|
||||
|
||||
# attempts to get new dimensions for the current geometry string given these old dimensions.
|
||||
# This doesn't implement the aspect flag (!) or the area flag (@). PDI
|
||||
def new_dimensions_for(orig_width, orig_height)
|
||||
new_width = orig_width
|
||||
new_height = orig_height
|
||||
|
||||
case @flag
|
||||
when :percent
|
||||
scale_x = @width.zero? ? 100 : @width
|
||||
scale_y = @height.zero? ? @width : @height
|
||||
new_width = scale_x.to_f * (orig_width.to_f / 100.0)
|
||||
new_height = scale_y.to_f * (orig_height.to_f / 100.0)
|
||||
when :<, :>, nil
|
||||
scale_factor =
|
||||
if new_width.zero? || new_height.zero?
|
||||
1.0
|
||||
else
|
||||
if @width.nonzero? && @height.nonzero?
|
||||
[@width.to_f / new_width.to_f, @height.to_f / new_height.to_f].min
|
||||
else
|
||||
@width.nonzero? ? (@width.to_f / new_width.to_f) : (@height.to_f / new_height.to_f)
|
||||
end
|
||||
end
|
||||
new_width = scale_factor * new_width.to_f
|
||||
new_height = scale_factor * new_height.to_f
|
||||
new_width = orig_width if @flag && orig_width.send(@flag, new_width)
|
||||
new_height = orig_height if @flag && orig_height.send(@flag, new_height)
|
||||
end
|
||||
|
||||
[new_width, new_height].collect! { |v| v.round }
|
||||
end
|
||||
end
|
||||
|
||||
class Array
|
||||
# allows you to get new dimensions for the current array of dimensions with a given geometry string
|
||||
#
|
||||
# [50, 64] / '40>' # => [40, 51]
|
||||
def /(geometry)
|
||||
raise ArgumentError, "Only works with a [width, height] pair" if size != 2
|
||||
raise ArgumentError, "Must pass a valid geometry string or object" unless geometry.is_a?(String) || geometry.is_a?(Geometry)
|
||||
geometry = Geometry.from_s(geometry) if geometry.is_a?(String)
|
||||
geometry.new_dimensions_for first, last
|
||||
end
|
||||
end
|
405
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb
vendored
Normal file
405
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu.rb
vendored
Normal file
@ -0,0 +1,405 @@
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
@@default_processors = %w(ImageScience Rmagick MiniMagick)
|
||||
@@tempfile_path = File.join(RAILS_ROOT, 'tmp', 'attachment_fu')
|
||||
@@content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg']
|
||||
mattr_reader :content_types, :tempfile_path, :default_processors
|
||||
mattr_writer :tempfile_path
|
||||
|
||||
class ThumbnailError < StandardError; end
|
||||
class AttachmentError < StandardError; end
|
||||
|
||||
module ActMethods
|
||||
# Options:
|
||||
# * <tt>:content_type</tt> - Allowed content types. Allows all by default. Use :image to allow all standard image types.
|
||||
# * <tt>:min_size</tt> - Minimum size allowed. 1 byte is the default.
|
||||
# * <tt>:max_size</tt> - Maximum size allowed. 1.megabyte is the default.
|
||||
# * <tt>:size</tt> - Range of sizes allowed. (1..1.megabyte) is the default. This overrides the :min_size and :max_size options.
|
||||
# * <tt>:resize_to</tt> - Used by RMagick to resize images. Pass either an array of width/height, or a geometry string.
|
||||
# * <tt>:thumbnails</tt> - Specifies a set of thumbnails to generate. This accepts a hash of filename suffixes and RMagick resizing options.
|
||||
# * <tt>:thumbnail_class</tt> - Set what class to use for thumbnails. This attachment class is used by default.
|
||||
# * <tt>:path_prefix</tt> - path to store the uploaded files. Uses public/#{table_name} by default for the filesystem, and just #{table_name}
|
||||
# for the S3 backend. Setting this sets the :storage to :file_system.
|
||||
# * <tt>:storage</tt> - Use :file_system to specify the attachment data is stored with the file system. Defaults to :db_system.
|
||||
#
|
||||
# Examples:
|
||||
# has_attachment :max_size => 1.kilobyte
|
||||
# has_attachment :size => 1.megabyte..2.megabytes
|
||||
# has_attachment :content_type => 'application/pdf'
|
||||
# has_attachment :content_type => ['application/pdf', 'application/msword', 'text/plain']
|
||||
# has_attachment :content_type => :image, :resize_to => [50,50]
|
||||
# has_attachment :content_type => ['application/pdf', :image], :resize_to => 'x50'
|
||||
# has_attachment :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
|
||||
# has_attachment :storage => :file_system, :path_prefix => 'public/files'
|
||||
# has_attachment :storage => :file_system, :path_prefix => 'public/files',
|
||||
# :content_type => :image, :resize_to => [50,50]
|
||||
# has_attachment :storage => :file_system, :path_prefix => 'public/files',
|
||||
# :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
|
||||
# has_attachment :storage => :s3
|
||||
def has_attachment(options = {})
|
||||
# this allows you to redefine the acts' options for each subclass, however
|
||||
options[:min_size] ||= 1
|
||||
options[:max_size] ||= 1.megabyte
|
||||
options[:size] ||= (options[:min_size]..options[:max_size])
|
||||
options[:thumbnails] ||= {}
|
||||
options[:thumbnail_class] ||= self
|
||||
options[:s3_access] ||= :public_read
|
||||
options[:content_type] = [options[:content_type]].flatten.collect! { |t| t == :image ? Technoweenie::AttachmentFu.content_types : t }.flatten unless options[:content_type].nil?
|
||||
|
||||
unless options[:thumbnails].is_a?(Hash)
|
||||
raise ArgumentError, ":thumbnails option should be a hash: e.g. :thumbnails => { :foo => '50x50' }"
|
||||
end
|
||||
|
||||
# doing these shenanigans so that #attachment_options is available to processors and backends
|
||||
class_inheritable_accessor :attachment_options
|
||||
self.attachment_options = options
|
||||
|
||||
# only need to define these once on a class
|
||||
unless included_modules.include?(InstanceMethods)
|
||||
attr_accessor :thumbnail_resize_options
|
||||
|
||||
attachment_options[:storage] ||= (attachment_options[:file_system_path] || attachment_options[:path_prefix]) ? :file_system : :db_file
|
||||
attachment_options[:path_prefix] ||= attachment_options[:file_system_path]
|
||||
if attachment_options[:path_prefix].nil?
|
||||
attachment_options[:path_prefix] = attachment_options[:storage] == :s3 ? table_name : File.join("public", table_name)
|
||||
end
|
||||
attachment_options[:path_prefix] = attachment_options[:path_prefix][1..-1] if options[:path_prefix].first == '/'
|
||||
|
||||
with_options :foreign_key => 'parent_id' do |m|
|
||||
m.has_many :thumbnails, :class_name => attachment_options[:thumbnail_class].to_s
|
||||
m.belongs_to :parent, :class_name => base_class.to_s
|
||||
end
|
||||
before_destroy :destroy_thumbnails
|
||||
|
||||
before_validation :set_size_from_temp_path
|
||||
after_save :after_process_attachment
|
||||
after_destroy :destroy_file
|
||||
extend ClassMethods
|
||||
include InstanceMethods
|
||||
include Technoweenie::AttachmentFu::Backends.const_get("#{options[:storage].to_s.classify}Backend")
|
||||
case attachment_options[:processor]
|
||||
when :none
|
||||
when nil
|
||||
processors = Technoweenie::AttachmentFu.default_processors.dup
|
||||
begin
|
||||
include Technoweenie::AttachmentFu::Processors.const_get("#{processors.first}Processor") if processors.any?
|
||||
rescue LoadError, MissingSourceFile
|
||||
processors.shift
|
||||
retry
|
||||
end
|
||||
else
|
||||
begin
|
||||
include Technoweenie::AttachmentFu::Processors.const_get("#{options[:processor].to_s.classify}Processor")
|
||||
rescue LoadError, MissingSourceFile
|
||||
puts "Problems loading #{options[:processor]}Processor: #{$!}"
|
||||
end
|
||||
end
|
||||
after_validation :process_attachment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
delegate :content_types, :to => Technoweenie::AttachmentFu
|
||||
|
||||
# Performs common validations for attachment models.
|
||||
def validates_as_attachment
|
||||
validates_presence_of :size, :content_type, :filename
|
||||
validate :attachment_attributes_valid?
|
||||
end
|
||||
|
||||
# Returns true or false if the given content type is recognized as an image.
|
||||
def image?(content_type)
|
||||
content_types.include?(content_type)
|
||||
end
|
||||
|
||||
# Callback after an image has been resized.
|
||||
#
|
||||
# class Foo < ActiveRecord::Base
|
||||
# acts_as_attachment
|
||||
# after_resize do |record, img|
|
||||
# record.aspect_ratio = img.columns.to_f / img.rows.to_f
|
||||
# end
|
||||
# end
|
||||
def after_resize(&block)
|
||||
write_inheritable_array(:after_resize, [block])
|
||||
end
|
||||
|
||||
# Callback after an attachment has been saved either to the file system or the DB.
|
||||
# Only called if the file has been changed, not necessarily if the record is updated.
|
||||
#
|
||||
# class Foo < ActiveRecord::Base
|
||||
# acts_as_attachment
|
||||
# after_attachment_saved do |record|
|
||||
# ...
|
||||
# end
|
||||
# end
|
||||
def after_attachment_saved(&block)
|
||||
write_inheritable_array(:after_attachment_saved, [block])
|
||||
end
|
||||
|
||||
# Callback before a thumbnail is saved. Use this to pass any necessary extra attributes that may be required.
|
||||
#
|
||||
# class Foo < ActiveRecord::Base
|
||||
# acts_as_attachment
|
||||
# before_thumbnail_saved do |record, thumbnail|
|
||||
# ...
|
||||
# end
|
||||
# end
|
||||
def before_thumbnail_saved(&block)
|
||||
write_inheritable_array(:before_thumbnail_saved, [block])
|
||||
end
|
||||
|
||||
# Get the thumbnail class, which is the current attachment class by default.
|
||||
# Configure this with the :thumbnail_class option.
|
||||
def thumbnail_class
|
||||
attachment_options[:thumbnail_class] = attachment_options[:thumbnail_class].constantize unless attachment_options[:thumbnail_class].is_a?(Class)
|
||||
attachment_options[:thumbnail_class]
|
||||
end
|
||||
|
||||
# Copies the given file path to a new tempfile, returning the closed tempfile.
|
||||
def copy_to_temp_file(file, temp_base_name)
|
||||
returning Tempfile.new(temp_base_name, Technoweenie::AttachmentFu.tempfile_path) do |tmp|
|
||||
tmp.close
|
||||
FileUtils.cp file, tmp.path
|
||||
end
|
||||
end
|
||||
|
||||
# Writes the given data to a new tempfile, returning the closed tempfile.
|
||||
def write_to_temp_file(data, temp_base_name)
|
||||
returning Tempfile.new(temp_base_name, Technoweenie::AttachmentFu.tempfile_path) do |tmp|
|
||||
tmp.binmode
|
||||
tmp.write data
|
||||
tmp.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Checks whether the attachment's content type is an image content type
|
||||
def image?
|
||||
self.class.image?(content_type)
|
||||
end
|
||||
|
||||
# Returns true/false if an attachment is thumbnailable. A thumbnailable attachment has an image content type and the parent_id attribute.
|
||||
def thumbnailable?
|
||||
image? && respond_to?(:parent_id) && parent_id.nil?
|
||||
end
|
||||
|
||||
# Returns the class used to create new thumbnails for this attachment.
|
||||
def thumbnail_class
|
||||
self.class.thumbnail_class
|
||||
end
|
||||
|
||||
# Gets the thumbnail name for a filename. 'foo.jpg' becomes 'foo_thumbnail.jpg'
|
||||
def thumbnail_name_for(thumbnail = nil)
|
||||
return filename if thumbnail.blank?
|
||||
ext = nil
|
||||
basename = filename.gsub /\.\w+$/ do |s|
|
||||
ext = s; ''
|
||||
end
|
||||
"#{basename}_#{thumbnail}#{ext}"
|
||||
end
|
||||
|
||||
# Creates or updates the thumbnail for the current attachment.
|
||||
def create_or_update_thumbnail(temp_file, file_name_suffix, *size)
|
||||
thumbnailable? || raise(ThumbnailError.new("Can't create a thumbnail if the content type is not an image or there is no parent_id column"))
|
||||
returning find_or_initialize_thumbnail(file_name_suffix) do |thumb|
|
||||
thumb.attributes = {
|
||||
:content_type => content_type,
|
||||
:filename => thumbnail_name_for(file_name_suffix),
|
||||
:temp_path => temp_file,
|
||||
:thumbnail_resize_options => size
|
||||
}
|
||||
callback_with_args :before_thumbnail_saved, thumb
|
||||
thumb.save!
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the content type.
|
||||
def content_type=(new_type)
|
||||
write_attribute :content_type, new_type.to_s.strip
|
||||
end
|
||||
|
||||
# Sanitizes a filename.
|
||||
def filename=(new_name)
|
||||
write_attribute :filename, sanitize_filename(new_name)
|
||||
end
|
||||
|
||||
# Returns the width/height in a suitable format for the image_tag helper: (100x100)
|
||||
def image_size
|
||||
[width.to_s, height.to_s] * 'x'
|
||||
end
|
||||
|
||||
# Returns true if the attachment data will be written to the storage system on the next save
|
||||
def save_attachment?
|
||||
File.file?(temp_path.to_s)
|
||||
end
|
||||
|
||||
# nil placeholder in case this field is used in a form.
|
||||
def uploaded_data() nil; end
|
||||
|
||||
# This method handles the uploaded file object. If you set the field name to uploaded_data, you don't need
|
||||
# any special code in your controller.
|
||||
#
|
||||
# <% form_for :attachment, :html => { :multipart => true } do |f| -%>
|
||||
# <p><%= f.file_field :uploaded_data %></p>
|
||||
# <p><%= submit_tag :Save %>
|
||||
# <% end -%>
|
||||
#
|
||||
# @attachment = Attachment.create! params[:attachment]
|
||||
#
|
||||
# TODO: Allow it to work with Merb tempfiles too.
|
||||
def uploaded_data=(file_data)
|
||||
return nil if file_data.nil? || file_data.size == 0
|
||||
self.content_type = file_data.content_type
|
||||
self.filename = file_data.original_filename if respond_to?(:filename)
|
||||
if file_data.is_a?(StringIO)
|
||||
file_data.rewind
|
||||
self.temp_data = file_data.read
|
||||
else
|
||||
self.temp_path = file_data.path
|
||||
end
|
||||
end
|
||||
|
||||
# Gets the latest temp path from the collection of temp paths. While working with an attachment,
|
||||
# multiple Tempfile objects may be created for various processing purposes (resizing, for example).
|
||||
# An array of all the tempfile objects is stored so that the Tempfile instance is held on to until
|
||||
# it's not needed anymore. The collection is cleared after saving the attachment.
|
||||
def temp_path
|
||||
p = temp_paths.first
|
||||
p.respond_to?(:path) ? p.path : p.to_s
|
||||
end
|
||||
|
||||
# Gets an array of the currently used temp paths. Defaults to a copy of #full_filename.
|
||||
def temp_paths
|
||||
@temp_paths ||= (new_record? || !File.exist?(full_filename)) ? [] : [copy_to_temp_file(full_filename)]
|
||||
end
|
||||
|
||||
# Adds a new temp_path to the array. This should take a string or a Tempfile. This class makes no
|
||||
# attempt to remove the files, so Tempfiles should be used. Tempfiles remove themselves when they go out of scope.
|
||||
# You can also use string paths for temporary files, such as those used for uploaded files in a web server.
|
||||
def temp_path=(value)
|
||||
temp_paths.unshift value
|
||||
temp_path
|
||||
end
|
||||
|
||||
# Gets the data from the latest temp file. This will read the file into memory.
|
||||
def temp_data
|
||||
save_attachment? ? File.read(temp_path) : nil
|
||||
end
|
||||
|
||||
# Writes the given data to a Tempfile and adds it to the collection of temp files.
|
||||
def temp_data=(data)
|
||||
self.temp_path = write_to_temp_file data unless data.nil?
|
||||
end
|
||||
|
||||
# Copies the given file to a randomly named Tempfile.
|
||||
def copy_to_temp_file(file)
|
||||
self.class.copy_to_temp_file file, random_tempfile_filename
|
||||
end
|
||||
|
||||
# Writes the given file to a randomly named Tempfile.
|
||||
def write_to_temp_file(data)
|
||||
self.class.write_to_temp_file data, random_tempfile_filename
|
||||
end
|
||||
|
||||
# Stub for creating a temp file from the attachment data. This should be defined in the backend module.
|
||||
def create_temp_file() end
|
||||
|
||||
# Allows you to work with a processed representation (RMagick, ImageScience, etc) of the attachment in a block.
|
||||
#
|
||||
# @attachment.with_image do |img|
|
||||
# self.data = img.thumbnail(100, 100).to_blob
|
||||
# end
|
||||
#
|
||||
def with_image(&block)
|
||||
self.class.with_image(temp_path, &block)
|
||||
end
|
||||
|
||||
protected
|
||||
# Generates a unique filename for a Tempfile.
|
||||
def random_tempfile_filename
|
||||
"#{rand Time.now.to_i}#{filename || 'attachment'}"
|
||||
end
|
||||
|
||||
def sanitize_filename(filename)
|
||||
returning filename.strip do |name|
|
||||
# NOTE: File.basename doesn't work right with Windows paths on Unix
|
||||
# get only the filename, not the whole path
|
||||
name.gsub! /^.*(\\|\/)/, ''
|
||||
|
||||
# Finally, replace all non alphanumeric, underscore or periods with underscore
|
||||
name.gsub! /[^\w\.\-]/, '_'
|
||||
end
|
||||
end
|
||||
|
||||
# before_validation callback.
|
||||
def set_size_from_temp_path
|
||||
self.size = File.size(temp_path) if save_attachment?
|
||||
end
|
||||
|
||||
# validates the size and content_type attributes according to the current model's options
|
||||
def attachment_attributes_valid?
|
||||
[:size, :content_type].each do |attr_name|
|
||||
enum = attachment_options[attr_name]
|
||||
errors.add attr_name, ActiveRecord::Errors.default_error_messages[:inclusion] unless enum.nil? || enum.include?(send(attr_name))
|
||||
end
|
||||
end
|
||||
|
||||
# Initializes a new thumbnail with the given suffix.
|
||||
def find_or_initialize_thumbnail(file_name_suffix)
|
||||
respond_to?(:parent_id) ?
|
||||
thumbnail_class.find_or_initialize_by_thumbnail_and_parent_id(file_name_suffix.to_s, id) :
|
||||
thumbnail_class.find_or_initialize_by_thumbnail(file_name_suffix.to_s)
|
||||
end
|
||||
|
||||
# Stub for a #process_attachment method in a processor
|
||||
def process_attachment
|
||||
@saved_attachment = save_attachment?
|
||||
end
|
||||
|
||||
# Cleans up after processing. Thumbnails are created, the attachment is stored to the backend, and the temp_paths are cleared.
|
||||
def after_process_attachment
|
||||
if @saved_attachment
|
||||
if respond_to?(:process_attachment_with_processing) && thumbnailable? && !attachment_options[:thumbnails].blank? && parent_id.nil?
|
||||
temp_file = temp_path || create_temp_file
|
||||
attachment_options[:thumbnails].each { |suffix, size| create_or_update_thumbnail(temp_file, suffix, *size) }
|
||||
end
|
||||
save_to_storage
|
||||
@temp_paths.clear
|
||||
@saved_attachment = nil
|
||||
callback :after_attachment_saved
|
||||
end
|
||||
end
|
||||
|
||||
# Resizes the given processed img object with either the attachment resize options or the thumbnail resize options.
|
||||
def resize_image_or_thumbnail!(img)
|
||||
if (!respond_to?(:parent_id) || parent_id.nil?) && attachment_options[:resize_to] # parent image
|
||||
resize_image(img, attachment_options[:resize_to])
|
||||
elsif thumbnail_resize_options # thumbnail
|
||||
resize_image(img, thumbnail_resize_options)
|
||||
end
|
||||
end
|
||||
|
||||
# Yanked from ActiveRecord::Callbacks, modified so I can pass args to the callbacks besides self.
|
||||
# Only accept blocks, however
|
||||
def callback_with_args(method, arg = self)
|
||||
notify(method)
|
||||
|
||||
result = nil
|
||||
callbacks_for(method).each do |callback|
|
||||
result = callback.call(self, arg)
|
||||
return false if result == false
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
# Removes the thumbnails for the attachment, if it has any
|
||||
def destroy_thumbnails
|
||||
self.thumbnails.each { |thumbnail| thumbnail.destroy } if thumbnailable?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,39 @@
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Backends
|
||||
# Methods for DB backed attachments
|
||||
module DbFileBackend
|
||||
def self.included(base) #:nodoc:
|
||||
Object.const_set(:DbFile, Class.new(ActiveRecord::Base)) unless Object.const_defined?(:DbFile)
|
||||
base.belongs_to :db_file, :class_name => '::DbFile', :foreign_key => 'db_file_id'
|
||||
end
|
||||
|
||||
# Creates a temp file with the current db data.
|
||||
def create_temp_file
|
||||
write_to_temp_file current_data
|
||||
end
|
||||
|
||||
# Gets the current data from the database
|
||||
def current_data
|
||||
db_file.data
|
||||
end
|
||||
|
||||
protected
|
||||
# Destroys the file. Called in the after_destroy callback
|
||||
def destroy_file
|
||||
db_file.destroy if db_file
|
||||
end
|
||||
|
||||
# Saves the data to the DbFile model
|
||||
def save_to_storage
|
||||
if save_attachment?
|
||||
(db_file || build_db_file).data = temp_data
|
||||
db_file.save!
|
||||
self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,97 @@
|
||||
require 'ftools'
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Backends
|
||||
# Methods for file system backed attachments
|
||||
module FileSystemBackend
|
||||
def self.included(base) #:nodoc:
|
||||
base.before_update :rename_file
|
||||
end
|
||||
|
||||
# Gets the full path to the filename in this format:
|
||||
#
|
||||
# # This assumes a model name like MyModel
|
||||
# # public/#{table_name} is the default filesystem path
|
||||
# RAILS_ROOT/public/my_models/5/blah.jpg
|
||||
#
|
||||
# Overwrite this method in your model to customize the filename.
|
||||
# The optional thumbnail argument will output the thumbnail's filename.
|
||||
def full_filename(thumbnail = nil)
|
||||
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s
|
||||
File.join(RAILS_ROOT, file_system_path, *partitioned_path(thumbnail_name_for(thumbnail)))
|
||||
end
|
||||
|
||||
# Used as the base path that #public_filename strips off full_filename to create the public path
|
||||
def base_path
|
||||
@base_path ||= File.join(RAILS_ROOT, 'public')
|
||||
end
|
||||
|
||||
# The attachment ID used in the full path of a file
|
||||
def attachment_path_id
|
||||
((respond_to?(:parent_id) && parent_id) || id).to_i
|
||||
end
|
||||
|
||||
# overrwrite this to do your own app-specific partitioning.
|
||||
# you can thank Jamis Buck for this: http://www.37signals.com/svn/archives2/id_partitioning.php
|
||||
def partitioned_path(*args)
|
||||
("%08d" % attachment_path_id).scan(/..../) + args
|
||||
end
|
||||
|
||||
# Gets the public path to the file
|
||||
# The optional thumbnail argument will output the thumbnail's filename.
|
||||
def public_filename(thumbnail = nil)
|
||||
full_filename(thumbnail).gsub %r(^#{Regexp.escape(base_path)}), ''
|
||||
end
|
||||
|
||||
def filename=(value)
|
||||
@old_filename = full_filename unless filename.nil? || @old_filename
|
||||
write_attribute :filename, sanitize_filename(value)
|
||||
end
|
||||
|
||||
# Creates a temp file from the currently saved file.
|
||||
def create_temp_file
|
||||
copy_to_temp_file full_filename
|
||||
end
|
||||
|
||||
protected
|
||||
# Destroys the file. Called in the after_destroy callback
|
||||
def destroy_file
|
||||
FileUtils.rm full_filename
|
||||
# remove directory also if it is now empty
|
||||
Dir.rmdir(File.dirname(full_filename)) if (Dir.entries(File.dirname(full_filename))-['.','..']).empty?
|
||||
rescue
|
||||
logger.info "Exception destroying #{full_filename.inspect}: [#{$!.class.name}] #{$1.to_s}"
|
||||
logger.warn $!.backtrace.collect { |b| " > #{b}" }.join("\n")
|
||||
end
|
||||
|
||||
# Renames the given file before saving
|
||||
def rename_file
|
||||
return unless @old_filename && @old_filename != full_filename
|
||||
if save_attachment? && File.exists?(@old_filename)
|
||||
FileUtils.rm @old_filename
|
||||
elsif File.exists?(@old_filename)
|
||||
FileUtils.mv @old_filename, full_filename
|
||||
end
|
||||
@old_filename = nil
|
||||
true
|
||||
end
|
||||
|
||||
# Saves the file to the file system
|
||||
def save_to_storage
|
||||
if save_attachment?
|
||||
# TODO: This overwrites the file if it exists, maybe have an allow_overwrite option?
|
||||
FileUtils.mkdir_p(File.dirname(full_filename))
|
||||
File.cp(temp_path, full_filename)
|
||||
File.chmod(attachment_options[:chmod] || 0644, full_filename)
|
||||
end
|
||||
@old_filename = nil
|
||||
true
|
||||
end
|
||||
|
||||
def current_data
|
||||
File.file?(full_filename) ? File.read(full_filename) : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,309 @@
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Backends
|
||||
# = AWS::S3 Storage Backend
|
||||
#
|
||||
# Enables use of {Amazon's Simple Storage Service}[http://aws.amazon.com/s3] as a storage mechanism
|
||||
#
|
||||
# == Requirements
|
||||
#
|
||||
# Requires the {AWS::S3 Library}[http://amazon.rubyforge.org] for S3 by Marcel Molina Jr. installed either
|
||||
# as a gem or a as a Rails plugin.
|
||||
#
|
||||
# == Configuration
|
||||
#
|
||||
# Configuration is done via <tt>RAILS_ROOT/config/amazon_s3.yml</tt> and is loaded according to the <tt>RAILS_ENV</tt>.
|
||||
# The minimum connection options that you must specify are a bucket name, your access key id and your secret access key.
|
||||
# If you don't already have your access keys, all you need to sign up for the S3 service is an account at Amazon.
|
||||
# You can sign up for S3 and get access keys by visiting http://aws.amazon.com/s3.
|
||||
#
|
||||
# Example configuration (RAILS_ROOT/config/amazon_s3.yml)
|
||||
#
|
||||
# development:
|
||||
# bucket_name: appname_development
|
||||
# access_key_id: <your key>
|
||||
# secret_access_key: <your key>
|
||||
#
|
||||
# test:
|
||||
# bucket_name: appname_test
|
||||
# access_key_id: <your key>
|
||||
# secret_access_key: <your key>
|
||||
#
|
||||
# production:
|
||||
# bucket_name: appname
|
||||
# access_key_id: <your key>
|
||||
# secret_access_key: <your key>
|
||||
#
|
||||
# You can change the location of the config path by passing a full path to the :s3_config_path option.
|
||||
#
|
||||
# has_attachment :storage => :s3, :s3_config_path => (RAILS_ROOT + '/config/s3.yml')
|
||||
#
|
||||
# === Required configuration parameters
|
||||
#
|
||||
# * <tt>:access_key_id</tt> - The access key id for your S3 account. Provided by Amazon.
|
||||
# * <tt>:secret_access_key</tt> - The secret access key for your S3 account. Provided by Amazon.
|
||||
# * <tt>:bucket_name</tt> - A unique bucket name (think of the bucket_name as being like a database name).
|
||||
#
|
||||
# If any of these required arguments is missing, a MissingAccessKey exception will be raised from AWS::S3.
|
||||
#
|
||||
# == About bucket names
|
||||
#
|
||||
# Bucket names have to be globaly unique across the S3 system. And you can only have up to 100 of them,
|
||||
# so it's a good idea to think of a bucket as being like a database, hence the correspondance in this
|
||||
# implementation to the development, test, and production environments.
|
||||
#
|
||||
# The number of objects you can store in a bucket is, for all intents and purposes, unlimited.
|
||||
#
|
||||
# === Optional configuration parameters
|
||||
#
|
||||
# * <tt>:server</tt> - The server to make requests to. Defaults to <tt>s3.amazonaws.com</tt>.
|
||||
# * <tt>:port</tt> - The port to the requests should be made on. Defaults to 80 or 443 if <tt>:use_ssl</tt> is set.
|
||||
# * <tt>:use_ssl</tt> - If set to true, <tt>:port</tt> will be implicitly set to 443, unless specified otherwise. Defaults to false.
|
||||
#
|
||||
# == Usage
|
||||
#
|
||||
# To specify S3 as the storage mechanism for a model, set the acts_as_attachment <tt>:storage</tt> option to <tt>:s3</tt>.
|
||||
#
|
||||
# class Photo < ActiveRecord::Base
|
||||
# has_attachment :storage => :s3
|
||||
# end
|
||||
#
|
||||
# === Customizing the path
|
||||
#
|
||||
# By default, files are prefixed using a pseudo hierarchy in the form of <tt>:table_name/:id</tt>, which results
|
||||
# in S3 urls that look like: http(s)://:server/:bucket_name/:table_name/:id/:filename with :table_name
|
||||
# representing the customizable portion of the path. You can customize this prefix using the <tt>:path_prefix</tt>
|
||||
# option:
|
||||
#
|
||||
# class Photo < ActiveRecord::Base
|
||||
# has_attachment :storage => :s3, :path_prefix => 'my/custom/path'
|
||||
# end
|
||||
#
|
||||
# Which would result in URLs like <tt>http(s)://:server/:bucket_name/my/custom/path/:id/:filename.</tt>
|
||||
#
|
||||
# === Permissions
|
||||
#
|
||||
# By default, files are stored on S3 with public access permissions. You can customize this using
|
||||
# the <tt>:s3_access</tt> option to <tt>has_attachment</tt>. Available values are
|
||||
# <tt>:private</tt>, <tt>:public_read_write</tt>, and <tt>:authenticated_read</tt>.
|
||||
#
|
||||
# === Other options
|
||||
#
|
||||
# Of course, all the usual configuration options apply, such as content_type and thumbnails:
|
||||
#
|
||||
# class Photo < ActiveRecord::Base
|
||||
# has_attachment :storage => :s3, :content_type => ['application/pdf', :image], :resize_to => 'x50'
|
||||
# has_attachment :storage => :s3, :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
|
||||
# end
|
||||
#
|
||||
# === Accessing S3 URLs
|
||||
#
|
||||
# You can get an object's URL using the s3_url accessor. For example, assuming that for your postcard app
|
||||
# you had a bucket name like 'postcard_world_development', and an attachment model called Photo:
|
||||
#
|
||||
# @postcard.s3_url # => http(s)://s3.amazonaws.com/postcard_world_development/photos/1/mexico.jpg
|
||||
#
|
||||
# The resulting url is in the form: http(s)://:server/:bucket_name/:table_name/:id/:file.
|
||||
# The optional thumbnail argument will output the thumbnail's filename (if any).
|
||||
#
|
||||
# Additionally, you can get an object's base path relative to the bucket root using
|
||||
# <tt>base_path</tt>:
|
||||
#
|
||||
# @photo.file_base_path # => photos/1
|
||||
#
|
||||
# And the full path (including the filename) using <tt>full_filename</tt>:
|
||||
#
|
||||
# @photo.full_filename # => photos/
|
||||
#
|
||||
# Niether <tt>base_path</tt> or <tt>full_filename</tt> include the bucket name as part of the path.
|
||||
# You can retrieve the bucket name using the <tt>bucket_name</tt> method.
|
||||
module S3Backend
|
||||
class RequiredLibraryNotFoundError < StandardError; end
|
||||
class ConfigFileNotFoundError < StandardError; end
|
||||
|
||||
def self.included(base) #:nodoc:
|
||||
mattr_reader :bucket_name, :s3_config
|
||||
|
||||
begin
|
||||
require 'aws/s3'
|
||||
include AWS::S3
|
||||
rescue LoadError
|
||||
raise RequiredLibraryNotFoundError.new('AWS::S3 could not be loaded')
|
||||
end
|
||||
|
||||
begin
|
||||
@@s3_config_path = base.attachment_options[:s3_config_path] || (RAILS_ROOT + '/config/amazon_s3.yml')
|
||||
@@s3_config = YAML.load_file(@@s3_config_path)[ENV['RAILS_ENV']].symbolize_keys
|
||||
#rescue
|
||||
# raise ConfigFileNotFoundError.new('File %s not found' % @@s3_config_path)
|
||||
end
|
||||
|
||||
@@bucket_name = s3_config[:bucket_name]
|
||||
|
||||
Base.establish_connection!(
|
||||
:access_key_id => s3_config[:access_key_id],
|
||||
:secret_access_key => s3_config[:secret_access_key],
|
||||
:server => s3_config[:server],
|
||||
:port => s3_config[:port],
|
||||
:use_ssl => s3_config[:use_ssl]
|
||||
)
|
||||
|
||||
# Bucket.create(@@bucket_name)
|
||||
|
||||
base.before_update :rename_file
|
||||
end
|
||||
|
||||
def self.protocol
|
||||
@protocol ||= s3_config[:use_ssl] ? 'https://' : 'http://'
|
||||
end
|
||||
|
||||
def self.hostname
|
||||
@hostname ||= s3_config[:server] || AWS::S3::DEFAULT_HOST
|
||||
end
|
||||
|
||||
def self.port_string
|
||||
@port_string ||= s3_config[:port] == (s3_config[:use_ssl] ? 443 : 80) ? '' : ":#{s3_config[:port]}"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def s3_protocol
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.protocol
|
||||
end
|
||||
|
||||
def s3_hostname
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.hostname
|
||||
end
|
||||
|
||||
def s3_port_string
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.port_string
|
||||
end
|
||||
end
|
||||
|
||||
# Overwrites the base filename writer in order to store the old filename
|
||||
def filename=(value)
|
||||
@old_filename = filename unless filename.nil? || @old_filename
|
||||
write_attribute :filename, sanitize_filename(value)
|
||||
end
|
||||
|
||||
# The attachment ID used in the full path of a file
|
||||
def attachment_path_id
|
||||
((respond_to?(:parent_id) && parent_id) || id).to_s
|
||||
end
|
||||
|
||||
# The pseudo hierarchy containing the file relative to the bucket name
|
||||
# Example: <tt>:table_name/:id</tt>
|
||||
def base_path
|
||||
File.join(attachment_options[:path_prefix], attachment_path_id)
|
||||
end
|
||||
|
||||
# The full path to the file relative to the bucket name
|
||||
# Example: <tt>:table_name/:id/:filename</tt>
|
||||
def full_filename(thumbnail = nil)
|
||||
File.join(base_path, thumbnail_name_for(thumbnail))
|
||||
end
|
||||
|
||||
# All public objects are accessible via a GET request to the S3 servers. You can generate a
|
||||
# url for an object using the s3_url method.
|
||||
#
|
||||
# @photo.s3_url
|
||||
#
|
||||
# The resulting url is in the form: <tt>http(s)://:server/:bucket_name/:table_name/:id/:file</tt> where
|
||||
# the <tt>:server</tt> variable defaults to <tt>AWS::S3 URL::DEFAULT_HOST</tt> (s3.amazonaws.com) and can be
|
||||
# set using the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>.
|
||||
#
|
||||
# The optional thumbnail argument will output the thumbnail's filename (if any).
|
||||
def s3_url(thumbnail = nil)
|
||||
File.join(s3_protocol + s3_hostname + s3_port_string, bucket_name, full_filename(thumbnail))
|
||||
end
|
||||
alias :public_filename :s3_url
|
||||
|
||||
# All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an
|
||||
# authenticated url for an object like this:
|
||||
#
|
||||
# @photo.authenticated_s3_url
|
||||
#
|
||||
# By default authenticated urls expire 5 minutes after they were generated.
|
||||
#
|
||||
# Expiration options can be specified either with an absolute time using the <tt>:expires</tt> option,
|
||||
# or with a number of seconds relative to now with the <tt>:expires_in</tt> option:
|
||||
#
|
||||
# # Absolute expiration date (October 13th, 2025)
|
||||
# @photo.authenticated_s3_url(:expires => Time.mktime(2025,10,13).to_i)
|
||||
#
|
||||
# # Expiration in five hours from now
|
||||
# @photo.authenticated_s3_url(:expires_in => 5.hours)
|
||||
#
|
||||
# You can specify whether the url should go over SSL with the <tt>:use_ssl</tt> option.
|
||||
# By default, the ssl settings for the current connection will be used:
|
||||
#
|
||||
# @photo.authenticated_s3_url(:use_ssl => true)
|
||||
#
|
||||
# Finally, the optional thumbnail argument will output the thumbnail's filename (if any):
|
||||
#
|
||||
# @photo.authenticated_s3_url('thumbnail', :expires_in => 5.hours, :use_ssl => true)
|
||||
def authenticated_s3_url(*args)
|
||||
thumbnail = args.first.is_a?(String) ? args.first : nil
|
||||
options = args.last.is_a?(Hash) ? args.last : {}
|
||||
S3Object.url_for(full_filename(thumbnail), bucket_name, options)
|
||||
end
|
||||
|
||||
def create_temp_file
|
||||
write_to_temp_file current_data
|
||||
end
|
||||
|
||||
def current_data
|
||||
S3Object.value full_filename, bucket_name
|
||||
end
|
||||
|
||||
def s3_protocol
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.protocol
|
||||
end
|
||||
|
||||
def s3_hostname
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.hostname
|
||||
end
|
||||
|
||||
def s3_port_string
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.port_string
|
||||
end
|
||||
|
||||
protected
|
||||
# Called in the after_destroy callback
|
||||
def destroy_file
|
||||
S3Object.delete full_filename, bucket_name
|
||||
end
|
||||
|
||||
def rename_file
|
||||
return unless @old_filename && @old_filename != filename
|
||||
|
||||
old_full_filename = File.join(base_path, @old_filename)
|
||||
|
||||
S3Object.rename(
|
||||
old_full_filename,
|
||||
full_filename,
|
||||
bucket_name,
|
||||
:access => attachment_options[:s3_access]
|
||||
)
|
||||
|
||||
@old_filename = nil
|
||||
true
|
||||
end
|
||||
|
||||
def save_to_storage
|
||||
if save_attachment?
|
||||
S3Object.store(
|
||||
full_filename,
|
||||
(temp_path ? File.open(temp_path) : temp_data),
|
||||
bucket_name,
|
||||
:content_type => content_type,
|
||||
:access => attachment_options[:s3_access]
|
||||
)
|
||||
end
|
||||
|
||||
@old_filename = nil
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,55 @@
|
||||
require 'image_science'
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Processors
|
||||
module ImageScienceProcessor
|
||||
def self.included(base)
|
||||
base.send :extend, ClassMethods
|
||||
base.alias_method_chain :process_attachment, :processing
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Yields a block containing an RMagick Image for the given binary data.
|
||||
def with_image(file, &block)
|
||||
::ImageScience.with_image file, &block
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def process_attachment_with_processing
|
||||
return unless process_attachment_without_processing && image?
|
||||
with_image do |img|
|
||||
self.width = img.width if respond_to?(:width)
|
||||
self.height = img.height if respond_to?(:height)
|
||||
resize_image_or_thumbnail! img
|
||||
end
|
||||
end
|
||||
|
||||
# Performs the actual resizing operation for a thumbnail
|
||||
def resize_image(img, size)
|
||||
# create a dummy temp file to write to
|
||||
filename.sub! /gif$/, 'png'
|
||||
self.temp_path = write_to_temp_file(filename)
|
||||
grab_dimensions = lambda do |img|
|
||||
self.width = img.width if respond_to?(:width)
|
||||
self.height = img.height if respond_to?(:height)
|
||||
img.save temp_path
|
||||
callback_with_args :after_resize, img
|
||||
end
|
||||
|
||||
size = size.first if size.is_a?(Array) && size.length == 1
|
||||
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
|
||||
if size.is_a?(Fixnum)
|
||||
img.thumbnail(size, &grab_dimensions)
|
||||
else
|
||||
img.resize(size[0], size[1], &grab_dimensions)
|
||||
end
|
||||
else
|
||||
new_size = [img.width, img.height] / size.to_s
|
||||
img.resize(new_size[0], new_size[1], &grab_dimensions)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,56 @@
|
||||
require 'mini_magick'
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Processors
|
||||
module MiniMagickProcessor
|
||||
def self.included(base)
|
||||
base.send :extend, ClassMethods
|
||||
base.alias_method_chain :process_attachment, :processing
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Yields a block containing an MiniMagick Image for the given binary data.
|
||||
def with_image(file, &block)
|
||||
begin
|
||||
binary_data = file.is_a?(MiniMagick::Image) ? file : MiniMagick::Image.from_file(file) unless !Object.const_defined?(:MiniMagick)
|
||||
rescue
|
||||
# Log the failure to load the image.
|
||||
logger.debug("Exception working with image: #{$!}")
|
||||
binary_data = nil
|
||||
end
|
||||
block.call binary_data if block && binary_data
|
||||
ensure
|
||||
!binary_data.nil?
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def process_attachment_with_processing
|
||||
return unless process_attachment_without_processing
|
||||
with_image do |img|
|
||||
resize_image_or_thumbnail! img
|
||||
self.width = img[:width] if respond_to?(:width)
|
||||
self.height = img[:height] if respond_to?(:height)
|
||||
callback_with_args :after_resize, img
|
||||
end if image?
|
||||
end
|
||||
|
||||
# Performs the actual resizing operation for a thumbnail
|
||||
def resize_image(img, size)
|
||||
size = size.first if size.is_a?(Array) && size.length == 1
|
||||
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
|
||||
if size.is_a?(Fixnum)
|
||||
size = [size, size]
|
||||
img.resize(size.join('x'))
|
||||
else
|
||||
img.resize(size.join('x') + '!')
|
||||
end
|
||||
else
|
||||
img.resize(size.to_s)
|
||||
end
|
||||
self.temp_path = img
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,53 @@
|
||||
require 'RMagick'
|
||||
module Technoweenie # :nodoc:
|
||||
module AttachmentFu # :nodoc:
|
||||
module Processors
|
||||
module RmagickProcessor
|
||||
def self.included(base)
|
||||
base.send :extend, ClassMethods
|
||||
base.alias_method_chain :process_attachment, :processing
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Yields a block containing an RMagick Image for the given binary data.
|
||||
def with_image(file, &block)
|
||||
begin
|
||||
binary_data = file.is_a?(Magick::Image) ? file : Magick::Image.read(file).first unless !Object.const_defined?(:Magick)
|
||||
rescue
|
||||
# Log the failure to load the image. This should match ::Magick::ImageMagickError
|
||||
# but that would cause acts_as_attachment to require rmagick.
|
||||
logger.debug("Exception working with image: #{$!}")
|
||||
binary_data = nil
|
||||
end
|
||||
block.call binary_data if block && binary_data
|
||||
ensure
|
||||
!binary_data.nil?
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def process_attachment_with_processing
|
||||
return unless process_attachment_without_processing
|
||||
with_image do |img|
|
||||
resize_image_or_thumbnail! img
|
||||
self.width = img.columns if respond_to?(:width)
|
||||
self.height = img.rows if respond_to?(:height)
|
||||
callback_with_args :after_resize, img
|
||||
end if image?
|
||||
end
|
||||
|
||||
# Performs the actual resizing operation for a thumbnail
|
||||
def resize_image(img, size)
|
||||
size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
|
||||
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
|
||||
size = [size, size] if size.is_a?(Fixnum)
|
||||
img.thumbnail!(*size)
|
||||
else
|
||||
img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols, rows) }
|
||||
end
|
||||
self.temp_path = write_to_temp_file(img.to_blob)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
6
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/amazon_s3.yml
vendored
Normal file
6
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/amazon_s3.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
test:
|
||||
bucket_name: afu
|
||||
access_key_id: YOURACCESSKEY
|
||||
secret_access_key: YOURSECRETACCESSKEY
|
||||
server: 127.0.0.1
|
||||
port: 3002
|
16
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/db_file_test.rb
vendored
Normal file
16
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/db_file_test.rb
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
|
||||
|
||||
class DbFileTest < Test::Unit::TestCase
|
||||
include BaseAttachmentTests
|
||||
attachment_model Attachment
|
||||
|
||||
def test_should_call_after_attachment_saved(klass = Attachment)
|
||||
attachment_model.saves = 0
|
||||
assert_created do
|
||||
upload_file :filename => '/files/rails.png'
|
||||
end
|
||||
assert_equal 1, attachment_model.saves
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_call_after_attachment_saved, Attachment
|
||||
end
|
80
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/file_system_test.rb
vendored
Normal file
80
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/file_system_test.rb
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
|
||||
|
||||
class FileSystemTest < Test::Unit::TestCase
|
||||
include BaseAttachmentTests
|
||||
attachment_model FileAttachment
|
||||
|
||||
def test_filesystem_size_for_file_attachment(klass = FileAttachment)
|
||||
attachment_model klass
|
||||
assert_created 1 do
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_equal attachment.size, File.open(attachment.full_filename).stat.size
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_filesystem_size_for_file_attachment, FileAttachment
|
||||
|
||||
def test_should_not_overwrite_file_attachment(klass = FileAttachment)
|
||||
attachment_model klass
|
||||
assert_created 2 do
|
||||
real = upload_file :filename => '/files/rails.png'
|
||||
assert_valid real
|
||||
assert !real.new_record?, real.errors.full_messages.join("\n")
|
||||
assert !real.size.zero?
|
||||
|
||||
fake = upload_file :filename => '/files/fake/rails.png'
|
||||
assert_valid fake
|
||||
assert !fake.size.zero?
|
||||
|
||||
assert_not_equal File.open(real.full_filename).stat.size, File.open(fake.full_filename).stat.size
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_not_overwrite_file_attachment, FileAttachment
|
||||
|
||||
def test_should_store_file_attachment_in_filesystem(klass = FileAttachment)
|
||||
attachment_model klass
|
||||
attachment = nil
|
||||
assert_created do
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_valid attachment
|
||||
assert File.exists?(attachment.full_filename), "#{attachment.full_filename} does not exist"
|
||||
end
|
||||
attachment
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_store_file_attachment_in_filesystem, FileAttachment
|
||||
|
||||
def test_should_delete_old_file_when_updating(klass = FileAttachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
old_filename = attachment.full_filename
|
||||
assert_not_created do
|
||||
use_temp_file 'files/rails.png' do |file|
|
||||
attachment.filename = 'rails2.png'
|
||||
attachment.temp_path = File.join(fixture_path, file)
|
||||
attachment.save!
|
||||
assert File.exists?(attachment.full_filename), "#{attachment.full_filename} does not exist"
|
||||
assert !File.exists?(old_filename), "#{old_filename} still exists"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_delete_old_file_when_updating, FileAttachment
|
||||
|
||||
def test_should_delete_old_file_when_renaming(klass = FileAttachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
old_filename = attachment.full_filename
|
||||
assert_not_created do
|
||||
attachment.filename = 'rails2.png'
|
||||
attachment.save
|
||||
assert File.exists?(attachment.full_filename), "#{attachment.full_filename} does not exist"
|
||||
assert !File.exists?(old_filename), "#{old_filename} still exists"
|
||||
assert !attachment.reload.size.zero?
|
||||
assert_equal 'rails2.png', attachment.filename
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_delete_old_file_when_renaming, FileAttachment
|
||||
end
|
103
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/remote/s3_test.rb
vendored
Normal file
103
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/backends/remote/s3_test.rb
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'test_helper'))
|
||||
require 'net/http'
|
||||
|
||||
class S3Test < Test::Unit::TestCase
|
||||
if File.exist?(File.join(File.dirname(__FILE__), '../../amazon_s3.yml'))
|
||||
include BaseAttachmentTests
|
||||
attachment_model S3Attachment
|
||||
|
||||
def test_should_create_correct_bucket_name(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_equal attachment.s3_config[:bucket_name], attachment.bucket_name
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_create_correct_bucket_name, S3Attachment
|
||||
|
||||
def test_should_create_default_path_prefix(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_equal File.join(attachment_model.table_name, attachment.attachment_path_id), attachment.base_path
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_create_default_path_prefix, S3Attachment
|
||||
|
||||
def test_should_create_custom_path_prefix(klass = S3WithPathPrefixAttachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_equal File.join('some/custom/path/prefix', attachment.attachment_path_id), attachment.base_path
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_create_custom_path_prefix, S3WithPathPrefixAttachment
|
||||
|
||||
def test_should_create_valid_url(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_equal "#{s3_protocol}#{s3_hostname}#{s3_port_string}/#{attachment.bucket_name}/#{attachment.full_filename}", attachment.s3_url
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_create_valid_url, S3Attachment
|
||||
|
||||
def test_should_create_authenticated_url(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_match /^http.+AWSAccessKeyId.+Expires.+Signature.+/, attachment.authenticated_s3_url(:use_ssl => true)
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_create_authenticated_url, S3Attachment
|
||||
|
||||
def test_should_save_attachment(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
assert_created do
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_valid attachment
|
||||
assert attachment.image?
|
||||
assert !attachment.size.zero?
|
||||
assert_kind_of Net::HTTPOK, http_response_for(attachment.s3_url)
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_save_attachment, S3Attachment
|
||||
|
||||
def test_should_delete_attachment_from_s3_when_attachment_record_destroyed(klass = S3Attachment)
|
||||
attachment_model klass
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
|
||||
urls = [attachment.s3_url] + attachment.thumbnails.collect(&:s3_url)
|
||||
|
||||
urls.each {|url| assert_kind_of Net::HTTPOK, http_response_for(url) }
|
||||
attachment.destroy
|
||||
urls.each do |url|
|
||||
begin
|
||||
http_response_for(url)
|
||||
rescue Net::HTTPForbidden, Net::HTTPNotFound
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test_against_subclass :test_should_delete_attachment_from_s3_when_attachment_record_destroyed, S3Attachment
|
||||
|
||||
protected
|
||||
def http_response_for(url)
|
||||
url = URI.parse(url)
|
||||
Net::HTTP.start(url.host, url.port) {|http| http.request_head(url.path) }
|
||||
end
|
||||
|
||||
def s3_protocol
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.protocol
|
||||
end
|
||||
|
||||
def s3_hostname
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.hostname
|
||||
end
|
||||
|
||||
def s3_port_string
|
||||
Technoweenie::AttachmentFu::Backends::S3Backend.port_string
|
||||
end
|
||||
else
|
||||
def test_flunk_s3
|
||||
puts "s3 config file not loaded, tests not running"
|
||||
end
|
||||
end
|
||||
end
|
57
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/base_attachment_tests.rb
vendored
Normal file
57
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/base_attachment_tests.rb
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
module BaseAttachmentTests
|
||||
def test_should_create_file_from_uploaded_file
|
||||
assert_created do
|
||||
attachment = upload_file :filename => '/files/foo.txt'
|
||||
assert_valid attachment
|
||||
assert !attachment.db_file.new_record? if attachment.respond_to?(:db_file)
|
||||
assert attachment.image?
|
||||
assert !attachment.size.zero?
|
||||
#assert_equal 3, attachment.size
|
||||
assert_nil attachment.width
|
||||
assert_nil attachment.height
|
||||
end
|
||||
end
|
||||
|
||||
def test_reassign_attribute_data
|
||||
assert_created 1 do
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_valid attachment
|
||||
assert attachment.size > 0, "no data was set"
|
||||
|
||||
attachment.temp_data = 'wtf'
|
||||
assert attachment.save_attachment?
|
||||
attachment.save!
|
||||
|
||||
assert_equal 'wtf', attachment_model.find(attachment.id).send(:current_data)
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_reassign_attribute_data_on_nil
|
||||
assert_created 1 do
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_valid attachment
|
||||
assert attachment.size > 0, "no data was set"
|
||||
|
||||
attachment.temp_data = nil
|
||||
assert !attachment.save_attachment?
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_overwrite_old_contents_when_updating
|
||||
attachment = upload_file :filename => '/files/rails.png'
|
||||
assert_not_created do # no new db_file records
|
||||
use_temp_file 'files/rails.png' do |file|
|
||||
attachment.filename = 'rails2.png'
|
||||
attachment.temp_path = File.join(fixture_path, file)
|
||||
attachment.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_save_without_updating_file
|
||||
attachment = upload_file :filename => '/files/foo.txt'
|
||||
assert_valid attachment
|
||||
assert !attachment.save_attachment?
|
||||
assert_nothing_raised { attachment.save! }
|
||||
end
|
||||
end
|
64
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/basic_test.rb
vendored
Normal file
64
P5B/ruby/3dossmanno_annuaire/vendor/plugins/attachment_fu/test/basic_test.rb
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
||||
|
||||
class BasicTest < Test::Unit::TestCase
|
||||
def test_should_set_default_min_size
|
||||
assert_equal 1, Attachment.attachment_options[:min_size]
|
||||
end
|
||||
|
||||
def test_should_set_default_max_size
|
||||
assert_equal 1.megabyte, Attachment.attachment_options[:max_size]
|
||||
end
|
||||
|
||||
def test_should_set_default_size
|
||||
assert_equal (1..1.megabyte), Attachment.attachment_options[:size]
|
||||
end
|
||||
|
||||
def test_should_set_default_thumbnails_option
|
||||
assert_equal Hash.new, Attachment.attachment_options[:thumbnails]
|
||||
end
|
||||
|
||||
def test_should_set_default_thumbnail_class
|
||||
assert_equal Attachment, Attachment.attachment_options[:thumbnail_class]
|
||||
end
|
||||
|
||||
def test_should_normalize_content_types_to_array
|
||||
assert_equal %w(pdf), PdfAttachment.attachment_options[:content_type]
|
||||
assert_equal %w(pdf doc txt), DocAttachment.attachment_options[:content_type]
|
||||
assert_equal ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'], ImageAttachment.attachment_options[:content_type]
|
||||
assert_equal ['pdf', 'image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'], ImageOrPdfAttachment.attachment_options[:content_type]
|
||||
end
|
||||
|
||||
def test_should_sanitize_content_type
|
||||
@attachment = Attachment.new :content_type => ' foo '
|
||||
assert_equal 'foo', @attachment.content_type
|
||||
end
|
||||
|
||||
def test_should_sanitize_filenames
|
||||
@attachment = Attachment.new :filename => 'blah/foo.bar'
|
||||
assert_equal 'foo.bar', @attachment.filename
|
||||
|
||||
@attachment.filename = 'blah\\foo.bar'
|
||||
assert_equal 'foo.bar', @attachment.filename
|
||||
|
||||
@attachment.filename = 'f o!O-.bar'
|
||||
assert_equal 'f_o_O-.bar', @attachment.filename
|
||||
end
|
||||
|
||||
def test_should_convert_thumbnail_name
|
||||
@attachment = FileAttachment.new :filename => 'foo.bar'
|
||||
assert_equal 'foo.bar', @attachment.thumbnail_name_for(nil)
|
||||
assert_equal 'foo.bar', @attachment.thumbnail_name_for('')
|
||||
assert_equal 'foo_blah.bar', @attachment.thumbnail_name_for(:blah)
|
||||
assert_equal 'foo_blah.blah.bar', @attachment.thumbnail_name_for('blah.blah')
|
||||
|
||||
@attachment.filename = 'foo.bar.baz'
|
||||
assert_equal 'foo.bar_blah.baz', @attachment.thumbnail_name_for(:blah)
|
||||
end
|
||||
|
||||
def test_should_require_valid_thumbnails_option
|
||||
klass = Class.new(ActiveRecord::Base)
|
||||
assert_raise ArgumentError do
|
||||
klass.has_attachment :thumbnails => []
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user