addressbook 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +21 -0
- data/app/assets/images/addressbook/default.png +0 -0
- data/app/assets/javascripts/addressbook/application.js +15 -0
- data/app/assets/stylesheets/addressbook/application.css +15 -0
- data/app/controllers/addressbook/application_controller.rb +11 -0
- data/app/controllers/addressbook/contacts_controller.rb +96 -0
- data/app/controllers/addressbook/groups_controller.rb +53 -0
- data/app/helpers/addressbook/application_helper.rb +25 -0
- data/app/models/concerns/addressbook_owner.rb +29 -0
- data/app/views/addressbook/contacts/_address.html.erb +35 -0
- data/app/views/addressbook/contacts/_email.html.erb +15 -0
- data/app/views/addressbook/contacts/_form.html.erb +97 -0
- data/app/views/addressbook/contacts/_phone.html.erb +19 -0
- data/app/views/addressbook/contacts/destroy.js.erb +1 -0
- data/app/views/addressbook/contacts/edit.html.erb +3 -0
- data/app/views/addressbook/contacts/index.html.erb +115 -0
- data/app/views/addressbook/contacts/new.html.erb +3 -0
- data/app/views/addressbook/groups/_form.html.erb +25 -0
- data/app/views/addressbook/groups/destroy.js.erb +1 -0
- data/app/views/addressbook/groups/edit.html.erb +3 -0
- data/app/views/addressbook/groups/index.html.erb +29 -0
- data/app/views/addressbook/groups/new.html.erb +3 -0
- data/app/views/layouts/addressbook/application.html.erb +24 -0
- data/config/initializers/addressbook.rb +1 -0
- data/config/locales/en.yml +73 -0
- data/config/routes.rb +10 -0
- data/db/migrate/20141110080715_add_addressbook_account_id_to_user_table.rb +11 -0
- data/lib/addressbook.rb +23 -0
- data/lib/addressbook/account.rb +27 -0
- data/lib/addressbook/base_uploader.rb +33 -0
- data/lib/addressbook/contact.rb +128 -0
- data/lib/addressbook/engine.rb +10 -0
- data/lib/addressbook/group.rb +18 -0
- data/lib/addressbook/import_file_uploader.rb +7 -0
- data/lib/addressbook/photo_uploader.rb +27 -0
- data/lib/addressbook/resource.rb +24 -0
- data/lib/addressbook/version.rb +3 -0
- data/lib/generators/addressbook/controllers/controllers_generator.rb +42 -0
- data/lib/generators/addressbook/initializer/initializer_generator.rb +9 -0
- data/lib/generators/addressbook/views/views_generator.rb +42 -0
- data/lib/tasks/addressbook_tasks.rake +4 -0
- data/spec/controllers/addressbook/contacts_controller_spec.rb +169 -0
- data/spec/controllers/addressbook/groups_controller_spec.rb +106 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/account.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/addressbook.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20141119152457_create_accounts.rb +11 -0
- data/spec/dummy/db/migrate/20141119152801_add_addressbook_account_id_to_user_table.addressbook.rb +12 -0
- data/spec/dummy/db/schema.rb +27 -0
- data/spec/dummy/db/seeds.rb +1 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +13510 -0
- data/spec/dummy/log/test.log +1468 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/uploads/addressbook/contact/imported/test.csv +10 -0
- data/spec/dummy/public/uploads/addressbook/contact/imported/test.vcf +23 -0
- data/spec/dummy/spec/factories/accounts.rb +9 -0
- data/spec/dummy/spec/models/account_spec.rb +5 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/006961e21c4f6a44101d2e9587f863f4 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/032ca99c7bed652dbad8de691558648d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/06b00b7743f1b8c8020fabbb5c63e13a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0765e86c020fb088284e8f45c584f8c6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1648d5d40aa02876f2cbb311ab2c4712 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/17405e31165e7d8129c846ceabaafec0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1dcb75c883fc0e57d08de7a76f0ada73 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/30f5fe3df78dce04aa1f998f6d790113 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4b14ea7024b3a7934c5cf77c17cc92aa +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/604356b7aab9d373aa02548bf52b266b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/69fcb234f5e891fcb7fbeb727a703968 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6ec3857048c9b608a152271ff3991df6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/786158650544be3da3cd56983ff6540f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/790fb0d33aad92cb9a1745822d4668aa +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7954e67edaec86854ad0d70dfdc1df99 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/816240bd34b73344a7ac901c29906f1d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8a8a4eebb0809e6e5a1d1dc83dafb50d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8e844acaef03b740a1d21aafa3030124 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/91d3ad598e6e79937286c98b2b259cee +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/98e43302695126215ff9abe4b05eda56 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aa5e472ff34ac18d968ac316ca9caff2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aa9a9307e1b4596e0b92b403107a2cfb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aee3f8a82c799313c63b922ed8b0f975 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c1f56035111b730c0c3807bf5ca35251 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c497b90d7dd45d368ec4e83d97d0a2b9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d23b864a4a8239210d53e50644405687 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/dee437547d5fb5fab72af8a8d8582d4d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e9553c97dc4fe95dc10736ee0475dc45 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ec3c091506a3d1ea73ac726b7ee9b3c2 +0 -0
- data/spec/fixtures/test.csv +10 -0
- data/spec/fixtures/test.vcf +23 -0
- data/spec/model/addressbook/account_spec.rb +33 -0
- data/spec/model/addressbook/contact_spec.rb +79 -0
- data/spec/model/addressbook/group_spec.rb +11 -0
- data/spec/model/addressbook/resource_spec.rb +10 -0
- data/spec/spec_helper.rb +65 -0
- metadata +517 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
<div class="nested-fields addressbook-contact-fields">
|
2
|
+
<%= hidden_field_tag "contact[phones_attributes][][id]", phone.id if phone.id.present? %>
|
3
|
+
<%= hidden_field_tag "contact[phones_attributes][][_destroy]", false, class: 'destroy_association_field' %>
|
4
|
+
|
5
|
+
<div class="field">
|
6
|
+
<%= label_tag :number, t('addressbook.contact.number') %>
|
7
|
+
<%= text_field_tag "contact[phones_attributes][][number]", phone.number %>
|
8
|
+
</div>
|
9
|
+
<div class="field">
|
10
|
+
<%= label_tag :phone_type, t('addressbook.contact.phone_type') %>
|
11
|
+
<%= select_tag "contact[phones_attributes][][phone_type]", options_for_select(Addressbook::Contact::Phone::TYPES, phone.phone_type) %>
|
12
|
+
</div>
|
13
|
+
<div class="field">
|
14
|
+
<%= label_tag :preferred, t('addressbook.contact.preferred') %>
|
15
|
+
<%= check_box_tag "contact[phones_attributes][][preferred]", true, (phone.try(:preferred) || false) %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<a class="remove_contact_associaton" href="#"><%= t('addressbook.contact.remove_phone') %></a>
|
19
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
$('.addressbook-contact[data-id="<%= @contact.id %>"]').fadeOut().remove();
|
@@ -0,0 +1,115 @@
|
|
1
|
+
<%= link_to t('addressbook.contact.new_contact'), new_contact_path %>
|
2
|
+
<%= form_tag({ action: 'import_vcard' }, { class: 'import-form', multipart: true }) do %>
|
3
|
+
<input type="file" name="vcard" style="display: none;">
|
4
|
+
<%= link_to t('addressbook.contact.import_vcard'), '#', class: 'import-file' %>
|
5
|
+
<% end %>
|
6
|
+
<%= form_tag({ action: 'import_csv' }, { class: 'import-form', multipart: true }) do %>
|
7
|
+
<input type="file" name="csv" style="display: none;">
|
8
|
+
<%= link_to t('addressbook.contact.import_csv'), '#', class: 'import-file' %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<%= form_tag contacts_path, method: :get, class: 'form-horizontal' do %>
|
13
|
+
<div class="field">
|
14
|
+
<%= label_tag :query, t('addressbook.contact.query') %>
|
15
|
+
<%= text_field_tag :query, params[:query], placeholder: t('addressbook.contact.query_placeholder') %>
|
16
|
+
</div>
|
17
|
+
<%= submit_tag t('addressbook.actions.search') %>
|
18
|
+
<% end %>
|
19
|
+
</p>
|
20
|
+
|
21
|
+
<% unless @contacts.blank? %>
|
22
|
+
<p><%= t('addressbook.contact.total') %>: <%= @contacts.total_count %></p>
|
23
|
+
|
24
|
+
<table class="table table-striped table-bordered table-condensed">
|
25
|
+
<thead>
|
26
|
+
<th><%= t('addressbook.contact.photo') %></th>
|
27
|
+
<th><%= t('addressbook.contact.name') %></th>
|
28
|
+
<th><%= t('addressbook.contact.group') %></th>
|
29
|
+
<th><%= t('addressbook.contact.company_address') %></th>
|
30
|
+
<th><%= t('addressbook.contact.contact_methods') %></th>
|
31
|
+
<th><%= t('addressbook.contact.status') %></th>
|
32
|
+
<th><%= t('addressbook.contact.created_at') %></th>
|
33
|
+
<th></th>
|
34
|
+
</thead>
|
35
|
+
<tbody>
|
36
|
+
<% @contacts.each do |contact| %>
|
37
|
+
<tr class="addressbook-contact" data-id='<%= contact.id %>'>
|
38
|
+
<td><%= image_tag contact.thumb_url || 'addressbook/default.png' %></td>
|
39
|
+
<td>
|
40
|
+
<%= [contact.first_name, contact.last_name].reject(&:blank?).join(' ') %>
|
41
|
+
<% unless contact.title.blank? %>
|
42
|
+
<br>
|
43
|
+
<%= contact.title %>
|
44
|
+
<% end %>
|
45
|
+
<% unless contact.nickname.blank? %>
|
46
|
+
<br>
|
47
|
+
<%= contact.nickname %>
|
48
|
+
<% end %>
|
49
|
+
<% unless contact.dob.blank? %>
|
50
|
+
<br>
|
51
|
+
<%= Time.parse(contact.dob).strftime('%m/%d/%Y') %>
|
52
|
+
<% end %>
|
53
|
+
<% unless contact.gender.blank? %>
|
54
|
+
<br>
|
55
|
+
<%= contact.gender %>
|
56
|
+
<% end %>
|
57
|
+
</td>
|
58
|
+
<td><%= contact.groups.map { |x| x.name }. join(', ') %></td>
|
59
|
+
<td>
|
60
|
+
<% unless contact.company.blank? %>
|
61
|
+
<%= contact.company %>
|
62
|
+
<br>
|
63
|
+
<% end %>
|
64
|
+
<% unless (address = contact.addresses.first).blank? %>
|
65
|
+
<%= [address.line1, address.line2, address.line3].reject(&:blank?).join(' ') %>
|
66
|
+
<br>
|
67
|
+
<%= [address.zipcode, address.city, address.state].reject(&:blank?).join(' ') %>
|
68
|
+
<br>
|
69
|
+
<%= address.country %>
|
70
|
+
<br>
|
71
|
+
<% end %>
|
72
|
+
</td>
|
73
|
+
<td>
|
74
|
+
<% contact.emails.each do |m| %>
|
75
|
+
<span class="glyphicon glyphicon-envelope"></span>
|
76
|
+
<%= m.email %>
|
77
|
+
<br>
|
78
|
+
<% end %>
|
79
|
+
<% contact.phones.each do |p| %>
|
80
|
+
<span class="glyphicon glyphicon-#{phone_icon_class(p.phone_type)}"></span>
|
81
|
+
<%= p.number %>
|
82
|
+
<br>
|
83
|
+
<% end %>
|
84
|
+
<% unless contact.homepage.blank? %>
|
85
|
+
<span class="glyphicon glyphicon-globe"></span>
|
86
|
+
<%= link_to contact.homepage.gsub(/http(s)?:\/\//, ''), contact.homepage, target: '_blank' %>
|
87
|
+
<% end %>
|
88
|
+
</td>
|
89
|
+
<td><%= contact.status %></td>
|
90
|
+
<td><%= Time.parse(contact.created_at) %></td>
|
91
|
+
<td>
|
92
|
+
<%= link_to t('addressbook.actions.edit'), edit_contact_path(contact.id) %> |
|
93
|
+
<%= link_to t('addressbook.actions.delete'), contact_path(contact.id), method: :delete, remote: true, data: { confirm: t('addressbook.contact.delete_confirm') } %>
|
94
|
+
</td>
|
95
|
+
</tr>
|
96
|
+
<%end%>
|
97
|
+
</tbody>
|
98
|
+
</table>
|
99
|
+
<% else %>
|
100
|
+
<p><%= t('addressbook.contact.no_contacts') %></p>
|
101
|
+
<% end %>
|
102
|
+
|
103
|
+
<%= paginate @contacts %>
|
104
|
+
|
105
|
+
<script>
|
106
|
+
$('.import-file').click(function(e) {
|
107
|
+
e.preventDefault();
|
108
|
+
$(this).parents('form').find('input').click();
|
109
|
+
return false;
|
110
|
+
});
|
111
|
+
|
112
|
+
$('.import-form input').change(function(e) {
|
113
|
+
$(this).parents('form').submit();
|
114
|
+
});
|
115
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<%= form_for @group do |f| %>
|
2
|
+
<% if @group.errors.any? %>
|
3
|
+
<div id="error_explanation">
|
4
|
+
<h2><%= t('addressbook.group.error_explanation', count: @group.errors.count) %></h2>
|
5
|
+
<ul>
|
6
|
+
<% @group.errors.full_messages.each do |msg| %>
|
7
|
+
<li><%= msg %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<div class="field">
|
14
|
+
<%= f.label :name, t('addressbook.group.name') %>
|
15
|
+
<%= f.text_field :name %>
|
16
|
+
</div>
|
17
|
+
<div class="field">
|
18
|
+
<%= f.label :description, t('addressbook.group.description') %>
|
19
|
+
<%= f.text_field :description %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="form-actions">
|
23
|
+
<%= f.submit t('addressbook.actions.save') %>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
$('.addressbook-group[data-id="<%= @group.id %>"]').fadeOut().remove();
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<%= link_to t('addressbook.group.new_group'), new_group_path %>
|
2
|
+
|
3
|
+
<% unless @groups.blank? %>
|
4
|
+
<table class="table table-striped table-bordered table-condensed">
|
5
|
+
<thead>
|
6
|
+
<th><%= t('addressbook.group.name') %></th>
|
7
|
+
<th><%= t('addressbook.group.description') %></th>
|
8
|
+
<th><%= t('addressbook.group.contacts_count') %></th>
|
9
|
+
<th><%= t('addressbook.group.created_at') %></th>
|
10
|
+
<th></th>
|
11
|
+
</thead>
|
12
|
+
<tbody>
|
13
|
+
<% @groups.each do |group| %>
|
14
|
+
<tr class="addressbook-group" data-id='<%= group.id %>'>
|
15
|
+
<td><%= group.name %></td>
|
16
|
+
<td><%= group.description %></td>
|
17
|
+
<td><%= group.contacts_count %></td>
|
18
|
+
<td><%= Time.parse(group.created_at) %></td>
|
19
|
+
<td>
|
20
|
+
<%= link_to t('addressbook.actions.edit'), edit_group_path(group.id) %> |
|
21
|
+
<%= link_to t('addressbook.actions.delete'), group_path(group.id), method: :delete, remote: true, data: { confirm: t('addressbook.group.delete_confirm') } %>
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
<%end%>
|
25
|
+
</tbody>
|
26
|
+
</table>
|
27
|
+
<% else %>
|
28
|
+
<p><%= t('addressbook.group.no_groups') %></p>
|
29
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Addressbook</title>
|
5
|
+
<%= stylesheet_link_tag "addressbook/application", :media => "all" %>
|
6
|
+
<%= javascript_include_tag "addressbook/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<div id="addressbook_container">
|
12
|
+
<% unless flash.blank? %>
|
13
|
+
<% flash.each do |type, message| %>
|
14
|
+
<div class="alert alert-#{type}">
|
15
|
+
<%= message %>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<%= yield %>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
</body>
|
24
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
Addressbook.user_class = 'Account'
|
@@ -0,0 +1,73 @@
|
|
1
|
+
en:
|
2
|
+
addressbook:
|
3
|
+
actions:
|
4
|
+
create: Create
|
5
|
+
save: Save
|
6
|
+
edit: Edit
|
7
|
+
delete: Delete
|
8
|
+
add: Add
|
9
|
+
search: Search
|
10
|
+
contact:
|
11
|
+
new_contact: New contact
|
12
|
+
query: Query
|
13
|
+
query_placeholder: Enter name, email or phone number
|
14
|
+
name: Name
|
15
|
+
photo: Photo
|
16
|
+
group: Group
|
17
|
+
company_address: Company, Address
|
18
|
+
contact_methods: Contact Methods
|
19
|
+
status: Status
|
20
|
+
created_at: Created at
|
21
|
+
delete_confirm: Are you sure?
|
22
|
+
no_contacts: No contacts.
|
23
|
+
first_name: First Name
|
24
|
+
last_name: Last Name
|
25
|
+
nickname: Nickname
|
26
|
+
gender: Gender
|
27
|
+
company: Company
|
28
|
+
homepage: Homepage
|
29
|
+
dob: Birthday
|
30
|
+
create_notice: Contact was saved successfully.
|
31
|
+
update_notice: Contact was updated successfully.
|
32
|
+
import_notice:
|
33
|
+
zero: There is no contact to import
|
34
|
+
one: Imported 1 contact successfully.
|
35
|
+
other: 'Imported %{count} contact successfully.'
|
36
|
+
invalid_vcard: Please select valid VCard file.
|
37
|
+
invalid_csv: Please select valid CSV file.
|
38
|
+
email: Email
|
39
|
+
preferred: Preferred
|
40
|
+
address: Address
|
41
|
+
line1: Line 1
|
42
|
+
line2: Line 2
|
43
|
+
line3: Line 3
|
44
|
+
zipcode: Zipcode
|
45
|
+
city: City
|
46
|
+
state: State
|
47
|
+
country: Country
|
48
|
+
add_email: Add email
|
49
|
+
remove_email: Remove email
|
50
|
+
add_address: Add address
|
51
|
+
remove_address: Remove address
|
52
|
+
add_phone: Add phone
|
53
|
+
remove_phone: Remove phone
|
54
|
+
phone: Phone
|
55
|
+
number: Number
|
56
|
+
phone_type: Phone type
|
57
|
+
total: Total
|
58
|
+
import_vcard: Import VCard
|
59
|
+
error_explanation:
|
60
|
+
one: '1 error prohibited this contact from being saved:'
|
61
|
+
other: '%{count} errors prohibited this contact from being saved:'
|
62
|
+
group:
|
63
|
+
name: Name
|
64
|
+
description: Description
|
65
|
+
contacts_count: Contacts
|
66
|
+
new_group: New group
|
67
|
+
no_groups: You have no groups yet.
|
68
|
+
create_notice: Group was saved successfully.
|
69
|
+
update_notice: Group was updated successfully.
|
70
|
+
delete_confirm: Are you sure?
|
71
|
+
error_explanation:
|
72
|
+
one: '1 error prohibited this group from being saved:'
|
73
|
+
other: '%{count} errors prohibited this group from being saved:'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
class AddAddressbookAccountIdToUserTable < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column Addressbook.user_class.tableize, :addressbook_account_id, :integer
|
4
|
+
add_index Addressbook.user_class.tableize, :addressbook_account_id
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
remove_index Addressbook.user_class.tableize, :addressbook_account_id
|
9
|
+
remove_column Addressbook.user_class.tableize, :addressbook_account_id
|
10
|
+
end
|
11
|
+
end
|
data/lib/addressbook.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "addressbook/engine"
|
2
|
+
require 'carrierwave'
|
3
|
+
require 'fog'
|
4
|
+
require 'mini_magick'
|
5
|
+
require 'kaminari'
|
6
|
+
require 'jquery-rails'
|
7
|
+
require "addressbook/base_uploader"
|
8
|
+
require "addressbook/import_file_uploader"
|
9
|
+
require "addressbook/photo_uploader"
|
10
|
+
require "addressbook/resource"
|
11
|
+
require "addressbook/contact"
|
12
|
+
require "addressbook/group"
|
13
|
+
require "addressbook/account"
|
14
|
+
|
15
|
+
|
16
|
+
module Addressbook
|
17
|
+
|
18
|
+
mattr_accessor :user_class, :aws_config
|
19
|
+
|
20
|
+
def self.configure &block
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Addressbook
|
2
|
+
class Account < Resource
|
3
|
+
|
4
|
+
has_many :contacts, class_name: 'Addressbook::Contact'
|
5
|
+
has_many :groups, class_name: 'Addressbook::Group'
|
6
|
+
|
7
|
+
validates_presence_of :email
|
8
|
+
|
9
|
+
alias_method :original_contacts, :contacts
|
10
|
+
alias_method :original_groups, :groups
|
11
|
+
|
12
|
+
def contacts
|
13
|
+
original_contacts.extend Addressbook::Contact::RelationExtensions
|
14
|
+
original_contacts
|
15
|
+
end
|
16
|
+
|
17
|
+
def groups
|
18
|
+
original_groups.extend Addressbook::Group::RelationExtensions
|
19
|
+
original_groups
|
20
|
+
end
|
21
|
+
|
22
|
+
schema do
|
23
|
+
attribute 'email', :string
|
24
|
+
attribute 'name', :string
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Addressbook
|
4
|
+
class BaseUploader < CarrierWave::Uploader::Base
|
5
|
+
if Rails.env.test? or Rails.env.cucumber?# or (Rails.env.development? and Figaro.env.host == 'localhost:3000')
|
6
|
+
storage :file
|
7
|
+
else
|
8
|
+
storage :fog
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(*)
|
12
|
+
super
|
13
|
+
|
14
|
+
unless Rails.env.test?
|
15
|
+
self.fog_credentials = {
|
16
|
+
provider: 'AWS',
|
17
|
+
aws_access_key_id: Addressbook.aws_config['access_key_id'],
|
18
|
+
aws_secret_access_key: Addressbook.aws_config['secret_access_key'],
|
19
|
+
region: 'eu-west-1'
|
20
|
+
}
|
21
|
+
self.fog_directory = Addressbook.aws_config['bucket']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def store_dir
|
26
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.token}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def cache_dir
|
30
|
+
"tmp/uploads"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'active_resource_response'
|
2
|
+
|
3
|
+
module Addressbook
|
4
|
+
class Contact < Resource
|
5
|
+
extend CarrierWave::Mount
|
6
|
+
|
7
|
+
self.element_name = "contact"
|
8
|
+
add_response_method :http_response
|
9
|
+
|
10
|
+
GENDER = %w(male female)
|
11
|
+
|
12
|
+
mount_uploader :photo, PhotoUploader
|
13
|
+
|
14
|
+
module RelationExtensions
|
15
|
+
def new(params = {})
|
16
|
+
params.merge!(original_params)
|
17
|
+
resource_class.new(params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def count
|
21
|
+
http_response['X-total'].to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.search(query = {})
|
26
|
+
self.find(:all, params: query)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.import_vcard(account, vcard)
|
30
|
+
uploader = ImportFileUploader.new
|
31
|
+
File.open(vcard.tempfile) { |file| uploader.store!(file) }
|
32
|
+
Addressbook::Contact.get :import_vcard, { account_id: account.id, filename: uploader.filename }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.import_csv(account, csv)
|
36
|
+
uploader = ImportFileUploader.new
|
37
|
+
File.open(csv.tempfile) { |file| uploader.store!(file) }
|
38
|
+
Addressbook::Contact.get :import_csv, { account_id: account.id, filename: uploader.filename }
|
39
|
+
end
|
40
|
+
|
41
|
+
def full_name
|
42
|
+
[first_name, last_name].reject(&:blank?).join(' ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def active?
|
46
|
+
status == 'active'
|
47
|
+
end
|
48
|
+
|
49
|
+
schema do
|
50
|
+
attribute 'first_name', :string
|
51
|
+
attribute 'last_name', :string
|
52
|
+
attribute 'nickname', :string
|
53
|
+
attribute 'title', :string
|
54
|
+
attribute 'gender', :string
|
55
|
+
attribute 'company', :string
|
56
|
+
attribute 'homepage', :string
|
57
|
+
attribute 'dob', :string
|
58
|
+
attribute 'group_ids', :string
|
59
|
+
attribute 'token', :string
|
60
|
+
attribute 'status', :integer
|
61
|
+
attribute 'photo_file_name', :string
|
62
|
+
end
|
63
|
+
|
64
|
+
class Nested < Resource
|
65
|
+
def initialize(attributes={}, persisted=false)
|
66
|
+
@attributes = attributes.present? ? attributes.stringify_keys : default_attributes
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Email < Nested
|
71
|
+
|
72
|
+
schema do
|
73
|
+
attribute 'email', :string
|
74
|
+
attribute 'preferred', :boolean
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def default_attributes
|
80
|
+
{ email: nil, preferred: false }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Address < Nested
|
85
|
+
|
86
|
+
def full_address
|
87
|
+
[line1, line2, line3].reject(&:blank?).join(' ')
|
88
|
+
end
|
89
|
+
|
90
|
+
def location
|
91
|
+
[zipcode, city, state].reject(&:blank?).join(' ')
|
92
|
+
end
|
93
|
+
|
94
|
+
schema do
|
95
|
+
attribute 'line1', :string
|
96
|
+
attribute 'line2', :string
|
97
|
+
attribute 'line3', :string
|
98
|
+
attribute 'zipcode', :string
|
99
|
+
attribute 'city', :string
|
100
|
+
attribute 'state', :string
|
101
|
+
attribute 'country', :string
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def default_attributes
|
107
|
+
{ line1: nil, line2: nil, line3: nil, zipcode: nil, city: nil, state: nil, country: nil }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Phone < Nested
|
112
|
+
|
113
|
+
TYPES = %w(mobile home iPhone cell work other)
|
114
|
+
|
115
|
+
schema do
|
116
|
+
attribute 'number', :string
|
117
|
+
attribute 'phone_type', :string
|
118
|
+
attribute 'preferred', :boolean
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def default_attributes
|
124
|
+
{ number: nil, phone_type: nil, preferred: false }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|