From bb3289e0d48c6aa8ccd523b02983ca63891a8f6e Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Fri, 16 Sep 2011 22:08:30 +0200 Subject: [PATCH 01/38] fixes for contacts import;flash fixes --- app/controllers/contacts_controller.rb | 10 +++++++--- app/models/contact.rb | 14 +++++++------- app/models/event.rb | 2 -- themes/olive/stylesheets/style.css | 5 +++++ themes/olive/views/contacts/_external.html.erb | 4 ++++ themes/olive/views/layouts/application.html.erb | 9 +-------- 6 files changed, 24 insertions(+), 20 deletions(-) delete mode 100755 app/models/event.rb diff --git a/app/controllers/contacts_controller.rb b/app/controllers/contacts_controller.rb index 002c5dc..c4c750b 100755 --- a/app/controllers/contacts_controller.rb +++ b/app/controllers/contacts_controller.rb @@ -82,13 +82,17 @@ class ContactsController < ApplicationController tmp_file.flush tmp_file.rewind tmp_file.readlines.each do |line| + next if line =~ /^#/ Contact.import(@current_user,line) end - rescue Exception => e - flash[:error] = e.to_s + 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[:notice] = t(:were_imported,:scope=>:contact) end end - flash[:notice] = t(:were_imported,:scope=>:contact) if not flash[:error] redirect_to :action => 'index' end diff --git a/app/models/contact.rb b/app/models/contact.rb index 4291ba8..4af64b7 100755 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -4,14 +4,14 @@ class Contact < ActiveRecord::Base validates_length_of :first_name,:last_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 => 50 + validates_length_of :info, :maximum => 100 validate :check_unique_nick, :on => :create + default_scope :order => 'nick ASC' belongs_to :user def self.getPageForUser(user,page,sort_field,sort_dir) - order = 'last_name asc' if sort_field if Contact.attribute_method?(sort_field) == true order = sort_field @@ -44,11 +44,11 @@ class Contact < ActiveRecord::Base def self.import(user,line) fields = line.split(/;/) - contact = user.contacts.build( :nick => fields[0], - :first_name => fields[1], - :last_name => fields[2], - :email => fields[3], - :info => fields[4]) + contact = user.contacts.build( :nick => fields[0].strip, + :first_name => fields[1].strip, + :last_name => fields[2].strip, + :email => fields[3].strip, + :info => fields[4].strip) contact.save! end end diff --git a/app/models/event.rb b/app/models/event.rb deleted file mode 100755 index 3a829fd..0000000 --- a/app/models/event.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Event < ActiveRecord::Base -end diff --git a/themes/olive/stylesheets/style.css b/themes/olive/stylesheets/style.css index 6e8a6a8..c87ce20 100755 --- a/themes/olive/stylesheets/style.css +++ b/themes/olive/stylesheets/style.css @@ -583,3 +583,8 @@ div.logo { div#logo { background-color: #EFF3E4; } + +div.flash p.info { + text-align: left; + font-size: 10px; +} diff --git a/themes/olive/views/contacts/_external.html.erb b/themes/olive/views/contacts/_external.html.erb index 0145a2a..f945a6a 100755 --- a/themes/olive/views/contacts/_external.html.erb +++ b/themes/olive/views/contacts/_external.html.erb @@ -2,6 +2,10 @@ <%= form_tag(contacts_external_path, :multipart => true) %> :  <%= file_field 'upload', 'datafile' %> +<% if @contacts.size.zero? %> +<%= raw single_action('import','contact','right.png') %> +<% else %> <%= raw group_action(@ei_buttons) %> +<% end %> diff --git a/themes/olive/views/layouts/application.html.erb b/themes/olive/views/layouts/application.html.erb index 9951d00..1605793 100755 --- a/themes/olive/views/layouts/application.html.erb +++ b/themes/olive/views/layouts/application.html.erb @@ -28,14 +28,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" <%= yield :sidebar %>
-
- <% flash.each do |key, msg| %> - <% unless msg.blank? %> - <%= content_tag(:div,(content_tag :p, msg), :class => "message #{key.to_s}" )%> - <%= flash[key.to_sym]='' %> - <% end %> - <% end %> -
+ <%= render :partial=>'layouts/flash', :object => flash %> <%= yield %>

