Compare commits

...

89 Commits

Author SHA1 Message Date
Wojciech Todryk b63cc2bd14 deps update 2013-02-12 20:06:43 +01:00
Wojciech Todryk 2a6e2a3d85 migrate script fo jquery 2013-02-07 19:48:25 +01:00
Wojciech Todryk 99fc560572 docs updated 2013-02-02 15:10:17 +01:00
Wojciech Todryk 5eb7874d79 favicon size 2013-02-02 15:04:27 +01:00
Wojciech Todryk 298eb0a023 favicon 2013-02-02 14:54:09 +01:00
Wojciech Todryk 362ec3bcbd bump gem versions 2013-02-02 13:58:43 +01:00
Wojciech Todryk 3ddedca1d3 cc & bcc adresses handle fix 2012-07-10 22:10:06 +02:00
Wojciech Todryk 11cd31cdee fixes in draft folder 2012-07-08 14:18:19 +02:00
Wojciech Todryk 0a2bbed2a0 fixes 2012-06-18 21:34:30 +02:00
Wojciech Todryk 0c3e911553 links fix 2012-05-28 21:17:24 +02:00
Wojciech Todryk ccd3636021 Update README.markdown 2012-04-27 23:44:03 +03:00
Wojciech Todryk 193f2fc524 info update 2012-04-27 22:39:08 +02:00
Wojciech Todryk a45506a642 gimnasium status added 2012-04-15 19:55:45 +02:00
Wojciech Todryk 38a7b015da Merge branch 'master' of github.com:musashimm/mailr 2012-04-15 19:54:44 +02:00
Wojciech Todryk a5b6df393c fixes 2012-04-15 19:51:58 +02:00
Wojciech Todryk 2517df1814 Update README.markdown 2012-04-07 00:06:32 +03:00
Wojciech Todryk 05dfa004c8 Update README.markdown 2012-04-07 00:05:05 +03:00
Wojciech Todryk 77c8d3e7d7 devel 2012-03-30 19:31:23 +02:00
Wojciech Todryk b8eddc8e48 devel cont 2012-03-26 20:22:01 +02:00
Wojciech Todryk 0872730d8d view cleanup 2012-03-24 18:09:31 +01:00
Wojciech Todryk 0daf487816 dev cont 2012-03-24 13:23:34 +01:00
Wojciech Todryk 7de07db812 cont 2012-03-10 21:03:56 +01:00
Wojciech Todryk cacd9575d0 rails 3.2.2 added tweeter bootstrap 2012-03-10 18:08:39 +01:00
Wojciech Todryk 74c23fa0d1 start to switch to rails 3.2.2 2012-03-03 18:53:39 +01:00
Wojciech Todryk 244942a78f switching to rails 3.2 2012-03-03 17:37:37 +01:00
Wojciech Todryk 9be6f493a3 fix in selecting messages from current folder 2012-03-02 20:30:11 +01:00
Wojciech Todryk f0dcdc3985 links added and other stuff 2012-02-04 00:45:28 +01:00
Wojciech Todryk eb455e704a new calendar view 2011-10-15 18:31:20 +02:00
Wojciech Todryk b74d28793d contacts_external_path fix 2011-10-04 20:45:44 +02:00
Wojciech Todryk eac30875f3 messages controller fix 2011-10-01 08:30:45 +02:00
Wojciech Todryk e96e11db96 identity,servers view 2011-09-30 01:14:41 +02:00
Wojciech Todryk ef0b894ad8 identity,servers view 2011-09-29 21:16:40 +02:00
Wojciech Todryk 9bb5f3a20f servers view 2011-09-24 22:13:45 +02:00
Wojciech Todryk 9e31ca239d TODO, CHANGES added 2011-09-23 21:37:23 +02:00
Wojciech Todryk 17a85a5916 calendar as separate gem, bluecloth integrated 2011-09-23 21:35:12 +02:00
Wojciech Todryk 66cba6bbc7 view fixes 2011-09-18 21:18:52 +02:00
Wojciech Todryk 1663cd60b5 version info 2011-09-16 22:21:57 +02:00
Wojciech Todryk 6fa55112da file_select fix 2011-09-16 22:19:22 +02:00
Wojciech Todryk bb3289e0d4 fixes for contacts import;flash fixes 2011-09-16 22:08:30 +02:00
Wojciech Todryk 2afeebad53 devel 2011-09-16 19:44:29 +02:00
Wojciech Todryk b4bc16ed14 some fixes 2011-09-10 20:34:12 +02:00
Wojciech Todryk a70bb1f515 some fixes 2011-09-10 20:33:34 +02:00
Wojciech Todryk 639d223629 some fixes 2011-09-10 20:31:55 +02:00
Wojciech Todryk a1352b9402 some fixes 2011-09-10 13:49:42 +02:00
Wojciech Todryk f3a3c6bded image view of attachmnts 2011-09-10 11:23:10 +02:00
Wojciech Todryk da8d0e4389 devel 2011-09-09 23:44:51 +02:00
Wojciech Todryk 7879dd49a2 devel 2011-09-09 22:10:25 +02:00
Wojciech Todryk 15ff3132f0 fix in url for edit 2011-09-05 20:42:45 +02:00
Wojciech Todryk 8868a84123 devel 2011-09-05 19:08:22 +02:00
Wojciech Todryk 910a6f3bc0 favicon 2011-09-03 16:25:19 +02:00
Wojciech Todryk fdd3019f55 devel 2011-09-03 13:07:40 +02:00
Wojciech Todryk 961e9d6f4b devel 2011-08-28 21:52:17 +02:00
Wojciech Todryk cead4d9d74 html view with cids 2011-08-28 18:01:37 +02:00
Wojciech Todryk 19aef7a5ed html view almost done 2011-08-27 22:00:06 +02:00
Wojciech Todryk 549238d734 20110826 2011-08-27 00:10:45 +02:00
Wojciech Todryk 52b4c63ddb devel 2011-08-26 22:59:43 +02:00
Wojciech Todryk 649dc660df Merge branch 'from_scratch' of github.com:musashimm/mailr into from_scratch
Conflicts:
	Gemfile.lock
	app/controllers/messages_controller.rb
	config/defaults.yml
	config/routes.rb
	themes/olive/stylesheets/style.css
	themes/olive/views/messages/_msg_ops.html.erb
	themes/olive/views/messages/show.html.erb
2011-08-24 19:28:51 +02:00
Wojciech Todryk b9561278d0 devel 2011-08-24 19:20:13 +02:00
Wojciech Todryk 751fae97f3 view work 2011-08-18 19:57:12 +02:00
Wojciech Todryk 081bf95ce7 fixes 2011-08-16 23:37:13 +02:00
Wojciech Todryk c995062544 gemfile modified 2011-08-16 21:18:57 +02:00
Wojciech Todryk 217c7a2288 contacts done 2011-08-16 20:05:58 +02:00
Wojciech Todryk 68b925ea5e new stuff 2011-08-02 23:12:17 +02:00
Wojciech Todryk 65472c55cc folders devel ended 2011-07-31 22:45:29 +02:00
Wojciech Todryk 565654d2a5 inbox name changed 2011-07-29 20:21:17 +02:00
Wojciech Todryk 7967e6d5c8 a lot of stuff added 2011-07-29 20:05:47 +02:00
Wojciech Todryk 37f548ce46 devel 2011-07-27 20:34:30 +02:00
Wojciech Todryk f81c1d69c5 internal controller added 2011-07-24 22:22:13 +02:00
Wojciech Todryk 86e1817e29 prefs model added 2011-07-24 15:56:47 +02:00
Wojciech Todryk da03add37b setup form finished 2011-07-23 21:55:26 +02:00
Wojciech Todryk 99fcbbb8d0 added unknown page 2011-07-22 22:57:36 +02:00
Wojciech Todryk 5ef1414979 change core controller to user 2011-07-22 21:58:59 +02:00
Wojciech Todryk 08dc7ecdd3 form scratch 2011-07-21 20:20:15 +02:00
Wojciech Todryk a7dd8c90c9 devel 2011-07-21 11:14:07 +02:00
Wojciech Todryk abfba98334 new olive theme 2011-06-26 21:37:50 +02:00
Wojciech Todryk aca4f8a3c6 logo project 2011-06-26 13:05:56 +02:00
Wojciech Todryk 921de13cc0 themes_for_rails integrated with original theme 2011-06-25 18:19:07 +02:00
Wojciech Todryk 573356a3a4 README fixed 2011-06-25 00:16:10 +02:00
Wojciech Todryk a43b1bed3c README fixed 2011-06-25 00:15:19 +02:00
Wojciech Todryk 12726a2c3c README fixed 2011-06-25 00:12:21 +02:00
Wojciech Todryk 1ea0781ddb README fixed 2011-06-25 00:05:54 +02:00
Wojciech Todryk 3b553b5e15 Edited README.markdown via GitHub 2011-06-24 14:46:11 -07:00
Wojciech Todryk c96e351ee8 README fixed 2011-06-24 23:53:55 +02:00
Wojciech Todryk e40a859b7d works but need some cleanup 2011-06-24 23:48:08 +02:00
Wojciech Todryk 0ec83db287 devel 2011-06-19 10:55:37 +02:00
Wojciech Todryk cf56c7555a some fixes to locale 2011-06-18 19:26:24 +02:00
Wojciech Todryk 860fc1479e return to rails 2.3.3 2011-06-18 19:24:50 +02:00
Wojciech Todryk 7145c6486d some fix for locale,new polish locale 2011-06-18 13:27:44 +02:00
Wojciech Todryk 83088930fe some changes for rails 2.3.11 2011-06-18 11:07:09 +02:00
873 changed files with 6515 additions and 42110 deletions

22
.gitignore vendored Normal file → Executable file
View File

@ -1,7 +1,15 @@
log
config/database.yml
.*.sw?
config/site.rb
tmp
mail_temp
config/site.rb
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile ~/.gitignore_global
# Ignore bundler config
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp

7
AUTHORS.markdown Executable file
View File

@ -0,0 +1,7 @@
## Authors
* Luben Manolov <luben.manolov@gmail.com>
* Nick Penkov <nick.penkov@gmail.com>
* Eugene Korbut
* Emilio Blanco
* Wojciech Todryk <wojciech(at)todryk(dot).pl>

718
CHANGELOG
View File

