Commit 7332d0d429d673aa611264155d5df5d93988798d
Exists in
master
and in
29 other branches
Merge remote-tracking branch 'dani/ActionItem3234' into stoa-merge
Conflicts: app/controllers/my_profile/memberships_controller.rb app/models/profile.rb app/views/friends/index.html.erb test/unit/profile_test.rb
Showing
42 changed files
with
1274 additions
and
214 deletions
Show diff stats
INSTALL.chat.md
1 | -XMPP/Chat Client Setup | |
2 | -====================== | |
1 | +XMPP/Chat Setup | |
2 | +=============== | |
3 | 3 | |
4 | -To configure XMPP/BOSH in Noosfero you need: | |
4 | +The samples of config file to configure a XMPP/BOSH server with ejabberd, | |
5 | +postgresql and apache2 can be found at util/chat directory. | |
5 | 6 | |
6 | -* REST Client - http://github.com/archiloque/rest-client | |
7 | -* SystemTimer - http://ph7spot.com/musings/system-timer | |
8 | -* Pidgin data files - http://www.pidgin.im/ | |
7 | +This setup supposes that you are using Noosfero installed via Debian package | |
8 | +in a production environment. | |
9 | 9 | |
10 | -If you use Debian 6.0 (squeeze): | |
11 | - | |
12 | - # apt-get install librestclient-ruby pidgin-data ruby1.8-dev | |
13 | - # gem install SystemTimer | |
14 | - | |
15 | -The samples of config file to configure a XMPP/BOSH server with ejabberd, postgresql and apache2 can be found at util/chat directory. | |
16 | - | |
17 | -XMPP/Chat Server Setup | |
18 | -====================== | |
10 | +Steps | |
11 | +===== | |
19 | 12 | |
20 | 13 | This is a step-by-step guide to get a XMPP service working, in a Debian system. |
21 | 14 | |
22 | 15 | ## 1. Install the required packages |
23 | 16 | |
24 | - # apt-get install ejabberd odbc-postgresql | |
17 | + # apt-get install ejabberd odbc-postgresql librestclient-ruby pidgin-data ruby1.8-dev | |
18 | + # gem install SystemTimer | |
25 | 19 | |
26 | 20 | ## 2. Ejabberd configuration |
27 | 21 | |
28 | -All the following changes must be done in config file: `/etc/ejabberd/ejabberd.cfg` | |
29 | - | |
30 | -### 2.1. Set the default admin user | |
31 | - | |
32 | - { acl, admin, { user, "john", "www.example.com" } }. | |
33 | - { acl, admin, { user, "bart", "www.example.com" } }. | |
34 | - | |
35 | -### 2.2. Set the default host | |
36 | - | |
37 | - { hosts, [ "www.example.com" ] }. | |
38 | - | |
39 | -### 2.3. Http-Bind activation | |
40 | - | |
41 | - { 5280, ejabberd_http, [ | |
42 | - http_bind, | |
43 | - web_admin | |
44 | - ] | |
45 | - } | |
46 | - | |
47 | - (...) | |
48 | - | |
49 | - { modules, [ | |
50 | - {mod_http_bind, []}, | |
51 | - ... | |
52 | - ] }. | |
53 | - | |
54 | -Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client needs non-anonymous room, then we need to change default params of creation rooms in ejabberd to create non-anonymous rooms. | |
55 | - | |
56 | -In non-anonymous rooms the jabber service sends the new occupant's full JID to all occupants in the room [[1]]. | |
22 | + # cp /usr/share/noosfero/util/chat/ejabberd.cfg /etc/ejabberd/ | |
57 | 23 | |
58 | -Add option "`{default_room_options, [{anonymous, false}]}`" to `/etc/ejabberd/ejabberd.cfg` in mod_muc session. See below: | |
24 | +Edit the /etc/ejabberd/ejabberd.cfg file and set your domain on the first 2 lines. | |
59 | 25 | |
60 | - { mod_muc, [ | |
61 | - %%{host, "conference.@HOST@"}, | |
62 | - {access, muc}, | |
63 | - {access_create, muc}, | |
64 | - {access_persistent, muc}, | |
65 | - {access_admin, muc_admin}, | |
66 | - {max_users, 500}, | |
67 | - {default_room_options, [{anonymous, false}]} | |
68 | - ]}, | |
69 | - | |
70 | -[1]: http://xmpp.org/extensions/xep-0045.html#enter-nonanon | |
71 | - | |
72 | - | |
73 | -### 2.4. Authentication method | |
74 | - | |
75 | -To use Postgresql through ODBC, the following modifications must be done: | |
76 | - | |
77 | - * Disable the default method: | |
78 | - `{auth_method, internal}.` | |
79 | - | |
80 | - * Enable autheticantion through ODBC: | |
81 | - `{auth_method, odbc}.` | |
26 | +## 3. Configuring Postgresql | |
82 | 27 | |
83 | - * Set database server name | |
84 | - `{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}.` | |
28 | +Give permission to noosfero user create new roles, login as | |
29 | +postgres user and execute: | |
85 | 30 | |
31 | + $ psql | |
32 | + postgres=# GRANT CREATE ON DATABASE noosfero TO noosfero; | |
86 | 33 | |
87 | -### 2.5. Increase the shaper traffic limit | |
34 | +Change the postgresql authentication method to md5 instead of ident, | |
35 | +add the following line to the file /etc/postgresql/8.4/main/pg_hba.conf: | |
88 | 36 | |
89 | - { shaper, normal, { maxrate, 10000000 } }. | |
37 | + # Noosfero user | |
38 | + local noosfero noosfero md5 | |
90 | 39 | |
40 | +(add this line before the following line) | |
91 | 41 | |
92 | -### 2.6. Disable unused modules | |
42 | + # "local" is for Unix domain socket connections only | |
43 | + local all all ident | |
93 | 44 | |
94 | -Unused modules can be disabled, for example: | |
45 | +Restart postgresql server: | |
95 | 46 | |
96 | - * s2s | |
97 | - * web_admin | |
98 | - * mod_pubsub | |
99 | - * mod_irc | |
100 | - * mod_offline | |
101 | - * mod_admin_extra | |
102 | - * mod_register | |
47 | + # service postgresql restart | |
103 | 48 | |
49 | +Login as noosfero user, and execute: | |
104 | 50 | |
105 | -### 2.7. Enable ODBC modules | |
51 | + $ psql -U noosfero -W noosfero < /usr/share/noosfero/util/chat/postgresql/ejabberd.sql | |
106 | 52 | |
107 | - * mod_privacy -> mod_privacy_odbc | |
108 | - * mod_private -> mod_private_odbc | |
109 | - * mod_roster -> mod_roster_odbc | |
53 | +(see database password in the /etc/noosfero/database.yml file) | |
110 | 54 | |
111 | -## 3. Configuring Postgresql | |
55 | +This will create a new schema inside the noosfero database, called `ejabberd`. | |
112 | 56 | |
113 | -Login as noosfero user, and execute: | |
57 | +Note that there should be at least one domain with `is_default = true` in | |
58 | +`domains` table, otherwise people won't be able to see their friends online. | |
114 | 59 | |
115 | - $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql | |
60 | +## 4. ODBC configuration | |
116 | 61 | |
117 | -Where `noosfero` may need to be replace by the name of the database used for Noosfero. | |
62 | +Create the following files: | |
118 | 63 | |
119 | -This will create a new schema inside the noosfero database, called `ejabberd`. | |
64 | + # cp /usr/share/noosfero/util/chat/odbc.ini /etc/ | |
65 | + # cp /usr/share/noosfero/util/chat/odbcinst.ini /etc/ | |
120 | 66 | |
121 | -Note `noosfero` user should have permission to create Postgresql schemas. Also, there should be at least one domain with `is_default = true` in `domains` table, otherwise people won't be able to see their friends online. | |
67 | +Edit the odbc.ini file and set the password for the database user, see | |
68 | +the file /etc/noosfero/database.yml to get the password. | |
122 | 69 | |
123 | -## 4. ODBC configuration | |
70 | +Adjust premissions: | |
124 | 71 | |
125 | -The following files must be created: | |
126 | - | |
127 | -`/etc/odbc.ini`: | |
128 | - | |
129 | - [PostgreSQLEjabberdNoosfero] | |
130 | - Description = PostgreSQL Noosfero ejabberd database | |
131 | - Driver = PostgreSQL Unicode | |
132 | - Trace = No | |
133 | - TraceFile = /tmp/psqlodbc.log | |
134 | - Database = noosfero | |
135 | - Servername = localhost | |
136 | - UserName = <DBUSER> | |
137 | - Password = <DBPASS> | |
138 | - Port = | |
139 | - ReadOnly = No | |
140 | - RowVersioning = No | |
141 | - ShowSystemTables = No | |
142 | - ShowOidColumn = No | |
143 | - FakeOidIndex = No | |
144 | - ConnSettings = SET search_path TO ejabberd | |
145 | - | |
146 | -`/etc/odbcinst.ini`: | |
147 | - | |
148 | - [PostgreSQL Unicode] | |
149 | - Description = PostgreSQL ODBC driver (Unicode version) | |
150 | - Driver = /usr/lib/odbc/psqlodbcw.so | |
151 | - Setup = /usr/lib/odbc/libodbcpsqlS.so | |
152 | - Debug = 0 | |
153 | - CommLog = 1 | |
154 | - UsageCount = 3 | |
72 | + # chmod 640 /etc/odbc.ini | |
73 | + # chown ejabberd /etc/odbc.ini | |
155 | 74 | |
156 | 75 | ## 4.1 testing all: |
157 | 76 | |
... | ... | @@ -159,7 +78,6 @@ The following files must be created: |
159 | 78 | |
160 | 79 | If the configuration was done right, the message "Connected!" will be displayed. |
161 | 80 | |
162 | - | |
163 | 81 | ## 5. Enabling kernel polling and SMP in `/etc/default/ejabberd` |
164 | 82 | |
165 | 83 | POLL=true |
... | ... | @@ -205,32 +123,45 @@ Note: module proxy_http must be enabled: |
205 | 123 | |
206 | 124 | # a2enmod proxy_http |
207 | 125 | |
208 | -## 8. DNS configuration | |
126 | +Restart services: | |
209 | 127 | |
210 | -For this point, we assume you are using BIND as your DNS server. You need to add the following entries to the DNS zone file corresponding to the domain of your noosfero site: | |
128 | + # service ejabberd restart | |
129 | + # service noosfero restart | |
130 | + # service apache2 restart | |
211 | 131 | |
212 | - _xmpp-client._tcp SRV 5 100 5222 master | |
213 | - conference CNAME master | |
214 | - _xmpp-client._tcp.conference SRV 5 100 5222 master | |
132 | +## 8. Test Apache Configuration | |
215 | 133 | |
216 | -If you are running a DNS server other than BIND, you will have to figure out how to create equivalente rules for your zone file. Patches to this documentation are welcome. | |
134 | +Open in your browser the address: | |
217 | 135 | |
218 | -## 9. Testing this Setup | |
136 | + http://<yout domain>/http-bind | |
219 | 137 | |
220 | -Adjust shell limits to proceed with some benchmarks and load tests: | |
138 | +You should see a page with a message like that: | |
221 | 139 | |
222 | - # ulimit −s 256 | |
223 | - # ulimit −n 8192 | |
224 | - # echo 10 > /proc/sys/net/ipv4/tcp_syn_retries | |
140 | + ejabberd mod_http_bind | |
141 | + An implementation of XMPP over BOSH (XEP-0206) | |
142 | + This web page is only informative. To use HTTP-Bind you need a Jabber/XMPP | |
143 | + client that supports it. | |
225 | 144 | |
226 | -To measure the bandwidth between server and client: | |
145 | +## 9. Test chat session | |
227 | 146 | |
228 | - * at server side: | |
229 | - `# iperf −s` | |
147 | +Open Noosfero console and execute: | |
230 | 148 | |
231 | - * at client side: | |
232 | - `# iperf −c server_ip` | |
149 | +>> environment = Environment.default | |
150 | +>> user = Person['guest'] | |
151 | +>> password = user.user.crypted_password | |
152 | +>> login = user.jid | |
153 | +>> RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind", :wait => 30, :hold => 1, :window => 5 | |
233 | 154 | |
234 | -For heavy load tests, clone and use this software: | |
155 | +If you have luck, should see something like that: | |
235 | 156 | |
236 | - $ git clone http://git.holoscopio.com/git/metal/tester.git | |
157 | +Ruby-BOSH - SEND | |
158 | +<body window="5" rid="60265" xmlns="http://jabber.org/protocol/httpbind" xmlns:xmpp="urn:xmpp:xbosh" to="vagrant-debian-squeeze.vagrantup.com" wait="30" xmpp:version="1.0" hold="1"/> | |
159 | +Ruby-BOSH - SEND | |
160 | +<body rid="60266" xmlns="http://jabber.org/protocol/httpbind" sid="24cdfc43646a2af1059a7060b677c2e11b26f34f" xmlns:xmpp="urn:xmpp:xbosh" xmpp:version="1.0"><auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">Z3Vlc3RAdmFncmFudC1kZWJpYW4tc3F1ZWV6ZS52YWdyYW50dXAuY29tAGd1ZXN0ADEzZTFhYWVlYjRhYjZlMTA0MmRkNWI1YWY0MzM4MjA1OGJiOWZmNzk=</auth></body> | |
161 | +Ruby-BOSH - SEND | |
162 | +<body xmpp:restart="true" rid="60267" xmlns="http://jabber.org/protocol/httpbind" sid="24cdfc43646a2af1059a7060b677c2e11b26f34f" xmlns:xmpp="urn:xmpp:xbosh" xmpp:version="1.0"/> | |
163 | +Ruby-BOSH - SEND | |
164 | +<body rid="60268" xmlns="http://jabber.org/protocol/httpbind" sid="24cdfc43646a2af1059a7060b677c2e11b26f34f" xmlns:xmpp="urn:xmpp:xbosh" xmpp:version="1.0"><iq type="set" xmlns="jabber:client" id="bind_29330"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>bosh_9631</resource></bind></iq></body> | |
165 | +Ruby-BOSH - SEND | |
166 | +<body rid="60269" xmlns="http://jabber.org/protocol/httpbind" sid="24cdfc43646a2af1059a7060b677c2e11b26f34f" xmlns:xmpp="urn:xmpp:xbosh" xmpp:version="1.0"><iq type="set" xmlns="jabber:client" id="sess_21557"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq></body> | |
167 | +=> ["guest@vagrant-debian-squeeze.vagrantup.com", "24cdfc43646a2af1059a7060b677c2e11b26f34f", 60270] | ... | ... |
... | ... | @@ -0,0 +1,115 @@ |
1 | +Setup Noosfero to use HTTPS | |
2 | +=========================== | |
3 | + | |
4 | +This document assumes that you have a fully and clean Noosfero | |
5 | +installation as explained at the `INSTALL.md` file. | |
6 | + | |
7 | +SSL certificate | |
8 | ++++++++++++++++ | |
9 | + | |
10 | +You should get a valid SSL certificate, but if you want to test | |
11 | +your setup before, you could generate a self-signed certificate | |
12 | +as below: | |
13 | + | |
14 | + # mkdir /etc/noosfero/ssl | |
15 | + # cd /etc/noosfero/ssl | |
16 | + # openssl genrsa 1024 > noosfero.key | |
17 | + # openssl req -new -x509 -nodes -sha1 -days $[10*365] -key noosfero.key > noosfero.cert | |
18 | + # cat noosfero.key noosfero.cert > noosfero.pem | |
19 | + | |
20 | +There are two ways of using SSL with Noosfero: 1) If you are not using | |
21 | +Varnish; and 2) If you are using Varnish. | |
22 | + | |
23 | +1) If you are are not using Varnish | |
24 | ++++++++++++++++++++++++++++++++++++ | |
25 | + | |
26 | +Simply do a redirect in apache to force all connections with SSL: | |
27 | + | |
28 | + <VirtualHost *:8080> | |
29 | + ServerName test.stoa.usp.br | |
30 | + | |
31 | + Redirect / https://example.com/ | |
32 | + </VirtualHost> | |
33 | + | |
34 | +And set a vhost to receive then: | |
35 | + | |
36 | + <VirtualHost *:443> | |
37 | + ServerName example.com | |
38 | + | |
39 | + SSLEngine On | |
40 | + SSLCertificateFile /etc/ssl/certs/cert.pem | |
41 | + SSLCertificateKeyFile /etc/ssl/private/cert.key | |
42 | + | |
43 | + Include /etc/noosfero/apache/virtualhost.conf | |
44 | + </VirtualHost> | |
45 | + | |
46 | +Be aware that if you had configured varnish, the requests won't reach | |
47 | +it with this configuration. | |
48 | + | |
49 | +2) If you are using Varnish | |
50 | ++++++++++++++++++++++++++++ | |
51 | + | |
52 | +Varnish isn't able to communicate with the SSL protocol, so we will | |
53 | +need some one who do this and Pound[1] can do the job. In order to | |
54 | +install it in Debian based systems: | |
55 | + | |
56 | + $ sudo apt-get install pound | |
57 | + | |
58 | +Set Varnish to listen in other port than 80: | |
59 | + | |
60 | +/etc/defaults/varnish | |
61 | +--------------------- | |
62 | + | |
63 | + DAEMON_OPTS="-a localhost:6081 \ | |
64 | + -T localhost:6082 \ | |
65 | + -f /etc/varnish/default.vcl \ | |
66 | + -S /etc/varnish/secret \ | |
67 | + -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G" | |
68 | + | |
69 | +Configure Pound: | |
70 | + | |
71 | + # cp /usr/share/noosfero/etc/pound.cfg /etc/pound/ | |
72 | + | |
73 | +Edit /etc/pound.cfg and set the IP and domain of your server. | |
74 | + | |
75 | +Configure Pound to start at system initialization: | |
76 | + | |
77 | +/etc/default/pound | |
78 | +------------------ | |
79 | + | |
80 | + startup=1 | |
81 | + | |
82 | +Set Apache to only listen to localhost: | |
83 | + | |
84 | +/etc/apache2/ports.conf | |
85 | +----------------------- | |
86 | + | |
87 | + Listen 127.0.0.1:8080 | |
88 | + | |
89 | +Restart the services: | |
90 | + | |
91 | + $ sudo service apache2 restart | |
92 | + $ sudo service varnish restart | |
93 | + | |
94 | +Start pound: | |
95 | + | |
96 | + $ sudo service pound start | |
97 | + | |
98 | +[1] http://www.apsis.ch/pound | |
99 | + | |
100 | +Noosfero XMPP chat | |
101 | +++++++++++++++++++ | |
102 | + | |
103 | +If you want to use chat over HTTPS, then you should add the domain | |
104 | +and IP of your server in the /etc/hosts file, example: | |
105 | + | |
106 | +/etc/hosts | |
107 | +---------- | |
108 | + | |
109 | + 192.168.1.86 mydomain.example.com | |
110 | + | |
111 | +Also, it's recomended that you remove lines above from the file | |
112 | +`/etc/apache2/sites-enabled/noosfero`: | |
113 | + | |
114 | + RewriteEngine On | |
115 | + Include /usr/share/noosfero/util/chat/apache/xmpp.conf | ... | ... |
INSTALL.varnish.md
... | ... | @@ -24,10 +24,6 @@ Install the RPAF apache module (or skip this step if not using apache): |
24 | 24 | |
25 | 25 | 3b) Edit `/etc/apache2/sites-enabled/*`, and change `<VirtualHost *:80>` to `<VirtualHost *:8080>` |
26 | 26 | |
27 | -3c) Restart apache | |
28 | - | |
29 | - # invoke-rc.d apache2 restart | |
30 | - | |
31 | 27 | 4) Varnish configuration |
32 | 28 | |
33 | 29 | 4a) Edit `/etc/default/varnish` |
... | ... | @@ -44,10 +40,6 @@ On manual installations, change `/etc/noosfero/*` to `{Rails.root}/etc/noosfero/ |
44 | 40 | |
45 | 41 | **NOTE**: it is very important that the `*.vcl` files are included in that order, i.e. *first* include `varnish-noosfero.vcl`, and *after* `noosfero-accept-language.cvl`. |
46 | 42 | |
47 | -4c) Restart Varnish | |
48 | - | |
49 | - # invoke-rc.d varnish restart | |
50 | - | |
51 | 43 | 5) Enable varnish logging: |
52 | 44 | |
53 | 45 | 5a) Edit `/etc/default/varnishncsa` and uncomment the line that contains: |
... | ... | @@ -56,8 +48,10 @@ On manual installations, change `/etc/noosfero/*` to `{Rails.root}/etc/noosfero/ |
56 | 48 | |
57 | 49 | The varnish log will be written to `/var/log/varnish/varnishncsa.log` in an apache-compatible format. You should change your statistics generation software (e.g. awstats) to use that instead of apache logs. |
58 | 50 | |
59 | -5b) Restart Varnish Logging service | |
51 | +Thanks to Cosimo Streppone for varnish-accept-language. See http://github.com/cosimo/varnish-accept-language for more information. | |
60 | 52 | |
61 | - # invoke-rc.d varnishncsa restart | |
53 | +6) Restart services | |
62 | 54 | |
63 | -Thanks to Cosimo Streppone for varnish-accept-language. See http://github.com/cosimo/varnish-accept-language for more information. | |
55 | + # service apache2 restart | |
56 | + # service varnish restart | |
57 | + # service varnishncsa restart | ... | ... |
app/controllers/my_profile/friends_controller.rb
... | ... | @@ -3,6 +3,7 @@ class FriendsController < MyProfileController |
3 | 3 | protect 'manage_friends', :profile |
4 | 4 | |
5 | 5 | def index |
6 | + @suggestions = profile.suggested_people.limit(per_page/2) | |
6 | 7 | if is_cache_expired?(profile.manage_friends_cache_key(params)) |
7 | 8 | @friends = profile.friends.paginate(:per_page => per_page, :page => params[:npage]) |
8 | 9 | end |
... | ... | @@ -16,6 +17,21 @@ class FriendsController < MyProfileController |
16 | 17 | end |
17 | 18 | end |
18 | 19 | |
20 | + def suggest | |
21 | + @suggestions = profile.suggested_people.paginate(:per_page => per_page, :page => params[:npage]) | |
22 | + end | |
23 | + | |
24 | + def remove_suggestion | |
25 | + @person = profile.suggested_people.find_by_identifier(params[:id]) | |
26 | + redirect_to :action => 'suggest' unless @person | |
27 | + if @person && request.post? | |
28 | + suggestion = profile.profile_suggestions.find_by_suggestion_id @person.id | |
29 | + suggestion.disable | |
30 | + session[:notice] = _('Suggestion removed') | |
31 | + redirect_to :action => 'suggest' | |
32 | + end | |
33 | + end | |
34 | + | |
19 | 35 | protected |
20 | 36 | |
21 | 37 | class << self | ... | ... |
app/controllers/my_profile/memberships_controller.rb
... | ... | @@ -7,9 +7,9 @@ class MembershipsController < MyProfileController |
7 | 7 | ra = profile.role_assignments.find_by_role_id(role.id) |
8 | 8 | ra.present? && ra.resource_type == 'Profile' |
9 | 9 | end |
10 | - @filter = params[:filter_type].blank? ? nil : params[:filter_type] | |
10 | + @filter = params[:filter_type].to_i | |
11 | 11 | begin |
12 | - @memberships = @filter.nil? ? profile.memberships : profile.memberships_by_role(environment.roles.find(@filter)) | |
12 | + @memberships = @filter.zero? ? profile.memberships : profile.memberships_by_role(environment.roles.find(@filter)) | |
13 | 13 | rescue ActiveRecord::RecordNotFound |
14 | 14 | @memberships = [] |
15 | 15 | end |
... | ... | @@ -38,4 +38,19 @@ class MembershipsController < MyProfileController |
38 | 38 | @community = Community.find(params[:community_id]) |
39 | 39 | @back_to = params[:back_to] |
40 | 40 | end |
41 | + | |
42 | + def suggest | |
43 | + @suggestions = profile.suggested_communities.paginate(:per_page => 8, :page => params[:npage]) | |
44 | + end | |
45 | + | |
46 | + def remove_suggestion | |
47 | + @community = profile.suggested_communities.find_by_identifier(params[:id]) | |
48 | + redirect_to :action => 'suggest' unless @community | |
49 | + if @community && request.post? | |
50 | + suggestion = profile.profile_suggestions.find_by_suggestion_id @community.id | |
51 | + suggestion.disable | |
52 | + redirect_to :action => 'suggest' | |
53 | + end | |
54 | + end | |
55 | + | |
41 | 56 | end | ... | ... |
app/mailers/user_mailer.rb
... | ... | @@ -41,6 +41,23 @@ class UserMailer < ActionMailer::Base |
41 | 41 | ) |
42 | 42 | end |
43 | 43 | |
44 | + def profiles_suggestions_email(user) | |
45 | + @recipient = user.name | |
46 | + @environment = user.environment.name | |
47 | + @url = user.environment.top_url | |
48 | + @people_suggestions_url = user.people_suggestions_url | |
49 | + @people_suggestions = user.suggested_people.sample(3) | |
50 | + @communities_suggestions_url = user.communities_suggestions_url | |
51 | + @communities_suggestions = user.suggested_communities.sample(3) | |
52 | + | |
53 | + mail( | |
54 | + content_type: 'text/html', | |
55 | + to: user.email, | |
56 | + from: "#{user.environment.name} <#{user.environment.contact_email}>", | |
57 | + subject: _("[%s] We have suggestions for your network") % user.environment.name | |
58 | + ) | |
59 | + end | |
60 | + | |
44 | 61 | class Job < Struct.new(:user, :method) |
45 | 62 | def perform |
46 | 63 | UserMailer.send(method, user).deliver | ... | ... |
app/models/add_friend.rb
... | ... | @@ -14,6 +14,11 @@ class AddFriend < Task |
14 | 14 | alias :friend :target |
15 | 15 | alias :friend= :target= |
16 | 16 | |
17 | + after_create do |task| | |
18 | + TaskMailer.invitation_notification(task).deliver unless task.friend | |
19 | + remove_from_suggestion_list(task) | |
20 | + end | |
21 | + | |
17 | 22 | def perform |
18 | 23 | target.add_friend(requestor, group_for_friend) |
19 | 24 | requestor.add_friend(target, group_for_person) |
... | ... | @@ -48,4 +53,8 @@ class AddFriend < Task |
48 | 53 | {:type => :profile_image, :profile => requestor, :url => requestor.url} |
49 | 54 | end |
50 | 55 | |
56 | + def remove_from_suggestion_list(task) | |
57 | + suggestion = task.requestor.profile_suggestions.find_by_suggestion_id task.target.id | |
58 | + suggestion.disable if suggestion | |
59 | + end | |
51 | 60 | end | ... | ... |
app/models/person.rb
... | ... | @@ -66,6 +66,10 @@ class Person < Profile |
66 | 66 | has_and_belongs_to_many :acepted_forums, :class_name => 'Forum', :join_table => 'terms_forum_people' |
67 | 67 | has_and_belongs_to_many :articles_with_access, :class_name => 'Article', :join_table => 'article_privacy_exceptions' |
68 | 68 | |
69 | + has_many :profile_suggestions, :foreign_key => :person_id, :dependent => :destroy | |
70 | + has_many :suggested_people, :through => :profile_suggestions, :source => :suggestion, :conditions => ['profile_suggestions.suggestion_type = ? AND profile_suggestions.enabled = ?', 'Person', true] | |
71 | + has_many :suggested_communities, :through => :profile_suggestions, :source => :suggestion, :conditions => ['profile_suggestions.suggestion_type = ? AND profile_suggestions.enabled = ?', 'Community', true] | |
72 | + | |
69 | 73 | scope :more_popular, :order => 'friends_count DESC' |
70 | 74 | |
71 | 75 | scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*' | ... | ... |
app/models/profile.rb
... | ... | @@ -227,6 +227,8 @@ class Profile < ActiveRecord::Base |
227 | 227 | |
228 | 228 | has_many :abuse_complaints, :foreign_key => 'requestor_id', :dependent => :destroy |
229 | 229 | |
230 | + has_many :profile_suggestions, :foreign_key => :suggestion_id, :dependent => :destroy | |
231 | + | |
230 | 232 | def top_level_categorization |
231 | 233 | ret = {} |
232 | 234 | self.profile_categorizations.each do |c| |
... | ... | @@ -521,6 +523,14 @@ class Profile < ActiveRecord::Base |
521 | 523 | generate_url(:profile => identifier, :controller => 'profile', :action => 'index') |
522 | 524 | end |
523 | 525 | |
526 | + def people_suggestions_url | |
527 | + generate_url(:profile => identifier, :controller => 'friends', :action => 'suggest') | |
528 | + end | |
529 | + | |
530 | + def communities_suggestions_url | |
531 | + generate_url(:profile => identifier, :controller => 'memberships', :action => 'suggest') | |
532 | + end | |
533 | + | |
524 | 534 | def generate_url(options) |
525 | 535 | url_options.merge(options) |
526 | 536 | end |
... | ... | @@ -645,6 +655,7 @@ private :generate_url, :url_options |
645 | 655 | self.affiliate(person, Profile::Roles.member(environment.id)) |
646 | 656 | end |
647 | 657 | person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel } |
658 | + remove_from_suggestion_list person | |
648 | 659 | else |
649 | 660 | raise _("%s can't have members") % self.class.name |
650 | 661 | end |
... | ... | @@ -973,4 +984,10 @@ private :generate_url, :url_options |
973 | 984 | def preferred_login_redirection |
974 | 985 | redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login |
975 | 986 | end |
987 | + | |
988 | + def remove_from_suggestion_list(person) | |
989 | + suggestion = person.profile_suggestions.find_by_suggestion_id self.id | |
990 | + suggestion.disable if suggestion | |
991 | + end | |
992 | + | |
976 | 993 | end | ... | ... |
... | ... | @@ -0,0 +1,160 @@ |
1 | +class ProfileSuggestion < ActiveRecord::Base | |
2 | + belongs_to :person | |
3 | + belongs_to :suggestion, :class_name => 'Profile', :foreign_key => :suggestion_id | |
4 | + | |
5 | + attr_accessible :person, :suggestion, :suggestion_type, :categories, :similarity, :enabled | |
6 | + | |
7 | + before_create do |profile_suggestion| | |
8 | + profile_suggestion.suggestion_type = self.suggestion.class.to_s | |
9 | + end | |
10 | + | |
11 | + acts_as_having_settings :field => :categories | |
12 | + | |
13 | + validate :must_be_a_valid_category, :on => :create | |
14 | + def must_be_a_valid_category | |
15 | + if categories.keys.map { |cat| self.respond_to?(cat)}.include?(false) | |
16 | + errors.add(:categories, 'Category must be valid') | |
17 | + end | |
18 | + end | |
19 | + | |
20 | + validates_uniqueness_of :suggestion_id, :scope => [ :person_id ] | |
21 | + | |
22 | + CATEGORIES = { | |
23 | + :common_friends => _('Friends in common'), | |
24 | + :common_communities => _('Communities in common'), | |
25 | + :common_tags => _('Tags in common') | |
26 | + } | |
27 | + | |
28 | + CATEGORIES.keys.each do |category| | |
29 | + settings_items category.to_sym | |
30 | + attr_accessible category.to_sym | |
31 | + end | |
32 | + | |
33 | + RULES = %w[ | |
34 | + friends_of_friends | |
35 | + people_with_common_communities | |
36 | + people_with_common_tags | |
37 | + communities_with_common_friends | |
38 | + communities_with_common_tags | |
39 | + ] | |
40 | + | |
41 | + # Number of suggestions | |
42 | + N_SUGGESTIONS = 30 | |
43 | + | |
44 | + # Number max of attempts | |
45 | + MAX_ATTEMPTS = N_SUGGESTIONS * 2 | |
46 | + | |
47 | + # Number of friends in common | |
48 | + COMMON_FRIENDS = 2 | |
49 | + | |
50 | + # Number of communities in common | |
51 | + COMMON_COMMUNITIES = 1 | |
52 | + | |
53 | + # Number of friends in common | |
54 | + COMMON_TAGS = 2 | |
55 | + | |
56 | + def self.friends_of_friends(person) | |
57 | + person_attempts = 0 | |
58 | + person_friends = person.friends | |
59 | + person_friends.each do |friend| | |
60 | + friend.friends.includes.each do |friend_of_friend| | |
61 | + person_attempts += 1 | |
62 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS | |
63 | + unless friend_of_friend == person || friend_of_friend.is_a_friend?(person) || person.already_request_friendship?(friend_of_friend) | |
64 | + common_friends = friend_of_friend.friends & person_friends | |
65 | + if common_friends.size >= COMMON_FRIENDS | |
66 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(friend_of_friend.id) | |
67 | + suggestion.common_friends = common_friends.size | |
68 | + suggestion.save | |
69 | + end | |
70 | + end | |
71 | + end | |
72 | + end | |
73 | + end | |
74 | + | |
75 | + def self.people_with_common_communities(person) | |
76 | + person_attempts = 0 | |
77 | + person_communities = person.communities | |
78 | + person_communities.each do |community| | |
79 | + community.members.each do |member| | |
80 | + person_attempts += 1 | |
81 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS | |
82 | + unless member == person || member.is_a_friend?(person) || person.already_request_friendship?(member) | |
83 | + common_communities = person_communities & member.communities | |
84 | + if common_communities.size >= COMMON_COMMUNITIES | |
85 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(member.id) | |
86 | + suggestion.common_communities = common_communities.size | |
87 | + suggestion.save | |
88 | + end | |
89 | + end | |
90 | + end | |
91 | + end | |
92 | + end | |
93 | + | |
94 | + def self.people_with_common_tags(person) | |
95 | + person_attempts = 0 | |
96 | + tags = person.article_tags.keys | |
97 | + tags.each do |tag| | |
98 | + person_attempts += 1 | |
99 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS | |
100 | + tagged_content = ActsAsTaggableOn::Tagging.includes(:taggable).where(:tag_id => ActsAsTaggableOn::Tag.find_by_name(tag)) | |
101 | + tagged_content.each do |tg| | |
102 | + author = tg.taggable.created_by | |
103 | + unless author.nil? || author == person || author.is_a_friend?(person) || person.already_request_friendship?(author) | |
104 | + common_tags = tags & author.article_tags.keys | |
105 | + if common_tags.size >= COMMON_TAGS | |
106 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(author.id) | |
107 | + suggestion.common_tags = common_tags.size | |
108 | + suggestion.save | |
109 | + end | |
110 | + end | |
111 | + end | |
112 | + end | |
113 | + end | |
114 | + | |
115 | + def self.communities_with_common_friends(person) | |
116 | + community_attempts = 0 | |
117 | + person_friends = person.friends | |
118 | + person_friends.each do |friend| | |
119 | + friend.communities.each do |community| | |
120 | + community_attempts += 1 | |
121 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && community_attempts < MAX_ATTEMPTS | |
122 | + unless person.is_member_of?(community) || community.already_request_membership?(person) | |
123 | + common_friends = community.members & person.friends | |
124 | + if common_friends.size >= COMMON_FRIENDS | |
125 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(community.id) | |
126 | + suggestion.common_friends = common_friends.size | |
127 | + suggestion.save | |
128 | + end | |
129 | + end | |
130 | + end | |
131 | + end | |
132 | + end | |
133 | + | |
134 | + def self.communities_with_common_tags(person) | |
135 | + community_attempts = 0 | |
136 | + tags = person.article_tags.keys | |
137 | + tags.each do |tag| | |
138 | + community_attempts += 1 | |
139 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && community_attempts < MAX_ATTEMPTS | |
140 | + tagged_content = ActsAsTaggableOn::Tagging.includes(:taggable).where(:tag_id => ActsAsTaggableOn::Tag.find_by_name(tag)) | |
141 | + tagged_content.each do |tg| | |
142 | + profile = tg.taggable.profile | |
143 | + unless !profile.community? || person.is_member_of?(profile) || profile.already_request_membership?(person) | |
144 | + common_tags = tags & profile.article_tags.keys | |
145 | + if common_tags.size >= COMMON_TAGS | |
146 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(profile.id) | |
147 | + suggestion.common_tags = common_tags.size | |
148 | + suggestion.save | |
149 | + end | |
150 | + end | |
151 | + end | |
152 | + end | |
153 | + end | |
154 | + | |
155 | + def disable | |
156 | + self.enabled = false | |
157 | + self.save | |
158 | + end | |
159 | + | |
160 | +end | ... | ... |
app/views/content_viewer/view_page.html.erb
... | ... | @@ -80,8 +80,8 @@ |
80 | 80 | </h3> |
81 | 81 | <% end %> |
82 | 82 | |
83 | - <% if @page.accept_comments? && @comments.present? && @comments.count > 1 %> | |
84 | - <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button', :onclick => "jQuery('#page-comment-form .display-comment-form').first().click();") %> | |
83 | + <% if @comments.present? && @comments.count > 1 %> | |
84 | + <%= link_to(_('Post a comment'), '#', :class => 'display-comment-form', :id => 'top-post-comment-button', :onclick => "jQuery('#page-comment-form .display-comment-form').first().click();") if @page.accept_comments? %> | |
85 | 85 | |
86 | 86 | <%= hidden_field_tag("page_url", url_for(:controller=>'content_viewer', :action=>'view_page', :profile=>profile.identifier, :page => @page.explode_path)) %> |
87 | 87 | <%= javascript_include_tag "comment_order.js" %> |
... | ... | @@ -90,12 +90,14 @@ |
90 | 90 | <%= select_tag 'comment_order', options_for_select({_('Oldest first')=>'oldest', _('Newest first')=>'newest'}, @comment_order) %> |
91 | 91 | <% end %> |
92 | 92 | </div> |
93 | + <% end %> | |
93 | 94 | |
94 | - <ul class="article-comments-list"> | |
95 | + <ul class="article-comments-list"> | |
96 | + <% if @comments.present? %> | |
95 | 97 | <%= render :partial => 'comment/comment', :collection => @comments %> |
96 | 98 | <%= pagination_links @comments, :param_name => 'comment_page' %> |
97 | - </ul> | |
98 | - <% end %> | |
99 | + <% end %> | |
100 | + </ul> | |
99 | 101 | |
100 | 102 | <% if @page.accept_comments? %> |
101 | 103 | <div id='page-comment-form' class='page-comment-form'><%= render :partial => 'comment/comment_form', :locals =>{:url => {:controller => :comment, :action => :create}, :display_link => true, :cancel_triggers_hide => true}%></div> | ... | ... |
app/views/friends/index.html.erb
... | ... | @@ -10,38 +10,8 @@ |
10 | 10 | <%= link_to _('Do you want to see other people in this environment?'), :controller => 'search', :action => 'assets', :asset => 'people' %> |
11 | 11 | </em> |
12 | 12 | </p> |
13 | - <% else %> | |
14 | - <% button_bar do %> | |
15 | - <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> | |
16 | - <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> | |
17 | - <% unless @plugins.dispatch(:remove_invite_friends_button).include?(true) %> | |
18 | - <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'invite_friends') %> | |
19 | - <% end %> | |
20 | - <% end %> | |
21 | 13 | <% end %> |
22 | 14 | |
23 | - <ul class="profile-list"> | |
24 | - <% @friends.each do |friend| %> | |
25 | - <li> | |
26 | - <%= link_to_profile profile_image(friend) + '<br/>' + friend.short_name, | |
27 | - friend.identifier, :class => 'profile-link' %> | |
28 | - <div class="controll"> | |
29 | - <%= link_to content_tag('span',_('remove')), | |
30 | - { :action => 'remove', :id => friend.id }, | |
31 | - :class => 'button icon-remove', | |
32 | - :title => _('remove') %> | |
33 | - <%= link_to content_tag('span',_('contact')), | |
34 | - friend.url.merge(:controller => 'contact', :action => 'new', :profile => friend.identifier), | |
35 | - :class => 'button icon-menu-mail', | |
36 | - :title => _('contact') %> | |
37 | - </div><!-- end class="controll" --> | |
38 | - </li> | |
39 | - <% end %> | |
40 | - </ul> | |
41 | - <div id='pagination-friends'> | |
42 | - <%= pagination_links @friends, :param_name => 'npage' %> | |
43 | - </div> | |
44 | - | |
45 | 15 | <% button_bar do %> |
46 | 16 | <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> |
47 | 17 | <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> |
... | ... | @@ -49,7 +19,23 @@ |
49 | 19 | <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'invite_friends') %> |
50 | 20 | <% end %> |
51 | 21 | <% end %> |
22 | + | |
23 | + <%= render :partial => 'shared/profile_list', :locals => { :profiles => @friends, :collection => :friends } %> | |
24 | + <div id='pagination-friends'> | |
25 | + <%= pagination_links @friends, :param_name => 'npage' %> | |
26 | + </div> | |
27 | + | |
28 | +<% end %> | |
29 | + | |
30 | +<% unless @suggestions.empty? %> | |
31 | + <br style="clear:both" /> | |
32 | + <h2><%= _("Friends suggestions") %></h2> | |
33 | + | |
34 | + <%= render :partial => 'shared/profile_list', :locals => { :profiles => @suggestions, :collection => :friends_suggestions } %> | |
35 | + | |
36 | + <% button_bar do %> | |
37 | + <%= link_to _('See more suggestions...'), :action => 'suggest' %> | |
38 | + <% end %> | |
52 | 39 | <% end %> |
53 | 40 | |
54 | 41 | </div><!-- end id="manage_friends" --> |
55 | - | ... | ... |
... | ... | @@ -0,0 +1,21 @@ |
1 | +<div class='profiles-suggestions'> | |
2 | + <h1><%= _("Friends suggestions for %s") % profile.name %></h1> | |
3 | + | |
4 | + <% button_bar do %> | |
5 | + <%= button(:back, _('Go to friends list'), :controller => 'friends') %> | |
6 | + <% end %> | |
7 | + | |
8 | + <% if @suggestions.empty? %> | |
9 | + <p> | |
10 | + <em> | |
11 | + <%= _('You have no suggestions yet.') %> | |
12 | + <%= link_to _('Do you want to see other people in this environment?'), :controller => 'search', :action => 'assets', :asset => 'people' %> | |
13 | + </em> | |
14 | + </p> | |
15 | + <% else %> | |
16 | + <%= render :partial => 'shared/profile_list', :locals => { :profiles => @suggestions, :collection => :friends_suggestions } %> | |
17 | + | |
18 | + <%= pagination_links @suggestions, :param_name => 'npage' %> | |
19 | + <% end %> | |
20 | + <br style="clear:both" /> | |
21 | +</div> | ... | ... |
app/views/memberships/index.html.erb
... | ... | @@ -8,13 +8,16 @@ |
8 | 8 | <%= button :back, _('Go back'), :controller => 'profile_editor' %> |
9 | 9 | <% end %> |
10 | 10 | |
11 | -<% type_collection = [[nil, _('All')]] %> | |
11 | +<% type_collection = [[0, _('All')]] %> | |
12 | 12 | <% type_collection += @roles.sort_by {|role| role.id}.map{|r| ["#{r.id}", r.name]} %> |
13 | 13 | |
14 | +<%= javascript_include_tag "memberships_filter.js" %> | |
14 | 15 | <p> |
15 | - <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :onchange => 'document.location.href = "?filter_type="+this.value')%> | |
16 | + <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :id => 'memberships_filter')%> | |
16 | 17 | </p> |
17 | 18 | |
19 | +<p><%= link_to _('See some suggestions of communities...'), :action => 'suggest' %></p> | |
20 | + | |
18 | 21 | <% if @memberships.empty? %> |
19 | 22 | <p> |
20 | 23 | <em><%= _('No groups to list') %></em> | ... | ... |
... | ... | @@ -0,0 +1,21 @@ |
1 | +<div class='profiles-suggestions'> | |
2 | + <h1><%= _("Communities suggestions for %s") % profile.name %></h1> | |
3 | + | |
4 | + <% button_bar do %> | |
5 | + <%= button(:back, _('Go to groups list'), :controller => 'memberships') %> | |
6 | + <% end %> | |
7 | + | |
8 | + <% if @suggestions.empty? %> | |
9 | + <p> | |
10 | + <em> | |
11 | + <%= _('You have no suggestions yet.') %> | |
12 | + <%= link_to _('Do you want to see communities in this environment?'), :controller => 'search', :action => 'assets', :asset => 'communities' %> | |
13 | + </em> | |
14 | + </p> | |
15 | + <% else %> | |
16 | + <%= render :partial => 'shared/profile_list', :locals => { :profiles => @suggestions, :collection => :communities_suggestions } %> | |
17 | + <% end %> | |
18 | + | |
19 | + <%= pagination_links @suggestions, :param_name => 'npage' %> | |
20 | + <br style="clear:both" /> | |
21 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,38 @@ |
1 | +<ul class="profile-list"> | |
2 | + <% profiles.each do |profile| %> | |
3 | + <li> | |
4 | + <%= link_to_profile profile_image(profile) + '<br/>' + profile.short_name, | |
5 | + profile.identifier, :class => 'profile-link' %> | |
6 | + <div class="controll"> | |
7 | + <% if collection == :friends %> | |
8 | + <%= button_without_text :remove, content_tag('span',_('remove')), | |
9 | + { :action => 'remove', :id => profile.id }, | |
10 | + :title => _('remove') %> | |
11 | + <%= button_without_text 'menu-mail', content_tag('span',_('contact')), | |
12 | + profile.url.merge(:controller => 'contact', :action => 'new', :profile => profile.identifier), | |
13 | + :title => _('contact') %> | |
14 | + <% elsif collection == :friends_suggestions %> | |
15 | + <%= button_without_text :add, content_tag('span',_('add')), | |
16 | + profile.add_url, | |
17 | + :class => 'add-friend', | |
18 | + :title => _('Add friend') %> | |
19 | + <%= button_without_text :remove, content_tag('span',_('remove')), | |
20 | + { :action => 'remove_suggestion', :id => profile.identifier }, | |
21 | + :title => _('Remove suggestion'), | |
22 | + :method => 'post', | |
23 | + :confirm => _('Are you sure you want to remove this suggestion?') %> | |
24 | + <% elsif collection == :communities_suggestions %> | |
25 | + <%= button_without_text :add, content_tag('span',_('join')), | |
26 | + profile.join_url, | |
27 | + :class => 'join-community', | |
28 | + :title => _("Join %s") % profile.name %> | |
29 | + <%= button_without_text :remove, content_tag('span',_('remove')), | |
30 | + { :action => 'remove_suggestion', :id => profile.identifier }, | |
31 | + :title => _('Remove suggestion'), | |
32 | + :method => 'post', | |
33 | + :confirm => _('Are you sure you want to remove this suggestion?') %> | |
34 | + <% end %> | |
35 | + </div><!-- end class="controll" --> | |
36 | + </li> | |
37 | + <% end %> | |
38 | +</ul> | ... | ... |
... | ... | @@ -0,0 +1,10 @@ |
1 | +<%= profile_image suggestion, :thumb, :class => 'suggestion_picture' %> | |
2 | + | |
3 | +<p> | |
4 | +<%= _('Are you sure you want to remove %s from your suggestions list?') % suggestion.name %> | |
5 | +</p> | |
6 | + | |
7 | +<%= form_tag do %> | |
8 | + <%= submit_button(:ok, _("Yes, I want to remove %s") % suggestion.name) %> | |
9 | + <%= button(:cancel, _("No"), :action => 'suggest') %> | |
10 | +<% end %> | ... | ... |
app/views/user_mailer/profiles_suggestions_email.html.erb
0 → 100644
... | ... | @@ -0,0 +1,32 @@ |
1 | +<%= _('Hi, %{recipient}!') % { :recipient => @recipient } %> | |
2 | + | |
3 | +<p><%= _('We want to give you some suggestions to grow up your network. Check it out!') %></p> | |
4 | + | |
5 | +<% unless @people_suggestions.empty? %> | |
6 | + <p><%= _('Friends suggestions:') %></p> | |
7 | + | |
8 | + <ul> | |
9 | + <% @people_suggestions.each do |person| %> | |
10 | + <li><a href="<%= url_for(person.public_profile_url) %>"><%= person.name %></a></li> | |
11 | + <% end %> | |
12 | + <ul> | |
13 | + <%= _("To see the full list of friends suggestions, follow the link: %s") % @people_suggestions_url %> | |
14 | +<% end %> | |
15 | + | |
16 | +<% unless @communities_suggestions.empty? %> | |
17 | + <p><%= _('Communities suggestions:') %></p> | |
18 | + | |
19 | + <ul> | |
20 | + <% @communities_suggestions.each do |community| %> | |
21 | + <li><a href="<%= url_for(community.public_profile_url) %>"><%= community.name %></a></li> | |
22 | + <% end %> | |
23 | + <ul> | |
24 | + <%= _("To see the full list of communities suggestions, follow the link: %s") % @communities_suggestions_url %> | |
25 | +<% end %> | |
26 | + | |
27 | + | |
28 | +<%= _("Greetings,") %> | |
29 | + | |
30 | +-- | |
31 | +<%= _('%s team.') % @environment %> | |
32 | +<%= @url %> | ... | ... |
... | ... | @@ -0,0 +1,15 @@ |
1 | +class CreateProfileSuggestions < ActiveRecord::Migration | |
2 | + def change | |
3 | + create_table :profile_suggestions do |t| | |
4 | + t.references :person | |
5 | + t.references :suggestion | |
6 | + t.string :suggestion_type | |
7 | + t.text :categories | |
8 | + t.boolean :enabled, :default => true | |
9 | + | |
10 | + t.timestamps | |
11 | + end | |
12 | + add_index :profile_suggestions, :person_id | |
13 | + add_index :profile_suggestions, :suggestion_id | |
14 | + end | |
15 | +end | ... | ... |
db/schema.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20140605222753) do | |
14 | +ActiveRecord::Schema.define(:version => 20140720144212) do | |
15 | 15 | |
16 | 16 | create_table "abuse_reports", :force => true do |t| |
17 | 17 | t.integer "reporter_id" |
... | ... | @@ -446,6 +446,19 @@ ActiveRecord::Schema.define(:version => 20140605222753) do |
446 | 446 | add_index "products", ["product_category_id"], :name => "index_products_on_product_category_id" |
447 | 447 | add_index "products", ["profile_id"], :name => "index_products_on_profile_id" |
448 | 448 | |
449 | + create_table "profile_suggestions", :force => true do |t| | |
450 | + t.integer "person_id" | |
451 | + t.integer "suggestion_id" | |
452 | + t.string "suggestion_type" | |
453 | + t.text "categories" | |
454 | + t.boolean "enabled", :default => true | |
455 | + t.datetime "created_at", :null => false | |
456 | + t.datetime "updated_at", :null => false | |
457 | + end | |
458 | + | |
459 | + add_index "profile_suggestions", ["person_id"], :name => "index_profile_suggestions_on_person_id" | |
460 | + add_index "profile_suggestions", ["suggestion_id"], :name => "index_profile_suggestions_on_suggestion_id" | |
461 | + | |
449 | 462 | create_table "profiles", :force => true do |t| |
450 | 463 | t.string "name" |
451 | 464 | t.string "type" | ... | ... |
... | ... | @@ -0,0 +1,44 @@ |
1 | +LogLevel 1 | |
2 | +Alive 10 | |
3 | +Client 120 | |
4 | +TimeOut 300 | |
5 | +Control "/var/run/pound/poundctl.socket" | |
6 | + | |
7 | +ListenHTTP | |
8 | + Address 192.168.1.86 | |
9 | + Port 80 | |
10 | + xHTTP 1 | |
11 | + # uncomment code above if you are using chat | |
12 | + #Service | |
13 | + # URL "/http-bind.*" | |
14 | + # Backend | |
15 | + # Address 127.0.0.1 | |
16 | + # Port 5280 | |
17 | + # End | |
18 | + #End | |
19 | + Service | |
20 | + Redirect "https://mydomain.example.com" | |
21 | + End | |
22 | +End | |
23 | + | |
24 | +ListenHTTPS | |
25 | + Address 192.168.1.86 | |
26 | + Port 443 | |
27 | + Cert "/etc/noosfero/ssl/noosfero.pem" | |
28 | + Ciphers "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM" | |
29 | + xHTTP 1 | |
30 | + # uncomment code above if you are using chat | |
31 | + #Service | |
32 | + # URL "/http-bind.*" | |
33 | + # Backend | |
34 | + # Address 127.0.0.1 | |
35 | + # Port 5280 | |
36 | + # End | |
37 | + #End | |
38 | + Service | |
39 | + BackEnd | |
40 | + Address 127.0.0.1 | |
41 | + Port 6081 | |
42 | + End | |
43 | + End | |
44 | +End | ... | ... |
... | ... | @@ -0,0 +1,17 @@ |
1 | +class ProfileSuggestionsJob < Struct.new(:person_id) | |
2 | + | |
3 | + def perform | |
4 | + begin | |
5 | + person = Person.find(person_id) | |
6 | + | |
7 | + ProfileSuggestion::RULES.each do |rule| | |
8 | + ProfileSuggestion.send(rule, person) | |
9 | + end | |
10 | + | |
11 | + UserMailer.profiles_suggestions_email(person).deliver | |
12 | + rescue Exception => exception | |
13 | + Rails.logger.warn("Error with suggestions for person ID %d\n%s" % [person_id, exception.to_s]) | |
14 | + end | |
15 | + end | |
16 | + | |
17 | +end | ... | ... |
plugins/people_block/controllers/people_block_plugin_profile_controller.rb
... | ... | @@ -4,7 +4,7 @@ class PeopleBlockPluginProfileController < ProfileController |
4 | 4 | |
5 | 5 | def members |
6 | 6 | if is_cache_expired?(profile.members_cache_key(params)) |
7 | - if(params[:role_key]) | |
7 | + unless params[:role_key].blank? | |
8 | 8 | role = Role.find_by_key_and_environment_id(params[:role_key], profile.environment) |
9 | 9 | @members = profile.members.with_role(role.id).includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage]) |
10 | 10 | @members_title = role.name | ... | ... |
plugins/people_block/test/functional/people_block_plugin_profile_controller_test.rb
... | ... | @@ -34,7 +34,7 @@ class PeopleBlockPluginProfileControllerTest < ActionController::TestCase |
34 | 34 | attr_accessor :profile, :block, :admin, :member, :moderator |
35 | 35 | |
36 | 36 | should 'list members without role_key' do |
37 | - get :members, :profile => profile.identifier | |
37 | + get :members, :profile => profile.identifier, :role_key => "" | |
38 | 38 | assert_response :success |
39 | 39 | assert_template 'members' |
40 | 40 | assert_equivalent [@admin, @member, @moderator], assigns(:members) | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +require_dependency 'profile_suggestion' | |
2 | + | |
3 | +class ProfileSuggestion | |
4 | + | |
5 | + CATEGORIES.merge!({:common_classroom => _('Classroom in common')}) | |
6 | + | |
7 | + RULES += %w[ | |
8 | + people_with_common_classroom | |
9 | + ] | |
10 | + | |
11 | + def self.people_with_common_classroom(person) | |
12 | + usp_id = person.usp_id | |
13 | + return if usp_id.nil? | |
14 | + person_attempts = 0 | |
15 | + StoaPlugin::UspAlunoTurmaGrad.classrooms_from_person(usp_id).each do |classroom| | |
16 | + person_attempts += 1 | |
17 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS | |
18 | + StoaPlugin::UspAlunoTurmaGrad.find_all_by_codtur(classroom.codtur).each do |same_class| | |
19 | + classmate = Person.find_by_usp_id(same_class.codpes) | |
20 | + unless classmate.nil? || classmate == person || classmate.is_a_friend?(person) || person.already_request_friendship?(classmate) | |
21 | + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(classmate.id) | |
22 | + suggestion.common_classroom = 1 | |
23 | + suggestion.save | |
24 | + end | |
25 | + end | |
26 | + end | |
27 | + end | |
28 | + | |
29 | + def self.people_with_common_discipline(person) | |
30 | + person_attempts = 0 | |
31 | + person_attempts += 1 | |
32 | + return unless person.profile_suggestions.count < N_SUGGESTIONS && person_attempts < MAX_ATTEMPTS | |
33 | + end | |
34 | + | |
35 | +end | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +class StoaPlugin::UspAlunoTurmaGrad < ActiveRecord::Base | |
2 | + | |
3 | + establish_connection(:stoa) | |
4 | + set_table_name('alunoturma_gr') | |
5 | + | |
6 | + def self.exists?(usp_id) | |
7 | + StoaPlugin::UspUser.find_by_codpes(usp_id.to_i) | |
8 | + end | |
9 | + | |
10 | + def self.classrooms_from_person(usp_id) | |
11 | + StoaPlugin::UspAlunoTurmaGrad.find_all_by_codpes(usp_id) | |
12 | + end | |
13 | + | |
14 | +end | ... | ... |
public/javascripts/add-and-join.js
public/javascripts/chat.js
... | ... | @@ -99,7 +99,7 @@ jQuery(function($) { |
99 | 99 | }, |
100 | 100 | |
101 | 101 | render_body_message: function(body) { |
102 | - body = body.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\r?\n/g, '<br>'); | |
102 | + body = body.replace(/\r?\n/g, '<br>'); | |
103 | 103 | body = $().emoticon(body); |
104 | 104 | body = linkify(body, { |
105 | 105 | callback: function(text, href) { |
... | ... | @@ -320,7 +320,7 @@ jQuery(function($) { |
320 | 320 | var jid_id = Jabber.jid_to_id(jid); |
321 | 321 | var name = Jabber.name_of(jid_id); |
322 | 322 | create_conversation_tab(name, jid_id); |
323 | - Jabber.show_message(jid, name, message.body, 'other', Strophe.getNodeFromJid(jid)); | |
323 | + Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid)); | |
324 | 324 | $.sound.play('/sounds/receive.wav'); |
325 | 325 | return true; |
326 | 326 | }, |
... | ... | @@ -336,7 +336,7 @@ jQuery(function($) { |
336 | 336 | // is a message from another user, not mine |
337 | 337 | else if ($own_name != name) { |
338 | 338 | var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name]; |
339 | - Jabber.show_message(message.from, name, message.body, name, Strophe.getNodeFromJid(jid)); | |
339 | + Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid)); | |
340 | 340 | $.sound.play('/sounds/receive.wav'); |
341 | 341 | } |
342 | 342 | return true; |
... | ... | @@ -432,7 +432,7 @@ jQuery(function($) { |
432 | 432 | .c('body').t(body).up() |
433 | 433 | .c('active', {xmlns: Strophe.NS.CHAT_STATES}); |
434 | 434 | Jabber.connection.send(message); |
435 | - Jabber.show_message(jid, $own_name, body, 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); | |
435 | + Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid)); | |
436 | 436 | }, |
437 | 437 | |
438 | 438 | is_a_room: function(jid_id) { |
... | ... | @@ -632,6 +632,13 @@ jQuery(function($) { |
632 | 632 | } |
633 | 633 | } |
634 | 634 | |
635 | + function escape_html(body) { | |
636 | + return body | |
637 | + .replace(/&/g, '&') | |
638 | + .replace(/</g, '<') | |
639 | + .replace(/>/g, '>'); | |
640 | + } | |
641 | + | |
635 | 642 | }); |
636 | 643 | |
637 | 644 | function checkTime(i) { | ... | ... |
public/stylesheets/application.css
... | ... | @@ -3869,12 +3869,16 @@ h1#agenda-title { |
3869 | 3869 | |
3870 | 3870 | /* ==> @import url(manage_contacts_list.css); <== */ |
3871 | 3871 | |
3872 | -.controller-favorite_enterprises .profile-list, .controller-friends .profile-list { | |
3872 | +.controller-favorite_enterprises .profile-list, | |
3873 | +.controller-friends .profile-list, | |
3874 | +.profiles-suggestions .profile-list { | |
3873 | 3875 | margin: 0px; |
3874 | 3876 | padding: 0px; |
3875 | 3877 | list-style: none; |
3876 | 3878 | } |
3877 | -.controller-favorite_enterprises .profile-list li, .controller-friends .profile-list li { | |
3879 | +.controller-favorite_enterprises .profile-list li, | |
3880 | +.controller-friends .profile-list li, | |
3881 | +.profiles-suggestions .profile-list li { | |
3878 | 3882 | float: left; |
3879 | 3883 | width: 90px; |
3880 | 3884 | height: 90px; |
... | ... | @@ -3886,52 +3890,74 @@ h1#agenda-title { |
3886 | 3890 | list-style: none; |
3887 | 3891 | position: relative; |
3888 | 3892 | } |
3889 | -.controller-favorite_enterprises .profile-list li:hover, .controller-friends .profile-list li:hover { | |
3893 | +.controller-favorite_enterprises .profile-list li:hover, | |
3894 | +.controller-friends .profile-list li:hover, | |
3895 | +.profiles-suggestions .profile-list li:hover { | |
3890 | 3896 | border: 2px solid #eeeeec; |
3891 | 3897 | } |
3892 | -.controller-favorite_enterprises .profile-list img, .controller-friends .profile-list img { | |
3898 | +.controller-favorite_enterprises .profile-list img, | |
3899 | +.controller-friends .profile-list img, | |
3900 | +.profiles-suggestions .profile-list img { | |
3893 | 3901 | border: none; |
3894 | 3902 | } |
3895 | -.controller-favorite_enterprises .profile-list a.profile-link, .controller-friends .profile-list a.profile-link { | |
3903 | +.controller-favorite_enterprises .profile-list a.profile-link, | |
3904 | +.controller-friends .profile-list a.profile-link, | |
3905 | +.profiles-suggestions .profile-list a.profile-link { | |
3896 | 3906 | text-decoration: none; |
3897 | 3907 | text-align: center; |
3898 | 3908 | display: block; |
3899 | 3909 | font-size: 11px; |
3900 | 3910 | } |
3901 | -.controller-favorite_enterprises .profile-list a.profile-link:hover, .controller-friends .profile-list a.profile-link:hover { | |
3911 | +.controller-favorite_enterprises .profile-list a.profile-link:hover, | |
3912 | +.controller-friends .profile-list a.profile-link:hover, | |
3913 | +.profiles-suggestions .profile-list a.profile-link:hover { | |
3902 | 3914 | color: #FFF; |
3903 | 3915 | } |
3904 | -.controller-favorite_enterprises .profile-list .profile_link span, .controller-friends .profile-list .profile_link span { | |
3916 | +.controller-favorite_enterprises .profile-list .profile_link span, | |
3917 | +.controller-friends .profile-list .profile_link span, | |
3918 | +.profiles-suggestions .profile-list .profile_link span { | |
3905 | 3919 | width: 80px; |
3906 | 3920 | display: block; |
3907 | 3921 | overflow: hidden; |
3908 | 3922 | } |
3909 | -.controller-favorite_enterprises .profile-list, .controller-friends .profile-list { | |
3923 | +.controller-favorite_enterprises .profile-list, | |
3924 | +.controller-friends .profile-list, | |
3925 | +.profiles-suggestions .profile-list { | |
3910 | 3926 | position: relative; |
3911 | 3927 | } |
3912 | -.controller-favorite_enterprises .profile-list .controll, .controller-friends .profile-list .controll { | |
3928 | +.controller-favorite_enterprises .profile-list .controll, | |
3929 | +.controller-friends .profile-list .controll, | |
3930 | +.profiles-suggestions .profile-list .controll { | |
3913 | 3931 | position: absolute; |
3914 | 3932 | top: 7px; |
3915 | 3933 | right: -10px; |
3916 | 3934 | } |
3917 | -.controller-favorite_enterprises .profile-list .controll a, .controller-friends .profile-list .controll a { | |
3935 | +.controller-favorite_enterprises .profile-list .controll a, | |
3936 | +.controller-friends .profile-list .controll a, | |
3937 | +.profiles-suggestions .profile-list .controll a { | |
3918 | 3938 | display: block; |
3919 | 3939 | margin-bottom: 2px; |
3920 | 3940 | } |
3921 | -.controller-favorite_enterprises .msie6 .profile-list .controll a, .controller-friends .msie6 .profile-list .controll a { | |
3941 | +.controller-favorite_enterprises .msie6 .profile-list .controll a, | |
3942 | +.controller-friends .msie6 .profile-list .controll a, | |
3943 | +.profiles-suggestions .msie6 .profile-list .controll a { | |
3922 | 3944 | width: 0px; |
3923 | 3945 | } |
3924 | -.controller-favorite_enterprises .button-bar, .controller-friends .button-bar { | |
3946 | +.controller-favorite_enterprises .button-bar, | |
3947 | +.controller-friends .button-bar, | |
3948 | +.profiles-suggestions .button-bar { | |
3925 | 3949 | clear: both; |
3926 | 3950 | padding-top: 20px; |
3927 | 3951 | } |
3928 | 3952 | /* ==> public/stylesheets/controller_friends.css <== */ |
3929 | 3953 | |
3930 | -.controller-friends #remove_friend .friend_picture { | |
3954 | +.controller-friends #remove_friend .friend_picture, | |
3955 | +#remove_suggestion .suggestion_picture { | |
3931 | 3956 | float: left; |
3932 | 3957 | margin-right: 15px; |
3933 | 3958 | } |
3934 | -.controller-friends #remove_friend form { | |
3959 | +.controller-friends #remove_friend form, | |
3960 | +#remove_suggestion form { | |
3935 | 3961 | clear: both; |
3936 | 3962 | padding-top: 20px; |
3937 | 3963 | } | ... | ... |
test/functional/friends_controller_test.rb
... | ... | @@ -70,4 +70,41 @@ class FriendsControllerTest < ActionController::TestCase |
70 | 70 | assert_no_tag :tag => 'a', :attributes => { :href => "/profile/testuser/invite/friends" } |
71 | 71 | end |
72 | 72 | |
73 | + should 'not display list suggestions button if there is no suggestion' do | |
74 | + get :index, :profile => 'testuser' | |
75 | + assert_no_tag :tag => 'a', :content => 'Suggest friends', :attributes => { :href => "/myprofile/testuser/friends/suggest" } | |
76 | + end | |
77 | + | |
78 | + should 'display link to list suggestions page' do | |
79 | + profile.profile_suggestions.create(:suggestion => friend) | |
80 | + get :index, :profile => 'testuser' | |
81 | + assert_tag :tag => 'a', :content => 'See more suggestions...', :attributes => { :href => "/myprofile/testuser/friends/suggest" } | |
82 | + end | |
83 | + | |
84 | + should 'display people suggestions' do | |
85 | + profile.profile_suggestions.create(:suggestion => friend) | |
86 | + get :suggest, :profile => 'testuser' | |
87 | + assert_tag :tag => 'a', :content => friend.name, :attributes => { :href => "/profile/#{friend.identifier}" } | |
88 | + end | |
89 | + | |
90 | + should 'display button to add friend suggestion' do | |
91 | + profile.profile_suggestions.create(:suggestion => friend) | |
92 | + get :suggest, :profile => 'testuser' | |
93 | + assert_tag :tag => 'a', :attributes => { :href => "/profile/#{friend.identifier}/add" } | |
94 | + end | |
95 | + | |
96 | + should 'display button to remove people suggestion' do | |
97 | + profile.profile_suggestions.create(:suggestion => friend) | |
98 | + get :suggest, :profile => 'testuser' | |
99 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/testuser/friends/remove_suggestion/#{friend.identifier}" } | |
100 | + end | |
101 | + | |
102 | + should 'remove suggestion of friend' do | |
103 | + suggestion = profile.profile_suggestions.create(:suggestion => friend) | |
104 | + post :remove_suggestion, :profile => 'testuser', :id => friend.identifier | |
105 | + | |
106 | + assert_redirected_to :action => 'suggest' | |
107 | + assert_equal false, ProfileSuggestion.find(suggestion.id).enabled | |
108 | + end | |
109 | + | |
73 | 110 | end | ... | ... |
test/functional/memberships_controller_test.rb
... | ... | @@ -328,4 +328,42 @@ class MembershipsControllerTest < ActionController::TestCase |
328 | 328 | assert_includes assigns(:roles), role1 |
329 | 329 | assert_not_includes assigns(:roles), role2 |
330 | 330 | end |
331 | + | |
332 | + should 'display list suggestions button' do | |
333 | + community = fast_create(Community) | |
334 | + profile.profile_suggestions.create(:suggestion => community) | |
335 | + get :index, :profile => 'testuser' | |
336 | + assert_tag :tag => 'a', :content => 'See some suggestions of communities...', :attributes => { :href => "/myprofile/testuser/memberships/suggest" } | |
337 | + end | |
338 | + | |
339 | + should 'display communities suggestions' do | |
340 | + community = fast_create(Community) | |
341 | + profile.profile_suggestions.create(:suggestion => community) | |
342 | + get :suggest, :profile => 'testuser' | |
343 | + assert_tag :tag => 'a', :content => community.name, :attributes => { :href => "/profile/#{community.identifier}" } | |
344 | + end | |
345 | + | |
346 | + should 'display button to join on community suggestion' do | |
347 | + community = fast_create(Community) | |
348 | + profile.profile_suggestions.create(:suggestion => community) | |
349 | + get :suggest, :profile => 'testuser' | |
350 | + assert_tag :tag => 'a', :attributes => { :href => "/profile/#{community.identifier}/join" } | |
351 | + end | |
352 | + | |
353 | + should 'display button to remove community suggestion' do | |
354 | + community = fast_create(Community) | |
355 | + profile.profile_suggestions.create(:suggestion => community) | |
356 | + get :suggest, :profile => 'testuser' | |
357 | + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/testuser/memberships/remove_suggestion/#{community.identifier}" } | |
358 | + end | |
359 | + | |
360 | + should 'remove suggestion of community' do | |
361 | + community = fast_create(Community) | |
362 | + suggestion = profile.profile_suggestions.create(:suggestion => community) | |
363 | + post :remove_suggestion, :profile => 'testuser', :id => community.identifier | |
364 | + | |
365 | + assert_redirected_to :action => 'suggest' | |
366 | + assert_equal false, ProfileSuggestion.find(suggestion.id).enabled | |
367 | + end | |
368 | + | |
331 | 369 | end | ... | ... |
test/unit/add_friend_test.rb
... | ... | @@ -137,4 +137,11 @@ class AddFriendTest < ActiveSupport::TestCase |
137 | 137 | assert_match(/#{task.requestor.name} wants to be your friend/, email.subject) |
138 | 138 | end |
139 | 139 | |
140 | + should 'disable suggestion if profile requested friendship' do | |
141 | + suggestion = ProfileSuggestion.create(:person => person1, :suggestion => person2, :enabled => true) | |
142 | + | |
143 | + task = AddFriend.create(:person => person1, :friend => person2) | |
144 | + assert_equal false, ProfileSuggestion.find(suggestion.id).enabled | |
145 | + end | |
146 | + | |
140 | 147 | end | ... | ... |
test/unit/person_test.rb
... | ... | @@ -1481,4 +1481,79 @@ class PersonTest < ActiveSupport::TestCase |
1481 | 1481 | person.reload |
1482 | 1482 | end |
1483 | 1483 | end |
1484 | + | |
1485 | + should 'have a list of suggested people to be friend' do | |
1486 | + person = create_user('person').person | |
1487 | + suggested_friend = fast_create(Person) | |
1488 | + | |
1489 | + ProfileSuggestion.create(:person => person, :suggestion => suggested_friend) | |
1490 | + assert_equal [suggested_friend], person.suggested_people | |
1491 | + end | |
1492 | + | |
1493 | + should 'have a list of suggested communities to be member' do | |
1494 | + person = create_user('person').person | |
1495 | + suggested_community = fast_create(Community) | |
1496 | + | |
1497 | + ProfileSuggestion.create(:person => person, :suggestion => suggested_community) | |
1498 | + assert_equal [suggested_community], person.suggested_communities | |
1499 | + end | |
1500 | + | |
1501 | + should 'remove profile suggestion when person is destroyed' do | |
1502 | + person = create_user('person').person | |
1503 | + suggested_community = fast_create(Community) | |
1504 | + | |
1505 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_community) | |
1506 | + | |
1507 | + person.destroy | |
1508 | + assert_raise ActiveRecord::RecordNotFound do | |
1509 | + ProfileSuggestion.find suggestion.id | |
1510 | + end | |
1511 | + end | |
1512 | + | |
1513 | + should 'remove profile suggestion when suggested profile is destroyed' do | |
1514 | + person = create_user('person').person | |
1515 | + suggested_community = fast_create(Community) | |
1516 | + | |
1517 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_community) | |
1518 | + | |
1519 | + suggested_community.destroy | |
1520 | + assert_raise ActiveRecord::RecordNotFound do | |
1521 | + ProfileSuggestion.find suggestion.id | |
1522 | + end | |
1523 | + end | |
1524 | + | |
1525 | + should 'not suggest disabled suggestion of people' do | |
1526 | + person = create_user('person').person | |
1527 | + suggested_person = fast_create(Person) | |
1528 | + disabled_suggested_person = fast_create(Person) | |
1529 | + | |
1530 | + enabled_suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_person) | |
1531 | + disabled_suggestion = ProfileSuggestion.create(:person => person, :suggestion => disabled_suggested_person, :enabled => false) | |
1532 | + | |
1533 | + assert_equal [suggested_person], person.suggested_people | |
1534 | + end | |
1535 | + | |
1536 | + should 'not suggest disabled suggestion of communities' do | |
1537 | + person = create_user('person').person | |
1538 | + suggested_community = fast_create(Community) | |
1539 | + disabled_suggested_community = fast_create(Community) | |
1540 | + | |
1541 | + enabled_suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_community) | |
1542 | + disabled_suggestion = ProfileSuggestion.create(:person => person, :suggestion => disabled_suggested_community, :enabled => false) | |
1543 | + | |
1544 | + assert_equal [suggested_community], person.suggested_communities | |
1545 | + end | |
1546 | + | |
1547 | + should 'return url to people suggestions for a person' do | |
1548 | + environment = create_environment('mycolivre.net') | |
1549 | + profile = build(Person, :identifier => 'testprofile', :environment_id => create_environment('mycolivre.net').id) | |
1550 | + assert_equal({ :host => "mycolivre.net", :profile => 'testprofile', :controller => 'friends', :action => 'suggest' }, profile.people_suggestions_url) | |
1551 | + end | |
1552 | + | |
1553 | + should 'return url to communities suggestions for a person' do | |
1554 | + environment = create_environment('mycolivre.net') | |
1555 | + profile = build(Person, :identifier => 'testprofile', :environment_id => create_environment('mycolivre.net').id) | |
1556 | + assert_equal({ :host => "mycolivre.net", :profile => 'testprofile', :controller => 'memberships', :action => 'suggest' }, profile.communities_suggestions_url) | |
1557 | + end | |
1558 | + | |
1484 | 1559 | end | ... | ... |
... | ... | @@ -0,0 +1,121 @@ |
1 | +# encoding: UTF-8 | |
2 | +require File.dirname(__FILE__) + '/../test_helper' | |
3 | + | |
4 | +class ProfileSuggestionTest < ActiveSupport::TestCase | |
5 | + | |
6 | + def setup | |
7 | + @person = create_user('test_user').person | |
8 | + @community = fast_create(Community) | |
9 | + end | |
10 | + attr_reader :person, :community | |
11 | + | |
12 | + should 'save the profile class' do | |
13 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => community) | |
14 | + assert_equal 'Community', suggestion.suggestion_type | |
15 | + end | |
16 | + | |
17 | + should 'only accept pre-defined categories' do | |
18 | + suggestion = ProfileSuggestion.new(:person => person, :suggestion => community) | |
19 | + | |
20 | + suggestion.categories = {:unexistent => 1} | |
21 | + suggestion.valid? | |
22 | + assert suggestion.errors[:categories.to_s].present? | |
23 | + end | |
24 | + | |
25 | + should 'disable a suggestion' do | |
26 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => community) | |
27 | + | |
28 | + suggestion.disable | |
29 | + assert_equal false, ProfileSuggestion.find(suggestion.id).enabled? | |
30 | + end | |
31 | + | |
32 | + should 'not suggest the same community twice' do | |
33 | + ProfileSuggestion.create(:person => person, :suggestion => community) | |
34 | + | |
35 | + repeated_suggestion = ProfileSuggestion.new(:person => person, :suggestion => community) | |
36 | + | |
37 | + repeated_suggestion.valid? | |
38 | + assert_equal true, repeated_suggestion.errors[:suggestion_id.to_s].present? | |
39 | + end | |
40 | + | |
41 | + should 'update existing person suggestion when the number of common friends increase' do | |
42 | + suggested_person = create_user('test_user').person | |
43 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_person, :common_friends => 2) | |
44 | + | |
45 | + friend = create_user('friend').person | |
46 | + friend2 = create_user('friend2').person | |
47 | + friend3 = create_user('friend2').person | |
48 | + person.add_friend friend | |
49 | + person.add_friend friend2 | |
50 | + person.add_friend friend3 | |
51 | + | |
52 | + friend.add_friend suggested_person | |
53 | + | |
54 | + suggested_person.add_friend friend | |
55 | + suggested_person.add_friend friend2 | |
56 | + suggested_person.add_friend friend3 | |
57 | + | |
58 | + assert_difference 'ProfileSuggestion.find(suggestion.id).common_friends', 1 do | |
59 | + ProfileSuggestion.friends_of_friends(person) | |
60 | + end | |
61 | + end | |
62 | + | |
63 | + should 'update existing person suggestion when the number of common communities increase' do | |
64 | + suggested_person = create_user('test_user').person | |
65 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_person, :common_communities => 1) | |
66 | + | |
67 | + community.add_member person | |
68 | + community.add_member suggested_person | |
69 | + | |
70 | + community2 = fast_create(Community) | |
71 | + community2.add_member person | |
72 | + community2.add_member suggested_person | |
73 | + | |
74 | + assert_difference 'ProfileSuggestion.find(suggestion.id).common_communities', 1 do | |
75 | + ProfileSuggestion.people_with_common_communities(person) | |
76 | + end | |
77 | + end | |
78 | + | |
79 | + should 'update existing person suggestion when the number of common tags increase' do | |
80 | + suggested_person = create_user('test_user').person | |
81 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => suggested_person, :common_tags => 1) | |
82 | + | |
83 | + create(Article, :created_by => person, :profile => person, :tag_list => 'first-tag, second-tag, third-tag, fourth-tag') | |
84 | + create(Article, :created_by => suggested_person, :profile => suggested_person, :tag_list => 'first-tag, second-tag, third-tag') | |
85 | + | |
86 | + assert_difference 'ProfileSuggestion.find(suggestion.id).common_tags', 2 do | |
87 | + ProfileSuggestion.people_with_common_tags(person) | |
88 | + end | |
89 | + end | |
90 | + | |
91 | + should 'update existing community suggestion when the number of common friends increase' do | |
92 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => community, :common_friends => 1) | |
93 | + | |
94 | + member1 = create_user('member1').person | |
95 | + member2 = create_user('member2').person | |
96 | + | |
97 | + person.add_friend member1 | |
98 | + person.add_friend member2 | |
99 | + | |
100 | + community.add_member member1 | |
101 | + community.add_member member2 | |
102 | + | |
103 | + assert_difference 'ProfileSuggestion.find(suggestion.id).common_friends', 1 do | |
104 | + ProfileSuggestion.communities_with_common_friends(person) | |
105 | + end | |
106 | + | |
107 | + end | |
108 | + | |
109 | + should 'update existing community suggestion when the number of common tags increase' do | |
110 | + other_person = create_user('test_user').person | |
111 | + | |
112 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => community, :common_tags => 1) | |
113 | + | |
114 | + create(Article, :created_by => person, :profile => person, :tag_list => 'first-tag, second-tag, third-tag, fourth-tag') | |
115 | + create(Article, :created_by => other_person, :profile => community, :tag_list => 'first-tag, second-tag, third-tag') | |
116 | + | |
117 | + assert_difference 'ProfileSuggestion.find(suggestion.id).common_tags', 2 do | |
118 | + ProfileSuggestion.communities_with_common_tags(person) | |
119 | + end | |
120 | + end | |
121 | +end | ... | ... |
... | ... | @@ -0,0 +1,97 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class ProfileSuggestionsJobTest < ActiveSupport::TestCase | |
4 | + | |
5 | + should 'suggest friends from friends' do | |
6 | + person = create_user('person').person | |
7 | + friend = create_user('friend').person | |
8 | + friend2 = create_user('friend2').person | |
9 | + | |
10 | + person.add_friend friend | |
11 | + person.add_friend friend2 | |
12 | + | |
13 | + friend_of_friend = create_user('friend_of_friend').person | |
14 | + friend.add_friend friend_of_friend | |
15 | + | |
16 | + friend_of_friend.add_friend friend | |
17 | + friend_of_friend.add_friend friend2 | |
18 | + | |
19 | + job = ProfileSuggestionsJob.new(person.id) | |
20 | + assert_difference 'ProfileSuggestion.count', 1 do | |
21 | + job.perform | |
22 | + end | |
23 | + assert_equal [friend_of_friend], person.suggested_people | |
24 | + end | |
25 | + | |
26 | + should 'suggest friends from communities' do | |
27 | + person = create_user('person').person | |
28 | + community = fast_create(Community) | |
29 | + | |
30 | + member1 = create_user('member1').person | |
31 | + member2 = create_user('member2').person | |
32 | + | |
33 | + community.add_member person | |
34 | + community.add_member member1 | |
35 | + community.add_member member2 | |
36 | + | |
37 | + job = ProfileSuggestionsJob.new(person.id) | |
38 | + assert_difference 'ProfileSuggestion.count', 2 do | |
39 | + job.perform | |
40 | + end | |
41 | + assert_equivalent [member1, member2], person.suggested_people | |
42 | + end | |
43 | + | |
44 | + should 'suggest friends from tags' do | |
45 | + person = create_user('person').person | |
46 | + person2 = create_user('person2').person | |
47 | + person3 = create_user('person3').person | |
48 | + | |
49 | + create(Article, :created_by => person, :profile => person, :tag_list => 'first-tag, second-tag') | |
50 | + create(Article, :created_by => person2, :profile => person2, :tag_list => 'first-tag, second-tag, third-tag') | |
51 | + create(Article, :created_by => person3, :profile => person3, :tag_list => 'first-tag') | |
52 | + | |
53 | + job = ProfileSuggestionsJob.new(person.id) | |
54 | + assert_difference 'ProfileSuggestion.count', 1 do | |
55 | + job.perform | |
56 | + end | |
57 | + assert_equivalent [person2], person.suggested_people | |
58 | + end | |
59 | + | |
60 | + should 'suggest from communities friends' do | |
61 | + person = create_user('person').person | |
62 | + | |
63 | + member1 = create_user('member1').person | |
64 | + member2 = create_user('member2').person | |
65 | + | |
66 | + person.add_friend member1 | |
67 | + person.add_friend member2 | |
68 | + | |
69 | + community = fast_create(Community) | |
70 | + community.add_member member1 | |
71 | + community.add_member member2 | |
72 | + | |
73 | + job = ProfileSuggestionsJob.new(person.id) | |
74 | + assert_difference 'ProfileSuggestion.count', 1 do | |
75 | + job.perform | |
76 | + end | |
77 | + assert_equivalent [community], person.suggested_communities | |
78 | + end | |
79 | + | |
80 | + should 'suggest communities from tags' do | |
81 | + person = create_user('person').person | |
82 | + person2 = create_user('person2').person | |
83 | + | |
84 | + community = fast_create(Community) | |
85 | + community.add_admin person2 | |
86 | + | |
87 | + create(Article, :created_by => person, :profile => person, :tag_list => 'first-tag, second-tag') | |
88 | + create(Article, :created_by => person2, :profile => community, :tag_list => 'first-tag, second-tag, third-tag') | |
89 | + | |
90 | + job = ProfileSuggestionsJob.new(person.id) | |
91 | + assert_difference 'ProfileSuggestion.count', 1 do | |
92 | + job.perform | |
93 | + end | |
94 | + assert_equivalent [community], person.suggested_communities | |
95 | + end | |
96 | + | |
97 | +end | ... | ... |
test/unit/profile_test.rb
... | ... | @@ -1987,4 +1987,14 @@ class ProfileTest < ActiveSupport::TestCase |
1987 | 1987 | template.save! |
1988 | 1988 | assert_equal body, template.welcome_page_content |
1989 | 1989 | end |
1990 | + | |
1991 | + should 'disable suggestion if profile requested membership' do | |
1992 | + person = fast_create(Person) | |
1993 | + community = fast_create(Community) | |
1994 | + suggestion = ProfileSuggestion.create(:person => person, :suggestion => community, :enabled => true) | |
1995 | + | |
1996 | + community.add_member person | |
1997 | + assert_equal false, ProfileSuggestion.find(suggestion.id).enabled | |
1998 | + end | |
1999 | + | |
1990 | 2000 | end | ... | ... |
... | ... | @@ -0,0 +1,74 @@ |
1 | +%%% | |
2 | +%%% Noosfero ejabberd configuration file | |
3 | +%%% This config must be in UTF-8 encoding | |
4 | +%%% | |
5 | + | |
6 | +{acl, admin, {user, "admin", "mydomain.example.com"}}. | |
7 | +{hosts, ["mydomain.example.com"]}. | |
8 | + | |
9 | +{loglevel, 4}. | |
10 | +{listen, | |
11 | + [ | |
12 | + {5222, ejabberd_c2s, [ | |
13 | + {access, c2s}, | |
14 | + {shaper, c2s_shaper}, | |
15 | + {max_stanza_size, 65536}, | |
16 | + starttls, {certfile, "/etc/ejabberd/ejabberd.pem"} | |
17 | + ]}, | |
18 | + {5280, ejabberd_http, [ | |
19 | + http_bind, | |
20 | + http_poll | |
21 | + ]} | |
22 | + ]}. | |
23 | +{max_fsm_queue, 1000}. | |
24 | +{auth_method, odbc}. | |
25 | +{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}. | |
26 | +{shaper, normal, {maxrate, 10000000}}. | |
27 | +{shaper, fast, {maxrate, 50000}}. | |
28 | +{acl, local, {user_regexp, ""}}. | |
29 | +{access, max_user_sessions, [{10, all}]}. | |
30 | +{access, local, [{allow, local}]}. | |
31 | +{access, c2s, [{deny, blocked}, | |
32 | + {allow, all}]}. | |
33 | +{access, c2s_shaper, [{none, admin}, | |
34 | + {normal, all}]}. | |
35 | +{access, announce, [{allow, admin}]}. | |
36 | +{access, configure, [{allow, admin}]}. | |
37 | +{access, muc_admin, [{allow, admin}]}. | |
38 | +{access, muc, [{allow, all}]}. | |
39 | +{access, pubsub_createnode, [{allow, all}]}. | |
40 | +{language, "pt"}. | |
41 | +{modules, | |
42 | + [ | |
43 | + {mod_adhoc, []}, | |
44 | + {mod_announce, [{access, announce}]}, % requires mod_adhoc | |
45 | + {mod_caps, []}, | |
46 | + {mod_configure,[]}, % requires mod_adhoc | |
47 | + {mod_disco, []}, | |
48 | + {mod_last, []}, | |
49 | + {mod_muc, [ | |
50 | + {access, muc}, | |
51 | + {access_create, muc}, | |
52 | + {access_persistent, muc}, | |
53 | + {access_admin, muc_admin}, | |
54 | + {max_users, 500}, | |
55 | + {default_room_options, [{anonymous, false}]} | |
56 | + ]}, | |
57 | + {mod_privacy_odbc, []}, | |
58 | + {mod_private_odbc, []}, | |
59 | + {mod_proxy65, [ | |
60 | + {access, local}, | |
61 | + {shaper, c2s_shaper} | |
62 | + ]}, | |
63 | + {mod_roster_odbc, []}, | |
64 | + {mod_stats, []}, | |
65 | + {mod_time, []}, | |
66 | + {mod_vcard, []}, | |
67 | + {mod_http_bind, []}, | |
68 | + {mod_version, []} | |
69 | + ]}. | |
70 | + | |
71 | +%%% Local Variables: | |
72 | +%%% mode: erlang | |
73 | +%%% End: | |
74 | +%%% vim: set filetype=erlang tabstop=8: | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +[PostgreSQLEjabberdNoosfero] | |
2 | +Description = PostgreSQL Noosfero ejabberd database | |
3 | +Driver = PostgreSQL Unicode | |
4 | +Trace = No | |
5 | +TraceFile = /tmp/psqlodbc.log | |
6 | +Database = noosfero | |
7 | +Servername = localhost | |
8 | +UserName = noosfero | |
9 | +Password = <copy the password present in the database.yml file> | |
10 | +Port = | |
11 | +ReadOnly = No | |
12 | +RowVersioning = No | |
13 | +ShowSystemTables = No | |
14 | +ShowOidColumn = No | |
15 | +FakeOidIndex = No | |
16 | +ConnSettings = SET search_path TO ejabberd | ... | ... |