From 6fa55112da21bcdaac5e0d87907a0ffb3a6019c2 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Fri, 16 Sep 2011 22:19:22 +0200 Subject: [PATCH 02/38] file_select fix --- themes/olive/views/messages/_file_select.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/olive/views/messages/_file_select.html.erb b/themes/olive/views/messages/_file_select.html.erb index 0405f62..1f67932 100755 --- a/themes/olive/views/messages/_file_select.html.erb +++ b/themes/olive/views/messages/_file_select.html.erb @@ -1,5 +1,5 @@
-<%= form_tag(contact_import_path, :multipart => true) %> +<%= form_tag(upload_path, :multipart => true) %> :  <%= file_field 'upload', 'datafile' %> <%= raw single_action('send_file','compose','up.png') %> From 1663cd60b5f6d2ccba76f2cb7d9ae55bed6b0747 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Fri, 16 Sep 2011 22:21:57 +0200 Subject: [PATCH 03/38] version info --- config/about.txt | 4 ++++ config/todo.txt | 16 ++++++++++++++++ themes/olive/views/layouts/_flash.html.erb | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100755 config/about.txt create mode 100755 config/todo.txt create mode 100644 themes/olive/views/layouts/_flash.html.erb diff --git a/config/about.txt b/config/about.txt new file mode 100755 index 0000000..1d544fe --- /dev/null +++ b/config/about.txt @@ -0,0 +1,4 @@ +0.8.3 + +* export, imports of contact + diff --git a/config/todo.txt b/config/todo.txt new file mode 100755 index 0000000..5d99a19 --- /dev/null +++ b/config/todo.txt @@ -0,0 +1,16 @@ +app/controllers/folders_controller.rb: + * [ 30] [TODO] recreate local copy of folders + * [ 99] [TODO] save system folders + +app/controllers/messages_controller.rb: + * [101] [FIXME] missing fields and support arrays + +app/controllers/messages_ops_controller.rb: + * [128] [FIXME] check if uploads directory exists + * [176] [FIXME] check if domain is set + * [192] [TODO] check if email address is valid if not get address from contacts + * [259] [FIXME] edit does not support attachments + +app/models/prefs.rb: + * [ 19] [TODO] move refresh to prefs and make refresh page with messages + diff --git a/themes/olive/views/layouts/_flash.html.erb b/themes/olive/views/layouts/_flash.html.erb new file mode 100644 index 0000000..24c1562 --- /dev/null +++ b/themes/olive/views/layouts/_flash.html.erb @@ -0,0 +1,16 @@ +<% if not flash.size.zero? %> +
+<% flash.each do |key, msg| %> +<% unless msg.blank? %> +<% if msg.class.eql?(Hash) %> +<% f = content_tag :p, msg[:title] %> +<% f += content_tag :p, msg[:info], :class=> "info" %> +<% else %> +<% f = content_tag :p, msg %> +<% end %> +<%= content_tag(:div,f, :class => "message #{key.to_s}" )%> +<%= flash[key.to_sym]='' %> +<% end %> +<% end %> +
+<% end %> From 66cba6bbc7855fe99a78f07a02b6ebe9b2406275 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Sun, 18 Sep 2011 21:18:52 +0200 Subject: [PATCH 04/38] view fixes --- config/defaults.yml | 2 +- config/locales/pl.yml | 2 +- themes/olive/images/logo_small.png | Bin 18309 -> 17995 bytes themes/olive/stylesheets/base.css | 2 +- themes/olive/stylesheets/style.css | 1 + themes/olive/views/folders/_list.html.erb | 2 +- themes/olive/views/sidebar/_logo.html.erb | 4 +++- themes/olive/views/user/login.html.erb | 2 +- themes/olive/views/user/setup.html.erb | 2 +- 9 files changed, 10 insertions(+), 7 deletions(-) diff --git a/config/defaults.yml b/config/defaults.yml index 93ff98a..ddc3076 100755 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -45,4 +45,4 @@ session_password: asDD3s2@sAdc983# mailbox_max_parent_folder_depth: 3 # array of logins which only can login to application, comment it to allow everyone to login -only_can_logins: [wtodryk] +only_can_logins: [wojciech@todryk.pl] diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 999e71c..1135ce8 100755 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -163,6 +163,7 @@ pl: setup: Konfiguracja login: Logowanie only_can_logins: Podany identyfikator użytkownika nie uprawnia do korzystania z aplikacji + logout: Wyloguj internal: imap_error: Błąd protokołu IMAP @@ -197,5 +198,4 @@ pl: view: Pokaż version: Wersja set: Ustaw - logout: Wyloguj no_file_chosen: Nie wybrano żadnego pliku diff --git a/themes/olive/images/logo_small.png b/themes/olive/images/logo_small.png index 3263e4c0744a2454e595fb5dfd210677ee9f7044..28b9079872cc7fbcdc8faf3815d3b2f0c8f29d27 100755 GIT binary patch literal 17995 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 literal 18309 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 <% end %>
- <%= raw single_action_onclick('logout','common','power.png',user_logout_path) %> + <%= raw single_action_onclick('logout','user','power.png',user_logout_path) %>
diff --git a/themes/olive/views/sidebar/_logo.html.erb b/themes/olive/views/sidebar/_logo.html.erb index f449fb5..1116476 100755 --- a/themes/olive/views/sidebar/_logo.html.erb +++ b/themes/olive/views/sidebar/_logo.html.erb @@ -1 +1,3 @@ - + diff --git a/themes/olive/views/user/login.html.erb b/themes/olive/views/user/login.html.erb index 324a4f8..533e2fd 100755 --- a/themes/olive/views/user/login.html.erb +++ b/themes/olive/views/user/login.html.erb @@ -35,7 +35,7 @@ diff --git a/themes/olive/views/user/setup.html.erb b/themes/olive/views/user/setup.html.erb index 74a3c98..0d8d42e 100755 --- a/themes/olive/views/user/setup.html.erb +++ b/themes/olive/views/user/setup.html.erb @@ -13,7 +13,7 @@ <%= raw form_field(@user,"first_name",nil,"Joe","") %> <%= raw form_field(@user,"last_name",nil,"Doe","") %> <%= raw form_field(@server,"name",nil,"server.domain","") %> - <%= raw form_button('send',"tick.png") %> + <%= raw single_action('send','compose',"up.png") %> From 17a85a59163ac432e6aeaa8d8d6bce0afe84c237 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Fri, 23 Sep 2011 21:35:12 +0200 Subject: [PATCH 05/38] calendar as separate gem, bluecloth integrated --- Gemfile | 2 + README.markdown | 16 ++---- config/about.txt | 4 -- config/defaults.yml | 4 +- config/todo.txt | 16 ------ themes/olive/stylesheets/style.css | 19 +++++++ themes/olive/views/internal/about.html.erb | 25 +++++---- .../olive/views/layouts/application.html.erb | 1 + .../views/sidebar/_calendar_view.html.erb | 7 ++- vendor/plugins/calendar_view/MIT-LICENSE | 20 ------- vendor/plugins/calendar_view/README | 13 ----- vendor/plugins/calendar_view/Rakefile | 23 -------- vendor/plugins/calendar_view/init.rb | 1 - vendor/plugins/calendar_view/install.rb | 1 - .../lib/app/helpers/calendar_view_helper.rb | 56 ------------------- .../calendar_view/lib/calendar_view.rb | 2 - .../calendar_view/test/calendar_view_test.rb | 8 --- .../plugins/calendar_view/test/test_helper.rb | 3 - vendor/plugins/calendar_view/uninstall.rb | 1 - 19 files changed, 47 insertions(+), 175 deletions(-) delete mode 100755 config/about.txt delete mode 100755 config/todo.txt delete mode 100755 vendor/plugins/calendar_view/MIT-LICENSE delete mode 100755 vendor/plugins/calendar_view/README delete mode 100755 vendor/plugins/calendar_view/Rakefile delete mode 100755 vendor/plugins/calendar_view/init.rb delete mode 100755 vendor/plugins/calendar_view/install.rb delete mode 100755 vendor/plugins/calendar_view/lib/app/helpers/calendar_view_helper.rb delete mode 100755 vendor/plugins/calendar_view/lib/calendar_view.rb delete mode 100755 vendor/plugins/calendar_view/test/calendar_view_test.rb delete mode 100755 vendor/plugins/calendar_view/test/test_helper.rb delete mode 100755 vendor/plugins/calendar_view/uninstall.rb diff --git a/Gemfile b/Gemfile index c7be26d..449a132 100755 --- a/Gemfile +++ b/Gemfile @@ -6,3 +6,5 @@ gem 'mysql2' , '~>0.2.7' gem 'will_paginate', '~> 3.0.beta' gem 'themes_for_rails' gem "ezcrypto", "~> 0.7.2" +gem "calendar_view", "~> 0.0.3" +gem 'bluecloth', '>= 2.0.0' diff --git a/README.markdown b/README.markdown index d810ce2..6dcff2b 100755 --- a/README.markdown +++ b/README.markdown @@ -1,16 +1,15 @@ ## Introduction -_Mailr_ is a IMAP mail client based on _Ruby on Rails_ platform. +_MailR_ is a IMAP mail client based on _Ruby on Rails_ platform. **NOTE** All path and filenames are based on _Rails.root_ directory. -### Requirements +## Requirements In _Rails 3_ all dependencies should be defined in file _Gemfile_. All needed gems can be installed using bundler. -### Installation procedure +## 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 @@ -18,17 +17,12 @@ bundle install ``` * Check _config/defaults.yml_ for proper values. - * 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 @@ -37,7 +31,7 @@ rake db:clear_data * Use it. -### Specific configuration +## Specific configuration For themes: if server sends files with no content in production mode comment out @@ -45,4 +39,4 @@ For themes: if server sends files with no content in production mode comment out config.action_dispatch.x_sendfile_header = "X-Sendfile" ``` -from _config/environments/production.rb_ file. +in _config/environments/production.rb_ file. diff --git a/config/about.txt b/config/about.txt deleted file mode 100755 index 1d544fe..0000000 --- a/config/about.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.8.3 - -* export, imports of contact - diff --git a/config/defaults.yml b/config/defaults.yml index ddc3076..526280e 100755 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -1,4 +1,4 @@ -version: 0.8.3 +version: 0.8.4 theme: olive locale: pl @@ -45,4 +45,4 @@ session_password: asDD3s2@sAdc983# mailbox_max_parent_folder_depth: 3 # array of logins which only can login to application, comment it to allow everyone to login -only_can_logins: [wojciech@todryk.pl] +only_can_logins: [soldier] diff --git a/config/todo.txt b/config/todo.txt deleted file mode 100755 index 5d99a19..0000000 --- a/config/todo.txt +++ /dev/null @@ -1,16 +0,0 @@ -app/controllers/folders_controller.rb: - * [ 30] [TODO] recreate local copy of folders - * [ 99] [TODO] save system folders - -app/controllers/messages_controller.rb: - * [101] [FIXME] missing fields and support arrays - -app/controllers/messages_ops_controller.rb: - * [128] [FIXME] check if uploads directory exists - * [176] [FIXME] check if domain is set - * [192] [TODO] check if email address is valid if not get address from contacts - * [259] [FIXME] edit does not support attachments - -app/models/prefs.rb: - * [ 19] [TODO] move refresh to prefs and make refresh page with messages - diff --git a/themes/olive/stylesheets/style.css b/themes/olive/stylesheets/style.css index 33c6f50..a7658a2 100755 --- a/themes/olive/stylesheets/style.css +++ b/themes/olive/stylesheets/style.css @@ -589,3 +589,22 @@ div.flash p.info { text-align: left; font-size: 10px; } + +div.md { + padding: 10px; + margin-bottom: 5px; +} + +div.md ul { + margin-left:20px; +} +div.md code { + background-color: #EEEEEE; + border: 1px solid #DDDDDD; + border-radius: 3px 3px 3px 3px; + color: #444444; + font-size: 12px; + margin: 1em 0; + overflow: auto; + padding: 5px; +} diff --git a/themes/olive/views/internal/about.html.erb b/themes/olive/views/internal/about.html.erb index 19336c1..8ab45e3 100755 --- a/themes/olive/views/internal/about.html.erb +++ b/themes/olive/views/internal/about.html.erb @@ -7,21 +7,24 @@ <% end %>
-
- <%= raw single_navigation(:about,:internal) %> -
-
+
+<%= raw single_navigation(:about,:internal) %> +
+
<%= t(:current_version,:scope=>:internal) + ": " + $defaults["version"] %>
-
-
-<%= render :file => 'config/about.txt' %>
-<%= render :text => "To do:\n" %>
-<%= render :file => 'config/todo.txt' %>
-
+ +
+<%= raw BlueCloth::new(render :file => 'README.markdown').to_html %> +
+
+<%= raw BlueCloth::new(render :file => 'CHANGES').to_html %> +
+
+<%= raw BlueCloth::new(render :file => 'TODO').to_html %> +
-
diff --git a/themes/olive/views/layouts/application.html.erb b/themes/olive/views/layouts/application.html.erb index 1605793..b2a03ab 100755 --- a/themes/olive/views/layouts/application.html.erb +++ b/themes/olive/views/layouts/application.html.erb @@ -11,6 +11,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" <%=stylesheet_link_tag current_theme_stylesheet_path('base') %> <%=stylesheet_link_tag current_theme_stylesheet_path('style') %> + <%=stylesheet_link_tag current_theme_stylesheet_path('calendar_olive') %> <%=stylesheet_link_tag current_theme_stylesheet_path('jquery-ui-dialog') %> <%=stylesheet_link_tag current_theme_stylesheet_path('jquery-ui-custom') %> diff --git a/themes/olive/views/sidebar/_calendar_view.html.erb b/themes/olive/views/sidebar/_calendar_view.html.erb index 6b5cc10..3a5f5db 100755 --- a/themes/olive/views/sidebar/_calendar_view.html.erb +++ b/themes/olive/views/sidebar/_calendar_view.html.erb @@ -1,3 +1,4 @@ -
- <%= raw calendar_small %> -
+<%= calendar_square(:month_delta=>-1) %> +<%= calendar_square() %> +<%= calendar_square(:month_delta=>1) %> + diff --git a/vendor/plugins/calendar_view/MIT-LICENSE b/vendor/plugins/calendar_view/MIT-LICENSE deleted file mode 100755 index bfe9381..0000000 --- a/vendor/plugins/calendar_view/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2011 [name of plugin creator] - -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. diff --git a/vendor/plugins/calendar_view/README b/vendor/plugins/calendar_view/README deleted file mode 100755 index d23ccd9..0000000 --- a/vendor/plugins/calendar_view/README +++ /dev/null @@ -1,13 +0,0 @@ -CalendarView -============ - -Introduction goes here. - - -Example -======= - -Example goes here. - - -Copyright (c) 2011 [name of plugin creator], released under the MIT license diff --git a/vendor/plugins/calendar_view/Rakefile b/vendor/plugins/calendar_view/Rakefile deleted file mode 100755 index 441a5c7..0000000 --- a/vendor/plugins/calendar_view/Rakefile +++ /dev/null @@ -1,23 +0,0 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -desc 'Default: run unit tests.' -task :default => :test - -desc 'Test the calendar_view plugin.' -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.libs << 'test' - t.pattern = 'test/**/*_test.rb' - t.verbose = true -end - -desc 'Generate documentation for the calendar_view plugin.' -Rake::RDocTask.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'CalendarView' - rdoc.options << '--line-numbers' << '--inline-source' - rdoc.rdoc_files.include('README') - rdoc.rdoc_files.include('lib/**/*.rb') -end \ No newline at end of file diff --git a/vendor/plugins/calendar_view/init.rb b/vendor/plugins/calendar_view/init.rb deleted file mode 100755 index 46003ef..0000000 --- a/vendor/plugins/calendar_view/init.rb +++ /dev/null @@ -1 +0,0 @@ -require 'calendar_view' diff --git a/vendor/plugins/calendar_view/install.rb b/vendor/plugins/calendar_view/install.rb deleted file mode 100755 index f7732d3..0000000 --- a/vendor/plugins/calendar_view/install.rb +++ /dev/null @@ -1 +0,0 @@ -# Install hook code here diff --git a/vendor/plugins/calendar_view/lib/app/helpers/calendar_view_helper.rb b/vendor/plugins/calendar_view/lib/app/helpers/calendar_view_helper.rb deleted file mode 100755 index fe3c7c4..0000000 --- a/vendor/plugins/calendar_view/lib/app/helpers/calendar_view_helper.rb +++ /dev/null @@ -1,56 +0,0 @@ -module CalendarViewHelper - def calendar_small(options={}) - now = DateTime.now - first = Date.new(now.year,now.month,1) - last = Date.new(now.year,now.month,-1) - curr_week = first.cweek - html = "