@ -1,718 +0,0 @@
*0.14.2 (RC3)* (October 26th, 2005)
* Constants set in the development/test/production environment file are set in Object
* Scaffold generator pays attention to the controller name. #2562 [self@mattmower.com]
* Include tasks from vendor/plugins/*/tasks in the Rakefile #2545 [Rick Olson]
*0.14.1 (RC2)* (October 19th, 2005)
* Don't clean RAILS_ROOT on windows
* Remove trailing '/' from RAILS_ROOT [Nicholas Seckar]
* Upgraded to Active Record 1.12.1 and Action Pack 1.10.1
*0.14.0 (RC1)* (October 16th, 2005)
* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Luetke]
* Fix rake dev and related commands [Nicholas Seckar]
* The rails command tries to deduce your MySQL socket by running `mysql_config
--socket`. If it fails, default to /path/to/your/mysql.sock
* Made the rails command use the application name for database names in the tailored database.yml file. Example: "rails ~/projects/blog" will use "blog_development" instead of "rails_development". [Florian Weber]
* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system)
* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Luetke]
* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. [Nicholas Seckar]
* Made the instantiated/transactional fixtures settings be controlled through Rails::Initializer. Transactional and non-instantiated fixtures are default from now on. [Florian Weber]
* Support using different database adapters for development and test with ActiveRecord::Base.schema_format = :ruby [Sam Stephenson]
* Make webrick work with session(:off)
* Add --version, -v option to the Rails command. Closes #1840. [stancell]
* Update Prototype to V1.4.0_pre11, script.aculo.us to V1.5_rc3 [2504] and fix the rails generator to include the new .js files [Thomas Fuchs]
* Make the generator skip a file if it already exists and is identical to the new file.
* Add experimental plugin support #2335
* Made Rakefile aware of new .js files in script.aculo.us [Thomas Fuchs]
* Make table_name and controller_name in generators honor AR::Base.pluralize_table_names. #1216 #2213 [kazuhiko@fdiary.net]
* Clearly label functional and unit tests in rake stats output. #2297 [lasse.koskela@gmail.com]
* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 [Chad Fowler]
* Added prevention of duplicate migrations from the generator #2240 [fbeausoleil@ftml.net]
* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file).
* Reformed all the config/environments/* files to conform to the new Rails::Configuration approach. Fully backwards compatible.
* Added create_sessions_table, drop_sessions_table, and purge_sessions_table as rake tasks for databases that supports migrations (MySQL, PostgreSQL, SQLite) to get a table for use with CGI::Session::ActiveRecordStore
* Added dump of schema version to the db_structure_dump task for databases that support migrations #1835 [Rick Olson]
* Fixed script/profiler for Ruby 1.8.2 #1863 [Rick Olson]
* Fixed clone_structure_to_test task for SQLite #1864 [jon@burningbush.us]
* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 [ask@develooper.com]
* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [kevin.clark@gmail.com]
* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 [wejn@box.cz]
* Make the default stats task extendable by modifying the STATS_DIRECTORIES constant
* Allow the selected environment to define RAILS_DEFAULT_LOGGER, and have Rails::Initializer use it if it exists.
* Moved all the shared tasks from Rakefile into Rails, so that the Rakefile is empty and doesn't require updating.
* Added Rails::Initializer and Rails::Configuration to abstract all of the common setup out of config/environment.rb (uses config/boot.rb to bootstrap the initializer and paths)
* Fixed the scaffold generator to fail right away if the database isn't accessible instead of in mid-air #1169 [Chad Fowler]
* Corrected project-local generator location in scripts.rb #2010 [Michael Schuerig]
* Don't require the environment just to clear the logs #2093 [Scott Barron]
* Make the default rakefile read *.rake files from config/tasks (for easy extension of the rakefile by e.g. generators)
* Only load breakpoint in development mode and when BREAKPOINT_SERVER_PORT is defined.
* Allow the --toggle-spin switch on process/reaper to be negated
* Replace render_partial with render :partial in scaffold generator [Nicholas Seckar]
* Added -w flag to ps in process/reaper #1934 [Scott Barron]
* Allow ERb in the database.yml file (just like with fixtures), so you can pull out the database configuration in environment variables #1822 [Duane Johnson]
* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina]
* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [michael@schubert]
* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down).
*0.13.1* (11 July, 2005)
* Look for app-specific generators in RAILS_ROOT/generators rather than the clunky old RAILS_ROOT/script/generators. Nobody really uses this feature except for the unit tests, so it's a negligible-impact change. If you want to work with third-party generators, drop them in ~/.rails/generators or simply install gems.
* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
* Added support for SQL Server in the database rake tasks #1652 [ken.barker@gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay@gmail.com]
* Improved documentation for environment config files #1625 [court3nay@gmail.com]
*0.13.0* (6 July, 2005)
* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged)
* Added migration generator: ./script/generate migration add_system_settings
* Added "migrate" as rake task to execute all the pending migrations from db/migrate
* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina]
* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows.
* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [skaes@web.de]
* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [michael@schubert.cx]
* ActionMailer::Base subclasses are reloaded with the other rails components #1262
* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false)
* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]
* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar]
* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
* Added -e/--environment option to script/runner #1408 [fbeausoleil@ftml.net]
* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367
* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis]
* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck]
* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck]
* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper]
* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper]
*0.12.1* (20th April, 2005)
* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1
*0.12.0* (19th April, 2005)
* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails@cogentdude.com]
* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples:
# runs the one statement 10 times
script/benchmarker 10 'Person.expensive_method(10)'
# pits the two statements against each other with 50 runs each
script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)'
* Added script/profiler to easily profile a single statement from within the environment. Examples:
script/profiler 'Person.expensive_method(10)'
script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times
* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson]
* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k@v2studio.com]
* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson@gmail.com]
* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
*0.11.1* (27th March, 2005)
* Fixed the dispatch.fcgi use of a logger
* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3
*0.11.0* (22th March, 2005)
* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar]
* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar]
* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Luetke]
* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
./script/runner 'ReminderService.deliver'
./script/runner 'Mailer.receive(STDIN.read)'
This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code.
* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave@cherryville.org]
* Fixed the Rakefile's interaction with postgresql to:
1. Use PGPASSWORD and PGHOST in the environment to fix prompting for
passwords when connecting to a remote db and local socket connections.
2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik]
3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson]
4. Use the port if available #875 [madrobby]
* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2
*0.10.1* (7th March, 2005)
* Fixed rake stats to ignore editor backup files like model.rb~ #791 [skanthak]
* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Luetke]
* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson]
* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm@galacticasoftware.com]
* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt]
* Added app/apis and components to code statistics report #729 [Scott Barron]
* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar]
* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN]
* Fixed -h/--help for generate and destroy generators #331
* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log)
* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis@comcard.de]
* Fixed test/mocks/testing to the correct test/mocks/test #740
* Added early failure if the Ruby version isn't 1.8.2 or above #735
* Removed the obsolete -i/--index option from the WEBrick servlet #743
* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1
*0.10.0* (24th February, 2005)
* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel]
* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
* Upgraded to breakpoint 92 which fixes:
* overload IRB.parse_opts(), fixes #443
=> breakpoints in tests work even when running them via rake
* untaint handlers, might fix an issue discussed on the Rails ML
* added verbose mode to breakpoint_client
* less noise caused by breakpoint_client by default
* ignored TerminateLineInput exception in signal handler
=> quiet exit on Ctrl-C
* Added support for independent components residing in /components. Example:
Controller: components/list/items_controller.rb
(holds a List::ItemsController class with uses_component_template_root called)
Model : components/list/item.rb
(namespace is still shared, so an Item model in app/models will take precedence)
Views : components/list/items/show.rhtml
* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper]
* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper]
* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper]
* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper]
* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik]
* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper]
* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt]
* Added Active Support as an independent utility and standard library extension bundle
* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0
*0.9.5* (January 25th, 2005)
* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require_dependency to do it.
* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross]
* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik]
* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt@kurowski.net]
* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter
* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload
* Fixed that generate scaffold would produce bad functional tests
* Fixed that FCGI can also display SyntaxErrors
* Upgraded to Active Record 1.6.0, Action Pack 1.4.0
*0.9.4.1* (January 18th, 2005)
* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina]
* Fixed binding of caller #496 [Alexey]
* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1
*0.9.4* (January 17th, 2005)
* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Luetke]
* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Luetke]
* Added rewrite rules to deal with caching to public/.htaccess
* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
RAILS_ENV instead of just forcing development #424 [Tobias Luetke]
* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [octopod]
* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes
* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6
*0.9.3* (January 4th, 2005)
* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416
* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra]
* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries
for functional testing.
* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date.
It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross]
* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that
the environment options needs to change from:
Before in development.rb:
ActionController::Base.reload_dependencies = true  
ActiveRecord::Base.reload_associations     = true
Now in development.rb:
Dependencies.mechanism = :load
Before in production.rb and test.rb:
ActionController::Base.reload_dependencies = false
ActiveRecord::Base.reload_associations     = false
Now in production.rb and test.rb:
Dependencies.mechanism = :require
* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351
* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson]
* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0
*0.9.2*
* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev]
* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron]
* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record.
* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr]
* Added support for a -h/--help parameter in the generator #331 [Ulysses]
* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin]
* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0
*0.9.1*
* Upgraded to Action Pack 1.0.1 for important bug fix
* Updated gem dependencies
*0.9.0*
* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb
* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible
for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb
* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to
see help.
* Added breakpoint support 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
* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you
can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server):
SetEnv RAILS_ENV production
...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this:
ruby public/dispatcher.servlet -e production
* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that.
* Added a start_server in the root of the Rails application to make it even easier to get started
* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods
* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe)
* Added support for controllers in directories. So you can have:
app/controllers/account_controller.rb # URL: /account/
app/controllers/admin/account_controller.rb # URL: /admin/account/
NOTE: You need to update your public/.htaccess with the new rules to pick it up
* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use
those environments for development. This is turned on by default, but can be turned off with
ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments.
* Added support for sub-directories in app/models. So now you can have something like Basecamp with:
app/models/accounting
app/models/project
app/models/participants
app/models/settings
It's poor man's namespacing, but only for file-system organization. You still require files just like before.
Nothing changes inside the files themselves.
* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper]
* Added support for mocks in testing with test/mocks
* Cleaned up the environments a bit and added global constant RAILS_ROOT
*0.8.5* (9)
* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]:
def test_complex_stuff
@david.projects << @new_project
breakpoint "Let's have a closer look at @david"
end
You need to install dev-utils yourself for this to work ("gem install dev-util").
* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper]
* Added the new helper style to both controller and helper templates [Jeremy Kemper]
* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper]
* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style
* Fixed that new_model was generating singular table/fixture names
* Upgraded to Action Mailer 0.4.0
* Upgraded to Action Pack 0.9.5
* Upgraded to Active Record 1.1.0
*0.8.0 (15)*
* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is
* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task.
* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey]
* Added user option for all postgresql tool calls in the rakefile [elvstone]
* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey]
* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based
on the Ruby runtime configuration. [Marcel Molina Jr.]
* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot]
* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector.
* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz]
* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant.
Class compilation cache can be turned on with "-c" or "--cache-classes".
* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp]
* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz]
* Added new_mailer generator to work with Action Mailer
* Included new framework: Action Mailer 0.3
* Upgraded to Action Pack 0.9.0
* Upgraded to Active Record 1.0.0
*0.7.0*
* Added an optional second argument to the new_model script that allows the programmer to specify the table name,
which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures.
[Kevin Radloff]
* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff]
* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz]
* Unified function_test_helper and unit_test_helper into test_helper
* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins]
* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation
and can generate more than one fixture at a time. Which makes it possible for assignments like:
@people, @projects, @project_access, @companies, @accounts =
create_fixtures "people", "projects", "project_access", "companies", "accounts"
* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file)
* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection)
*0.6.5*
* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile)
* The new_model generator will now use the same rules for plural wordings as Active Record
(so Category will give categories, not categorys) [Kevin Radloff]
* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp]
* Made the GEM work with Windows
* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers
* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff]
* Errors thrown by the dispatcher are now properly handled in FCGI.
* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes)
* Upgraded to Active Record 0.9.4 (a bunch of fixes)
*0.6.0*
* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used
to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all
controllers will also automatically have available.
* Added environments that can be included from any script to get the full Active Record and Action Controller
context running. This can be used by maintenance scripts or to interact with the model through IRB. Example:
require 'config/environments/production'
for account in Account.find_all
account.recalculate_interests
end
A short migration script for an account model that had it's interest calculation strategy changed.
* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick)
* Simplified the default Apache config so even remote requests are served off CGI as a default.
You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls).
This should make it easier for new comers that start on an external server.
* Added more of the necessary Apache options to .htaccess to make it easier to setup
* Upgraded to Action Pack 0.7.9 (lots of fixes)
* Upgraded to Active Record 0.9.3 (lots of fixes)
*0.5.7*
* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL
(through GET requests or otherwise)
* Added lib in root as a place to store app specific libraries
* Added lib and vendor to load_path, so anything store within can be loaded directly.
Hence lib/redcloth.rb can be loaded with require "redcloth"
* Upgraded to Action Pack 0.7.8 (lots of fixes)
* Upgraded to Active Record 0.9.2 (minor upgrade)
*0.5.6*
* Upgraded to Action Pack 0.7.7 (multipart form fix)
* Updated the generated template stubs to valid XHTML files
* Ensure that controllers generated are capitalized, so "new_controller TodoLists"
gives the same as "new_controller Todolists" and "new_controller todolists".
*0.5.5*
* Works on Windows out of the box! (Dropped symlinks)
* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross]
* Report errors about initialization to browser (instead of attempting to use uninitialized logger)
* Upgraded to Action Pack 0.7.6
* Upgraded to Active Record 0.9.1
* Added distinct 500.html instead of reusing 404.html
* Added MIT license
*0.5.0*
* First public release

47
CHANGES.markdown Executable file
View File

@ -0,0 +1,47 @@
## Changes
#### 0.9.5
* favicon added
#### 0.9.4
* bump gems
#### 0.9.3
* handle Cc & Bcc adresses fix
#### 0.9.2
* fixes in handling draft folder
#### 0.9.1
* nowrap to edit column in contacts & links
* decoded changed in mail gem
* fixes in pl locale
#### 0.9.0
* switch to Rails 3.2.x
* Tweeter Bootstrap as default theme
* many fixes
#### 0.8.6
* new calendar view
#### 0.8.5
* servers view
* identity modification
#### 0.8.4
* calendar view as separate gem
* adding bluecloth for rendering markdown text
#### 0.8.3
* export, imports of contact

47
Gemfile Executable file
View File

@ -0,0 +1,47 @@
source 'https://rubygems.org'
gem 'rails', '>= 3.2.11'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'mysql2'
gem 'json', '>= 1.7.6'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails'
gem 'coffee-rails'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer'
gem 'uglifier'
end
gem 'jquery-rails'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
# To use Jbuilder templates for JSON
# gem 'jbuilder'
# Use unicorn as the app server
# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
# To use debugger
# gem 'ruby-debug'
gem 'will_paginate'
gem "ezcrypto"
gem 'calendar_view'
gem 'bluecloth'
gem 'sass'
gem 'haml'
#gem 'twitter_bootstrap_form_for'

125
Gemfile.lock Normal file
View File

@ -0,0 +1,125 @@
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.11)
actionpack (= 3.2.11)
mail (~> 2.4.4)
actionpack (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.11)
activesupport (= 3.2.11)
builder (~> 3.0.0)
activerecord (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
activesupport (3.2.11)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
bluecloth (2.2.0)
builder (3.0.4)
calendar_view (0.0.7)
rails (>= 3.0.0)
coffee-rails (3.2.2)
coffee-script (>= 2.2.0)
railties (~> 3.2.0)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.4.0)
erubis (2.7.0)
execjs (1.4.0)
multi_json (~> 1.0)
ezcrypto (0.7.2)
haml (3.1.7)
hike (1.2.1)
i18n (0.6.1)
journey (1.0.4)
jquery-rails (2.2.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.7.6)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.19)
multi_json (1.5.0)
mysql2 (0.3.11)
polyglot (0.3.3)
rack (1.4.4)
rack-cache (1.2)
rack (>= 0.4)
rack-ssl (1.3.3)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.11)
actionmailer (= 3.2.11)
actionpack (= 3.2.11)
activerecord (= 3.2.11)
activeresource (= 3.2.11)
activesupport (= 3.2.11)
bundler (~> 1.0)
railties (= 3.2.11)
railties (3.2.11)
actionpack (= 3.2.11)
activesupport (= 3.2.11)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (10.0.3)
rdoc (3.12)
json (~> 1.4)
sass (3.2.5)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thor (0.17.0)
tilt (1.3.3)
treetop (1.4.12)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.35)
uglifier (1.3.0)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
will_paginate (3.0.4)
PLATFORMS
ruby
DEPENDENCIES
bluecloth
calendar_view
coffee-rails
ezcrypto
haml
jquery-rails
json (>= 1.7.6)
mysql2
rails (>= 3.2.11)
sass
sass-rails
uglifier
will_paginate

View File

@ -1,23 +0,0 @@
Copyright (c) 2005, 2006 VibrantPlanet Ltd.
Copyright (c) 2005, 2006 Luben Manolov
Copyright (c) 2005, 2006 Nick Penkov
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

31
README
View File

@ -1,31 +0,0 @@
Installation Guide
Requirements
* Ruby 1.8.7
* Rails 2.3.2
Installation
1. Checkout the source code
2. If you need to override some of the default constants used in the application take a look at config/default_site.rb. Then create config/site.rb that contains only the keys which you want to override. Example content of config/site.rb is:
module CDF
LOCALCONFIG = {
:imap_server => 'your.imap.server'
}
end
3. Configure SMTP settings
# initializers/smtp_settings.rb
ActionMailer::Base.smtp_settings = {
:address => "mail.example.com.py",
:port => 26,
:authentication => :plain,
:enable_starttls_auto => true,
:user_name => "emilio@example.com.py",
:password => "yourpass"
}
4 Use it

38
README.markdown Executable file
View File

@ -0,0 +1,38 @@
[![Dependency Status](https://gemnasium.com/musashimm/mailr.png)](https://gemnasium.com/musashimm/mailr)
## Introduction
_MailR_ is a IMAP mail client based on _Ruby on Rails_ platform.
**NOTE** All path and filenames are based on _Rails.root_ directory.
## Requirements
In _Rails 3_ and above all dependencies should be defined in file _Gemfile_. All needed gems can be installed using bundler.
## Installation procedure
* Checkout the source code.
* Install all dependiences. Check if proper gems (sqlite3/mysql/postgresql) are defined in _Gemfile_ and installed. Use _bundler_ for that:
```shell
bundle install
```
* Check _config/settings.yml_ for proper values. (see _config/settings.yml.example_).
* Prepare config/database.yml file (see _config/database.yml.example_).
* Migrate database (rake db:migrate)
* Start rails server if applicable
* Point your browser to application URL:
For local access: http://localhost:3000
For remote access: http://some_url/mailr
* Using browser do basic setup. If You make a mistake delete all data from DB using rake task:
```shell
rake db:clear_data
```
* Use it.
## Specific configuration
None

11
Rakefile Normal file → Executable file
View File

@ -1,10 +1,7 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require File.expand_path('../config/application', __FILE__)
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'tasks/rails'
Mailr::Application.load_tasks

21
TODO.markdown Executable file
View File

@ -0,0 +1,21 @@
## Todo
* add themes
app/controllers/folders_controller.rb:
* [ 29] [TODO] recreate local copy of folders
* [ 98] [TODO] save system folders
app/controllers/messages_controller.rb:
* [101] [FIXME] missing fields and support arrays
app/controllers/messages_ops_controller.rb:
* [261] [FIXME] edit does not support attachments
* [325] [TODO] check if email address is valid if not get address from contacts
app/models/prefs.rb:
* [ 21] [TODO] move refresh to prefs and make refresh page with messages

26
UNLICENSE.markdown Executable file
View File

@ -0,0 +1,26 @@
## License
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
app/assets/images/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
app/assets/images/rails.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,15 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

1
app/assets/javascripts/bootstrap.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,498 @@
/*!
* jQuery Migrate - v1.0.0 - 2013-01-14
* https://github.com/jquery/jquery-migrate
* Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
*/
(function( jQuery, window, undefined ) {
"use strict";
var warnedAbout = {};
// List of warnings already given; public read only
jQuery.migrateWarnings = [];
// Set to true to prevent console output; migrateWarnings still maintained
// jQuery.migrateMute = false;
// Forget any warnings we've already given; public
jQuery.migrateReset = function() {
warnedAbout = {};
jQuery.migrateWarnings.length = 0;
};
function migrateWarn( msg) {
if ( !warnedAbout[ msg ] ) {
warnedAbout[ msg ] = true;
jQuery.migrateWarnings.push( msg );
if ( window.console && console.warn && !jQuery.migrateMute ) {
console.warn( "JQMIGRATE: " + msg );
}
}
}
function migrateWarnProp( obj, prop, value, msg ) {
if ( Object.defineProperty ) {
// On ES5 browsers (non-oldIE), warn if the code tries to get prop;
// allow property to be overwritten in case some other plugin wants it
try {
Object.defineProperty( obj, prop, {
configurable: true,
enumerable: true,
get: function() {
migrateWarn( msg );
return value;
},
set: function( newValue ) {
migrateWarn( msg );
value = newValue;
}
});
return;
} catch( err ) {
// IE8 is a dope about Object.defineProperty, can't warn there
}
}
// Non-ES5 (or broken) browser; just set the property
jQuery._definePropertyBroken = true;
obj[ prop ] = value;
}
if ( document.compatMode === "BackCompat" ) {
// jQuery has never supported or tested Quirks Mode
migrateWarn( "jQuery is not compatible with Quirks Mode" );
}
var attrFn = {},
attr = jQuery.attr,
valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
function() { return null; },
valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
function() { return undefined; },
rnoType = /^(?:input|button)$/i,
rnoAttrNodeType = /^[238]$/,
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
ruseDefault = /^(?:checked|selected)$/i;
// jQuery.attrFn
migrateWarnProp( jQuery, "attrFn", attrFn, "jQuery.attrFn is deprecated" );
jQuery.attr = function( elem, name, value, pass ) {
var lowerName = name.toLowerCase(),
nType = elem && elem.nodeType;
if ( pass ) {
migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
if ( elem && !rnoAttrNodeType.test( nType ) && jQuery.isFunction( jQuery.fn[ name ] ) ) {
return jQuery( elem )[ name ]( value );
}
}
// Warn if user tries to set `type` since it breaks on IE 6/7/8
if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) ) {
migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
}
// Restore boolHook for boolean property/attribute synchronization
if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
jQuery.attrHooks[ lowerName ] = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
// Fall back to attribute presence where some booleans are not supported
var attrNode,
property = jQuery.prop( elem, name );
return property === true || typeof property !== "boolean" &&
( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
name.toLowerCase() :
undefined;
},
set: function( elem, value, name ) {
var propName;
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else {
// value is true since we know at this point it's type boolean and not false
// Set boolean attributes to the same name and set the DOM property
propName = jQuery.propFix[ name ] || name;
if ( propName in elem ) {
// Only set the IDL specifically if it already exists on the element
elem[ propName ] = true;
}
elem.setAttribute( name, name.toLowerCase() );
}
return name;
}
};
// Warn only for attributes that can remain distinct from their properties post-1.9
if ( ruseDefault.test( lowerName ) ) {
migrateWarn( "jQuery.fn.attr(" + lowerName + ") may use property instead of attribute" );
}
}
return attr.call( jQuery, elem, name, value );
};
// attrHooks: value
jQuery.attrHooks.value = {
get: function( elem, name ) {
var nodeName = ( elem.nodeName || "" ).toLowerCase();
if ( nodeName === "button" ) {
return valueAttrGet.apply( this, arguments );
}
if ( nodeName !== "input" && nodeName !== "option" ) {
migrateWarn("property-based jQuery.fn.attr('value') is deprecated");
}
return name in elem ?
elem.value :
null;
},
set: function( elem, value ) {
var nodeName = ( elem.nodeName || "" ).toLowerCase();
if ( nodeName === "button" ) {
return valueAttrSet.apply( this, arguments );
}
if ( nodeName !== "input" && nodeName !== "option" ) {
migrateWarn("property-based jQuery.fn.attr('value', val) is deprecated");
}
// Does not return so that setAttribute is also used
elem.value = value;
}
};
var matched, browser,
oldInit = jQuery.fn.init,
// Note this does NOT include the # XSS fix from 1.7!
rquickExpr = /^(?:.*(<[\w\W]+>)[^>]*|#([\w\-]*))$/;
// $(html) "looks like html" rule change
jQuery.fn.init = function( selector, context, rootjQuery ) {
var match;
if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
(match = rquickExpr.exec( selector )) && match[1] ) {
// This is an HTML string according to the "old" rules; is it still?
if ( selector.charAt( 0 ) !== "<" ) {
migrateWarn("$(html) HTML strings must start with '<' character");
}
// Now process using loose rules; let pre-1.8 play too
if ( context && context.context ) {
// jQuery object as context; parseHTML expects a DOM object
context = context.context;
}
if ( jQuery.parseHTML ) {
return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ),
context, rootjQuery );
}
}
return oldInit.apply( this, arguments );
};
jQuery.fn.init.prototype = jQuery.fn;
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
// Warn if the code tries to get jQuery.browser
migrateWarnProp( jQuery, "browser", browser, "jQuery.browser is deprecated" );
jQuery.sub = function() {
function jQuerySub( selector, context ) {
return new jQuerySub.fn.init( selector, context );
}
jQuery.extend( true, jQuerySub, this );
jQuerySub.superclass = this;
jQuerySub.fn = jQuerySub.prototype = this();
jQuerySub.fn.constructor = jQuerySub;
jQuerySub.sub = this.sub;
jQuerySub.fn.init = function init( selector, context ) {
if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
context = jQuerySub( context );
}
return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
};
jQuerySub.fn.init.prototype = jQuerySub.fn;
var rootjQuerySub = jQuerySub(document);
migrateWarn( "jQuery.sub() is deprecated" );
return jQuerySub;
};
var oldFnData = jQuery.fn.data;
jQuery.fn.data = function( name ) {
var ret, evt,
elem = this[0];
// Handles 1.7 which has this behavior and 1.8 which doesn't
if ( elem && name === "events" && arguments.length === 1 ) {
ret = jQuery.data( elem, name );
evt = jQuery._data( elem, name );
if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
migrateWarn("Use of jQuery.fn.data('events') is deprecated");
return evt;
}
}
return oldFnData.apply( this, arguments );
};
var rscriptType = /\/(java|ecma)script/i,
oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack,
oldFragment = jQuery.buildFragment;
jQuery.fn.andSelf = function() {
migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
return oldSelf.apply( this, arguments );
};
// Since jQuery.clean is used internally on older versions, we only shim if it's missing
if ( !jQuery.clean ) {
jQuery.clean = function( elems, context, fragment, scripts ) {
// Set context per 1.8 logic
context = context || document;
context = !context.nodeType && context[0] || context;
context = context.ownerDocument || context;
migrateWarn("jQuery.clean() is deprecated");
var i, elem, handleScript, jsTags,
ret = [];
jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );
// Complex logic lifted directly from jQuery 1.8
if ( fragment ) {
// Special handling of each script element
handleScript = function( elem ) {
// Check if we consider it executable
if ( !elem.type || rscriptType.test( elem.type ) ) {
// Detach the script and store it in the scripts array (if provided) or the fragment
// Return truthy to indicate that it has been handled
return scripts ?
scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
fragment.appendChild( elem );
}
};
for ( i = 0; (elem = ret[i]) != null; i++ ) {
// Check if we're done after handling an executable script
if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
// Append to fragment and handle embedded scripts
fragment.appendChild( elem );
if ( typeof elem.getElementsByTagName !== "undefined" ) {
// handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
// Splice the scripts into ret after their former ancestor and advance our index beyond them
ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
i += jsTags.length;
}
}
}
}
return ret;
};
}
jQuery.buildFragment = function( elems, context, scripts, selection ) {
var ret,
warning = "jQuery.buildFragment() is deprecated";
// Set context per 1.8 logic
context = context || document;
context = !context.nodeType && context[0] || context;
context = context.ownerDocument || context;
try {
ret = oldFragment.call( jQuery, elems, context, scripts, selection );
// jQuery < 1.8 required arrayish context; jQuery 1.9 fails on it
} catch( x ) {
ret = oldFragment.call( jQuery, elems, context.nodeType ? [ context ] : context[ 0 ], scripts, selection );
// Success from tweaking context means buildFragment was called by the user
migrateWarn( warning );
}
// jQuery < 1.9 returned an object instead of the fragment itself
if ( !ret.fragment ) {
migrateWarnProp( ret, "fragment", ret, warning );
migrateWarnProp( ret, "cacheable", false, warning );
}
return ret;
};
var eventAdd = jQuery.event.add,
eventRemove = jQuery.event.remove,
eventTrigger = jQuery.event.trigger,
oldToggle = jQuery.fn.toggle,
oldLive = jQuery.fn.live,
oldDie = jQuery.fn.die,
ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
hoverHack = function( events ) {
if ( typeof( events ) != "string" || jQuery.event.special.hover ) {
return events;
}
if ( rhoverHack.test( events ) ) {
migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
}
return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
};
// Event props removed in 1.9, put them back if needed; no practical way to warn them
if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
}
// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );
// Support for 'hover' pseudo-event and ajax event warnings
jQuery.event.add = function( elem, types, handler, data, selector ){
if ( elem !== document && rajaxEvent.test( types ) ) {
migrateWarn( "AJAX events should be attached to document: " + types );
}
eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
};
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
};
jQuery.fn.error = function() {
var args = Array.prototype.slice.call( arguments, 0);
migrateWarn("jQuery.fn.error() is deprecated");
args.splice( 0, 0, "error" );
if ( arguments.length ) {
return this.bind.apply( this, args );
}
// error event should not bubble to window, although it does pre-1.7
this.triggerHandler.apply( this, args );
return this;
};
jQuery.fn.toggle = function( fn, fn2 ) {
// Don't mess with animation or css toggles
if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
return oldToggle.apply( this, arguments );
}
migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");
// Save reference to arguments for access in closure
var args = arguments,
guid = fn.guid || jQuery.guid++,
i = 0,
toggler = function( event ) {
// Figure out which function to execute
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
// Make sure that clicks stop
event.preventDefault();
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
};
// link all the functions, so any of them can unbind this click handler
toggler.guid = guid;
while ( i < args.length ) {
args[ i++ ].guid = guid;
}
return this.click( toggler );
};
jQuery.fn.live = function( types, data, fn ) {
migrateWarn("jQuery.fn.live() is deprecated");
if ( oldLive ) {
return oldLive.apply( this, arguments );
}
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
};
jQuery.fn.die = function( types, fn ) {
migrateWarn("jQuery.fn.die() is deprecated");
if ( oldDie ) {
return oldDie.apply( this, arguments );
}
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
};
// Turn global events into document-triggered events
jQuery.event.trigger = function( event, data, elem, onlyHandlers ){
if ( !elem & !rajaxEvent.test( event ) ) {
migrateWarn( "Global events are undocumented and deprecated" );
}
return eventTrigger.call( this, event, data, elem || document, onlyHandlers );
};
jQuery.each( ajaxEvents.split("|"),
function( _, name ) {
jQuery.event.special[ name ] = {
setup: function() {
var elem = this;
// The document needs no shimming; must be !== for oldIE
if ( elem !== document ) {
jQuery.event.add( document, name + "." + jQuery.guid, function() {
jQuery.event.trigger( name, null, elem, true );
});
jQuery._data( this, name, jQuery.guid++ );
}
return false;
},
teardown: function() {
if ( this !== document ) {
jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
}
return false;
}
};
}
);
})( jQuery, window );

View File

@ -0,0 +1,9 @@
$(function() {
$("#toggleall").click(function() {
var checked_status = this.checked;
jQuery("input[type='checkbox']").each(function() {
this.checked = checked_status;
});
});
});

View File

@ -0,0 +1,13 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require_tree .
*/

File diff suppressed because one or more lines are too long

632
app/assets/stylesheets/bootstrap.min.css vendored Executable file
View File

@ -0,0 +1,632 @@
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
audio:not([controls]){display:none;}
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
a:hover,a:active{outline:0;}
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
sup{top:-0.5em;}
sub{bottom:-0.25em;}
img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;}
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
button,input{*overflow:visible;line-height:normal;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
textarea{overflow:auto;vertical-align:top;}
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
.clearfix:after{clear:both;}
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
a{color:#0088cc;text-decoration:none;}
a:hover{color:#005580;text-decoration:underline;}
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
.row:after{clear:both;}
[class*="span"]{float:left;margin-left:20px;}
.span1{width:60px;}
.span2{width:140px;}
.span3{width:220px;}
.span4{width:300px;}
.span5{width:380px;}
.span6{width:460px;}
.span7{width:540px;}
.span8{width:620px;}
.span9{width:700px;}
.span10{width:780px;}
.span11{width:860px;}
.span12,.container{width:940px;}
.offset1{margin-left:100px;}
.offset2{margin-left:180px;}
.offset3{margin-left:260px;}
.offset4{margin-left:340px;}
.offset5{margin-left:420px;}
.offset6{margin-left:500px;}
.offset7{margin-left:580px;}
.offset8{margin-left:660px;}
.offset9{margin-left:740px;}
.offset10{margin-left:820px;}
.offset11{margin-left:900px;}
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
.row-fluid:after{clear:both;}
.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
.row-fluid>[class*="span"]:first-child{margin-left:0;}
.row-fluid>.span1{width:6.382978723%;}
.row-fluid>.span2{width:14.89361702%;}
.row-fluid>.span3{width:23.404255317%;}
.row-fluid>.span4{width:31.914893614%;}
.row-fluid>.span5{width:40.425531911%;}
.row-fluid>.span6{width:48.93617020799999%;}
.row-fluid>.span7{width:57.446808505%;}
.row-fluid>.span8{width:65.95744680199999%;}
.row-fluid>.span9{width:74.468085099%;}
.row-fluid>.span10{width:82.97872339599999%;}
.row-fluid>.span11{width:91.489361693%;}
.row-fluid>.span12{width:99.99999998999999%;}
.container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
.container:after{clear:both;}
.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
.container-fluid:after{clear:both;}
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
h4,h5,h6{line-height:18px;}
h4{font-size:14px;}h4 small{font-size:12px;}
h5{font-size:12px;}
h6{font-size:11px;color:#999999;text-transform:uppercase;}
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
.page-header h1{line-height:1;}
ul,ol{padding:0;margin:0 0 9px 25px;}
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
ul{list-style:disc;}
ol{list-style:decimal;}
li{line-height:18px;}
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
dl{margin-bottom:18px;}
dt,dd{line-height:18px;}
dt{font-weight:bold;}
dd{margin-left:9px;}
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
strong{font-weight:bold;}
em{font-style:italic;}
.muted{color:#999999;}
abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;}
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
q:before,q:after,blockquote:before,blockquote:after{content:"";}
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
small{font-size:100%;}
cite{font-style:normal;}
code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
code{padding:3px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;}
pre code{padding:0;color:inherit;background-color:transparent;border:0;}
.pre-scrollable{max-height:340px;overflow-y:scroll;}
form{margin:0 0 18px;}
fieldset{padding:0;margin:0;border:0;}
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
label{display:block;margin-bottom:5px;color:#333333;}
input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.uneditable-textarea{width:auto;height:auto;}
label input,label textarea,label select{display:block;}
input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;}
input[type="image"]{border:0;}
input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
input[type="file"]{line-height:18px \9;}
select{width:220px;background-color:#ffffff;}
select[multiple],select[size]{height:auto;}
input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
textarea{height:auto;}
input[type="hidden"]{display:none;}
.radio,.checkbox{padding-left:18px;}
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.input-mini{width:60px;}
.input-small{width:90px;}
.input-medium{width:150px;}
.input-large{width:210px;}
.input-xlarge{width:270px;}
.input-xxlarge{width:530px;}
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
input.span1,textarea.span1,.uneditable-input.span1{width:50px;}
input.span2,textarea.span2,.uneditable-input.span2{width:130px;}
input.span3,textarea.span3,.uneditable-input.span3{width:210px;}
input.span4,textarea.span4,.uneditable-input.span4{width:290px;}
input.span5,textarea.span5,.uneditable-input.span5{width:370px;}
input.span6,textarea.span6,.uneditable-input.span6{width:450px;}
input.span7,textarea.span7,.uneditable-input.span7{width:530px;}
input.span8,textarea.span8,.uneditable-input.span8{width:610px;}
input.span9,textarea.span9,.uneditable-input.span9{width:690px;}
input.span10,textarea.span10,.uneditable-input.span10{width:770px;}
input.span11,textarea.span11,.uneditable-input.span11{width:850px;}
input.span12,textarea.span12,.uneditable-input.span12{width:930px;}
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;}
.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;}
.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
:-moz-placeholder{color:#999999;}
::-webkit-input-placeholder{color:#999999;}
.help-block{display:block;margin-top:5px;margin-bottom:0;color:#999999;}
.help-inline{display:inline-block;*display:inline;*zoom:1;margin-bottom:9px;vertical-align:middle;padding-left:5px;}
.input-prepend,.input-append{margin-bottom:5px;*zoom:1;}.input-prepend:before,.input-append:before,.input-prepend:after,.input-append:after{display:table;content:"";}
.input-prepend:after,.input-append:after{clear:both;}
.input-prepend input,.input-append input,.input-prepend .uneditable-input,.input-append .uneditable-input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
.input-prepend .add-on,.input-append .add-on{float:left;display:block;width:auto;min-width:16px;height:18px;margin-right:-1px;padding:4px 5px;font-weight:normal;line-height:18px;color:#999999;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#f5f5f5;border:1px solid #ccc;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
.input-prepend .add-on{*margin-top:1px;}
.input-append input,.input-append .uneditable-input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;}
.input-append .add-on{margin-right:0;margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
.input-append input:first-child{*margin-left:-160px;}.input-append input:first-child+.add-on{*margin-left:-21px;}
.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input{display:inline-block;margin-bottom:0;}
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
.form-search label,.form-inline label,.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{display:inline-block;}
.form-search .input-append .add-on,.form-inline .input-prepend .add-on,.form-search .input-append .add-on,.form-inline .input-prepend .add-on{vertical-align:middle;}
.form-search .radio,.form-inline .radio,.form-search .checkbox,.form-inline .checkbox{margin-bottom:0;vertical-align:middle;}
.control-group{margin-bottom:9px;}
legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
.form-horizontal .control-group:after{clear:both;}
.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
.form-horizontal .controls{margin-left:160px;}
.form-horizontal .form-actions{padding-left:160px;}
table{max-width:100%;border-collapse:collapse;border-spacing:0;}
.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd;}
.table th{font-weight:bold;}
.table thead th{vertical-align:bottom;}
.table thead:first-child tr th,.table thead:first-child tr td{border-top:0;}
.table tbody+tbody{border-top:2px solid #ddd;}
.table-condensed th,.table-condensed td{padding:4px 5px;}
.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;}
.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
table .span1{float:none;width:44px;margin-left:0;}
table .span2{float:none;width:124px;margin-left:0;}
table .span3{float:none;width:204px;margin-left:0;}
table .span4{float:none;width:284px;margin-left:0;}
table .span5{float:none;width:364px;margin-left:0;}
table .span6{float:none;width:444px;margin-left:0;}
table .span7{float:none;width:524px;margin-left:0;}
table .span8{float:none;width:604px;margin-left:0;}
table .span9{float:none;width:684px;margin-left:0;}
table .span10{float:none;width:764px;margin-left:0;}
table .span11{float:none;width:844px;margin-left:0;}
table .span12{float:none;width:924px;margin-left:0;}
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("/assets/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
.icon-white{background-image:url("/assets/glyphicons-halflings-white.png");}
.icon-glass{background-position:0 0;}
.icon-music{background-position:-24px 0;}
.icon-search{background-position:-48px 0;}
.icon-envelope{background-position:-72px 0;}
.icon-heart{background-position:-96px 0;}
.icon-star{background-position:-120px 0;}
.icon-star-empty{background-position:-144px 0;}
.icon-user{background-position:-168px 0;}
.icon-film{background-position:-192px 0;}
.icon-th-large{background-position:-216px 0;}
.icon-th{background-position:-240px 0;}
.icon-th-list{background-position:-264px 0;}
.icon-ok{background-position:-288px 0;}
.icon-remove{background-position:-312px 0;}
.icon-zoom-in{background-position:-336px 0;}
.icon-zoom-out{background-position:-360px 0;}
.icon-off{background-position:-384px 0;}
.icon-signal{background-position:-408px 0;}
.icon-cog{background-position:-432px 0;}
.icon-trash{background-position:-456px 0;}
.icon-home{background-position:0 -24px;}
.icon-file{background-position:-24px -24px;}
.icon-time{background-position:-48px -24px;}
.icon-road{background-position:-72px -24px;}
.icon-download-alt{background-position:-96px -24px;}
.icon-download{background-position:-120px -24px;}
.icon-upload{background-position:-144px -24px;}
.icon-inbox{background-position:-168px -24px;}
.icon-play-circle{background-position:-192px -24px;}
.icon-repeat{background-position:-216px -24px;}
.icon-refresh{background-position:-240px -24px;}
.icon-list-alt{background-position:-264px -24px;}
.icon-lock{background-position:-287px -24px;}
.icon-flag{background-position:-312px -24px;}
.icon-headphones{background-position:-336px -24px;}
.icon-volume-off{background-position:-360px -24px;}
.icon-volume-down{background-position:-384px -24px;}
.icon-volume-up{background-position:-408px -24px;}
.icon-qrcode{background-position:-432px -24px;}
.icon-barcode{background-position:-456px -24px;}
.icon-tag{background-position:0 -48px;}
.icon-tags{background-position:-25px -48px;}
.icon-book{background-position:-48px -48px;}
.icon-bookmark{background-position:-72px -48px;}
.icon-print{background-position:-96px -48px;}
.icon-camera{background-position:-120px -48px;}
.icon-font{background-position:-144px -48px;}
.icon-bold{background-position:-167px -48px;}
.icon-italic{background-position:-192px -48px;}
.icon-text-height{background-position:-216px -48px;}
.icon-text-width{background-position:-240px -48px;}
.icon-align-left{background-position:-264px -48px;}
.icon-align-center{background-position:-288px -48px;}
.icon-align-right{background-position:-312px -48px;}
.icon-align-justify{background-position:-336px -48px;}
.icon-list{background-position:-360px -48px;}
.icon-indent-left{background-position:-384px -48px;}
.icon-indent-right{background-position:-408px -48px;}
.icon-facetime-video{background-position:-432px -48px;}
.icon-picture{background-position:-456px -48px;}
.icon-pencil{background-position:0 -72px;}
.icon-map-marker{background-position:-24px -72px;}
.icon-adjust{background-position:-48px -72px;}
.icon-tint{background-position:-72px -72px;}
.icon-edit{background-position:-96px -72px;}
.icon-share{background-position:-120px -72px;}
.icon-check{background-position:-144px -72px;}
.icon-move{background-position:-168px -72px;}
.icon-step-backward{background-position:-192px -72px;}
.icon-fast-backward{background-position:-216px -72px;}
.icon-backward{background-position:-240px -72px;}
.icon-play{background-position:-264px -72px;}
.icon-pause{background-position:-288px -72px;}
.icon-stop{background-position:-312px -72px;}
.icon-forward{background-position:-336px -72px;}
.icon-fast-forward{background-position:-360px -72px;}
.icon-step-forward{background-position:-384px -72px;}
.icon-eject{background-position:-408px -72px;}
.icon-chevron-left{background-position:-432px -72px;}
.icon-chevron-right{background-position:-456px -72px;}
.icon-plus-sign{background-position:0 -96px;}
.icon-minus-sign{background-position:-24px -96px;}
.icon-remove-sign{background-position:-48px -96px;}
.icon-ok-sign{background-position:-72px -96px;}
.icon-question-sign{background-position:-96px -96px;}
.icon-info-sign{background-position:-120px -96px;}
.icon-screenshot{background-position:-144px -96px;}
.icon-remove-circle{background-position:-168px -96px;}
.icon-ok-circle{background-position:-192px -96px;}
.icon-ban-circle{background-position:-216px -96px;}
.icon-arrow-left{background-position:-240px -96px;}
.icon-arrow-right{background-position:-264px -96px;}
.icon-arrow-up{background-position:-289px -96px;}
.icon-arrow-down{background-position:-312px -96px;}
.icon-share-alt{background-position:-336px -96px;}
.icon-resize-full{background-position:-360px -96px;}
.icon-resize-small{background-position:-384px -96px;}
.icon-plus{background-position:-408px -96px;}
.icon-minus{background-position:-433px -96px;}
.icon-asterisk{background-position:-456px -96px;}
.icon-exclamation-sign{background-position:0 -120px;}
.icon-gift{background-position:-24px -120px;}
.icon-leaf{background-position:-48px -120px;}
.icon-fire{background-position:-72px -120px;}
.icon-eye-open{background-position:-96px -120px;}
.icon-eye-close{background-position:-120px -120px;}
.icon-warning-sign{background-position:-144px -120px;}
.icon-plane{background-position:-168px -120px;}
.icon-calendar{background-position:-192px -120px;}
.icon-random{background-position:-216px -120px;}
.icon-comment{background-position:-240px -120px;}
.icon-magnet{background-position:-264px -120px;}
.icon-chevron-up{background-position:-288px -120px;}
.icon-chevron-down{background-position:-313px -119px;}
.icon-retweet{background-position:-336px -120px;}
.icon-shopping-cart{background-position:-360px -120px;}
.icon-folder-close{background-position:-384px -120px;}
.icon-folder-open{background-position:-408px -120px;}
.icon-resize-vertical{background-position:-432px -119px;}
.icon-resize-horizontal{background-position:-456px -118px;}
.dropdown{position:relative;}
.dropdown-toggle{*margin-bottom:-3px;}
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
.caret{display:inline-block;width:0;height:0;text-indent:-99999px;*text-indent:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"\2193";}
.dropdown .caret{margin-top:8px;margin-left:2px;}
.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;_width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.bottom-up{top:auto;bottom:100%;margin-bottom:2px;}
.dropdown-menu .divider{height:1px;margin:5px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#555555;white-space:nowrap;}
.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
.dropdown.open .dropdown-menu{display:block;}
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
.btn{display:inline-block;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;}
.btn:active,.btn.active{background-color:#cccccc \9;}
.btn:first-child{*margin-left:0;}
.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;}
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.btn-large [class^="icon-"]{margin-top:1px;}
.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
.btn-small [class^="icon-"]{margin-top:-1px;}
.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-dark.active{color:rgba(255, 255, 255, 0.75);}
.btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;}
.btn-primary:active,.btn-primary.active{background-color:#003399 \9;}
.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
.btn-inverse{background-color:#393939;background-image:-moz-linear-gradient(top, #454545, #262626);background-image:-ms-linear-gradient(top, #454545, #262626);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#454545), to(#262626));background-image:-webkit-linear-gradient(top, #454545, #262626);background-image:-o-linear-gradient(top, #454545, #262626);background-image:linear-gradient(top, #454545, #262626);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#454545', endColorstr='#262626', GradientType=0);border-color:#262626 #262626 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#262626;}
.btn-inverse:active,.btn-inverse.active{background-color:#0c0c0c \9;}
button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;}
button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;}
.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
.btn-group:after{clear:both;}
.btn-group:first-child{*margin-left:0;}
.btn-group+.btn-group{margin-left:5px;}
.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:5px;*padding-bottom:5px;}
.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
.btn .caret{margin-top:7px;margin-left:0;}
.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
.btn-primary .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
.btn-small .caret{margin-top:4px;}
.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.alert,.alert-heading{color:#c09853;}
.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;}
.alert-success,.alert-success .alert-heading{color:#468847;}
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;}
.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading{color:#b94a48;}
.alert-info{background-color:#d9edf7;border-color:#bce8f1;}
.alert-info,.alert-info .alert-heading{color:#3a87ad;}
.alert-block{padding-top:14px;padding-bottom:14px;}
.alert-block>p,.alert-block>ul{margin-bottom:0;}
.alert-block p+p{margin-top:5px;}
.nav{margin-left:0;margin-bottom:18px;list-style:none;}
.nav>li>a{display:block;}
.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
.nav li+.nav-header{margin-top:9px;}
.nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;}
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
.nav-list>li>a{padding:3px 15px;}
.nav-list .active>a,.nav-list .active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
.nav-list [class^="icon-"]{margin-right:2px;}
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
.nav-tabs:after,.nav-pills:after{clear:both;}
.nav-tabs>li,.nav-pills>li{float:left;}
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
.nav-tabs{border-bottom:1px solid #ddd;}
.nav-tabs>li{margin-bottom:-1px;}
.nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;}
.nav-stacked>li{float:none;}
.nav-stacked>li>a{margin-right:0;}
.nav-tabs.nav-stacked{border-bottom:0;}
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;}
.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;}
.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;}
.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
.tabs-stacked .open>a:hover{border-color:#999999;}
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
.tabbable:after{clear:both;}
.tab-content{overflow:hidden;}
.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
.tab-content>.active,.pill-content>.active{display:block;}
.tabs-below .nav-tabs{border-top:1px solid #ddd;}
.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
.navbar{overflow:visible;margin-bottom:18px;}
.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
.nav-collapse.collapse{height:auto;}
.navbar .brand:hover{text-decoration:none;}
.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
.navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;}
.navbar .btn,.navbar .btn-group{margin-top:5px;}
.navbar .btn-group .btn{margin-top:0;}
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
.navbar-form:after{clear:both;}
.navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;}
.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;}
.navbar-search .search-query::-webkit-input-placeholder{color:#eeeeee;}
.navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);}
.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;}
.navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
.navbar .nav.pull-right{float:right;}
.navbar .nav>li{display:block;float:left;}
.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;}
.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
.navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;}
.navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;}
.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;text-shadow:0 1px 0 #ffffff;}
.breadcrumb .divider{padding:0 5px;color:#999999;}
.breadcrumb .active a{color:#333333;}
.pagination{height:36px;margin:18px 0;}
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
.pagination li{display:inline;}
.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
.pagination .active a{color:#999999;cursor:default;}
.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
.pagination-centered{text-align:center;}
.pagination-right{text-align:right;}
.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
.pager:after{clear:both;}
.pager li{display:inline;}
.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
.pager .next a{float:right;}
.pager .previous a{float:left;}
.modal-open .dropdown-menu{z-index:2050;}
.modal-open .dropdown.open{*z-index:2050;}
.modal-open .popover{z-index:2060;}
.modal-open .tooltip{z-index:2070;}
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
.modal{position:fixed;top:50%;left:50%;z-index:1050;max-height:500px;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
.modal.fade.in{top:50%;}
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
.modal-body{padding:15px;}
.modal-body .modal-form{margin-bottom:0;}
.modal-footer{padding:14px 15px 15px;margin-bottom:0;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
.modal-footer:after{clear:both;}
.modal-footer .btn{float:right;margin-left:5px;margin-bottom:0;}
.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
.tooltip.top{margin-top:-2px;}
.tooltip.right{margin-left:2px;}
.tooltip.bottom{margin-top:2px;}
.tooltip.left{margin-left:-2px;}
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.tooltip-arrow{position:absolute;width:0;height:0;}
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
.popover.right{margin-left:5px;}
.popover.bottom{margin-top:5px;}
.popover.left{margin-left:-5px;}
.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.popover .arrow{position:absolute;width:0;height:0;}
.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
.thumbnails:after{clear:both;}
.thumbnails>li{float:left;margin:0 0 18px 20px;}
.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
.thumbnail .caption{padding:9px;}
.label{padding:2px 4px 3px;font-size:11.049999999999999px;font-weight:bold;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.label:hover{color:#ffffff;text-decoration:none;}
.label-important{background-color:#b94a48;}
.label-important:hover{background-color:#953b39;}
.label-warning{background-color:#f89406;}
.label-warning:hover{background-color:#c67605;}
.label-success{background-color:#468847;}
.label-success:hover{background-color:#356635;}
.label-info{background-color:#3a87ad;}
.label-info:hover{background-color:#2d6987;}
@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.accordion{margin-bottom:18px;}
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.accordion-heading{border-bottom:0;}
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
.carousel{position:relative;margin-bottom:18px;line-height:1;}
.carousel-inner{overflow:hidden;width:100%;position:relative;}
.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
.carousel .item>img{display:block;line-height:1;}
.carousel .active,.carousel .next,.carousel .prev{display:block;}
.carousel .active{left:0;}
.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
.carousel .next{left:100%;}
.carousel .prev{left:-100%;}
.carousel .next.left,.carousel .prev.right{left:0;}
.carousel .active.left{left:-100%;}
.carousel .active.right{left:100%;}
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
.carousel-caption h4,.carousel-caption p{color:#ffffff;}
.hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;}
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;}
.pull-right{float:right;}
.pull-left{float:left;}
.hide{display:none;}
.show{display:block;}
.invisible{visibility:hidden;}

View File

@ -0,0 +1,55 @@
body.simple
margin-top: 20px
margin-bottom: 20px
#footer-simple
text-align: center
.top-pix18
margin-top: 18px
body.application
margin-top: 10px
margin-bottom: 10px
#sidebar
.logo
img
margin-bottom: 10px
p.version
text-align: center
tr.unseen
font-weight: bold
.bottom-pix18
margin-bottom: 18px
table.header
td.field_name
text-align: right
font-weight: bold
padding-right: 10px
p.help-block
font-size: 10px
.custom_pagination
text-align: right
a,span,em
line-height: 18px
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)
margin-bottom: 0
margin-left: 0
border: 1px solid #DDDDDD
padding: 3px
table.records
font-size: 12px
td
padding: 4px
iframe
width: 700px
height: 800px

View File

@ -0,0 +1,53 @@
$blue: #0088CC
$outline: #DDDDDD
$month: #0044CC
$outside: #DDDDDD
$today: #DA4F49
.calendar
margin-bottom: 18px
h3
color: $month
table
width: 100%
border: 1px solid $outline
border-collapse: separate
border-radius: 4px 4px 4px 4px
td
text-align: right
margin: 2px
padding: 2px
border-top: 1px solid $outline
border-left: 1px solid $outline
td.wday
color: $blue
font-weight: bold
text-align: center
td.corner
border-top: 0 none
border-left: 0 none
td.weekend
background-color: $outline
td.outside
background-color: white
color: $outside
td.today
background-color: $today
color: white
font-weight: bold
td.weeknum
color: $blue
font-weight: bold
text-align: center
.calendar.square
td.wday
border-top: 0 none
td.weeknum
border-left: 0 none
.calendar.window
td.monthnum
border-top: 0 none
tr > td:first-child
border-left: 0 none

248
app/controllers/application_controller.rb Normal file → Executable file
View File

@ -1,175 +1,95 @@
# The filters added to this controller will be run for all controllers in the application.
# Likewise will all the methods added be available for all controllers.
require 'yaml'
class ApplicationController < ActionController::Base
before_filter :user_login_filter
before_filter :add_scripts
#before_filter :localize
filter_parameter_logging :password
protected
def secure_user?() true end
def secure_cust?() false end
def additional_scripts() "" end
def onload_function() "" end
private
def add_scripts
@additional_scripts = additional_scripts()
@onload_function = onload_function()
end
def user_login_filter
if (secure_user? or secure_cust? )and logged_user.nil?
session["return_to"] = request.request_uri
redirect_to :controller=>"/login", :action => "index"
return false
end
end
alias login_required user_login_filter
def logged_user # returns customer id
session['user']
#logger.custom("session",session.inspect)
#protect_from_forgery
before_filter :load_settings,:current_user,:set_locale
#before_filter :plugins_configuration
def load_settings
$defaults ||= YAML::load(File.open(Rails.root.join('config','settings.yml')))
end
################################# protected section ###########################################
protected
def theme_resolver
if @current_user.nil?
$defaults['theme']
else
@current_user.prefs.theme || $defaults['theme']
end
end
def set_locale
if @current_user.nil?
I18n.locale = $defaults['locale'] || I18n.default_locale
else
I18n.locale = @current_user.prefs.locale.to_sym || I18n.default_locale
end
end
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
logger.custom("current_user",@current_user.inspect)
end
def check_current_user
if @current_user.nil?
session["return_to"] = request.fullpath
redirect_to :controller => 'user', :action => 'login'
return false
end
end
def selected_folder
if session[:selected_folder]
@selected_folder = session[:selected_folder]
else
folder = @current_user.folders.inbox.first
if not folder.nil?
@selected_folder = folder.full_name
end
end
end
def get_current_folders
@folders_shown = @current_user.folders.shown.order("name asc")
if not @selected_folder.nil?
@current_folder = @current_user.folders.find_by_full_name(@selected_folder)
end
end
def prepare_compose_buttons
@buttons = []
@buttons << {:text => 'sendout',:scope=>:compose,:image => 'email.png'}
@buttons << {:text => 'save',:scope=>:compose,:image => 'save.png'}
end
def logged_customer
session['user']
end
def localize
# We will use instance vars for the locale so we can make use of them in
# the templates.
@charset = 'utf-8'
headers['Content-Type'] = "text/html; charset=#{@charset}"
# Here is a very simplified approach to extract the prefered language
# from the request. If all fails, just use 'en_EN' as the default.
temp = if request.env['HTTP_ACCEPT_LANGUAGE'].nil?
[]
else
request.env['HTTP_ACCEPT_LANGUAGE'].split(',').first.split('-') rescue []
end
language = temp.slice(0)
dialect = temp.slice(1)
@language = language.nil? ? 'en' : language.downcase # default is en
# If there is no dialect use the language code ('en' becomes 'en_EN').
@dialect = dialect.nil? ? @language.upcase : dialect
# The complete locale string consists of
# language_DIALECT (en_EN, en_GB, de_DE, ...)
@locale = "#{@language}_#{@dialect.upcase}"
@htmllang = @language == @dialect ? @language : "#{@language}-#{@dialect}"
# Finally, bind the textdomain to the locale. From now on every used
# _('String') will get translated into the right language. (Provided
# that we have a corresponding mo file in the right place).
bindtextdomain('messages', "#{RAILS_ROOT}/locale", @locale, @charset)
def create_message_with_params
@message = Message.new(params[:message])
# if params[:message]
# @message.update_attributes(params[:message])
# end
files = Dir.glob(File.join($defaults["msg_upload_dir"],@current_user.username + "*"))
@attachments = []
files.each do |f|
@attachments << {:name => File.basename(f).gsub!(/#{@current_user.username}_/,"") , :size => File.stat(f).size }
end
end
public
def include_tinymce(mode="textareas",elements="")
tinymce=''
tinymce << '
<script language="javascript" type="text/javascript" src="/tiny_mce/tiny_mce.js"></script>
<script language="javascript" type="text/javascript">
tinyMCE.init({
mode : "'
tinymce << mode << '",'
if mode == "exact"
tinymce << 'elements : "' << elements << '",
'
def get_system_folders
@drafts_folder = @current_user.folders.drafts.first
@sent_folder = @current_user.folders.sent.first
@inbox_folder = @current_user.folders.inbox.first
@trash_folder = @current_user.folders.trash.first
end
tinymce << '
theme : "advanced",
cleanup : true,
width: "100%",
remove_linebreaks : false,
entity_encoding : "named",
relative_urls : false,
plugins : "table,save,advhr,advimage,advlink,iespell,preview,zoom,searchreplace,print,contextmenu,fullscreen,linkattach",
theme_advanced_buttons1_add : "fontselect,fontsizeselect",
theme_advanced_buttons2_add : "separator,preview,zoom",
theme_advanced_buttons2_add_before: "cut,copy,paste,separator,search,replace,separator",
theme_advanced_buttons3_add_before : "tablecontrols,separator",
theme_advanced_buttons3_add : "iespell,forecolor,backcolor,fullscreen",
theme_advanced_source_editor_width : "700",
theme_advanced_source_editor_height : "500",
theme_advanced_styles : "Header 1=header1",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_path_location : "none",
extended_valid_elements : ""
+"a[accesskey|charset|class|coords|href|hreflang|id|lang|name"
+"|onblur|onclick|ondblclick|onfocus|onkeydown|onkeypress|onkeyup"
+"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|rel|rev"
+"|shape|style|tabindex|title|target|type],"
+"dd[class|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
+"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
+"div[align|class|id|lang|onclick"
+"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
+"|onmouseout|onmouseover|onmouseup|style|title],"
+"dl[class|compact|id|lang|onclick|ondblclick|onkeydown"
+"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
+"|onmouseup|style|title],"
+"dt[class|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup"
+"|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title],"
+"img[align|alt|border|class|height"
+"|hspace|id|ismap|lang|longdesc|name|onclick|ondblclick|onkeydown"
+"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
+"|onmouseup|src|style|title|usemap|vspace|width],"
+"script[charset|defer|language|src|type],"
+"style[lang|media|title|type],"
+"table[align|bgcolor|border|cellpadding|cellspacing|class"
+"|frame|height|id|lang|onclick|ondblclick|onkeydown|onkeypress"
+"|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|rules"
+"|style|summary|title|width],"
+"td[abbr|align|axis|bgcolor|char|charoff|class"
+"|colspan|headers|height|id|lang|nowrap|onclick"
+"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
+"|onmouseout|onmouseover|onmouseup|rowspan|scope"
+"|style|title|valign|width],"
+"hr[align|class|id|lang|noshade|onclick"
+"|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove"
+"|onmouseout|onmouseover|onmouseup|size|style|title|width],"
+"font[class|color|face|id|lang|size|style|title],"
+"span[align|class|class|id|lang|onclick|ondblclick|onkeydown"
+"|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover"
+"|onmouseup|style|title]",
external_link_list_url : "/cms/urlchoose/choose_tinymce",
external_attachments_list_url : "/attachments/attachments/choose_tinymce",
external_image_list_url : "/gallery/imgchoose/choose_tinymce",
flash_external_list_url : "example_data/example_flash_list.js"
});
</script>'
tinymce
end
helper_method :include_tinymce
def include_simple_tinymce(mode="textareas",elements="")
tinymce = ''
tinymce << '<script language="javascript" type="text/javascript" src="/tiny_mce/tiny_mce.js"></script>
<script language="javascript" type="text/javascript">
tinyMCE.init({
mode : "'
tinymce << mode << '",'
if mode == "exact"
tinymce << 'elements : "' << elements << '",
'
end
tinymce << '
theme : "default",
width : "100%",
auto_reset_designmode : true
});
</script>'
tinymce
end
def _(text)
t text
end
helper_method :include_simple_tinymce, :_
##################################### private section ##########################################
end

View File

@ -1,52 +0,0 @@
class ContactGroupsController < ApplicationController
layout 'public'
def index
@contact_group = ContactGroup.new
@contact_group.customer_id = logged_user
@contactgroups = ContactGroup.find_by_user(logged_user)
end
def add
@contactgroup = ContactGroup.new
@contactgroup.customer_id = logged_user
render("/contact_group/edit")
end
def delete
contactgroup = ContactGroup.find(@params["id"])
contactgroup.destroy
redirect_to(:action=>"list")
end
def edit
@contactgroup = ContactGroup.find(@params["id"])
end
def save
begin
if @params["contactgroup"]["id"].nil? or @params["contactgroup"]["id"] == ""
# New contactgroup
@contactgroup = ContactGroup.create(@params["contactgroup"])
else
# Edit existing
@contactgroup = ContactGroup.find(@params["contactgroup"]["id"])
@contactgroup.attributes = @params["contactgroup"]
end
if @contactgroup.save
redirect_to(:action=>"list")
else
render "/contact_group/edit"
end
rescue CDF::ValidationError => e
logger.info("RESCUE")
@contactgroup = e.entity
render("/contact_group/edit")
end
end
protected
def secure_user?() true end
end

484
app/controllers/contacts_controller.rb Normal file → Executable file
View File

@ -1,375 +1,109 @@
class ContactsController < ApplicationController
layout :select_layout
def index
if params[:letter] && params[:letter].any?
@contacts = Contact.for_customer(logged_user).letter(params[:letter]).paginate :page => params[:page],
:per_page => CDF::CONFIG[:contacts_per_page]
else
@contacts = Contact.for_customer(logged_user).paginate :page => params[:page], :per_page => CDF::CONFIG[:contacts_per_page]
end
end
def listLetter
letters = CDF::CONFIG[:contact_letters]
@contact_pages = Paginator.new(self, Contact.count(
["customer_id = %s and substr(UPPER(fname),1,1) = '%s'", logged_user, letters[params['id'].to_i]]), CDF::CONFIG[:contacts_per_page], params['page'])
@contacts = Contact.find(:all, :conditions=>["customer_id = %s and substr(UPPER(fname),1,1) = '%s'", logged_user, letters[params['id'].to_i]],
:order=>['fname'], :limit=>CDF::CONFIG[:contacts_per_page], :offset=>@contact_pages.current.offset)
if params["mode"] == "groups"
if params["group_id"] and not params["group_id"].nil? and not params["group_id"] == ''
@group_id = params["group_id"].to_i
@contacts_for_group = Hash.new
for contact in @contacts
@contacts_for_group[contact.id] = 0 # initialize
for gr in contact.groups
if gr.contact_group_id.to_i == @group_id
@contacts_for_group[contact.id] = 1 # checked
end
end
end
end
end
render :action => "list"
end
def new
@contact = Contact.new
@contact.customer_id = logged_user
# load related lists
loadLists
# Init groups: because of checkbox
# Set all to 0 => unchecked
@groups = Hash.new
@contactgroups.each {|g|
@groups[g.id] = 0
}
end
def add_multiple
@contact = Contact.new
@contact["file_type"] = "1"
end
def add_from_mail
cstr = params['cstr']
retmsg = params['retmsg']
session["return_to"] = url_for(:controller=>'/webmail/webmail',
:action=>'folders',
:msg_id=>retmsg)
# parse string
if i = cstr.index("<")
name, email = cstr.slice(0, i), cstr.slice((i+1)..(cstr.strip().index(">")-1))
fname = name.split().first
lname = name.split().last if name.split().size() > 1
else
fname, lname, email = "", "", cstr
end
if @contact = Contact.find_by_user_email(logged_user, email)
# load related lists
loadLists
@contact.fname, @contact.lname = fname, lname
# groups = @contact.groups
@groups = Hash.new
@contactgroups.each {|g|
groupSelected = false
@contact.groups.each {|gr|
if gr.contact_group_id.to_i == g.id.to_i
groupSelected = true
break
end
}
if groupSelected
@groups[g.id] = 1 # checked
else
@groups[g.id] = 0 # unchecked
end
}
else
@contact = Contact.new("fname"=>fname, "lname" => lname, "email" => email)
@contact.customer_id = logged_user
# load related lists
loadLists
# Init groups: because of checkbox
# Set all to 0 => unchecked
@groups = Hash.new
@contactgroups.each {|g|
@groups[g.id] = 0
}
end
render :action => "new"
end
def import_preview
file = params["contact"]["data"]
flash["errors"] = Array.new
if file.size == 0
flash["errors"] << _('You haven\'t selected file or the file is empty')
@contact = Contact.new
@contact["file_type"] = params["contact"]["file_type"]
render :action => "add_multiple"
end
file_type = params["contact"]["file_type"]
if file_type.nil? or file_type == '1'
separator = ','
else
separator = /\t/
end
@contacts = Array.new
emails = Array.new
file.each {|line|
cdata = line.strip.chomp.split(separator)
cont = Contact.new
cont.fname = cdata[0].to_s.strip.chomp
cont.lname = cdata[1].to_s.strip.chomp
cont.email = cdata[2].to_s.strip.chomp
# Check for duplicate emails in the file
if emails.include?(cont.email)
flash["errors"] << sprintf(_('Contact %'), file.lineno.to_s) + ": " + _('The e-mail duplicates the e-mail of another record!')
else
emails << cont.email
end
@contacts << cont
}
end
def import
contacts_count = params["contact"].length
contacts_to_import = params["contact"]
@contacts = Array.new
emails = Array.new
flash["errors"] = Array.new
for i in 0...contacts_count
contact = Contact.new
contact.customer_id = logged_user
contact.fname = contacts_to_import[i.to_s]["fname"]
contact.lname = contacts_to_import[i.to_s]["lname"]
contact.email = contacts_to_import[i.to_s]["email"]
begin
# Check for duplicate emails in the submitted data
if emails.include?(contact.email)
flash["errors"] << sprintf(_('Contact %'), (i+1).to_s) + ": " + _('The e-mail duplicates the e-mail of another record!')
else
emails << contact.email
end
# Check if contact is valid
contact.valid?
rescue CDF::ValidationError => e
if not contact.errors.empty?
["fname", "lname", "email"].each do |attr|
attr_errors = contact.errors.on(attr)
attr_errors = [attr_errors] unless attr_errors.nil? or attr_errors.is_a? Array
if not attr_errors.nil?
attr_errors.each do |msg|
flash["errors"] << l(:contact_addmultiple_errorforcontact, (i+1).to_s) + ": " + l(msg)
end
end
end
end
end # rescue
@contacts << contact
end # for
# If there are validation errors - display them
if not flash["errors"].nil? and not flash["errors"].empty?
render :action => "import_preview"
else
# save
begin
for contact in @contacts
Contact.create(contact.attributes)
end
# Set message for successful import
flash["alert"] = Array.new
flash["alert"] << l(:contact_addmultiple_success, @contacts.length.to_s)
keep_flash()
redirect_to(:action=>"list")
rescue Exception => exc
flash["errors"] << exc
render :action => "import_preview"
end
end
end
def choose
if params["mode"] == "groups"
save_groups
end
@tos, @ccs, @bccs = Array.new, Array.new, Array.new
params["contacts_to"].each{ |id,value| @tos << Contact.find(id) if value == "1" } if params["contacts_to"]
params["contacts_cc"].each{ |id,value| @ccs << Contact.find(id) if value == "1" } if params["contacts_cc"]
params["contacts_bcc"].each{ |id,value| @bccs << Contact.find(id) if value == "1" } if params["contacts_bcc"]
params["groups_to"].each{ |id,value|
ContactGroup.find(id).contacts.each {|c| @tos << c} if value == "1" } if params["groups_to"]
params["groups_cc"].each{ |id,value|
ContactGroup.find(id).contacts.each {|c| @ccs << c} if value == "1" } if params["groups_cc"]
params["groups_bcc"].each{ |id,value|
ContactGroup.find(id).contacts.each {|c| @bccs << c} if value == "1" } if params["groups_bcc"]
end
def save_groups
contacts_for_group = params["contacts_for_group"]
group_id = params["group_id"]
contact_group = ContactGroup.find(group_id)
contacts_for_group.each { |contact_id,value|
contact = Contact.find(contact_id)
if value == "1" and not contact_group.contacts.include?(contact)
contact_group.contacts << contact
end
if value == "0" and contact_group.contacts.include?(contact)
contact_group.contacts.delete(contact)
end
}
redirect_to(:action=>"index", :id=>group_id, :params=>{"mode"=>params["mode"]})
end
def edit
@contact = Contact.find(params["id"])
# load related lists
loadLists
# groups = @contact.groups
@groups = Hash.new
@contactgroups.each {|g|
groupSelected = false
@contact.groups.each {|gr|
if gr.contact_group_id.to_i == g.id.to_i
groupSelected = true
break
end
}
if groupSelected
@groups[g.id] = 1 # checked
else
@groups[g.id] = 0 # unchecked
end
}
render :action => "new"
end
# Insert or update
def create
if params["contact"]["id"] == ""
# New contact
@contact = Contact.create(params["contact"])
else
# Edit existing
@contact = Contact.find(params["contact"]["id"])
@contact.attributes = params["contact"]
end
@contactgroups = ContactGroup.find_by_user(logged_user)
# Groups displayed
groups = params['groups']
tempGroups = Array.new
tempGroups.concat(@contact.groups)
@contactgroups.each { |cgroup|
includesCGroup = false
tempGroups.each {|gr|
if gr.contact_group_id.to_i == cgroup.id.to_i
includesCGroup = true
break
end
}
if groups["#{cgroup.id}"] == "1" and not includesCGroup
@contact.groups << cgroup
end
if groups["#{cgroup.id}"] == "0" and includesCGroup
@contact.groups.delete(cgroup)
end
}
if @contact.save
if params["paction"] == t(:save)
redirect_to :action =>:index
else
redirect_to :action => :new
end
else
loadLists
@groups = Hash.new
@contactgroups.each {|g|
if @contact.groups.include?(g)
@groups[g.id] = 1
else
@groups[g.id] = 0
end
}
render :action => :new
end
end
def delete
Contact.destroy(params['id'])
redirect_to(:action=>'index')
end
protected
def secure_user?() true end
def additional_scripts()
add_s = ''
if action_name == "choose"
add_s<<'<script type="text/javascript" src="/javascripts/global.js"></script>'
add_s<<'<script type="text/javascript" src="/javascripts/contact_choose.js"></script>'
end
add_s
end
def onload_function()
if action_name == "choose"
"javascript:respondToCaller();"
else
""
end
end
private
def select_layout
if params["mode"] == "choose"
@mode = "choose"
@contactgroups = ContactGroup.find_by_user(logged_user)
'chooser'
elsif params["mode"] == "groups"
@mode = "groups"
'public'
else
@mode = "normal"
'public'
end
end
def loadLists
if @contactgroups.nil?
@contactgroups = ContactGroup.find_by_user(logged_user)
end
end
end
require 'tempfile'
class ContactsController < ApplicationController
before_filter :check_current_user,:selected_folder, :get_current_folders
before_filter :get_contacts, :only => [:index]
def index
end
#problem http://binary10ve.blogspot.com/2011/05/migrating-to-rails-3-got-stuck-with.html
#def destroy
# @current_user.contacts.find(params[:id]).destroy
# redirect_to(contacts_path)
#end
def new
@contact = Contact.new
end
def edit
@contact = @current_user.contacts.find(params[:id])
render 'edit'
end
def create
if params["compose_to_selected"]
if params["items_ids"]
redirect_to compose_path(:cids => params["items_ids"])
return
end
end
if params["delete_selected"]
if params["items_ids"]
params["items_ids"].each do |id|
@current_user.contacts.find_by_id(id).destroy
end
end
redirect_to(contacts_path)
return
end
@contact = @current_user.contacts.build(params[:contact])
if @contact.valid?
@contact.save
flash[:success] = t(:was_created,:scope=>:contact)
redirect_to(contacts_path)
else
render 'new'
end
end
def update
@contact = @current_user.contacts.find(params[:id])
if @contact.update_attributes(params[:contact])
redirect_to(contacts_path)
else
render 'edit'
end
end
def import_export
if params["export"]
contacts = @current_user.contacts
s = ""
contacts.each do |c|
s += c.export + "\r\n"
end
headers['Content-type'] = "text/csv"
headers['Content-Disposition'] = %(attachment; filename="contacts.csv")
render :text => s
return
elsif params["import"]
begin
raise t(:no_file_chosen,:scope=>:common) if not params[:upload]
raise t(:no_tmp_dir,:scope=>:common) if not File.exists?($defaults["msg_upload_dir"])
tmp_file = Tempfile.new($defaults["contact_tmp_filename"],$defaults["msg_upload_dir"])
tmp_file.write(params[:upload][:datafile].read)
tmp_file.flush
tmp_file.rewind
tmp_file.readlines.each do |line|
next if line =~ /^#/
Contact.import(@current_user,line)
end
rescue ActiveRecord::RecordInvalid => e
flash[:error] = {:title => e.to_s,:info => e.record.inspect + e.record.errors.inspect}
rescue Exception => e
flash[:error] = e.to_s
else
flash[:success] = t(:were_imported,:scope=>:contact)
end
end
redirect_to :action => 'index'
end
####################################### protected section ################################
protected
####################################### private section ##################################
private
def get_contacts
@contacts = Contact.getPageForUser(@current_user,params[:page],params[:sort_field],params[:sort_dir])
end
end

182
app/controllers/folders_controller.rb Normal file → Executable file
View File

@ -1,24 +1,172 @@
require 'ezcrypto'
require 'imap_mailbox'
require 'imap_session'
class FoldersController < ApplicationController
include ImapUtils
before_filter :login_required
before_filter :load_imap_session
after_filter :close_imap_session
include ImapMailboxModule
include ImapSessionModule
layout 'public'
before_filter :check_current_user,:selected_folder, :get_current_folders
def index
@folders = @mailbox.folders
end
before_filter :open_imap_session, :except => [:index,:show_hide,:system]
after_filter :close_imap_session, :except => [:index,:show_hide,:system]
def create
@mailbox.create_folder(CDF::CONFIG[:mail_inbox] + '.' + params[:folder])
redirect_to folders_path
end
before_filter :get_folders
#before_filter :prepare_buttons_to_folders
#theme :theme_resolver
def index
#before_filter
end
def create
if params[:folder][:target].empty?
flash[:warning] = t(:to_create_empty,:scope=>:folder)
render "index"
else
begin
#TODO recreate local copy of folders
if params[:folder][:parent].empty?
@mailbox.create_folder(params[:folder][:target])
else
parent_folder = @current_user.folders.find(params[:folder][:parent])
if parent_folder.depth >= $defaults["mailbox_max_parent_folder_depth"].to_i
raise Exception, t(:max_depth,:scope=>:folder)
end
@mailbox.create_folder(parent_folder.full_name + parent_folder.delim + params[:folder][:target])
end
rescue Exception => e
flash[:error] = t(:can_not_create,:scope=>:folder) + ' (' + e.to_s + ')'
render 'index'
return
end
flash[:success] = t(:was_created,:scope=>:folder)
redirect_to :action => 'index'
end
end
def delete
if params[:folder][:delete].empty?
flash[:warning] = t(:to_delete_empty,:scope=>:folder)
render "index"
else
begin
folder = @current_user.folders.find(params[:folder][:delete])
if @folders_system.include?(folder)
raise Exception, t(:system,:scope=>:folder)
end
@mailbox.delete_folder(folder.full_name)
if @current_folder.eql? folder
session[:selected_folder] = nil
end
folder.destroy
rescue Exception => e
flash[:error] = t(:can_not_delete,:scope=>:folder) + ' (' + e.to_s + ')'
render 'index'
return
end
flash[:success] = t(:was_deleted,:scope=>:folder)
redirect_to :action => 'index'
end
end
def system
logger.custom('sss',params[:folder].inspect)
@folders.each do |f|
logger.custom('s',f.inspect)
if f.isSystem?
f.setNone
end
if f.id == params[:folder][:mailbox_inbox].to_i
f.setInbox
end
if f.id == params[:folder][:mailbox_sent].to_i
f.setSent
end
if f.id == params[:folder][:mailbox_trash].to_i
f.setTrash
end
if f.id == params[:folder][:mailbox_drafts].to_i
f.setDrafts
end
end
redirect_to :action => 'index'
end
def refresh
# TODO save system folders
if params[:refresh]
Folder.refresh(@mailbox,@current_user)
flash.keep
elsif params[:show_hide]
if !params["folders_to_show"].nil?
@folders.each do |f|
if params["folders_to_show"].include?(f.id.to_s)
f.shown = true
f.save
else
f.shown = false
f.save
end
end
end
end
redirect_to :action => 'index'
end
def select
session[:selected_folder] = params[:id]
redirect_to :controller => 'messages', :action => 'index'
end
def refresh_status
@folders_shown.each do |f|
@mailbox.set_folder(f.full_name)
folder_status = @mailbox.status
f.update_attributes(:total => folder_status['MESSAGES'], :unseen => folder_status['UNSEEN'])
end
redirect_to :controller=> 'messages', :action => 'index'
end
def emptybin
begin
trash_folder = @current_user.folders.trash.first
if trash_folder.nil?
raise Exception, t(:not_configured_trash,:scope=>:folder)
end
@mailbox.set_folder(trash_folder.full_name)
trash_folder.messages.each do |m|
@mailbox.delete_message(m.uid)
end
@mailbox.expunge
trash_folder.messages.destroy_all
trash_folder.update_attributes(:unseen => 0, :total => 0)
rescue Exception => e
flash[:error] = "#{t(:imap_error,:scope=>:common)} (#{e.to_s})"
end
redirect_to :controller => 'messages', :action => 'index'
end
############################################# protected section #######################################
protected
#def prepare_buttons_to_folders
#@buttons = []
#@buttons << {:text => 'show_hide',:scope=>'folder',:image => 'flag.png'}
#@buttons << {:text => 'refresh',:scope=>'folder',:image => 'refresh.png'}
#end
def get_folders
@folders = @current_user.folders
@folders_shown = @current_user.folders.shown
@folders_system = @current_user.folders.sys
@current_user.folders.inbox.first.nil? ? @folder_inbox = "" : @folder_inbox = @current_user.folders.inbox.first.id
@current_user.folders.drafts.first.nil? ? @folder_drafts = "" : @folder_drafts = @current_user.folders.drafts.first.id
@current_user.folders.sent.first.nil? ? @folder_sent = "" : @folder_sent = @current_user.folders.sent.first.id
@current_user.folders.trash.first.nil? ? @folder_trash = "" : @folder_trash = @current_user.folders.trash.first.id
end
def destroy
@mailbox.delete_folder params[:id]
redirect_to folders_path
end
end

View File

@ -0,0 +1,61 @@
class InternalController < ApplicationController
before_filter :check_current_user ,:selected_folder, :get_current_folders, :only => [:about]
#theme :theme_resolver
layout "simple"
ERRORS = [
:internal_server_error,
:not_found,
:unprocessable_entity,
:allready_configured
].freeze
ERRORS.each do |e|
define_method e do
@title = t(e,:scope=>:internal)
flash[:error] = t(e,:scope=>:internal)
render 'error'
end
end
def error
@title = t(:unspecified_error,:scope=>:internal)
@error = params[:error] || t(:unspecified_error,:scope=>:internal)
end
def imaperror
@title = t(:imap_error,:scope => :internal)
@error = params[:error] || t(:unspecified_error, :scope => :internal)
logger.error "!!! InternalControllerImapError: " + @error
render 'error'
end
def loginfailure
reset_session
flash[:error] = t(:login_failure,:scope=>:user)
@current_user = nil
redirect_to :controller=>'user', :action => 'login'
end
#def onlycanlogins
#reset_session
#flash[:error] = t(:only_can_logins,:scope=>:user)
#@current_user = nil
#redirect_to :controller=>'user', :action => 'login'
#end
#def onlycanlogins
#reset_session
#flash[:error] = t(:allready_configured,:scope=>:user)
#@current_user = nil
#redirect_to :controller=>'user', :action => 'login'
#end
def about
render 'internal/about', :layout => 'application'
end
end

View File

@ -0,0 +1,97 @@
require 'tempfile'
class LinksController < ApplicationController
before_filter :check_current_user,:selected_folder, :get_current_folders
before_filter :get_links, :only => [:index]
def index
end
def new
@link = Link.new
end
def edit
@link = @current_user.links.find(params[:id])
render 'edit'
end
def create
if params["delete_selected"]
if params["items_ids"]
params["items_ids"].each do |id|
@current_user.links.find_by_id(id).destroy
end
end
redirect_to(links_path)
return
end
@link = @current_user.links.build(params[:link])
if @link.valid?
@link.save
flash[:success] = t(:was_created,:scope=>:link)
redirect_to(links_path)
else
render 'new'
end
end
def update
@link = @current_user.links.find(params[:id])
if @link.update_attributes(params[:link])
redirect_to(links_path)
else
render 'edit'
end
end
def import_export
if params["export"]
links = @current_user.links
s = ""
links.each do |l|
s += l.export + "\r\n"
end
headers['Content-type'] = "text/csv"
headers['Content-Disposition'] = %(attachment; filename="links.csv")
render :text => s
return
elsif params["import"]
begin
raise t(:no_file_chosen,:scope=>:common) if not params[:upload]
raise t(:no_tmp_dir,:scope=>:common) if not File.exists?($defaults["msg_upload_dir"])
tmp_file = Tempfile.new($defaults["contact_tmp_filename"],$defaults["msg_upload_dir"])
tmp_file.write(params[:upload][:datafile].read)
tmp_file.flush
tmp_file.rewind
tmp_file.readlines.each do |line|
next if line =~ /^#/
Link.import(@current_user,line)
end
rescue ActiveRecord::RecordInvalid => e
flash[:error] = {:title => e.to_s,:info => e.record.inspect + e.record.errors.inspect}
rescue Exception => e
flash[:error] = e.to_s
else
flash[:success] = t(:were_imported,:scope=>:link)
end
end
redirect_to :action => 'index'
end
####################################### protected section ################################
protected
####################################### private section ##################################
private
def get_links
@links = Link.getPageForUser(@current_user,params[:page],params[:sort_field],params[:sort_dir])
end
end

View File

@ -1,65 +0,0 @@
require 'ezcrypto'
class LoginController < ApplicationController
def index
if not(logged_user.nil?)
redirect_to :controller =>"webmail", :action=>"index"
else
@login_user = Customer.new
end
end
def authenticate
if user = auth(params['login_user']["email"], params['login_user']["password"])
session["user"] = user.id
if CDF::CONFIG[:crypt_session_pass]
session["wmp"] = EzCrypto::Key.encrypt_with_password(CDF::CONFIG[:encryption_password], CDF::CONFIG[:encryption_salt], params['login_user']["password"])
else
# dont use crypt
session["wmp"] = params['login_user']["password"]
end
if session["return_to"]
redirect_to(session["return_to"])
session["return_to"] = nil
else
redirect_to :action=>"index"
end
else
@login_user = Customer.new
flash["error"] = t :wrong_email_or_password
redirect_to :action => "index"
end
end
def logout
reset_session
flash["status"] = t(:user_logged_out)
redirect_to :action => "index"
end
protected
def need_subdomain?() true end
def secure_user?() false end
private
def auth(email, password)
mailbox = IMAPMailbox.new
begin
mailbox.connect(email, password)
rescue
return nil
end
mailbox.disconnect
mailbox = nil
if user = Customer.find_by_email(email)
return user
else
# create record in database
user = Customer.create("email"=>email)
MailPref.create('customer_id' => user.id)
return user
end
end
end

View File

@ -0,0 +1,214 @@
require 'imap_session'
require 'imap_mailbox'
require 'imap_message'
require 'mail'
require 'mail_plugin_extension'
class MessagesController < ApplicationController
include ImapMailboxModule
include ImapSessionModule
include ImapMessageModule
include MessagesHelper
before_filter :check_current_user ,:selected_folder,:get_current_folders
before_filter :open_imap_session, :select_imap_folder
before_filter :prepare_compose_buttons, :only => [:compose]
before_filter :get_system_folders, :only => [:index]
before_filter :create_message_with_params, :only => [:compose]
#before_filter :prepare_multi1_buttons, :only => [:index,:show]
#before_filter :prepare_multi2_buttons, :only => [:index]
#before_filter :prepare_multi3_buttons, :only => [:show]
after_filter :close_imap_session
#theme :theme_resolver
def index
if @sent_folder.nil? || @drafts_folder.nil? || @inbox_folder.nil? || @trash_folder.nil?
flash[:warning] = t(:not_all_configured,:scope => :folder)
end
if @current_folder.nil?
flash[:warning] = t(:no_selected,:scope => :folder)
redirect_to :controller => 'folders', :action => 'index'
return
end
@messages = []
folder_status = @mailbox.status
@current_folder.update_attributes(:total => folder_status['MESSAGES'], :unseen => folder_status['UNSEEN'])
folder_status['MESSAGES'].zero? ? uids_remote = [] : uids_remote = @mailbox.fetch_uids
uids_local = @current_user.messages.where(:folder_id => @current_folder).collect(&:uid)
logger.custom('current_folder',@current_folder.inspect)
logger.custom('uids_local',uids_local.join(","))
logger.custom('uids_remote',uids_remote.join(","))
logger.custom('to_delete',(uids_local-uids_remote).join(","))
logger.custom('to_fetch',(uids_remote-uids_local).join(","))
(uids_local-uids_remote).each do |uid|
@current_folder.messages.find_by_uid(uid).destroy
end
(uids_remote-uids_local).each_slice($defaults["imap_fetch_slice"].to_i) do |slice|
messages = @mailbox.uid_fetch(slice, ImapMessageModule::IMAPMessage.fetch_attr)
messages.each do |m|
Message.createForUser(@current_user,@current_folder,m)
end
end
@messages = Message.getPageForUser(@current_user,@current_folder,params[:page],params[:sort_field],params[:sort_dir])
end
def compose
#before filter :prepare_compose_buttons, :create_message_with_params
@operation = :new
if params["cid"].present?
contact = @current_user.contacts.find_by_id(params["cid"])
if not contact.nil?
@message.to_addr = contact.email
end
elsif params["cids"].present?
contacts = []
params["cids"].each do |c|
contact = @current_user.contacts.find_by_id(c)
if not contact.nil?
contacts << contact.email
end
end
@message.to_addr = contacts.join(';')
end
end
def show
@images = []
@attachments = []
@text_part = nil
@html_part = nil
@message = @current_user.messages.where('folder_id = ? and uid = ?',@current_folder,params[:id]).first
@message.update_attributes(:unseen => false)
imap_message = @mailbox.fetch_body(@message.uid)
mail = Mail.new(imap_message)
@mail = Mail.new(imap_message)
@plain_header = mail.header.to_s
# FIXME missing fields and support arrays
#@from = mail.From.addrs.presence
#@to = mail.To.addrs.presence
@from = @message.from_addr
@to = @message.to_addr
#@cc = mail.cc
#@bcc = mail.bcc
#@subject = mail.Subject
@date = mail.date.presence
if mail.multipart? == true
if not mail.text_part.nil?
@text_part = mail.text_part.decoded_and_charseted
end
if not mail.html_part.nil?
@html_part = mail.html_part.decoded_and_charseted
end
attachments = mail.attachments
if not attachments.size.zero?
for idx in 0..attachments.size - 1
a = attachments[idx]
a.idx = idx
a.parent_id = @message.uid
if a.isImage? and @current_user.prefs.msg_image_view_as.to_sym.eql?(:thumbnail)
@images << a
else
@attachments << a
end
end
end
else
logger.custom('mail',mail.inspect)
part = mail
#part = Mail::Part.new(mail)
part.idx = 0
part.parent_id = @message.uid
if part.isText?
@text_part = part.decoded_and_charseted
elsif part.isImage? and @current_user.prefs.msg_image_view_as.to_sym.eql?(:thumbnail)
@images << part
elsif part.isHtml?
@html_part = part.decoded_and_charseted
else
@attachments << part
end
end
end
def html_body
message = @current_user.messages.where('folder_id = ? and uid = ?',@current_folder,params[:id]).first
mail = Mail.new(@mailbox.fetch_body(message.uid))
if mail.multipart?
@body = mail.html_part.decoded_and_charseted
else
@body = mail.decoded_and_charseted
end
if @body.nil?
@body = t(:no_body,:scope=>:message)
else
if @body=~/cid:([\w@\.]+)/
attachments = mail.attachments
if not attachments.size.zero?
for idx in 0..attachments.size - 1
@body.gsub!(/cid:#{attachments[idx].cid}/,attachment_download_path(message.uid,idx))
end
end
end
end
render 'html_body',:layout => 'html_body'
end
def attachment
attachments = []
message = @current_user.messages.where('folder_id = ? and uid = ?',@current_folder,params[:id]).first
mail = Mail.new(@mailbox.fetch_body(message.uid))
if mail.multipart? == true
attachments = mail.attachments
else
#attachments << Mail::Part.new(mail)
attachments << mail
end
a = attachments[params[:idx].to_i]
headers['Content-type'] = a.main_type + "/" + a.sub_type
headers['Content-Disposition'] = %(attachment; filename="#{a.filename}")
render :text => a.decoded
end
############################################# protected section ##########################################
protected
#def prepare_multi2_buttons
#@multi2_buttons = []
#@multi2_buttons << {:text => 'trash',:scope=>:message,:image => 'trash.png'}
#@multi2_buttons << {:text => 'set_unread',:scope=>:message,:image => 'unseen.png'}
#@multi2_buttons << {:text => 'set_read',:scope=>:message,:image => 'seen.png'}
#end
#def prepare_multi1_buttons
#@multi1_buttons = []
#@multi1_buttons << {:text => 'copy',:scope=>:message,:image => 'copy.png'}
#@multi1_buttons << {:text => 'move',:scope=>:message,:image => 'move.png'}
#end
#def prepare_multi3_buttons
#@multi3_buttons = []
#@multi3_buttons << {:text => 'show_header',:scope=>:show,:image => 'zoom.png'}
#@multi3_buttons << {:text => 'trash',:scope=>:show,:image => 'trash.png'}
#@multi3_buttons << {:text => 'reply',:scope=>:show,:image => 'reply.png'}
#end
end

View File

@ -0,0 +1,367 @@
require 'imap_session'
require 'imap_mailbox'
require 'imap_message'
require 'mail'
require 'mail_plugin_extension'
require 'net/smtp'
class MessagesOpsController < ApplicationController
include ImapMailboxModule
include ImapSessionModule
include ImapMessageModule
include MessagesHelper
before_filter :check_current_user ,:selected_folder,:get_current_folders
before_filter :open_imap_session, :select_imap_folder
before_filter :prepare_compose_buttons
before_filter :get_system_folders, :only => [:composed,:single,:multi]
before_filter :prepare_composed , :only => [:composed]
before_filter :create_message_with_params, :only=> [:composed,:single,:multi]
after_filter :close_imap_session
#theme :theme_resolver
############################################### single #####################################
def single
if params[:reply] or params[:reply_all]
reply
return
elsif params[:trash]
trash
elsif params[:move]
move
elsif params[:copy]
copy
end
redirect_to :controller => 'messages', :action => 'index'
end
############################################### multi ######################################
def multi
begin
if !params[:uids]
flash[:warning] = t(:no_selected,:scope=>:message)
elsif params[:set_unread]
set_unread
elsif params[:set_read]
set_read
elsif params[:trash]
trash
elsif params[:copy]
copy
elsif params[:move]
move
end
rescue Exception => e
flash[:error] = "#{t(:imap_error,:scope=>:internal)} (#{e.to_s})"
end
redirect_to :controller => 'messages', :action => 'index'
end
############################################### ################################################
def set_unread
params["uids"].each do |uid|
@mailbox.set_unread(uid)
@current_user.messages.where('folder_id = ? and uid = ?',@current_folder,uid).first.update_attributes(:unseen => 1)
end
end
def set_read
params["uids"].each do |uid|
@mailbox.set_read(uid)
@current_user.messages.where('folder_id = ? and uid = ?',@current_folder,uid).first.update_attributes(:unseen => 0)
end
end
def trash
if @trash_folder.nil?
flash[:warning] = t(:not_configured_trash, :scope=>:folder)
else
params["uids"].each do |uid|
@mailbox.move_message(uid,@trash_folder.full_name)
message = @current_folder.messages.find_by_uid(uid)
message.change_folder(@trash_folder)
end
@mailbox.expunge
@trash_folder.update_stats
@current_folder.update_stats
end
end
def copy
if params[:folder][:target].empty?
flash[:warning] = t(:no_selected,:scope=>:folder)
else
dest_folder = @current_user.folders.find(params[:folder][:target])
params["uids"].each do |uid|
@mailbox.copy_message(uid,dest_folder.full_name)
message = @current_folder.messages.find_by_uid(uid)
new_message = message.clone
new_message.folder_id = dest_folder.id
new_message.save
end
dest_folder.update_stats
@current_folder.update_stats
end
end
def move
if params[:folder][:target].empty?
flash[:warning] = t(:no_selected,:scope=>:folder)
else
dest_folder = @current_user.folders.find(params[:folder][:target])
logger.info "DEST: "+dest_folder.inspect
params["uids"].each do |uid|
logger.info "UID: "+uid
logger.info "DEST_FULL: "+dest_folder.full_name
@mailbox.move_message(uid,dest_folder.full_name)
message = @current_folder.messages.find_by_uid(uid)
logger.info "M: "+message.inspect
logger.info "UPDATE_DEST_BEFORE1: "+dest_folder.inspect
message.change_folder(dest_folder)
logger.info "UPDATE_DEST_BEFORE2: "+dest_folder.inspect
end
logger.info "UPDATE_DEST_BEFORE: "+dest_folder.inspect
@mailbox.expunge
dest_folder.update_stats
logger.info "UPDATE_DEST: "+dest_folder.inspect
@current_folder.update_stats
logger.info "UPDATE_CUT: "+@current_folder.inspect
end
end
def upload
begin
raise MailrException.new :cause=>:no_tmp_dir,:scope=>:common if not File.exists?($defaults["msg_upload_dir"])
raise MailrException.new :cause=>:no_file_chosen,:scope=>:common if not params[:upload]
@operation = :upload
name = params[:file][:data].original_filename
upload_dir = $defaults["msg_upload_dir"]
path = File.join(upload_dir, @current_user.username + "_" + name)
File.open(path, "wb") { |f| f.write(params[:file][:data].read) }
rescue MailrException => e
flash[:error] = t(e.message[:cause],:scope => e.message[:scope])
rescue Exception => e
flash[:error] = t(:general_error,:scope=>:internal) + " (" + e.class.name + " " + e.to_s + ")"
end
create_message_with_params
render 'messages/compose'
end
# Files uploaded from Internet Explorer:
#
#Internet Explorer includes the entire path of a file in the filename sent, so the original_filename routine will return something like:
#
#C:\Documents and Files\user_name\Pictures\My File.jpg
#
#instead of just:
#
#My File.jpg
#
#This is easily handled by File.basename, which strips out everything before the filename.
#
#def sanitize_filename(file_name)
# # get only the filename, not the whole path (from IE)
# just_filename = File.basename(file_name)
# # replace all none alphanumeric, underscore or perioids
# # with underscore
# just_filename.sub(/[^\w\.\-]/,'_')
#end
#
#Deleting an existing File:
#
#If you want to delete any existing file then its simple and need to write following code:
#
# def cleanup
# File.delete("#{RAILS_ROOT}/dirname/#{@filename}")
# if File.exist?("#{RAILS_ROOT}/dirname/#{@filename}")
# end
def composed
if params[:delete_marked] and params[:files]
params[:files].each do |filename|
path = File.join(Rails.root,$defaults["msg_upload_dir"],@current_user.username + "_" +filename)
File.delete(path) if File.exist?(path)
end
create_message_with_params
@operation = :new
render 'messages/compose'
return
elsif params[:upload]
upload
elsif params[:save]
save
elsif params[:sendout]
sendout
else
redirect_to :controller => 'messages', :action => 'index'
end
end
def sendout
begin
smtp_server = @current_user.servers.primary_for_smtp
raise MailrException.new :cause=>:not_configured_smtp,:scope => :compose if smtp_server.nil?
raise MailrException.new :cause=>:has_no_domain,:scope=>:user if @current_user.has_domain?.nil?
raise MailrException.new :cause=>:not_configured_sent,:scope=>:compose if @sent_folder.nil?
send_mail_message( smtp_server,
@current_user.has_domain?,
@current_user.login,
@current_user.get_cached_password(session),
@mail.to_s,
@current_user.email,
params[:message][:to_addr]
)
@mailbox.append(@sent_folder.full_name,@mail.to_s,[:Seen])
upload_dir = $defaults["msg_upload_dir"]
@attachments.each do |file|
path = File.join(upload_dir, @current_user.username + "_" + file[:name])
File.delete(path) if File.exist?(path)
end
rescue MailrException => e
flash[:error] = t(e.message[:cause],:scope => e.message[:scope])
rescue Exception => e
flash[:error] = t(:general_error,:scope=>:internal) + " (" + e.class.name + " " + e.to_s + ")"
else
flash[:success] = t(:was_sent,:scope => :compose)
redirect_to :controller => 'messages', :action => 'index'
return
end
@operation = :new
render 'messages/compose'
end
def save
begin
raise MailrException.new :cause=>:not_configured_drafts,:scope=>:folder if @drafts_folder.nil?
@mailbox.append(@drafts_folder.full_name,@mail.to_s,[:Seen])
if params[:olduid].present?
@mailbox.move_message(params[:olduid],@trash_folder.full_name)
@mailbox.expunge
end
rescue MailrException => e
flash[:error] = t(e.message[:cause],:scope => e.message[:scope])
rescue Exception => e
flash[:error] = t(:general_error,:scope=>:internal) + " (" + e.class.name + " " + e.to_s + ")"
else
@attachments.each do |filename|
path = File.join(Rails.root,filename[:name])
File.delete(path) if File.exist?(path)
end
flash[:success] = t(:was_saved,:scope => :compose)
end
redirect_to :controller => 'messages', :action => 'index'
end
#FIXME edit does not support attachments
def edit
#logger.info @current_folder.inspect
#logger.info params.inspect
old_message = @current_user.messages.where('folder_id = ? and uid = ?',@current_folder,params[:id]).first
@message = Message.new
@message.to_addr = old_message.to_addr
@message.subject = old_message.subject
imap_message = @mailbox.fetch_body(old_message.uid)
mail = Mail.new(imap_message)
if mail.multipart?
@message.body = mail.text_part.nil? ? "" : mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "")
else
@message.body = mail.decoded_and_charseted.gsub(/<\/?[^>]*>/, "")
end
@attachments = []
@operation = :edit
@olduid = old_message.uid
render 'messages/compose'
end
def reply
old_message = @current_user.messages.where('folder_id = ? and uid = ?',@current_folder,params[:uids].first).first
@message = Message.new
#@message.to_addr = old_message.from_addr
#@message.to_addr = old_message.from.first
#@message.subject = old_message.subject
imap_message = @mailbox.fetch_body(old_message.uid)
mail = Mail.new(imap_message)
@message.to_addr = mail.from.first
@message.subject = mail.subject
if params[:reply_all]
@message.cc_addr = mail.cc.join('; ')
end
if mail.multipart?
@message.body = mail.text_part.nil? ? "" : mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "")
else
@message.body = mail.decoded_and_charseted.gsub(/<\/?[^>]*>/, "")
end
@attachments = []
@operation = :reply
render 'messages/compose'
end
###################################### protected section #######################################
protected
def send_mail_message(smtp_server,domain,username,password,msgstr,from,to)
if smtp_server.auth.nil?
smtp = Net::SMTP.start(smtp_server.name, smtp_server.port, domain)
else
smtp = Net::SMTP.start(smtp_server.name, smtp_server.port, domain, username, password, smtp_server.auth)
end
smtp.send_message msgstr, from, to
smtp.finish
end
def prepare_composed
@mail = Mail.new
@mail.subject = params[:message][:subject]
@mail.from = @current_user.full_id
#TODO check if email address is valid if not get address from contacts
@mail.to = params[:message][:to_addr]
@mail.cc = params[:message][:cc_addr]
@mail.body = params[:message][:body]
@attachments = Dir.glob(File.join($defaults["msg_upload_dir"],@current_user.username + "*"))
@attachments.each do |a|
@mail.add_file :filename => File.basename(a.gsub(/#{@current_user.username}_/,"")), :content => File.read(a)
end
end
############################################ set_mail_defaults ####################################
def set_mail_defaults(user,server,session)
if server.auth.nil? or server.auth == 'none'
password = nil
authentication = nil
enable_starttls_auto = nil
openssl_verify_mode = nil
user_name = nil
else
password = user.get_cached_password(session)
authentication = server.auth
enable_starttls_auto = server.use_tls
openssl_verify_mode = OpenSSL::SSL::VERIFY_NONE
user_name = user.login
end
Mail.defaults do
delivery_method :smtp, {:address => server.name,
:port => server.port,
:domain => user.domain,
:user_name => user_name,
:password => password,
:authentication => authentication,
:enable_starttls_auto => enable_starttls_auto,
:openssl_verify_mode => openssl_verify_mode
}
end
end
end

View File

@ -0,0 +1,55 @@
class PrefsController < ApplicationController
before_filter :check_current_user,:selected_folder
before_filter :get_current_folders
before_filter :get_prefs, :only => [:look,:update_look]
#theme :theme_resolver
def update_look
if params[:prefs]
@prefs.update_attributes(params[:prefs])
end
flash[:success] = t(:were_saved,:scope=>:prefs)
redirect_to :action => 'look'
end
def update_servers
redirect_to :action => 'servers'
end
def update_identity
if params[:user]
@current_user.first_name = params[:user][:first_name]
@current_user.last_name = params[:user][:last_name]
@current_user.domain = params[:user][:domain]
if @current_user.valid?
@current_user.save
flash[:success] = t(:were_saved,:scope=>:prefs)
redirect_to :action => 'identity'
else
render 'prefs/identity'
end
end
end
def look
end
def identity
end
def servers
@servers = @current_user.servers
end
############################# protected section ##################################
def get_prefs
@prefs = @current_user.prefs
end
end

View File

@ -0,0 +1,91 @@
class UserController < ApplicationController
#theme :theme_resolver
layout "simple"
def login
# database empty redirect to setup screen
users = User.all
if users.count.zero?
redirect_to :controller => 'user', :action => 'setup'
return false
end
end
def logout
reset_session
flash[:success] = t(:logged_out,:scope=>:user)
redirect_to :action => "login"
end
def authenticate
# check if user can use application
if not $defaults["only_can_logins"].nil?
if not $defaults["only_can_logins"].include?(params[:user][:login])
flash[:error] = t(:only_can_logins,:scope=>:user)
redirect_to :action => 'login'
return false
end
end
user = User.find_by_login(params[:user][:login])
if user.nil?
flash[:error] = t(:login_failure,:scope=>:user)
redirect_to :action => 'login'
return false
else
session[:user_id] = user.id
user.set_cached_password(session,params[:user][:password])
if session["return_to"]
redirect = session["return_to"]
session["return_to"] = nil
redirect_to(redirect)
else
redirect_to :controller=> 'messages', :action=> 'index'
end
end
end
#def loginfailure
#end
def setup
users = User.all
if !users.count.zero?
redirect_to :controller => 'internal', :action => 'allready_configured'
return false
end
@user = User.new
@server = Server.new
end
def create
@user = User.new
@user.login = params[:user][:login]
@user.first_name = params[:user][:first_name]
@user.last_name = params[:user][:last_name]
@server = Server.new
@server.name = params[:server][:name]
if @user.valid? and @server.valid?
@user.save
#@server.user_id = @user.id
#@server.save
Prefs.create_default(@user)
Server.create_server(@user,@server.name)
flash[:success] = t(:setup_done,:scope=>:user)
redirect_to :action => 'login'
else
render "setup"
end
end
end

View File

@ -1,417 +0,0 @@
require 'cdfmail'
require 'net/smtp'
require 'net/imap'
require 'mail2screen'
require 'ezcrypto'
class WebmailController < ApplicationController
include ImapUtils
# Administrative functions
before_filter :login_required
before_filter :obtain_cookies_for_search_and_nav, :only=>[:messages]
before_filter :load_imap_session
after_filter :close_imap_session
layout "public", :except => [:view_source, :download]
# model :filter, :expression, :mail_pref, :customer
BOOL_ON = "on"
def index
redirect_to(:action=>"messages")
end
def error_connection
end
def refresh
@mailbox.reload
@folders = @mailbox.folders
redirect_to(:action=>'messages')
end
def messages
session["return_to"] = nil
@search_field = params['search_field']
@search_value = params['search_value']
# handle sorting - tsort session field contains last reverse or no for field
# and lsort - last sort field
if session['tsort'].nil? or session['lsort'].nil?
session['lsort'] = "DATE"
session['tsort'] = {"DATE" => true, "FROM" => true, "SUBJECT" => true, "TO" => false}
end
case operation_param
when t(:copy) # copy
msg_ids = []
messages_param.each { |msg_id, bool|
msg_ids << msg_id.to_i if bool == BOOL_ON and dst_folder != @folder_name } if messages_param
folder.copy_multiple(msg_ids, dst_folder) if msg_ids.size > 0
when t(:move) # move
msg_ids = []
messages_param.each { |msg_id, bool|
msg_ids << msg_id.to_i if bool == BOOL_ON and dst_folder != @folder_name } if messages_param
folder.move_multiple(msg_ids, dst_folder) if msg_ids.size > 0
when t(:delete) # delete
msg_ids = []
messages_param.each { |msg_id, bool| msg_ids << msg_id.to_i if bool == BOOL_ON } if messages_param
folder.delete_multiple(msg_ids) if msg_ids.size > 0
when t(:mark_read) # mark as read
messages_param.each { |msg_id, bool| msg = folder.mark_read(msg_id.to_i) if bool == BOOL_ON } if messages_param
when t(:mark_unread) # mark as unread
messages_param.each { |msg_id, bool| msg = folder.mark_unread(msg_id.to_i) if bool == BOOL_ON } if messages_param
when "SORT"
session['lsort'] = sort_query = params["scc"]
session['tsort'][sort_query] = (session['tsort'][sort_query]? false : true)
@search_field, @search_value = session['search_field'], session['search_value']
when t(:search) # search
session['search_field'] = @search_field
session['search_value'] = @search_value
when t(:show_all) # search
session['search_field'] = @search_field = nil
session['search_value'] = @search_value = nil
else
# get search criteria from session
@search_field = session['search_field']
@search_value = session['search_value']
end
sort_query = session['lsort']
reverse_sort = session['tsort'][sort_query]
query = ["ALL"]
@page = params["page"]
@page ||= session['page']
session['page'] = @page
if @search_field and @search_value and not(@search_field.strip() == "") and not(@search_value.strip() == "")
@pages = Paginator.new self, 0, get_mail_prefs.wm_rows, @page
@messages = folder.messages_search([@search_field, @search_value], sort_query + (reverse_sort ? ' desc' : ' asc'))
else
@pages = Paginator.new self, folder.total, get_mail_prefs.wm_rows, @page
@messages = folder.messages(@pages.current.first_item - 1, get_mail_prefs.wm_rows, sort_query + (reverse_sort ? ' desc' : ' asc'))
end
end
def delete
@msg_id = msg_id_param.to_i
folder.delete(@msg_id)
redirect_to(:action=>"messages")
end
def reply # not ready at all
@msg_id = msg_id_param.to_i
@imapmail = folder.message(@msg_id)
fb = @imapmail.full_body
@tmail = TMail::Mail.parse(fb)
@mail = prepare_mail
@mail.reply(@tmail, fb, get_mail_prefs.mail_type)
render :action => 'compose'
end
def forward
@msg_id = msg_id_param.to_i
@imapmail = folder.message(@msg_id)
fb = @imapmail.full_body
@tmail = TMail::Mail.parse(fb)
@mail = prepare_mail
@mail.forward(@tmail, fb)
render :action => 'compose'
end
def compose
if @mail.nil?
operation = operation_param
if operation == t(:send)
@mail = create_mail
encmail = @mail.send_mail
get_imap_session
@mailbox.message_sent(encmail)
# delete temporary files (attachments)
@mail.delete_attachments()
render :action => :mailsent
elsif operation == t(:add)
@mail = create_mail
if params['attachment']
attachment = CDF::Attachment.new(@mail)
attachment.file = params['attachment']
end
else
# default - new email create
@mail = create_mail
end
end
end
def empty # empty trash folder (works for any one else :-))
folder.messages(0, -1).each{ |message|
folder.delete(message)
}
folder.expunge
redirect_to(:action=>"messages")
end
def message
@msg_id = msg_id_param
@imapmail = folder.message(@msg_id)
folder.mark_read(@imapmail.uid) if @imapmail.unread
@mail = TMail::Mail.parse(@imapmail.full_body)
end
def download
msg_id = msg_id_param
imapmail = folder.message(msg_id)
mail = TMail::Mail.parse(imapmail.full_body)
if mail.multipart?
get_parts(mail).each { |part|
return send_part(part) if part.header and part.header['content-type']['name'] == params['ctype']
}
render("webmail/noattachment")
else
render("webmail/noattachment")
end
end
def prefs
@customer = Customer.find(logged_customer)
@mailpref = MailPref.find_or_create_by_customer_id logged_customer
if params['op'] == _('Save')
if params['customer']
@customer.fname = params['customer']['fname']
@customer.lname = params['customer']['lname']
@customer.save
end
@mailpref.attributes = params["mailpref"]
@mailpref.save
session["wmimapseskey"] = nil
redirect_to(:action=>"messages")
end
end
# Message filters management
def filters
end
def filter
if params['op']
@filter = Filter.new(params['filter'])
@filter.customer_id = logged_customer
params['expression'].each { |index, expr| @filter.expressions << Expression.new(expr) unless expr["expr_value"].nil? or expr["expr_value"].strip == "" }
case params['op']
when _('Add')
@filter.expressions << Expression.new
when _('Save')
if params['filter']['id'] and params['filter']['id'] != ""
@sf = Filter.find(params['filter']['id'])
@sf.name, @sf.destination_folder = @filter.name, @filter.destination_folder
@sf.expressions.each{|expr| Expression.delete(expr.id) }
@filter.expressions.each {|expr| @sf.expressions << Expression.create(expr.attributes) }
else
@sf = Filter.create(@filter.attributes)
@sf.order_num = @user.filters.size
@filter.expressions.each {|expr| @sf.expressions << Expression.create(expr.attributes) }
end
# may be some validation will be needed
@sf.save
@user.serialize_to_file
return redirect_to(:action=>"filters")
end
@expressions = @filter.expressions
else
@filter = Filter.find(params["id"]) if params["id"]
@expressions = @filter.expressions
end
@destfolders = get_to_folders
end
def filter_delete
Filter.delete(params["id"])
# reindex other filters
@user = Customer.find(logged_customer)
findex = 0
@user.filters.each { |filter|
findex = findex + 1
filter.order_num = findex
filter.save
}
@user.serialize_to_file
redirect_to :action=>"filters"
end
def filter_up
filt = @user.filters.find(params['id'])
ufilt = @user.filters.find_all("order_num = #{filt.order_num - 1}").first
ufilt.order_num = ufilt.order_num + 1
filt.order_num = filt.order_num - 1
ufilt.save
filt.save
@user.serialize_to_file
redirect_to :action=>"filters"
end
def filter_down
filt = Filter.find(params["id"])
dfilt = @user.filters[filt.order_num]
dfilt.order_num = dfilt.order_num - 1
filt.order_num = filt.order_num + 1
dfilt.save
filt.save
@user.serialize_to_file
redirect_to :action=>"filters"
end
def filter_add
@filter = Filter.new
@filter.expressions << Expression.new
@expressions = @filter.expressions
@destfolders = get_to_folders
render "filter"
end
# end of filters
def view_source
@msg_id = msg_id_param.to_i
@imapmail = folder.message(@msg_id)
@msg_source = CGI.escapeHTML(@imapmail.full_body).gsub("\n", "<br/>")
end
def auto_complete_for_mail_to
auto_complete_responder_for_contacts params[:mail][:to]
end
def auto_complete_for_mail_cc
auto_complete_responder_for_contacts params[:mail][:cc]
end
def auto_complete_for_mail_bcc
auto_complete_responder_for_contacts params[:mail][:bcc]
end
private
def auto_complete_responder_for_contacts(value)
# first split by "," and take last name
searchName = value.split(',').last.strip
# if there are 2 names search by them
if searchName.split.size > 1
fname, lname = searchName.split.first, searchName.split.last
conditions = ['customer_id = ? and LOWER(fname) LIKE ? and LOWER(lname) like ?', logged_customer, fname.downcase + '%', lname.downcase + '%']
else
conditions = ['customer_id = ? and LOWER(fname) LIKE ?', logged_customer, searchName.downcase + '%']
end
@contacts = Contact.find(:all, :conditions => conditions, :order => 'fname ASC',:limit => 8)
render :partial => 'contacts'
end
protected
def additional_scripts()
@additional_css = ["webmail/webmail"]
@additional_js = ["webmail"]
end
private
def get_to_folders
res = Array.new
@folders.each{|f| res << f unless f.name == CDF::CONFIG[:mail_sent] or f.name == CDF::CONFIG[:mail_inbox] }
res
end
def create_mail
m = CDF::Mail.new(user.mail_temporary_path)
if params["mail"]
ma = params["mail"]
m.body, m.content_type, m.from, m.to, m.cc, m.bcc, m.subject = ma["body"], ma["content_type"], ma["from"], ma["to"], ma["cc"], ma["bcc"], ma["subject"]
if params["att_files"]
att_files, att_tfiles, att_ctypes = params["att_files"], params["att_tfiles"], params["att_ctypes"]
att_files.each {|i, value|
att = CDF::Attachment.new(m)
att.filename, att.temp_filename, att.content_type = value, att_tfiles[i], att_ctypes[i]
}
end
else
m.from, m.content_type = user.friendlly_local_email, get_mail_prefs.mail_type
end
m.customer_id = logged_customer
m
end
def prepare_mail
m = CDF::Mail.new(user.mail_temporary_path)
m.from, m.content_type = user.friendlly_local_email, get_mail_prefs.mail_type
m
end
def send_part(part)
if part.content_type == "text/html"
disposition = "inline"
elsif part.content_type.include?("image/")
disposition = "inline"
else
disposition = "attachment"
end
headers['Content-Length'] = part.body.size
response.headers['Accept-Ranges'] = 'bytes'
headers['Content-type'] = part.content_type.strip
headers['Content-Disposition'] = disposition << %(; filename="#{part.header['content-type']['name']}")
render :text => part.body
end
def get_parts(mail)
parts = Array.new
parts << mail
mail.parts.each { |part|
if part.multipart?
parts = parts.concat(get_parts(part))
elsif part.content_type and part.content_type.include?("rfc822")
parts = parts.concat(get_parts(TMail::Mail.parse(part.body))) << part
else
parts << part
end
}
parts
end
def obtain_cookies_for_search_and_nav
@srch_class = ((cookies['_wmlms'] and cookies['_wmlms'] == 'closed') ? 'closed' : 'open')
@srch_img_src = ((cookies['_wmlms'] and cookies['_wmlms'] == 'closed') ? 'closed' : 'opened')
@ops_class = ((cookies['_wmlmo'] and cookies['_wmlmo'] == 'closed') ? 'closed' : 'open')
@ops_img_src = ((cookies['_wmlmo'] and cookies['_wmlmo'] == 'closed') ? 'closed' : 'opened')
end
###################################################################
### Some fixed parameters and session variables
###################################################################
def folder
@folders[@folder_name]
end
def msg_id_param
params["msg_id"]
end
def messages_param
params["messages"]
end
def dst_folder
params["cpdest"]
end
def operation_param
params["op"]
end
end

450
app/helpers/application_helper.rb Normal file → Executable file
View File

@ -1,138 +1,334 @@
# The methods added to this helper will be available to all templates in the application.
require 'iconv'
module ApplicationHelper
include NavigationHelper
protected
def format_datetime(datetime)
datetime.strftime "%d.%m.%Y %H:%M"
end
#def form_field(object,field,flabel,example,val)
#model_name = eval(object.class.model_name)
#html = ""
#html << "<div class=\"param_group\">"
#if not object.errors[field.to_sym].empty?
#html << "<div class=\"fieldWithErrors\">"
def errors_base(form_name)
errors = instance_variable_get("@#{form_name}").errors.on_base()
errors_out = ""
if errors
errors = [errors] unless errors.is_a? Array
errors.each do |e|
errors_out << "<span class=\"error\">#{e}</span>"
end
end
errors_out
end
#end
# Useful abstraction for form input fields - combines an input field with error message (if any)
# and writes an appropriate style (for errors)
# Usage:
# form_input :text_field, 'postform', 'subject'
# form_input :text_area, 'postform', 'text', 'Please enter text:', 'cols' => 80
# form_input :hidden_field, 'postform', 'topic_id'
def form_input(helper_method, form_name, field_name, prompt = field_name.capitalize, options = {})
case helper_method.to_s
when 'hidden_field'
self.hidden_field(form_name, field_name)
when /^.*button$/
<<-EOL
<tr><td class="button" colspan="2">
#{self.send(helper_method, form_name, prompt, options)}
</td></tr>
EOL
else
field = (
if :select == helper_method
self.send(helper_method, form_name, field_name, options.delete('values'), options)
elsif :collection_select == helper_method
self.send(helper_method, form_name, field_name, options.delete('collection'), options.delete('value_method'), options.delete('text_method'), options)
else
self.send(helper_method, form_name, field_name, options)
end)
errors = instance_variable_get("@#{form_name}").errors[field_name] unless instance_variable_get("@#{form_name}").nil?
errors = Array.new if errors.nil?
errors_out = ""
if errors
errors = [errors] unless errors.is_a? Array
errors.each do |e|
errors_out << "<span class=\"error\">#{e}</span>"
end
end
if options['class'] == 'two_columns'
<<-EOL
<tr class="two_columns">
<td class="prompt"><label>#{prompt}:</label></td>
<td class="value">#{field}#{errors_out}</td>
</tr>
EOL
else
<<-EOL
<tr><td class="prompt"><strong>#{prompt}:</strong></td></tr>
<tr><td class="value">#{field}#{errors_out}</td></tr>
EOL
end
end
end
#html << "<label class=\"label\">"
#flabel.nil? ? html << model_name.human_attribute_name(field) : html << t(flabel.to_sym)
#html << "</label>"
# Helper method that has the same signature as real input field helpers, but simply displays
# the value of a given field enclosed within <p> </p> tags.
# Usage:
# <%= form_input :read_only_field, 'new_user', 'name', _('user_name')) %>
def read_only_field(form_name, field_name, html_options)
"<span #{attributes(html_options)}>#{instance_variable_get('@' + form_name)[field_name]}</span>"
end
#if not object.errors[field.to_sym].empty?
#html << "<span class=\"error\"> "
#html << object.errors[field.to_sym].to_s
#html << "</span>"
#html << "</div>"
#end
#html << "<input id=\""
#html << object.class.name.downcase+"_"+field
#html << "\""
#html << " name=\"#{object.class.name.downcase}[#{field}]\""
#html << " type=\"text\" class=\"text_field\" value=\""
#value = val || object.instance_eval(field) || ""
#html << value
#html << "\"/>"
#html << "<span class=\"description\">"
#html << t(:example,:scope=>:common)
#html << ": "
#html << example
#html << "</span>"
#html << "</div>"
def submit_button(form_name, prompt, html_options)
%{<input name="submit" type="submit" value="#{prompt}" />}
end
#end
# Converts a hash to XML attributes string. E.g.:
# to_attributes('a' => 'aaa', 'b' => 1)
# => 'a="aaa" b="1" '
def attributes(hash)
hash.keys.inject("") { |attrs, key| attrs + %{#{key}="#{hash[key]}" } }
end
def initListClass
@itClass = 1
end
def popListClass
ret = getListClass
@itClass = @itClass + 1
return ret
end
def getListClass
return "even" if @itClass%2 == 0
return "odd" if @itClass%2 == 1
end
def get_meta_info
'<meta name="rating" content="General">'
'<meta name="robots" content="Index, ALL">'
'<meta name="description" content="">'
'<meta name="keywords" content="">'
'<meta name content="">'
end
def user
@user = Customer.find(@session["user"]) if @user.nil?
@user
end
#def show_param_view(object,field,value)
#model_name = eval(object.class.model_name)
#html = ""
#html << "<div class=\"group clearfix\">"
#html << "<label class=\"label\">#{model_name.human_attribute_name(field)}: </label>"
#html << value
#html << "</div>"
#html
#end
def link_main
link_to( t(:contacts), contacts_path)
end
#def area_field(object,field,flabel,example,val,cols,rows)
#model_name = eval(object.class.model_name)
#html = ""
#html << "<div class=\"group\">"
def alternator
if @alternator.nil?
@alternator = 1
end
@alternator = -@alternator
if @alternator == -1
return "even"
else
return "odd"
end
end
#if not object.errors[field.to_sym].empty?
#html << "<div class=\"fieldWithErrors\">"
#end
#html << "<label class=\"label\">"
#flabel.nil? ? html << model_name.human_attribute_name(field) : html << t(flabel.to_sym)
#html << "</label>"
#if not object.errors[field.to_sym].empty?
#html << "<span class=\"error\">"
#html << object.errors[field.to_sym].to_s
#html << "</span>"
#html << "</div>"
#end
#name = object.class.name.downcase + '[' + field + ']'
#id = object.class.name.downcase+"_"+field
#value = val || object.instance_eval(field) || ""
#html << "<textarea id=\"#{id}\" name=\"#{name}\" class=\"text_area\" cols=\"#{cols}\" rows=\"#{rows}\">#{value}</textarea>"
#desc = t(:example,:scope=>:common) + ": " + example
#html << "<span class=\"description\">#{desc}</span>"
#html << "</div>"
#end
#def form_button(text,image)
#html = ""
#html << "<div class=\"group\">"
#html << "<button class=\"button\" type=\"submit\">"
#html << "<img src=\""
#html << current_theme_image_path(image)
#html << "\" alt=\""
#html << t(text.to_sym)
#html << "\" />"
#html << t(text.to_sym)
#html << "</button></div>"
#end
#def single_action(text,scope,image)
#html = ""
#html << "<div class=\"actiongroup clearfix\">"
#html << "<button class=\"button\" name=\"#{text}\" type=\"submit\">"
#html << "<img src=\""
#html << current_theme_image_path(image)
#html << "\" alt=\""
#html << t(text.to_sym, :scope => scope.to_sym)
#html << "\" />"
#html << t(text.to_sym, :scope => scope.to_sym)
#html << "</button></div>"
#end
#def single_action_onclick(text,scope,image,onclick)
#html = ""
#html << "<div class=\"actiongroup clearfix\">"
#html << "<button class=\"button\" type=\"submit\" onclick=\"window.location='"
#html << onclick
#html << "'\">"
#html << "<img src=\""
#html << current_theme_image_path(image)
#html << "\" alt=\""
#html << t(text.to_sym, :scope => scope.to_sym)
#html << "\" />"
#html << t(text.to_sym, :scope => scope.to_sym)
#html << "</button>"
#html << "</div>"
#end
#def group_action(buttons)
#html = ""
#html << "<div class=\"actiongroup clearfix\">"
#buttons.each do |b|
#html << "<button class=\"button\" type=\"submit\" name=\"#{b[:text]}\">"
#html << "<img src=\""
#html << current_theme_image_path(b[:image])
#html << "\" alt=\""
#html << t(b[:text].to_sym,:scope=>b[:scope].to_sym)
#html << "\" />"
#html << t(b[:text].to_sym,:scope=>b[:scope].to_sym)
#html << "</button> "
#end
#html << "</div>"
#end
#def group_action_text(buttons,text)
#html = ""
#html << "<div class=\"group\">"
#buttons.each do |b|
#html << "<button class=\"button\" type=\"submit\" name=\"#{b[:text]}\">"
#html << "<img src=\""
#html << current_theme_image_path(b[:image])
#html << "\" alt=\""
#html << t(b[:text].to_sym,:scope=>b[:scope].to_sym)
#html << "\" />"
#html << t(b[:text].to_sym,:scope=>b[:scope].to_sym)
#html << "</button> "
#end
#html << text
#html << "</div>"
#end
#def form_buttons(buttons)
#html = ""
#html << "<div class=\"group\">"
#buttons.each do |b|
#html << "<button class=\"button\" type=\"submit\" name=\"#{b[:text]}\">"
#html << "<img src=\""
#html << current_theme_image_path(b[:image])
#html << "\" alt=\""
#html << t(b[:text].to_sym)
#html << "\" />"
#html << t(b[:text].to_sym)
#html << "</button> "
#end
#html << "</div>"
#end
#def form_button_value(text,image,onclick)
#html = ""
#html << "<div class=\"group\">"
#html << "<button class=\"button\" type=\"submit\" onclick=\"window.location='"
#html << onclick
#html << "'\">"
#html << "<img src=\""
#html << current_theme_image_path(image)
#html << "\" alt=\""
#html << text
#html << "\" />"
#html << t(text.to_sym)
#html << "</button></div>"
#end
#def simple_input_field(name,id,label,value)
#html = ""
#html << "<div class=\"param_group\">"
#html << "<label class=\"label\">#{label}</label>"
#html << "<input name=\"#{name}[#{id}]\" id=\"#{name}_#{id} class=\"text_field\" type=\"text\" value=\"#{value}\">"
#html << "</div>"
#end
#def select_field(name,object,label,blank)
#html = ""
#html << "<div class=\"group\">"
#html << "<label class=\"label\">#{label}</label>"
#html << select(name, name, object.all.collect {|p| [ p.name, p.id ] }, { :include_blank => (blank == true ? true : false)})
#html << "</div>"
#end
#def select_field_table(object,field,table_choices,choice,blank)
#model_name = eval(object.class.model_name)
#html = ""
#html << "<div class=\"param_group\">"
#html << "<label class=\"label\">#{model_name.human_attribute_name(field)}</label>"
#html << select(object.class.to_s.downcase, field, options_for_select(table_choices,choice), {:include_blank => blank})
#html << "</div>"
#end
#def select_field_table_t(object,field,table_choices,choice,blank)
#model_name = eval(object.class.model_name)
#html = ""
#html << "<div class=\"param_group\">"
#html << "<label class=\"label\">#{model_name.human_attribute_name(field)}</label>"
#t = []
#table_choices.each do |c|
#t << [t(c.to_sym,:scope=>:prefs),c.to_s]
#end
#html << select(object.class.to_s.downcase, field, options_for_select(t,choice), {:include_blank => blank})
#html << "</div>"
#end
##def form_simle_field(name,label,value)
## html = ""
## html << "<div class=\"group\">"
## html << "<label class=\"label\">#{label}</label>"
## html << "<input class=\"text_field\" type=\"text\" value=\"#{value}\">"
## html << "</div>"
##end
##def nav_to_folders
## link_to( t(:folders,:scope=>:folder), :controller=>:folders, :action=>:index )
##end
##
##def nav_to_messages
## link_to( t(:messages,:scope=>:message), :controller=>:messages, :action=>:index )
##end
##
##def nav_to_compose
## link_to( t(:compose,:scope=>:compose), :controller=>:messages, :action=>:compose )
##end
##
##def nav_to_contacts
## link_to( t(:contacts,:scope=>:contact), contacts_path )
##end
##
##def nav_to_prefs
## link_to( t(:prefs,:scope=>:prefs), prefs_look_path )
##end
#def single_navigation(label,scope)
#s = ""
#s += "<ul>"
#s += "<li class=\"first active\">#{link_to(t(label,:scope=>scope),'#')}</li>"
#s += "<li class=\"last\">&nbsp;</li>"
#s += "</ul>"
#end
#def main_navigation(active)
#instance_variable_set("@#{active}", "active")
#s = ""
#s += "<ul>"
#s += "<li class=\"first #{@messages_tab}\">#{link_to( t(:messages,:scope=>:message), messages_path )}</li>"
#s += "<li class=\"#{@compose_tab}\">#{link_to( t(:compose,:scope=>:compose), compose_path )}</li>"
#s += "<li class=\"#{@folders_tab}\">#{link_to( t(:folders,:scope=>:folder), folders_path )}</li>"
#s += "<li class=\"#{@contacts_tab}\">#{link_to( t(:contacts,:scope=>:contact), contacts_path )}</li>"
#s += "<li class=\"#{@prefs_tab}\">#{link_to( t(:prefs,:scope=>:prefs), prefs_look_path )}</li>"
#s += "<li class=\"last #{@links_tab}\">#{link_to( t(:links,:scope=>:link), links_path )}</li>"
#s += "</ul>"
#end
#def prefs_navigation(active)
#instance_variable_set("@#{active}", "active")
#s = ""
#s += "<ul>"
#s += "<li class=\"first #{@look_tab}\">#{link_to( t(:look,:scope=>:prefs), prefs_look_path )}</li>"
#s += "<li class=\"#{@identity_tab}\">#{link_to( t(:identity,:scope=>:prefs), prefs_identity_path )}</li>"
#s += "<li class=\"last #{@servers_tab}\">#{link_to( t(:servers,:scope=>:prefs), prefs_servers_path )}</li>"
#s += "</ul>"
#end
#def multi_select(id, name, objects, selected_objects, label, value,joiner,content = {})
#options = ""
#objects.each do |o|
#selected = selected_objects.include?(o) ? " selected=\"selected\"" : ""
#option_value = escape_once(o.send(value))
#text = [option_value]
#unless content[:text].nil?
#text = []
#content[:text].each do |t|
#text << o.send(t)
#end
#text = text.join(joiner)
#end
#text.gsub!(/^\./,'')
#bracket = []
#unless content[:bracket].nil?
#content[:bracket].each do |b|
#bracket << o.send(b)
#end
#bracket = bracket.join(joiner)
#end
#option_content = bracket.empty? ? "#{text}" : "#{text} (#{bracket})"
#options << "<option value=\"#{option_value}\"#{selected}>&nbsp;&nbsp;#{option_content}&nbsp;&nbsp;</option>\n"
#end
#"<div class=\"param_group\"><label class=\"label\">#{label}</label><select multiple=\"multiple\" size=10 id=\"#{id}\" name=\"#{name}\">\n#{options}</select></div>"
#end
#def force_charset(text)
#begin
#Iconv.iconv("UTF-8",$defaults["msg_unknown_charset"],text)
#rescue
#text
#end
#end
#def content_for_sidebar
#s = render :partial => 'sidebar/logo'
#s += render :partial => 'folders/list'
#s += render :partial => 'sidebar/calendar_view'
#s += render :partial => 'internal/version'
#s
#end
def boolean_answer(answer)
answer == true ? t(:true_answer,:scope=>:common) : t(:false_answer,:scope=>:common)
end
end

View File

@ -1,4 +0,0 @@
module ContactGroupHelper
def link_save() "/contact_group/save" end
def link_list() "/contact_group/list" end
end

20
app/helpers/contacts_helper.rb Normal file → Executable file
View File

@ -1,3 +1,17 @@
module ContactsHelper
end
module ContactsHelper
def contacts_table_header
html = ""
$defaults["contacts_table_fields"].each do |f|
html << "<th>"
if params[:sort_field] == f
params[:sort_dir].nil? ? dir = 'desc' : dir = nil
end
html << link_to(Contact.human_attribute_name(f), {:controller => 'contacts',:action => 'index',:sort_field => f,:sort_dir => dir}, {:class=>"header"})
html << "</th>"
end
html
end
end

72
app/helpers/folder_helper.rb Executable file
View File

@ -0,0 +1,72 @@
module FolderHelper
def folder_link(options={})
folder = options[:folder]
active = ""
if options[:active]
active = "icon-white"
end
folder.parent.empty? ? name = folder.name : name = folder.parent.gsub(/\./,'#') + "#" + folder.name
if folder.isInbox?
name_shown = "<i class=\"icon-inbox #{active}\"></i>" + t(:inbox_name,:scope => :folder)
elsif folder.isSent?
name_shown = "<i class=\"icon-plane #{active}\"></i>" + t(:sent_name,:scope => :folder)
elsif folder.isDrafts?
name_shown = "<i class=\"icon-book #{active}\"></i>" + t(:drafts_name,:scope => :folder)
elsif folder.isTrash?
name_shown = "<i class=\"icon-trash #{active}\"></i>" +t(:trash_name,:scope => :folder)
else
name_shown = "<i class=\"icon-none\"></i>" + folder.name.capitalize
end
if folder.isTrash?
if not folder.total.zero?
name_shown += " <button class=\"btn btn-mini btn-danger\" onclick=\"window.location='#{folders_emptybin_path}'\" href=\"#\">#{t(:emptybin,:scope=>:folder)}</button>"
#name_shown += raw link_to(t(:emptybin,:scope=>:folder),folders_emptybin_path)
#name_shown += ')'
end
else
if !folder.unseen.zero?
name_shown += ' (' + folder.unseen.to_s + ')'
end
end
link_to name_shown.html_safe, folders_select_path(:id => name)
end
def pretty_folder_name(folder)
if folder.nil?
t(:no_selected,:scope=>:folder)
else
if folder.isInbox?
t(:inbox_name,:scope => :folder)
elsif folder.isSent?
t(:sent_name,:scope => :folder)
elsif folder.isDrafts?
t(:drafts_name,:scope => :folder)
elsif folder.isTrash?
t(:trash_name,:scope => :folder)
else
folder.name.capitalize
end
end
end
def select_for_folders(name,id,collection,label,choice,blank)
html = ""
html << "<div class=\"param_group\">"
html << "<label class=\"label\">#{label}</label>"
html << simple_select_for_folders(name,id,collection,choice,blank)
html << "</div>"
end
def simple_select_for_folders(name,id,collection,choice,blank)
html = ""
html << select(name , id, options_from_collection_for_select(collection, 'id', 'full_name', choice),{ :include_blank => (blank == true ? true : false)})
html
end
end

View File

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

2
app/helpers/internal_helper.rb Executable file
View File

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

17
app/helpers/links_helper.rb Executable file
View File

@ -0,0 +1,17 @@
module LinksHelper
def links_table_header
html = ""
$defaults["links_table_fields"].each do |f|
html << "<th>"
if params[:sort_field] == f
params[:sort_dir].nil? ? dir = 'desc' : dir = nil
end
html << link_to(Link.human_attribute_name(f), {:controller => 'links',:action => 'index',:sort_field => f,:sort_dir => dir}, {:class=>"header"})
html << "</th>"
end
html
end
end

130
app/helpers/messages_helper.rb Executable file
View File

@ -0,0 +1,130 @@
module MessagesHelper
def size_formatter(size)
if size <= 2**10
"#{size} #{t(:bytes,:scope=>:common)}"
elsif size <= 2**20
sprintf("%.1f #{t(:kbytes,:scope=>:common)}",size.to_f/2**10)
else
sprintf("%.1f #{t(:mbytes,:scope=>:common)}",size.to_f/2**20)
end
end
def date_formatter(date)
date.nil? ? t(:no_date,:scope=>:message) : date.strftime("%Y-%m-%d %H:%M")
end
def address_formatter(addr,op)
return "" if addr.nil?
s = ""
return t(:no_address,:scope=>:message) if addr.empty?
length = $defaults["msg_address_length"].to_i
case op
when :index
logger.custom('addr',addr)
fs = addr.gsub(/\"/,"").split(/</)
fs[0].size.zero? ? s = fs[1] : s = fs[0]
s.length >= length ? s = s[0,length]+"..." : s
return h(s)
when :show
#addr = addr[0].charseted.gsub(/\"/,"")
return h(addr.gsub(/\"/,""))
when :raw
#fs = addr.gsub(/\"/,"").split(/</)
#fs[0].size.zero? ? s = fs[1] : s << fs[0] + " <" + fs[1] + ">"
s = h(addr)
return s
when :reply
return addr
end
end
def body_formatter(body,op)
case op
when :reply
s = "\n\n\n"
body.split(/\n/).each do |line|
s += '>' + line.strip + "\n"
end
s
when :edit
return body
when :plain
safe_body = h(body)
s = ""
safe_body.split(/\n/).each do |line|
s += line.gsub(/^\s+/,"") + "<br/>"
end
s.html_safe
end
end
def subject_formatter(message,op)
case op
when :index
if message.subject.nil? or message.subject.size.zero?
s = t(:no_subject,:scope=>:message)
else
length = $defaults["msg_subject_length"].to_i
message.subject.length >= length ? s = message.subject[0,length]+"..." : s = message.subject
end
link_to s,{:controller => 'messages', :action => 'show', :id => message.uid} , :title => message.subject
when :show
if message.subject.nil? or message.subject.size.zero?
t(:no_subject,:scope=>:message)
else
message.subject
end
when :reply
if message.nil? or message.size.zero?
t(:reply_string,:scope=>:show)
else
t(:reply_string,:scope=>:show) + " " + message
end
end
end
def attachment_formatter(message)
message.content_type =~ /^text\/plain/ ? "" : "<i class=\"icon-file\"></i>"
end
def headers_links
#if @current_folder.hasFullName?(@folder_sent_name) || @current_folder.hasFullName?(@folder_drafts_name)
if @current_folder == @sent_folder || @current_folder == @drafts_folder
fields = $defaults["msgs_sent_view_fields"]
else
fields = $defaults["msgs_inbox_view_fields"]
end
html = ""
fields.each do |f|
html << "<th>"
if params[:sort_field] == f
params[:sort_dir].nil? ? dir = 'desc' : dir = nil
end
html << link_to(Message.human_attribute_name(f), {:controller => 'messages',:action => 'index',:sort_field => f,:sort_dir => dir}, {:class=>"header"})
html << "</th>"
end
if @current_folder == @drafts_folder
html << "<th>&nbsp;</th>"
end
html
end
#def content_text_plain_for_render(text)
#html = "<pre class=\"clearfix\">"
##html << text.gsub!(/\r\n/,"\n")
#html << h(text)
#html << "</pre>"
#html
#end
def humanize_attr(object,attr)
model_name = eval(object.class.model_name)
return model_name.human_attribute_name(attr)
end
end

View File

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

View File

@ -1,60 +0,0 @@
module NavigationHelper
def link_back_to_messages
link_to("&#171;" << t(:back_to_message), :controller=>"webmail", :action=>"messages")
end
def link_send_mail
link_to( t(:compose), :controller=>"webmail", :action=>"compose")
end
def link_mail_prefs
link_to( t(:preferences), :controller=>"webmail", :action=>"prefs")
end
def link_mail_filters
link_to( t(:filters), :controller=>"webmail", :action=>"filters")
end
def folder_manage_link(folder)
if folder.name == CDF::CONFIG[:mail_trash] or folder.name == CDF::CONFIG[:mail_inbox] or folder.name == CDF::CONFIG[:mail_sent]
short_fn(folder)
else
short_fn(folder) + '&nbsp;' + link_to(t(:delete), folder_path(folder.name), :method => :delete)
end
end
def link_import_preview() "/contacts/import_preview" end
def link_main_index() root_url end
def link_contact_import() url_for(:controller => :contacts, :action => :import) end
def link_contact_choose() url_for(:controller => :contacts, :action => :choose) end
def link_contact_list
link_to(t(:list), :controller => :contacts, :action => :index)
end
def link_contact_add_one
link_to(t(:add_one_contact), new_contact_path)
end
def link_contact_add_multiple
link_to(t(:add_multiple), :controller => :contacts, :action => "add_multiple")
end
def link_contact_group_list
link_to(t(:groups), :controller => :contact_group, :action => :index)
end
def link_folders
link_to( t(:folders), :controller=>:webmail, :action=>:messages)
end
private
def short_fn(folder)
if folder.name.include? folder.delim
folder.name.split(folder.delim).last
else
folder.name
end
end
end

16
app/helpers/prefs_helper.rb Executable file
View File

@ -0,0 +1,16 @@
module PrefsHelper
def servers_table_header
html = ""
$defaults["servers_table_fields"].each do |f|
html << "<th>"
if params[:sort_field] == f
params[:sort_dir].nil? ? dir = 'desc' : dir = nil
end
html << link_to(Server.human_attribute_name(f), {:controller => 'prefs',:action => 'servers',:sort_field => f,:sort_dir => dir}, {:class=>"header"})
html << "</th>"
end
html
end
end

2
app/helpers/user_helper.rb Executable file
View File

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

View File

@ -1,166 +0,0 @@
require 'cdfutils'
require 'mail2screen'
module WebmailHelper
include Mail2Screen
def link_compose_new
link_to(t(:compose_txt), :controller=>"webmail", :action=>"compose")
end
def link_refresh
link_to(t(:refresh), :controller=>"webmail", :action=>"refresh")
end
def link_message_list
link_to(_('Message list'), :controller=>"webmail", :action=>"messages")
end
def link_reply_to_sender(msg_id)
link_to(t(:reply), :controller=>"webmail", :action=>"reply", :params=>{"msg_id"=>msg_id})
end
def link_forward_message(msg_id)
link_to(t(:forward), :controller=>"webmail", :action=>"forward", :params=>{"msg_id"=>msg_id})
end
def link_flag_for_deletion(msg_id)
link_to(t(:delete), :controller=>"webmail", :action=>"delete", :params=>{"msg_id"=>msg_id})
end
def link_view_source(msg_id)
link_to(t(:view_source), {:controller=>"webmail", :action=>"view_source", :params=>{"msg_id"=>msg_id}}, {'target'=>"_blank"})
end
def link_filter_add
link_to(t(:add_filter), :controller=>'webmail', :action=>'filter_add')
end
def folder_link(folder)
return folder.name if folder.attribs.include?(:Noselect)
folder_name = short_fn(folder)
folder_name = t(folder_name.downcase.to_sym, :default => folder_name)
title = folder.unseen > 0 ? "#{folder_name} (#{folder.unseen})" : "#{folder_name}"
link = link_to title, :controller => 'webmail', :action => 'messages', :folder_name => folder.name
link = content_tag('b', link) if folder.name == @folder_name
link += '&nbsp;' + empty_trash_link(folder.name) if folder.trash?
link
end
def message_date(datestr)
t = Time.now
begin
if datestr.kind_of?(String)
d = (Time.rfc2822(datestr) rescue Time.parse(value)).localtime
else
d = datestr
end
if d.day == t.day and d.month == t.month and d.year == t.year
d.strftime("%H:%M")
else
d.strftime("%Y-%m-%d")
end
rescue
begin
d = imap2time(datestr)
if d.day == t.day and d.month == t.month and d.year == t.year
d.strftime("%H:%M")
else
d.strftime("%Y-%m-%d")
end
rescue
datestr
end
end
end
def attachment(att, index)
ret = "#{att.filename}"
# todo: add link to delete attachment
#ret <<
ret << "<input type='hidden' name='att_files[#{index}]' value='#{att.filename}'/>"
ret << "<input type='hidden' name='att_tfiles[#{index}]' value='#{att.temp_filename}'/>"
ret << "<input type='hidden' name='att_ctypes[#{index}]' value='#{att.content_type}'/>"
end
def link_filter_up(filter_id)
link_to(_('Up'), :controller=>"webmail", :action=>"filter_up", :id=>filter_id)
end
def link_filter_down(filter_id)
link_to(_('Down'), :controller=>"webmail", :action=>"filter_down", :id=>filter_id)
end
def link_filter_edit(filter_id)
link_to(_('Edit'), :controller=>"webmail", :action=>"filter", :id=>filter_id)
end
def link_filter_delete(filter_id)
link_to(_('Delete'), :controller=>"webmail", :action=>"filter_delete", :id=>filter_id)
end
def page_navigation_webmail(pages)
nav = "<p class='paginator'><small>"
nav << "(#{pages.length} #{t :pages}) &nbsp; "
window_pages = pages.current.window.pages
nav << "..." unless window_pages[0].first?
for page in window_pages
if pages.current == page
nav << page.number.to_s << " "
else
nav << link_to(page.number, :controller=>"webmail", :action=>'messages', :page=>page.number) << " "
end
end
nav << "..." unless window_pages[-1].last?
nav << " &nbsp; "
nav << link_to(t(:first), :controller=>"webmail", :action=>'messages', :page=>@pages.first.number) << " | " unless @pages.current.first?
nav << link_to(t(:prev), :controller=>"webmail", :action=>'messages', :page=>@pages.current.previous.number) << " | " if @pages.current.previous
nav << link_to(t(:next), :controller=>"webmail", :action=>'messages', :page=>@pages.current.next.number) << " | " if @pages.current.next
nav << link_to(t(:last), :controller=>"webmail", :action=>'messages', :page=>@pages.last.number) << " | " unless @pages.current.last?
nav << "</small></p>"
return nav
end
def parse_subject(subject)
begin
if mime_encoded?(subject)
if mime_decode(subject) == ''
_('(No subject)')
else
mime_decode(subject)
end
else
if from_qp(subject) == ''
_('(No subject)')
else
from_qp(subject)
end
end
rescue Exception => ex
RAILS_DEFAULT_LOGGER.debug('Exception occured - #{ex}')
return ""
end
end
def message_size(size)
if size / (1024*1024) > 0
return "#{(size / (1024*1024)).round}&nbsp;MB"
elsif size / 1024 > 0
return "#{(size / (1024)).round}&nbsp;KB"
else
return "#{size}&nbsp;B"
end
end
private
def empty_trash_link(folder_name)
link_to( "(#{t :empty})",
{ :controller => "webmail", :action => "empty", :params=>{"folder_name"=>folder_name}},
:confirm => t(:want_to_empty_trash_message))
end
end

0
app/mailers/.gitkeep Executable file
View File

0
app/models/.gitkeep Executable file
View File

116
app/models/contact.rb Normal file → Executable file
View File

@ -1,71 +1,45 @@
require 'cdfutils'
require_association 'contact_group'
class Contact < ActiveRecord::Base
has_and_belongs_to_many :groups, :class_name => "ContactGroup", :join_table => "contact_contact_groups", :association_foreign_key => "contact_group_id", :foreign_key => "contact_id"
# Finder methods follow
def Contact.find_by_user(user_id)
find(:all, :conditions => ["customer_id = ?", user_id], :order => "fname asc", :limit => 10)
end
def Contact.find_by_user_email(user_id, email)
find(:first, :conditions => ["customer_id = #{user_id} and email = ?", email])
end
def Contact.find_by_group_user(user_id, grp_id)
result = Array.new
find(:all, :conditions => ["customer_id = ?", user_id], :order => "fname asc").each { |c|
begin
c.groups.find(grp_id)
result << c
rescue ActiveRecord::RecordNotFound
end
}
result
end
named_scope :for_customer, lambda{ |customer_id| {:conditions => {:customer_id => customer_id}} }
named_scope :letter, lambda{ |letter| {:conditions => ["contacts.fname LIKE ?", "#{letter}%"]} }
def Contact.find_by_user_letter(user_id, letter)
find_by_sql("select * from contacts where customer_id=#{user_id} and substr(UPPER(fname),1,1) = '#{letter}' order by fname")
end
def full_name
"#{fname}&nbsp;#{lname}"
end
def show_name
"#{fname} #{lname}"
end
def full_address
"#{fname} #{lname}<#{email}>"
end
protected
def validate
errors.add 'fname', I18n.t(:validate_fname_error) unless self.fname =~ /^.{2,20}$/i
errors.add 'lname', I18n.t(:validate_lname_error) unless self.lname =~ /^.{2,20}$/i
# Contact e-mail cannot be changed
unless self.new_record?
old_record = Contact.find(self.id)
errors.add 'email', I18n.t(:contacto_cannot_be_changed) unless old_record.email == self.email
end
end
def validate_on_create
# Contact e-mail cannot be changed, so we only need to validate it on create
errors.add 'email', I18n.t(:validate_email_error) unless valid_email?(self.email)
# Already existing e-mail in contacts for this user is not allowed
if self.new_record?
if Contact.find :first, :conditions => {:email => email, :customer_id => customer_id}
errors.add('email', I18n.t(:email_exists))
end
end
end
end
class Contact < ActiveRecord::Base
validates_length_of :name, :within => 3..20
validates_length_of :email, :within => 5..50
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates_length_of :info, :maximum => 100
validate :check_unique_name, :on => :create
default_scope :order => 'name ASC'
belongs_to :user
def self.getPageForUser(user,page,sort_field,sort_dir)
if sort_field
if Contact.attribute_method?(sort_field) == true
order = sort_field
sort_dir == 'desc' ? order += ' desc' : sort_dir
end
end
Contact.paginate :page => page , :per_page => $defaults["contacts_per_page"], :conditions=> ['user_id = ?', user.id],:order => order
end
def check_unique_name
if !Contact.where('upper(name) = ? and user_id = ?',name.upcase,user_id).size.zero?
errors.add(:name, :not_unique)
end
end
def export
fields = []
fields << name || ""
fields << email || ""
fields << info || ""
fields.join(';')
end
def self.import(user,line)
fields = line.split(/;/)
contact = user.contacts.build( :name => fields[0].strip,
:email => fields[1].strip,
:info => fields[2].strip)
contact.save!
end
end

View File

@ -1,25 +0,0 @@
class ContactGroup < ActiveRecord::Base
has_and_belongs_to_many :contacts, :class_name => "Contact", :join_table => "contact_contact_groups", :association_foreign_key => "contact_id", :foreign_key => "contact_group_id"
def ContactGroup.find_by_user(user_id)
find_by_sql("select * from contact_groups where customer_id = #{user_id} order by name asc")
end
protected
def validate
errors.add('name', :contactgroup_name_invalid) unless self.name =~ /^.{1,50}$/i
end
def validate_on_create
if ContactGroup.find_first(["name = '#{name}' and customer_id = #{user_id}"])
errors.add("name", _('Please enter group name (1 to 50 characters)'))
end
end
def validate_on_update
if ContactGroup.find_first(["name = '#{name}' and customer_id = #{user_id} and id <> #{id}"])
errors.add("name", _('You already have contact group with this name'))
end
end
end

View File

@ -1,32 +0,0 @@
require_dependency 'maildropserializator'
class Customer < ActiveRecord::Base
include MaildropSerializator
has_many :filters, :order => "order_num"
has_one :mail_pref
attr_accessor :password
def mail_temporary_path
"#{CDF::CONFIG[:mail_temp_path]}/#{self.email}"
end
def friendlly_local_email
encode_email("#{self.fname} #{self.lname}", check_for_domain(email))
end
def mail_filter_path
"#{CDF::CONFIG[:mail_filters_path]}/#{self.email}"
end
def local_email
self.email
end
def check_for_domain(email)
if email && !email.nil? && !email.include?("@") && CDF::CONFIG[:send_from_domain]
email + "@" + CDF::CONFIG[:send_from_domain]
else
email
end
end
end

146
app/models/folder.rb Executable file
View File

@ -0,0 +1,146 @@
class Folder < ActiveRecord::Base
belongs_to :user
validates_presence_of :name, :on => :create
before_save :check_fill_params, :on => :create
has_many :messages, :dependent => :destroy
SYS_NONE = 0
SYS_TRASH = 1
SYS_INBOX = 2
SYS_SENT = 3
SYS_DRAFTS = 4
default_scope :order => 'name asc'
scope :shown, where(['shown = ?',true])
scope :inbox, where(['sys = ?',SYS_INBOX])
scope :sent, where(['sys = ?',SYS_SENT])
scope :drafts, where(['sys = ?',SYS_DRAFTS])
scope :trash, where(['sys = ?',SYS_TRASH])
scope :sys, where(['sys > ?',SYS_NONE])
def full_name
if parent.empty?
name
else
parent + delim + name
end
end
def depth
parent.split('.').size
end
def selected?(session_folder)
fields = session_folder.split("#")
fields[1].nil? ? fields.insert(0,"") : fields
(fields[1].downcase == name.downcase) && (fields[0].downcase == parent.downcase)
end
def update_stats
logger.info "MESS_BEFORE: "+messages.inspect
unseen = messages.where(:unseen => true).count
total = messages.count
logger.info "MESS: "+messages.inspect
logger.info "MESS: #{unseen} #{total}"
update_attributes(:unseen => unseen, :total => total)
end
def hasFullName?(folder_name)
full_name.downcase == folder_name.downcase
end
def isSystem?
sys > SYS_NONE
end
def isTrash?
sys == SYS_TRASH
end
def isSent?
sys == SYS_SENT
end
def isInbox?
sys == SYS_INBOX
end
def isDrafts?
sys == SYS_DRAFTS
end
def setNone
update_attributes(:sys => SYS_NONE)
end
def setTrash
update_attributes(:sys => SYS_TRASH)
end
def setSent
update_attributes(:sys => SYS_SENT)
end
def setInbox
update_attributes(:sys => SYS_INBOX)
end
def setDrafts
update_attributes(:sys => SYS_DRAFTS)
end
############################################## private section #####################################
private
def check_fill_params
self.total.nil? ? self.total = 0 : self.total
self.unseen.nil? ? self.unseen = 0 : self.unseen
self.parent.nil? ? self.parent = "" : self.parent
self.haschildren.nil? ? self.haschildren = false : self.haschildren
self.delim.nil? ? self.delim = "." : self.delim
self.sys.nil? ? self.sys = SYS_NONE : self.sys
end
def self.createBulk(user,imapFolders)
imapFolders.each do |name,data|
data.attribs.find_index(:Haschildren).nil? ? has_children = 0 : has_children = 1
name_fields = name.split(data.delim)
if name_fields.count > 1
name = name_fields.delete_at(name_fields.size - 1)
parent = name_fields.join(data.delim)
else
name = name_fields[0]
parent = ""
end
user.folders.create(
:name => name,
:parent => parent,
:haschildren => has_children,
:delim => data.delim,
:total => data.messages,
:unseen => data.unseen,
:sys => SYS_NONE)
end
end
def self.find_by_full_name(data)
folder = data.gsub(/\./,'#')
fields = folder.split("#")
nam = fields.delete_at(fields.size - 1)
fields.size.zero? == true ? par = "" : par = fields.join(".")
where(['name = ? and parent = ?',nam,par]).first
end
def self.refresh(mailbox,user)
user.folders.destroy_all
folders=mailbox.folders
Folder.createBulk(user,folders)
end
end

33
app/models/link.rb Executable file
View File

@ -0,0 +1,33 @@
class Link < ActiveRecord::Base
validates_length_of :url, :within => 5..150
validates_length_of :info, :maximum => 50
belongs_to :user
default_scope :order => 'url asc'
def self.getPageForUser(user,page,sort_field,sort_dir)
if sort_field
if Link.attribute_method?(sort_field) == true
order = sort_field
sort_dir == 'desc' ? order += ' desc' : sort_dir
end
end
Link.paginate :page => page , :per_page => $defaults["links_per_page"], :conditions=> ['user_id = ?', user.id],:order => order
end
def export
fields = []
fields << url || ""
fields << info || ""
fields.join(';')
end
def self.import(user,line)
fields = line.split(/;/)
contact = user.links.build( :url => fields[0].strip,
:info => fields[1].strip)
contact.save!
end
end

View File

@ -1,9 +0,0 @@
# require_association 'customer'
class MailPref < ActiveRecord::Base
belongs_to :customer
# def MailPref.find_by_customer(customer_id)
# find :first, :conditions => (["customer_id = #{customer_id}"])
# end
end

73
app/models/message.rb Executable file
View File

@ -0,0 +1,73 @@
require 'iconv'
require 'mail'
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :folder
#set_primary_key :uid
self.primary_key = :uid
attr_accessible :unseen, :to_addr, :size, :content_type, :folder_id, :subject, :date, :uid, :from_addr, :user_id, :msg_id, :body, :cc_addr, :bcc_addr
attr_accessor :body
def self.addr_to_db(addr)
ret = ""
name = addr.name
name.nil? ? ret : ret << ApplicationController.decode_quoted(name)
ret << "<" + addr.mailbox + "@" + addr.host
ret
end
def self.getPageForUser(user,folder,page,sort_field,sort_dir)
order = 'date desc'
if sort_field
if Message.attribute_method?(sort_field) == true
order = sort_field
sort_dir == 'desc' ? order += ' desc' : sort_dir
end
end
Message.paginate :page => page , :per_page => user.prefs.msgs_per_page.to_i, :conditions=> ['user_id = ? and folder_id = ?', user.id,folder.id],:order => order
end
def self.createForUser(user,folder,message)
# envelope = imap_message.attr['ENVELOPE']
#
# envelope.from.nil? ? from = "" : from = addr_to_db(envelope.from[0])
# envelope.to.nil? ? to = "" : to = addr_to_db(envelope.to[0])
# envelope.subject.nil? ? subject = "" : subject = ApplicationController.decode_quoted(envelope.subject)
mail = Mail.new(message.attr['RFC822.HEADER'])
mail.date.nil? ? date = nil : date = mail.date.to_s
mail.From.nil? ? from = nil : from = mail.From.charseted
mail.To.nil? ? to = nil : to = mail.To.charseted
mail.Subject.nil? ? subject = nil : subject = mail.Subject.charseted
#logger.custom('subject',mail.Subject.encoded)
#logger.custom('subject',subject)
#logger.custom('mail',mail.inspect)
create(
:user_id => user.id,
:folder_id => folder.id,
:msg_id => mail.message_id,
:uid => message.attr['UID'].to_i,
:from_addr => from,
:to_addr => to,
:subject => subject,
:content_type => mail.content_type,
:date => date,
:unseen => !(message.attr['FLAGS'].member? :Seen),
:size => message.attr['RFC822.SIZE']
)
end
def change_folder(folder)
update_attributes(:folder_id => folder.id)
end
end

21
app/models/prefs.rb Executable file
View File

@ -0,0 +1,21 @@
class Prefs < ActiveRecord::Base
validates_presence_of :theme,:locale
has_one :user
protected
def self.create_default(user)
Prefs.create(:user_id => user.id,
:theme => $defaults['theme'],
:locale => $defaults['locale'],
:msgs_per_page => $defaults['msgs_per_page'],
:msg_send_type => $defaults['msg_send_type'],
:msg_image_view_as => 'attachment',
:msg_image_thumbnail_size => '192x144'
)
end
end
# TODO move refresh to prefs and make refresh page with messages

45
app/models/server.rb Executable file
View File

@ -0,0 +1,45 @@
class Server < ActiveRecord::Base
validates_presence_of :name
belongs_to :user
#before_save :fill_params
def self.primary_for_imap
where(:for_imap=>true).first
end
def self.primary_for_smtp
where(:for_smtp=>true).first
end
def self.create_default(user)
create_server(user,"localhost")
end
def self.create_server(user,server)
create( :user_id=>user.id,
:name=>server,
:port=>$defaults['imap_port'],
:use_ssl=>false,
:use_tls=>false,
:for_smtp=>false,
:for_imap=>true
)
create( :user_id=>user.id,
:name=>server,
:port=>$defaults['smtp_port'],
:use_ssl=>false,
:use_tls=>false,
:for_smtp=>true,
:for_imap=>false
)
end
# private
# def fill_params
# port.nil? ? port = $defaults['imap_port'] : port
# $defaults['imap_use_ssl'] == true ? self.use_ssl = 1 : self.use_ssl = 0
# end
end

64
app/models/user.rb Executable file
View File

@ -0,0 +1,64 @@
require 'ezcrypto'
class User < ActiveRecord::Base
#acts_as_notes_owner
validates_presence_of :first_name,:last_name,:login
validates_uniqueness_of :login
has_many :servers, :dependent => :destroy
has_one :prefs, :dependent => :destroy
has_many :folders, :dependent => :destroy
has_many :messages, :dependent => :destroy
has_many :contacts, :dependent => :destroy
has_many :links, :dependent => :destroy
def set_cached_password(session,password)
if $defaults['session_encryption']
session[:session_salt] = generate_salt
session[:user_password] = EzCrypto::Key.encrypt_with_password($defaults['session_password'], session[:session_salt], password)
else
session[:user_password] = password
end
end
def get_cached_password(session)
if $defaults['session_encryption']
EzCrypto::Key.decrypt_with_password($defaults['session_password'], session[:session_salt], session[:user_password])
else
session[:user_password]
end
end
def generate_salt
(0...8).map{65.+(rand(25)).chr}.join
end
def name
first_name + " " + last_name
end
def full_id
(name + " <" + email + ">") if email
end
def email
if login =~ /\@/
login
else
(login + "@" + domain) if domain.presence
end
end
def username
login.gsub(/\@/,"_").gsub(/\./,"_")
end
def has_domain?
return domain if domain.presence
if login =~ /\@/
login.split(/\@/)[1]
end
end
end

View File

@ -0,0 +1,8 @@
- size ||= "btn-small"
- type ||= "btn-primary"
- icon ||= ""
%a{:class=>"btn #{size} #{type}",:href=>"#{href}"}
- if !icon.empty?
%i{:class=>"#{icon}"}
= caption

View File

@ -0,0 +1,27 @@
- model = eval(object.class.model_name)
- model_string = object.class.model_name.downcase
- label.nil? ? model_label = model.human_attribute_name(attr) : model_label = t(label.to_sym)
- val = value || object.instance_eval(attr) || ""
- if object.errors[attr.to_sym].empty?
- to_class ||= ""
- rows ||= 5
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
%textarea{:rows=>"#{rows}",:class=>"#{to_class}",:id=>"#{model_string}_#{attr}",:name=>"#{model_string}[#{attr}]"}
= val
%p{:class=>"help-block"}
= t(:example,:scope=>:common)
= example
- else
.control-group.error
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
%input{:id=>"#{model_string}_#{attr}",:name=>"#{model_string}[#{attr}]",:value=>"#{val}"}
%span{:class=>"help-inline"}
= object.errors[attr.to_sym].to_s
%p{:class=>"help-block"}
= t(:example,:scope=>:common)
= example

View File

@ -0,0 +1,19 @@
- size ||= "btn-small"
- type ||= "btn-primary"
- icon ||= ""
- onclick ||= 'empty'
- def isOnclick(value)
- if value != 'empty'
- {:onclick => "#{value}"}
- else
- {}
%button{isOnclick(onclick),:class=>"btn #{size} #{type}",
:type=>"submit",
:name=>"#{name}"}
- if !icon.empty?
%i{:class=>"#{icon}"}
= caption

View File

@ -0,0 +1,5 @@
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
%input{:id=>"#{model_string}_#{attr}",:name=>"#{model_string}[#{attr}]",:type=>"file"}

View File

@ -0,0 +1,12 @@
%form{:enctype=>"multipart/form-data", :class=>"form-horizontal top-pix18",:action=>"#{im_ex_path}",:method=>"post"}
= render :partial => "common/file_select", :locals => { :model_label => "#{im_ex_label}",
:model_string => "upload",
:attr => "datafile"}
%p
= render :partial => "common/button", :locals => {:name=>'import',
:caption=>t('import',:scope=>'contact'),
:icon=>'icon-upload icon-white'}
- if !im_ex_size.zero?
= render :partial => "common/button", :locals => {:name=>'export',
:caption=>t('export',:scope=>'contact'),
:icon=>'icon-download icon-white'}

View File

@ -0,0 +1,27 @@
- model = eval(object.class.model_name)
- model_string = object.class.model_name.downcase
- label.nil? ? model_label = model.human_attribute_name(attr) : model_label = t(label.to_sym)
- val = value || object.instance_eval(attr) || ""
- if object.errors[attr.to_sym].empty?
- to_class ||= ""
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
%input{:class=>"#{to_class}",:id=>"#{model_string}_#{attr}",:name=>"#{model_string}[#{attr}]",:value=>"#{val}"}
%p{:class=>"help-block"}
= t(:example,:scope=>:common)
= example
- else
.control-group.error
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
%input{:id=>"#{model_string}_#{attr}",:name=>"#{model_string}[#{attr}]",:value=>"#{val}"}
%span{:class=>"help-inline"}
= object.errors[attr.to_sym].to_s
%p{:class=>"help-block"}
= t(:example,:scope=>:common)
= example
-#= render :partial => "common/input_form_desc_field",:locals => {:object => @user,:attr => 'login',:label => nil,:example => 'joe.doe',:value => params[:user] ? params[:user][:login] : "" }

View File

@ -0,0 +1,8 @@
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model.capitalize.constantize.human_attribute_name(attr)
.controls
%input{:type=>"text",:id=>"#{model}_#{attr}",:name=>"#{model}[#{attr}]"}
-#= render :partial => "common/input_form_field",:locals => { :model => 'user',:attr => 'login'}

View File

@ -0,0 +1,5 @@
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model.capitalize.constantize.human_attribute_name(attr)
.controls
%input{:type=>"password",:id=>"#{model}_#{attr}",:name=>"#{model}[#{attr}]"}

View File

@ -0,0 +1,2 @@
.logo
= link_to image_tag("logo.png",:alt=> t(:mailr,:scope=>:common)), root_path

View File

@ -0,0 +1 @@
= calendar_window(:title=>t(:calendar,:scope=>:common))

View File

@ -0,0 +1,21 @@
- messages ||= ""
- compose ||= ""
- folders ||= ""
- contacts ||= ""
- prefs ||= ""
- links ||= ""
%ul{:class=>"nav nav-pills"}
%li{:class=>"#{messages}"}
= link_to( t(:messages,:scope=>:message), messages_path )
%li{:class=>"#{compose}"}
= link_to( t(:compose,:scope=>:compose), compose_path )
%li{:class=>"#{folders}"}
= link_to( t(:folders,:scope=>:folder), folders_path )
%li{:class=>"#{contacts}"}
= link_to( t(:contacts,:scope=>:contact), contacts_path )
%li{:class=>"#{links}"}
= link_to( t(:links,:scope=>:link), links_path )
%li{:class=>"#{prefs}"}
= link_to( t(:prefs,:scope=>:prefs), prefs_look_path )

View File

@ -0,0 +1,22 @@
%select{:multiple=>"multiple",:class=>"#{style}",:id=>"#{id}",:name=>"#{name}"}
- objects.each do |o|
- option_value = escape_once(o.send(value))
- option_text = [option_value]
- unless text.nil?
- option_text = []
- text.each do |t|
- option_text << o.send(t)
- option_text = option_text.join(joiner)
- option_text.gsub!(/^\./,'')
- if selected_objects.include?(o)
%option{:value=>"#{option_value}",:selected=>"selected"}
= option_text
- else
%option{:value=>"#{option_value}"}
= option_text
-#<%= raw multi_select("", 'folders_to_show[]', @folders, @folders_shown,t(:shown,:scope=>:folder),:id,"",{:text => [:parent,:delim,:name]}) %>
-#def multi_select(id, name, objects, selected_objects, label, value,joiner,content = {})

View File

@ -0,0 +1,11 @@
- look ||= ""
- identity ||= ""
- servers ||= ""
%ul{:class=>"nav nav-pills"}
%li{:class=>"#{look}"}
= link_to( t(:look,:scope=>:prefs), prefs_look_path )
%li{:class=>"#{identity}"}
= link_to( t(:identity,:scope=>:prefs), prefs_identity_path )
%li{:class=>"#{servers}"}
= link_to( t(:servers,:scope=>:prefs), prefs_servers_path )

View File

@ -0,0 +1,22 @@
- model = eval(object.class.model_name)
- model_string = object.class.model_name.downcase
- model_label = model.human_attribute_name(attr)
- translation_scope ||= false
.control-group
%label{:class=>"control-label",:for=>"#{attr}"}
= model_label
.controls
- if translation_scope
- t = []
- choices.each do |c|
- t << [t(c.to_sym,:scope=>translation_scope),c.to_s]
= select(model_string, attr, options_for_select(t,choice), {:include_blank => blank})
- else
= select(model_string, attr, options_for_select(choices,choice), {:include_blank => blank})
-# select(model.downcase, attr, options_for_select(choices,choice), {:include_blank => blank})

View File

@ -0,0 +1,5 @@
.control-group
%label{:class=>"control-label"}
= label
.controls
= raw simple_select_for_folders(name,id,collection,choice,blank)

View File

@ -0,0 +1,2 @@
%p{:class=>"version"}
= link_to (t(:version,:scope=>:common) + " " + $defaults["version"]),about_path

View File

@ -1,24 +0,0 @@
<h1><%=_('Edit/Create Contact Group')%></h1>
<%=
form_tag(
link_save,
'method' => 'post',
'class' => 'two_columns'
)
%>
<%= form_input(:hidden_field, 'contactgroup', 'id') %>
<%= form_input(:hidden_field, 'contactgroup', 'customer_id') %>
<table>
<%= form_input(:text_field, 'contactgroup', 'name', _('Name'), 'class'=>'two_columns') %>
</table>
<table>
<tr>
<td colspan=2 class="buttonBar">
<input type="submit" name="Save" value="<%=_('Save')%>"/>
<input type="button" value="<%=_('Back to groups')%>" onclick="window.location='<%=link_list%>'"/>
</td>
</tr>
</table>
<%= end_form_tag %>

View File

@ -1,26 +0,0 @@
<h1><%=_('Contact Groups')%></h1>
<%- form_for @contact_group do |f| %>
<%= hidden_field "contactgroup", "user_id" %>
<table class="list">
<tr>
<th><%=_('Name')%></th>
<th colspan=3>&nbsp;</th>
</tr>
<%
for contactgroup in @contactgroups %>
<tr class="even">
<td><%= contactgroup.name %></td>
<td><%= link_to(_('members'), :controller=>'contact', :action=>'list', :id=>contactgroup.id, :params=>{"mode"=>"groups"}) %></td>
<td><%= link_to(_('edit'), :controller=>'/contacts/contact_group', :action=>'edit', :id=>contactgroup.id) %></td>
<td><%= link_to(_('delete'), {:controller=>'/contacts/contact_group', :action=>'delete', :id=>contactgroup.id}, {:confirm=>sprintf(_('DELETE CONTACT GROUP \'%s\'?'), contactgroup.name)})%></td>
</tr>
<% end %>
<tr>
<td colspan=2 class="buttonBar">
<input type="submit" value="<%=_('Add Contact Group')%>"/>
<input type="button" value="<%=_('Back to folders')%>" onclick="window.location='/webmail/folders'">
</td>
</tr>
</table>
<%- end %>

View File

@ -0,0 +1,18 @@
= render :partial => "common/input_form_desc_field",:locals => {:object => @contact,
:attr => 'name',
:label => nil,
:example => 'Joe Doe',
:value => @contact.name,
:to_class=>"span6" }
= render :partial => "common/input_form_desc_field",:locals => {:object => @contact,
:attr => 'email',
:label => nil,
:example => 'joe.doe@domain.com',
:value => @contact.email,
:to_class=>"span6" }
= render :partial => "common/input_form_desc_field",:locals => {:object => @contact,
:attr => 'info',
:label => nil,
:example => t(:some_add_info,:scope=>:common),
:value => @contact.info,
:to_class=>"span6" }

View File

@ -0,0 +1,11 @@
%tr
%td{:nowrap=>"nowrap"}
= check_box_tag "items_ids[]", contact.id
\/
= link_to "<i class=\"icon-edit\"></i>".html_safe,edit_contact_path(contact)
%td{:nowrap=>"nowrap"}
= link_to contact.name,compose_contact_path(contact.id)
%td{:nowrap=>"nowrap"}
= link_to contact.email, compose_contact_path(contact.id)
%td{:nowrap=>"nowrap"}
= contact.info

View File

@ -0,0 +1,13 @@
= will_paginate @contacts
%table{:class=>"table table-bordered records"}
%thead
%tr
%th
%input{:id=>"toggleall",:type=>"checkbox",:name=>"allbox"}
= raw contacts_table_header
%tbody
- @contacts.each do |c|
= render :partial => 'contact', :locals => {:contact => c}
= will_paginate @contacts

View File

@ -1,26 +0,0 @@
<h1><%=t :add_multiple_contacts %></h1>
<% if flash["errors"] and not flash["errors"].empty?%>
<%= t(:errors)%>
<ul>
<% flash["errors"].each do |message| %>
<li><%= message %>
<% end %>
</ul>
<% end %>
<form action="<%=link_import_preview%>" enctype="multipart/form-data" method="post">
<%= radio_button("contact", "file_type", "1")%> <%= t(:csv_file)%>
<%= radio_button("contact", "file_type", "2")%> <%= t(:tab_file)%>
<table>
<tr>
<th><label for="contact[data]"><%=t(:select_file)%></label></th>
<td><input type="file" name="contact[data]"/></td>
</tr>
<tr>
<td colspan=2>
<input type="submit" value="<%= t(:import)%>"/>
<input type="button" value="<%= t(:back_to_contacts)%>" onclick="window.location='<%=contacts_url%>'">
<input type="button" value="<%= t(:back_to_folders)%>" onclick="window.location='<%=url_for(:controller => :webmail)%>'">
</td>
</tr>
</table>
</form>

View File

@ -1,11 +0,0 @@
<script language="javascript">
<% for to in @tos %>
respondTo("<%=to.full_address%>", "<%=to.id%>");
<% end %>
<% for cc in @ccs %>
respondCC("<%=cc.full_address%>");
<% end %>
<% for bcc in @bccs %>
respondBCC("<%=bcc.full_address%>");
<% end %>
</script>

View File

@ -0,0 +1,20 @@
= content_for :sidebar do
= render :partial => "sidebar/sidebar"
= content_for :title do
\-
= t(:contacts,:scope=>:contact)
= render :partial => 'common/main_navigation', :locals => { :contacts => :active }
.well{:style=>"padding: 5px 3pt;"}
%h3
= t(:modifying,:scope=>:contact)
%form{:class=>"form-horizontal",:action=>url_for(@contact),:method=>"post"}
%input{:name=>"_method",:type=>"hidden",:value=>"put"}
%fieldset
= render :partial => "attrs"
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:save, :caption => t(:save,:scope=>:common), :icon =>'icon-cog icon-white'}

View File

@ -1,43 +0,0 @@
<h1><%= _('Contacts You Are About To Import')%></h1>
<% if flash["errors"] and not flash["errors"].empty?%>
<%= _('Errors')%>
<ul>
<% flash["errors"].each do |message| %>
<li><%= message %>
<% end %>
</ul>
<% end %>
<form action="<%=link_contact_import%>" method="post">
<table class="list">
<tr>
<th>&nbsp;</th>
<th width="100px"><%= _('First name')%></th>
<th width="100px"><%= _('Last name')%></th>
<th><%= _('E-mail')%></th>
</tr>
<%
for i in 0...@contacts.length
contact = @contacts[i]
%>
<tr class="<%= alternator %>">
<td><%=i+1%></td>
<td><input type="text" name="contact[<%=i%>][fname]" value="<%=contact.fname%>" size="15" /></td>
<td><input type="text" name="contact[<%=i%>][lname]" value="<%=contact.lname%>" size="15" /></td>
<td><input type="text" name="contact[<%=i%>][email]" value="<%=contact.email%>" size="45" /></td>
</tr>
<% end %>
<tr>
<td colspan=4 class="buttonBar">
<input type="submit" value="<%= _('Import')%>">
<input type="button" value="<%= _('Choose another file')%>" onclick="window.location='<%=link_contact_add_multiple%>'">
<input type="button" value="<%= _('Back to contacts')%>" onclick="window.location='<%=link_contact_list%>'">
<input type="button" value="<%= _('Back to folders')%>" onclick="window.location='<%=link_main_index%>'">
</td>
</tr>
</table>
</form>

View File

@ -1,115 +0,0 @@
<h1><%= t :contacts %></h1>
<% unless @mode == "choose" %>
<div id="header">
<ul id="primary">
<li><%=link_folders%></li>
<li><%=link_send_mail%></li>
<li><%=link_mail_prefs%></li>
<li><%=link_mail_filters%></li>
<li><span><%= t :contacts %></span>
<ul id="secondary">
<li><%=link_contact_add_one%></li>
<li><%=link_to t(:add_multiple), add_multiple_contacts_path %></li>
<% if ret = session["return_to"] %>
<li><%=link_to(t(:back_to_message), ret) %></li>
<% end %>
</ul>
</li>
</ul>
</div>
<% end -%>
<div id="tab_main">
<div id="tab_content">
<% if flash["alert"] %><ul><li><%= flash["alert"] %></li></ul><% end %>
<form action="<%=link_contact_choose%>?mode=<%=@mode%>" method="post">
<input type="hidden" name="mode" value="<%=@mode%>"/>
<% if @group_id and not @group_id.nil? %>
<input type="hidden" name="group_id" value="<%=@group_id%>"/>
<% end %>
<table class="list">
<tr>
<td colspan="4" id="alphaListHeader">
<% CDF::CONFIG[:contact_letters].each do |letter| %>
<%= link_to letter, contacts_path(:letter => letter) %>
<% end %>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<%= link_to t(:show_all), contacts_path %>
</td>
</tr>
<tr>
<td colspan="3"><%= will_paginate @contacts %></td>
</tr>
<% if @mode == "choose" %>
<tr>
<th><%= "#{t :to}&nbsp;#{t :cc}&nbsp;#{t :bcc}" %></th>
<th><%= t :name %></th>
<th><%= t :email %></th>
</tr>
<% for contact in @contacts %>
<tr class="<%= alternator %>">
<td><input type="checkbox" name="contacts_to[<%=contact.id%>]" value="1"/>
<input type="checkbox" name="contacts_cc[<%=contact.id%>]" value="1"/>
<input type="checkbox" name="contacts_bcc[<%=contact.id%>]" value="1"/></td>
<td><%=contact.full_name%></td>
<td><%=contact.email%></td>
</tr>
<% end %>
<tr class="rowsep"><td colspan="3"><%=t(:groups)%>:</td></tr>
<% for group in @contactgroups %>
<tr class="<%= alternator %>">
<td><input type="checkbox" name="groups_to[<%=group.id%>]" value="1"/>
<input type="checkbox" name="groups_cc[<%=group.id%>]" value="1"/>
<input type="checkbox" name="groups_bcc[<%=group.id%>]" value="1"/></td>
<td><%=group.name%></td>
<td>&nbsp;</td>
</tr>
<% end %>
<tr>
<td colspan=3 class="buttonBar">
<input type="submit" value="<%= t(:choose)%>">
<input type="button" value="<%= t(:cancel)%>" onclick="javascript:window.close();">
</td>
</tr>
<% elsif @mode == "groups"%>
<tr>
<th></th>
<th width="200px"><%= t(:name)%></th>
<th><%= t(:email)%></th>
</tr>
<% for contact in @contacts %>
<input type="hidden" id="contacts_for_group[<%=contact.id%>]" name="contacts_for_group[<%=contact.id%>]" value="<%=@contacts_for_group[contact.id]%>" >
<tr class="<%= alternator %>">
<td><input type="checkbox" id="contacts_for_group[<%=contact.id%>]" name="contacts_for_group[<%=contact.id%>]"
value="<%=@contacts_for_group[contact.id]%>" onclick="toggleCheckbox(this)"
<%=@contacts_for_group[contact.id] == 1 ? " checked " : " " %> ></td>
<td><%=contact.full_name%></td>
<td><%=contact.email%></td>
</tr>
<% end %>
<tr>
<td colspan=2 class="buttonBar">
<input type="submit" value="<%= t(:save)%>">
<input type="button" value="<%= t(:back_to_groups)%>" onclick="window.location='<%=link_contact_group_list%>'">
</td>
</tr>
<% else %>
<tr>
<th width="200px"><%= t(:name)%></th>
<th><%= t(:email)%></th>
<th>&nbsp;</th>
</tr>
<% for contact in @contacts %>
<tr class="<%= alternator %>">
<td><%= link_to(contact.full_name, :controller=>:contacts, :action => "edit", :id => contact.id ) %></td>
<td><%= link_to( contact.email, :controller => :webmail, :action => "compose", :params => { "mail[to]" => contact.email } ) %></td>
<td><%= link_to(t(:delete), {:controller=>:contacts, :action=>'delete', :id=>contact.id},
{:confirm=>t(:delete_contact_question, :name => contact.show_name, :email => contact.email)})%>
</td>
</tr>
<% end %>
<% end %>
</table>
</form>
</div>
</div>

View File

@ -0,0 +1,38 @@
= content_for :sidebar do
= render :partial => "sidebar/sidebar"
= content_for :title do
\-
= t(:contacts,:scope=>:contact)
= render :partial => 'common/main_navigation', :locals => { :contacts => :active }
%form{:class=>"form-horizontal top-pix18",:action=>url_for(@contact),:method=>"post"}
- if @contacts.size.zero?
.alert
= t(:no_entries,:scope=>:contact)
%p{:class=>"bottom-pix18"}
= render :partial => "common/anchor", :locals => {:caption=>t('create_new',:scope=>'contact'),
:icon=>'icon-plus icon-white',
:href=>new_contact_path}
- else
.well{:style=>"padding: 5px 3pt;"}
%h5
= t(:total_entries,:scope=>:contact)
\:
= @contacts.total_entries
%p{:class=>"bottom-pix18"}
= render :partial => "common/anchor", :locals => {:caption=>t('create_new',:scope=>'contact'),
:icon=>'icon-plus icon-white',
:href=>new_contact_path}
= render :partial => "common/button", :locals => {:name=>'delete_selected',
:caption=>t('delete_selected',:scope=>'contact'),
:icon=>'icon-minus icon-white'}
= render :partial => "common/button", :locals => {:name=>'compose_to_selected',
:caption=>t('compose_to_selected',:scope=>'contact'),
:icon=>'icon-envelope icon-white'}
= render :partial => 'list'
= render :partial => 'common/import_export',:locals=>{:im_ex_path => contacts_import_export_path,
:im_ex_label => t(:select_file,:scope=>:contact),
:im_ex_size => @contacts.total_entries }

View File

@ -1,73 +0,0 @@
<h1><%=t(:edit_create_contact)%></h1>
<div id="header">
<ul id="primary">
<li><%=link_folders%></li>
<li><%=link_send_mail%></li>
<li><%=link_mail_prefs%></li>
<li><%=link_mail_filters%></li>
<li><span><%= t :contacts %></span>
<ul id="secondary">
<li><%=link_to t(:back_to_contacts), contacts_url%></li>
<% if ret = session["return_to"] %>
<li><%=link_to(t(:back_to_message), ret) %></li>
<% end %>
</ul>
</li>
</ul>
</div>
<div id="tab_main">
<div id="tab_content">
<% form_tag( contacts_path, 'method' => 'post', 'class' => 'two_columns') do %>
<%= form_input(:hidden_field, 'contact', 'id') %>
<%= form_input(:hidden_field, 'contact', 'customer_id') %>
<table>
<%= form_input(:text_field, 'contact', 'fname', t(:first_name), 'class'=>'two_columns') %>
<%= form_input(:text_field, 'contact', 'lname', t(:last_name), 'class'=>'two_columns') %>
<%= form_input((@contact.new_record? ? :text_field : :read_only_field), 'contact', 'email', t(:email), 'class'=>'two_columns')%>
</table>
<% for group in @contactgroups %>
<input id="groups[<%=group.id%>]" type="hidden" name="groups[<%=group.id%>]" value="<%=@groups[group.id]%>">
<% end %>
<% if not(@contactgroups.empty?) %>
<%=_('Contact belong to these groups')%>:
<table class="list">
<tr>
<%
end
col = 1
for group in @contactgroups %>
<th>
<input id="groups[<%=group.id%>]" type="checkbox" name="groups[<%=group.id%>]" value="<%=@groups[group.id]%>" onclick="toggleCheckbox(this)"
<%=@groups[group.id] == 1 ? " checked " : " " %> >
&nbsp;<%=group.name %>
</th>
<% if col%2 == 0 %>
</tr>
<tr>
<% end
col = col + 1 %>
<% end %>
<% if col%2 == 0 and not(@contactgroups.empty?) %>
<th>&nbsp;</th>
<% end %>
<% if not(@contactgroups.empty?) %>
</tr>
</table>
<% end %>
<table class="edit">
<tr>
<td colspan=2 class="buttonBar">
<input type="submit" name="paction" value="<%=t(:save)%>"/>
<input type="submit" name="paction" value="<%=t(:save_and_add_another)%>"/>
</td>
</tr>
</table>
<% end %>
</div>
</div>

View File

@ -0,0 +1,19 @@
= content_for :sidebar do
= render :partial => "sidebar/sidebar"
= content_for :title do
\-
= t(:contacts,:scope=>:contact)
= render :partial => 'common/main_navigation', :locals => { :contacts => :active }
.well{:style=>"padding: 5px 3pt;"}
%h3
= t(:creating_new,:scope=>:contact)
%form{:class=>"form-horizontal",:action=>url_for(@contact),:method=>"post"}
%fieldset
= render :partial => "attrs"
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:save, :caption => t(:save,:scope=>:common), :icon =>'icon-cog icon-white'}

View File

@ -0,0 +1,8 @@
%form{:class=>"form-horizontal",:action=>folders_create_path,:method=>"post"}
%fieldset
= render :partial => "common/select_for_folders",:locals => { :label=> t(:parent,:scope=>:folder), :name => "folder", :id => "parent", :collection => @folders, :choice => "", :blank => true}
= render :partial => "common/input_form_field",:locals => { :model => 'folder',:attr => 'target'}
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:create, :caption => t(:create,:scope=>:common), :icon =>'icon-plus icon-white'}

View File

@ -0,0 +1,6 @@
%form{:class=>"form-horizontal",:action=>folders_delete_path,:method=>"post"}
%fieldset
= render :partial => "common/select_for_folders",:locals => { :label=> t(:to_delete,:scope=>:folder), :name => "folder", :id => "delete", :collection => @folders, :choice => "", :blank => true}
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:delete, :caption => t(:delete,:scope=>:common), :icon =>'icon-minus icon-white'}

View File

@ -0,0 +1,16 @@
.well{:style=>"padding: 8px 3pt;"}
- if @folders_shown.nil? or @folders_shown.size.zero?
%p
= t(:no_shown,:scope=>:folder)
= link_to t(:folders,:scope=>:folder), folders_path
- else
%ul{:class=>"nav nav-list"}
%li{:class=>"nav-header"}
=t(:folders,:scope=>:folder)
- @folders_shown.each do |folder|
- if folder == @current_folder
%li{:class=>"active"}
= folder_link(:folder=>folder,:active=>true)
- else
%li
= folder_link(:folder=>folder,:active=>false)

View File

@ -0,0 +1,11 @@
%form{:class=>"form-horizontal",:action=>folders_refresh_path,:method=>"post"}
%fieldset
.control-group
%label{:class=>"control-label"}
= t(:presentation,:scope=>:folder)
.controls
= render :partial=>"common/multiselect",:locals => {:objects => @folders, :selected_objects => @folders_shown,:style=>"",:id=>"multiselect_form",:name=>"folders_to_show[]",:value=>:id,:joiner=>"",:text=>[:parent,:delim,:name]}
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:show_hide, :caption => t(:show_hide,:scope=>:folder), :icon =>'icon-eye-open icon-white'}
= render :partial => "common/button",:locals => { :name=>:refresh, :caption => t(:refresh,:scope=>:folder), :icon =>'icon-refresh icon-white'}

View File

@ -0,0 +1,9 @@
%form{:class=>"form-horizontal",:action=>folders_system_path,:method=>"post"}
%fieldset
= render :partial => "common/select_for_folders",:locals => { :label=> t(:folder,:scope => :folder) + " " + t(:inbox_name,:scope=>:folder), :name => "folder", :id => "mailbox_inbox", :collection => @folders, :choice => @folder_inbox, :blank => true}
= render :partial => "common/select_for_folders",:locals => { :label=> t(:folder,:scope => :folder) + " " + t(:trash_name,:scope=>:folder), :name => "folder", :id => "mailbox_trash", :collection => @folders, :choice => @folder_trash, :blank => true}
= render :partial => "common/select_for_folders",:locals => { :label=> t(:folder,:scope => :folder) + " " + t(:sent_name,:scope=>:folder), :name => "folder", :id => "mailbox_sent", :collection => @folders, :choice => @folder_sent, :blank => true}
= render :partial => "common/select_for_folders",:locals => { :label=> t(:folder,:scope => :folder) + " " + t(:drafts_name,:scope=>:folder), :name => "folder", :id => "mailbox_drafts", :collection => @folders, :choice => @folder_drafts, :blank => true}
.control-group
.controls
= render :partial => "common/button",:locals => { :name=>:set, :caption => t(:set,:scope=>:common), :icon =>'icon-cog icon-white'}

View File

@ -1,48 +0,0 @@
<h1><%=t :mailbox %></h1>
<div id="header">
<ul id="primary">
<li><span><%= t :folders %></span>
<ul id="secondary">
<li><%=link_back_to_messages%></li>
</ul>
</li>
<li><%=link_send_mail%></li>
<li><%=link_mail_prefs%></li>
<li><%=link_mail_filters%></li>
<li><%=link_main%></li>
</ul>
</div>
<div id="tab_main">
<div id="tab_content">
<% content_for('sidebar') { %>
<div id="folders">
<h4><%= t :add_folder %></h4>
<hr/>
<% form_tag folders_path, :id => 'new_folder' do %>
<ul>
<li><label for='folder'><%= t :name %>:</label></li>
<li><%= text_field_tag 'folder', '', :size => 18 %></li>
<li><%= submit_tag t(:add_folder) %></li>
</ul>
<% end %>
</div>
<% } %>
<div id="messages">
<div id='msg-fl-list'>
<table>
<thead><tr><th><%= t :folder %></th><th><%= t :total_messages %></th><th><%= t :unseen%></th></tr></thead>
<tbody>
<% for folder in @folders %>
<tr>
<td><%=folder_manage_link(folder)%></td>
<td><%= folder.total %></td>
<td><%= folder.unseen > 0 ? "<b>#{folder.unseen}</b>" : "#{folder.unseen}" %></td></tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
= content_for :sidebar do
= render :partial => "sidebar/sidebar"
= content_for :title do
\-
= t(:folders,:scope=>:folder)
= render :partial => 'common/main_navigation', :locals => { :folders => :active }
.row
.span9
= render :partial => "refresh"
= render :partial => "create"
= render :partial => "delete"
= render :partial => "system"

View File

@ -0,0 +1,23 @@
= content_for :sidebar do
= render :partial => "sidebar/sidebar"
= content_for :title do
\-
= t(:about,:scope=>:internal)
= render :partial => 'common/main_navigation', :locals => { :about => :active }
.well{:style=>"padding: 5px 3pt;"}
%h3
= t(:current_version,:scope=>:internal) + ": " + $defaults["version"]
.well
= raw BlueCloth::new(render :file => 'README.markdown').to_html
.well
= raw BlueCloth::new(render :file => 'CHANGES.markdown').to_html
.well
= raw BlueCloth::new(render :file => 'TODO.markdown').to_html
.well
= raw BlueCloth::new(render :file => 'AUTHORS.markdown').to_html
.well
= raw BlueCloth::new(render :file => 'UNLICENSE.markdown').to_html

View File

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