" - html << t(:month_names,:scope=>:date)[now.month] - html << "

" - html << "" - - html << "" - 1.upto(6) do |i| - html << "" - end - html << "" - html << "" - - - html << "" - html << "" - - (first.wday-1).downto(1) do |i| - prev = first - i - html << "" - end - - (first.day).upto(last.day) do |i| - curr = Date.new(now.year,now.month,i) - if curr.wday == 1 - html << "" - html << "" - curr_week += 1 - html << "" - end - if now.day == i - html << "" - else - if curr.wday == 0 || curr.wday == 6 - html << "" - else - html << "" - end - end - end - - 1.upto(7-last.wday) do |i| - post = last + i - html << "" - end - - html << "" - html << "
#{t(:abbr_day_names,:scope=>:date)[i]}#{t(:abbr_day_names,:scope=>:date)[0]}
#{first.cweek}#{prev.day}
#{curr_week}#{i}#{i}#{i}#{post.day}
" - html - end -end diff --git a/vendor/plugins/calendar_view/lib/calendar_view.rb b/vendor/plugins/calendar_view/lib/calendar_view.rb deleted file mode 100755 index 6dbc90a..0000000 --- a/vendor/plugins/calendar_view/lib/calendar_view.rb +++ /dev/null @@ -1,2 +0,0 @@ -require File.join(File.dirname(__FILE__), 'app', 'helpers', 'calendar_view_helper') -ActionController::Base.helper(CalendarViewHelper) diff --git a/vendor/plugins/calendar_view/test/calendar_view_test.rb b/vendor/plugins/calendar_view/test/calendar_view_test.rb deleted file mode 100755 index 35edf62..0000000 --- a/vendor/plugins/calendar_view/test/calendar_view_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'test_helper' - -class CalendarViewTest < ActiveSupport::TestCase - # Replace this with your real tests. - test "the truth" do - assert true - end -end diff --git a/vendor/plugins/calendar_view/test/test_helper.rb b/vendor/plugins/calendar_view/test/test_helper.rb deleted file mode 100755 index 2ca36a1..0000000 --- a/vendor/plugins/calendar_view/test/test_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'rubygems' -require 'test/unit' -require 'active_support' diff --git a/vendor/plugins/calendar_view/uninstall.rb b/vendor/plugins/calendar_view/uninstall.rb deleted file mode 100755 index 9738333..0000000 --- a/vendor/plugins/calendar_view/uninstall.rb +++ /dev/null @@ -1 +0,0 @@ -# Uninstall hook code here From 9e31ca239db9ddb05f3be5c230c9f79c37028280 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Fri, 23 Sep 2011 21:37:23 +0200 Subject: [PATCH 06/38] TODO, CHANGES added --- CHANGES | 10 ++++++++++ TODO | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 CHANGES create mode 100644 TODO diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..1c4a5a7 --- /dev/null +++ b/CHANGES @@ -0,0 +1,10 @@ +## Changes + +#### Version 0.8.4 + + * calendar view as separate gem + * adding bluecloth for rendering markdown text + +#### Version 0.8.3 + + * export, imports of contact diff --git a/TODO b/TODO new file mode 100644 index 0000000..1ef14db --- /dev/null +++ b/TODO @@ -0,0 +1,21 @@ +## Todo + +app/controllers/folders_controller.rb: + + * [ 30] [TODO] recreate local copy of folders + * [ 99] [TODO] save system folders + +app/controllers/messages_controller.rb: + + * [101] [FIXME] missing fields and support arrays + +app/controllers/messages_ops_controller.rb: + + * [128] [FIXME] check if uploads directory exists + * [176] [FIXME] check if domain is set + * [192] [TODO] check if email address is valid if not get address from contacts + * [259] [FIXME] edit does not support attachments + +app/models/prefs.rb: + + * [ 19] [TODO] move refresh to prefs and make refresh page with messages From 9bb5f3a20fad1879d6a7f97ae6e8299984612712 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Sat, 24 Sep 2011 22:13:45 +0200 Subject: [PATCH 07/38] servers view --- CHANGES | 9 +++++++-- Gemfile | 2 +- app/helpers/application_helper.rb | 3 +++ app/helpers/prefs_helper.rb | 14 ++++++++++++++ config/defaults.yml | 2 ++ config/locales/pl.yml | 9 +++++++++ themes/olive/views/prefs/_server.html.erb | 9 +++++++++ themes/olive/views/prefs/_servers_list.html.erb | 16 ++++++++++++++++ themes/olive/views/prefs/servers.html.erb | 13 ++++--------- 9 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 themes/olive/views/prefs/_server.html.erb create mode 100644 themes/olive/views/prefs/_servers_list.html.erb diff --git a/CHANGES b/CHANGES index 1c4a5a7..ea0208c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,15 @@ ## Changes -#### Version 0.8.4 +#### 0.8.5 candidate + + * servers view + * identity modification + +#### 0.8.4 * calendar view as separate gem * adding bluecloth for rendering markdown text -#### Version 0.8.3 +#### 0.8.3 * export, imports of contact diff --git a/Gemfile b/Gemfile index 449a132..8aa7193 100755 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'http://rubygems.org' -gem 'rails', '~>3.0.7' +gem 'rails', '~>3.0.9' gem 'mysql2' , '~>0.2.7' gem 'will_paginate', '~> 3.0.beta' diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6dbc0f6..957e311 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -325,5 +325,8 @@ def content_for_sidebar s end +def boolean_answer(answer) + answer == true ? t(:true_answer,:scope=>:common) : t(:false_answer,:scope=>:common) +end end diff --git a/app/helpers/prefs_helper.rb b/app/helpers/prefs_helper.rb index a28d430..1d01dcf 100755 --- a/app/helpers/prefs_helper.rb +++ b/app/helpers/prefs_helper.rb @@ -1,2 +1,16 @@ module PrefsHelper + def servers_table_header + html = "" + $defaults["servers_table_fields"].each do |f| + html << "" + 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 << "" + end + html + end + end diff --git a/config/defaults.yml b/config/defaults.yml index 526280e..0827102 100755 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -13,6 +13,8 @@ msg_image_thumbnail_size: [128x128, 128x96, 192x192, 192x144, 256x256, 256x192] contacts_table_fields: [nick, first_name, last_name, email, info] contacts_per_page: 25 +servers_table_fields: [name, port, use_ssl, use_tls, for_imap, for_smtp, auth] + msgs_per_page: 20 msgs_refresh_time: 300 msgs_send_type: html diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 1135ce8..20929ac 100755 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -17,6 +17,7 @@ pl: record_invalid: Nieprawidłowy format danych models: contact: Kontakt + server: Serwer attributes: contact: nick: Pseudonim @@ -47,6 +48,12 @@ pl: last_name: Nazwisko server: name: Nazwa serwera + port: Port + use_ssl: SSL? + use_tls: TLS? + for_imap: IMAP? + for_smtp: SMTP? + auth: Metoda autoryzacji contact: compose_to_selected: Napisz do wybranych @@ -175,6 +182,8 @@ pl: current_version: Aktualna wersja common: + false_answer: Nie + true_answer: Tak file_format_error: Błędny format pliku no_tmp_dir: Brak katalogu tymczasowego must_be_unique: musi być unikalny diff --git a/themes/olive/views/prefs/_server.html.erb b/themes/olive/views/prefs/_server.html.erb new file mode 100644 index 0000000..ca8b0f8 --- /dev/null +++ b/themes/olive/views/prefs/_server.html.erb @@ -0,0 +1,9 @@ +<%= check_box_tag "cids[]", server.id %> +<%= server.name %> +<%= server.port %> +<%= boolean_answer(server.use_ssl) %> +<%= boolean_answer(server.use_tls) %> +<%= boolean_answer(server.for_imap) %> +<%= boolean_answer(server.for_smtp) %> +<%= server.auth %> +  diff --git a/themes/olive/views/prefs/_servers_list.html.erb b/themes/olive/views/prefs/_servers_list.html.erb new file mode 100644 index 0000000..10aec53 --- /dev/null +++ b/themes/olive/views/prefs/_servers_list.html.erb @@ -0,0 +1,16 @@ + + + + +<%= raw servers_table_header %> + + +<% trclass = :even %> +<% @servers.each do |s| %> + +<%= render :partial => 'prefs/server', :object => s %> + +<% trclass == :even ? trclass = :odd : trclass = :even %> +<% end %> + +
diff --git a/themes/olive/views/prefs/servers.html.erb b/themes/olive/views/prefs/servers.html.erb index 273b073..154b8de 100755 --- a/themes/olive/views/prefs/servers.html.erb +++ b/themes/olive/views/prefs/servers.html.erb @@ -13,15 +13,10 @@
<%= raw prefs_navigation(:servers_tab) %>
-
<%= form_tag(prefs_update_servers_path,:name=>'prefs') %> -
-
- <%= render :partial => 'prefs/servers_left' %> -
-
- <%= render :partial => 'prefs/servers_right' %> -
-
+
+ <%= form_tag(prefs_update_servers_path,:name=>'prefs') %> + <%= render :partial => 'prefs/servers_list' %> +
From ef0b894ad8e7f5f1bc644e70267dd4339e6b05b2 Mon Sep 17 00:00:00 2001 From: Wojciech Todryk Date: Thu, 29 Sep 2011 21:16:40 +0200 Subject: [PATCH 08/38] identity,servers view --- CHANGES | 2 +- Gemfile | 4 +- TODO | 7 +- app/controllers/application_controller.rb | 12 +- app/controllers/folders_controller.rb | 11 +- app/controllers/messages_ops_controller.rb | 187 ++++++++++-------- app/controllers/prefs_controller.rb | 15 +- app/controllers/user_controller.rb | 8 +- app/helpers/application_helper.rb | 2 +- app/models/user.rb | 26 ++- config/application.rb | 4 + config/defaults.yml | 4 +- config/locales/pl.yml | 9 +- config/routes.rb | 170 ++++++---------- .../20110927091830_rename_email_in_user.rb | 9 + db/schema.rb | 27 +-- lib/imap_session.rb | 2 +- themes/olive/stylesheets/style.css | 8 +- themes/olive/views/layouts/_flash.html.erb | 0 .../olive/views/messages/_attachment.html.erb | 4 +- .../views/messages/_file_select.html.erb | 6 +- .../olive/views/messages/_html_part.html.erb | 2 +- themes/olive/views/messages/_message.html.erb | 2 +- themes/olive/views/messages/_new.html.erb | 3 - themes/olive/views/messages/compose.html.erb | 4 +- .../olive/views/prefs/_identity_left.html.erb | 25 ++- themes/olive/views/prefs/_server.html.erb | 0 .../olive/views/prefs/_servers_list.html.erb | 0 themes/olive/views/user/login.html.erb | 4 +- themes/olive/views/user/setup.html.erb | 2 +- themes/olive/views/user/unknown.html.erb | 2 +- 31 files changed, 281 insertions(+), 280 deletions(-) mode change 100644 => 100755 CHANGES mode change 100644 => 100755 TODO create mode 100755 db/migrate/20110927091830_rename_email_in_user.rb mode change 100644 => 100755 themes/olive/views/layouts/_flash.html.erb mode change 100644 => 100755 themes/olive/views/prefs/_server.html.erb mode change 100644 => 100755 themes/olive/views/prefs/_servers_list.html.erb diff --git a/CHANGES b/CHANGES old mode 100644 new mode 100755 index ea0208c..61bd801 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ ## Changes -#### 0.8.5 candidate +#### 0.8.5 * servers view * identity modification diff --git a/Gemfile b/Gemfile index 8aa7193..f975baa 100755 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,10 @@ source 'http://rubygems.org' -gem 'rails', '~>3.0.9' +gem 'rails', '~>3.0.7' gem 'mysql2' , '~>0.2.7' gem 'will_paginate', '~> 3.0.beta' gem 'themes_for_rails' gem "ezcrypto", "~> 0.7.2" -gem "calendar_view", "~> 0.0.3" +gem "calendar_view", "~> 0.0.4" gem 'bluecloth', '>= 2.0.0' diff --git a/TODO b/TODO old mode 100644 new mode 100755 index 1ef14db..2979b2d --- a/TODO +++ b/TODO @@ -11,11 +11,10 @@ app/controllers/messages_controller.rb: app/controllers/messages_ops_controller.rb: - * [128] [FIXME] check if uploads directory exists - * [176] [FIXME] check if domain is set - * [192] [TODO] check if email address is valid if not get address from contacts - * [259] [FIXME] edit does not support attachments + * [249] [FIXME] edit does not support attachments + * [304] [TODO] check if email address is valid if not get address from contacts app/models/prefs.rb: * [ 19] [TODO] move refresh to prefs and make refresh page with messages + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 008b927..d9697a4 100755 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -63,15 +63,15 @@ class ApplicationController < ActionController::Base def prepare_compose_buttons @buttons = [] - @buttons << {:text => 'send',:scope=>:compose,:image => 'email.png'} - @buttons << {:text => 'save_as_draft',:scope=>:compose,:image => 'save.png'} + @buttons << {:text => 'sendout',:scope=>:compose,:image => 'email.png'} + @buttons << {:text => 'save',:scope=>:compose,:image => 'save.png'} end def create_message_with_params - @message = Message.new - if params[:message] - @message.update_attributes(params[:message]) - end + @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| diff --git a/app/controllers/folders_controller.rb b/app/controllers/folders_controller.rb index f661d77..e60ff85 100755 --- a/app/controllers/folders_controller.rb +++ b/app/controllers/folders_controller.rb @@ -12,13 +12,12 @@ class FoldersController < ApplicationController after_filter :close_imap_session, :except => [:index,:show_hide,:system] before_filter :get_folders + before_filter :prepare_buttons_to_folders theme :theme_resolver def index - @buttons = [] - @buttons << {:text => 'show_hide',:scope=>'folder',:image => 'flag.png'} - @buttons << {:text => 'refresh',:scope=>'folder',:image => 'refresh.png'} + #before_filter end def create @@ -154,6 +153,12 @@ class FoldersController < ApplicationController 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 diff --git a/app/controllers/messages_ops_controller.rb b/app/controllers/messages_ops_controller.rb index b675bc9..6f5c628 100755 --- a/app/controllers/messages_ops_controller.rb +++ b/app/controllers/messages_ops_controller.rb @@ -3,6 +3,7 @@ require 'imap_mailbox' require 'imap_message' require 'mail' require 'mail_plugin_extension' +require 'net/smtp' class MessagesOpsController < ApplicationController @@ -14,8 +15,9 @@ class MessagesOpsController < ApplicationController 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 => [:sendout_or_save,:single,:multi] - before_filter :create_message_with_params , :only => [:sendout_or_save] + 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 @@ -125,17 +127,20 @@ class MessagesOpsController < ApplicationController end def upload - #FIXME check if uploads directory exists - @operation = :upload - create_message_with_params - if not params[:upload] - flash[:error] = t(:no_file_chosen,:scope=>:common) - else - name = params[:upload][:datafile].original_filename + 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[:upload][:datafile].read) } + 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 @@ -168,13 +173,7 @@ class MessagesOpsController < ApplicationController # if File.exist?("#{RAILS_ROOT}/dirname/#{@filename}") # end - - ############################################### sendout_or_save ############################ - - def sendout_or_save - - #FIXME check if domain is set - + 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) @@ -184,77 +183,70 @@ class MessagesOpsController < ApplicationController @operation = :new render 'messages/compose' return - end - - mail = Mail.new - mail.subject = params[:message][:subject] - mail.from = @current_user.full_address - #TODO check if email address is valid if not get address from contacts - mail.to = params[:message][:to_addr] - mail.body = params[:message][:body] - - attachments = Dir.glob(File.join($defaults["msg_upload_dir"],@current_user.username + "*")) - #logger.custom('attach',attachments.inspect) - attachments.each do |a| - mail.add_file :filename => File.basename(a.gsub(/#{@current_user.username}_/,"")), :content => File.read(a) - end - - if params[:send] - smtp_server = @current_user.servers.primary_for_smtp - - if smtp_server.nil? - flash[:error] = t(:not_configured_smtp,:scope => :compose) - @operation = :new - render 'messages/compose' - return - end - - begin - - set_mail_defaults(@current_user,smtp_server,session) - logger.custom('mail',Mail.delivery_method.inspect) - - @response = mail.deliver! - logger.custom('response',@response.inspect) - - if @sent_folder.nil? - raise t(:not_configured_sent,:scope=>:compose) - end - @mailbox.append(@sent_folder.full_name,mail.to_s,[:Seen]) - - rescue Exception => e - flash[:error] = "#{t(:imap_err,:scope=>:internal)} (#{e.to_s})" - redirect_to :controller => 'messages', :action => 'index' - return - end - - attachments.each do |filename| - path = File.join(Rails.root,filename) - File.delete(path) if File.exist?(path) - end - - flash[:notice] = t(:was_sent,:scope => :compose) - redirect_to :controller => 'messages', :action => 'index' - elsif params[:save_as_draft] - begin - if @drafts_folder.nil? - raise t(:not_configured_drafts,:scope=>:compose) - end - @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 Exception => e - flash[:error] = "#{t(:imap_error,:scope=>:internal)} (#{e.to_s})" - redirect_to :controller => 'messages', :action => 'index' - return - end - flash[:notice] = t(:was_saved,:scope => :compose) + 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] + ) + 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[:notice] = 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) + File.delete(path) if File.exist?(path) + end + flash[:notice] = t(:was_saved,:scope => :compose) + end + redirect_to :controller => 'messages', :action => 'index' + end #FIXME edit does not support attachments def edit @@ -266,7 +258,7 @@ class MessagesOpsController < ApplicationController imap_message = @mailbox.fetch_body(old_message.uid) mail = Mail.new(imap_message) if mail.multipart? - @message.body = mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") + @message.body = mail.text_part.nil? ? "" : mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") else @message.body = mail.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") end @@ -285,7 +277,7 @@ class MessagesOpsController < ApplicationController imap_message = @mailbox.fetch_body(old_message.uid) mail = Mail.new(imap_message) if mail.multipart? - @message.body = mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") + @message.body = mail.text_part.nil? ? "" : mail.text_part.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") else @message.body = mail.decoded_and_charseted.gsub(/<\/?[^>]*>/, "") end @@ -297,7 +289,28 @@ class MessagesOpsController < ApplicationController 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.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 #################################### @@ -313,7 +326,7 @@ class MessagesOpsController < ApplicationController authentication = server.auth enable_starttls_auto = server.use_tls openssl_verify_mode = OpenSSL::SSL::VERIFY_NONE - user_name = user.full_address + user_name = user.login end Mail.defaults do delivery_method :smtp, {:address => server.name, diff --git a/app/controllers/prefs_controller.rb b/app/controllers/prefs_controller.rb index cdb0b6d..a3032ad 100755 --- a/app/controllers/prefs_controller.rb +++ b/app/controllers/prefs_controller.rb @@ -22,10 +22,18 @@ class PrefsController < ApplicationController end def update_identity - if params[:user] - @current_user.update_attributes(params[:user]) + 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[:notice] = t(:were_saved,:scope=>:prefs) + redirect_to :action => 'identity' + else + render 'prefs/identity' + end end - redirect_to :action => 'identity' end def look @@ -33,7 +41,6 @@ class PrefsController < ApplicationController end def identity - @identity = @curent_user end def servers diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index ad2e7b1..b07c1e1 100755 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -15,15 +15,15 @@ class UserController < ApplicationController def authenticate if not $defaults["only_can_logins"].nil? - if not $defaults["only_can_logins"].include?(params[:user][:email]) + if not $defaults["only_can_logins"].include?(params[:user][:login]) redirect_to :controller => 'internal', :action => 'onlycanlogins' return false end end - user = User.find_by_email(params[:user][:email]) + user = User.find_by_login(params[:user][:login]) if user.nil? - redirect_to :action => 'unknown' ,:email=> params[:user][:email] + redirect_to :action => 'unknown' ,:login=> params[:user][:login] else session[:user_id] = user.id user.set_cached_password(session,params[:user][:password]) @@ -52,7 +52,7 @@ class UserController < ApplicationController def create @user = User.new - @user.email = params[:user][:email] + @user.login = params[:user][:login] @user.first_name = params[:user][:first_name] @user.last_name = params[:user][:last_name] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 957e311..f3f1f92 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,7 +5,7 @@ module ApplicationHelper def form_field(object,field,flabel,example,val) model_name = eval(object.class.model_name) html = "" - html << "
" + html << "
" if not object.errors[field.to_sym].empty? html << "
" diff --git a/app/models/user.rb b/app/models/user.rb index a184b16..afc821c 100755 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,7 +5,7 @@ class User < ActiveRecord::Base #acts_as_notes_owner validates_presence_of :first_name,:last_name - validates_uniqueness_of :email + validates_uniqueness_of :login has_many :servers, :dependent => :destroy has_one :prefs, :dependent => :destroy has_many :folders, :dependent => :destroy @@ -33,21 +33,31 @@ class User < ActiveRecord::Base (0...8).map{65.+(rand(25)).chr}.join end - def full_name + def name first_name + " " + last_name end - def full_address - d = domain.presence || "" - if email =~ /\@/ - email + def full_id + (name + " <" + email + ">") if email + end + + def email + if login =~ /\@/ + login else - email + "@" + d + (login + "@" + domain) if domain.presence end end def username - email.gsub(/\@/,"_").gsub(/\./,"_") + login.gsub(/\@/,"_").gsub(/\./,"_") + end + + def has_domain? + return domain if domain.presence + if login =~ /\@/ + login.split(/\@/)[1] + end end end diff --git a/config/application.rb b/config/application.rb index ff7faab..7976b10 100755 --- a/config/application.rb +++ b/config/application.rb @@ -51,3 +51,7 @@ class ActiveSupport::BufferedLogger end end +class MailrException < Exception +end + + diff --git a/config/defaults.yml b/config/defaults.yml index 0827102..363000d 100755 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -1,4 +1,4 @@ -version: 0.8.4 +version: 0.8.5 theme: olive locale: pl @@ -47,4 +47,4 @@ session_password: asDD3s2@sAdc983# mailbox_max_parent_folder_depth: 3 # array of logins which only can login to application, comment it to allow everyone to login -only_can_logins: [soldier] +only_can_logins: [somelogin1, somelogin2] diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 20929ac..b54c2b5 100755 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -46,6 +46,7 @@ pl: password: Hasło first_name: Imię last_name: Nazwisko + domain: Domena server: name: Nazwa serwera port: Port @@ -147,9 +148,9 @@ pl: not_configured_smtp: Brak konfiguracji SMTP select_file: Wybierz plik delete_marked: Usuń zaznaczone - send_file: Wyślij plik - send: Wyślij - save_as_draft: Zapisz w katalogu roboczym + upload: Wyślij plik + sendout: Wyślij + save: Zapisz w katalogu roboczym show: reply: Odpowiedz @@ -171,6 +172,7 @@ pl: login: Logowanie only_can_logins: Podany identyfikator użytkownika nie uprawnia do korzystania z aplikacji logout: Wyloguj + has_no_domain: Użytkownik nie ma ustawionej domeny internal: imap_error: Błąd protokołu IMAP @@ -180,6 +182,7 @@ pl: unprocessable_entity: Błąd procesowania about: Informacje o programie current_version: Aktualna wersja + general_error: Błąd aplikacji common: false_answer: Nie diff --git a/config/routes.rb b/config/routes.rb index 6f6d2f6..9a33370 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,130 +1,70 @@ Mailr::Application.routes.draw do - #match "prefs/index" => "prefs#index", :as => :prefs + namespace :prefs do + post "update_look" + post "update_identity" + post "update_servers" + end + match "prefs/look" => "prefs#look", :as => :prefs_look + match "prefs/identity" => "prefs#identity", :as => :prefs_identity + match "prefs/servers" => "prefs#servers", :as => :prefs_servers - post "prefs/update_look" - post "prefs/update_identity" - post "prefs/update_servers" + namespace :contacts do + post "ops" + get "export" + end + match "/external" => "contacts#external", :as => :external - match "prefs/look" => "prefs#look", :as => :prefs_look - match "prefs/identity" => "prefs#identity", :as => :prefs_identity - match "prefs/servers" => "prefs#servers", :as => :prefs_servers + resources :contacts - post "contacts/ops" - get "contacts/export" - match "contacts/external" => "contacts#external", :as => :contacts_external - resources :contacts + namespace :folders do + post "create" + post "delete" + post "system" + post "show_hide" + post "refresh" + get "refresh_status" + get "emptybin" + end + match "/folders/index" => 'folders#index', :as => :folders + match "/folders/select/:id" => 'folders#select', :as => :folders_select - #resources :folders - match "folders/index" => 'folders#index', :as => :folders - post "folders/create" - post "folders/delete" - post "folders/system" - post "folders/show_hide" - post "folders/refresh" - get "folders/refresh_status" - post "folders/refresh" - match "folders/select/:id" => 'folders#select', :as => :folders_select - get "folders/emptybin" + namespace :internal do + get "error" + get "imaperror" + get "loginfailure" + get "onlycanlogins" + end + match "/internal/about" => 'internal#about' ,:as => :about - get "internal/error" - get "internal/imaperror" - get "internal/loginfailure" - get "internal/onlycanlogins" - match "internal/about" => 'internal#about' ,:as => :about - - match "messages_ops/single" => 'messages_ops#single' - match "messages_ops/multi" => 'messages_ops#multi' - match "messages_ops/sendout_or_save" => 'messages_ops#sendout_or_save' ,:as =>:sendout_or_save - match "messages_ops/upload" => 'messages_ops#upload',:as => :upload - match "messages_ops/edit/:id" => 'messages_ops#edit', :as => :messages_ops_edit + match "/messages_ops/single" => 'messages_ops#single' + match "/messages_ops/multi" => 'messages_ops#multi' + match "/messages_ops/sendout_or_save" => 'messages_ops#sendout_or_save' ,:as =>:sendout_or_save + match "/messages_ops/upload" => 'messages_ops#upload',:as => :upload + match "/messages_ops/edit/:id" => 'messages_ops#edit', :as => :edit + match "/messages_ops/composed" => 'messages_ops#composed', :as => :composed root :to => "messages#index" - match "messages/index" => 'messages#index', :as => :messages - match "messages/compose" => 'messages#compose', :as => :compose - match "messages/compose/:cid" => 'messages#compose', :as => :compose_contact - #get "messages/refresh_status" - #get "messages/emptybin" - #match "messages/select/:id" => 'messages#select', :as => :messages_select - get "messages/index" - #match 'messages/folder/:id' => 'messages#folder', :as => :messages_folder - #post "messages/ops" - #post "messages/msgops" - #match "messages/edit/:id" => 'messages#edit' ,:as => :messages_edit - #match "messages/reply/:id" => 'messages#reply' + match "/messages/index" => 'messages#index', :as => :messages + match "/messages/compose" => 'messages#compose', :as => :compose + match "/messages/compose/:cid" => 'messages#compose', :as => :compose_contact + match "/messages/show/:id" => 'messages#show' + match "/messages/html_body/:id" => 'messages#html_body' , :as => :html_body + match "/messages/attachment/:id/:idx" => 'messages#attachment', :as => :attachment_download - match "messages/show/:id" => 'messages#show' - #match "messages/body/:id/:idx" => 'messages#body' , :as => :messages_part_body - match "messages/html_body/:id" => 'messages#html_body' , :as => :messages_html_body - match "messages/attachment/:id/:idx" => 'messages#attachment', :as => :messages_attachment_download - - get "user/logout" - post "user/authenticate" - post "user/create" - get "user/login" - get "user/setup" - match 'user/setup/:id' => 'user#setup' - get "user/unknown" + namespace :user do + get "logout" + post "authenticate" + post "create" + get "login" + get "setup" + get "unknown" + end + match "/user/setup/:login" => 'user#setup' themes_for_rails - #acts_as_notes_owner - #match '*a', :to => 'internal#not_found' - - # The priority is based upon order of creation: - # first created -> highest priority. - - # Sample of regular route: - # match 'products/:id' => 'catalog#view' - # Keep in mind you can assign values other than :controller and :action - - # Sample of named route: - # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase - # This route can be invoked with purchase_url(:id => product.id) - - # Sample resource route (maps HTTP verbs to controller actions automatically): - # resources :products - # Sample resource route with options: - # resources :products do - # member do - # get 'short' - # post 'toggle' - # end - # - # collection do - # get 'sold' - # end - # end - - # Sample resource route with sub-resources: - # resources :products do - # resources :comments, :sales - # resource :seller - # end - - # Sample resource route with more complex sub-resources - # resources :products do - # resources :comments - # resources :sales do - # get 'recent', :on => :collection - # end - # end - - # Sample resource route within a namespace: - # namespace :admin do - # # Directs /admin/products/* to Admin::ProductsController - # # (app/controllers/admin/products_controller.rb) - # resources :products - # end - - # You can have the root of your site routed with "root" - # just remember to delete public/index.html. - - - # See how all your routes lay out with "rake routes" - - # This is a legacy wild controller route that's not recommended for RESTful applications. - # Note: This route will make all actions in every controller accessible via GET requests. - # match ':controller(/:action(/:id(.:format)))' + #match '*a', :to => 'internal#not_found' + #match ':controller(/:action(/:id(.:format)))' end diff --git a/db/migrate/20110927091830_rename_email_in_user.rb b/db/migrate/20110927091830_rename_email_in_user.rb new file mode 100755 index 0000000..11a9215 --- /dev/null +++ b/db/migrate/20110927091830_rename_email_in_user.rb @@ -0,0 +1,9 @@ +class RenameEmailInUser < ActiveRecord::Migration + def self.up + rename_column :users, :email, :login + end + + def self.down + rename_column :users, :login, :email + end +end diff --git a/db/schema.rb b/db/schema.rb index 8dfce34..ee3579e 100755 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110913114841) do +ActiveRecord::Schema.define(:version => 20110927091830) do create_table "contacts", :force => true do |t| t.string "nick" @@ -23,6 +23,18 @@ ActiveRecord::Schema.define(:version => 20110913114841) do t.datetime "updated_at" end + create_table "events", :force => true do |t| + t.integer "user_id" + t.integer "priority" + t.text "description" + t.string "category" + t.datetime "start" + t.datetime "stop" + t.boolean "allday" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "folders", :force => true do |t| t.string "name" t.string "delim" @@ -54,17 +66,6 @@ ActiveRecord::Schema.define(:version => 20110913114841) do t.datetime "updated_at" end - create_table "notes", :force => true do |t| - t.integer "owner_id" - t.string "owner_type" - t.string "title" - t.text "content" - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "notes", ["owner_id", "owner_type"], :name => "index_notes_on_owner_id_and_owner_type" - create_table "prefs", :force => true do |t| t.string "theme" t.string "locale" @@ -91,7 +92,7 @@ ActiveRecord::Schema.define(:version => 20110913114841) do end create_table "users", :force => true do |t| - t.string "email" + t.string "login" t.string "first_name" t.string "last_name" t.datetime "created_at" diff --git a/lib/imap_session.rb b/lib/imap_session.rb index 8183cce..13354fb 100755 --- a/lib/imap_session.rb +++ b/lib/imap_session.rb @@ -6,7 +6,7 @@ module ImapSessionModule def open_imap_session begin @mailbox ||= ImapMailboxModule::IMAPMailbox.new(logger,$defaults["imap_debug"]) - @mailbox.connect(@current_user.servers.primary_for_imap,@current_user.email, @current_user.get_cached_password(session)) + @mailbox.connect(@current_user.servers.primary_for_imap,@current_user.login, @current_user.get_cached_password(session)) rescue Exception => ex redirect_to :controller => 'internal', :action => 'loginfailure' end diff --git a/themes/olive/stylesheets/style.css b/themes/olive/stylesheets/style.css index a7658a2..3a8ac2a 100755 --- a/themes/olive/stylesheets/style.css +++ b/themes/olive/stylesheets/style.css @@ -403,6 +403,7 @@ div.param_group { div.param_group label.label { margin-right: 5px; + font-weight: bold; } div.params div.group { @@ -441,14 +442,15 @@ div.params input, div.params textarea, div.params select { border:1px solid #CFCFCF; } -div.params div.group .description { +div.params div.group .description, div.params div.param_group .description{ font-style: italic; color: #8C8C8C; - font-size: .9em; + font-size: 10px; } -div.params div.group .fieldWithErrors .error { +.fieldWithErrors .error { color: red; + font-size: 10px; } div.attachments { diff --git a/themes/olive/views/layouts/_flash.html.erb b/themes/olive/views/layouts/_flash.html.erb old mode 100644 new mode 100755 diff --git a/themes/olive/views/messages/_attachment.html.erb b/themes/olive/views/messages/_attachment.html.erb index aa75a35..0ff539b 100755 --- a/themes/olive/views/messages/_attachment.html.erb +++ b/themes/olive/views/messages/_attachment.html.erb @@ -1,5 +1,5 @@ -<%= link_to attachment.filename_charseted, messages_attachment_download_path(attachment.parent_id,attachment.idx) %> +<%= link_to attachment.filename_charseted, attachment_download_path(attachment.parent_id,attachment.idx) %> <%= attachment.main_type %>/<%= attachment.sub_type %> @@ -14,6 +14,6 @@ <%= size_formatter(attachment.getSize) %> -<%= link_to image_tag(current_theme_image_path('download.png')), messages_attachment_download_path(attachment.parent_id,attachment.idx) %> +<%= link_to image_tag(current_theme_image_path('download.png')), attachment_download_path(attachment.parent_id,attachment.idx) %> diff --git a/themes/olive/views/messages/_file_select.html.erb b/themes/olive/views/messages/_file_select.html.erb index 1f67932..7e02d1f 100755 --- a/themes/olive/views/messages/_file_select.html.erb +++ b/themes/olive/views/messages/_file_select.html.erb @@ -1,9 +1,7 @@
-<%= form_tag(upload_path, :multipart => true) %> :  -<%= file_field 'upload', 'datafile' %> -<%= raw single_action('send_file','compose','up.png') %> - +<%= file_field 'file', 'data' %> +<%= raw single_action('upload','compose','up.png') %>
diff --git a/themes/olive/views/messages/_html_part.html.erb b/themes/olive/views/messages/_html_part.html.erb index 51b0c68..7beb3d0 100755 --- a/themes/olive/views/messages/_html_part.html.erb +++ b/themes/olive/views/messages/_html_part.html.erb @@ -1,3 +1,3 @@ - diff --git a/themes/olive/views/messages/_message.html.erb b/themes/olive/views/messages/_message.html.erb index 51af893..6e81fa9 100755 --- a/themes/olive/views/messages/_message.html.erb +++ b/themes/olive/views/messages/_message.html.erb @@ -11,7 +11,7 @@ <%= date_formatter(message.date) %> <%= size_formatter(message.size) %> <% if @current_folder == @drafts_folder %> -<%= link_to(t(:edit,:scope=>:message),messages_ops_edit_path(message.uid)) %> +<%= link_to(t(:edit,:scope=>:message),edit_path(message.uid)) %> <% else %> <%= raw(' ') %> <% end %> diff --git a/themes/olive/views/messages/_new.html.erb b/themes/olive/views/messages/_new.html.erb index eaaae30..8ec1896 100755 --- a/themes/olive/views/messages/_new.html.erb +++ b/themes/olive/views/messages/_new.html.erb @@ -1,4 +1,3 @@ -<%= form_tag(sendout_or_save_path)%>
<%= raw form_field( @message, "to_addr", @@ -25,6 +24,4 @@ <%= hidden_field_tag 'olduid', @olduid %> <% end %> <%= raw group_action(@buttons) %> -<%= render :partial=> 'messages/file_attachs' %> - diff --git a/themes/olive/views/messages/compose.html.erb b/themes/olive/views/messages/compose.html.erb index 960f850..9b4707a 100755 --- a/themes/olive/views/messages/compose.html.erb +++ b/themes/olive/views/messages/compose.html.erb @@ -13,9 +13,11 @@

<%= t(:new_message,:scope=>:compose) %>

+ <%= form_tag(composed_path, :multipart => true) %> <%= render :partial => 'messages/new' %> + <%= render :partial=> 'messages/file_attachs' %> <%= render :partial => 'messages/file_select' %> -
+
diff --git a/themes/olive/views/prefs/_identity_left.html.erb b/themes/olive/views/prefs/_identity_left.html.erb index 2b601a5..1857e07 100755 --- a/themes/olive/views/prefs/_identity_left.html.erb +++ b/themes/olive/views/prefs/_identity_left.html.erb @@ -1,10 +1,21 @@ -
-

-<%= h @current_user.full_name %> -

-

-<%= h @current_user.full_address %> -

+<%= raw form_field(@current_user, + "first_name", + nil, + "Joe", + @current_user.first_name + ) %> +<%= raw form_field(@current_user, + "last_name", + nil, + "Doe", + @current_user.last_name + ) %> +<%= raw form_field(@current_user, + "domain", + nil, + "domain.com", + @current_user.domain + ) %>
<%= raw single_action('save','common','save.png') %> diff --git a/themes/olive/views/prefs/_server.html.erb b/themes/olive/views/prefs/_server.html.erb old mode 100644 new mode 100755 diff --git a/themes/olive/views/prefs/_servers_list.html.erb b/themes/olive/views/prefs/_servers_list.html.erb old mode 100644 new mode 100755 diff --git a/themes/olive/views/user/login.html.erb b/themes/olive/views/user/login.html.erb index 533e2fd..a37fe16 100755 --- a/themes/olive/views/user/login.html.erb +++ b/themes/olive/views/user/login.html.erb @@ -18,10 +18,10 @@
- <%= password_field "user", "password" %> + <%= password_field "user", "password" %>